PRIC_Work/问题解决/3k+RSI_01060102 - 已解决.mq5
super.admin 7f89bfbc25 convert
2025-05-30 16:17:12 +02:00

372 lines
28 KiB
MQL5

//+------------------------------------------------------------------+
//| 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 <PRIC_Class\PRIC_C_OrderSystem_V1.0.mqh>
//▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉
//+------------------------------------------------------------------+------------------------------------------------------------------+
//| 此区域↓↓↓→定义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);
}
}