Add CSV exports: BarHistory, TradeHistory, AgentInteraction, DecisionLog, plus .gitignore

This commit is contained in:
pietro_giacobazzi
2026-06-14 09:48:43 +02:00
parent dcf01418ea
commit e2ca6336af
3 changed files with 347 additions and 35 deletions
+2
View File
@@ -0,0 +1,2 @@
*.ex5
*.log
+316 -22
View File
@@ -18,9 +18,55 @@ struct BarSnapshot {
double combinedZ;
};
struct AgentSnapshot {
double lastZScore;
double weight;
double rho;
double bias;
double biasStd;
double rhoLearn;
double rawSignal;
};
struct TradeRecord {
int ticket;
bool isBuy;
datetime entryTime;
datetime closeTime;
double entryPrice;
double closePrice;
double entryATR;
double entryZ;
double slPrice;
double highestPrice;
double lowestPrice;
int barsHeld;
double maeATR;
double mfeATR;
double actualReturn;
double entryZScores[MAX_AGENTS];
double entryFeatures[NN_FEATURES];
string exitReason;
};
struct DecisionRecord {
datetime time;
string action;
int direction;
double zScore;
double combinedZ;
double price;
int ticket;
int agreeingCount;
int totalAgents;
double confidence;
double minZ;
};
// Posizione aperta tracciata con MAE/MFE
struct TrackedTrade {
int ticket;
datetime entryTime;
double entryPrice;
double entryATR;
double entryZ;
@@ -127,6 +173,14 @@ private:
BarSnapshot history[MAX_HISTORY];
int histIdx;
int histCount;
// --- Export buffers ---
AgentSnapshot agentHistory[MAX_HISTORY][MAX_AGENTS];
TradeRecord completedTrades[];
int completedTradeCount;
DecisionRecord decisionLog[];
int decisionCount;
int decisionCapacity;
void InitCorrelation(int idx) {
corrMeanX[idx] = 0;
@@ -250,11 +304,20 @@ public:
m_trainBuffer = new NNTrainBuffer();
}
histIdx = 0;
histCount = 0;
}
histIdx = 0;
histCount = 0;
~Orchestrator() {
completedTradeCount = 0;
ArrayResize(completedTrades, 200);
decisionCount = 0;
decisionCapacity = 500;
ArrayResize(decisionLog, decisionCapacity);
for(int ai = 0; ai < MAX_HISTORY; ai++)
for(int aj = 0; aj < MAX_AGENTS; aj++)
agentHistory[ai][aj].lastZScore = 0;
}
~Orchestrator() {
ReleaseAgents();
if(m_neuralNet) delete m_neuralNet;
if(m_trainBuffer) delete m_trainBuffer;
@@ -425,10 +488,11 @@ public:
return -1;
}
openTrades[idx].ticket = ticket;
openTrades[idx].entryPrice = price;
openTrades[idx].entryATR = MathMax(atr, 1e-10);
openTrades[idx].entryZ = z;
openTrades[idx].isBuy = isBuy;
openTrades[idx].entryPrice = price;
openTrades[idx].entryATR = MathMax(atr, 1e-10);
openTrades[idx].entryZ = z;
openTrades[idx].isBuy = isBuy;
openTrades[idx].entryTime = TimeCurrent();
openTrades[idx].highestPrice = price;
openTrades[idx].lowestPrice = price;
openTrades[idx].barsHeld = 0;
@@ -463,11 +527,40 @@ openTrades[idx].ticket = ticket;
return idx;
}
void OnTradeOpen(int ticket, double price, double atr) {
AddTrade(ticket, price, atr, combinedZ, combinedZ > 0);
}
void OnTradeClose(int ticket, double closePrice) {
void OnTradeOpen(int ticket, double price, double atr) {
AddTrade(ticket, price, atr, combinedZ, combinedZ > 0);
LogDecision("ENTRY", combinedZ > 0 ? 1 : -1, 0, price, ticket);
}
void LogDecision(string action, int dir, double minZ, double price, int ticket = 0) {
if(decisionCount >= decisionCapacity) {
decisionCapacity *= 2;
ArrayResize(decisionLog, decisionCapacity);
}
DecisionRecord d;
d.time = TimeCurrent();
d.action = action;
d.direction = dir;
d.zScore = MathAbs(combinedZ);
d.combinedZ = combinedZ;
d.price = price;
d.ticket = ticket;
d.agreeingCount = 0;
d.totalAgents = agentCount;
d.confidence = MathAbs(combinedZ);
d.minZ = minZ;
if(action == "SIGNAL" || action == "ENTRY") {
for(int i=0; i<agentCount; i++) {
if(agents[i].enabled && MathAbs(agents[i].lastZScore) > 0.1) {
if((agents[i].lastZScore > 0) == (dir > 0)) d.agreeingCount++;
}
}
}
decisionLog[decisionCount] = d;
decisionCount++;
}
void OnTradeClose(int ticket, double closePrice, string exitReason = "MANUAL") {
// Trova il trade nell'array
int idx = -1;
for(int i=0; i<maxOpenTrades; i++) {
@@ -572,10 +665,34 @@ UpdateCorrelation(i, openTrades[idx].entryZScores[i], actualReturnZ);
}
UpdateHealth(actualReturn);
LogHealth();
LogHealth();
openTrades[idx].active = false;
}
// Save completed trade record
int ct = completedTradeCount;
ArrayResize(completedTrades, ct + 1);
completedTrades[ct].ticket = openTrades[idx].ticket;
completedTrades[ct].isBuy = openTrades[idx].isBuy;
completedTrades[ct].entryPrice = openTrades[idx].entryPrice;
completedTrades[ct].closePrice = closePrice;
completedTrades[ct].entryATR = openTrades[idx].entryATR;
completedTrades[ct].entryZ = openTrades[idx].entryZ;
completedTrades[ct].slPrice = openTrades[idx].slPrice;
completedTrades[ct].highestPrice = openTrades[idx].highestPrice;
completedTrades[ct].lowestPrice = openTrades[idx].lowestPrice;
completedTrades[ct].barsHeld = openTrades[idx].barsHeld;
completedTrades[ct].maeATR = openTrades[idx].maeATR;
completedTrades[ct].mfeATR = openTrades[idx].mfeATR;
completedTrades[ct].actualReturn = actualReturn;
completedTrades[ct].entryTime = 0;
completedTrades[ct].closeTime = TimeCurrent();
completedTrades[ct].exitReason = exitReason;
for(int ei = 0; ei < agentCount; ei++) completedTrades[ct].entryZScores[ei] = openTrades[idx].entryZScores[ei];
for(int ef = 0; ef < NN_FEATURES; ef++) completedTrades[ct].entryFeatures[ef] = openTrades[idx].entryFeatures[ef];
LogDecision("EXIT", openTrades[idx].isBuy ? 1 : -1, 0, closePrice, ticket);
openTrades[idx].active = false;
}
// Trova un trade per ticket
int FindTrade(int ticket) const {
@@ -776,11 +893,20 @@ UpdateHealth(actualReturn);
for(int i=0; i<MAX_AGENTS; i++) prevCorr[i] = 0;
for(int i=0; i<ROLLING_TRADES; i++) rollingReturns[i] = 0;
histIdx = 0;
histCount = 0;
}
// --- Self-evaluation ---
histIdx = 0;
histCount = 0;
completedTradeCount = 0;
ArrayResize(completedTrades, 200);
decisionCount = 0;
decisionCapacity = 500;
ArrayResize(decisionLog, decisionCapacity);
for(int ai = 0; ai < MAX_HISTORY; ai++)
for(int aj = 0; aj < MAX_AGENTS; aj++)
agentHistory[ai][aj].lastZScore = 0;
}
// --- Self-evaluation ---
void UpdateHealth(double actualReturn) {
// Rolling returns buffer
rollingReturns[rollingIdx] = actualReturn;
@@ -856,8 +982,16 @@ UpdateHealth(actualReturn);
void LogBarHistory(datetime time) {
history[histIdx].time = time;
history[histIdx].combinedZ = combinedZ;
for(int i=0; i<agentCount; i++)
for(int i=0; i<agentCount; i++) {
history[histIdx].z[i] = agents[i].lastZScore;
agentHistory[histIdx][i].lastZScore = agents[i].lastZScore;
agentHistory[histIdx][i].weight = agents[i].weight;
agentHistory[histIdx][i].rho = GetCorrelation(i);
agentHistory[histIdx][i].bias = agents[i].predictionError.Mean();
agentHistory[histIdx][i].biasStd = agents[i].predictionError.Std();
agentHistory[histIdx][i].rhoLearn = agents[i].predCorr.Ready() ? agents[i].predCorr.Correlation() : 0;
agentHistory[histIdx][i].rawSignal = agents[i].lastRawSignal;
}
histIdx = (histIdx + 1) % MAX_HISTORY;
if(histCount < MAX_HISTORY) histCount++;
}
@@ -1299,5 +1433,165 @@ UpdateHealth(actualReturn);
FileClose(fh);
Print("Analisi salvata: ", fn);
}
// ===================== SAVE BAR HISTORY CSV =====================
void SaveBarHistoryCSV(string symbol, ENUM_TIMEFRAMES tf) const {
string fn = "BarHistory_" + symbol + "_" + EnumToString(tf) + ".csv";
int fh = FileOpen(fn, FILE_TXT|FILE_WRITE|FILE_COMMON);
if(fh == INVALID_HANDLE) { Print("BarHistoryCSV: errore apertura ", fn); return; }
string header = "bar,time,combinedZ";
string agentNames[MAX_AGENTS];
for(int i=0; i<agentCount; i++) {
agentNames[i] = agents[i].name;
header += "," + agentNames[i] + "_z";
}
header += ",regimeH,adxRaw,regimeConsensus,agreement,trendStrength,patternName";
FileWriteString(fh, header + "\r\n");
int n = MathMin(histCount, MAX_HISTORY);
int start = (histCount >= MAX_HISTORY) ? histIdx : 0;
for(int i=0; i<n; i++) {
int ii = (start + i) % MAX_HISTORY;
string line = (string)i + "," + TimeToString(history[ii].time) + "," + StringFormat("%+.6f", history[ii].combinedZ);
for(int j=0; j<agentCount; j++)
line += "," + StringFormat("%+.6f", history[ii].z[j]);
line += "," + StringFormat("%.4f", SHARED_regimeH);
line += "," + StringFormat("%.1f", SHARED_adxRaw);
line += "," + StringFormat("%+.4f", SHARED_regimeConsensus);
line += "," + StringFormat("%.4f", SHARED_regimeAgreement);
line += "," + StringFormat("%.4f", SHARED_trendStrength);
line += "," + SHARED_patternName;
FileWriteString(fh, line + "\r\n");
}
FileClose(fh);
Print("Bar history salvata: ", fn, " (", n, " barre)");
}
// ===================== SAVE TRADE HISTORY CSV =====================
void SaveTradeHistoryCSV(string symbol, ENUM_TIMEFRAMES tf) const {
string fn = "TradeHistory_" + symbol + "_" + EnumToString(tf) + ".csv";
int fh = FileOpen(fn, FILE_TXT|FILE_WRITE|FILE_COMMON);
if(fh == INVALID_HANDLE) { Print("TradeHistoryCSV: errore apertura ", fn); return; }
string header = "ticket,isBuy,entryTime,closeTime,barsHeld,entryPrice,closePrice,entryATR,entryZ,slPrice,highestPrice,lowestPrice,maeATR,mfeATR,actualReturn,exitReason";
string agentNames[MAX_AGENTS];
for(int i=0; i<agentCount; i++) {
agentNames[i] = agents[i].name;
header += "," + agentNames[i] + "_entryZ";
}
string featLabels[NN_FEATURES] = {"Hurst","ADX","MA","Momentum","Consensus","Hunter","Agreement","TrendStr"};
for(int f=0; f<NN_FEATURES; f++) header += ",feat_" + featLabels[f];
FileWriteString(fh, header + "\r\n");
// Completed trades
for(int t=0; t<completedTradeCount; t++) {
string line = (string)completedTrades[t].ticket;
line += "," + (string)(completedTrades[t].isBuy ? 1 : 0);
line += "," + TimeToString(completedTrades[t].entryTime);
line += "," + TimeToString(completedTrades[t].closeTime);
line += "," + (string)completedTrades[t].barsHeld;
line += "," + StringFormat("%.5f", completedTrades[t].entryPrice);
line += "," + StringFormat("%.5f", completedTrades[t].closePrice);
line += "," + StringFormat("%.4f", completedTrades[t].entryATR);
line += "," + StringFormat("%+.4f", completedTrades[t].entryZ);
line += "," + StringFormat("%.5f", completedTrades[t].slPrice);
line += "," + StringFormat("%.5f", completedTrades[t].highestPrice);
line += "," + StringFormat("%.5f", completedTrades[t].lowestPrice);
line += "," + StringFormat("%.4f", completedTrades[t].maeATR);
line += "," + StringFormat("%.4f", completedTrades[t].mfeATR);
line += "," + StringFormat("%+.4f", completedTrades[t].actualReturn);
line += "," + completedTrades[t].exitReason;
for(int j=0; j<agentCount; j++)
line += "," + StringFormat("%+.4f", completedTrades[t].entryZScores[j]);
for(int f=0; f<NN_FEATURES; f++)
line += "," + StringFormat("%+.4f", completedTrades[t].entryFeatures[f]);
FileWriteString(fh, line + "\r\n");
}
// Open trades (still active)
for(int i=0; i<maxOpenTrades; i++) {
if(!openTrades[i].active) continue;
string line = (string)openTrades[i].ticket;
line += "," + (string)(openTrades[i].isBuy ? 1 : 0);
line += ",,"; // no close time yet
line += "," + (string)openTrades[i].barsHeld;
line += "," + StringFormat("%.5f", openTrades[i].entryPrice);
line += ","; // no close price yet
line += "," + StringFormat("%.4f", openTrades[i].entryATR);
line += "," + StringFormat("%+.4f", openTrades[i].entryZ);
line += "," + StringFormat("%.5f", openTrades[i].slPrice);
line += "," + StringFormat("%.5f", openTrades[i].highestPrice);
line += "," + StringFormat("%.5f", openTrades[i].lowestPrice);
line += "," + StringFormat("%.4f", openTrades[i].maeATR);
line += "," + StringFormat("%.4f", openTrades[i].mfeATR);
line += ",,OPEN"; // no return, exit=OPEN
for(int j=0; j<agentCount; j++)
line += "," + StringFormat("%+.4f", openTrades[i].entryZScores[j]);
for(int f=0; f<NN_FEATURES; f++)
line += "," + StringFormat("%+.4f", openTrades[i].entryFeatures[f]);
FileWriteString(fh, line + "\r\n");
}
FileClose(fh);
Print("Trade history salvata: ", fn, " (", completedTradeCount, " completati, ", OpenTradeCount(), " aperti)");
}
// ===================== SAVE AGENT INTERACTION CSV =====================
void SaveAgentInteractionCSV(string symbol, ENUM_TIMEFRAMES tf) const {
string fn = "AgentInteraction_" + symbol + "_" + EnumToString(tf) + ".csv";
int fh = FileOpen(fn, FILE_TXT|FILE_WRITE|FILE_COMMON);
if(fh == INVALID_HANDLE) { Print("AgentInteractionCSV: errore apertura ", fn); return; }
string header = "bar,time,combinedZ";
for(int i=0; i<agentCount; i++) {
string n = agents[i].name;
header += "," + n + "_z," + n + "_weight," + n + "_rho," + n + "_bias," + n + "_biasStd," + n + "_rhoLearn," + n + "_rawSignal";
}
FileWriteString(fh, header + "\r\n");
int n = MathMin(histCount, MAX_HISTORY);
int start = (histCount >= MAX_HISTORY) ? histIdx : 0;
for(int i=0; i<n; i++) {
int ii = (start + i) % MAX_HISTORY;
string line = (string)i + "," + TimeToString(history[ii].time) + "," + StringFormat("%+.6f", history[ii].combinedZ);
for(int j=0; j<agentCount; j++) {
line += "," + StringFormat("%+.4f", agentHistory[ii][j].lastZScore);
line += "," + StringFormat("%.4f", agentHistory[ii][j].weight);
line += "," + StringFormat("%+.4f", agentHistory[ii][j].rho);
line += "," + StringFormat("%+.4f", agentHistory[ii][j].bias);
line += "," + StringFormat("%.4f", agentHistory[ii][j].biasStd);
line += "," + StringFormat("%+.4f", agentHistory[ii][j].rhoLearn);
line += "," + StringFormat("%+.4f", agentHistory[ii][j].rawSignal);
}
FileWriteString(fh, line + "\r\n");
}
FileClose(fh);
Print("Agent interaction salvata: ", fn, " (", n, " barre)");
}
// ===================== SAVE DECISION LOG CSV =====================
void SaveDecisionLogCSV(string symbol, ENUM_TIMEFRAMES tf) const {
string fn = "DecisionLog_" + symbol + "_" + EnumToString(tf) + ".csv";
int fh = FileOpen(fn, FILE_TXT|FILE_WRITE|FILE_COMMON);
if(fh == INVALID_HANDLE) { Print("DecisionLogCSV: errore apertura ", fn); return; }
FileWriteString(fh, "time,action,direction,zScore,combinedZ,price,ticket,agreeingCount,totalAgents,confidence,minZ\r\n");
for(int d=0; d<decisionCount; d++) {
string line = TimeToString(decisionLog[d].time);
line += "," + decisionLog[d].action;
line += "," + (string)decisionLog[d].direction;
line += "," + StringFormat("%+.4f", decisionLog[d].zScore);
line += "," + StringFormat("%+.4f", decisionLog[d].combinedZ);
line += "," + StringFormat("%.5f", decisionLog[d].price);
line += "," + (string)decisionLog[d].ticket;
line += "," + (string)decisionLog[d].agreeingCount;
line += "," + (string)decisionLog[d].totalAgents;
line += "," + StringFormat("%.4f", decisionLog[d].confidence);
line += "," + StringFormat("%.4f", decisionLog[d].minZ);
FileWriteString(fh, line + "\r\n");
}
FileClose(fh);
Print("Decision log salvato: ", fn, " (", decisionCount, " decisioni)");
}
};
#endif
+29 -13
View File
@@ -169,15 +169,27 @@ void OnDeinit(const int reason) {
}
}
// Agent learning summary
orchestrator.PrintAgentLearningSummary();
orchestrator.SaveAgentLearningCsv();
// Agent learning summary
orchestrator.PrintAgentLearningSummary();
orchestrator.SaveAgentLearningCsv();
// Analisi completa: tutti i parametri derivati, soglie, periodi, stats
orchestrator.SaveAnalysisCSV(sym, Inp_TF);
// Per-bar agent z-scores, combinedZ, SHARED variables
orchestrator.SaveBarHistoryCSV(sym, Inp_TF);
// Save state file (include NN weights inline se addestrata)
orchestrator.SaveState(sym, Inp_TF);
// Per-bar agent weights, biases, correlations, raw signals
orchestrator.SaveAgentInteractionCSV(sym, Inp_TF);
// Per-trade details with PnL, exitReason, full features
orchestrator.SaveTradeHistoryCSV(sym, Inp_TF);
// Entry/exit decision log
orchestrator.SaveDecisionLogCSV(sym, Inp_TF);
// Analisi completa: tutti i parametri derivati, soglie, periodi, stats
orchestrator.SaveAnalysisCSV(sym, Inp_TF);
// Save state file (include NN weights inline se addestrata)
orchestrator.SaveState(sym, Inp_TF);
// Salva anche NN standalone (utile per debug/backup)
if(nnTrained || orchestrator.IsNeuralReady())
@@ -300,11 +312,11 @@ void ManagePositions(const MarketData &data, const FinalSignal &fs) {
if(PositionSelectByTicket(tkt)) {
bool isBuy = orchestrator.IsBuyTrade(tkt);
double closePrice = isBuy ? SymbolInfoDouble(sym, SYMBOL_BID) : SymbolInfoDouble(sym, SYMBOL_ASK);
orchestrator.OnTradeClose(tkt, closePrice);
trade.PositionClose(tkt);
}
}
}
orchestrator.OnTradeClose(tkt, closePrice, "REVERSAL");
trade.PositionClose(tkt);
}
}
}
// 3. Aggiorna trailing stop sul terminale (tester supporta PositionModify)
for(int i=PositionsTotal()-1; i>=0; i--) {
@@ -409,6 +421,10 @@ void LogSignal(const MarketData &data, const FinalSignal &fs) {
}
printCounter++;
// Log decision even for non-actionable signals
double minZ = (Inp_MinZ > 0) ? Inp_MinZ : orchestrator.AdaptiveMinZ();
orchestrator.LogDecision("SIGNAL", fs.direction, minZ, data.Close(0));
if(MathAbs(fs.zScore) > Inp_MinZ) {
Print(log);
Print(" Agents: ", fs.contributingAgents);
@@ -438,7 +454,7 @@ void OnTrade() {
// Trade closed by SL/TP (not by ManagePositions)
bool isBuy = orchestrator.IsBuyTrade(tkt);
double closePrice = isBuy ? SymbolInfoDouble(sym, SYMBOL_BID) : SymbolInfoDouble(sym, SYMBOL_ASK);
orchestrator.OnTradeClose(tkt, closePrice);
orchestrator.OnTradeClose(tkt, closePrice, "SLTP");
onTradeCloses++;
Print("SL/TP chiuso trade #", tkt, " (totale SL/TP: ", onTradeCloses, ")");
if(onTradeCloses % saveInterval == 0)