设为首页 收藏本站 切换语言

RSI&MA策略,黄金策略,顺势而为,根据MA和RSI开发出的黄金策略,适合中长线投资者

| 发表于 前天 11:22 | 显示全部楼层 |复制链接
  1. /*策略思路:
  2. 当价格在MA之上,RSI穿越下轨时做多
  3. 当价格在MA之下,RSI穿越上轨时做空
  4. */
  5. //- - - - - - - - - - 加载库&声明库对象 - - - - - - - - - -
  6. #include <Trade\Trade.mqh>
  7. #include <Trade\PositionInfo.mqh>
  8. #include <Trade\OrderInfo.mqh>
  9. CTrade trade;
  10. CPositionInfo positionInfo;
  11. COrderInfo orderInfo;
  12. //- - - - - - - - - - 加载库&声明库对象 - - - - - - - - - -
  13. //- - - - - - - - - - EA相关设置 - - - - - - - - - -
  14. input group "设置参数"
  15. sinput long MagicNum = 8888;                      // EA 编号
  16. sinput int SlType = 1;                         // 止损方式(1:固定金额[100]美金 2:账户[1]% 3:固定[0.1]手, 4:真仓总账户[1]%)
  17. sinput double SlParam = 30;                     // 止损依据
  18. input group "参数优化"
  19. input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1;           // 时间级别
  20. input int            RSIPeriod = 14;            // RSI周期
  21. input int            RSILevel = 70;             // RSI上沿值
  22. input int            StopLoss = 50;             // 止损点数 (0=没有止损)
  23. input int            TakeProfit = 50;           // 止盈点数 (0=没有止盈)
  24. input bool           CloseSignal = true;          // 出现相反信号时是否平仓
  25. input group "均线过滤"
  26. input bool           UseMAFilter = true;          // 是否使用均线过滤?
  27. input int            MAPeriod = 20;             // 均线周期
  28. input ENUM_TIMEFRAMES MATimeFrame = PERIOD_H1;         // 均线时间级别
  29. //- - - - - - - - - - EA相关设置 - - - - - - - - - -
  30. //- - - - - - - - - - 全局变量 - - - - - - - - - -
  31. int barsTotal;
  32. double point;
  33. double initBalance;
  34. int RsiHandle;
  35. double RsiBuffer[];
  36. int MAHandle;
  37. double MABuffer[];
  38. MqlTick currentTick;
  39. //- - - - - - - - - - 全局变量 - - - - - - - - - -
  40. //+------------------------------------------------------------------+
  41. //|                                                                  |
  42. //+------------------------------------------------------------------+
  43. int OnInit() {
  44. //检查输入参数
  45.    if(MagicNum<0) {
  46.       Alert("MagicNum<0");
  47.       return INIT_PARAMETERS_INCORRECT;
  48.    }
  49.    if(RSIPeriod<=1) {
  50.       Alert("RSIPeriod <=1");
  51.       return INIT_PARAMETERS_INCORRECT;
  52.    }
  53.    if(RSILevel>=100 || RSILevel<=50) {
  54.       Alert("RSILevel>=100 || RSILevel<=50");
  55.       return INIT_PARAMETERS_INCORRECT;
  56.    }
  57.    if(UseMAFilter && MAPeriod<=1) {
  58.       Alert("UseMAFilter is True & MAPeriod <=1");
  59.       return INIT_PARAMETERS_INCORRECT;
  60.    }
  61.    if(StopLoss<0) {
  62.       Alert("Stop loss<0");
  63.       return INIT_PARAMETERS_INCORRECT;
  64.    }
  65.    if(TakeProfit < 0) {
  66.       Alert("Take profit <0");
  67.       return INIT_PARAMETERS_INCORRECT;
  68.    }
  69.    if(UseMAFilter && MATimeFrame<TimeFrame) {
  70.       Alert("UseMAFiilter is True & MATimeFrame <TimeFrame");
  71.       return INIT_PARAMETERS_INCORRECT;
  72.    }
  73. //句柄买例化
  74.    RsiHandle = iRSI(_Symbol,TimeFrame,RSIPeriod,PRICE_CLOSE);
  75.    if(RsiHandle == INVALID_HANDLE) {
  76.       Alert((string)MagicNum,"",_Symbol,"RsiHandle创建失败!");
  77.       return INIT_FAILED;
  78.    }
  79.    ArraySetAsSeries (RsiBuffer, true);
  80.    if(UseMAFilter) {
  81.       MAHandle = iMA(_Symbol,MATimeFrame,MAPeriod,0,MODE_SMA,PRICE_CLOSE);
  82.       if(MAHandle == INVALID_HANDLE) {
  83.          Alert((string)MagicNum," ",_Symbol,"MAHandle创建失败!");
  84.          return INIT_FAILED;
  85.       }
  86.       ArraySetAsSeries (MABuffer, true);
  87.    }
  88.    Print(MagicNum," ",_Symbol,"实例化成功");
  89.    if(!MQLInfoInteger(MQL_TESTER)) ChartSetSymbolPeriod(0,_Symbol,TimeFrame);
  90.    initBalance = SlParam;
  91.    Comment("MagicNum:",MagicNum," SlType:",SlType,"SlParam:", SlParam);
  92.    return(INIT_SUCCEEDED);
  93. }
  94. //+------------------------------------------------------------------+
  95. //|                                                                  |
  96. //+------------------------------------------------------------------+
  97. void OnTick() {
  98.    if(!isNewBar(_Symbol, TimeFrame, barsTotal)) return;
  99.    point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
  100.    if(!SymbolInfoTick (_Symbol,currentTick)) {
  101.       Print("tick数据获取异常!");
  102.       return;
  103.    }
  104.    CopyBuffer(RsiHandle,0,0,3,RsiBuffer);
  105.    if(UseMAFilter) CopyBuffer(MAHandle,0,0,2,MABuffer);
  106. // 统计开仓数量
  107.    int cntBuy,cntSell;
  108.    if(!countOpenPositions(MagicNum, cntBuy,cntSell)) return ;
  109. // 多单开仓条件
  110.    bool bullSignal = cntBuy==0 && RsiBuffer[2]>=(100-RSILevel) && RsiBuffer[1]<(100-RSILevel);
  111.    if(UseMAFilter) bullSignal = bullSignal && currentTick.ask>MABuffer[1];
  112.    if(bullSignal) {
  113.       if(CloseSignal&&cntSell>0) closePositions(MagicNum,POSITION_TYPE_SELL);
  114.       double sl   = StopLoss==0 ? 0 : currentTick.ask - StopLoss * point;
  115.       double tp   = TakeProfit==0 ? 0 : currentTick.ask + TakeProfit * point;
  116.       if(!NormalizePrice(_Symbol, sl)) return;
  117.       if(!NormalizePrice(_Symbol, tp)) return;
  118.       double slp = SlParam;
  119.       if(SlType==4) slp = calculateEachEaBalace(MagicNum, initBalance);//子账户百分之1
  120.       double lotSize = calculateLots(_Symbol,currentTick.ask,sl,SlType,slp);
  121.       trade.SetExpertMagicNumber(MagicNum);
  122.       trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,lotSize,currentTick.ask,sl,tp,__FILE__);
  123.    }
  124. // 空单开仓条件
  125.    bool bearSignal = cntSell==0 && RsiBuffer[2]<=RSILevel && RsiBuffer[1]>RSILevel;
  126.    if(UseMAFilter) bearSignal = bearSignal && currentTick.bid<MABuffer[1];
  127.    if(bearSignal) {
  128.       if(CloseSignal&&cntBuy>0) closePositions(MagicNum,POSITION_TYPE_BUY);
  129.       double sl   = StopLoss==0 ? 0 : currentTick.bid + StopLoss * point;
  130.       double tp   = TakeProfit==0 ? 0 : currentTick.bid - TakeProfit * point;
  131.       if(!NormalizePrice(_Symbol, sl)) return;
  132.       if(!NormalizePrice(_Symbol, tp)) return;
  133.       double slp = SlParam;
  134.       if(SlType==4) slp = calculateEachEaBalace(MagicNum, initBalance);//子账户百分之1
  135.       double lotSize = calculateLots(_Symbol,currentTick.bid,sl,SlType,slp);
  136.       trade.SetExpertMagicNumber(MagicNum);
  137.       trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,lotSize,currentTick.bid,sl,tp,__FILE__);
  138.    }
  139. }
  140. //+------------------------------------------------------------------+
  141. //|                                                                  |
  142. //+------------------------------------------------------------------+
  143. void OnDeinit(const int reason) {
  144.    if(RsiHandle!=INVALID_HANDLE) IndicatorRelease(RsiHandle);
  145.    if(MAHandle!=INVALID_HANDLE) IndicatorRelease(MAHandle);
  146. }
  147. //+------------------------------------------------------------------+
  148. //|                                                                  |
  149. //+------------------------------------------------------------------+
  150. bool isNewBar(string symbol, ENUM_TIMEFRAMES timeframe, int& bTotal) {
  151.    int bars = iBars(symbol,timeframe);
  152.    if(bTotal == bars) return false;
  153.    bTotal = bars;
  154.    return true;
  155. }
  156. //-----------------------------------规范化价格-----------------------------------
  157. // 交易品种symbol和一个引用类型的价格price
  158. bool NormalizePrice(string symbol, double &price) {
  159.    // 获取交易品种symbol的最小价格波动大小tickSize
  160.    double tickSize=0;
  161.    if (!SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE,tickSize)) {
  162.       Print("SYMBOL_TRADE_TICK_SIZE 获取异常!");
  163.       return false;
  164.    }
  165.    // 获取交易品种symbol的小数点位数digits
  166.    int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
  167.    // 将价格price除以tickSize取整,然后再乘以tickSize得到规范化后的价格,并使用NormalizeDouble函数将其舍入到指定的小数点位数digits。
  168.    price = NormalizeDouble(MathRound(price/tickSize)*tickSize,digits);
  169.    return true;
  170. }
  171. //-----------------------------------平仓-----------------------------------
  172. bool closePositions(long magicNum, ENUM_POSITION_TYPE positionType) {
  173. // 获取当前已开仓的仓位数量
  174.    int total = PositionsTotal();
  175. // 循环遍历所有开仓仓位。从最后一个仓位开始循环,直到第一个仓位(i=0)。
  176.    for(int i=total-1; i>=0; i--) {
  177.       // 获取第i个仓位的订单号,并将其赋值给positionTicket。每个订单都有唯一的订单号,该函数的参数i表示第i个订单。
  178.       ulong positionTicket = PositionGetTicket(i);
  179.       if(positionTicket<=0) {
  180.          Print("获取positionTicket失败");
  181.          return false;
  182.       }
  183.       // 根据订单编号选择指定持仓
  184.       if(!PositionSelectByTicket(positionTicket)) {
  185.          Print("选中持仓失败");
  186.          return false;
  187.       }
  188.       // 获取持仓的魔术号
  189.       long magic;
  190.       if(!PositionGetInteger(POSITION_MAGIC,magic)) {
  191.          Print ("获取魔术号失败");
  192.          return false;
  193.       }
  194.       // 判断持仓的魔术号是否与传入的魔术号相同
  195.       if(magic==magicNum) {
  196.          // 获取持仓的类型(买入/卖出)
  197.          long type;
  198.          if(!PositionGetInteger (POSITION_TYPE,type) ) {
  199.             Print("持仓的类型获取失败");
  200.             return false;
  201.          }
  202.          // 如果要关闭的仓位类型是多单,但是当前选择的仓位类型是空单,则跳过本次循环,继续下一个仓位的处理。
  203.          if(positionType==POSITION_TYPE_BUY && type==POSITION_TYPE_SELL) continue;
  204.          // 如果要关闭的仓位类型是空单,但是当前选择的仓位类型是多单,则跳过本次循环,继续下一个仓位的处理。
  205.          if(positionType==POSITION_TYPE_SELL && type==POSITION_TYPE_BUY) continue;
  206.          // 平仓 并且检查结果是否成功。如果不成功,则提示用户
  207.          trade.PositionClose(positionTicket);
  208.          if(trade.ResultRetcode()!=TRADE_RETCODE_DONE) {
  209.             Print("平仓失败 positionTicket:",(string)positionTicket," 异常:", (string)trade.ResultRetcode(), " :", trade.ResultRetcodeDescription());
  210.             return false;
  211.          }
  212.       }
  213.    }
  214.    return true;
  215. }
  216. //-----------------------------------计算仓位-----------------------------------
  217. // symbol(品种名称), entryPrice(入场价格), slPrice(止损价格), slType(止损类型), slParam(止损参数)
  218. double calculateLots(string symbol, double entryPrice, double slPrice, int slType, double slParam) {
  219.    double slMoney = 0;
  220.    if(slType == 1) slMoney = slParam; //1 固定止损金额, eg:100U
  221.    else if(slType == 2) slMoney = AccountInfoDouble(ACCOUNT_BALANCE) * slParam / 100; //2 账户余额百分比, eg:1%
  222.    else if(slType == 3) return slParam; //3 固定手数, eg:0.1
  223.    else if(slType == 4) slMoney = slParam; //4 子账户百分比, eg:1%
  224.    //计算止损距离
  225.    int digits = (int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);//小数位数
  226.    double slDistance = NormalizeDouble(MathAbs(entryPrice - slPrice), digits) / point;
  227.    if(slDistance <= 0) return 0; //zero divide
  228. //获取点值 每次尝试间隔1秒。如果尝试三次后仍然无法获取到交易点值,则打印异常信息,并返回0。
  229.    double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
  230.    if(tickValue == 0) {
  231.       Sleep(1000);
  232.       tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
  233.       Print("SYMBOL_TRADE_TICK_VALUE异常第一次尝试:"+(string)tickValue);
  234.       if(tickValue == 0) {
  235.          Sleep(1000);
  236.          tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
  237.          Print("SYMBOL_TRADE_TICK_VALUE异常第二次尝试:"+(string)tickValue);
  238.       }
  239.       if(tickValue == 0) {
  240.          Sleep(1000);
  241.          tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
  242.          Print("SYMBOL_TRADE_TICK_VALUE异常第三次尝试:"+(string)tickValue);
  243.       }
  244.       if(tickValue == 0) {
  245.          Print("获取SYMBOL_TRADE_TICK_VALUE异常:",symbol,"-",entryPrice,"-",slPrice,"-",slType,"-",slParam);
  246.          Print("获取SYMBOL_TRADE_TICK_VALUE异常2:",point,"-",slDistance,"-",tickValue);
  247.          return 0;
  248.       }
  249.    }
  250. // 计算手数
  251.    double lot = NormalizeDouble(slMoney / slDistance / tickValue, 2);//风控 / 止损 / 点值
  252. // 规范手数 确保手数符合品种的步长要求
  253.    double lotstep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
  254.    lot = MathRound(lot / lotstep) * lotstep;
  255.    if(lot < SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN)) lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
  256.    else if(lot >= SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX)) {
  257.       Print("手数异常:",symbol,"-",entryPrice,"-",slPrice,"-",slType,"-",slParam);
  258.       Print("手数异常2:",point,"-",slDistance,"-",tickValue,"-",lot,"-",lotstep);
  259.       return 0;
  260.    }
  261.    return lot;
  262. }
  263. //-----------------------------------计算子账户余额-----------------------------------
  264. // magicNum(用于筛选交易单的魔术数字)和initBa(初始余额)
  265. double calculateEachEaBalace(long magicNum, double initBa) {
  266.    double countProfit = 0, countCommission = 0,countSwap = 0, countMoney = 0;
  267.    // 选择历史交易记录
  268.    HistorySelect(0,TimeCurrent());
  269.    // 获取历史交易总数
  270.    uint deals=HistoryDealsTotal();
  271.    // 遍历历史交易记录
  272.    for(uint i=0;i<deals;i++) {
  273.       // 获取特定索引处交易的订单号
  274.       ulong ticket = HistoryDealGetTicket(i);
  275.       if(ticket<=0) continue;
  276.       long magic = HistoryDealGetInteger(ticket,DEAL_MAGIC);
  277.       string symbol = HistoryDealGetString(ticket,DEAL_SYMBOL);
  278.       double profit = HistoryDealGetDouble(ticket,DEAL_PROFIT);
  279.       double commission = HistoryDealGetDouble(ticket,DEAL_COMMISSION);
  280.       double swap = HistoryDealGetDouble(ticket,DEAL_SWAP);
  281.       // 累加全部的利润 手续费 库存费 得到该ea 的 绩效
  282.       if(magic==magicNum) {
  283.          countProfit+=profit;
  284.          countCommission+=commission;
  285.          countSwap+=swap;
  286.          countMoney = countProfit + countCommission + countSwap;
  287.       }
  288.    }
  289.    // 目前账户的余额是多少
  290.    double currentBalance = countMoney + initBa;
  291.    return currentBalance / 100;
  292. }
  293. //-----------------------------------统计该ea的多单和空单开仓数量-----------------------------------
  294. // magicNum是要统计的特定魔术号
  295. // countBuy和countSell是输出参数,分别用于存储多单和空单的开仓数量。在函数执行完毕后,它们会被更新。
  296. // &countBuy和&countSell是引用参数。它们的作用是将函数内部的计算结果传递回函数外部。
  297. // 值传递
  298. bool countOpenPositions(long magicNum, int &countBuy,int &countSell) {
  299.    // 初始化count为0,用于统计多/空单开仓数量。
  300.    countBuy =0;
  301.    countSell=0;
  302.    // 获取当前已开仓的仓位数量
  303.    int total =PositionsTotal();
  304.    // 循环遍历所有开仓仓位。从最后一个仓位开始循环,直到第一个仓位(i=0)。
  305.    for(int i=total-1; i>=0; i--) {
  306.       // 获取第i个仓位的订单号,并将其赋值给positionTicket。每个订单都有唯一的订单号,该函数的参数i表示第i个订单。
  307.       ulong positionTicket = PositionGetTicket(i);
  308.       if(positionTicket<=0) {
  309.          Print("获取positionTicket失败");
  310.          return false;
  311.       }
  312.       // 根据订单编号选择指定持仓
  313.       if(!PositionSelectByTicket(positionTicket)) {
  314.          Print("选中持仓失败");
  315.          return false;
  316.       }
  317.       // 获取持仓的魔术号
  318.       long magic;
  319.       if(!PositionGetInteger(POSITION_MAGIC,magic)) {
  320.          Print ("获取魔术号失败");
  321.          return false;
  322.       }
  323.       // 判断持仓的魔术号是否与传入的魔术号相同
  324.       if(magic==magicNum) {
  325.          // 获取持仓的类型(买入/卖出)
  326.          long type;
  327.          if(!PositionGetInteger (POSITION_TYPE,type)) {
  328.             Print("持仓的类型获取失败");
  329.             return false;
  330.          }
  331.          // 买入持仓
  332.          if(type==POSITION_TYPE_BUY) countBuy++;
  333.          // 卖出持仓
  334.          if(type==POSITION_TYPE_SELL) countSell++;
  335.       }
  336.    }
  337.    return true;
  338. }
  339. //+------------------------------------------------------------------+
复制代码
image.png
image.png
最近访问 头像模式
举报

评论 使用道具

精彩评论2

jjj321
D
 楼主 | 发表于 前天 11:23 | 显示全部楼层
有用的话,给博主打赏一下
举报

点赞 评论 使用道具

JH0924
D
| 发表于 4 小时前 | 显示全部楼层
bucuo 支持一下
举报

点赞 评论 使用道具

发新帖
EA交易
您需要登录后才可以评论 登录 | 立即注册

简体中文
繁體中文
English(英语)
日本語(日语)
Deutsch(德语)
Русский язык(俄语)
بالعربية(阿拉伯语)
Türkçe(土耳其语)
Português(葡萄牙语)
ภาษาไทย(泰国语)
한어(朝鲜语/韩语)
Français(法语)