//+------------------------------------------------------------------+ //| TopoBeta.mq5 | //| Copyright 2021, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include #include #include #include #include #include #include "..\\utils\\Utils.mqh" #include "..\\utils\\ChartUtils.mqh" #include "..\\models\\PositionInfo.mqh" #include "..\\models\\HistoryPositionInfo.mqh" #include "..\\models\\RiskInfo.mqh" CTrade trade; Utils utils; ChartUtils chartUtils; //utils #define TOPO_MAGIC 47829674 enum IntervalsToAmplitude {all = 0, id5 = 5, id10 = 10, id30 = 30, id50 = 50, id70 = 70, id100 = 100, id120 = 120, id150 = 150, id240 = 240, id500 = 500, id1440 = 1440}; // select Predefined intervals with good results enum ModeEnv {dev = 0, test = 1, prod = 2}; //input input group "--- Generals ---" input ModeEnv modeEnv = 0; // env mode input IntervalsToAmplitude temporalToAmplitude = 0; // temporals to analize the amplitude input group "--- Risk ---" input double MaxRiskByTradeInPercent = 1; // Max Risk by trade in percentage input double MaxDrawdownInPercent = 10; // Max Risk by day in percentage input int numberConsecutiveLossToStop = 40; // number of the consecutive loss to stop input int timeInMinuteToStopConsecutiveLoss = 5; // time in minutes to stop after consecutive loss input bool useDisableRisk = false; // disable drawdown and consecutive loss risk input group "--- lotage, positions, takeprofit and stoploss ---" input bool useMoveStoploss = true; // use movement of stoploss input bool usePartialClose = false; // use partial close input bool useMinLotInTradeIfNotEnough = true; // use min lot in a trade if not enough to operate input bool useFixedLotage = false; // use fixed lotage(0.01) input bool useFixedStopLoss = true; // use fixed stopLoss 0.5% input bool useFixedTakeProfit = true; // use fixed takeProfit = amplitude input double numberPeriodsToClosePosition = 20; // number of periods to close positions input group "--- confirmations ---" input double spreadUmbral = 10; input bool useMacDConfirm = true; // use macd to confirm input int SIGNAL=9; // macd signal for each period input bool useRegressionConfirm = false; // use regression confirm input double macdUmbral = 70; // macd umbral, (lastHistBar - penultimeHistBar) > macdUmbral input group "--- TODO confirmations, please do not change them ---" input int numberToDivPeriod = 0; input int consecutiveCandleToConfirmInPercentUmbral = 4; input group "--- to try period and amplitude umbrals in test mode ---" input int periodSelected = 5; // period in bars to analize the amplitude (only in test mode) input double amplitudeValues = 2; // umbral values to analize the amplitude (only in test mode)) //consts const int positionsInfoLength = 1500; const int tradeDeviation = 10; //amplitude data int periodInBars[]; double amplitudeUmbrals[]; //trade info PositionInfo positionsInfo[]; HistoryPositionInfo historyPositionsInfo[]; int periodInBarsLength; //init time datetime initTime; //risk info RiskInfo riskInfo; //indicators handles int MACDhadlingBuffer[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- initialize common information trade.SetExpertMagicNumber(TOPO_MAGIC); // magic trade.SetMarginMode(); trade.SetTypeFillingBySymbol(Symbol()); chartUtils.setChartTemplate(); //initTime initTime = TimeCurrent(); //init periodInBars and amplitudeUmbrals setTemporalsAmplitudes(); periodInBarsLength = ArraySize(periodInBars); //init risk info double moneyAllowToLoss = NormalizeDouble(AccountInfoDouble(ACCOUNT_BALANCE)*(MaxDrawdownInPercent/100),_Digits); riskInfo = initRiskInfo(MaxRiskByTradeInPercent, MaxDrawdownInPercent, initTime,moneyAllowToLoss,numberConsecutiveLossToStop,timeInMinuteToStopConsecutiveLoss); //initial positionInfo with default values ArrayResize(positionsInfo,positionsInfoLength); ArrayResize(historyPositionsInfo,positionsInfoLength); ArrayResize(MACDhadlingBuffer,positionsInfoLength); for(int i = 0; i < positionsInfoLength; i++) { positionsInfo[i] = createEmptyPosition(initTime); historyPositionsInfo[i] = createEmptyHistoryPosition(initTime); } //restrictions if(!useDisableRisk && useFixedLotage) { Print("we need calculate better the riskInMoney when the lotage is a value fixed"); return -10; } TesterHideIndicators(true); for(int i=0; i minLot) { double newVToClose = v / 4; newVToClose = NormalizeDouble(NormalizeDouble(newVToClose/lotStep,_Digits)*lotStep, 2); if(newVToClose < minLot) newVToClose = minLot; if(newVToClose > maxLot ) newVToClose = maxLot; trade.PositionClosePartial(ticket,newVToClose,tradeDeviation); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int openPositions() { ResetLastError(); int cantPositionsOpen = 0; datetime timeCurrent = TimeCurrent(); for(int i=0; i 0) { break; } } if(operation < 0 && linePoint < closeJ && dist > um) { chartUtils.drawLine(string((string)timeCurrent + (string)bars + (string)j + "trend"), rates[bars + 1].time, closeInPassed, rates[1].time, rates[1].close); chartUtils.drawLine(string((string)timeCurrent + (string)bars + (string)j + "linePoint"), rates[bars + 1].time, linePoint, rates[closePos].time, linePoint); passedUmInSell += 1; if(turnBackInSell) { break; } } } if(operation > 0 && passedUmInBuy > 0 && turnBackInBuy > 0) { operation = 0; } if(operation < 0 && passedUmInSell > 0 && turnBackInSell > 0) { operation = 0; } } // end passed oportunity confirm // spread confirm double ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); double bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); double spread=ask-bid; if(spread > spreadUmbral) { operation = 0; } // end spread confirm //macd confirm double MACDlineBuffer[]; double signallineBuffer[]; CopyBuffer(MACDhadlingBuffer[i],0,1,2,MACDlineBuffer); CopyBuffer(MACDhadlingBuffer[i],1,1,2,signallineBuffer); ArraySetAsSeries(MACDlineBuffer,true); ArraySetAsSeries(signallineBuffer,true); double lasthist = MathAbs(MACDlineBuffer[0]-signallineBuffer[0]); double penultimehist = MathAbs(MACDlineBuffer[1]-signallineBuffer[1]); double diff = utils.absDiffInPercent(lasthist,penultimehist); if(useMacDConfirm && penultimehist < lasthist && diff > macdUmbral) operation = 0; //end macd confirm // TODO this confirm code there are in testing, please use numberToDivPeriod in 0 for now if(bars >= numberToDivPeriod && consecutiveCandleToConfirmInPercentUmbral !=0 && numberToDivPeriod != 0) { int consecutiveCandleToConfirm = 0; int length = bars / numberToDivPeriod; int consecutiveUmbral = consecutiveCandleToConfirmInPercentUmbral * length / 100; for(int k = 0; k < length; k++) { int closePos = k*numberToDivPeriod + 1; int openPos = k*numberToDivPeriod + numberToDivPeriod; double close = rates[closePos].close; double open = rates[openPos].open; if(close > open) { consecutiveCandleToConfirm += 1; } else { consecutiveCandleToConfirm-=1; } } if(MathAbs(consecutiveCandleToConfirm) >= consecutiveUmbral) operation = 0; } //end confirm segment if (operation != 0) { double takeProfit = amplitude / 2; double stopLoss = amplitude / 4; double lotage = 0.01; double riskInMoney = 0; double position = 0; double takeProfitLevel = 0; double stopLossLevel = 0; if(useFixedTakeProfit) { takeProfit = amplitude; } if(useFixedStopLoss) { stopLoss = 0.5; } bool isLong = utils.isLong(operation); if(isLong) { position=ask; takeProfitLevel = NormalizeDouble(position * ((100 + takeProfit)/100), _Digits) + spread; stopLossLevel = NormalizeDouble(position * ((100 - stopLoss)/100), _Digits) - spread; } else { position=bid; takeProfitLevel = NormalizeDouble(position * ((100 - takeProfit)/100), _Digits) - spread; stopLossLevel = NormalizeDouble(position * ((100 + stopLoss)/100), _Digits) + spread; } if(!useFixedLotage) { lotage = utils.betterLotage(MaxRiskByTradeInPercent, stopLossLevel,isLong, useMinLotInTradeIfNotEnough,true); // TODO we need calculate better the riskInMoney when the lotage is a value fixed riskInMoney = NormalizeDouble(AccountInfoDouble(ACCOUNT_MARGIN_FREE)*(MaxRiskByTradeInPercent/100),_Digits); } PositionInfo tmp = createPositionInfo(-1,position,amplitude,lotage,riskInMoney,bars,timeCurrent,operation,takeProfit,takeProfitLevel,stopLoss,stopLossLevel); positionsInfo[bars] = tmp; cantPositionsOpen +=1; chartUtils.drawLine(string(timeCurrent + bars), rates[bars + 1].time, closeInPassed, rates[1].time, rates[1].close); } } return cantPositionsOpen; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void MakeTrade(int bars) { ResetLastError(); //if position don't was open bool exitPosition = exitPositionByPeriodInBars(positionsInfo, bars); if(!exitPosition) return; // if position was trade bool madeTrade = madeTradeInPosition(positionsInfo, bars); if(madeTrade) return; PositionInfo currentPositionInfo = positionsInfo[bars]; bool isLong = utils.isLong(currentPositionInfo.signal); double position = currentPositionInfo.position; double takeProfit = currentPositionInfo.takeProfit; double takeProfitLevel = currentPositionInfo.takeProfitLevel; double stopLoss = currentPositionInfo.stopLoss; double lotage = currentPositionInfo.lotage; double stopLossLevel = currentPositionInfo.stopLossLevel; string comment = "sl: " + (string)NormalizeDouble(stopLoss, 2) + " tp: " + (string)NormalizeDouble(takeProfit, 2); bool orderCompleted = false; if(isLong) { orderCompleted = trade.Buy(lotage,_Symbol,position,stopLossLevel,takeProfitLevel,comment); } else { orderCompleted = trade.Sell(lotage,_Symbol,position,stopLossLevel,takeProfitLevel,comment); } ulong code = trade.ResultRetcode(); string order = isLong ? "buy" : "sell"; long ticket = (long)trade.ResultOrder(); string msj = ""; if( orderCompleted && code == TRADE_RETCODE_DONE) { msj = StringFormat("(%s): ticket:(%d) ,position:(%f) ,stopLossLevel:(%f) ,takeProfitLevel:(%f), comment: (%s)", order, ticket, position, stopLossLevel, takeProfitLevel, comment); } else { msj = StringFormat("ERROR code(%d): in (%s) with: ticket:(%d) ,position:(%f) ,stopLossLevel:(%f) ,takeProfitLevel:(%f), comment: (%s)", code, order, ticket, position, stopLossLevel, takeProfitLevel, comment); } Print(msj); Alert(msj); currentPositionInfo.ticket = ticket; currentPositionInfo.openPositionTime = TimeCurrent(); datetime curr = TimeCurrent(); positionsInfo[bars] = currentPositionInfo; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void checkIfClosePositionByExpiration() { for( int i=0; i < periodInBarsLength; i++) { int bars = periodInBars[i]; bool exitPosition = exitPositionByPeriodInBars(positionsInfo, bars); if(!exitPosition) { continue; } PositionInfo currentPositionInfo = positionsInfo[bars]; ulong timePassedInSec = TimeCurrent() - currentPositionInfo.openPositionTime; ulong timeToClose = (int)numberPeriodsToClosePosition * bars * 60; if(timePassedInSec >= timeToClose) { trade.PositionClose(currentPositionInfo.ticket,tradeDeviation); positionsInfo[bars] = createEmptyPosition(initTime); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void setTemporalsAmplitudes() { // if mode test if(modeEnv == 1) { double periodInBarsTmp [1]= {5}; periodInBarsTmp[0] = periodSelected; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[1] = {0.5}; amplitudeUmbralsTmp[0] = amplitudeValues; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); return; } switch (temporalToAmplitude) { case 0: { double periodInBarsTmp [12]= {2, 4, 6, 10, 13, 15, 17, 19, 25, 27, 30, 40}; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[12] = {2, 2, 2.5, 3, 4, 4.5, 4.5, 4.5, 5, 5.5, 5.5, 6.5}; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); break; } case 5: { double periodInBarsTmp [1]= {5}; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[1] = {2}; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); break; } case 10: { double periodInBarsTmp [1]= {10}; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[1] = {2.5}; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); break; } case 30: { double periodInBarsTmp [1]= {30}; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[1] = {3}; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); break; } case 50: { double periodInBarsTmp [1]= {50}; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[1] = {2.5}; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); break; } case 70: { double periodInBarsTmp [1]= {70}; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[1] = {3}; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); break; } case 100: { double periodInBarsTmp [1]= {100}; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[1] = {7.5}; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); break; } case 120: { double periodInBarsTmp [1]= {120}; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[1] = {4.5}; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); break; } case 150: { double periodInBarsTmp [1]= {150}; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[1] = {5.5}; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); break; } case 240: { double periodInBarsTmp [1]= {240}; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[1] = {8.5}; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); break; } case 500: { double periodInBarsTmp [1]= {500}; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[1] = {11}; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); break; } case 1440: { double periodInBarsTmp [1]= {1440}; ArrayCopy(periodInBars, periodInBarsTmp, 0, 0, WHOLE_ARRAY); double amplitudeUmbralsTmp[1] = {17}; ArrayCopy(amplitudeUmbrals, amplitudeUmbralsTmp, 0, 0, WHOLE_ARRAY); break; } } } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+