forked from animatedread/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>
63 lines
3.1 KiB
MQL5
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
|