Warrior_EA/System/TradeLog.mqh
AnimateDread 75d7362161 feat(warrior): the vol-gated dip-buy book, ported into Warrior_EA
Warrior's defaults are now the validated book: DIP_ZSCORE alone, long only, H4,
risk 0.25%, one chart per index with a shared Magic.

- System/BarCache.mqh: whole-history closed bars, Wilder ATR, GK sigma and the
  expanding vol percentile (no 1024-bar stdlib ceiling)
- System/AccountGuard.mqh: open-risk cap, kill switch, cross-chart lock and
  Friday flat, shared through terminal globals by Magic
- CWarriorExpert: guard on every tick; a transient open failure retries the bar
- CWarriorSignal::SetupStop: the dip owns its 3 x Wilder ATR stop from the bid
- SignalDipBuy: no entry vote while holding (a still-dipping time exit never
  closed, and Processing re-entered on the exit bar); no entry on a stop bar
- WarriorMoney sizes on equity; WARRIOR_RISK allows fractional risk
- TradeLog + research/compare_ea.py: trade-for-trade check vs WarriorDipZ -
  SP500/US30/DAX40 identical to the cent, NAS100 96.9% (stale-quote timer fills)
- research/nn_cross_index.py: pre-registered cross-index NN meta-label - FAIL
  (AUC 0.564, CI [0.498, 0.630]); DipMetaCut stays off

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 21:14:53 -04:00

63 lines
3.1 KiB
MQL5

//+------------------------------------------------------------------+
//| TradeLog.mqh |
//| AnimateDread |
//| |
//| ONE ROW PER CLOSED POSITION of this symbol and magic, written to |
//| the common folder at deinit: <prefix>_trades_<SYMBOL>.csv. The |
//| columns are WarriorDipZ's journal, so research/reconcile.py and |
//| grid_summary.py read either EA's output - which is how Warrior |
//| was checked trade for trade against the reference implementation. |
//+------------------------------------------------------------------+
#ifndef WARRIOR_TRADELOG_MQH
#define WARRIOR_TRADELOG_MQH
void WriteTradeLog(const string prefix, const string symbol, const long magic)
{
if(!HistorySelect(0, TimeCurrent() + 86400))
return;
const int h = FileOpen(prefix + "_trades_" + symbol + ".csv",
FILE_WRITE | FILE_CSV | FILE_ANSI | FILE_COMMON, ',');
if(h == INVALID_HANDLE)
return;
FileWrite(h, "position", "symbol", "entry_time", "entry_price", "volume",
"exit_time", "exit_price", "net_profit", "exit_reason",
"gross_profit", "swap", "commission");
const int deals = HistoryDealsTotal();
for(int i = 0; i < deals; i++)
{
const ulong d = HistoryDealGetTicket(i);
if(HistoryDealGetInteger(d, DEAL_MAGIC) != magic ||
HistoryDealGetInteger(d, DEAL_ENTRY) != DEAL_ENTRY_OUT ||
HistoryDealGetString(d, DEAL_SYMBOL) != symbol)
continue;
const long pos = HistoryDealGetInteger(d, DEAL_POSITION_ID);
datetime et = 0;
double ep = 0, vol = 0, gross = 0, swp = 0, com = 0;
for(int j = 0; j < deals; j++)
{
const ulong e = HistoryDealGetTicket(j);
if(HistoryDealGetInteger(e, DEAL_POSITION_ID) != pos)
continue;
gross += HistoryDealGetDouble(e, DEAL_PROFIT);
swp += HistoryDealGetDouble(e, DEAL_SWAP);
com += HistoryDealGetDouble(e, DEAL_COMMISSION);
if(HistoryDealGetInteger(e, DEAL_ENTRY) == DEAL_ENTRY_IN)
{
et = (datetime)HistoryDealGetInteger(e, DEAL_TIME);
ep = HistoryDealGetDouble(e, DEAL_PRICE);
vol = HistoryDealGetDouble(e, DEAL_VOLUME);
}
}
const long rsn = HistoryDealGetInteger(d, DEAL_REASON);
const string why = (rsn == DEAL_REASON_SL ? "stop" : (rsn == DEAL_REASON_SO ? "stopout" : "expert"));
FileWrite(h, (string)pos, symbol,
TimeToString(et, TIME_DATE | TIME_MINUTES), DoubleToString(ep, 5),
DoubleToString(vol, 2),
TimeToString((datetime)HistoryDealGetInteger(d, DEAL_TIME), TIME_DATE | TIME_MINUTES),
DoubleToString(HistoryDealGetDouble(d, DEAL_PRICE), 5),
DoubleToString(gross + swp + com, 2), why, DoubleToString(gross, 2),
DoubleToString(swp, 2), DoubleToString(com, 2));
}
FileClose(h);
}
#endif // WARRIOR_TRADELOG_MQH