Cleanup: remove dead code, stale docs, and all non-main branches

- Delete back-compat shims data_provider.py / oanda_provider.py;
  test_all.py now imports providers.* directly (ForexDataProvider name
  retired; the two shim-only tests removed, 268 -> 266 tests)
- Delete backtest/research/calendar_store.py (no importer, no entry
  point); config.py audit comment reworded
- Delete executed plan doc (2026-07-21 centralized-config) and the two
  unbuildable guide PDFs; HTML guides remain the source of truth
- OANDA_MIGRATION_ROADMAP.md status header corrected: phases 1-2
  shipped 2026-07-28, feed is live on Oanda; phases 3-6 still unbuilt
- Branches deleted locally + on both remotes: oanda_demo (merged),
  web-dashboard, eurusd-modular, reversal-exit-alert (owner-approved
  permanent deletion); July stash dropped

Claude-Session: https://claude.ai/code/session_019zR58p8ACW1oJ5QXVUZ5es
This commit is contained in:
Harwinder Sekhon
2026-08-03 11:08:48 -04:00
parent 042b079861
commit bcda6e0ebd
10 changed files with 75 additions and 1213 deletions
+70 -101
View File
@@ -214,29 +214,29 @@ def _():
# ══════════════════════════════════════════════════════════════════════
# MODULE 2: data_provider.py
# MODULE 2: providers/twelve_data.py
# ══════════════════════════════════════════════════════════════════════
print("\n═══ Module 2: data_provider.py ═══")
print("\n═══ Module 2: providers/twelve_data.py ═══")
from data_provider import ForexDataProvider, DataIntegrityError
from providers import TwelveDataProvider, DataIntegrityError
@test("ForexDataProvider._to_td_symbol conversion")
@test("TwelveDataProvider._to_td_symbol conversion")
def _():
assert ForexDataProvider._to_td_symbol("XAU/USD") == "XAU/USD"
assert TwelveDataProvider._to_td_symbol("XAU/USD") == "XAU/USD"
@test("ForexDataProvider TD_INTERVALS complete mapping")
@test("TwelveDataProvider TD_INTERVALS complete mapping")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
for tf in Timeframe:
assert tf in dp.TD_INTERVALS, f"Missing TD_INTERVALS mapping for {tf}"
@test("ForexDataProvider BARS_PER_DAY mapping")
@test("TwelveDataProvider BARS_PER_DAY mapping")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
for tf in Timeframe:
assert tf in dp.BARS_PER_DAY, f"Missing BARS_PER_DAY for {tf}"
assert dp.BARS_PER_DAY[Timeframe.H1] == 24
@@ -244,9 +244,9 @@ def _():
assert dp.BARS_PER_DAY[Timeframe.D1] == 1
@test("ForexDataProvider CANDLE_SECONDS mapping")
@test("TwelveDataProvider CANDLE_SECONDS mapping")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
for tf in Timeframe:
assert tf in dp.CANDLE_SECONDS, f"Missing CANDLE_SECONDS for {tf}"
assert dp.CANDLE_SECONDS[Timeframe.M1] == 60
@@ -254,9 +254,9 @@ def _():
assert dp.CANDLE_SECONDS[Timeframe.D1] == 86400
@test("ForexDataProvider._validate_and_clean basic valid data")
@test("TwelveDataProvider._validate_and_clean basic valid data")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
df = make_ohlcv(300)
result = dp._validate_and_clean(df, "XAU/USD", Timeframe.H1)
assert len(result) > 0
@@ -264,9 +264,9 @@ def _():
assert not result.index.has_duplicates
@test("ForexDataProvider._validate_and_clean empty df raises error")
@test("TwelveDataProvider._validate_and_clean empty df raises error")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
try:
dp._validate_and_clean(pd.DataFrame(), "XAU/USD", Timeframe.H1)
assert False, "Should have raised DataIntegrityError"
@@ -274,9 +274,9 @@ def _():
pass
@test("ForexDataProvider._validate_and_clean None df raises error")
@test("TwelveDataProvider._validate_and_clean None df raises error")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
try:
dp._validate_and_clean(None, "XAU/USD", Timeframe.H1)
assert False, "Should have raised DataIntegrityError"
@@ -284,18 +284,18 @@ def _():
pass
@test("ForexDataProvider._validate_and_clean fixes OHLC inconsistencies")
@test("TwelveDataProvider._validate_and_clean fixes OHLC inconsistencies")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
df = make_ohlcv(300)
df.iloc[10, df.columns.get_loc("High")] = df.iloc[10]["Low"] - 1
result = dp._validate_and_clean(df, "XAU/USD", Timeframe.H1)
assert (result["High"] >= result["Low"]).all()
@test("ForexDataProvider._validate_and_clean drops future timestamps")
@test("TwelveDataProvider._validate_and_clean drops future timestamps")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
df = make_ohlcv(300)
future = pd.Timestamp.now(tz="UTC") + pd.Timedelta(hours=10)
df.loc[future] = df.iloc[-1]
@@ -303,9 +303,9 @@ def _():
assert (result.index <= pd.Timestamp.utcnow() + pd.Timedelta(minutes=5)).all()
@test("ForexDataProvider._validate_and_clean removes duplicates")
@test("TwelveDataProvider._validate_and_clean removes duplicates")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
df = make_ohlcv(300)
dup = pd.concat([df, df.iloc[-5:]])
assert dup.index.has_duplicates, "Setup: should have duplicates"
@@ -313,9 +313,9 @@ def _():
assert not result.index.has_duplicates, "Should remove duplicate indices"
@test("ForexDataProvider._validate_and_clean forward fills NaN (limit 3)")
@test("TwelveDataProvider._validate_and_clean forward fills NaN (limit 3)")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
df = make_ohlcv(300)
df.iloc[100, df.columns.get_loc("Close")] = np.nan
df.iloc[101, df.columns.get_loc("Close")] = np.nan
@@ -323,9 +323,9 @@ def _():
assert not result["Close"].isna().any()
@test("ForexDataProvider._is_data_fresh weekend override")
@test("TwelveDataProvider._is_data_fresh weekend override")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
# Create data that ends on a Friday, call _is_data_fresh on a Saturday
friday = pd.Timestamp("2025-05-09 16:00:00", tz="UTC")
old_dates = pd.date_range(end=friday, periods=300, freq="h")
@@ -339,16 +339,16 @@ def _():
assert result is True, "Weekend data should always be considered fresh"
@test("ForexDataProvider._is_data_fresh returns False for empty df")
@test("TwelveDataProvider._is_data_fresh returns False for empty df")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
assert dp._is_data_fresh(pd.DataFrame(), "XAU/USD", Timeframe.H1) is False
assert dp._is_data_fresh(None, "XAU/USD", Timeframe.H1) is False
@test("ForexDataProvider.clear_cache clears all entries")
@test("TwelveDataProvider.clear_cache clears all entries")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
dp._cache["test_key"] = make_ohlcv(10)
dp._cache_timestamps["test_key"] = time.time()
count = dp.clear_cache()
@@ -357,9 +357,9 @@ def _():
assert len(dp._cache_timestamps) == 0
@test("ForexDataProvider.get_pair_info returns correct info")
@test("TwelveDataProvider.get_pair_info returns correct info")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
info = dp.get_pair_info(XAUUSD)
assert info["symbol"] == XAUUSD
assert info["display"] == "XAU/USD"
@@ -369,18 +369,18 @@ def _():
assert info_unknown["display"] == "XAU/USD"
@test("ForexDataProvider._throttle rate limiting logic")
@test("TwelveDataProvider._throttle rate limiting logic")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
dp._request_times.clear()
for _ in range(10):
dp._throttle(credits=1)
assert len(dp._request_times) == 10
@test("ForexDataProvider caching - in-memory cache returns same data")
@test("TwelveDataProvider caching - in-memory cache returns same data")
def _():
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
df = make_ohlcv(300)
dp._cache["XAU/USD_1h"] = df
dp._cache_timestamps["XAU/USD_1h"] = time.time()
@@ -2267,7 +2267,7 @@ def _():
@test("Outlier filter drops the spike bar but keeps the snap-back bar")
def _():
provider = ForexDataProvider(PlatformConfig())
provider = TwelveDataProvider(PlatformConfig())
df = make_ohlcv(n=300, seed=3)
k = 150
spike_ts, after_ts = df.index[k], df.index[k + 1]
@@ -2288,7 +2288,7 @@ def _():
"Close": v, "Volume": 1000.0}, index=idx)
for _attempt in range(2):
provider = ForexDataProvider(PlatformConfig())
provider = TwelveDataProvider(PlatformConfig())
started = pd.Timestamp.now(tz="UTC").floor("15min")
last_closed = started - pd.Timedelta(minutes=15)
stale = bars_ending(last_closed - pd.Timedelta(minutes=15))
@@ -2323,13 +2323,13 @@ def _():
"Close": v, "Volume": 1000.0}, index=idx)
for _attempt in range(2):
provider = ForexDataProvider(PlatformConfig())
provider = TwelveDataProvider(PlatformConfig())
started = pd.Timestamp.now(tz="UTC").floor("15min")
last_closed = started - pd.Timedelta(minutes=15)
lagging = bars_ending(last_closed - pd.Timedelta(minutes=15))
complete = bars_ending(last_closed)
with patch("providers.base._is_forex_weekend", return_value=False), \
patch.object(ForexDataProvider, "FRESH_FETCH_RETRY_SECONDS", 0.0):
patch.object(TwelveDataProvider, "FRESH_FETCH_RETRY_SECONDS", 0.0):
# (a) lagging first response → one retry returns the closed bar.
with patch.object(provider, "_fetch_twelve_data",
side_effect=[lagging.copy(), complete.copy()]) as mf:
@@ -2966,30 +2966,30 @@ def _():
f"example config.json must be byte-identical to defaults on {tf}"
@test("data_provider: api key resolves env → config.json (no code default)")
@test("providers: api key resolves env → config.json (no code default)")
def _():
from data_provider import ForexDataProvider
from providers import TwelveDataProvider
app = AppConfig(provider_settings={"twelve_data": {"api_key": "json-key"}})
set_app_config(app)
try:
with patch.dict(os.environ, {}, clear=True):
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
assert dp._api_key == "json-key"
with patch.dict(os.environ, {"TWELVE_DATA_API_KEY": "env-key"}):
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
assert dp._api_key == "env-key"
set_app_config(AppConfig()) # no key anywhere
with patch.dict(os.environ, {}, clear=True):
dp = ForexDataProvider(PlatformConfig())
dp = TwelveDataProvider(PlatformConfig())
assert dp._api_key is None and dp._td is None
finally:
set_app_config(_TEST_APP)
@test("data_provider: make_data_provider dispatches on active provider")
@test("providers: make_data_provider dispatches on active provider")
def _():
from data_provider import ForexDataProvider, make_data_provider
assert isinstance(make_data_provider(PlatformConfig()), ForexDataProvider)
from providers import TwelveDataProvider, make_data_provider
assert isinstance(make_data_provider(PlatformConfig()), TwelveDataProvider)
set_app_config(AppConfig(active_provider="polygon",
provider_settings={"polygon": {"api_key": "k"}}))
try:
@@ -3132,30 +3132,12 @@ def _():
print("\n═══ Module 15: Oanda provider / executor / daemon ═══")
import oanda_provider
import providers.oanda
import oanda_executor
import oanda_daemon
@test("oanda_provider: _is_forex_weekend matches data_provider's")
def _():
from data_provider import _is_forex_weekend as dp_weekend
from oanda_provider import _is_forex_weekend as oa_weekend
import pandas as pd
for ts_str in [
"2026-07-17 12:00", # Friday noon
"2026-07-17 22:30", # Friday after close
"2026-07-18 15:00", # Saturday
"2026-07-19 12:00", # Sunday before open
"2026-07-19 22:30", # Sunday after open
"2026-07-20 08:00", # Monday
]:
ts = pd.Timestamp(ts_str, tz="UTC")
assert dp_weekend(ts) == oa_weekend(ts), \
f"Weekend check mismatch at {ts_str}"
@test("oanda_provider: OandaDataProvider parses Oanda candle JSON into correct DataFrame")
@test("providers.oanda: OandaDataProvider parses Oanda candle JSON into correct DataFrame")
def _():
sample_candles = [
{
@@ -3190,8 +3172,8 @@ def _():
"raise_for_status": lambda self: None,
})()
provider2 = oanda_provider.OandaDataProvider.__new__(
oanda_provider.OandaDataProvider)
provider2 = providers.oanda.OandaDataProvider.__new__(
providers.oanda.OandaDataProvider)
provider2.config = PlatformConfig()
provider2._base_url = "https://test"
provider2._cache = {}
@@ -3219,25 +3201,25 @@ def _():
assert abs(spread - 0.30) < 0.01, f"Expected spread ~0.30, got {spread}"
@test("oanda_provider: instrument name mapping")
@test("providers.oanda: instrument name mapping")
def _():
provider = oanda_provider.OandaDataProvider.__new__(
oanda_provider.OandaDataProvider)
provider = providers.oanda.OandaDataProvider.__new__(
providers.oanda.OandaDataProvider)
assert provider._to_oanda_instrument("XAU/USD") == "XAU_USD"
assert provider._to_oanda_instrument("EUR/USD") == "EUR_USD"
@test("oanda_provider: granularity mapping covers all scan timeframes")
@test("providers.oanda: granularity mapping covers all scan timeframes")
def _():
for tf in [Timeframe.M5, Timeframe.M15, Timeframe.H1, Timeframe.H4]:
assert tf in oanda_provider.OandaDataProvider.OANDA_GRANULARITY, \
assert tf in providers.oanda.OandaDataProvider.OANDA_GRANULARITY, \
f"{tf} missing from OANDA_GRANULARITY"
@test("oanda_provider: get_spread returns default when no cache")
@test("providers.oanda: get_spread returns default when no cache")
def _():
provider = oanda_provider.OandaDataProvider.__new__(
oanda_provider.OandaDataProvider)
provider = providers.oanda.OandaDataProvider.__new__(
providers.oanda.OandaDataProvider)
provider._spread_cache = {}
spread = provider.get_spread("XAU/USD")
assert spread == 0.30, f"Expected default spread 0.30, got {spread}"
@@ -3820,15 +3802,15 @@ def _():
assert "oanda.com" in url, f"Expected Oanda URL, got {url}"
@test("oanda_provider: HTTP error surfaces status code and body")
@test("providers.oanda: HTTP error surfaces status code and body")
def _():
mock_resp = type("MockResp", (), {
"status_code": 401,
"text": '{"errorMessage":"Insufficient authorization to perform request"}',
})()
provider = oanda_provider.OandaDataProvider.__new__(
oanda_provider.OandaDataProvider)
provider = providers.oanda.OandaDataProvider.__new__(
providers.oanda.OandaDataProvider)
provider._base_url = "https://test"
provider._spread_cache = {}
provider._session = type("S", (), {
@@ -3839,17 +3821,17 @@ def _():
try:
provider._fetch_oanda_candles("XAU/USD", Timeframe.H1, 300)
assert False, "Should have raised OandaDataError"
except oanda_provider.OandaDataError as e:
except providers.oanda.OandaDataError as e:
msg = str(e)
assert "401" in msg, f"Status code missing from error: {msg}"
assert "Insufficient authorization" in msg, \
f"Body detail missing from error: {msg}"
@test("oanda_provider: boundary-retry failure keeps original data")
@test("providers.oanda: boundary-retry failure keeps original data")
def _():
provider = oanda_provider.OandaDataProvider.__new__(
oanda_provider.OandaDataProvider)
provider = providers.oanda.OandaDataProvider.__new__(
providers.oanda.OandaDataProvider)
provider.config = PlatformConfig()
provider._cache = {}
provider._cache_timestamps = {}
@@ -3868,7 +3850,7 @@ def _():
call_count["n"] += 1
if call_count["n"] == 1:
return original_df
raise oanda_provider.OandaDataError("boom")
raise providers.oanda.OandaDataError("boom")
def _validate_and_clean(df, symbol, timeframe):
return df
@@ -4029,19 +4011,6 @@ def _():
assert TiingoDataProvider.CANDLE_SECONDS is BaseDataProvider.CANDLE_SECONDS
@test("providers: back-compat shims re-export the real classes")
def _():
import data_provider as dp_shim
import oanda_provider as oa_shim
assert dp_shim.ForexDataProvider is TwelveDataProvider
assert dp_shim.make_data_provider is make_data_provider
assert oa_shim.OandaDataProvider is _POanda
from providers.base import DataIntegrityError as base_die
assert dp_shim.DataIntegrityError is base_die
# Provider-specific errors are catchable as the shared type.
assert issubclass(oa_shim.OandaDataError, base_die)
@test("providers: fetch_range paginates, dedupes, and trims to the window")
def _():
class FakeProvider(BaseDataProvider):
@@ -4288,7 +4257,7 @@ def _():
# must never change the session clock.
import signal_aggregator as sa
assert sa.BaseDataProvider.CANDLE_SECONDS[Timeframe.M15] == 900
assert not hasattr(sa, "ForexDataProvider"), \
assert not hasattr(sa, "TwelveDataProvider"), \
"aggregator must not depend on a concrete provider class"