forked from mnbvc188199/Warrior_EA
Four defects in 64c5dd5/1a05e63, found by review + a baseline compile. Goals 1-8 of that session are unchanged; this makes 6 and 8 actually reachable. 1. HEAD DID NOT COMPILE - 6 errors. CControlPanel::Minimize/Maximize were declared `virtual bool ... override`, but CAppDialog declares both as `virtual void` (Controls\Dialog.mqh). errors 265 + 404 on each, plus 151 on `bool ok = CAppDialog::Minimize()`. Return type is void now; there was never a success flag to forward. Verified: 0 errors, 0 warnings. 2. THE BACKFILL COULD NEVER ADVANCE, and neither could the OOS continual simulation (that one has been dead since it was written). Both are armed at the instant convergence is declared, and both advance only from inside Train(), one chunk per call. But ScheduleTrainingIfNeeded's only per-tick ArmStudyEvent site sits in the `else` of a branch taken whenever m_trainingComplete is set and m_trainRunActive is clear - which is exactly the state FinalizeTrainRun() leaves behind one line before they are armed. Train() was never called again, so the walks sat at their start index forever: no "simulation complete" line, and not one row written to the DB this feature exists to fill. Only a manual Resume/Retrain unstuck them. Both flags now keep the model schedulable. 3. IN AI_HYBRID - the mode this ships in - the backfill was never even armed. Ensemble members deploy at Train() ENTRY and return immediately (so no era is wasted), which skips the era-end block the backfill was started from. All four members were a no-op for a second, independent reason. Armed on the ensemble deploy path too, from m_resumeBars/m_resumeOosCutoff. 4. RE-RUNS DUPLICATED ROWS. RegisterSignal inserts unconditionally - no key, no duplicate check - and m_dbBackfillDone is in-memory, so every later attach that retrained to convergence wrote a second full set of rows for the same bars. The ranking would count one bar once per model that ever deployed, weighting superseded opinions as heavily as the live one. A .dbfill marker stamps the deployed era; written only on completion (an interrupted walk redoes itself rather than ranking a partial window) and deleted with the other sidecars on reset-weights. Also: WarmBlocking's timeout was silent, which restored the exact silent pin failure it was added to prevent - it now says so in the journal, and returns true for "no reference pairs to wait for" so the warning stays rare enough to be read. Not addressed, needs a decision: the backfill scores the OOS window with the checkpoint that was SELECTED as best on that same window, then writes those win rates into the table filter weights rank on - the selection set consumed twice, undiscounted, while the deploy gate right next to it applies a family-wise correction for exactly that effect. The rows are also simulated triple-barrier outcomes at today's spread sharing a table with realised fills. The completion log line now states both plainly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
236 lines
12 KiB
MQL5
236 lines
12 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| ControlPanel.mqh |
|
|
//| AnimateDread |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
//--- Built on the standard MQL5 Controls library (CAppDialog/CButton). Per the library's own source
|
|
//--- (Controls\Wnd.mqh CWnd::OnEvent, Controls\Dialog.mqh CAppDialog::ChartEvent), every control in
|
|
//--- this framework - including the dialog's own caption drag and close/minimize buttons - detects
|
|
//--- clicks exclusively through CHARTEVENT_MOUSE_MOVE hit-testing, which raises an internal
|
|
//--- EventChartCustom(CONTROLS_SELF_MESSAGE, ON_CLICK, control.Id(), ...) event; CAppDialog::ChartEvent()
|
|
//--- explicitly ignores CHARTEVENT_OBJECT_CLICK outright ("we are working with the CHARTEVENT_MOUSE_MOVE
|
|
//--- events"). So button clicks are wired the same way CDialog wires its own Close button - an
|
|
//--- EVENT_MAP/ON_EVENT(ON_CLICK, control, handler) entry - not by matching object names against
|
|
//--- CHARTEVENT_OBJECT_CLICK's sparam in the EA's OnChartEvent().
|
|
#include <Controls\Dialog.mqh>
|
|
#include <Controls\Button.mqh>
|
|
#define CP_PANEL_W 176
|
|
#define CP_ROW_H 24
|
|
#define CP_MARGIN 8
|
|
#define CP_BTN_H 20
|
|
#define CP_CAPTION_H 20
|
|
#define CP_NUM_BUTTONS 9
|
|
//--- total outer height needed for the dialog (caption bar + top margin + N button rows + bottom margin)
|
|
#define CP_PANEL_H (CP_CAPTION_H + CP_MARGIN + CP_NUM_BUTTONS * CP_ROW_H + CP_MARGIN)
|
|
//--- gap left between the status label's last line and the minimized panel docked under it
|
|
#define CP_DOCK_GAP 6
|
|
//--- one action per button click; the EA's OnChartEvent() consumes this right after ExtPanel.ChartEvent()
|
|
enum ENUM_CP_ACTION
|
|
{
|
|
CP_ACTION_NONE,
|
|
CP_ACTION_TOGGLE_SIGNALS,
|
|
CP_ACTION_TOGGLE_PAUSE,
|
|
CP_ACTION_TOGGLE_STOP,
|
|
//--- one button, both directions: deploy the trained model as final, or put an already-deployed
|
|
//--- model back into training (the EA decides which from the signal's own state - see
|
|
//--- HandleControlPanelAction/RefreshControlPanelLabels in Warrior_EA.mq5)
|
|
CP_ACTION_TOGGLE_DEPLOY,
|
|
CP_ACTION_SAVE,
|
|
CP_ACTION_LOAD,
|
|
CP_ACTION_RESET,
|
|
CP_ACTION_REPORT,
|
|
CP_ACTION_RESET_DB
|
|
};
|
|
class CControlPanel : public CAppDialog
|
|
{
|
|
private:
|
|
CButton m_signalsBtn;
|
|
CButton m_pauseBtn;
|
|
CButton m_stopBtn;
|
|
CButton m_deployBtn;
|
|
CButton m_saveBtn;
|
|
CButton m_loadBtn;
|
|
CButton m_resetBtn;
|
|
CButton m_reportBtn;
|
|
CButton m_resetDbBtn;
|
|
ENUM_CP_ACTION m_pendingAction;
|
|
//--- Where the panel was before it was last minimized, so Maximize() can put it back instead of
|
|
//--- leaving it wherever Minimize() docked it - see Minimize()'s declaration comment.
|
|
int m_restoreX;
|
|
int m_restoreY;
|
|
bool m_restoreSaved;
|
|
bool CreateOneButton(CButton &btn, const string name, const int y, const string text);
|
|
bool CreateButtons(void);
|
|
protected:
|
|
//--- ON_CLICK handlers (see EVENT_MAP below) - just record which button fired; the EA polls
|
|
//--- ConsumeAction() once per OnChartEvent() call and performs the actual work
|
|
void OnClickSignals(void) { m_pendingAction = CP_ACTION_TOGGLE_SIGNALS; }
|
|
void OnClickPause(void) { m_pendingAction = CP_ACTION_TOGGLE_PAUSE; }
|
|
void OnClickStop(void) { m_pendingAction = CP_ACTION_TOGGLE_STOP; }
|
|
void OnClickDeploy(void) { m_pendingAction = CP_ACTION_TOGGLE_DEPLOY; }
|
|
void OnClickSave(void) { m_pendingAction = CP_ACTION_SAVE; }
|
|
void OnClickLoad(void) { m_pendingAction = CP_ACTION_LOAD; }
|
|
void OnClickReset(void) { m_pendingAction = CP_ACTION_RESET; }
|
|
void OnClickReport(void) { m_pendingAction = CP_ACTION_REPORT; }
|
|
void OnClickResetDb(void) { m_pendingAction = CP_ACTION_RESET_DB; }
|
|
public:
|
|
CControlPanel(void) : m_pendingAction(CP_ACTION_NONE), m_restoreX(0), m_restoreY(0),
|
|
m_restoreSaved(false) {}
|
|
~CControlPanel(void) {}
|
|
virtual bool Create(const long chart, const string name, const int subwin,
|
|
const int x1, const int y1, const int x2, const int y2);
|
|
//--- chart event handler (EVENT_MAP macro below generates the body)
|
|
virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam);
|
|
//--- CAppDialog's default OnClickButtonClose() calls Destroy(), which for a PROGRAM_EXPERT calls
|
|
//--- ExpertRemove() - i.e. the caption bar's "x" would silently detach this EA from the chart and
|
|
//--- stop live trading/training. Redirect it to Minimize() so "x" behaves like the native "-" button
|
|
//--- instead of destroying the EA.
|
|
virtual void OnClickButtonClose(void) override { Minimize(); }
|
|
//--- Docks the minimized bar directly under the status label (System\StatusLabel.mqh) rather than
|
|
//--- leaving it at its maximized position, which - since the label's line count/height changes
|
|
//--- live with training progress - could otherwise sit ON TOP of the label text (user request
|
|
//--- 2026-08-16: minimized should go under the label, not overlap it). Position is remembered so
|
|
//--- Maximize() below can restore it.
|
|
//--- Return type is VOID, matching CAppDialog's own `virtual void Minimize(void)` /
|
|
//--- `virtual void Maximize(void)` (Controls\Dialog.mqh). Declaring these `bool` is a hard compile
|
|
//--- error (265 "overriding virtual function with different return type" + 404 "does not override
|
|
//--- any base class method", plus 151 on assigning the void call to a bool) - the base pair returns
|
|
//--- nothing, so there is no success flag to forward.
|
|
virtual void Minimize(void) override
|
|
{
|
|
if(!m_restoreSaved)
|
|
{
|
|
m_restoreX = Left();
|
|
m_restoreY = Top();
|
|
m_restoreSaved = true;
|
|
}
|
|
CAppDialog::Minimize();
|
|
Move(Left(), StatusLabelBottomY() + CP_DOCK_GAP);
|
|
}
|
|
//--- Restores the position Minimize() displaced it from, rather than leaving a maximized panel
|
|
//--- parked under the label where it was only ever meant to sit while minimized.
|
|
virtual void Maximize(void) override
|
|
{
|
|
CAppDialog::Maximize();
|
|
if(m_restoreSaved)
|
|
{
|
|
Move(m_restoreX, m_restoreY);
|
|
m_restoreSaved = false;
|
|
}
|
|
}
|
|
//--- keeps button labels synced with live training/signal-visibility state
|
|
void SetSignalsText(const string text) { m_signalsBtn.Text(text); }
|
|
void SetPauseText(const string text) { m_pauseBtn.Text(text); }
|
|
void SetStopText(const string text) { m_stopBtn.Text(text); }
|
|
void SetDeployText(const string text) { m_deployBtn.Text(text); }
|
|
//--- returns and clears whichever button (if any) was clicked since the last call
|
|
ENUM_CP_ACTION ConsumeAction(void) { ENUM_CP_ACTION a = m_pendingAction; m_pendingAction = CP_ACTION_NONE; return a; }
|
|
//--- Maximize() is protected in CAppDialog; expose it so the EA can defensively un-minimize on creation
|
|
void ForceMaximize(void) { Maximize(); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Event map - same mechanism CDialog uses internally for its own |
|
|
//| Close button (Controls\Dialog.mqh) |
|
|
//+------------------------------------------------------------------+
|
|
EVENT_MAP_BEGIN(CControlPanel)
|
|
ON_EVENT(ON_CLICK, m_signalsBtn, OnClickSignals)
|
|
ON_EVENT(ON_CLICK, m_pauseBtn, OnClickPause)
|
|
ON_EVENT(ON_CLICK, m_stopBtn, OnClickStop)
|
|
ON_EVENT(ON_CLICK, m_deployBtn, OnClickDeploy)
|
|
ON_EVENT(ON_CLICK, m_saveBtn, OnClickSave)
|
|
ON_EVENT(ON_CLICK, m_loadBtn, OnClickLoad)
|
|
ON_EVENT(ON_CLICK, m_resetBtn, OnClickReset)
|
|
ON_EVENT(ON_CLICK, m_reportBtn, OnClickReport)
|
|
ON_EVENT(ON_CLICK, m_resetDbBtn, OnClickResetDb)
|
|
EVENT_MAP_END(CAppDialog)
|
|
//+------------------------------------------------------------------+
|
|
bool CControlPanel::Create(const long chart, const string name, const int subwin,
|
|
const int x1, const int y1, const int x2, const int y2)
|
|
{
|
|
if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2))
|
|
{
|
|
Print("CControlPanel: CAppDialog::Create() failed, error " + IntegerToString(GetLastError()));
|
|
return false;
|
|
}
|
|
if(!Caption("Warrior EA Control Panel"))
|
|
{
|
|
Print("CControlPanel: Caption() failed, error " + IntegerToString(GetLastError()));
|
|
return false;
|
|
}
|
|
if(!CreateButtons())
|
|
return false;
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
bool CControlPanel::CreateOneButton(CButton &btn, const string name, const int y, const string text)
|
|
{
|
|
if(!btn.Create(m_chart_id, m_name + name, m_subwin, CP_MARGIN, y, CP_PANEL_W - CP_MARGIN, y + CP_BTN_H))
|
|
{
|
|
Print("CControlPanel: button '" + name + "' Create() failed, error " + IntegerToString(GetLastError()));
|
|
return false;
|
|
}
|
|
if(!btn.Text(text))
|
|
{
|
|
Print("CControlPanel: button '" + name + "' Text() failed, error " + IntegerToString(GetLastError()));
|
|
return false;
|
|
}
|
|
if(!Add(btn))
|
|
{
|
|
Print("CControlPanel: button '" + name + "' Add() failed, error " + IntegerToString(GetLastError()));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
bool CControlPanel::CreateButtons(void)
|
|
{
|
|
int y = CP_MARGIN;
|
|
//--- Report first, so the two destructive (red) buttons below end up adjacent at the bottom instead
|
|
//--- of having Report sandwiched between them.
|
|
if(!CreateOneButton(m_reportBtn, "Report", y, "Export Trade Journal"))
|
|
return false;
|
|
y += CP_ROW_H;
|
|
if(!CreateOneButton(m_signalsBtn, "Signals", y, "Hide Signals"))
|
|
return false;
|
|
y += CP_ROW_H;
|
|
if(!CreateOneButton(m_pauseBtn, "Pause", y, "Pause Training"))
|
|
return false;
|
|
y += CP_ROW_H;
|
|
if(!CreateOneButton(m_stopBtn, "Stop", y, "Stop Training"))
|
|
return false;
|
|
y += CP_ROW_H;
|
|
//--- Sits directly under Stop: both end the current training run, but Deploy is the one that promotes
|
|
//--- the result to THE model (and, clicked again, sends it back to training). Green so it reads as the
|
|
//--- constructive counterpart to the red destructive pair at the bottom.
|
|
if(!CreateOneButton(m_deployBtn, "Deploy", y, "Deploy Model"))
|
|
return false;
|
|
if(!m_deployBtn.ColorBackground(clrDarkGreen) || !m_deployBtn.Color(clrWhite))
|
|
{
|
|
Print("CControlPanel: button 'Deploy' color styling failed, error " + IntegerToString(GetLastError()));
|
|
return false;
|
|
}
|
|
y += CP_ROW_H;
|
|
if(!CreateOneButton(m_saveBtn, "Save", y, "Save Weights Now"))
|
|
return false;
|
|
y += CP_ROW_H;
|
|
if(!CreateOneButton(m_loadBtn, "Load", y, "Load Weights From Disk"))
|
|
return false;
|
|
y += CP_ROW_H;
|
|
if(!CreateOneButton(m_resetBtn, "Reset", y, "Delete && Reset Weights"))
|
|
return false;
|
|
if(!m_resetBtn.ColorBackground(clrDarkRed) || !m_resetBtn.Color(clrWhite))
|
|
{
|
|
Print("CControlPanel: button 'Reset' color styling failed, error " + IntegerToString(GetLastError()));
|
|
return false;
|
|
}
|
|
y += CP_ROW_H;
|
|
if(!CreateOneButton(m_resetDbBtn, "ResetDB", y, "Delete && Reset Database"))
|
|
return false;
|
|
if(!m_resetDbBtn.ColorBackground(clrDarkRed) || !m_resetDbBtn.Color(clrWhite))
|
|
{
|
|
Print("CControlPanel: button 'ResetDB' color styling failed, error " + IntegerToString(GetLastError()));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|