fix: EA restarted at 14:36 — all ORB sessions lost (sessions showed [WAIT]), g_TradingHalted = true blocked UpdateORBSessions() on new bars, Daily loss halt didn't persist across restarts, g_GlobalBiasDir never flipped on sweep reversals, SELL 0.02 is a wrongly-opened hedge position
This commit is contained in:
+135
-31
@@ -5,7 +5,7 @@
|
||||
//| Based on: ORB-All-Sessions.pine + LuxAlgo Order Block Detector |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "NANDR"
|
||||
#property version "1.35"
|
||||
#property version "1.37"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
@@ -682,8 +682,9 @@ void UpdateRetestState(int sessIdx)
|
||||
if(sweepRev)
|
||||
{
|
||||
g_Sessions[sessIdx].breakoutDir = -1;
|
||||
PrintFormat("NANDR EA: [%s] Bullish sweep reversal at orbHigh %.2f — flipping to BEARISH",
|
||||
g_Sessions[sessIdx].name, orbHigh);
|
||||
g_GlobalBiasDir = -1; // price swept the level and closed below — day bias now bearish
|
||||
PrintFormat("NANDR EA: [%s] Bullish sweep reversal at orbHigh %.2f — flipping to BEARISH [DayBias=%d]",
|
||||
g_Sessions[sessIdx].name, orbHigh, g_GlobalBiasDir);
|
||||
}
|
||||
// Full invalidation: closed below mid
|
||||
else if(c0 < orbMid && c1 > orbMid)
|
||||
@@ -701,8 +702,9 @@ void UpdateRetestState(int sessIdx)
|
||||
if(sweepRev)
|
||||
{
|
||||
g_Sessions[sessIdx].breakoutDir = 1;
|
||||
PrintFormat("NANDR EA: [%s] Bearish sweep reversal at orbLow %.2f — flipping to BULLISH",
|
||||
g_Sessions[sessIdx].name, orbLow);
|
||||
g_GlobalBiasDir = 1; // price swept the level and closed above — day bias now bullish
|
||||
PrintFormat("NANDR EA: [%s] Bearish sweep reversal at orbLow %.2f — flipping to BULLISH [DayBias=%d]",
|
||||
g_Sessions[sessIdx].name, orbLow, g_GlobalBiasDir);
|
||||
}
|
||||
// Full invalidation: closed above mid
|
||||
else if(c0 > orbMid && c1 < orbMid)
|
||||
@@ -1055,8 +1057,9 @@ void CheckRetestEntriesTick()
|
||||
{
|
||||
dir = -1;
|
||||
g_Sessions[s].breakoutDir = -1;
|
||||
PrintFormat("NANDR EA: [%s] Tick sweep reversal below orbHigh %.2f → SELL",
|
||||
g_Sessions[s].name, orbHigh);
|
||||
g_GlobalBiasDir = -1; // tick sweep below orbHigh — day bias flips bearish
|
||||
PrintFormat("NANDR EA: [%s] Tick sweep reversal below orbHigh %.2f → SELL [DayBias=%d]",
|
||||
g_Sessions[s].name, orbHigh, g_GlobalBiasDir);
|
||||
}
|
||||
}
|
||||
// Mid retest: prev close above mid, wick dipped to mid
|
||||
@@ -1086,8 +1089,9 @@ void CheckRetestEntriesTick()
|
||||
{
|
||||
dir = 1;
|
||||
g_Sessions[s].breakoutDir = 1;
|
||||
PrintFormat("NANDR EA: [%s] Tick sweep reversal above orbLow %.2f → BUY",
|
||||
g_Sessions[s].name, orbLow);
|
||||
g_GlobalBiasDir = 1; // tick sweep above orbLow — day bias flips bullish
|
||||
PrintFormat("NANDR EA: [%s] Tick sweep reversal above orbLow %.2f → BUY [DayBias=%d]",
|
||||
g_Sessions[s].name, orbLow, g_GlobalBiasDir);
|
||||
}
|
||||
}
|
||||
// Mid retest: prev close below mid, wick came back up to mid
|
||||
@@ -1297,13 +1301,16 @@ void ManageOpenTrades()
|
||||
double fullLots = g_Position.Volume();
|
||||
double closeLots = NormalizeLot(fullLots * InpPartialCloseRatio);
|
||||
double minLot = SymbolInfoDouble(g_Symbol, SYMBOL_VOLUME_MIN);
|
||||
if(closeLots >= minLot)
|
||||
// Ensure we leave at least minLot remaining so the position stays open
|
||||
double remaining = NormalizeLot(fullLots - closeLots);
|
||||
if(closeLots >= minLot && remaining >= minLot)
|
||||
{
|
||||
bool partResult = (posDir > 0)
|
||||
? g_Trade.Sell(closeLots, g_Symbol, 0, 0, 0,
|
||||
StringFormat("NANDR|PartialClose|%.0fpips", InpBreakevenTrigPips))
|
||||
: g_Trade.Buy(closeLots, g_Symbol, 0, 0, 0,
|
||||
StringFormat("NANDR|PartialClose|%.0fpips", InpBreakevenTrigPips));
|
||||
// PositionClosePartial reduces the existing position by closeLots WITHOUT
|
||||
// opening a new opposite trade. This is correct for both netting and hedging
|
||||
// accounts. Using g_Trade.Sell/Buy here would open a new hedge position
|
||||
// instead of reducing the original one — that was the previous bug.
|
||||
g_Trade.SetComment(StringFormat("NANDR|PartialClose|%.0fpips", InpBreakevenTrigPips));
|
||||
bool partResult = g_Trade.PositionClosePartial(ticket, closeLots);
|
||||
if(partResult)
|
||||
{
|
||||
MarkPartialClosed(ticket);
|
||||
@@ -1683,6 +1690,101 @@ void RemoveAllObjects()
|
||||
ChartRedraw(0);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Reconstruct ORB levels from historical bars on startup |
|
||||
//| Handles mid-day EA restarts — finds today's session open bars |
|
||||
//| in price history and restores High/Low so sessions show ARMED. |
|
||||
//+------------------------------------------------------------------+
|
||||
void ReconstructORBFromHistory()
|
||||
{
|
||||
MqlDateTime dtNow;
|
||||
TimeToStruct(TimeCurrent(), dtNow);
|
||||
|
||||
for(int s = 0; s < g_SessionCount; s++)
|
||||
{
|
||||
if(!g_Sessions[s].enabled || g_Sessions[s].orbComplete) continue;
|
||||
|
||||
// Build this session's open datetime for today
|
||||
MqlDateTime dtSess = dtNow;
|
||||
dtSess.hour = g_Sessions[s].startHour;
|
||||
dtSess.min = g_Sessions[s].startMin;
|
||||
dtSess.sec = 0;
|
||||
datetime sessOpenTime = StructToTime(dtSess);
|
||||
|
||||
// Only reconstruct if the opening bar has fully closed
|
||||
long barSecs = (long)g_OrbBarsNeeded * (int)InpOrbTimeframe * 60;
|
||||
if(TimeCurrent() < sessOpenTime + barSecs) continue;
|
||||
|
||||
// Find the bar index for this session open time
|
||||
int barIdx = iBarShift(g_Symbol, PERIOD_CURRENT, sessOpenTime, false);
|
||||
if(barIdx < 1) continue; // bar[0] is the live bar
|
||||
|
||||
// Verify exact time match (session bar must exist in chart history)
|
||||
if(iTime(g_Symbol, PERIOD_CURRENT, barIdx) != sessOpenTime) continue;
|
||||
|
||||
// Accumulate ORB range across all required bars (M1: 15 bars, M5: 3, M15: 1)
|
||||
double h = iHigh(g_Symbol, PERIOD_CURRENT, barIdx);
|
||||
double l = iLow(g_Symbol, PERIOD_CURRENT, barIdx);
|
||||
for(int b = 1; b < g_OrbBarsNeeded; b++)
|
||||
{
|
||||
int idx = barIdx - b;
|
||||
if(idx < 1) break;
|
||||
h = MathMax(h, iHigh(g_Symbol, PERIOD_CURRENT, idx));
|
||||
l = MathMin(l, iLow(g_Symbol, PERIOD_CURRENT, idx));
|
||||
}
|
||||
|
||||
g_Sessions[s].orbHigh = h;
|
||||
g_Sessions[s].orbLow = l;
|
||||
g_Sessions[s].orbBarCount = g_OrbBarsNeeded;
|
||||
g_Sessions[s].orbStartTime = sessOpenTime;
|
||||
g_Sessions[s].orbComplete = true;
|
||||
|
||||
PrintFormat("NANDR EA: [%s] ORB reconstructed from history. High=%.2f Low=%.2f",
|
||||
g_Sessions[s].name, h, l);
|
||||
if(InpShowORBLines) DrawORBLines(s);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Sync daily state from deal history on startup/restart |
|
||||
//| Restores g_DayStartEquity so the daily loss limit persists across|
|
||||
//| EA restarts, and syncs g_TodayTrades to prevent double-counting. |
|
||||
//+------------------------------------------------------------------+
|
||||
void SyncDailyState()
|
||||
{
|
||||
datetime dayStart = iTime(g_Symbol, PERIOD_D1, 0);
|
||||
if(dayStart == 0) return;
|
||||
HistorySelect(dayStart, TimeCurrent());
|
||||
|
||||
int deals = HistoryDealsTotal();
|
||||
double closedPnL = 0;
|
||||
int tradeOpens = 0;
|
||||
|
||||
for(int i = 0; i < deals; i++)
|
||||
{
|
||||
ulong ticket = HistoryDealGetTicket(i);
|
||||
if(HistoryDealGetInteger(ticket, DEAL_MAGIC) != InpMagicNumber) continue;
|
||||
if(HistoryDealGetString(ticket, DEAL_SYMBOL) != g_Symbol) continue;
|
||||
|
||||
long entry = HistoryDealGetInteger(ticket, DEAL_ENTRY);
|
||||
if(entry == DEAL_ENTRY_IN)
|
||||
tradeOpens++;
|
||||
if(entry == DEAL_ENTRY_OUT)
|
||||
closedPnL += HistoryDealGetDouble(ticket, DEAL_PROFIT)
|
||||
+ HistoryDealGetDouble(ticket, DEAL_SWAP)
|
||||
+ HistoryDealGetDouble(ticket, DEAL_COMMISSION);
|
||||
}
|
||||
|
||||
// Reconstruct day-start equity: current balance minus today's already-realized P&L.
|
||||
// This ensures the daily loss limit check works correctly after a mid-day restart.
|
||||
double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
g_DayStartEquity = currentBalance - closedPnL;
|
||||
g_TodayTrades = tradeOpens;
|
||||
|
||||
PrintFormat("NANDR EA: Daily state synced. DayStartEquity=%.2f TodayTrades=%d ClosedPnL=%.2f",
|
||||
g_DayStartEquity, g_TodayTrades, closedPnL);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| OnInit |
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -1770,10 +1872,18 @@ int OnInit()
|
||||
if(g_SessionCount == 0)
|
||||
Print("NANDR EA: WARNING — No sessions enabled. EA will not trade. Enable at least one session.");
|
||||
|
||||
// Daily init
|
||||
// Daily init — set conservative defaults; SyncDailyState() will correct them from history
|
||||
g_DayStartEquity = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
g_LastDayReset = TimeCurrent();
|
||||
|
||||
// Sync trade count and day-start equity from today's deal history
|
||||
// (handles mid-day restarts so daily loss limits carry over correctly)
|
||||
SyncDailyState();
|
||||
|
||||
// Restore ORB levels from historical bars (handles mid-day restarts
|
||||
// where the session opening bars have already closed before EA loaded)
|
||||
ReconstructORBFromHistory();
|
||||
|
||||
PrintFormat("NANDR EA: Initialized. Symbol=%s OrbBarsNeeded=%d BreakoutConf=%d OBPivot=%d Sessions=%d",
|
||||
g_Symbol, g_OrbBarsNeeded, g_BreakoutConfBars, g_OBPivotLength, g_SessionCount);
|
||||
|
||||
@@ -1828,28 +1938,22 @@ void OnTick()
|
||||
// Daily reset
|
||||
CheckDailyReset();
|
||||
|
||||
if(g_TradingHalted)
|
||||
{
|
||||
DrawDashboard();
|
||||
return;
|
||||
}
|
||||
|
||||
// ORB accumulation
|
||||
// Always update ORB and OB state regardless of halt status.
|
||||
// This keeps session levels current for display and ensures breakouts are detected
|
||||
// as soon as trading resumes (e.g. next day after a daily-loss halt).
|
||||
UpdateORBSessions();
|
||||
|
||||
// Order Block updates
|
||||
ScanOrderBlocks();
|
||||
MitigateOrderBlocks();
|
||||
|
||||
// Breakout detection
|
||||
DetectBreakouts();
|
||||
UpdateClosedTrades();
|
||||
DrawDashboard();
|
||||
|
||||
// Skip trade execution only when halted — state tracking continues above
|
||||
if(g_TradingHalted)
|
||||
return;
|
||||
|
||||
// Entry signals (direct breakout / limit orders on new bar)
|
||||
CheckEntrySignals();
|
||||
|
||||
// Update closed trade stats & dashboard
|
||||
UpdateClosedTrades();
|
||||
DrawDashboard();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
Reference in New Issue
Block a user