forked from MrBaro75/Warrior_EA
278 lines
14 KiB
MQL5
278 lines
14 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| AltDataCatalog.mqh |
| |||
//| AnimateDread |
| |||
//| Symbol catalog + alias resolution + user-mapping persistence. |
| |||
//| An MT5 symbol cannot be mapped to a CFTC contract by rule - the |
| |||
//| naming universes are unrelated and every broker invents its own |
| |||
//| index tickers - so this catalog IS the mechanism: the chart |
| |||
//| symbol selects a row at runtime and ONLY that row's sources are |
| |||
//| fetched. Knows nothing about HTTP or feature math. |
| |||
//+------------------------------------------------------------------+
| |||
#ifndef ALTDATACATALOG_MQH
| |||
#define ALTDATACATALOG_MQH
| |||
| |||
//--- one symbol catalog row: which sources feed the instrument and which feature columns its
| |||
//--- {SYM}_D1.csv carries (column order = EA input order, APPEND-ONLY per the .cfg name pin)
| |||
struct SAltSymbolSpec
| |||
{
| |||
string canonical; // catalog key - also names the shared COT raw cache
| |||
string aliases; // ';'-separated broker names; matched EXACT first, then as a
| |||
// PREFIX so suffixed variants (US500.cash, XAUUSDm, EURUSD.r)
| |||
// resolve without being listed one by one
| |||
string cotWhere; // SoQL predicate selecting the CFTC contract; "" = no COT leg
| |||
double cotSign; // +1 futures quote matches the chart; -1 inverted (JPY/CAD futures are FX/USD)
| |||
string features; // ';'-separated feature ids, in CSV column order
| |||
string ivolSeries;// FRED id of THIS instrument's own implied-vol index; "" = none
| |||
};
| |||
| |||
class CAltDataCatalog
| |||
{
| |||
private:
| |||
SAltSymbolSpec m_specs[];
| |||
string m_userFrom[]; // broker symbol (from symbol_map.cfg / the dialog)
| |||
string m_userTo[]; // canonical, or "NONE" = user declined alt data
| |||
bool m_userMapLoaded;
| |||
| |||
void AddSpec(string canonical, string aliases, string cotWhere,
| |||
double cotSign, string feats, string ivolSeries = "")
| |||
{
| |||
int n = ArraySize(m_specs);
| |||
ArrayResize(m_specs, n + 1);
| |||
m_specs[n].canonical = canonical;
| |||
m_specs[n].aliases = aliases;
| |||
m_specs[n].cotWhere = cotWhere;
| |||
m_specs[n].cotSign = cotSign;
| |||
m_specs[n].features = feats;
| |||
m_specs[n].ivolSeries = ivolSeries;
| |||
}
| |||
| |||
void BuildCatalog(void)
| |||
{
| |||
//--- feature bundles (locals, deliberately prefixed so no macro elsewhere can collide) POLICY
| |||
//--- (user directive 2026-08-16, twice): every feature the fetched sources can serve is wired
| |||
//--- on every symbol - the research screens inform priors but do NOT gate the input list ("we
| |||
//--- will leave the NN to do its job").
| |||
string f_eia = "eia_stk_idx1y;eia_stk_chg4;eia_util";
| |||
string f_risk = "vix_chg5;vix;usd_chg5"; // equity-risk complex
| |||
string f_cot = "cot_idx_1y;cot_idx_3y;cot_chg_4w"; // COT positioning family
| |||
//--- US macro block: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB
| |||
//--- policy gap, CPI yoy, unemployment 12m change.
| |||
string f_mac = "mac_y10;mac_curve;mac_bei;mac_gap;mac_cpi;mac_unemp";
| |||
//--- CFTC predicates. A predicate that matches nothing costs only an empty column.
| |||
string w_es = "market_and_exchange_names in('E-MINI S&P 500 - CHICAGO MERCANTILE EXCHANGE',"
| |||
"'E-MINI S&P 500 STOCK INDEX - CHICAGO MERCANTILE EXCHANGE')";
| |||
string w_jpy = "market_and_exchange_names in('JAPANESE YEN - CHICAGO MERCANTILE EXCHANGE')";
| |||
string w_eur = "market_and_exchange_names like 'EURO FX -%'";
| |||
string w_gbp = "market_and_exchange_names like 'BRITISH POUND%'";
| |||
string w_cad = "market_and_exchange_names like 'CANADIAN DOLLAR%'";
| |||
string w_aud = "market_and_exchange_names like 'AUSTRALIAN DOLLAR%'";
| |||
string w_nzd = "market_and_exchange_names like 'NZ DOLLAR%'";
| |||
string w_chf = "market_and_exchange_names like 'SWISS FRANC%'";
| |||
string w_btc = "market_and_exchange_names like 'BITCOIN%'";
| |||
| |||
//--- ---------------- equity indices (VIX/USD complex; only SP500 is screened)
| |||
//--- Screened findings preserved as priors, not gates: SP500 vix_chg5/vix/usd_chg5/
| |||
//--- cot_spec_net and USDJPY COT family cleared family-wise + incremental; XAUUSD
| |||
//--- gvz_chg5 (GVZ = gold's own IV) is the campaign's 2nd-strongest incremental
| |||
//--- feature (MI|vol 0.0197 p=0.002, 4.6x vix_chg5 on gold); EURUSD/USDJPY vix_chg5
| |||
//--- screened clean; the macro block screened null everywhere; EIA screened null on
| |||
//--- WTI. All ship regardless - see the policy note above.
| |||
AddSpec("SP500", "SP500;SPX500;US500;USA500;USA500IDX;US_500;SPXUSD;S&P500;SP_500;"
| |||
"SPX;USA500IDXUSD;US500Cash", w_es, 1.0,
| |||
f_risk + ";cot_spec_net;" + f_eia + ";" + f_mac);
| |||
AddSpec("NAS100", "NAS100;NASDAQ;US100;USA100;USATEC;USTEC;NDX100;NQ100;TECH100;"
| |||
"USA100IDX;US_100;NDX;USATECHIDX", "", 1.0,
| |||
f_risk + ";ivol_chg5;ivol;" + f_eia + ";" + f_mac, "VXNCLS");
| |||
AddSpec("US30", "US30;DJ30;DOW30;USA30;WALLSTREET;WS30;DJIUSD;USA30IDX;"
| |||
"US_30;DOWJONES;DJI", "", 1.0,
| |||
f_risk + ";ivol_chg5;ivol;" + f_eia + ";" + f_mac, "VXDCLS");
| |||
AddSpec("US2000", "US2000;RUSSELL2000;RUT;USA2000;RTY;US_2000", "", 1.0,
| |||
f_risk + ";ivol_chg5;ivol;" + f_eia + ";" + f_mac, "RVXCLS");
| |||
AddSpec("DE40", "DE40;GER40;DAX40;DAX;GER30;DE30;GERMANY40;DEU40;GRXEUR;"
| |||
"DE_40;GER_40", "", 1.0, f_risk + ";" + f_eia + ";" + f_mac);
| |||
AddSpec("UK100", "UK100;FTSE100;FTSE;GBR100;UKX;GB100;UK_100;BRXGBP", "", 1.0,
| |||
f_risk + ";" + f_eia + ";" + f_mac);
| |||
AddSpec("JP225", "JP225;NIKKEI;NI225;JPN225;J225;JPXJPY;JP_225", "", 1.0,
| |||
f_risk + ";" + f_eia + ";" + f_mac);
| |||
AddSpec("EU50", "EU50;STOXX50;ESTX50;EUSTX50;SX5E;EUR50;E50EUR", "", 1.0,
| |||
f_risk + ";" + f_eia + ";" + f_mac);
| |||
AddSpec("FR40", "FR40;CAC40;FRA40;CAC;FRXEUR", "", 1.0, f_risk + ";" + f_eia + ";" + f_mac);
| |||
AddSpec("AU200", "AU200;ASX200;AUS200;SPI200;AU_200;AUXAUD", "", 1.0,
| |||
f_risk + ";" + f_eia + ";" + f_mac);
| |||
AddSpec("HK50", "HK50;HSI;HKG33;HK33;HANGSENG;HKXHKD", "", 1.0,
| |||
f_risk + ";" + f_eia + ";" + f_mac);
| |||
| |||
//--- ---------------- FX majors (full complement: COT + risk complex + EIA + macro;
| |||
//--- no per-instrument IV - EVZ was discontinued 2025-03 and must never be wired)
| |||
AddSpec("EURUSD", "EURUSD;EUR_USD", w_eur, 1.0, f_cot + ";" + f_risk + ";" + f_eia + ";" + f_mac);
| |||
AddSpec("USDJPY", "USDJPY;USD_JPY", w_jpy, -1.0, f_cot + ";" + f_risk + ";" + f_eia + ";" + f_mac);
| |||
AddSpec("GBPUSD", "GBPUSD;GBP_USD", w_gbp, 1.0, f_cot + ";" + f_risk + ";" + f_eia + ";" + f_mac);
| |||
AddSpec("USDCAD", "USDCAD;USD_CAD", w_cad, -1.0, f_cot + ";" + f_risk + ";" + f_eia + ";" + f_mac);
| |||
AddSpec("AUDUSD", "AUDUSD;AUD_USD", w_aud, 1.0, f_cot + ";" + f_risk + ";" + f_eia + ";" + f_mac);
| |||
AddSpec("NZDUSD", "NZDUSD;NZD_USD", w_nzd, 1.0, f_cot + ";" + f_risk + ";" + f_eia + ";" + f_mac);
| |||
AddSpec("USDCHF", "USDCHF;USD_CHF", w_chf, -1.0, f_cot + ";" + f_risk + ";" + f_eia + ";" + f_mac);
| |||
| |||
//--- ---------------- metals (gold/silver COT is in the DISAGGREGATED CFTC dataset -
| |||
//--- different id AND column names - still not wired; silver has no free IV index)
| |||
AddSpec("XAUUSD", "XAUUSD;GOLD;XAU_USD;GOLDUSD", "", 1.0,
| |||
f_risk + ";ivol_chg5;ivol;" + f_eia + ";" + f_mac, "GVZCLS");
| |||
AddSpec("XAGUSD", "XAGUSD;SILVER;XAG_USD;SILVERUSD", "", 1.0,
| |||
f_risk + ";" + f_eia + ";" + f_mac);
| |||
| |||
//--- ---------------- energy
| |||
AddSpec("XTIUSD", "XTIUSD;USOIL;WTI;CRUDE;USCRUDE;OIL;WTICOUSD;"
| |||
"OILUSD;LIGHTCMDUSD;CRUDEOIL", "", 1.0,
| |||
f_risk + ";ivol_chg5;ivol;" + f_eia + ";" + f_mac, "OVXCLS");
| |||
AddSpec("XBRUSD", "XBRUSD;UKOIL;BRENT;UKOUSD;BCOUSD;BRENTCMDUSD", "", 1.0,
| |||
f_risk + ";ivol_chg5;ivol;" + f_eia + ";" + f_mac, "OVXCLS");
| |||
AddSpec("NATGAS", "NATGAS;NGAS;XNGUSD;NATURALGAS", "", 1.0,
| |||
f_risk + ";" + f_eia + ";" + f_mac);
| |||
| |||
//--- ---------------- crypto
| |||
AddSpec("BTCUSD", "BTCUSD;BITCOIN;XBTUSD;BTC_USD", w_btc, 1.0,
| |||
f_cot + ";" + f_risk + ";" + f_eia + ";" + f_mac);
| |||
}
| |||
| |||
//--- symbol_map.cfg: one "BROKERSYMBOL=CANONICAL" per line, written by the mapping
| |||
//--- dialog. "NONE" records a deliberate decline so the dialog never nags again.
| |||
void LoadUserMap(void)
| |||
{
| |||
if(m_userMapLoaded)
| |||
return;
| |||
m_userMapLoaded = true;
| |||
int h = FileOpen(ALTFETCH_DIR + "symbol_map.cfg", FILE_READ | FILE_TXT | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_COMMON);
| |||
if(h == INVALID_HANDLE)
| |||
return;
| |||
while(!FileIsEnding(h))
| |||
{
| |||
string line = FileReadString(h);
| |||
StringTrimLeft(line);
| |||
StringTrimRight(line);
| |||
if(line == "" || StringGetCharacter(line, 0) == '#')
| |||
continue;
| |||
string kv[];
| |||
if(StringSplit(line, '=', kv) != 2)
| |||
continue;
| |||
int n = ArraySize(m_userFrom);
| |||
ArrayResize(m_userFrom, n + 1);
| |||
ArrayResize(m_userTo, n + 1);
| |||
m_userFrom[n] = kv[0];
| |||
m_userTo[n] = kv[1];
| |||
}
| |||
FileClose(h);
| |||
if(ArraySize(m_userFrom) > 0)
| |||
PrintFormat("AltDataFetch: %d user symbol mapping(s) loaded from symbol_map.cfg.",
| |||
ArraySize(m_userFrom));
| |||
}
| |||
| |||
//--- exact, then prefix, over one row's alias list
| |||
bool AliasMatches(int spec, string symbol, bool prefixPass)
| |||
{
| |||
string a[];
| |||
int n = StringSplit(m_specs[spec].aliases, ';', a);
| |||
for(int i = 0; i < n; i++)
| |||
{
| |||
if(a[i] == "")
| |||
continue;
| |||
if(!prefixPass && symbol == a[i])
| |||
return true;
| |||
//--- require the alias to be a strict prefix AND at least 3 chars, so a short
| |||
//--- ticker can never swallow an unrelated symbol
| |||
if(prefixPass && StringLen(a[i]) >= 3 && StringFind(symbol, a[i]) == 0)
| |||
return true;
| |||
}
| |||
return false;
| |||
}
| |||
| |||
public:
| |||
CAltDataCatalog(void) : m_userMapLoaded(false)
| |||
{
| |||
BuildCatalog();
| |||
}
| |||
| |||
int FindSpec(string symbol)
| |||
{
| |||
LoadUserMap();
| |||
//--- a user mapping wins over everything, including a catalog row for the same name
| |||
for(int u = 0; u < ArraySize(m_userFrom); u++)
| |||
{
| |||
if(m_userFrom[u] != symbol)
| |||
continue;
| |||
if(m_userTo[u] == "NONE")
| |||
return -2; // deliberately declined - do not ask again
| |||
for(int i = 0; i < ArraySize(m_specs); i++)
| |||
if(m_specs[i].canonical == m_userTo[u])
| |||
return i;
| |||
PrintFormat("AltDataFetch: symbol_map.cfg maps %s to unknown catalog entry '%s' - ignoring.",
| |||
symbol, m_userTo[u]);
| |||
}
| |||
for(int i = 0; i < ArraySize(m_specs); i++)
| |||
if(AliasMatches(i, symbol, false))
| |||
return i;
| |||
for(int i = 0; i < ArraySize(m_specs); i++)
| |||
if(AliasMatches(i, symbol, true))
| |||
return i;
| |||
return -1; // unknown - the dialog asks the user
| |||
}
| |||
| |||
//--- catalog row by index, by value - callers read it read-only (4 strings + 1 double, cheap copy)
| |||
SAltSymbolSpec Spec(int i)
| |||
{
| |||
SAltSymbolSpec empty;
| |||
return (i >= 0 && i < ArraySize(m_specs)) ? m_specs[i] : empty;
| |||
}
| |||
| |||
bool NeedsMapping(string symbol) { return FindSpec(symbol) == -1; }
| |||
| |||
int CatalogCount(void) { return ArraySize(m_specs); }
| |||
string CatalogName(int i)
| |||
{
| |||
return (i >= 0 && i < ArraySize(m_specs)) ? m_specs[i].canonical : "";
| |||
}
| |||
//--- one-line description for the dropdown: canonical + what it would contribute
| |||
string CatalogLabel(int i)
| |||
{
| |||
if(i < 0 || i >= ArraySize(m_specs))
| |||
return "";
| |||
string f[];
| |||
int n = StringSplit(m_specs[i].features, ';', f);
| |||
return m_specs[i].canonical + " (" + IntegerToString(n) + " features)";
| |||
}
| |||
| |||
//--- Persist the user's answer and apply it immediately. canonical == "NONE" records a
| |||
//--- deliberate decline. Appends to symbol_map.cfg, replacing any previous line for the
| |||
//--- same broker symbol, so the answer survives restarts and a wiped feature folder.
| |||
bool SaveUserMapping(string symbol, string canonical)
| |||
{
| |||
LoadUserMap();
| |||
bool replaced = false;
| |||
for(int i = 0; i < ArraySize(m_userFrom); i++)
| |||
if(m_userFrom[i] == symbol)
| |||
{
| |||
m_userTo[i] = canonical;
| |||
replaced = true;
| |||
}
| |||
if(!replaced)
| |||
{
| |||
int n = ArraySize(m_userFrom);
| |||
ArrayResize(m_userFrom, n + 1);
| |||
ArrayResize(m_userTo, n + 1);
| |||
m_userFrom[n] = symbol;
| |||
m_userTo[n] = canonical;
| |||
}
| |||
int h = FileOpen(ALTFETCH_DIR + "symbol_map.cfg", FILE_WRITE | FILE_TXT | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_COMMON);
| |||
if(h == INVALID_HANDLE)
| |||
{
| |||
PrintFormat("AltDataFetch: cannot write symbol_map.cfg (%d) - the mapping applies to "
| |||
"this session only and will be asked again next attach.", GetLastError());
| |||
return false;
| |||
}
| |||
FileWriteString(h, "# Warrior EA alt-data symbol map: BROKERSYMBOL=CANONICAL (NONE = no alt data)\n");
| |||
for(int i = 0; i < ArraySize(m_userFrom); i++)
| |||
FileWriteString(h, m_userFrom[i] + "=" + m_userTo[i] + "\n");
| |||
FileClose(h);
| |||
PrintFormat("AltDataFetch: %s mapped to %s (saved in symbol_map.cfg).", symbol, canonical);
| |||
return true;
| |||
}
| |||
};
| |||
#endif
|