
Description
enum ENUM_NRISK { NR_LOW = 0, NR_MEDIUM = 1, NR_HIGH = 2 };
enum ENUM_NDIR { ND_BOTH = 0, ND_BUY_ONLY = 1, ND_SELL_ONLY = 2 };
enum ENUM_NTREND { NT_FOLLOW = 0, NT_REVERSE = 1 };
enum ENUM_NRED { NRED_MONEY = 0, NRED_PERCENT = 1 };
input group "=== RISK / LOT ==="
input ENUM_NRISK InpRisk = NR_MEDIUM; // risk level: LOW 0.005 / MEDIUM 0.01 / HIGH 0.02 lot per 1000 of balance
input double InpLotSizeMultiplier = 1.0; // multiply the auto lot
input double InpLotSize = 0.0; // FIXED first lot (0 = auto by risk level)
input double InpMaxAutoLot = 1.0; // max first lot in auto mode
input double InpCustomBalance = 0.0; // calculate lot as if balance = this (0 = real balance)
input int InpMaxSpread = 0; // max spread in points to open (0 = off)
input group "=== RECOVERY CHAIN ==="
input int InpRecovery = 8; // max trades in one chain (1 = no recovery)
input int InpFixDistance = 0; // fixed distance between trades in points (0 = auto ATR)
input double InpAtrDistFactor = 1.0; // auto distance = ATR(H1,14) x this
input int InpMinDistance = 0; // min distance points (0 = off)
input int InpMaxDistance = 0; // max distance points (0 = off)
input double InpDistMultiplier = 1.2; // each next distance x this (1.0 = equal steps)
input double InpRecoveryLotMult = 1.0; // each next lot x this (1.0 = same lot, >1 = martingale)
input string InpSpacingList = ""; // optional custom distances in points, e.g. "300,400,600,900"
input group "=== CHAIN PROFIT EXIT ==="
input double InpMinProfit = 0.0; // chain target in money (0 = auto)
input double InpAutoProfitMult = 1.0; // auto target = profit of first lot moving half a distance x this
input double InpBreakEvenAfter = 0; // after N trades in chain, target = break-even + this money (0 = off)
input int InpReduceDelayMin = 240; // profit reduction: start after N minutes (0 = off)
input int InpReduceStepMin = 60; // reduce every N minutes
input ENUM_NRED InpReduceType = NRED_PERCENT; // reduce by money or % of the target
input double InpReduceValue = 15; // reduce amount per step
input double InpMinNetProfit = 0.0; // never reduce below this money (0 = break-even)
input double InpTrailStart = 0.0; // basket trailing: start when chain profit >= this money (0 = off)
input double InpTrailDist = 0.0; // basket trailing: close if profit falls this money from its peak
input group "=== PROTECTION ==="
input double InpMaxChainDDPct = 20; // close a chain when its loss reaches x% of balance (0 = off)
input int InpPauseAfterChainDD = 120; // minutes without new chains after that (0 = none)
input int InpChainStopPoints = 0; // close chain if price is x points against the FIRST entry (0 = off)
input double InpDailyLossPct = 10; // daily loss % -> close all, stop today (0 = off)
input double InpWeeklyLossPct = 20; // weekly loss % -> close all, stop this week (0 = off)
input double InpHardStopPct = 35; // equity x% below peak balance -> close all (0 = off)
input bool InpPauseOnHardStop = true; // after hard stop: stop until EA restart
input double InpEquityStop = 0; // equity below this money -> close all + stop (0 = off)
input bool InpOneChainAtATime = true; // only one chain (buy OR sell) at a time
input int InpMaxChainsPerDay = 0; // max new chains per day (0 = off)
input double InpDailyTargetMoney = 0; // daily profit target money -> no new chains today (0 = off)
input double InpDailyTargetPct = 0; // daily profit target % -> no new chains today (0 = off)
input group "=== SIGNAL ==="
input ENUM_TIMEFRAMES InpSignalTF = PERIOD_M15; // signal timeframe
input bool InpUseAIScore = true; // AI score: weighted model of trend, RSI, MACD, slope, candle
input double InpAIThreshold = 0.35; // AI score needed (0..1), higher = fewer, stronger signals
input ENUM_NTREND InpTrendMode = NT_FOLLOW; // FOLLOW the signal or REVERSE it (fade)
input bool InpUseSR = false; // bounce from support / resistance (last 50 bars)
input bool InpUseCandle = false; // engulfing candle patterns
input bool InpUseTrend = false; // EMA 20 / 50 cross
input bool InpUsePrice = false; // close above / below previous candle range
input ENUM_NDIR InpDirection = ND_BOTH; // trade direction
input group "=== SIGNAL CONFIRMATION ==="
input int InpPullbackPoints = 0; // wait for a pullback of x points before entering (0 = enter now)
input int InpConfirmTimeoutMin = 15; // cancel the signal after N minutes
input int InpMaxDriftPoints = 0; // cancel if price runs x points away in signal direction (0 = off)
input group "=== NEWS / TIME ==="
input bool InpHighNews = true; // pause new chains around HIGH impact news (live only)
input bool InpMedNews = false; // also MEDIUM impact
input string InpNewsCurrencies = "USD"; // currencies to watch
input int InpNewsBuffer = 60; // minutes before / after the news
input string InpHours = ""; // trading hours server time, e.g. "02:00-20:00" (empty = 24h)
input int InpFridayStopHour = 20; // no new chains on Friday after this hour (0 = off)
input group "=== GENERAL ==="
input long InpMagic = 777888; // magic number
input string InpComment = "NAIF_SR"; // order comment
input bool InpShowPanel = true; // info panel
input bool InpNotify = true; // MT5 push notifications for limits / big events
//------------------------------------------------------------------ globals
CTrade trade;
int hAtrH1 = INVALID_HANDLE, hAtrS = INVALID_HANDLE, hEma20 = INVALID_HANDLE, hEma50 = INVALID_HANDLE, hRsi = INVALID_HANDLE, hMacd = INVALID_HANDLE;
datetime gLastBar = 0;
int gPendDir = 0; double gPendPrice = 0; datetime gPendTime = 0; // signal waiting for confirmation
double gDayEq = 0, gWeekEq = 0, gPeakBal = 0;
int gDayKey = -1, gWeekKey = -1, gChainsToday = 0;
bool gDayStop = false, gWeekStop = false, gHardStop = false, gEqStop = false;
datetime gPauseUntil = 0;
double gTrailPeak[2] = {0, 0};
double gSpacing[]; int gSpacingN = 0;
string gBlock = "", gLastSignal = "-";
double gLastScore = 0;
datetime gNewsChk = 0; bool gNewsNow = false; string gNewsTxt = "";
//------------------------------------------------------------------ helpers
double Bid() { return SymbolInfoDouble(_Symbol, SYMBOL_BID); }
double Ask() { return SymbolInfoDouble(_Symbol, SYMBOL_ASK); }
double Buf(int h, int buf, int shift) { double a[]; if(h == INVALID_HANDLE || CopyBuffer(h, buf, shift, 1, a) != 1) return 0; return a[0]; }
double NormLot(double lot)
{
double st = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP), mn = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN), mx = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
if(st <= 0) st = 0.01;
lot = MathFloor(lot / st + 1e-9) * st;
lot = MathMax(mn, MathMin(lot, mx));
int d = (int)MathMax(0, MathRound(-MathLog10(st)));
return NormalizeDouble(lot, d);
}
void Notify(string s) { Print("NAIF SR: ", s); if(InpNotify && TerminalInfoInteger(TERMINAL_NOTIFICATIONS_ENABLED)) SendNotification("NAIF SR " + _Symbol + ": " + s); }
double FirstLot()
{
if(InpLotSize > 0) return NormLot(InpLotSize);
double per = (InpRisk == NR_LOW ? 0.005 : InpRisk == NR_MEDIUM ? 0.01 : 0.02);
double bal = (InpCustomBalance > 0 ? InpCustomBalance : AccountInfoDouble(ACCOUNT_BALANCE));
double lot = bal / 1000.0 * per * InpLotSizeMultiplier;
if(InpMaxAutoLot > 0) lot = MathMin(lot, InpMaxAutoLot);
return NormLot(lot);
}
//------------------------------------------------------------------ chain info (read from open positions, survives restarts)
struct Chain
{
int n;
double lots, profit, firstPrice, lastPrice, firstLot, lastLot;
datetime firstTime;
};
void ReadChain(int dir, Chain &c) // dir 1 = buy, -1 = sell
{
c.n = 0; c.lots = 0; c.profit = 0; c.firstPrice = 0; c.lastPrice = 0; c.firstLot = 0; c.lastLot = 0; c.firstTime = 0;
datetime lastT = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong t = PositionGetTicket(i);
if(t == 0 || PositionGetString(POSITION_SYMBOL) != _Symbol || PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
bool buy = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY);
if((dir == 1) != buy) continue;
datetime ot = (datetime)PositionGetInteger(POSITION_TIME);
double vol = PositionGetDouble(POSITION_VOLUME), op = PositionGetDouble(POSITION_PRICE_OPEN);
c.n++; c.lots += vol; c.profit += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP);
if(c.firstTime == 0 || ot < c.firstTime) { c.firstTime = ot; c.firstPrice = op; c.firstLot = vol; }
if(ot >= lastT) { lastT = ot; c.lastPrice = op; c.lastLot = vol; }
}
}
void CloseChain(int dir, string why)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong t = PositionGetTicket(i);
if(t == 0 || PositionGetString(POSITION_SYMBOL) != _Symbol || PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
bool buy = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY);
if(dir == 0 || (dir == 1) == buy) trade.PositionClose(t, 100);
}
if(dir != 0) gTrailPeak[dir == 1 ? 0 : 1] = 0; else { gTrailPeak[0] = 0; gTrailPeak[1] = 0; }
Print("NAIF SR: chain closed (", (dir == 1 ? "BUY" : dir == -1 ? "SELL" : "ALL"), ") - ", why);
}
//------------------------------------------------------------------ distances
double BaseDistance() // price units
{
double d;
if(InpFixDistance > 0) d = InpFixDistance * _Point;
else d = Buf(hAtrH1, 0, 1) * InpAtrDistFactor;
if(InpMinDistance > 0) d = MathMax(d, InpMinDistance * _Point);
if(InpMaxDistance > 0) d = MathMin(d, InpMaxDistance * _Point);
if(d <= 0) d = 300 * _Point;
return d;
}
double StepDistance(int k) // distance before trade number k+1 (k = trades already open, 1..)
{
if(gSpacingN > 0) return gSpacing[MathMin(k - 1, gSpacingN - 1)] * _Point;
return BaseDistance() * MathPow(InpDistMultiplier, k - 1);
}
//------------------------------------------------------------------ chain target
double ChainTarget(const Chain &c)
{
double target = InpMinProfit;
if(target <= 0)
{
double p = 0, half = BaseDistance() * 0.5;
if(!OrderCalcProfit(ORDER_TYPE_BUY, _Symbol, c.firstLot, Bid(), Bid() + half, p)) p = 0;
target = MathMax(p * InpAutoProfitMult, 0.01);
}
if(InpBreakEvenAfter > 0 && c.n >= (int)InpBreakEvenAfter) target = MathMin(target, MathMax(InpMinNetProfit, 0.01));
if(InpReduceDelayMin > 0 && InpReduceStepMin > 0)
{
long mins = (long)(TimeCurrent() - c.firstTime) / 60;
if(mins >= InpReduceDelayMin)
{
long steps = 1 + (mins - InpReduceDelayMin) / InpReduceStepMin;
double t0 = target;
if(InpReduceType == NRED_MONEY) target -= steps * InpReduceValue;
else target -= t0 * MathMin(1.0, steps * InpReduceValue / 100.0);
target = MathMax(target, InpMinNetProfit);
}
}
return target;
}
//------------------------------------------------------------------ signals
int SigAI(double &score)
{
double atr = Buf(hAtrS, 0, 1); score = 0;
if(atr <= 0) return 0;
double c1 = iClose(_Symbol, InpSignalTF, 1), o1 = iOpen(_Symbol, InpSignalTF, 1);
double e20 = Buf(hEma20, 0, 1), e20b = Buf(hEma20, 0, 6), e50 = Buf(hEma50, 0, 1);
double rsi = Buf(hRsi, 0, 1), mh = Buf(hMacd, 0, 1) - Buf(hMacd, 1, 1);
double f1 = MathMax(-3, MathMin(3, (c1 - e50) / atr)); // position vs trend
double f2 = (rsi - 50.0) / 50.0; // momentum
double f3 = MathMax(-3, MathMin(3, mh / atr * 4)); // MACD histogram
double f4 = MathMax(-3, MathMin(3, (e20 - e20b) / atr)); // slope
double f5 = MathMax(-2, MathMin(2, (c1 - o1) / atr)); // last candle
double z = 0.45 * f1 + 1.10 * f2 + 0.50 * f3 + 0.60 * f4 + 0.30 * f5;
score = (MathExp(z) - MathExp(-z)) / (MathExp(z) + MathExp(-z)); // tanh -> -1..+1
if(score >= InpAIThreshold) return 1;
if(score <= -InpAIThreshold) return -1;
return 0;
}
int SigSR()
{
int lb = 50;
int ih = iHighest(_Symbol, InpSignalTF, MODE_HIGH, lb, 2), il = iLowest(_Symbol, InpSignalTF, MODE_LOW, lb, 2);
if(ih < 0 || il < 0) return 0;
double hi = iHigh(_Symbol, InpSignalTF, ih), lo = iLow(_Symbol, InpSignalTF, il), atr = Buf(hAtrS, 0, 1);
double l1 = iLow(_Symbol, InpSignalTF, 1), h1 = iHigh(_Symbol, InpSignalTF, 1), c1 = iClose(_Symbol, InpSignalTF, 1), o1 = iOpen(_Symbol, InpSignalTF, 1);
if(l1 <= lo + atr * 0.2 && c1 > o1 && c1 > lo) return 1;
if(h1 >= hi - atr * 0.2 && c1 < o1 && c1 < hi) return -1;
return 0;
}
int SigCandle()
{
double o1 = iOpen(_Symbol, InpSignalTF, 1), c1 = iClose(_Symbol, InpSignalTF, 1), o2 = iOpen(_Symbol, InpSignalTF, 2), c2 = iClose(_Symbol, InpSignalTF, 2);
if(c2 < o2 && c1 > o1 && c1 >= o2 && o1 <= c2) return 1; // bullish engulfing
if(c2 > o2 && c1 < o1 && c1 <= o2 && o1 >= c2) return -1; // bearish engulfing
return 0;
}
int SigTrend()
{
double a1 = Buf(hEma20, 0, 1), b1 = Buf(hEma50, 0, 1), a2 = Buf(hEma20, 0, 2), b2 = Buf(hEma50, 0, 2);
if(a2 <= b2 && a1 > b1) return 1;
if(a2 >= b2 && a1 < b1) return -1;
return 0;
}
int SigPrice()
{
double c1 = iClose(_Symbol, InpSignalTF, 1), h2 = iHigh(_Symbol, InpSignalTF, 2), l2 = iLow(_Symbol, InpSignalTF, 2);
if(c1 > h2) return 1;
if(c1 < l2) return -1;
return 0;
}
int GetSignal() // combines enabled strategies; conflicting votes = no trade
{
int buy = 0, sell = 0, s;
if(InpUseAIScore) { s = SigAI(gLastScore); if(s > 0) buy++; if(s < 0) sell++; }
if(InpUseSR) { s = SigSR(); if(s > 0) buy++; if(s < 0) sell++; }
if(InpUseCandle) { s = SigCandle(); if(s > 0) buy++; if(s < 0) sell++; }
if(InpUseTrend) { s = SigTrend(); if(s > 0) buy++; if(s < 0) sell++; }
if(InpUsePrice) { s = SigPrice(); if(s > 0) buy++; if(s < 0) sell++; }
int d = (buy > 0 && sell == 0) ? 1 : (sell > 0 && buy == 0) ? -1 : 0;
if(InpTrendMode == NT_REVERSE) d = -d;
if(InpDirection == ND_BUY_ONLY && d < 0) d = 0;
if(InpDirection == ND_SELL_ONLY && d > 0) d = 0;
return d;
}
//------------------------------------------------------------------ filters
bool InHours(const MqlDateTime &dt)
{
string h = InpHours; StringTrimLeft(h); StringTrimRight(h);
if(h == "") return true;
string p[]; if(StringSplit(h, '-', p) != 2) return true;
string a[], b[];
if(StringSplit(p[0], ':', a) < 1 || StringSplit(p[1], ':', b) < 1) return true;
int s = (int)StringToInteger(a[0]) * 60 + (ArraySize(a) > 1 ? (int)StringToInteger(a[1]) : 0);
int e = (int)StringToInteger(b[0]) * 60 + (ArraySize(b) > 1 ? (int)StringToInteger(b[1]) : 0);
int m = dt.hour * 60 + dt.min;
return (s <= e) ? (m >= s && m < e) : (m >= s || m < e);
}
void CheckNews()
{
if(TimeCurrent() - gNewsChk < 60) return;
gNewsChk = TimeCurrent(); gNewsNow = false; gNewsTxt = "";
if(MQLInfoInteger(MQL_TESTER) || (!InpHighNews && !InpMedNews)) return;
datetime now = TimeTradeServer();
MqlCalendarValue v[];
if(CalendarValueHistory(v, now - InpNewsBuffer * 60, now + InpNewsBuffer * 60) <= 0) return;
for(int i = 0; i < ArraySize(v); i++)
{
MqlCalendarEvent ev; MqlCalendarCountry co;
if(!CalendarEventById(v[i].event_id, ev) || !CalendarCountryById(ev.country_id, co)) continue;
if(StringFind(InpNewsCurrencies, co.currency) < 0) continue;
if(ev.importance == CALENDAR_IMPORTANCE_HIGH || (InpMedNews && ev.importance == CALENDAR_IMPORTANCE_MODERATE))
{ gNewsNow = true; gNewsTxt = co.currency + " " + ev.name + " " + TimeToString(v[i].time, TIME_MINUTES); return; }
}
}
bool CanStartChain(const MqlDateTime &dt)
{
gBlock = "";
if(gHardStop) { gBlock = "HARD STOP"; return false; }
if(gEqStop) { gBlock = "EQUITY STOP"; return false; }
if(gWeekStop) { gBlock = "weekly loss limit"; return false; }
if(gDayStop) { gBlock = "daily loss limit"; return false; }
if(TimeCurrent() < gPauseUntil) { gBlock = "pause after chain drawdown"; return false; }
if(dt.day_of_week == 0 || dt.day_of_week == 6) { gBlock = "weekend"; return false; }
if(InpFridayStopHour > 0 && dt.day_of_week == 5 && dt.hour >= InpFridayStopHour) { gBlock = "Friday stop"; return false; }
if(!InHours(dt)) { gBlock = "outside hours"; return false; }
if(InpMaxChainsPerDay > 0 && gChainsToday >= InpMaxChainsPerDay) { gBlock = "max chains today"; return false; }
double eq = AccountInfoDouble(ACCOUNT_EQUITY);
if(InpDailyTargetMoney > 0 && gDayEq > 0 && eq - gDayEq >= InpDailyTargetMoney) { gBlock = "daily target reached"; return false; }
if(InpDailyTargetPct > 0 && gDayEq > 0 && (eq / gDayEq - 1) * 100 >= InpDailyTargetPct) { gBlock = "daily target reached"; return false; }
if(InpMaxSpread > 0 && (Ask() - Bid()) / _Point > InpMaxSpread) { gBlock = "spread high"; return false; }
CheckNews();
if(gNewsNow) { gBlock = "news: " + gNewsTxt; return false; }
return true;
}
//------------------------------------------------------------------ protections
void Protect()
{
MqlDateTime dt; TimeToStruct(TimeCurrent(), dt);
double eq = AccountInfoDouble(ACCOUNT_EQUITY), bal = AccountInfoDouble(ACCOUNT_BALANCE);
int dk = dt.year * 1000 + dt.day_of_year;
if(dk != gDayKey) { gDayKey = dk; gDayEq = eq; gDayStop = false; gChainsToday = 0; }
int wk = dt.year * 100 + (dt.day_of_year - dt.day_of_week + 7) / 7;
if(wk != gWeekKey) { gWeekKey = wk; gWeekEq = eq; gWeekStop = false; }
if(bal > gPeakBal) gPeakBal = bal;
if(InpDailyLossPct > 0 && !gDayStop && gDayEq > 0 && eq <= gDayEq * (1 - InpDailyLossPct / 100))
{ gDayStop = true; CloseChain(0, "daily loss limit"); Notify("daily loss limit hit - stopped until tomorrow"); }
if(InpWeeklyLossPct > 0 && !gWeekStop && gWeekEq > 0 && eq <= gWeekEq * (1 - InpWeeklyLossPct / 100))
{ gWeekStop = true; CloseChain(0, "weekly loss limit"); Notify("weekly loss limit hit - stopped until next week"); }
if(InpHardStopPct > 0 && !gHardStop && gPeakBal > 0 && eq <= gPeakBal * (1 - InpHardStopPct / 100))
{ CloseChain(0, "hard stop"); Notify("HARD STOP hit"); if(InpPauseOnHardStop) gHardStop = true; else gPeakBal = bal; }
if(InpEquityStop > 0 && !gEqStop && eq <= InpEquityStop)
{ gEqStop = true; CloseChain(0, "equity stop"); Notify("EQUITY STOP hit - EA stopped"); }
}
//------------------------------------------------------------------ chain management
void ManageChain(int dir)
{
Chain c; ReadChain(dir, c);
int ix = (dir == 1 ? 0 : 1);
if(c.n == 0) { gTrailPeak[ix] = 0; return; }
double bal = AccountInfoDouble(ACCOUNT_BALANCE);
// 1) chain loss limit
if(InpMaxChainDDPct > 0 && c.profit <= -bal * InpMaxChainDDPct / 100)
{
CloseChain(dir, "chain drawdown " + DoubleToString(InpMaxChainDDPct, 1) + "%");
if(InpPauseAfterChainDD > 0) gPauseUntil = TimeCurrent() + InpPauseAfterChainDD * 60;
Notify("chain closed at max drawdown " + DoubleToString(c.profit, 2));
return;
}
if(InpChainStopPoints > 0)
{
double adverse = (dir == 1 ? c.firstPrice - Bid() : Ask() - c.firstPrice) / _Point;
if(adverse >= InpChainStopPoints) { CloseChain(dir, "chain stop points"); return; }
}
// 2) profit exit + basket trailing
double target = ChainTarget(c);
if(InpTrailStart > 0 && InpTrailDist > 0)
{
if(c.profit >= InpTrailStart) gTrailPeak[ix] = MathMax(gTrailPeak[ix], c.profit);
if(gTrailPeak[ix] > 0 && c.profit <= gTrailPeak[ix] - InpTrailDist && c.profit > 0) { CloseChain(dir, "basket trailing"); return; }
if(gTrailPeak[ix] > 0) return; // trailing is running, let it ride
}
if(c.profit >= target) { CloseChain(dir, "target " + DoubleToString(target, 2) + " reached"); return; }
// 3) add a recovery trade
if(c.n >= InpRecovery || gHardStop || gEqStop || gDayStop || gWeekStop) return;
double dist = StepDistance(c.n);
bool add = (dir == 1 ? Ask() <= c.lastPrice - dist : Bid() >= c.lastPrice + dist);
if(!add) return;
if(InpMaxSpread > 0 && (Ask() - Bid()) / _Point > InpMaxSpread) return;
double lot = NormLot(c.lastLot * InpRecoveryLotMult);
string cm = InpComment + "_R" + IntegerToString(c.n + 1);
bool ok = (dir == 1 ? trade.Buy(lot, _Symbol, 0, 0, 0, cm) : trade.Sell(lot, _Symbol, 0, 0, 0, cm));
if(ok) Print("NAIF SR: recovery #", c.n + 1, " ", (dir == 1 ? "BUY " : "SELL "), DoubleToString(lot, 2), " distance ", DoubleToString(dist / _Point, 0), " pts");
}
void TryOpen(int dir)
{
double lot = FirstLot();
string cm = InpComment + "_R1";
bool ok = (dir == 1 ? trade.Buy(lot, _Symbol, 0, 0, 0, cm) : trade.Sell(lot, _Symbol, 0, 0, 0, cm));
if(ok) { gChainsToday++; Print("NAIF SR: new chain ", (dir == 1 ? "BUY " : "SELL "), DoubleToString(lot, 2), " score ", DoubleToString(gLastScore, 2)); }
}
void Entries()
{
MqlDateTime dt; TimeToStruct(TimeCurrent(), dt);
Chain b, s; ReadChain(1, b); ReadChain(-1, s);
bool newBar = false;
datetime bt = iTime(_Symbol, InpSignalTF, 0);
if(bt != gLastBar) { gLastBar = bt; newBar = true; }
if(newBar)
{
int d = GetSignal();
if(d != 0) { gPendDir = d; gPendPrice = (d == 1 ? Ask() : Bid()); gPendTime = TimeCurrent(); gLastSignal = (d == 1 ? "BUY " : "SELL ") + TimeToString(TimeCurrent(), TIME_MINUTES); }
}
if(gPendDir == 0) return;
if(TimeCurrent() - gPendTime > InpConfirmTimeoutMin * 60 && InpPullbackPoints > 0) { gPendDir = 0; return; }
if(!CanStartChain(dt)) { if(InpPullbackPoints == 0) gPendDir = 0; return; }
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && !MQLInfoInteger(MQL_TESTER)) return;
int d = gPendDir;
if(d == 1 && b.n > 0) { gPendDir = 0; return; }
if(d == -1 && s.n > 0) { gPendDir = 0; return; }
if(InpOneChainAtATime && (b.n > 0 || s.n > 0)) { gPendDir = 0; return; }
if(InpPullbackPoints > 0)
{
double px = (d == 1 ? Ask() : Bid());
double moved = (d == 1 ? gPendPrice - px : px - gPendPrice) / _Point; // + = pullback
if(InpMaxDriftPoints > 0 && -moved >= InpMaxDriftPoints) { gPendDir = 0; return; }
if(moved < InpPullbackPoints) return;
}
TryOpen(d);
gPendDir = 0;
}
//------------------------------------------------------------------ panel
void Panel()
{
if(!InpShowPanel || (MQLInfoInteger(MQL_TESTER) && !MQLInfoInteger(MQL_VISUAL_MODE))) return;
static uint last = 0; if(GetTickCount() - last < 1000) return; last = GetTickCount();
Chain b, s; ReadChain(1, b); ReadChain(-1, s);
string t = "NAIF SMART RECOVERY | @najran2015\n";
t += "Balance " + DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2) + " Equity " + DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2) + "\n";
t += "Day " + DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY) - gDayEq, 2) + " chains today " + IntegerToString(gChainsToday) + "\n";
t += "First lot " + DoubleToString(FirstLot(), 2) + " base distance " + DoubleToString(BaseDistance() / _Point, 0) + " pts\n";
if(b.n > 0) t += "BUY chain " + IntegerToString(b.n) + "/" + IntegerToString(InpRecovery) + " lots " + DoubleToString(b.lots, 2) + " P/L " + DoubleToString(b.profit, 2) + " target " + DoubleToString(ChainTarget(b), 2) + " next " + DoubleToString(b.lastPrice - StepDistance(b.n), _Digits) + "\n";
if(s.n > 0) t += "SELL chain " + IntegerToString(s.n) + "/" + IntegerToString(InpRecovery) + " lots " + DoubleToString(s.lots, 2) + " P/L " + DoubleToString(s.profit, 2) + " target " + DoubleToString(ChainTarget(s), 2) + " next " + DoubleToString(s.lastPrice + StepDistance(s.n), _Digits) + "\n";
if(b.n == 0 && s.n == 0) t += "no chain - waiting for signal\n";
t += "AI score " + DoubleToString(gLastScore, 2) + " last signal " + gLastSignal + (gPendDir != 0 ? " (waiting pullback)" : "") + "\n";
if(gBlock != "") t += "BLOCKED: " + gBlock + "\n";
Comment(t);
}
//------------------------------------------------------------------ events
int OnInit()
{
trade.SetExpertMagicNumber(InpMagic);
trade.SetDeviationInPoints(50);
trade.SetTypeFillingBySymbol(_Symbol);
hAtrH1 = iATR(_Symbol, PERIOD_H1, 14);
hAtrS = iATR(_Symbol, InpSignalTF, 14);
hEma20 = iMA(_Symbol, InpSignalTF, 20, 0, MODE_EMA, PRICE_CLOSE);
hEma50 = iMA(_Symbol, InpSignalTF, 50, 0, MODE_EMA, PRICE_CLOSE);
hRsi = iRSI(_Symbol, InpSignalTF, 14, PRICE_CLOSE);
hMacd = iMACD(_Symbol, InpSignalTF, 12, 26, 9, PRICE_CLOSE);
if(hAtrH1 == INVALID_HANDLE || hAtrS == INVALID_HANDLE || hEma20 == INVALID_HANDLE || hEma50 == INVALID_HANDLE || hRsi == INVALID_HANDLE || hMacd == INVALID_HANDLE)
return INIT_FAILED;
gSpacingN = 0; ArrayResize(gSpacing, 0);
string p[]; int n = StringSplit(InpSpacingList, ',', p);
for(int i = 0; i < n; i++) { double v = StringToDouble(p[i]); if(v > 0) { ArrayResize(gSpacing, gSpacingN + 1); gSpacing[gSpacingN++] = v; } }
gPeakBal = AccountInfoDouble(ACCOUNT_BALANCE);
gLastBar = iTime(_Symbol, InpSignalTF, 0); // first signal on the next new candle
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
IndicatorRelease(hAtrH1); IndicatorRelease(hAtrS); IndicatorRelease(hEma20); IndicatorRelease(hEma50); IndicatorRelease(hRsi); IndicatorRelease(hMacd);
Comment("");
}
void OnTick()
{
Protect();
ManageChain(1);
ManageChain(-1);
Entries();
Panel();
}
//+------------------------------------------------------------------+
Tags
to write a review
to purchase