Warrior_EA/System/AltDataFetch.mqh

1008 lines
43 KiB
MQL5
Raw Permalink Normal View History

//+------------------------------------------------------------------+
//| AltDataFetch.mqh |
//| AnimateDread |
//| missing history at attach time and keeps appending forward while |
//| deployed, so online learning never depends on an external Python |
//| process. research/altdata remains the RESEARCH side (screening, |
//| new-source adjudication); this module maintains the exact same |
//| files in production. |
//+------------------------------------------------------------------+
#include "AltData.mqh"
#include "AtomicFile.mqh"
#define ALTFETCH_DIR "Warrior_EA\\AltData\\"
#define ALTFETCH_RETRY_SECONDS 3600
#define ALTFETCH_GRID_START D'2010.01.01'
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
//--- generic HTTP client (backoff/masking/JSON parse) and the symbol catalog + user-mapping
//--- persistence are separate collaborators - see those files' headers for why each is its own
//--- responsibility. Declared here, after ALTFETCH_DIR, since CAltDataCatalog's ctor path uses it.
#include "AltDataHttpClient.mqh"
#include "AltDataCatalog.mqh"
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
//--- one raw series: weekly COT positioning, a daily FRED series, or the EIA WPSR block
struct SAltRawSeries
{
datetime date[]; // observation date, ascending
datetime published[];
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
double v1[]; // FRED: value | COT: open interest | EIA: crude stocks ex-SPR
double v2[]; // COT: spec long | EIA: field production
double v3[]; // COT: spec short | EIA: refinery utilization %
int rows;
};
//+------------------------------------------------------------------+
//| Append one empty row and return its index. |
//| |
//| The five arrays are ONE record split across five buffers, so a |
//| resize missed on any of them reads out of range on the next |
//| append rather than failing here. Callers fill the values they |
//| have; the rest stay 0. |
//+------------------------------------------------------------------+
int AltSeriesAppend(SAltRawSeries &s, const datetime date, const datetime published)
{
int n = s.rows;
ArrayResize(s.date, n + 1);
ArrayResize(s.published, n + 1);
ArrayResize(s.v1, n + 1);
ArrayResize(s.v2, n + 1);
ArrayResize(s.v3, n + 1);
s.date[n] = date;
s.published[n] = published;
s.v1[n] = 0.0;
s.v2[n] = 0.0;
s.v3[n] = 0.0;
s.rows = n + 1;
return n;
}
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
//--- Whole-file raw-series cache load/save (concern separated from the fetch pipelines that use
//--- it): pure functions, no member state - just the SAltRawSeries <-> ';'-delimited CSV mapping.
bool AltRawLoad(string name, SAltRawSeries &s, int cols)
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
{
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
s.rows = 0;
int h = FileOpen(ALTFETCH_DIR + name, FILE_READ | FILE_TXT | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_COMMON);
if(h == INVALID_HANDLE)
return false;
FileReadString(h); // header
int cap = 2048;
ArrayResize(s.date, cap);
ArrayResize(s.published, cap);
ArrayResize(s.v1, cap);
ArrayResize(s.v2, cap);
ArrayResize(s.v3, cap);
while(!FileIsEnding(h))
{
string line = FileReadString(h);
string f[];
if(StringSplit(line, ';', f) < cols + 2)
continue;
if(s.rows >= cap)
{
cap = cap * 3 / 2;
ArrayResize(s.date, cap);
ArrayResize(s.published, cap);
ArrayResize(s.v1, cap);
ArrayResize(s.v2, cap);
ArrayResize(s.v3, cap);
}
s.date[s.rows] = StringToTime(f[0]);
s.published[s.rows] = StringToTime(f[1]);
s.v1[s.rows] = StringToDouble(f[2]);
s.v2[s.rows] = cols > 1 ? StringToDouble(f[3]) : 0.0;
s.v3[s.rows] = cols > 2 ? StringToDouble(f[4]) : 0.0;
s.rows++;
}
FileClose(h);
return s.rows > 0;
}
//--- Whole-file rewrites go through System\AtomicFile.mqh's temp + FileMove swap: multiple
//--- charts share these caches (raw_VIXCLS serves every symbol; two timeframes of one symbol
//--- share all of them), and a reader hitting a truncate-then-write mid-flight parses a torn file.
bool AltRawSave(string name, SAltRawSeries &s, int cols, string header)
{
string tmpName = "";
int h = AtomicWriteBegin(ALTFETCH_DIR + name, FILE_COMMON, tmpName, FILE_TXT | FILE_ANSI);
if(h == INVALID_HANDLE)
{
PrintFormat("AltDataFetch: cannot write %s (%d)", tmpName, GetLastError());
return false;
}
FileWriteString(h, header + "\n");
for(int i = 0; i < s.rows; i++)
{
string line = TimeToString(s.date[i], TIME_DATE) + ";" + TimeToString(s.published[i], TIME_DATE) + ";"
+ DoubleToString(s.v1[i], 6);
if(cols > 1)
line += ";" + DoubleToString(s.v2[i], 6);
if(cols > 2)
line += ";" + DoubleToString(s.v3[i], 6);
FileWriteString(h, line + "\n");
}
return AtomicWriteEnd(h, ALTFETCH_DIR + name, tmpName, FILE_COMMON, true, __FUNCTION__);
}
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
class CAltDataFetch
{
protected:
string m_fredKey;
bool m_keyLoaded;
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
string m_eiaKey;
bool m_eiaWarned;
feat(altdata): GEX forward recorder (CBOE delayed-quotes CDN, no key) Option open interest is a snapshot source - no free history exists anywhere - so the series only accrues from the day recording starts. That is why this ships BEFORE the redeploy: every day the EA is not running is a day of history that cannot be recovered later. Records one row per weekday after 21:00 UTC to gex_{CANONICAL}.csv: net/call/put dollar GEX per 1% move, call and put OI, the three nearest expiries and the front expiry code. Feeds NOTHING - wiring a feature that is missing across ~100% of the training sample would waste input width and hand batch-norm a constant. It becomes a screening candidate at ~250 rows, gated like every other feature. Thesis: dealer gamma is a RANGE mechanism (long gamma -> hedging sells rallies and buys dips, range compresses; short gamma amplifies both ways), and range is this project's one proven channel. Verified in situ against the live SPX chain before writing any MQL5: 29,362 contracts, 20,993 with nonzero gamma, 54 expiries, total +90.7 Bn/1% (calls +305.7, puts -215.0), and 100% of net GEX inside 5% of spot. The CDN publishes per-contract gamma directly, so no pricing model - and no model risk - enters the recorded data. Also verified the CDN does NOT gate on User-Agent (the old "CBOE is UA-gated" note in DESIGN.md was a different CBOE path), so plain WebRequest reaches it. Dropped a zero-gamma "flip level" field: the probe returned a crossing above spot while total GEX was strongly positive, which is incoherent - a static gamma snapshot cannot give a flip level without repricing. Recording a plausible-looking wrong number is worse than recording nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 16:30:39 -04:00
datetime m_lastGexDate; // last UTC day whose GEX row is already on disk
bool m_gexDateLoaded;
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
datetime m_lastAttempt[16]; // per-source retry throttle: 0=VIX 1=USD 2=COT 3=EIA 4=IVOL
// 5=DGS10 6=T10Y2Y 7=T5YIE 8=DFF 9=ECBDFR 10=CPI 11=UNRATE
//--- ---------------------------------------------------------- utils
//--- Shared contract behind FredKey()/EiaKey(): the EA input is the key's home - it travels
//--- with the EA and survives the Common\Files wipe that starts every fresh test (the
//--- 2026-08-16 incident: keys.txt was wiped with the folder, FredKey() returned "" with NO
//--- log line, COT updated but FRED never ran and the SP500 rebuild - gated on all three raws -
//--- never fired). keys.txt is only the fallback for a blanked input, re-read on every
//--- hourly-throttled attempt until a key is found; warnedFlag means "warned once", never fail
//--- silently. fileMissingMsg == "" means "no distinct message for a missing file" (EIA's
//--- shape) - the missing-file case then just falls through to notFoundMsg below.
string LoadCommonKey(const string inputValue, string &cache, bool &warnedFlag,
const string prefix, const string fileMissingMsg, const string notFoundMsg)
{
if(inputValue != "")
return inputValue;
if(cache != "")
return cache;
int h = FileOpen(ALTFETCH_DIR + "keys.txt", FILE_READ | FILE_TXT | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_COMMON);
if(h == INVALID_HANDLE)
{
if(fileMissingMsg != "")
{
if(!warnedFlag)
Print(fileMissingMsg);
warnedFlag = true;
return "";
}
}
else
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
{
while(!FileIsEnding(h))
{
string line = FileReadString(h);
if(StringFind(line, prefix) == 0)
cache = StringSubstr(line, StringLen(prefix));
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
}
FileClose(h);
}
if(cache == "" && !warnedFlag)
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
{
warnedFlag = true;
Print(notFoundMsg);
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
}
return cache;
}
string FredKey(void)
{
return LoadCommonKey(FredApiKey, m_fredKey, m_keyLoaded, "fred=",
"AltDataFetch: the FredApiKey input is blank and " + ALTFETCH_DIR + "keys.txt "
"does not exist in Common\\Files - FRED features (VIX, USD index) cannot update, "
"so the SP500/XAUUSD feature files will not build. Set the FredApiKey input "
"(free key: fred.stlouisfed.org) or create keys.txt with one line: fred=<key>. "
"The file is re-checked hourly; no restart needed.",
"AltDataFetch: the FredApiKey input is blank and " + ALTFETCH_DIR + "keys.txt has "
"no 'fred=<key>' line - FRED-based features will not update until one is provided "
"(free key: fred.stlouisfed.org).");
}
string EiaKey(void)
{
return LoadCommonKey(EiaApiKey, m_eiaKey, m_eiaWarned, "eia=", "",
"AltDataFetch: the EiaApiKey input is blank and no 'eia=<key>' line exists in " +
ALTFETCH_DIR + "keys.txt - the EIA feature columns will stay empty (panel 0-fills; "
"free key: eia.gov/opendata). Re-checked hourly; no restart needed.");
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
}
//--- Shared gate at the top of every per-source Update method (Fred/Cot/Eia): skip while the
//--- cached tail is still fresh (within staleDays), then skip if a fetch attempt for this
//--- source's throttle slot already happened within ALTFETCH_RETRY_SECONDS - stamping the
//--- attempt time only when neither check skips, so a genuine fetch below is exactly one
//--- attempt per hour. UpdateGex has its own unrelated day/hour gate and does not use this.
bool ShouldAttemptFetch(const SAltRawSeries &s, int staleDays, int throttleSlot, datetime now)
{
if(s.rows > 0 && now - s.date[s.rows - 1] < staleDays * 86400)
return false;
if(m_lastAttempt[throttleSlot] != 0 && now - m_lastAttempt[throttleSlot] < ALTFETCH_RETRY_SECONDS)
return false;
m_lastAttempt[throttleSlot] = now;
return true;
}
datetime SaturdayAfter(datetime reportDate)
{
MqlDateTime st;
TimeToStruct(reportDate, st);
int days = (6 - st.day_of_week + 7) % 7; // MQL5: 0=Sunday .. 6=Saturday
if(days == 0)
days = 7;
return reportDate + days * 86400;
}
//--- ------------------------------------------------- FRED source appends observations after
//--- the cached tail; full history when the cache is empty vmin/vmax: per-series plausibility
//--- band. Rejected rows are counted and reported, never silently dropped.
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
bool UpdateFred(string seriesId, SAltRawSeries &s, int throttleSlot,
int staleDays = 4, double vmin = -1e18, double vmax = 1e18)
{
datetime now = TimeCurrent();
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
//--- daily series: current until ~2 business days behind (weekends/holidays).
//--- monthly series pass staleDays ~32 so a 30-day-old latest print does not
//--- trigger a pointless fetch attempt every hour for weeks.
if(!ShouldAttemptFetch(s, staleDays, throttleSlot, now))
return false;
string key = FredKey();
if(key == "")
return false;
string url = "https://api.stlouisfed.org/fred/series/observations?series_id=" + seriesId +
"&api_key=" + key + "&file_type=json&limit=100000";
//--- ALWAYS bound the start. 2005 leaves 5y of lookback margin ahead of the grid for the
//--- longest window (3y COT percentile analog).
fix(altdata): first live fetch findings - key leak masked, EIA UA, bounded FRED backfill Log review of the 18:12 attach. The wiring works: VIX, dollar index, COT, all seven macro series fetched and SP500_D1.csv rebuilt with its full 13 features on the first pass. Three findings from the same log, fixed: KEY LEAK: the EIA failure line echoed the first 80 chars of the URL, which included most of the api_key. Every URL-echoing error path now goes through MaskUrl(). The key itself is unchanged - it was printed to a local journal, not transmitted - but rotate it if that log ever leaves the machine. EIA HTTP 1003: an MT5 transport-layer code, not a server response. Requests now carry a User-Agent (gateways reject empty-UA at the edge; the CBOE probe showed no-UA is fine THERE, but EIA fronts differ) and 1xxx codes are explained in the log line. Retries were already hourly. UNBOUNDED BACKFILL: an empty cache fetched full series history - CPIAUCNS goes back to 1913, whose pre-1970 dates are outside MQL5 datetime range and whose 1913-era levels sat below the plausibility band, producing 157 scary-but-meaningless REJECTED lines. All FRED fetches now start at 2005 (5y of lookback margin ahead of the 2010 grid). DTWEXBGS staleness horizon raised to 10 days to match its weekly H.10 publication lag. Also confirmed from the log: the running build predates the H4 fallback, so the H4 panels still show 0 features - resolved by the recompile this commit requires anyway. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 18:20:39 -04:00
string startDate = (s.rows > 0)
? TimeToString(s.date[s.rows - 1] + 86400, TIME_DATE)
: "2005.01.01";
StringReplace(startDate, ".", "-"); // TimeToString is yyyy.mm.dd; FRED wants ISO dashes
url += "&observation_start=" + startDate;
string body;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
if(!m_http.HttpGet(url, body))
return false;
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
int pos = 0, added = 0, rejected = 0;
while(true)
{
int e1;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
string d = m_http.JsonField(body, "date", pos, e1);
if(d == "" || e1 < 0)
break;
int e2;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
string v = m_http.JsonField(body, "value", e1, e2);
pos = (e2 > e1 ? e2 : e1);
if(v == "" || v == ".")
continue;
datetime dt = StringToTime(d); // accepts yyyy-mm-dd
if(dt <= 0 || (s.rows > 0 && dt <= s.date[s.rows - 1]))
continue;
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
double val = StringToDouble(v);
if(val < vmin || val > vmax || !MathIsValidNumber(val))
{
rejected++;
continue;
}
int n = AltSeriesAppend(s, dt, dt + 86400); // unrevised daily: knowable next day
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
s.v1[n] = val;
added++;
}
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
if(rejected > 0)
PrintFormat("AltDataFetch: %s REJECTED %d value(s) outside the plausibility band "
"[%g, %g] - transport corruption or format drift; investigate if repeated.",
seriesId, rejected, vmin, vmax);
if(added > 0)
{
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
AltRawSave("raw_" + seriesId + ".csv", s, 1, "date;published;value");
PrintFormat("AltDataFetch: %s +%d rows (through %s)", seriesId, added,
TimeToString(s.date[s.rows - 1], TIME_DATE));
}
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
else if(s.rows == 0)
PrintFormat("AltDataFetch: %s returned a response with ZERO usable observations and "
"the cache is empty - series id wrong or API format changed. Features "
"depending on it will stay empty (panel 0-fills).", seriesId);
return added > 0;
}
//--- -------------------------------------------------- COT source
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
bool UpdateCot(string cacheName, string cotWhere, SAltRawSeries &s, int throttleSlot)
{
datetime now = TimeCurrent();
//--- weekly: a new Tuesday report exists once the last one is >7d old; allow 3 extra
//--- days before nagging the API (Friday release + weekend)
if(!ShouldAttemptFetch(s, 10, throttleSlot, now))
return false;
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
string where = cotWhere;
if(s.rows > 0)
{
string lastDate = TimeToString(s.date[s.rows - 1], TIME_DATE);
StringReplace(lastDate, ".", "-"); // ISO for the SoQL timestamp comparison
where += " AND report_date_as_yyyy_mm_dd > '" + lastDate + "'";
}
string url = "https://publicreporting.cftc.gov/resource/gpe5-46if.json?%24select=" +
"report_date_as_yyyy_mm_dd,open_interest_all,lev_money_positions_long,lev_money_positions_short" +
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
"&%24where=" + m_http.UrlEncodePart(where) +
"&%24order=report_date_as_yyyy_mm_dd&%24limit=50000";
string body;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
if(!m_http.HttpGet(url, body))
return false;
int pos = 0, added = 0;
while(true)
{
int e1;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
string d = m_http.JsonField(body, "report_date_as_yyyy_mm_dd", pos, e1);
if(d == "" || e1 < 0)
break;
int e2, e3, e4;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
string oi = m_http.JsonField(body, "open_interest_all", e1, e2);
string ll = m_http.JsonField(body, "lev_money_positions_long", e2, e3);
string ls = m_http.JsonField(body, "lev_money_positions_short", e3, e4);
pos = (e4 > e1 ? e4 : e1);
datetime dt = StringToTime(StringSubstr(d, 0, 10));
if(dt <= 0 || oi == "" || ll == "" || ls == "")
continue;
if(s.rows > 0 && dt == s.date[s.rows - 1])
{
//--- same report date from a second contract-name variant: keep the larger-OI row
if(StringToDouble(oi) > s.v1[s.rows - 1])
{
s.v1[s.rows - 1] = StringToDouble(oi);
s.v2[s.rows - 1] = StringToDouble(ll);
s.v3[s.rows - 1] = StringToDouble(ls);
}
continue;
}
if(s.rows > 0 && dt < s.date[s.rows - 1])
continue;
int n = AltSeriesAppend(s, dt, SaturdayAfter(dt));
s.v1[n] = StringToDouble(oi);
s.v2[n] = StringToDouble(ll);
s.v3[n] = StringToDouble(ls);
added++;
}
if(added > 0)
{
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
AltRawSave(cacheName, s, 3, "date;published;oi;lev_long;lev_short");
PrintFormat("AltDataFetch: %s +%d weekly reports (through %s)", cacheName, added,
TimeToString(s.date[s.rows - 1], TIME_DATE));
}
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
else if(s.rows == 0)
PrintFormat("AltDataFetch: COT query for %s returned ZERO reports on an empty cache - "
"the contract-name predicate probably matches nothing at the endpoint "
"(the like-predicates for non-SP500/JPY contracts are unverified; see the "
"catalog). COT features stay empty (panel 0-fills) until it is corrected.",
cacheName);
return added > 0;
}
//--- ---------------------------------------------------- EIA source Weekly Petroleum Status
//--- Report via the EIA v2 API, three series in one call (2010->now x 3 ~ 2,600 rows, under the
//--- API's 5000-row page - revisit if series are added).
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
bool UpdateEia(SAltRawSeries &s, int throttleSlot)
{
datetime now = TimeCurrent();
//--- weekly cadence, same nag guard as COT
if(!ShouldAttemptFetch(s, 10, throttleSlot, now))
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
return false;
string key = EiaKey();
if(key == "")
return false;
string startDate = TimeToString(s.rows > 0 ? s.date[s.rows - 1] + 86400 : ALTFETCH_GRID_START, TIME_DATE);
StringReplace(startDate, ".", "-"); // TimeToString is yyyy.mm.dd; the API wants ISO dashes
string url = "https://api.eia.gov/v2/petroleum/sum/sndw/data/?api_key=" + key +
"&frequency=weekly&data%5B0%5D=value" +
"&facets%5Bseries%5D%5B0%5D=WCESTUS1" + // crude stocks ex-SPR
"&facets%5Bseries%5D%5B1%5D=WCRFPUS2" + // field production
"&facets%5Bseries%5D%5B2%5D=WPULEUS3" + // refinery utilization %
"&sort%5B0%5D%5Bcolumn%5D=period&sort%5B0%5D%5Bdirection%5D=asc" +
"&length=5000&start=" + startDate;
string body;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
if(!m_http.HttpGet(url, body))
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
return false;
int pos = 0, added = 0;
while(true)
{
int e1, e2, e3;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
string d = m_http.JsonField(body, "period", pos, e1);
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
if(d == "" || e1 < 0)
break;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
string sid = m_http.JsonField(body, "series", e1, e2);
string v = m_http.JsonField(body, "value", (e2 > e1 ? e2 : e1), e3);
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
pos = (e3 > e1 ? e3 : (e2 > e1 ? e2 : e1));
datetime dt = StringToTime(d); // accepts yyyy-mm-dd
if(dt <= 0 || sid == "" || v == "")
continue;
if(s.rows > 0 && dt < s.date[s.rows - 1])
continue;
if(s.rows == 0 || dt > s.date[s.rows - 1])
{
int n = AltSeriesAppend(s, dt, dt + 6 * 86400);
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
added++;
}
int r = s.rows - 1;
if(sid == "WCESTUS1") s.v1[r] = StringToDouble(v);
else if(sid == "WCRFPUS2") s.v2[r] = StringToDouble(v);
else if(sid == "WPULEUS3") s.v3[r] = StringToDouble(v);
}
if(added > 0)
{
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
AltRawSave("raw_EIA_WPSR.csv", s, 3, "date;published;stocks;production;utilization");
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
PrintFormat("AltDataFetch: raw_EIA_WPSR.csv +%d weekly reports (through %s)", added,
TimeToString(s.date[s.rows - 1], TIME_DATE));
}
return added > 0;
}
//--- ------------------------------------ GEX forward recorder WHY THIS IS A RECORDER AND NOT A
//--- FEATURE: option open interest is a SNAPSHOT source. Revisit when the file holds a year or
//--- so, then screen it like every other candidate.
feat(altdata): GEX forward recorder (CBOE delayed-quotes CDN, no key) Option open interest is a snapshot source - no free history exists anywhere - so the series only accrues from the day recording starts. That is why this ships BEFORE the redeploy: every day the EA is not running is a day of history that cannot be recovered later. Records one row per weekday after 21:00 UTC to gex_{CANONICAL}.csv: net/call/put dollar GEX per 1% move, call and put OI, the three nearest expiries and the front expiry code. Feeds NOTHING - wiring a feature that is missing across ~100% of the training sample would waste input width and hand batch-norm a constant. It becomes a screening candidate at ~250 rows, gated like every other feature. Thesis: dealer gamma is a RANGE mechanism (long gamma -> hedging sells rallies and buys dips, range compresses; short gamma amplifies both ways), and range is this project's one proven channel. Verified in situ against the live SPX chain before writing any MQL5: 29,362 contracts, 20,993 with nonzero gamma, 54 expiries, total +90.7 Bn/1% (calls +305.7, puts -215.0), and 100% of net GEX inside 5% of spot. The CDN publishes per-contract gamma directly, so no pricing model - and no model risk - enters the recorded data. Also verified the CDN does NOT gate on User-Agent (the old "CBOE is UA-gated" note in DESIGN.md was a different CBOE path), so plain WebRequest reaches it. Dropped a zero-gamma "flip level" field: the probe returned a crossing above spot while total GEX was strongly positive, which is incoherent - a static gamma snapshot cannot give a flip level without repricing. Recording a plausible-looking wrong number is worse than recording nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 16:30:39 -04:00
string CboeTicker(string canonical)
{
if(canonical == "SP500") return "_SPX"; // index options - where dealer positioning concentrates
if(canonical == "NAS100") return "_NDX";
if(canonical == "US30") return "_DJX";
if(canonical == "US2000") return "IWM";
if(canonical == "XAUUSD") return "GLD";
if(canonical == "XAGUSD") return "SLV";
if(canonical == "XTIUSD") return "USO";
if(canonical == "BTCUSD") return "IBIT";
return ""; // no listed-options proxy worth recording
}
//--- last date already in the file, or 0
datetime LastGexDate(string file)
{
int h = FileOpen(file, FILE_READ | FILE_TXT | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_COMMON);
if(h == INVALID_HANDLE)
return 0;
string last = "";
while(!FileIsEnding(h))
{
string line = FileReadString(h);
if(StringLen(line) > 8 && StringFind(line, "date;") != 0)
last = line;
}
FileClose(h);
if(last == "")
return 0;
string f[];
if(StringSplit(last, ';', f) < 1)
return 0;
return StringToTime(f[0]);
}
bool UpdateGex(string canonical)
{
string ticker = CboeTicker(canonical);
if(ticker == "")
return false;
//--- Once per UTC day, after the US cash close (21:00 UTC covers both DST regimes),
//--- and never on a weekend - the chain would just repeat Friday's settled numbers.
MqlDateTime g;
TimeToStruct(TimeGMT(), g);
if(g.day_of_week == 0 || g.day_of_week == 6)
return false;
if(g.hour < 21)
return false;
string file = ALTFETCH_DIR + "gex_" + canonical + ".csv";
datetime today = StringToTime(StringFormat("%04d.%02d.%02d", g.year, g.mon, g.day));
if(!m_gexDateLoaded)
{
m_lastGexDate = LastGexDate(file);
m_gexDateLoaded = true;
}
if(m_lastGexDate >= today)
return false;
//--- Re-read from DISK before spending the download: two charts of the same symbol
//--- (e.g. D1 + H4) each run their own fetcher, and a per-instance cached date would
//--- let the second chart append a duplicate row for the day the first just recorded.
m_lastGexDate = LastGexDate(file);
feat(altdata): GEX forward recorder (CBOE delayed-quotes CDN, no key) Option open interest is a snapshot source - no free history exists anywhere - so the series only accrues from the day recording starts. That is why this ships BEFORE the redeploy: every day the EA is not running is a day of history that cannot be recovered later. Records one row per weekday after 21:00 UTC to gex_{CANONICAL}.csv: net/call/put dollar GEX per 1% move, call and put OI, the three nearest expiries and the front expiry code. Feeds NOTHING - wiring a feature that is missing across ~100% of the training sample would waste input width and hand batch-norm a constant. It becomes a screening candidate at ~250 rows, gated like every other feature. Thesis: dealer gamma is a RANGE mechanism (long gamma -> hedging sells rallies and buys dips, range compresses; short gamma amplifies both ways), and range is this project's one proven channel. Verified in situ against the live SPX chain before writing any MQL5: 29,362 contracts, 20,993 with nonzero gamma, 54 expiries, total +90.7 Bn/1% (calls +305.7, puts -215.0), and 100% of net GEX inside 5% of spot. The CDN publishes per-contract gamma directly, so no pricing model - and no model risk - enters the recorded data. Also verified the CDN does NOT gate on User-Agent (the old "CBOE is UA-gated" note in DESIGN.md was a different CBOE path), so plain WebRequest reaches it. Dropped a zero-gamma "flip level" field: the probe returned a crossing above spot while total GEX was strongly positive, which is incoherent - a static gamma snapshot cannot give a flip level without repricing. Recording a plausible-looking wrong number is worse than recording nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 16:30:39 -04:00
if(m_lastGexDate >= today)
return false;
//--- the SPX chain is ~13 MB: this needs a far longer timeout than the small feeds
string body;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
if(!m_http.HttpGet("https://cdn.cboe.com/api/global/delayed_quotes/options/" + ticker + ".json",
feat(altdata): GEX forward recorder (CBOE delayed-quotes CDN, no key) Option open interest is a snapshot source - no free history exists anywhere - so the series only accrues from the day recording starts. That is why this ships BEFORE the redeploy: every day the EA is not running is a day of history that cannot be recovered later. Records one row per weekday after 21:00 UTC to gex_{CANONICAL}.csv: net/call/put dollar GEX per 1% move, call and put OI, the three nearest expiries and the front expiry code. Feeds NOTHING - wiring a feature that is missing across ~100% of the training sample would waste input width and hand batch-norm a constant. It becomes a screening candidate at ~250 rows, gated like every other feature. Thesis: dealer gamma is a RANGE mechanism (long gamma -> hedging sells rallies and buys dips, range compresses; short gamma amplifies both ways), and range is this project's one proven channel. Verified in situ against the live SPX chain before writing any MQL5: 29,362 contracts, 20,993 with nonzero gamma, 54 expiries, total +90.7 Bn/1% (calls +305.7, puts -215.0), and 100% of net GEX inside 5% of spot. The CDN publishes per-contract gamma directly, so no pricing model - and no model risk - enters the recorded data. Also verified the CDN does NOT gate on User-Agent (the old "CBOE is UA-gated" note in DESIGN.md was a different CBOE path), so plain WebRequest reaches it. Dropped a zero-gamma "flip level" field: the probe returned a crossing above spot while total GEX was strongly positive, which is incoherent - a static gamma snapshot cannot give a flip level without repricing. Recording a plausible-looking wrong number is worse than recording nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 16:30:39 -04:00
body, ALTFETCH_GEX_TIMEOUT_MS))
return false;
if(StringLen(body) < 1000)
{
Print("AltDataFetch: GEX response for " + ticker + " was too short to be a chain - skipping.");
return false;
}
int e0;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
string spotS = m_http.JsonField(body, "current_price", 0, e0); // absent from option rows, so
feat(altdata): GEX forward recorder (CBOE delayed-quotes CDN, no key) Option open interest is a snapshot source - no free history exists anywhere - so the series only accrues from the day recording starts. That is why this ships BEFORE the redeploy: every day the EA is not running is a day of history that cannot be recovered later. Records one row per weekday after 21:00 UTC to gex_{CANONICAL}.csv: net/call/put dollar GEX per 1% move, call and put OI, the three nearest expiries and the front expiry code. Feeds NOTHING - wiring a feature that is missing across ~100% of the training sample would waste input width and hand batch-norm a constant. It becomes a screening candidate at ~250 rows, gated like every other feature. Thesis: dealer gamma is a RANGE mechanism (long gamma -> hedging sells rallies and buys dips, range compresses; short gamma amplifies both ways), and range is this project's one proven channel. Verified in situ against the live SPX chain before writing any MQL5: 29,362 contracts, 20,993 with nonzero gamma, 54 expiries, total +90.7 Bn/1% (calls +305.7, puts -215.0), and 100% of net GEX inside 5% of spot. The CDN publishes per-contract gamma directly, so no pricing model - and no model risk - enters the recorded data. Also verified the CDN does NOT gate on User-Agent (the old "CBOE is UA-gated" note in DESIGN.md was a different CBOE path), so plain WebRequest reaches it. Dropped a zero-gamma "flip level" field: the probe returned a crossing above spot while total GEX was strongly positive, which is incoherent - a static gamma snapshot cannot give a flip level without repricing. Recording a plausible-looking wrong number is worse than recording nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 16:30:39 -04:00
if(spotS == "") // a plain scan finds the header one
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
spotS = m_http.JsonField(body, "close", 0, e0);
feat(altdata): GEX forward recorder (CBOE delayed-quotes CDN, no key) Option open interest is a snapshot source - no free history exists anywhere - so the series only accrues from the day recording starts. That is why this ships BEFORE the redeploy: every day the EA is not running is a day of history that cannot be recovered later. Records one row per weekday after 21:00 UTC to gex_{CANONICAL}.csv: net/call/put dollar GEX per 1% move, call and put OI, the three nearest expiries and the front expiry code. Feeds NOTHING - wiring a feature that is missing across ~100% of the training sample would waste input width and hand batch-norm a constant. It becomes a screening candidate at ~250 rows, gated like every other feature. Thesis: dealer gamma is a RANGE mechanism (long gamma -> hedging sells rallies and buys dips, range compresses; short gamma amplifies both ways), and range is this project's one proven channel. Verified in situ against the live SPX chain before writing any MQL5: 29,362 contracts, 20,993 with nonzero gamma, 54 expiries, total +90.7 Bn/1% (calls +305.7, puts -215.0), and 100% of net GEX inside 5% of spot. The CDN publishes per-contract gamma directly, so no pricing model - and no model risk - enters the recorded data. Also verified the CDN does NOT gate on User-Agent (the old "CBOE is UA-gated" note in DESIGN.md was a different CBOE path), so plain WebRequest reaches it. Dropped a zero-gamma "flip level" field: the probe returned a crossing above spot while total GEX was strongly positive, which is incoherent - a static gamma snapshot cannot give a flip level without repricing. Recording a plausible-looking wrong number is worse than recording nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 16:30:39 -04:00
double spot = StringToDouble(spotS);
if(spot <= 0.0)
{
Print("AltDataFetch: GEX " + ticker + " - no usable spot price in the response, skipping.");
return false;
}
//--- per-expiry accumulation (54 expiries on SPX; the cap is generous)
int expCode[128];
double expGex[128];
int nExp = 0;
double gexCall = 0.0, gexPut = 0.0;
double oiCall = 0.0, oiPut = 0.0;
int rows = 0;
int pos = 0;
//--- Row layout is option, ..., open_interest, volume, delta, gamma - so one forward
//--- sweep reads each row's three fields in the order they appear.
while(true)
{
int e1;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
string sym = m_http.JsonField(body, "option", pos, e1);
feat(altdata): GEX forward recorder (CBOE delayed-quotes CDN, no key) Option open interest is a snapshot source - no free history exists anywhere - so the series only accrues from the day recording starts. That is why this ships BEFORE the redeploy: every day the EA is not running is a day of history that cannot be recovered later. Records one row per weekday after 21:00 UTC to gex_{CANONICAL}.csv: net/call/put dollar GEX per 1% move, call and put OI, the three nearest expiries and the front expiry code. Feeds NOTHING - wiring a feature that is missing across ~100% of the training sample would waste input width and hand batch-norm a constant. It becomes a screening candidate at ~250 rows, gated like every other feature. Thesis: dealer gamma is a RANGE mechanism (long gamma -> hedging sells rallies and buys dips, range compresses; short gamma amplifies both ways), and range is this project's one proven channel. Verified in situ against the live SPX chain before writing any MQL5: 29,362 contracts, 20,993 with nonzero gamma, 54 expiries, total +90.7 Bn/1% (calls +305.7, puts -215.0), and 100% of net GEX inside 5% of spot. The CDN publishes per-contract gamma directly, so no pricing model - and no model risk - enters the recorded data. Also verified the CDN does NOT gate on User-Agent (the old "CBOE is UA-gated" note in DESIGN.md was a different CBOE path), so plain WebRequest reaches it. Dropped a zero-gamma "flip level" field: the probe returned a crossing above spot while total GEX was strongly positive, which is incoherent - a static gamma snapshot cannot give a flip level without repricing. Recording a plausible-looking wrong number is worse than recording nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 16:30:39 -04:00
if(sym == "" || e1 < 0)
break;
int e2, e3;
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
string oiS = m_http.JsonField(body, "open_interest", e1, e2);
string gS = m_http.JsonField(body, "gamma", (e2 > e1 ? e2 : e1), e3);
feat(altdata): GEX forward recorder (CBOE delayed-quotes CDN, no key) Option open interest is a snapshot source - no free history exists anywhere - so the series only accrues from the day recording starts. That is why this ships BEFORE the redeploy: every day the EA is not running is a day of history that cannot be recovered later. Records one row per weekday after 21:00 UTC to gex_{CANONICAL}.csv: net/call/put dollar GEX per 1% move, call and put OI, the three nearest expiries and the front expiry code. Feeds NOTHING - wiring a feature that is missing across ~100% of the training sample would waste input width and hand batch-norm a constant. It becomes a screening candidate at ~250 rows, gated like every other feature. Thesis: dealer gamma is a RANGE mechanism (long gamma -> hedging sells rallies and buys dips, range compresses; short gamma amplifies both ways), and range is this project's one proven channel. Verified in situ against the live SPX chain before writing any MQL5: 29,362 contracts, 20,993 with nonzero gamma, 54 expiries, total +90.7 Bn/1% (calls +305.7, puts -215.0), and 100% of net GEX inside 5% of spot. The CDN publishes per-contract gamma directly, so no pricing model - and no model risk - enters the recorded data. Also verified the CDN does NOT gate on User-Agent (the old "CBOE is UA-gated" note in DESIGN.md was a different CBOE path), so plain WebRequest reaches it. Dropped a zero-gamma "flip level" field: the probe returned a crossing above spot while total GEX was strongly positive, which is incoherent - a static gamma snapshot cannot give a flip level without repricing. Recording a plausible-looking wrong number is worse than recording nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 16:30:39 -04:00
pos = (e3 > e1 ? e3 : (e2 > e1 ? e2 : e1));
double oi = StringToDouble(oiS);
double gam = StringToDouble(gS);
if(oi <= 0.0 || gam == 0.0)
continue; // no exposure - skip the string work entirely
//--- OCC symbol: <root><YYMMDD><C|P><strike*1000, 8 digits>, so the tail is fixed
//--- width regardless of how long the root is (SPX, SPXW, IWM, ...).
int len = StringLen(sym);
if(len < 15)
continue;
string kind = StringSubstr(sym, len - 9, 1);
int expiry = (int)StringToInteger(StringSubstr(sym, len - 15, 6));
//--- dollar gamma per 1% move: gamma x OI x 100 (contract multiplier) x S^2 x 0.01
double dollar = gam * oi * 100.0 * spot * spot * 0.01;
if(kind == "C")
{
gexCall += dollar;
oiCall += oi;
}
else
{
gexPut -= dollar; // dealers short puts: opposite sign convention
oiPut += oi;
dollar = -dollar;
}
int slot = -1;
for(int i = 0; i < nExp; i++)
if(expCode[i] == expiry)
{
slot = i;
break;
}
if(slot < 0 && nExp < 128)
{
slot = nExp++;
expCode[slot] = expiry;
expGex[slot] = 0.0;
}
if(slot >= 0)
expGex[slot] += dollar;
rows++;
}
if(rows == 0)
{
Print("AltDataFetch: GEX " + ticker + " - parsed 0 contracts with exposure, skipping "
"(the CDN response format may have changed).");
return false;
}
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
//--- Partial-chain guard: a truncated 13 MB download parses cleanly and yields a
//--- plausible-but-wrong GEX. A day-over-day contract-count collapse (>50%) is the
//--- fingerprint of truncation, not of markets - OI does not halve overnight.
int prevRows = LastGexRowCount(file);
if(prevRows > 0 && rows < prevRows / 2)
{
PrintFormat("AltDataFetch: GEX %s - only %d contracts vs %d yesterday (<50%%): response "
"looks TRUNCATED, not recording. Will retry on the next timer pass.",
ticker, rows, prevRows);
return false;
}
feat(altdata): GEX forward recorder (CBOE delayed-quotes CDN, no key) Option open interest is a snapshot source - no free history exists anywhere - so the series only accrues from the day recording starts. That is why this ships BEFORE the redeploy: every day the EA is not running is a day of history that cannot be recovered later. Records one row per weekday after 21:00 UTC to gex_{CANONICAL}.csv: net/call/put dollar GEX per 1% move, call and put OI, the three nearest expiries and the front expiry code. Feeds NOTHING - wiring a feature that is missing across ~100% of the training sample would waste input width and hand batch-norm a constant. It becomes a screening candidate at ~250 rows, gated like every other feature. Thesis: dealer gamma is a RANGE mechanism (long gamma -> hedging sells rallies and buys dips, range compresses; short gamma amplifies both ways), and range is this project's one proven channel. Verified in situ against the live SPX chain before writing any MQL5: 29,362 contracts, 20,993 with nonzero gamma, 54 expiries, total +90.7 Bn/1% (calls +305.7, puts -215.0), and 100% of net GEX inside 5% of spot. The CDN publishes per-contract gamma directly, so no pricing model - and no model risk - enters the recorded data. Also verified the CDN does NOT gate on User-Agent (the old "CBOE is UA-gated" note in DESIGN.md was a different CBOE path), so plain WebRequest reaches it. Dropped a zero-gamma "flip level" field: the probe returned a crossing above spot while total GEX was strongly positive, which is incoherent - a static gamma snapshot cannot give a flip level without repricing. Recording a plausible-looking wrong number is worse than recording nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 16:30:39 -04:00
//--- three nearest expiries, by selection (nExp is tiny)
double front[3] = {0.0, 0.0, 0.0};
int frontCode = 0, prevCode = 0;
for(int k = 0; k < 3; k++)
{
int best = -1;
for(int i = 0; i < nExp; i++)
{
if(expCode[i] <= prevCode)
continue;
if(best < 0 || expCode[i] < expCode[best])
best = i;
}
if(best < 0)
break;
front[k] = expGex[best] / 1e9;
prevCode = expCode[best];
if(k == 0)
frontCode = prevCode;
}
bool isNew = !FileIsExist(file, FILE_COMMON);
int h = FileOpen(file, FILE_READ | FILE_WRITE | FILE_TXT | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_COMMON);
if(h == INVALID_HANDLE)
{
PrintFormat("AltDataFetch: cannot write %s (%d) - GEX not recorded today.", file, GetLastError());
return false;
}
if(isNew)
FileWriteString(h, "date;recorded_utc;spot;gex_total;gex_call;gex_put;oi_call;oi_put;"
"gex_exp1;gex_exp2;gex_exp3;expiry1;n_expiry;n_rows\n");
FileSeek(h, 0, SEEK_END);
FileWriteString(h, StringFormat("%s;%s;%.4f;%.6f;%.6f;%.6f;%.0f;%.0f;%.6f;%.6f;%.6f;%d;%d;%d\n",
TimeToString(today, TIME_DATE),
TimeToString(TimeGMT(), TIME_DATE | TIME_MINUTES),
spot, (gexCall + gexPut) / 1e9, gexCall / 1e9, gexPut / 1e9,
oiCall, oiPut, front[0], front[1], front[2],
frontCode, nExp, rows));
FileClose(h);
m_lastGexDate = today;
//--- Progress matters here: the file is useless until it is long enough to screen, and
//--- the only way to know how far along it is, is to say so.
int have = 1;
datetime probe = LastGexDate(file);
if(probe > 0)
have = CountGexRows(file);
PrintFormat("AltDataFetch: GEX %s recorded - total %+.2f Bn/1%% (calls %+.2f, puts %+.2f), "
"%d contracts, %d expiries, spot %.2f. %d day(s) banked; this is a FORWARD "
"RECORDER - no free history exists, so it feeds no model until there is enough "
"to screen (~250 trading days).",
ticker, (gexCall + gexPut) / 1e9, gexCall / 1e9, gexPut / 1e9, rows, nExp, spot, have);
return true;
}
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
//--- n_rows (field 14) of the last data line - the previous session's contract count
int LastGexRowCount(string file)
{
int h = FileOpen(file, FILE_READ | FILE_TXT | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_COMMON);
if(h == INVALID_HANDLE)
return 0;
string last = "";
while(!FileIsEnding(h))
{
string line = FileReadString(h);
if(StringLen(line) > 8 && StringFind(line, "date;") != 0)
last = line;
}
FileClose(h);
if(last == "")
return 0;
string f[];
if(StringSplit(last, ';', f) < 14)
return 0;
return (int)StringToInteger(f[13]);
}
feat(altdata): GEX forward recorder (CBOE delayed-quotes CDN, no key) Option open interest is a snapshot source - no free history exists anywhere - so the series only accrues from the day recording starts. That is why this ships BEFORE the redeploy: every day the EA is not running is a day of history that cannot be recovered later. Records one row per weekday after 21:00 UTC to gex_{CANONICAL}.csv: net/call/put dollar GEX per 1% move, call and put OI, the three nearest expiries and the front expiry code. Feeds NOTHING - wiring a feature that is missing across ~100% of the training sample would waste input width and hand batch-norm a constant. It becomes a screening candidate at ~250 rows, gated like every other feature. Thesis: dealer gamma is a RANGE mechanism (long gamma -> hedging sells rallies and buys dips, range compresses; short gamma amplifies both ways), and range is this project's one proven channel. Verified in situ against the live SPX chain before writing any MQL5: 29,362 contracts, 20,993 with nonzero gamma, 54 expiries, total +90.7 Bn/1% (calls +305.7, puts -215.0), and 100% of net GEX inside 5% of spot. The CDN publishes per-contract gamma directly, so no pricing model - and no model risk - enters the recorded data. Also verified the CDN does NOT gate on User-Agent (the old "CBOE is UA-gated" note in DESIGN.md was a different CBOE path), so plain WebRequest reaches it. Dropped a zero-gamma "flip level" field: the probe returned a crossing above spot while total GEX was strongly positive, which is incoherent - a static gamma snapshot cannot give a flip level without repricing. Recording a plausible-looking wrong number is worse than recording nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 16:30:39 -04:00
int CountGexRows(string file)
{
int h = FileOpen(file, FILE_READ | FILE_TXT | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_COMMON);
if(h == INVALID_HANDLE)
return 0;
int n = 0;
while(!FileIsEnding(h))
{
string line = FileReadString(h);
if(StringLen(line) > 8 && StringFind(line, "date;") != 0)
n++;
}
FileClose(h);
return n;
}
//--- -------------------------------- feature math (mirrors export.py)
//--- pandas rolling(W, min_periods=M).rank(pct=True) on the LAST element: rank of the current
//--- value among the window's values (average rank for ties) divided by the window count.
double RollingPctRank(const double &v[], int i, int window, int minPeriods)
{
int lo = MathMax(0, i - window + 1);
int n = i - lo + 1;
if(n < minPeriods)
return EMPTY_VALUE;
double cur = v[i];
int below = 0, ties = 0;
for(int k = lo; k <= i; k++)
{
if(v[k] < cur)
below++;
else if(v[k] == cur)
ties++;
}
double rank = below + (1.0 + ties) / 2.0;
return rank / n;
}
//--- as-of index: largest row with published <= day, else -1
int AsOf(const SAltRawSeries &s, datetime day)
{
int lo = 0, hi = s.rows - 1, ans = -1;
while(lo <= hi)
{
int mid = (lo + hi) / 2;
if(s.published[mid] <= day)
{
ans = mid;
lo = mid + 1;
}
else
hi = mid - 1;
}
return ans;
}
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
//--- raw series kept resident between timer ticks (one chart = one symbol, so only the
//--- sources the chart's catalog row names are ever populated)
feat(altdata): wire instrument-specific implied vol; fix FRED vintage path Wires the screen_ivol survivors (41d726c). New per-symbol `ivolSeries` in the catalog feeds a generic `ivol_chg5` feature from whichever CBOE vol index the instrument owns, so one code path serves every symbol: XAUUSD + ivol_chg5 (GVZ) - MI|vol 0.01971 p=0.002, 3.6x the positive control and 4.6x the vix_chg5 gold had alone. vix_chg5 KEPT: this appends, it does not replace. EURUSD + vix_chg5 - screened, incremental p<=0.006, and its first real feature ever (it had only exploratory EIA). USDJPY + vix_chg5 - screened, incremental. NAS100 / US30 / US2000 + ivol_chg5 (VXN / VXD / RVX) - exploratory by analogy. XTIUSD / XBRUSD + ivol_chg5 (OVX) - exploratory, no oil bars to screen yet. SP500 unchanged - its features already screened clean and VXN/VIX3M edging out VIX is a correlated within-family best-of-N, not a real ranking. On EURUSD/USDJPY the screen put VXD marginally above VIX, but they are near-duplicates and the gap sits inside the noise, so the tie is broken by a rule rather than by the number: take the series already in the fetch path. Also fixes a real collector bug: fetch_vintaged built ALFRED realtime windows out to 2028, and FRED rejects realtime_end after today - so every REVISED series (unemployment, CPI, GDP: exactly the ones needing the vintage path) was unreachable, while unrevised series never noticed because they bail earlier. UNRATE and CPIAUCSL now return first prints correctly. Adds screen_macro.py (rates, curve, breakevens, Fed/ECB policy differential, plus monthly country stats) with a `distinct` column that reports the honest effective sample size - a monthly series pasted onto D1 bars is a step function, and that column is what decides whether it can clear a gate at all. Not yet run: the Market Data bars directory is being regenerated right now. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:14:03 -04:00
//--- m_ivol holds whichever instrument-specific implied-vol index THIS chart's catalog
//--- row names (GVZ for gold, OVX for oil, VXN/VXD/RVX for the index symbols) - one
//--- chart is one symbol, so a single slot serves regardless of which series it is.
SAltRawSeries m_vix, m_usd, m_cot, m_eia, m_ivol;
//--- US macro block. UNRATE is the exception: it IS revised (seasonal refits, ~0.1-0.2pp), there
//--- is no unrevised variant, and the EA cannot run the ALFRED first-print protocol - so a fresh
//--- backfill carries small revisions into history.
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
SAltRawSeries m_dgs10, m_curve, m_bei, m_dff, m_ecb, m_cpi, m_unrate;
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
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
//--- generic HTTP client and symbol catalog/user-mapping are separate collaborators - see
//--- AltDataHttpClient.mqh/AltDataCatalog.mqh. Plain composition (no view/adapter): this class
//--- has no single-inheritance parent forcing one, unlike the Expert\AIBase\* partials.
CAltDataHttpClient m_http;
CAltDataCatalog m_catalog;
public:
CAltDataFetch(void) : m_fredKey(""), m_keyLoaded(false),
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
m_eiaKey(""), m_eiaWarned(false),
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
m_lastGexDate(0), m_gexDateLoaded(false)
{
feat(altdata): wire instrument-specific implied vol; fix FRED vintage path Wires the screen_ivol survivors (41d726c). New per-symbol `ivolSeries` in the catalog feeds a generic `ivol_chg5` feature from whichever CBOE vol index the instrument owns, so one code path serves every symbol: XAUUSD + ivol_chg5 (GVZ) - MI|vol 0.01971 p=0.002, 3.6x the positive control and 4.6x the vix_chg5 gold had alone. vix_chg5 KEPT: this appends, it does not replace. EURUSD + vix_chg5 - screened, incremental p<=0.006, and its first real feature ever (it had only exploratory EIA). USDJPY + vix_chg5 - screened, incremental. NAS100 / US30 / US2000 + ivol_chg5 (VXN / VXD / RVX) - exploratory by analogy. XTIUSD / XBRUSD + ivol_chg5 (OVX) - exploratory, no oil bars to screen yet. SP500 unchanged - its features already screened clean and VXN/VIX3M edging out VIX is a correlated within-family best-of-N, not a real ranking. On EURUSD/USDJPY the screen put VXD marginally above VIX, but they are near-duplicates and the gap sits inside the noise, so the tie is broken by a rule rather than by the number: take the series already in the fetch path. Also fixes a real collector bug: fetch_vintaged built ALFRED realtime windows out to 2028, and FRED rejects realtime_end after today - so every REVISED series (unemployment, CPI, GDP: exactly the ones needing the vintage path) was unreachable, while unrevised series never noticed because they bail earlier. UNRATE and CPIAUCSL now return first prints correctly. Adds screen_macro.py (rates, curve, breakevens, Fed/ECB policy differential, plus monthly country stats) with a `distinct` column that reports the honest effective sample size - a monthly series pasted onto D1 bars is a step function, and that column is what decides whether it can clear a gate at all. Not yet run: the Market Data bars directory is being regenerated right now. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:14:03 -04:00
for(int i = 0; i < ArraySize(m_lastAttempt); i++)
m_lastAttempt[i] = 0;
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
}
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
//--- ------------------------------------- symbol mapping interface (forwards to the catalog)
bool NeedsMapping(string symbol) { return m_catalog.NeedsMapping(symbol); }
int CatalogCount(void) { return m_catalog.CatalogCount(); }
string CatalogName(int i) { return m_catalog.CatalogName(i); }
string CatalogLabel(int i) { return m_catalog.CatalogLabel(i); }
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
bool SaveUserMapping(string symbol, string canonical)
{
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
return m_catalog.SaveUserMapping(symbol, canonical);
}
//--- Maintain the raw caches and the feature CSV for `symbol`. Returns true when the feature
//--- file was rebuilt (caller should reload the panels).
bool Update(string symbol)
{
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
int si = m_catalog.FindSpec(symbol);
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
if(si < 0)
return false; // unknown symbol or user declined - nothing to serve
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
SAltSymbolSpec spec = m_catalog.Spec(si);
bool needVix = StringFind(spec.features, "vix") >= 0;
bool needUsd = StringFind(spec.features, "usd_") >= 0;
bool needEia = StringFind(spec.features, "eia_") >= 0;
bool needCot = (spec.cotWhere != "");
bool needIvol = (spec.ivolSeries != "" && StringFind(spec.features, "ivol") >= 0);
bool needMac = StringFind(spec.features, "mac_") >= 0;
bool changed = false;
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
//--- plausibility bands: generous enough for any real print in the series' history
//--- (VIX peaked 89 in 2008; utilization-style checks live in FeatureValue), tight
//--- enough that transport garbage cannot pass
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
if(needVix)
{
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
if(m_vix.rows == 0)
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
AltRawLoad("raw_VIXCLS.csv", m_vix, 1);
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
if(UpdateFred("VIXCLS", m_vix, 0, 4, 1.0, 200.0))
changed = true;
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
}
if(needUsd)
{
fix(altdata): first live fetch findings - key leak masked, EIA UA, bounded FRED backfill Log review of the 18:12 attach. The wiring works: VIX, dollar index, COT, all seven macro series fetched and SP500_D1.csv rebuilt with its full 13 features on the first pass. Three findings from the same log, fixed: KEY LEAK: the EIA failure line echoed the first 80 chars of the URL, which included most of the api_key. Every URL-echoing error path now goes through MaskUrl(). The key itself is unchanged - it was printed to a local journal, not transmitted - but rotate it if that log ever leaves the machine. EIA HTTP 1003: an MT5 transport-layer code, not a server response. Requests now carry a User-Agent (gateways reject empty-UA at the edge; the CBOE probe showed no-UA is fine THERE, but EIA fronts differ) and 1xxx codes are explained in the log line. Retries were already hourly. UNBOUNDED BACKFILL: an empty cache fetched full series history - CPIAUCNS goes back to 1913, whose pre-1970 dates are outside MQL5 datetime range and whose 1913-era levels sat below the plausibility band, producing 157 scary-but-meaningless REJECTED lines. All FRED fetches now start at 2005 (5y of lookback margin ahead of the 2010 grid). DTWEXBGS staleness horizon raised to 10 days to match its weekly H.10 publication lag. Also confirmed from the log: the running build predates the H4 fallback, so the H4 panels still show 0 features - resolved by the recompile this commit requires anyway. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 18:20:39 -04:00
//--- staleDays 10: the H.10 dollar index posts with ~a week's lag (live fetch
//--- 2026-08-16 was current only through 08-07), so a 4-day horizon would fire a
//--- futile refetch attempt every hour in the gap between releases
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
if(m_usd.rows == 0)
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
AltRawLoad("raw_DTWEXBGS.csv", m_usd, 1);
fix(altdata): first live fetch findings - key leak masked, EIA UA, bounded FRED backfill Log review of the 18:12 attach. The wiring works: VIX, dollar index, COT, all seven macro series fetched and SP500_D1.csv rebuilt with its full 13 features on the first pass. Three findings from the same log, fixed: KEY LEAK: the EIA failure line echoed the first 80 chars of the URL, which included most of the api_key. Every URL-echoing error path now goes through MaskUrl(). The key itself is unchanged - it was printed to a local journal, not transmitted - but rotate it if that log ever leaves the machine. EIA HTTP 1003: an MT5 transport-layer code, not a server response. Requests now carry a User-Agent (gateways reject empty-UA at the edge; the CBOE probe showed no-UA is fine THERE, but EIA fronts differ) and 1xxx codes are explained in the log line. Retries were already hourly. UNBOUNDED BACKFILL: an empty cache fetched full series history - CPIAUCNS goes back to 1913, whose pre-1970 dates are outside MQL5 datetime range and whose 1913-era levels sat below the plausibility band, producing 157 scary-but-meaningless REJECTED lines. All FRED fetches now start at 2005 (5y of lookback margin ahead of the 2010 grid). DTWEXBGS staleness horizon raised to 10 days to match its weekly H.10 publication lag. Also confirmed from the log: the running build predates the H4 fallback, so the H4 panels still show 0 features - resolved by the recompile this commit requires anyway. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 18:20:39 -04:00
if(UpdateFred("DTWEXBGS", m_usd, 1, 10, 50.0, 250.0))
changed = true;
}
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
if(needCot)
{
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
//--- cache is named by CANONICAL, so two brokers' names for the same instrument
//--- share one download instead of fetching the same contract twice
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
string cache = "raw_COT_" + spec.canonical + ".csv";
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
if(m_cot.rows == 0)
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
AltRawLoad(cache, m_cot, 3);
if(UpdateCot(cache, spec.cotWhere, m_cot, 2))
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
changed = true;
}
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
if(needEia)
{
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
if(m_eia.rows == 0)
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
AltRawLoad("raw_EIA_WPSR.csv", m_eia, 3);
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
if(UpdateEia(m_eia, 3))
changed = true;
}
feat(altdata): wire instrument-specific implied vol; fix FRED vintage path Wires the screen_ivol survivors (41d726c). New per-symbol `ivolSeries` in the catalog feeds a generic `ivol_chg5` feature from whichever CBOE vol index the instrument owns, so one code path serves every symbol: XAUUSD + ivol_chg5 (GVZ) - MI|vol 0.01971 p=0.002, 3.6x the positive control and 4.6x the vix_chg5 gold had alone. vix_chg5 KEPT: this appends, it does not replace. EURUSD + vix_chg5 - screened, incremental p<=0.006, and its first real feature ever (it had only exploratory EIA). USDJPY + vix_chg5 - screened, incremental. NAS100 / US30 / US2000 + ivol_chg5 (VXN / VXD / RVX) - exploratory by analogy. XTIUSD / XBRUSD + ivol_chg5 (OVX) - exploratory, no oil bars to screen yet. SP500 unchanged - its features already screened clean and VXN/VIX3M edging out VIX is a correlated within-family best-of-N, not a real ranking. On EURUSD/USDJPY the screen put VXD marginally above VIX, but they are near-duplicates and the gap sits inside the noise, so the tie is broken by a rule rather than by the number: take the series already in the fetch path. Also fixes a real collector bug: fetch_vintaged built ALFRED realtime windows out to 2028, and FRED rejects realtime_end after today - so every REVISED series (unemployment, CPI, GDP: exactly the ones needing the vintage path) was unreachable, while unrevised series never noticed because they bail earlier. UNRATE and CPIAUCSL now return first prints correctly. Adds screen_macro.py (rates, curve, breakevens, Fed/ECB policy differential, plus monthly country stats) with a `distinct` column that reports the honest effective sample size - a monthly series pasted onto D1 bars is a step function, and that column is what decides whether it can clear a gate at all. Not yet run: the Market Data bars directory is being regenerated right now. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:14:03 -04:00
if(needIvol)
{
if(m_ivol.rows == 0)
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
AltRawLoad("raw_" + spec.ivolSeries + ".csv", m_ivol, 1);
if(UpdateFred(spec.ivolSeries, m_ivol, 4, 4, 1.0, 300.0))
feat(altdata): wire instrument-specific implied vol; fix FRED vintage path Wires the screen_ivol survivors (41d726c). New per-symbol `ivolSeries` in the catalog feeds a generic `ivol_chg5` feature from whichever CBOE vol index the instrument owns, so one code path serves every symbol: XAUUSD + ivol_chg5 (GVZ) - MI|vol 0.01971 p=0.002, 3.6x the positive control and 4.6x the vix_chg5 gold had alone. vix_chg5 KEPT: this appends, it does not replace. EURUSD + vix_chg5 - screened, incremental p<=0.006, and its first real feature ever (it had only exploratory EIA). USDJPY + vix_chg5 - screened, incremental. NAS100 / US30 / US2000 + ivol_chg5 (VXN / VXD / RVX) - exploratory by analogy. XTIUSD / XBRUSD + ivol_chg5 (OVX) - exploratory, no oil bars to screen yet. SP500 unchanged - its features already screened clean and VXN/VIX3M edging out VIX is a correlated within-family best-of-N, not a real ranking. On EURUSD/USDJPY the screen put VXD marginally above VIX, but they are near-duplicates and the gap sits inside the noise, so the tie is broken by a rule rather than by the number: take the series already in the fetch path. Also fixes a real collector bug: fetch_vintaged built ALFRED realtime windows out to 2028, and FRED rejects realtime_end after today - so every REVISED series (unemployment, CPI, GDP: exactly the ones needing the vintage path) was unreachable, while unrevised series never noticed because they bail earlier. UNRATE and CPIAUCSL now return first prints correctly. Adds screen_macro.py (rates, curve, breakevens, Fed/ECB policy differential, plus monthly country stats) with a `distinct` column that reports the honest effective sample size - a monthly series pasted onto D1 bars is a step function, and that column is what decides whether it can clear a gate at all. Not yet run: the Market Data bars directory is being regenerated right now. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:14:03 -04:00
changed = true;
}
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
if(needMac)
{
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
if(m_dgs10.rows == 0) AltRawLoad("raw_DGS10.csv", m_dgs10, 1);
if(m_curve.rows == 0) AltRawLoad("raw_T10Y2Y.csv", m_curve, 1);
if(m_bei.rows == 0) AltRawLoad("raw_T5YIE.csv", m_bei, 1);
if(m_dff.rows == 0) AltRawLoad("raw_DFF.csv", m_dff, 1);
if(m_ecb.rows == 0) AltRawLoad("raw_ECBDFR.csv", m_ecb, 1);
if(m_cpi.rows == 0) AltRawLoad("raw_CPIAUCNS.csv", m_cpi, 1);
if(m_unrate.rows == 0) AltRawLoad("raw_UNRATE.csv", m_unrate, 1);
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
if(UpdateFred("DGS10", m_dgs10, 5, 4, -5.0, 30.0)) changed = true;
if(UpdateFred("T10Y2Y", m_curve, 6, 4, -10.0, 10.0)) changed = true;
if(UpdateFred("T5YIE", m_bei, 7, 4, -5.0, 15.0)) changed = true;
if(UpdateFred("DFF", m_dff, 8, 4, -2.0, 30.0)) changed = true;
if(UpdateFred("ECBDFR", m_ecb, 9, 4, -5.0, 30.0)) changed = true;
if(UpdateFred("CPIAUCNS", m_cpi, 10, 32, 20.0, 1000.0)) changed = true;
if(UpdateFred("UNRATE", m_unrate, 11, 32, 0.0, 35.0)) changed = true;
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
}
//--- Rebuild when stale/missing and ANY needed source has rows: a temporarily dead source
//--- leaves its columns empty (header intact) and the panel 0-fills them - degradation must
//--- never take the working features down with it (the silent-FRED incident took exactly that
//--- shape under the old all-sources-required gate).
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
UpdateGex(spec.canonical);
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
bool anyReady = (needVix && m_vix.rows > 0) || (needUsd && m_usd.rows > 0)
feat(altdata): wire instrument-specific implied vol; fix FRED vintage path Wires the screen_ivol survivors (41d726c). New per-symbol `ivolSeries` in the catalog feeds a generic `ivol_chg5` feature from whichever CBOE vol index the instrument owns, so one code path serves every symbol: XAUUSD + ivol_chg5 (GVZ) - MI|vol 0.01971 p=0.002, 3.6x the positive control and 4.6x the vix_chg5 gold had alone. vix_chg5 KEPT: this appends, it does not replace. EURUSD + vix_chg5 - screened, incremental p<=0.006, and its first real feature ever (it had only exploratory EIA). USDJPY + vix_chg5 - screened, incremental. NAS100 / US30 / US2000 + ivol_chg5 (VXN / VXD / RVX) - exploratory by analogy. XTIUSD / XBRUSD + ivol_chg5 (OVX) - exploratory, no oil bars to screen yet. SP500 unchanged - its features already screened clean and VXN/VIX3M edging out VIX is a correlated within-family best-of-N, not a real ranking. On EURUSD/USDJPY the screen put VXD marginally above VIX, but they are near-duplicates and the gap sits inside the noise, so the tie is broken by a rule rather than by the number: take the series already in the fetch path. Also fixes a real collector bug: fetch_vintaged built ALFRED realtime windows out to 2028, and FRED rejects realtime_end after today - so every REVISED series (unemployment, CPI, GDP: exactly the ones needing the vintage path) was unreachable, while unrevised series never noticed because they bail earlier. UNRATE and CPIAUCSL now return first prints correctly. Adds screen_macro.py (rates, curve, breakevens, Fed/ECB policy differential, plus monthly country stats) with a `distinct` column that reports the honest effective sample size - a monthly series pasted onto D1 bars is a step function, and that column is what decides whether it can clear a gate at all. Not yet run: the Market Data bars directory is being regenerated right now. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:14:03 -04:00
|| (needCot && m_cot.rows > 0) || (needEia && m_eia.rows > 0)
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
|| (needIvol && m_ivol.rows > 0) || (needMac && m_dgs10.rows > 0);
fix(altdata): robustness pass for arbitrary symbol/timeframe - validation + error handling Systematic audit of the alt-data stack against "any symbol, any timeframe", prompted by the H4 surprise. Findings, each fixed: CAPACITY: ALTDATA_MAX_FEATURES was 16 with FX symbols already at 15 - the next added column would have been silently truncated by a MathMin. Raised to 32, pin-chars 512 -> 1024. SYMBOL NAMES: the panel reload path re-derived symbol/timeframe by splitting the file path on its FIRST underscore - mis-parsing every symbol containing one (OANDA-style EUR_USD and US_500 are in our own alias lists) and knowing only three timeframes. It now stores the (symbol, period) Load() was called with and reuses them verbatim. Path-hostile characters in broker symbols ("EUR/USD") are sanitized by a shared AltDataFileSymbol() used by the panel, the fetcher and TunedPeriods, so a slash cannot route a write into an unintended subfolder. DOWNLOAD VALIDATION: every FRED-family fetch now enforces a per-series plausibility band (VIX 1-200, yields -5..30, CPI index 20-1000, ...) - StringToDouble on transport garbage returns 0.0, and one absurd value poisons every change/percentile feature computed across it (the BatchNorm NaN-latch incident came from exactly one huge-but-finite input). Rejected rows are counted and reported, never dropped silently. LOUD EMPTINESS: a successful response with zero observations on an empty cache now says so - naming the series (wrong id / format drift) or the COT predicate (the unverified like-clauses) instead of leaving 0-filled features unexplained. GEX gains a truncation guard: a day-over-day contract-count collapse >50% is the fingerprint of a partial 13 MB download, not of markets, and is skipped rather than recorded as a plausible-but-wrong number. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:59:10 -04:00
string outFile = AltDataFileSymbol(symbol) + "_D1.csv"; // sanitizer shared with the panel
if((changed || !FileIsExist(ALTFETCH_DIR + outFile, FILE_COMMON)) && anyReady)
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
return RebuildFeatures(spec, outFile);
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
return false;
}
protected:
//--- --------------------------------- catalog-driven feature writer Walks the daily grid and
//--- emits exactly export.py's format; the transforms are the FIXED a-priori constants
//--- documented there.
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
bool RebuildFeatures(const SAltSymbolSpec &spec, string outFile)
{
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
string feats[];
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
int nf = StringSplit(spec.features, ';', feats);
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
if(nf <= 0)
return false;
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
//--- weekly COT net-spec series with the catalog sign applied
double cspec[];
ArrayResize(cspec, m_cot.rows);
for(int i = 0; i < m_cot.rows; i++)
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
cspec[i] = (m_cot.v1[i] > 0) ? spec.cotSign * (m_cot.v2[i] - m_cot.v3[i]) / m_cot.v1[i] : 0.0;
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
//--- EIA crude-stocks copy for the rolling percentile
double estk[];
ArrayResize(estk, m_eia.rows);
for(int i = 0; i < m_eia.rows; i++)
estk[i] = m_eia.v1[i];
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
//--- temp + swap via AtomicFile.mqh, same reasoning as AltRawSave: panels on other charts read this file
string tmpName = "";
int h = AtomicWriteBegin(ALTFETCH_DIR + outFile, FILE_COMMON, tmpName, FILE_TXT | FILE_ANSI);
if(h == INVALID_HANDLE)
return false;
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
string header = "date";
for(int f = 0; f < nf; f++)
header += ";" + feats[f];
FileWriteString(h, header + "\n");
datetime today = TimeCurrent();
for(datetime day = ALTFETCH_GRID_START; day <= today; day += 86400)
{
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
string line = TimeToString(day, TIME_DATE);
bool any = false;
for(int f = 0; f < nf; f++)
{
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
string val = FeatureValue(feats[f], day, cspec, estk);
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
if(val != "")
any = true;
line += ";" + val;
}
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
if(any)
FileWriteString(h, line + "\n");
}
if(!AtomicWriteEnd(h, ALTFETCH_DIR + outFile, tmpName, FILE_COMMON, true, __FUNCTION__))
return false;
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
PrintFormat("AltDataFetch: %s rebuilt (EA-maintained) - %d features: %s.",
refactor(altdata): split CAltDataFetch's HTTP client and symbol catalog into collaborators CAltDataFetch (System/AltDataFetch.mqh) mixed six unrelated concerns in one 1406-line class. This is plain composition, not the Expert/AIBase view+adapter pattern - CAltDataFetch has no single-inheritance parent forcing an adapter, same shape as Database/DatabaseManager.mqh composing its four Database* managers. Extracted, grep-confirmed zero external callers of any moved method (only Warrior_EA.mq5's already-public Update/NeedsMapping/CatalogCount/CatalogName/ CatalogLabel/SaveUserMapping surface, unchanged): - CAltDataHttpClient (System/AltDataHttpClient.mqh): HttpGet/HostOf/MaskUrl/ BlockedIndex (4014 per-host backoff), JsonField, UrlEncodePart. Owns the m_blockedHost/m_blockedUntil/m_urlAlerted state - STATEFUL, moved verbatim. - CAltDataCatalog (System/AltDataCatalog.mqh): the SAltSymbolSpec catalog (AddSpec/BuildCatalog/AliasMatches/FindSpec) plus symbol_map.cfg user-mapping persistence (LoadUserMap/SaveUserMapping) and the public NeedsMapping/CatalogCount/CatalogName/CatalogLabel surface. Owns m_specs/m_userFrom/m_userTo/m_userMapLoaded - STATEFUL, moved verbatim. Added one new Spec(i) getter (by value - 4 strings + 1 double, cheap) so CAltDataFetch::Update()/RebuildFeatures() can read a resolved catalog row without reaching into the collaborator's array. - LoadRaw/SaveRaw (raw-series CSV <-> file mapping) had zero member-state dependency - turned into free functions AltRawLoad/AltRawSave, matching the file's own existing AltSeriesAppend precedent, instead of a needless class. CAltDataFetch itself keeps three concerns as a deliberate partial, same judgment already applied to Topology's boot sequence / Features' shared- indicator lifecycle elsewhere in this campaign: the four per-source fetch pipelines (UpdateFred/UpdateCot/UpdateEia/UpdateGex, including FredKey/EiaKey/ LoadCommonKey/ShouldAttemptFetch/the GEX CBOE helpers) and the feature-CSV builder (RebuildFeatures/FeatureValue/RollingPctRank/AsOf) both read/write the 9 SAltRawSeries caches kept resident on the orchestrator between timer ticks - splitting them out would mean either relocating that cache's ownership or a 9-13 parameter signature per method, a larger design decision better made as its own pass rather than forced through unattended given the finding's own "high risk" estimate. Every moved method body is copied verbatim (statement-by-statement diffed against git show HEAD~1:System/AltDataFetch.mqh) with only the mechanical substitution HttpGet/JsonField/UrlEncodePart -> m_http.*, LoadRaw/SaveRaw -> AltRawLoad/AltRawSave, FindSpec -> m_catalog.FindSpec, and m_specs[si].X -> spec.X (spec = m_catalog.Spec(si), resolved once per Update() call instead of re-indexing). ALTFETCH_TIMEOUT_MS/ALTFETCH_GEX_TIMEOUT_MS macros moved into AltDataHttpClient.mqh (their logical owner); ALTFETCH_DIR stays in AltDataFetch.mqh, defined before both new #includes since CAltDataCatalog's ctor path (symbol_map.cfg) and CAltDataHttpClient's HttpGet default param reference it. Self-compiled 0 errors, 0 warnings (_claude_stage, MetaEditor64 /compile), twice - once before and once after a stale doc-comment fix (a leftover "same reasoning as SaveRaw" mention updated to AltRawSave). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 03:32:02 -04:00
outFile, nf, spec.features);
return true;
}
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
//--- One feature value at one day; "" while its source history is not yet deep enough.
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
//--- Each branch does its own as-of lookup (binary search, cheap at rebuild frequency),
//--- so adding a source never widens a parameter list again.
string FeatureValue(string id, datetime day,
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
const double &cspec[], const double &estk[])
{
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
if(id == "vix_chg5")
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
{
int i = AsOf(m_vix, day);
return (i >= 5) ? DoubleToString((m_vix.v1[i] - m_vix.v1[i - 5]) / 10.0, 6) : "";
}
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
if(id == "vix")
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
{
int i = AsOf(m_vix, day);
return (i >= 0) ? DoubleToString(m_vix.v1[i] / 100.0, 6) : "";
}
//--- the chart's OWN implied-vol index; same a-priori scales as vix/vix_chg5 so the
//--- pairs are directly comparable to the first batch-norm layer
if(id == "ivol_chg5")
{
int i = AsOf(m_ivol, day);
return (i >= 5) ? DoubleToString((m_ivol.v1[i] - m_ivol.v1[i - 5]) / 10.0, 6) : "";
}
if(id == "ivol")
{
int i = AsOf(m_ivol, day);
return (i >= 0) ? DoubleToString(m_ivol.v1[i] / 100.0, 6) : "";
}
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
if(id == "usd_chg5")
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
{
int i = AsOf(m_usd, day);
return (i >= 5) ? DoubleToString(m_usd.v1[i] - m_usd.v1[i - 5], 6) : "";
}
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
if(id == "cot_spec_net")
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
{
int i = AsOf(m_cot, day);
return (i >= 0 && m_cot.v1[i] > 0) ? DoubleToString(cspec[i], 6) : "";
}
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
if(id == "cot_idx_1y" || id == "cot_idx_3y")
{
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
int i = AsOf(m_cot, day);
if(i < 0)
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
return "";
bool oneYear = (id == "cot_idx_1y");
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
double r = RollingPctRank(cspec, i, oneYear ? 52 : 156, oneYear ? 26 : 78);
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
return (r != EMPTY_VALUE) ? DoubleToString(r - 0.5, 6) : "";
}
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
if(id == "cot_chg_4w")
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
{
int i = AsOf(m_cot, day);
return (i >= 4) ? DoubleToString(cspec[i] - cspec[i - 4], 6) : "";
}
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
if(id == "eia_stk_idx1y")
{
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
int i = AsOf(m_eia, day);
if(i < 0 || estk[i] <= 0)
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
return "";
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
double r = RollingPctRank(estk, i, 52, 26);
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
return (r != EMPTY_VALUE) ? DoubleToString(r - 0.5, 6) : "";
}
if(id == "eia_stk_chg4")
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
{
int i = AsOf(m_eia, day);
return (i >= 4 && estk[i] > 0 && estk[i - 4] > 0)
? DoubleToString((estk[i] / estk[i - 4] - 1.0) * 10.0, 6) : "";
}
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
if(id == "eia_util")
feat(altdata): wire everything the sources serve - screens become priors, not gates Owner decision (stated twice): available data gets wired; the networks judge usefulness; the deploy gate remains the arbiter of what trades. Implemented: MACRO block (6) on every symbol: 10y yield 20d change, curve slope, 5y breakeven 20d change, Fed-ECB policy gap, CPI yoy, unemployment 12m change. Screened null vs forward range on all four research symbols - recorded as the honest prior in the catalog comment, wired regardless. RISK block (3) extended to every symbol (FX majors, metals, energy, BTC all now carry vix/vix_chg5/usd_chg5). IVOL pair extended with the level alongside the change. Vintage integrity kept where it is free: CPI is fetched as CPIAUCNS (NSA, essentially never revised) so the plain-FRED backfill stays first-print-clean; yields/curve/breakevens/policy rates are unrevised by nature. UNRATE is the one exception (seasonal refits, ~0.1-0.2pp) - the EA cannot run the ALFRED protocol, accepted and documented at the declaration site. UpdateFred gains a staleDays parameter so the monthly series do not fire a pointless fetch attempt every hour for three weeks after each print. FeatureValue now takes the day and does its own as-of lookups - adding a source no longer widens a parameter list. Feature counts: 12-15 per symbol; symbol feature-order changed, safe only because no models exist yet. export.py mirrors the new catalog for the five research symbols (13-15 features), smoke-tested: all five CSVs written, 6,072 daily rows each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 17:29:22 -04:00
{
int i = AsOf(m_eia, day);
return (i >= 0 && m_eia.v3[i] > 0)
? DoubleToString((m_eia.v3[i] - 90.0) / 10.0, 6) : "";
}
//--- US macro block. Index arithmetic is in OBSERVATIONS (business days for the
//--- daily series, months for CPI/UNRATE), matching the research screen exactly.
if(id == "mac_y10")
{
int i = AsOf(m_dgs10, day);
return (i >= 20) ? DoubleToString(m_dgs10.v1[i] - m_dgs10.v1[i - 20], 6) : "";
}
if(id == "mac_curve")
{
int i = AsOf(m_curve, day);
return (i >= 0) ? DoubleToString(m_curve.v1[i], 6) : "";
}
if(id == "mac_bei")
{
int i = AsOf(m_bei, day);
return (i >= 20) ? DoubleToString(m_bei.v1[i] - m_bei.v1[i - 20], 6) : "";
}
if(id == "mac_gap")
{
int i = AsOf(m_dff, day), j = AsOf(m_ecb, day);
return (i >= 0 && j >= 0)
? DoubleToString((m_dff.v1[i] - m_ecb.v1[j]) / 10.0, 6) : "";
}
if(id == "mac_cpi")
{
int i = AsOf(m_cpi, day);
return (i >= 12 && m_cpi.v1[i - 12] > 0)
? DoubleToString((m_cpi.v1[i] / m_cpi.v1[i - 12] - 1.0) * 10.0, 6) : "";
}
if(id == "mac_unemp")
{
int i = AsOf(m_unrate, day);
return (i >= 12) ? DoubleToString((m_unrate.v1[i] - m_unrate.v1[i - 12]) / 10.0, 6) : "";
}
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
return "";
}
};