| //+------------------------------------------------------------------+
|
| //| Adaptive MultiMarket EA v1.00 |
|
| //| Prototype based on the first five user-defined feature groups. |
|
| //| AI is optional and OFF by default; configure a compatible local |
|
| //| HTTP service to enable it. |
|
| //+------------------------------------------------------------------+
|
| #property strict
|
| #property version "1.00"
|
| #property description "Multi-symbol configurable EA: technical/momentum confirmation, ATR exits, recovery sizing, profit lock, CSV log."
|
| #include <Trade/Trade.mqh>
|
| CTrade trade;
|
|
|
| //--- General
|
| input ulong InpMagicNumber=2026110201;
|
| input bool InpAllowBuy=true;
|
| input bool InpAllowSell=true;
|
| input int InpMaxSlippagePoints=30;
|
| input bool InpOnePositionPerSymbol=true;
|
| input bool InpUseGlobalRecoveryLock=true;
|
| input int InpLossesBeforeGlobalLock=3; // lock other new entries after 3 consecutive losses, starting with recovery trade #4
|
|
|
| //--- Confirmation switches (any selected filters must pass)
|
| input bool InpUseTechnicalConfirmation=true;
|
| input bool InpUseMomentumConfirmation=true;
|
| input bool InpUseAIConfirmation=false; // OFF until a compatible local/API endpoint is configured
|
| input double InpAIMinProbability=51.0;
|
| input string InpAIEndpoint=""; // HTTP endpoint; response should contain buy_probability and sell_probability numbers
|
| input int InpAIRequestTimeoutMs=2500;
|
| input int InpAIHorizonMinutes=15; // 15, 30 or 60
|
| input int InpSignalCheckSeconds=5; // evaluate a fresh signal every five minutes
|
|
|
| //--- Indicators / technical filters
|
| input ENUM_TIMEFRAMES InpTrendTF1=PERIOD_H1;
|
| input ENUM_TIMEFRAMES InpTrendTF2=PERIOD_M15;
|
| input ENUM_TIMEFRAMES InpFastTF=PERIOD_M5;
|
| input int InpSMAFast=14;
|
| input int InpSMASlow=21;
|
| input int InpADXPeriod=14;
|
| input double InpADXMinimum=18.0;
|
| input int InpRSIPeriod=14;
|
| input double InpRSIBuyMinimum=52.0;
|
| input double InpRSISellMaximum=48.0;
|
| input int InpMACDFast=12;
|
| input int InpMACDSlow=26;
|
| input int InpMACDSignal=9;
|
| input int InpATRPeriod=14;
|
|
|
| //--- Momentum: Points mode or ADR/ratio mode
|
| input bool InpUseAutomaticADRMovement=true;
|
| input int InpADRDays=30;
|
| input double InpADRDivisor=200.0;
|
| input double InpManualMovementPoints=15.0;
|
| input int InpMomentumWindowSeconds=60; // 30 or 60
|
| input double InpMomentumMultiplier=1.0;
|
|
|
| //--- ATR exits / manual exits
|
| input bool InpUseATRStops=true;
|
| input double InpSL_ATR_Multiplier=1.5;
|
| input double InpTP_ATR_Multiplier=2.0;
|
| input double InpManualSLPoints=300.0;
|
| input double InpManualTPPoints=400.0;
|
|
|
| //--- Lot sizing and loss recovery
|
| input bool InpAutoRiskLot=true;
|
| input bool InpRiskUseEquity=false;
|
| input double InpRiskPercent=1.0;
|
| input double InpManualLot=0.01;
|
| input double InpLossLotMultiplier=2.0;
|
| input double InpMinimumLot=0.01;
|
| input double InpMaximumLot=5.0;
|
|
|
| //--- Position management
|
| input bool InpEnableProfitLock=true;
|
| input double InpLockStartPercent=60.0;
|
| input double InpLockStartSLPercent=30.0;
|
| input double InpLockStepPercent=10.0;
|
| input double InpLockStepSLStart=30.0;
|
| input double InpLockAt95Percent=85.0;
|
| input double InpPost95TrailKeepPercent=32.0;
|
| input bool InpExtendTPAfter95=true;
|
| input double InpExtendedTP_ATR_Multiplier=3.0;
|
|
|
| //--- Reporting
|
| input bool InpEnableCSVReport=true;
|
| input bool InpSeparateReportByDate=true;
|
| input string InpReportFolder="AdaptiveEA";
|
|
|
| int hFast1=INVALID_HANDLE,hSlow1=INVALID_HANDLE,hFast2=INVALID_HANDLE,hSlow2=INVALID_HANDLE;
|
| int hADX=INVALID_HANDLE,hRSI=INVALID_HANDLE,hMACD=INVALID_HANDLE,hATR=INVALID_HANDLE;
|
| datetime g_lastSignalCheck=0;
|
| double g_recentBid=0.0;
|
| datetime g_recentTickTime=0;
|
| double g_momentumStartBid=0.0;
|
| datetime g_momentumStartTime=0;
|
| string g_symbol;
|
|
|
| string Key(const string suffix){ return "AM_EA_"+(string)InpMagicNumber+"_"+g_symbol+"_"+suffix; }
|
| string GlobalLockKey(){ return "AM_EA_"+(string)InpMagicNumber+"_GLOBAL_LOCK"; }
|
| string GlobalOwnerKey(){ return "AM_EA_"+(string)InpMagicNumber+"_GLOBAL_OWNER"; }
|
| double SymbolCode(){ double h=0.0; for(int i=0;i<StringLen(g_symbol);i++) h=h*31.0+(double)StringGetCharacter(g_symbol,i); return h; }
|
|
|
| bool ReadBuffer(const int handle,const int buffer,const int shift,double &value)
|
| {
|
| if(handle==INVALID_HANDLE) return false;
|
| double a[]; ArraySetAsSeries(a,true);
|
| if(CopyBuffer(handle,buffer,shift,1,a)!=1) return false;
|
| if(!MathIsValidNumber(a[0])) return false;
|
| value=a[0]; return true;
|
| }
|
|
|
| double NormalizeVolume(double lots)
|
| {
|
| double step=SymbolInfoDouble(g_symbol,SYMBOL_VOLUME_STEP);
|
| double minv=SymbolInfoDouble(g_symbol,SYMBOL_VOLUME_MIN);
|
| double maxv=SymbolInfoDouble(g_symbol,SYMBOL_VOLUME_MAX);
|
| if(step<=0) step=0.01;
|
| minv=MathMax(minv,InpMinimumLot); maxv=MathMin(maxv,InpMaximumLot);
|
| lots=MathMax(minv,MathMin(maxv,lots));
|
| lots=MathFloor(lots/step+1e-8)*step;
|
| int digits=2; if(step<0.01) digits=3; if(step<0.001) digits=4;
|
| return NormalizeDouble(lots,digits);
|
| }
|
|
|
| int ConsecutiveLosses()
|
| {
|
| if(!HistorySelect(0,TimeCurrent())) return 0;
|
| int losses=0; ulong lastSeen=0;
|
| for(int i=HistoryDealsTotal()-1;i>=0;i--)
|
| {
|
| ulong d=HistoryDealGetTicket(i); if(d==0) continue;
|
| if(HistoryDealGetString(d,DEAL_SYMBOL)!=g_symbol) continue;
|
| if((ulong)HistoryDealGetInteger(d,DEAL_MAGIC)!=InpMagicNumber) continue;
|
| ENUM_DEAL_ENTRY e=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(d,DEAL_ENTRY);
|
| if(e!=DEAL_ENTRY_OUT && e!=DEAL_ENTRY_OUT_BY) continue;
|
| if(d==lastSeen) continue; lastSeen=d;
|
| double net=HistoryDealGetDouble(d,DEAL_PROFIT)+HistoryDealGetDouble(d,DEAL_SWAP)+HistoryDealGetDouble(d,DEAL_COMMISSION);
|
| if(net<0.0) losses++; else if(net>0.0) break;
|
| }
|
| return losses;
|
| }
|
|
|
| bool HasOurPosition()
|
| {
|
| for(int i=PositionsTotal()-1;i>=0;i--)
|
| {
|
| ulong ticket=PositionGetTicket(i); if(ticket==0) continue;
|
| if(PositionGetString(POSITION_SYMBOL)==g_symbol && (ulong)PositionGetInteger(POSITION_MAGIC)==InpMagicNumber) return true;
|
| }
|
| return false;
|
| }
|
|
|
| bool GlobalLockActive()
|
| {
|
| if(!InpUseGlobalRecoveryLock) return false;
|
| if(!GlobalVariableCheck(GlobalLockKey())) return false;
|
| return GlobalVariableGet(GlobalLockKey())>0.5;
|
| }
|
|
|
| void SetGlobalLock(const bool on){ if(InpUseGlobalRecoveryLock){ GlobalVariableSet(GlobalLockKey(),on?1.0:0.0); if(on) GlobalVariableSet(GlobalOwnerKey(),SymbolCode()); } }
|
|
|
| bool GetATR(double &atr){ return ReadBuffer(hATR,0,1,atr) && atr>0.0; }
|
|
|
| double AverageDailyRange()
|
| {
|
| int n=MathMax(1,InpADRDays); MqlRates rates[]; ArraySetAsSeries(rates,true);
|
| int got=CopyRates(g_symbol,PERIOD_D1,1,n,rates);
|
| if(got<=0) return 0.0;
|
| double sum=0.0; int count=0;
|
| for(int i=0;i<got;i++){ double r=rates[i].high-rates[i].low; if(r>0){sum+=r;count++;} }
|
| if(count==0) return 0.0;
|
| return sum/(double)count;
|
| }
|
|
|
| double RequiredMovementPrice()
|
| {
|
| if(InpUseAutomaticADRMovement)
|
| {
|
| double adr=AverageDailyRange();
|
| if(adr>0.0 && InpADRDivisor>0.0) return (adr/InpADRDivisor)*InpMomentumMultiplier;
|
| }
|
| return InpManualMovementPoints*_Point*InpMomentumMultiplier;
|
| }
|
|
|
| bool TechnicalSignal(int &direction,double &atr,double &rsi,double &adx,double &macdMain,double &macdSignal)
|
| {
|
| direction=0;
|
| double f1,s1,f2,s2;
|
| if(!ReadBuffer(hFast1,0,1,f1)||!ReadBuffer(hSlow1,0,1,s1)||!ReadBuffer(hFast2,0,1,f2)||!ReadBuffer(hSlow2,0,1,s2)) return false;
|
| if(!ReadBuffer(hADX,0,1,adx)||!ReadBuffer(hRSI,0,1,rsi)||!ReadBuffer(hMACD,0,1,macdMain)||!ReadBuffer(hMACD,1,1,macdSignal)||!GetATR(atr)) return false;
|
| double close1=iClose(g_symbol,InpTrendTF2,1); if(close1<=0) return false;
|
| bool buy=(f1>s1 && f2>s2 && close1>f2 && adx>=InpADXMinimum && rsi>=InpRSIBuyMinimum && macdMain>macdSignal);
|
| bool sell=(f1<s1 && f2<s2 && close1<f2 && adx>=InpADXMinimum && rsi<=InpRSISellMaximum && macdMain<macdSignal);
|
| if(buy) direction=1; else if(sell) direction=-1;
|
| return true;
|
| }
|
|
|
| bool MomentumPass(const int direction,double &moved)
|
| {
|
| moved=0.0; if(!InpUseMomentumConfirmation) return true;
|
| MqlTick tick; if(!SymbolInfoTick(g_symbol,tick)) return false;
|
| datetime now=TimeCurrent();
|
| if(g_momentumStartTime==0 || (now-g_momentumStartTime)>MathMax(1,InpMomentumWindowSeconds))
|
| { g_momentumStartTime=now; g_momentumStartBid=tick.bid; return false; }
|
| moved=MathAbs(tick.bid-g_momentumStartBid);
|
| double need=RequiredMovementPrice(); if(need<=0) return false;
|
| if(direction>0) return (tick.bid-g_momentumStartBid)>=need;
|
| if(direction<0) return (g_momentumStartBid-tick.bid)>=need;
|
| return false;
|
| }
|
|
|
| // Optional HTTP JSON service. Add endpoint to MT5: Tools > Options > Expert Advisors > Allow WebRequest.
|
| bool AIConfirm(const int direction,double &probability)
|
| {
|
| probability=0.0;
|
| if(!InpUseAIConfirmation) return true;
|
| if(StringLen(InpAIEndpoint)==0) return false;
|
| string payload="{\"symbol\":\""+g_symbol+"\",\"horizon_minutes\":"+(string)InpAIHorizonMinutes+",\"direction\":\""+(direction>0?"BUY":"SELL")+"\"}";
|
| char data[],result[]; StringToCharArray(payload,data,0,WHOLE_ARRAY,CP_UTF8);
|
| string headers="Content-Type: application/json\r\n"; string response_headers;
|
| ResetLastError(); int code=WebRequest("POST",InpAIEndpoint,headers,InpAIRequestTimeoutMs,data,result,response_headers);
|
| if(code<200 || code>=300) return false;
|
| string body=CharArrayToString(result,0,WHOLE_ARRAY,CP_UTF8);
|
| string key=(direction>0?"buy_probability":"sell_probability"); int pos=StringFind(body,key);
|
| if(pos<0) return false; pos=StringFind(body,":",pos); if(pos<0) return false;
|
| string tail=StringSubstr(body,pos+1); StringReplace(tail," ",""); StringReplace(tail,"\"","");
|
| int end=StringFind(tail,","); if(end<0) end=StringFind(tail,"}"); if(end<0) return false;
|
| probability=StringToDouble(StringSubstr(tail,0,end));
|
| return probability>=InpAIMinProbability;
|
| }
|
|
|
| bool CalculateStops(const int direction,const double atr,double &sl,double &tp)
|
| {
|
| MqlTick t; if(!SymbolInfoTick(g_symbol,t)) return false;
|
| double slDist,tpDist;
|
| if(InpUseATRStops){ slDist=atr*InpSL_ATR_Multiplier; tpDist=atr*InpTP_ATR_Multiplier; }
|
| else { slDist=InpManualSLPoints*_Point; tpDist=InpManualTPPoints*_Point; }
|
| if(slDist<=0||tpDist<=0) return false;
|
| int digits=(int)SymbolInfoInteger(g_symbol,SYMBOL_DIGITS);
|
| if(direction>0){ sl=NormalizeDouble(t.ask-slDist,digits); tp=NormalizeDouble(t.ask+tpDist,digits); }
|
| else { sl=NormalizeDouble(t.bid+slDist,digits); tp=NormalizeDouble(t.bid-tpDist,digits); }
|
| long stops=SymbolInfoInteger(g_symbol,SYMBOL_TRADE_STOPS_LEVEL); double minDist=stops*_Point;
|
| if(direction>0 && (t.ask-sl<minDist || tp-t.ask<minDist)) return false;
|
| if(direction<0 && (sl-t.bid<minDist || t.bid-tp<minDist)) return false;
|
| return true;
|
| }
|
|
|
| double CalculateLots(const double slDistance)
|
| {
|
| int losses=ConsecutiveLosses();
|
| double base=InpManualLot;
|
| if(InpAutoRiskLot)
|
| {
|
| double capital=InpRiskUseEquity?AccountInfoDouble(ACCOUNT_EQUITY):AccountInfoDouble(ACCOUNT_BALANCE);
|
| double riskMoney=capital*InpRiskPercent/100.0;
|
| double tickVal=SymbolInfoDouble(g_symbol,SYMBOL_TRADE_TICK_VALUE);
|
| double tickSize=SymbolInfoDouble(g_symbol,SYMBOL_TRADE_TICK_SIZE);
|
| if(tickVal>0 && tickSize>0 && slDistance>0) base=riskMoney/(slDistance/tickSize*tickVal);
|
| }
|
| double lots=base*MathPow(MathMax(1.0,InpLossLotMultiplier),losses);
|
| return NormalizeVolume(lots);
|
| }
|
|
|
| void WriteReport(const string event,const int direction,const double probability,const double rsi,const double adx,const double macd,const double atr,const double moved,const double lots,const double price,const double sl,const double tp,const double pnl=0.0)
|
| {
|
| if(!InpEnableCSVReport) return;
|
| string day=TimeToString(TimeCurrent(),TIME_DATE); StringReplace(day,".","");
|
| string filename=InpReportFolder+"/"+g_symbol+"_"+day+".csv";
|
| int flags=FILE_READ|FILE_WRITE|FILE_CSV|FILE_ANSI|FILE_SHARE_READ|FILE_SHARE_WRITE;
|
| int f=FileOpen(filename,flags,';'); if(f==INVALID_HANDLE) return;
|
| if(FileSize(f)==0) FileWrite(f,"Time","Event","Symbol","Direction","AI_Probability","RSI","ADX","MACD_Main","ATR","Momentum_PriceMove","Lots","Price","SL","TP","NetPnL");
|
| FileSeek(f,0,SEEK_END);
|
| FileWrite(f,TimeToString(TimeCurrent(),TIME_DATE|TIME_SECONDS),event,g_symbol,(direction>0?"BUY":(direction<0?"SELL":"NA")),DoubleToString(probability,2),DoubleToString(rsi,2),DoubleToString(adx,2),DoubleToString(macd,6),DoubleToString(atr,_Digits),DoubleToString(moved,_Digits),DoubleToString(lots,2),DoubleToString(price,_Digits),DoubleToString(sl,_Digits),DoubleToString(tp,_Digits),DoubleToString(pnl,2));
|
| FileClose(f);
|
| }
|
|
|
| void ManagePosition()
|
| {
|
| if(!InpEnableProfitLock) return;
|
| for(int i=PositionsTotal()-1;i>=0;i--)
|
| {
|
| ulong ticket=PositionGetTicket(i); if(ticket==0) continue;
|
| if(PositionGetString(POSITION_SYMBOL)!=g_symbol || (ulong)PositionGetInteger(POSITION_MAGIC)!=InpMagicNumber) continue;
|
| long type=PositionGetInteger(POSITION_TYPE); bool buy=(type==POSITION_TYPE_BUY);
|
| double entry=PositionGetDouble(POSITION_PRICE_OPEN),sl=PositionGetDouble(POSITION_SL),tp=PositionGetDouble(POSITION_TP);
|
| double current=buy?SymbolInfoDouble(g_symbol,SYMBOL_BID):SymbolInfoDouble(g_symbol,SYMBOL_ASK);
|
| double initialTP=GlobalVariableCheck(Key("INITIAL_TP"))?GlobalVariableGet(Key("INITIAL_TP")):tp;
|
| double initialSL=GlobalVariableCheck(Key("INITIAL_SL"))?GlobalVariableGet(Key("INITIAL_SL")):sl;
|
| double full=MathAbs(initialTP-entry); if(full<=0) continue;
|
| double progress=(buy?(current-entry):(entry-current))/full*100.0;
|
| double desiredSL=sl;
|
| if(progress>=InpLockAt95Percent)
|
| {
|
| double locked=full*(InpLockAt95Percent/100.0);
|
| double extra=MathMax(0.0,(buy?(current-entry):(entry-current))-locked);
|
| double lockDist=locked+extra*InpPost95TrailKeepPercent/100.0;
|
| desiredSL=buy?entry+lockDist:entry-lockDist;
|
| if(InpExtendTPAfter95)
|
| {
|
| double atr; if(GetATR(atr))
|
| {
|
| double ext=atr*InpExtendedTP_ATR_Multiplier;
|
| double newTP=buy?current+ext:current-ext;
|
| if(buy && newTP>tp) tp=newTP; if(!buy && newTP<tp) tp=newTP;
|
| }
|
| }
|
| }
|
| else if(progress>=InpLockStartPercent)
|
| {
|
| double step=InpLockStepPercent; if(step<=0) step=10;
|
| double stage=MathFloor((progress-InpLockStartPercent)/step);
|
| double lockPct=InpLockStepSLStart+stage*step;
|
| if(progress>=InpLockAt95Percent) lockPct=InpLockAt95Percent;
|
| lockPct=MathMin(lockPct,InpLockAt95Percent);
|
| if(lockPct<InpLockStartSLPercent) lockPct=InpLockStartSLPercent;
|
| desiredSL=buy?entry+full*lockPct/100.0:entry-full*lockPct/100.0;
|
| }
|
| int digits=(int)SymbolInfoInteger(g_symbol,SYMBOL_DIGITS); desiredSL=NormalizeDouble(desiredSL,digits);
|
| bool improve=(buy?(desiredSL>sl && desiredSL<current):(sl==0.0 || (desiredSL<sl && desiredSL>current)));
|
| if(improve || (tp!=PositionGetDouble(POSITION_TP))) trade.PositionModify(ticket,improve?desiredSL:sl,tp);
|
| }
|
| }
|
|
|
| void TryOpenTrade()
|
| {
|
| if(InpOnePositionPerSymbol && HasOurPosition()) return;
|
| if(GlobalLockActive()) return;
|
| int dir=0; double atr=0,rsi=0,adx=0,macd=0,macds=0;
|
| if(InpUseTechnicalConfirmation)
|
| {
|
| if(!TechnicalSignal(dir,atr,rsi,adx,macd,macds) || dir==0) return;
|
| }
|
| else
|
| {
|
| if(!GetATR(atr)) atr=SymbolInfoDouble(g_symbol,SYMBOL_POINT)*100.0;
|
| // Without technical filter, direction is inferred from short-term SMA on M5.
|
| double a=iClose(g_symbol,InpFastTF,1),b=iClose(g_symbol,InpFastTF,2); if(a==0||b==0||a==b) return; dir=(a>b?1:-1);
|
| }
|
| if((dir>0&&!InpAllowBuy)||(dir<0&&!InpAllowSell)) return;
|
| double prob=0.0;
|
| if(InpUseAIConfirmation && !AIConfirm(dir,prob)) return;
|
| double moved=0.0;
|
| if(InpUseMomentumConfirmation && !MomentumPass(dir,moved)) return;
|
| double sl,tp; if(!CalculateStops(dir,atr,sl,tp)) return;
|
| double price=(dir>0?SymbolInfoDouble(g_symbol,SYMBOL_ASK):SymbolInfoDouble(g_symbol,SYMBOL_BID));
|
| double lots=CalculateLots(MathAbs(price-sl)); if(lots<=0) return;
|
| trade.SetExpertMagicNumber(InpMagicNumber); trade.SetDeviationInPoints(InpMaxSlippagePoints);
|
| bool ok=(dir>0?trade.Buy(lots,g_symbol,0.0,sl,tp,"AdaptiveEA BUY"):trade.Sell(lots,g_symbol,0.0,sl,tp,"AdaptiveEA SELL"));
|
| if(ok)
|
| {
|
| GlobalVariableSet(Key("INITIAL_TP"),tp); GlobalVariableSet(Key("INITIAL_SL"),sl);
|
| if(ConsecutiveLosses()>=InpLossesBeforeGlobalLock) SetGlobalLock(true);
|
| WriteReport("OPEN",dir,prob,rsi,adx,macd,atr,moved,lots,price,sl,tp,0.0);
|
| }
|
| }
|
|
|
| int OnInit()
|
| {
|
| g_symbol=_Symbol;
|
| hFast1=iMA(g_symbol,InpTrendTF1,InpSMAFast,0,MODE_SMA,PRICE_CLOSE);
|
| hSlow1=iMA(g_symbol,InpTrendTF1,InpSMASlow,0,MODE_SMA,PRICE_CLOSE);
|
| hFast2=iMA(g_symbol,InpTrendTF2,InpSMAFast,0,MODE_SMA,PRICE_CLOSE);
|
| hSlow2=iMA(g_symbol,InpTrendTF2,InpSMASlow,0,MODE_SMA,PRICE_CLOSE);
|
| hADX=iADX(g_symbol,InpFastTF,InpADXPeriod); hRSI=iRSI(g_symbol,InpFastTF,InpRSIPeriod,PRICE_CLOSE);
|
| hMACD=iMACD(g_symbol,InpFastTF,InpMACDFast,InpMACDSlow,InpMACDSignal,PRICE_CLOSE);
|
| hATR=iATR(g_symbol,InpFastTF,InpATRPeriod);
|
| trade.SetExpertMagicNumber(InpMagicNumber); trade.SetDeviationInPoints(InpMaxSlippagePoints);
|
| if(InpEnableCSVReport) FolderCreate(InpReportFolder);
|
| EventSetTimer(1);
|
| return INIT_SUCCEEDED;
|
| }
|
|
|
| void OnDeinit(const int reason)
|
| {
|
| EventKillTimer();
|
| if(hFast1!=INVALID_HANDLE) IndicatorRelease(hFast1); if(hSlow1!=INVALID_HANDLE) IndicatorRelease(hSlow1);
|
| if(hFast2!=INVALID_HANDLE) IndicatorRelease(hFast2); if(hSlow2!=INVALID_HANDLE) IndicatorRelease(hSlow2);
|
| if(hADX!=INVALID_HANDLE) IndicatorRelease(hADX); if(hRSI!=INVALID_HANDLE) IndicatorRelease(hRSI);
|
| if(hMACD!=INVALID_HANDLE) IndicatorRelease(hMACD); if(hATR!=INVALID_HANDLE) IndicatorRelease(hATR);
|
| }
|
|
|
| void OnTick()
|
| {
|
| ManagePosition();
|
|
|
| int checkInterval=MathMax(1,InpSignalCheckSeconds);
|
|
|
| if(InpUseMomentumConfirmation)
|
| {
|
| int maxInterval=MathMax(1,InpMomentumWindowSeconds/6);
|
| checkInterval=MathMin(checkInterval,maxInterval);
|
| }
|
|
|
| datetime now=TimeCurrent();
|
|
|
| if(g_lastSignalCheck==0 ||
|
| (now-g_lastSignalCheck)>=checkInterval)
|
| {
|
| g_lastSignalCheck=now;
|
| TryOpenTrade();
|
| }
|
| }
|
|
|
| void OnTimer()
|
| {
|
| ManagePosition();
|
| // Global lock is released by the recovery-owner chart after its profitable recovery close.
|
| }
|
|
|
| void OnTradeTransaction(const MqlTradeTransaction &trans,const MqlTradeRequest &request,const MqlTradeResult &result)
|
| {
|
| if(trans.type!=TRADE_TRANSACTION_DEAL_ADD || trans.deal==0) return;
|
| if(!HistoryDealSelect(trans.deal)) return;
|
| if(HistoryDealGetString(trans.deal,DEAL_SYMBOL)!=g_symbol) return;
|
| if((ulong)HistoryDealGetInteger(trans.deal,DEAL_MAGIC)!=InpMagicNumber) return;
|
| ENUM_DEAL_ENTRY e=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(trans.deal,DEAL_ENTRY);
|
| if(e!=DEAL_ENTRY_OUT && e!=DEAL_ENTRY_OUT_BY) return;
|
| double pnl=HistoryDealGetDouble(trans.deal,DEAL_PROFIT)+HistoryDealGetDouble(trans.deal,DEAL_SWAP)+HistoryDealGetDouble(trans.deal,DEAL_COMMISSION);
|
| if(pnl>0.0 && ConsecutiveLosses()==0 && GlobalLockActive() && GlobalVariableCheck(GlobalOwnerKey()) && GlobalVariableGet(GlobalOwnerKey())==SymbolCode()) SetGlobalLock(false);
|
| WriteReport("CLOSE",0,0,0,0,0,0,0,HistoryDealGetDouble(trans.deal,DEAL_VOLUME),HistoryDealGetDouble(trans.deal,DEAL_PRICE),0,0,pnl);
|
| }
|
| //+------------------------------------------------------------------+
|