- /*策略思路:
- 当价格在MA之上,RSI穿越下轨时做多
- 当价格在MA之下,RSI穿越上轨时做空
- */
-
- //- - - - - - - - - - 加载库&声明库对象 - - - - - - - - - -
- #include <Trade\Trade.mqh>
- #include <Trade\PositionInfo.mqh>
- #include <Trade\OrderInfo.mqh>
-
- CTrade trade;
- CPositionInfo positionInfo;
- COrderInfo orderInfo;
- //- - - - - - - - - - 加载库&声明库对象 - - - - - - - - - -
-
- //- - - - - - - - - - EA相关设置 - - - - - - - - - -
- input group "设置参数"
- sinput long MagicNum = 8888; // EA 编号
- sinput int SlType = 1; // 止损方式(1:固定金额[100]美金 2:账户[1]% 3:固定[0.1]手, 4:真仓总账户[1]%)
- sinput double SlParam = 30; // 止损依据
-
- input group "参数优化"
- input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // 时间级别
- input int RSIPeriod = 14; // RSI周期
- input int RSILevel = 70; // RSI上沿值
- input int StopLoss = 50; // 止损点数 (0=没有止损)
- input int TakeProfit = 50; // 止盈点数 (0=没有止盈)
- input bool CloseSignal = true; // 出现相反信号时是否平仓
-
- input group "均线过滤"
- input bool UseMAFilter = true; // 是否使用均线过滤?
- input int MAPeriod = 20; // 均线周期
- input ENUM_TIMEFRAMES MATimeFrame = PERIOD_H1; // 均线时间级别
- //- - - - - - - - - - EA相关设置 - - - - - - - - - -
-
- //- - - - - - - - - - 全局变量 - - - - - - - - - -
- int barsTotal;
- double point;
- double initBalance;
-
- int RsiHandle;
- double RsiBuffer[];
- int MAHandle;
- double MABuffer[];
- MqlTick currentTick;
-
- //- - - - - - - - - - 全局变量 - - - - - - - - - -
-
- //+------------------------------------------------------------------+
- //| |
- //+------------------------------------------------------------------+
- int OnInit() {
- //检查输入参数
- if(MagicNum<0) {
- Alert("MagicNum<0");
- return INIT_PARAMETERS_INCORRECT;
- }
- if(RSIPeriod<=1) {
- Alert("RSIPeriod <=1");
- return INIT_PARAMETERS_INCORRECT;
- }
- if(RSILevel>=100 || RSILevel<=50) {
- Alert("RSILevel>=100 || RSILevel<=50");
- return INIT_PARAMETERS_INCORRECT;
- }
- if(UseMAFilter && MAPeriod<=1) {
- Alert("UseMAFilter is True & MAPeriod <=1");
- return INIT_PARAMETERS_INCORRECT;
- }
- if(StopLoss<0) {
- Alert("Stop loss<0");
- return INIT_PARAMETERS_INCORRECT;
- }
- if(TakeProfit < 0) {
- Alert("Take profit <0");
- return INIT_PARAMETERS_INCORRECT;
- }
- if(UseMAFilter && MATimeFrame<TimeFrame) {
- Alert("UseMAFiilter is True & MATimeFrame <TimeFrame");
- return INIT_PARAMETERS_INCORRECT;
- }
-
- //句柄买例化
- RsiHandle = iRSI(_Symbol,TimeFrame,RSIPeriod,PRICE_CLOSE);
- if(RsiHandle == INVALID_HANDLE) {
- Alert((string)MagicNum,"",_Symbol,"RsiHandle创建失败!");
- return INIT_FAILED;
- }
- ArraySetAsSeries (RsiBuffer, true);
-
- if(UseMAFilter) {
- MAHandle = iMA(_Symbol,MATimeFrame,MAPeriod,0,MODE_SMA,PRICE_CLOSE);
- if(MAHandle == INVALID_HANDLE) {
- Alert((string)MagicNum," ",_Symbol,"MAHandle创建失败!");
- return INIT_FAILED;
- }
- ArraySetAsSeries (MABuffer, true);
- }
-
- Print(MagicNum," ",_Symbol,"实例化成功");
-
- if(!MQLInfoInteger(MQL_TESTER)) ChartSetSymbolPeriod(0,_Symbol,TimeFrame);
-
- initBalance = SlParam;
-
- Comment("MagicNum:",MagicNum," SlType:",SlType,"SlParam:", SlParam);
-
- return(INIT_SUCCEEDED);
- }
-
- //+------------------------------------------------------------------+
- //| |
- //+------------------------------------------------------------------+
- void OnTick() {
- if(!isNewBar(_Symbol, TimeFrame, barsTotal)) return;
- point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
-
- if(!SymbolInfoTick (_Symbol,currentTick)) {
- Print("tick数据获取异常!");
- return;
- }
-
- CopyBuffer(RsiHandle,0,0,3,RsiBuffer);
- if(UseMAFilter) CopyBuffer(MAHandle,0,0,2,MABuffer);
-
- // 统计开仓数量
- int cntBuy,cntSell;
- if(!countOpenPositions(MagicNum, cntBuy,cntSell)) return ;
-
-
- // 多单开仓条件
- bool bullSignal = cntBuy==0 && RsiBuffer[2]>=(100-RSILevel) && RsiBuffer[1]<(100-RSILevel);
- if(UseMAFilter) bullSignal = bullSignal && currentTick.ask>MABuffer[1];
-
- if(bullSignal) {
- if(CloseSignal&&cntSell>0) closePositions(MagicNum,POSITION_TYPE_SELL);
-
- double sl = StopLoss==0 ? 0 : currentTick.ask - StopLoss * point;
- double tp = TakeProfit==0 ? 0 : currentTick.ask + TakeProfit * point;
- if(!NormalizePrice(_Symbol, sl)) return;
- if(!NormalizePrice(_Symbol, tp)) return;
-
- double slp = SlParam;
- if(SlType==4) slp = calculateEachEaBalace(MagicNum, initBalance);//子账户百分之1
- double lotSize = calculateLots(_Symbol,currentTick.ask,sl,SlType,slp);
-
- trade.SetExpertMagicNumber(MagicNum);
- trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,lotSize,currentTick.ask,sl,tp,__FILE__);
- }
-
- // 空单开仓条件
- bool bearSignal = cntSell==0 && RsiBuffer[2]<=RSILevel && RsiBuffer[1]>RSILevel;
- if(UseMAFilter) bearSignal = bearSignal && currentTick.bid<MABuffer[1];
- if(bearSignal) {
- if(CloseSignal&&cntBuy>0) closePositions(MagicNum,POSITION_TYPE_BUY);
-
- double sl = StopLoss==0 ? 0 : currentTick.bid + StopLoss * point;
- double tp = TakeProfit==0 ? 0 : currentTick.bid - TakeProfit * point;
- if(!NormalizePrice(_Symbol, sl)) return;
- if(!NormalizePrice(_Symbol, tp)) return;
-
- double slp = SlParam;
- if(SlType==4) slp = calculateEachEaBalace(MagicNum, initBalance);//子账户百分之1
- double lotSize = calculateLots(_Symbol,currentTick.bid,sl,SlType,slp);
-
- trade.SetExpertMagicNumber(MagicNum);
- trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,lotSize,currentTick.bid,sl,tp,__FILE__);
- }
- }
-
- //+------------------------------------------------------------------+
- //| |
- //+------------------------------------------------------------------+
- void OnDeinit(const int reason) {
- if(RsiHandle!=INVALID_HANDLE) IndicatorRelease(RsiHandle);
- if(MAHandle!=INVALID_HANDLE) IndicatorRelease(MAHandle);
- }
-
- //+------------------------------------------------------------------+
- //| |
- //+------------------------------------------------------------------+
- bool isNewBar(string symbol, ENUM_TIMEFRAMES timeframe, int& bTotal) {
- int bars = iBars(symbol,timeframe);
- if(bTotal == bars) return false;
- bTotal = bars;
- return true;
- }
- //-----------------------------------规范化价格-----------------------------------
- // 交易品种symbol和一个引用类型的价格price
- bool NormalizePrice(string symbol, double &price) {
- // 获取交易品种symbol的最小价格波动大小tickSize
- double tickSize=0;
- if (!SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE,tickSize)) {
- Print("SYMBOL_TRADE_TICK_SIZE 获取异常!");
- return false;
- }
- // 获取交易品种symbol的小数点位数digits
- int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
- // 将价格price除以tickSize取整,然后再乘以tickSize得到规范化后的价格,并使用NormalizeDouble函数将其舍入到指定的小数点位数digits。
- price = NormalizeDouble(MathRound(price/tickSize)*tickSize,digits);
- return true;
- }
- //-----------------------------------平仓-----------------------------------
- bool closePositions(long magicNum, ENUM_POSITION_TYPE positionType) {
- // 获取当前已开仓的仓位数量
- int total = PositionsTotal();
- // 循环遍历所有开仓仓位。从最后一个仓位开始循环,直到第一个仓位(i=0)。
- for(int i=total-1; i>=0; i--) {
- // 获取第i个仓位的订单号,并将其赋值给positionTicket。每个订单都有唯一的订单号,该函数的参数i表示第i个订单。
- ulong positionTicket = PositionGetTicket(i);
- if(positionTicket<=0) {
- Print("获取positionTicket失败");
- return false;
- }
- // 根据订单编号选择指定持仓
- if(!PositionSelectByTicket(positionTicket)) {
- Print("选中持仓失败");
- return false;
- }
- // 获取持仓的魔术号
- long magic;
- if(!PositionGetInteger(POSITION_MAGIC,magic)) {
- Print ("获取魔术号失败");
- return false;
- }
- // 判断持仓的魔术号是否与传入的魔术号相同
- if(magic==magicNum) {
- // 获取持仓的类型(买入/卖出)
- long type;
- if(!PositionGetInteger (POSITION_TYPE,type) ) {
- Print("持仓的类型获取失败");
- return false;
- }
- // 如果要关闭的仓位类型是多单,但是当前选择的仓位类型是空单,则跳过本次循环,继续下一个仓位的处理。
- if(positionType==POSITION_TYPE_BUY && type==POSITION_TYPE_SELL) continue;
- // 如果要关闭的仓位类型是空单,但是当前选择的仓位类型是多单,则跳过本次循环,继续下一个仓位的处理。
- if(positionType==POSITION_TYPE_SELL && type==POSITION_TYPE_BUY) continue;
-
- // 平仓 并且检查结果是否成功。如果不成功,则提示用户
- trade.PositionClose(positionTicket);
- if(trade.ResultRetcode()!=TRADE_RETCODE_DONE) {
- Print("平仓失败 positionTicket:",(string)positionTicket," 异常:", (string)trade.ResultRetcode(), " :", trade.ResultRetcodeDescription());
- return false;
- }
- }
- }
- return true;
- }
-
- //-----------------------------------计算仓位-----------------------------------
- // symbol(品种名称), entryPrice(入场价格), slPrice(止损价格), slType(止损类型), slParam(止损参数)
- double calculateLots(string symbol, double entryPrice, double slPrice, int slType, double slParam) {
-
- double slMoney = 0;
- if(slType == 1) slMoney = slParam; //1 固定止损金额, eg:100U
- else if(slType == 2) slMoney = AccountInfoDouble(ACCOUNT_BALANCE) * slParam / 100; //2 账户余额百分比, eg:1%
- else if(slType == 3) return slParam; //3 固定手数, eg:0.1
- else if(slType == 4) slMoney = slParam; //4 子账户百分比, eg:1%
- //计算止损距离
- int digits = (int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);//小数位数
- double slDistance = NormalizeDouble(MathAbs(entryPrice - slPrice), digits) / point;
- if(slDistance <= 0) return 0; //zero divide
-
- //获取点值 每次尝试间隔1秒。如果尝试三次后仍然无法获取到交易点值,则打印异常信息,并返回0。
- double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
- if(tickValue == 0) {
- Sleep(1000);
- tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
- Print("SYMBOL_TRADE_TICK_VALUE异常第一次尝试:"+(string)tickValue);
- if(tickValue == 0) {
- Sleep(1000);
- tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
- Print("SYMBOL_TRADE_TICK_VALUE异常第二次尝试:"+(string)tickValue);
- }
- if(tickValue == 0) {
- Sleep(1000);
- tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
- Print("SYMBOL_TRADE_TICK_VALUE异常第三次尝试:"+(string)tickValue);
- }
- if(tickValue == 0) {
- Print("获取SYMBOL_TRADE_TICK_VALUE异常:",symbol,"-",entryPrice,"-",slPrice,"-",slType,"-",slParam);
- Print("获取SYMBOL_TRADE_TICK_VALUE异常2:",point,"-",slDistance,"-",tickValue);
- return 0;
- }
- }
-
- // 计算手数
- double lot = NormalizeDouble(slMoney / slDistance / tickValue, 2);//风控 / 止损 / 点值
-
- // 规范手数 确保手数符合品种的步长要求
- double lotstep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
- lot = MathRound(lot / lotstep) * lotstep;
-
- if(lot < SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN)) lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
- else if(lot >= SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX)) {
- Print("手数异常:",symbol,"-",entryPrice,"-",slPrice,"-",slType,"-",slParam);
- Print("手数异常2:",point,"-",slDistance,"-",tickValue,"-",lot,"-",lotstep);
- return 0;
- }
-
- return lot;
- }
-
- //-----------------------------------计算子账户余额-----------------------------------
- // magicNum(用于筛选交易单的魔术数字)和initBa(初始余额)
- double calculateEachEaBalace(long magicNum, double initBa) {
- double countProfit = 0, countCommission = 0,countSwap = 0, countMoney = 0;
- // 选择历史交易记录
- HistorySelect(0,TimeCurrent());
- // 获取历史交易总数
- uint deals=HistoryDealsTotal();
- // 遍历历史交易记录
- for(uint i=0;i<deals;i++) {
- // 获取特定索引处交易的订单号
- ulong ticket = HistoryDealGetTicket(i);
- if(ticket<=0) continue;
-
- long magic = HistoryDealGetInteger(ticket,DEAL_MAGIC);
- string symbol = HistoryDealGetString(ticket,DEAL_SYMBOL);
- double profit = HistoryDealGetDouble(ticket,DEAL_PROFIT);
- double commission = HistoryDealGetDouble(ticket,DEAL_COMMISSION);
- double swap = HistoryDealGetDouble(ticket,DEAL_SWAP);
-
- // 累加全部的利润 手续费 库存费 得到该ea 的 绩效
- if(magic==magicNum) {
- countProfit+=profit;
- countCommission+=commission;
- countSwap+=swap;
-
- countMoney = countProfit + countCommission + countSwap;
- }
- }
-
- // 目前账户的余额是多少
- double currentBalance = countMoney + initBa;
- return currentBalance / 100;
- }
-
- //-----------------------------------统计该ea的多单和空单开仓数量-----------------------------------
- // magicNum是要统计的特定魔术号
- // countBuy和countSell是输出参数,分别用于存储多单和空单的开仓数量。在函数执行完毕后,它们会被更新。
- // &countBuy和&countSell是引用参数。它们的作用是将函数内部的计算结果传递回函数外部。
- // 值传递
- bool countOpenPositions(long magicNum, int &countBuy,int &countSell) {
- // 初始化count为0,用于统计多/空单开仓数量。
- countBuy =0;
- countSell=0;
- // 获取当前已开仓的仓位数量
- int total =PositionsTotal();
- // 循环遍历所有开仓仓位。从最后一个仓位开始循环,直到第一个仓位(i=0)。
- for(int i=total-1; i>=0; i--) {
- // 获取第i个仓位的订单号,并将其赋值给positionTicket。每个订单都有唯一的订单号,该函数的参数i表示第i个订单。
- ulong positionTicket = PositionGetTicket(i);
- if(positionTicket<=0) {
- Print("获取positionTicket失败");
- return false;
- }
- // 根据订单编号选择指定持仓
- if(!PositionSelectByTicket(positionTicket)) {
- Print("选中持仓失败");
- return false;
- }
- // 获取持仓的魔术号
- long magic;
- if(!PositionGetInteger(POSITION_MAGIC,magic)) {
- Print ("获取魔术号失败");
- return false;
- }
- // 判断持仓的魔术号是否与传入的魔术号相同
- if(magic==magicNum) {
- // 获取持仓的类型(买入/卖出)
- long type;
- if(!PositionGetInteger (POSITION_TYPE,type)) {
- Print("持仓的类型获取失败");
- return false;
- }
- // 买入持仓
- if(type==POSITION_TYPE_BUY) countBuy++;
- // 卖出持仓
- if(type==POSITION_TYPE_SELL) countSell++;
- }
- }
- return true;
- }
- //+------------------------------------------------------------------+
-
复制代码
|