Warrior_EA/Panel/AltDataMapDialog.mqh
AnimateDread 0a198fb95f feat(altdata): EIA wired, 24-instrument symbol catalog, mapping dialog for unknown symbols
EIA (user directive: "the NN might find patterns in it for both oil and regular
symbols"). Weekly Petroleum Status Report via the v2 API - crude stocks ex-SPR,
field production, refinery utilization - three features (1y percentile, 4w
change, utilization) on EVERY catalog symbol, not just oil. EIA screened NULL on
WTI's short 7y sample, so these ship as EXPLORATORY inputs: the deploy gate, not
the screen, decides whether a model trained on them trades. Publication stamp
observed+6d mirrors research/altdata/eia.py.

Symbol handling was hardcoded to three if-blocks; it is now a catalog of 24
instruments x alias lists covering The5ers/FTMO/AvaTrade/Dukascopy/OANDA/IC
Markets naming, with prefix matching for the broker suffix zoo (US500.cash,
XAUUSDm, EURUSD.r). Adding an instrument is one AddSpec row. COT caches are
named by CANONICAL so two brokers' names for one contract share a download.

Unrecognised symbol -> a chart dialog (Panel\AltDataMapDialog.mqh, CAppDialog +
dropdown) asks which instrument it is; the answer persists in symbol_map.cfg and
"No alternative data" is a recorded choice, not a nag. Non-blocking by design:
an unmapped symbol contributes 0 features and must never hold up a chart.

Also: UrlEncodePart now escapes '%' - SoQL like-predicates use it as the
wildcard and an unescaped one corrupts the query; docs/ gains the whitelist
URLs, an API-key backup, and the catalog reference.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 16:18:29 -04:00

154 lines
6 KiB
MQL5

//+------------------------------------------------------------------+
//| AltDataMapDialog.mqh |
//| AnimateDread |
//+------------------------------------------------------------------+
//| Asks the user which alt-data instrument an UNRECOGNISED broker |
//| symbol corresponds to. Every broker invents its own index |
//| tickers - Dukascopy calls the S&P 500 "USA500IDX", OANDA calls it |
//| "SPX500", FTMO "US500.cash" - and the CFTC/FRED/EIA universes |
//| have no naming rule that could map them automatically. The |
//| catalog in AltDataFetch.mqh covers the common cases; this dialog |
//| covers the rest, once, and remembers the answer in symbol_map.cfg.|
//| |
//| NON-BLOCKING BY DESIGN: the EA initialises, trains and trades |
//| normally while the dialog is open. An unmapped symbol simply |
//| contributes 0 alt-data features (the panel 0-fills, exactly as it |
//| does when a feed is temporarily down) - it must never be able to |
//| hold up a chart. The user can also close the dialog outright; |
//| "No alternative data" records that choice so it never asks again. |
//| |
//| Follows ControlPanel.mqh's conventions: CAppDialog subclass, an |
//| EVENT_MAP for the buttons, and the EA forwards chart events to it |
//| from OnChartEvent then polls Done(). |
//+------------------------------------------------------------------+
#ifndef WARRIOR_ALTDATA_MAP_DIALOG_MQH
#define WARRIOR_ALTDATA_MAP_DIALOG_MQH
#include <Controls\Dialog.mqh>
#include <Controls\Label.mqh>
#include <Controls\ComboBox.mqh>
#include <Controls\Button.mqh>
#define ADM_PANEL_W 400
#define ADM_PANEL_H 215
#define ADM_MARGIN 12
#define ADM_ROW_H 20
#define ADM_BTN_H 26
#define ADM_PREFIX "WarriorAltMap_"
class CAltDataMapDialog : public CAppDialog
{
protected:
CLabel m_lineA, m_lineB, m_lineC, m_lineD;
CComboBox m_combo;
CButton m_okBtn;
CButton m_noneBtn;
string m_symbol;
string m_canonicals[]; // combo index -> canonical name
bool m_done;
string m_result; // canonical chosen, or "NONE"
bool MakeLabel(CLabel &lbl, string suffix, int y, string text)
{
if(!lbl.Create(m_chart_id, m_name + suffix, m_subwin, ADM_MARGIN, y,
ADM_PANEL_W - ADM_MARGIN, y + ADM_ROW_H))
return false;
lbl.Text(text);
return Add(lbl);
}
void OnClickOk(void)
{
long v = m_combo.Value();
int idx = (int)v;
if(idx >= 0 && idx < ArraySize(m_canonicals))
m_result = m_canonicals[idx];
else
m_result = "NONE";
m_done = true;
}
void OnClickNone(void)
{
m_result = "NONE";
m_done = true;
}
public:
CAltDataMapDialog(void) : m_symbol(""), m_done(false), m_result("") {}
bool Done(void) const { return m_done; }
string Result(void) const { return m_result; }
virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam);
//--- Build and show. `labels` are what the dropdown displays, `canonicals` the values
//--- they map to (parallel arrays, same length).
bool Show(const string symbol, const string &labels[], const string &canonicals[])
{
m_symbol = symbol;
m_done = false;
m_result = "";
ArrayCopy(m_canonicals, canonicals);
int x1 = 30, y1 = 30;
if(!CAppDialog::Create(0, ADM_PREFIX + symbol, 0, x1, y1,
x1 + ADM_PANEL_W, y1 + ADM_PANEL_H))
{
Print("AltDataMapDialog: Create() failed, error " + IntegerToString(GetLastError()));
return false;
}
Caption("Warrior EA - map " + symbol + " to alternative data");
int y = ADM_MARGIN;
if(!MakeLabel(m_lineA, "L1", y, "This chart's symbol is not in the alt-data catalog:"))
return false;
y += ADM_ROW_H;
if(!MakeLabel(m_lineB, "L2", y, " " + symbol))
return false;
y += ADM_ROW_H + 4;
if(!MakeLabel(m_lineC, "L3", y, "Which instrument is it? (e.g. USA500IDX -> SP500)"))
return false;
y += ADM_ROW_H;
if(!MakeLabel(m_lineD, "L4", y, "Alt data is optional - the EA trains and trades either way."))
return false;
y += ADM_ROW_H + 6;
if(!m_combo.Create(m_chart_id, m_name + "Combo", m_subwin, ADM_MARGIN, y,
ADM_PANEL_W - ADM_MARGIN, y + ADM_ROW_H))
{
Print("AltDataMapDialog: combo Create() failed, error " + IntegerToString(GetLastError()));
return false;
}
if(!Add(m_combo))
return false;
for(int i = 0; i < ArraySize(labels); i++)
m_combo.ItemAdd(labels[i], i);
if(ArraySize(labels) > 0)
m_combo.Select(0);
y += ADM_ROW_H + 10;
int half = (ADM_PANEL_W - ADM_MARGIN * 3) / 2;
if(!m_okBtn.Create(m_chart_id, m_name + "Ok", m_subwin, ADM_MARGIN, y,
ADM_MARGIN + half, y + ADM_BTN_H))
return false;
m_okBtn.Text("Use this mapping");
if(!Add(m_okBtn))
return false;
if(!m_noneBtn.Create(m_chart_id, m_name + "None", m_subwin, ADM_MARGIN * 2 + half, y,
ADM_MARGIN * 2 + half * 2, y + ADM_BTN_H))
return false;
m_noneBtn.Text("No alternative data");
if(!Add(m_noneBtn))
return false;
if(!Run())
{
Print("AltDataMapDialog: Run() failed, error " + IntegerToString(GetLastError()));
return false;
}
return true;
}
};
EVENT_MAP_BEGIN(CAltDataMapDialog)
ON_EVENT(ON_CLICK, m_okBtn, OnClickOk)
ON_EVENT(ON_CLICK, m_noneBtn, OnClickNone)
EVENT_MAP_END(CAppDialog)
#endif // WARRIOR_ALTDATA_MAP_DIALOG_MQH