v5.6: Thursday wind-down + Friday shadow metric

Wind-down (Thu 19:00 server = Thu noon ET -> weekend close): cancel
entry pendings, no new grids, trail tightened to 200/150pts so open
positions walk out at their natural end instead of being chopped at the
midnight deadline. Hard close unchanged as backstop.

Shadow metric: first tick of each new server week, every EA reports what
its weekend-flattened positions would have done through Friday (MTM at
Friday's last H1 close + worst adverse excursion) — quantifies the cost
of the no-Friday policy from live data.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 09:43:55 -04:00
co-authored by Claude Fable 5
parent e2fbe0d01d
commit e72a2ea5c4
18 changed files with 232 additions and 5 deletions
+130 -5
View File
@@ -5,12 +5,12 @@
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Garfield Heron"
#property link "https://fetcherpay.com"
#property version "5.5"
#property version "5.6"
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#define VERSION "Version 5.5 Smart Grid Breakout BO MT5"
#define VERSION "Version 5.6 Smart Grid Breakout BO MT5"
#define MAX_TRADES 600
#define MAX_LOG_TRADES 1200
@@ -118,6 +118,14 @@ input bool InpCloseBeforeWeekend = true;
input int InpWeekendCloseHour = 17;
input bool InpCancelPendingBeforeWeekend = true;
input int InpMondayStartHour = 7; // Monday: no new grids before this server hour (0 = off)
//--- Thursday Wind-Down (5.6): approach the weekend close algorithmically —
// stop taking new risk Thursday afternoon and let tight trails walk
// positions out at their natural end; the hard close stays as backstop.
input string WinddownSettings = "=== Thursday Wind-Down ===";
input bool InpUseWinddown = true;
input int InpWinddownStartHour = 19; // Server hour Thursday: no new grids from here (Thu 12:00 ET)
input int InpWinddownTrailStart = 200; // Wind-down trail activation (points)
input int InpWinddownTrailStop = 150; // Wind-down trail distance (points)
//--- Trade Object
CTrade trade;
@@ -171,6 +179,8 @@ datetime lastWeeklyScan = 0;
//--- Weekend Protection
bool weekendCloseExecuted = false;
bool mondayStandDownLogged = false;
bool winddownLogged = false;
bool winddownPendingsCancelled = false;
//--- Master one-shot (1.4)
bool masterShutdownDone = false;
@@ -456,6 +466,73 @@ bool CheckWeekendProtection()
return true;
}
//+------------------------------------------------------------------+
//| Thursday Wind-Down window (5.6) |
//| Server Thursday >= InpWinddownStartHour through the Friday weekend |
//| close: no new grids, pendings cancelled, tight trails take over. |
//+------------------------------------------------------------------+
bool InWinddown()
{
if(!InpUseWinddown) return false;
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
if(dt.day_of_week == THURSDAY && dt.hour >= InpWinddownStartHour) return true;
if(dt.day_of_week == FRIDAY && dt.hour < InpWeekendCloseHour) return true;
return false;
}
//+------------------------------------------------------------------+
//| Friday shadow metric (5.6) |
//| First tick of the new trading week: report what each position |
//| flattened at the weekend close would have done through Friday — |
//| mark-to-market at Friday's last H1 close plus worst adverse |
//| excursion. Pure telemetry: quantifies what the flatten policy |
//| costs (or saves) for the weekly analysis. |
//+------------------------------------------------------------------+
void ReportFridayShadow()
{
datetime monday = WeekStartBroker();
datetime friStart = monday - 3*86400 + InpWeekendCloseHour*3600;
datetime friEnd = monday - 2*86400;
MqlRates bars[];
int n = CopyRates(_Symbol, PERIOD_H1, friStart, friEnd, bars);
if(n < 1) return;
double friClose = bars[n-1].close;
double hi = bars[0].high, lo = bars[0].low;
for(int i = 1; i < n; i++)
{
if(bars[i].high > hi) hi = bars[i].high;
if(bars[i].low < lo) lo = bars[i].low;
}
double tickVal = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
if(tickSize <= 0 || tickVal <= 0) return;
if(!HistorySelect(friStart - 300, friStart + 900)) return;
for(int i = 0; i < HistoryDealsTotal(); i++)
{
ulong d = HistoryDealGetTicket(i);
if(d == 0) continue;
if(HistoryDealGetString(d, DEAL_SYMBOL) != _Symbol) continue;
if(HistoryDealGetInteger(d, DEAL_MAGIC) != MagicNum) continue;
if((ENUM_DEAL_ENTRY)HistoryDealGetInteger(d, DEAL_ENTRY) != DEAL_ENTRY_OUT) continue;
// a SELL deal closes a long position
int dir = ((ENUM_DEAL_TYPE)HistoryDealGetInteger(d, DEAL_TYPE) == DEAL_TYPE_SELL) ? 1 : -1;
double vol = HistoryDealGetDouble(d, DEAL_VOLUME);
double closeP = HistoryDealGetDouble(d, DEAL_PRICE);
double mtm = (friClose - closeP) * dir / tickSize * tickVal * vol;
double worstP = (dir > 0) ? lo : hi;
double worst = (worstP - closeP) * dir / tickSize * tickVal * vol;
PrintS("SHADOW " + (dir > 0 ? "long" : "short") + " " + DoubleToString(vol, 2) +
" flattened@" + DoubleToString(closeP, _Digits) +
" friClose=" + DoubleToString(friClose, _Digits) +
" mtm=$" + DoubleToString(mtm, 2) +
" worst=$" + DoubleToString(worst, 2));
}
}
//+------------------------------------------------------------------+
//| Monday Morning Stand-Down (5.3) |
//| Sunday-evening ET liquidity is thin and the pivots/ATR are built |
@@ -864,13 +941,17 @@ void ApplyBreakeven()
void ApplyTrailingStop()
{
if(!InpUseTrailingStop) return;
if(InpTrailStartPoints <= 0 || InpTrailStop <= 0) return;
// Wind-down (5.6): much tighter trail so positions walk themselves out
// ahead of the weekend close instead of being chopped at the deadline
int effStartPts = InWinddown() ? InpWinddownTrailStart : InpTrailStartPoints;
int effDistPts = InWinddown() ? InpWinddownTrailStop : InpTrailStop;
if(effStartPts <= 0 || effDistPts <= 0) return;
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double trailStart = InpTrailStartPoints * point;
double trailDist = InpTrailStop * point;
double trailStart = effStartPts * point;
double trailDist = effDistPts * point;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
@@ -1332,6 +1413,20 @@ void OnTick()
lastHeartbeat = TimeCurrent();
}
// --- Friday shadow report (5.6) — once per week on the first tick of the
// new server week (Sunday evening ET), before any gate can block it ---
MqlDateTime shdt;
TimeToStruct(TimeCurrent(), shdt);
if(shdt.day_of_week == MONDAY)
{
datetime wk = WeekStartBroker();
if((datetime)GlobalVariableGet(GvKey("shadowWk")) != wk)
{
GlobalVariableSet(GvKey("shadowWk"), (double)wk);
ReportFridayShadow();
}
}
// --- Per-EA daily drawdown (3.1B) ---
if(!CheckDailyDrawdown())
{
@@ -1398,6 +1493,36 @@ void OnTick()
if(currentBarTime == lastBarTime) return;
lastBarTime = currentBarTime;
// --- Thursday wind-down (5.6): no new risk into the shortened week.
// Trailing/breakeven keep running in the 5s block above; only new grid
// placement (and existing entry pendings) are shut off.
if(InWinddown())
{
if(!winddownLogged)
{
PrintS("🌙 WIND-DOWN — no new grids; trail tightened to " +
IntegerToString(InpWinddownTrailStart) + "/" +
IntegerToString(InpWinddownTrailStop) + "pts until weekend close");
winddownLogged = true;
}
if(!winddownPendingsCancelled)
{
CancelAllOrders("Wind-down — no new risk");
winddownPendingsCancelled = true;
if(gridPlaced)
{
gridPlaced = false;
SaveGridState();
}
}
return;
}
if(winddownLogged || winddownPendingsCancelled)
{
winddownLogged = false;
winddownPendingsCancelled = false;
}
// --- Update adaptive-filter state each bar ---
UpdateFilterRelaxation();