//+------------------------------------------------------------------+ //| NNDataLogger.mqh| //| Logs training samples ONLY when SignalCluster is trade-ready | //+------------------------------------------------------------------+ #property strict #include #include "NNFeatures.mqh" // ===================== INPUTS ====================== input bool NN_LogData = true; input string NN_LogFileName = "nn_dataset_signalready.csv"; input int NN_LabelLookaheadBars = 180; // lookahead window for label input int NN_MinSecondsBetweenLogs = 10; // avoid spam // SL/TP mapping (points) — match your bot defaults input double NN_Cont_SL_Points = 150; input double NN_Cont_TP_Points = 4000; input double NN_Counter_SL_Points = 50; input double NN_Counter_TP_Points = 3000; // Optional: only log if conflict <= this (extra safety gate) input double NN_MaxConflictToLog = 40.0; // ===================== INTERNAL ====================== datetime g_nn_last_log_time = 0; // Conservative “first hit” label using OHLC of future bars. // If both TP and SL occur in same bar -> SL wins (tp_first=0). int NN_LabelTPBeforeSL(bool is_buy, double entry, double tp_pts, double sl_pts, int lookaheadBars) { double tp = is_buy ? (entry + tp_pts * _Point) : (entry - tp_pts * _Point); double sl = is_buy ? (entry - sl_pts * _Point) : (entry + sl_pts * _Point); for(int i=1; i<=lookaheadBars; i++) { double hi = iHigh(_Symbol, PERIOD_CURRENT, i); double lo = iLow(_Symbol, PERIOD_CURRENT, i); bool hitTP = false, hitSL = false; if(is_buy) { if(hi >= tp) hitTP = true; if(lo <= sl) hitSL = true; } else { if(lo <= tp) hitTP = true; // TP is below entry for sell if(hi >= sl) hitSL = true; // SL is above entry for sell } // Conservative tie-break: SL wins if(hitSL) return 0; if(hitTP) return 1; } return 0; // no TP within window => treat as fail } // Public function: call this ONLY when your cluster is trade-ready. void NN_LogSignalReadySample( bool is_buy, int category, // 0 continuation, 1 counter (use your TRADE_CATEGORY) double cluster_strength, // 0..100 if you have it double conflict_score // 0..100 if you have it ) { if(!NN_LogData) return; datetime now = TimeCurrent(); if(g_nn_last_log_time > 0 && (now - g_nn_last_log_time) < NN_MinSecondsBetweenLogs) return; if(conflict_score > NN_MaxConflictToLog) return; // Feature vector from your existing globals (Indicators/Warn/Praise/State) double feat[]; int n = BuildNNFeatures(feat); if(n <= 0) return; // Use current open price (you can change to bid/ask if you prefer) double entry = iOpen(_Symbol, PERIOD_CURRENT, 0); // Pick SL/TP based on category double sl_pts = (category == 0 ? NN_Cont_SL_Points : NN_Counter_SL_Points); double tp_pts = (category == 0 ? NN_Cont_TP_Points : NN_Counter_TP_Points); int tp_first = NN_LabelTPBeforeSL(is_buy, entry, tp_pts, sl_pts, NN_LabelLookaheadBars); // Write CSV row int h = FileOpen(NN_LogFileName, FILE_READ|FILE_WRITE|FILE_CSV|FILE_ANSI); if(h == INVALID_HANDLE) return; FileSeek(h, 0, SEEK_END); // Header if(FileSize(h) == 0) { FileWrite(h, "time","symbol","tf", "is_buy","category", "cluster_strength","conflict_score", "entry","tp_points","sl_points", "tp_first", "n_features","features_json" ); } // Serialize features string fjson = "["; for(int k=0; k