//+------------------------------------------------------------------+ //| PRIC_EA_V1.0_3Kline.mq5 | //| PRIC VENTURES | //| 耐心 | 理性 | 坚持 | 勇气 | //+------------------------------------------------------------------+ #property copyright "PRIC VENTURES" #property link "Patience, Rationality, Insistence, Courage" #property version "1.0" #property description "PRIC_EA_V1.0_3Kline.mq5" #property description "魔术号码:202301" /* (此区域介绍策略) 【策略名称】 3根k线策略 【策略实现】 【技术指标】 */ //▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ //+------------------------------------------------------------------+------------------------------------------------------------------+ //| ☀☀☀此区域↓↓↓→导入类/头文件★█ ▼▼▼▼▼▼导入类/头文件▼▼▼▼▼▼ //+------------------------------------------------------------------+------------------------------------------------------------------+ #include //▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ //+------------------------------------------------------------------+------------------------------------------------------------------+ //| 此区域↓↓↓→定义Input变量★█ ▼▼▼▼▼▼定义Input变量▼▼▼▼▼▼ //+------------------------------------------------------------------+------------------------------------------------------------------+ input int MA1=5; input ENUM_MA_METHOD MAType1 = MODE_SMA; input int MA2=50; input ENUM_MA_METHOD MAType2 = MODE_SMA; input int Slippage = 50; // 滑点 input double LotSize = 0.1; // 下单量 //▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ //+------------------------------------------------------------------+------------------------------------------------------------------+ //| 此区域↓↓↓→定义全局变量★█ ▼▼▼▼▼▼定义全局变量▼▼▼▼▼▼ //+------------------------------------------------------------------+------------------------------------------------------------------+ #define def_MagicValue 202301 //此处设置Magic 魔术单号 的数值(一个策略一个编号) enum eChecker {None_Open, Long_Open, Short_Open}; eChecker m_Check; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double Lowest = 0; double Highest = 0; int sl_lastBar = 0; double MA1Buffer[]; double MA2Buffer[]; int MA1Handle; int MA2Handle; int count; //▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ //+------------------------------------------------------------------+------------------------------------------------------------------+ //| 此区域↓↓↓→定义结构★█ ▼▼▼▼▼▼定义结构▼▼▼▼▼▼ //+------------------------------------------------------------------+------------------------------------------------------------------+ //--- 本策略不涉及 //▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ //+------------------------------------------------------------------+------------------------------------------------------------------+ //| 此区域↓↓↓→声明对象/变量★█ ▼▼▼▼▼▼声明对象/变量▼▼▼▼▼▼ //+------------------------------------------------------------------+------------------------------------------------------------------+ COrderSystem *order; //▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ //+------------------------------------------------------------------+------------------------------------------------------------------+ //| OnInit() 函数是 初始化 事件处理程序。★█ ▼▼▼▼▼▼OnInit()▼▼▼▼▼▼ //+------------------------------------------------------------------+------------------------------------------------------------------+ int OnInit() { //+------------------------------------------------------------------------------------------------------------------------+ //| 此处↓↓↓→定义指标句柄/初始启动变量★★★★★★〓 ★★★★★★编写策略★★★★★★ //+------------------------------------------------------------------------------------------------------------------------+ order = new COrderSystem(def_MagicValue); m_Check = None_Open; count = 0; // 获取MA指标句柄,适应当前周期 MA1Handle = iMA(Symbol(), Period(), MA1, 0, MAType1, PRICE_CLOSE); MA2Handle = iMA(Symbol(), Period(), MA2, 0, MAType2, PRICE_CLOSE); if(MA1Handle == INVALID_HANDLE || MA2Handle == INVALID_HANDLE) { Print("无法创建MA指标: ", GetLastError()); } // 定义缓冲区 ArraySetAsSeries(MA1Buffer, true); ArraySetAsSeries(MA2Buffer, true); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+------------------------------------------------------------------+ //| OnDeinit() 函数称为失败初始化,是初始化失败事件处理程序。★█ ▼▼▼▼▼▼OnDeinit()▼▼▼▼▼▼ //+------------------------------------------------------------------+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- 释放分配的内存,防止内存泄露 delete order; //--- destroy timer EventKillTimer(); } //+------------------------------------------------------------------+------------------------------------------------------------------+ //| OnTick() 函数是图表驱动,每当图表上Tick跳动时运行。★█ ▼▼▼▼▼▼OnTick()▼▼▼▼▼▼ //+------------------------------------------------------------------+------------------------------------------------------------------+ void OnTick() { double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); // 从MA指标中复制数据 if(CopyBuffer(MA1Handle, 0, 0, 3, MA1Buffer) <= 0 || CopyBuffer(MA2Handle, 0, 0, 3, MA2Buffer) <= 0) { Print("CopyBuffer 失败: ", GetLastError()); return; } // 获取当前和前一根K线的 double MA1_1 = MA1Buffer[1]; // 当前K线 double MA1_2 = MA1Buffer[2]; // 前一根K线 double MA2_1 = MA2Buffer[1]; double MA2_2 = MA2Buffer[2]; if(m_Check == None_Open) { if(MA1_1 > MA2_1) { order.MarketOpen(Symbol(), ORDER_TYPE_BUY, LotSize, Ask, Ask - 400*Point(), 0, "202301_Buy"); Print("正常开多!"); m_Check = Long_Open; Print("仓位状态【多】"); Print("【开多仓】后信号【", m_Check, "】"); } } int sl_currBar = iBars(Symbol(),Period()); //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+------------------------------------------------------------------+ // Start - 解决平仓后很长一段时间不开单————如下: /* 【说明】: 为什么一段时间不能开单后,突然又能开单了,原因如下: 因为在【196行】极值判断平仓的代码部分,要先if(m_Check == None_Open)为true,再if(sl_currBar > sl_lastBar)为true,才会运行 get3KlineExtre();获取极值;因为m_Check因【固定止损】平仓后未被重置,仍保持Long_Open,所以极值值判断平仓的代码,每更新1根 K线后仍会运行一次,且因为1根K线只运行1次,那在if(Bid < Lowest)中,Bid的价格每根K线也只获取1次,且在K线刚形成时获取,Bid 价格就会出现以下情况: 1、当Bid获取时,K线的Open价格在上一【低值】上方,那当前K线的if(Bid < Lowest)就会为false,不会进入平仓并重置信号的代码,导致一段时间不开单; 2、当Bid获取时,K线的Open价格在上一【低值】下方,那当前K线的if(Bid < Lowest)就会为true,进入平仓部分的代码: (1)进入平仓部分代码后,因为MarketClose()是一个bool型函数,只要传递的参数格式正确,就会被正常执行,然后返回true 或false,这也是系统的MqlRequest特性决定的。 (2)所以当新K线的Open价格在上一【低值】下方时,因为仓位早就被【固定止损】平掉了,那么 MarketClose()内运行PositionSelect(Symbol()) ? PositionGetInteger(POSITION_TICKET): (long)0时,自然找不到仓位返回了0, 接着该函数仍然会被正确执行发送,然后执行接下来的m_Check = None_Open;重置信号,就又突然可以开单了。 【解决办法】:因为获取极值更新是为了平仓用,其前提是有仓位存在,才有执行这段代码的必要,所以在信号检测后,加一个仓位检测就解决了上述问题。 当信号检测if(m_Check == Long_Open)为true,但接着却检测不到仓位,就说明仓位被【固定止损】平了,为其加个else,执行信号重置。 // End - 解决平仓后很长一段时间不开单————如上 */ //+------------------------------------------------------------------+------------------------------------------------------------------+ if(m_Check == Long_Open) { if(PositionSelect(_Symbol)) //【新增代码】仓位检测。因为这里用过PositionSelect()了,后面MarketClose()里就不需要了,已删除。 { if(sl_currBar > sl_lastBar) { sl_lastBar = sl_currBar; get3KlineExtre(); if(Bid < Lowest) { order.MarketClose(PositionGetInteger(POSITION_TICKET), Slippage); Print("多单因【小于Lowest】关闭!"); //【代码变动】调试用 Print("平仓时 Lowest 的值为:", Lowest); m_Check = None_Open; Print("【平仓】后信号【", m_Check, "】"); //【新增代码】调试用 Lowest = 0; //【新增代码】平仓后初始化极值 } } } else //【新增代码】当信号为Long_Open仓位却不存在时,重置信号 { m_Check = None_Open; } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTimer() { } //OnTimer尾括号 //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline eChecker CheckPositionsStatus(void) { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionGetTicket(i)>0) { if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { return Long_Open; } if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { return Short_Open; } } if(PositionGetTicket(i)==0) { return None_Open; } } return None_Open; } //+------------------------------------------------------------------+ //--- 获取3根K线顶底形态函数:顶-暮星-eveningStar;底-晨星-morningStar int get3KlineExtre() { MqlRates klineArray[]; ArraySetAsSeries(klineArray, true); int mqlrates_date = CopyRates(_Symbol, PERIOD_CURRENT, 0, 4, klineArray); //--- 获取判断K线形态所需的数据 datetime klineOpenTime = klineArray[2].time; //【代码变动】索引改为2,用于绘制低值的指示箭头 double open1 = klineArray[1].open; double open2 = klineArray[2].open; double open3 = klineArray[3].open; double high1 = klineArray[1].high; double high2 = klineArray[2].high; double high3 = klineArray[3].high; double low1 = klineArray[1].low; double low2 = klineArray[2].low; double low3 = klineArray[3].low; double close1 = klineArray[1].close; double close2 = klineArray[2].close; double close3 = klineArray[3].close; long vol1 = klineArray[1].tick_volume; long vol2 = klineArray[2].tick_volume; long vol3 = klineArray[3].tick_volume; // if((low1 > low2) && (low3 > low2)) { if((open1 < close1) && (open2 < close2)) { Lowest = low2; count++; //【新增代码】用于绘制低值的指示文本 string text0 = (string)count; //【新增代码】用于绘制低值的指示文本 string text1 = (string)Lowest; //【新增代码】用于绘制低值的指示文本 createObj(klineOpenTime, Lowest, 217, clrYellow, "第" + text0 + "次更新低值" + text1); //【新增代码】用于绘制低值的指示文本 Print("极小值更新:【", Lowest, "】"); //【新增代码】用于调试 return -1; } //--- 判断顶部形态:暮星-Evening Star //--- 如果倒数第2根 看跌 if((high1 < high2) && (high3 < high2)) { if((open1 > close1) && (open2 > close2)) { Highest = high2; return 1; } } } //--- 返回0代表3根k线未形成顶、底结构 return 0; } //+------------------------------------------------------------------+ //【新增函数】用于调用以在图表上绘制出,检测到的符合条件的低值。这时一个通用函数,可根据需要定制修改 //+------------------------------------------------------------------+ //--- 在图表创建顶、底形态文字标识函数 void createObj(datetime klineOpenTime, double price, int arrawCode, color clr, string txt) { //--- 设置图表标识名称,可根据需要自定义,这里留空便于在下方设置 string objName = " "; //--- 创建字符串信息,并存储在objName中 StringConcatenate(objName, "信号@", klineOpenTime, "在", DoubleToString(price, _Digits), "(", arrawCode, ")"); //--- 在图表上创建一个箭头物件,并判断,如果返回true创建成功 if(ObjectCreate(0, objName, OBJ_ARROW, 0, klineOpenTime, price)) { //--- 箭头创建成功,为其设置箭头样式和颜色 ObjectSetInteger(0, objName, OBJPROP_ARROWCODE, arrawCode); ObjectSetInteger(0, objName, OBJPROP_COLOR, clr); //--- 判断是晨星/暮星,决定箭头放置的位置 if(clr == clrYellow) { //--- 晨星,箭头放置在倒数第2根k线的最低价处 ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_TOP); } if(clr == clrRed) { //--- 暮星,箭头放置在倒数第2根k线的最高价处 ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_BOTTOM); } } //--- 创建一个新的物件名字,用于创建文本物件时指向 string klineName = objName + txt; //--- 在箭头旁再创建一个文本物件,用于解释箭头的含义 if(ObjectCreate(0, klineName, OBJ_TEXT, 0, klineOpenTime, price)) { //--- 文本物件创建成功,为其设置颜色和文本内容 ObjectSetString(0, klineName, OBJPROP_TEXT, " " + txt); ObjectSetInteger(0, klineName, OBJPROP_COLOR, clr); } }