Skip to content

Commit 4f4a8f3

Browse files
committed
Fix AI smoke runner price sourcing
1 parent 751370a commit 4f4a8f3

1 file changed

Lines changed: 69 additions & 3 deletions

File tree

run_ai_trading_smoke.py

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,80 @@
44

55
import pandas as pd
66
import yaml
7+
import yfinance as yf
78

89
from ingest_prices import PriceIngestor
910
from llm_trader import propose_trades_with_llm
1011

1112

13+
REQUIRED_PRICE_COLUMNS = ["date", "open", "high", "low", "close", "volume"]
14+
15+
1216
def load_config(path="config.yaml"):
1317
with open(path, "r") as handle:
1418
return yaml.safe_load(handle)
1519

1620

21+
def _normalize_prices(symbol, prices_df):
22+
if prices_df is None:
23+
return None
24+
df = prices_df.copy()
25+
if df.empty:
26+
return None
27+
28+
if "date" not in df.columns:
29+
df = df.reset_index()
30+
31+
normalized_columns = {}
32+
for col in df.columns:
33+
key = str(col).strip().lower().replace(" ", "_")
34+
normalized_columns[col] = key
35+
df = df.rename(columns=normalized_columns)
36+
37+
if "datetime" in df.columns and "date" not in df.columns:
38+
df = df.rename(columns={"datetime": "date"})
39+
if "adj_close" in df.columns and "close" not in df.columns:
40+
df = df.rename(columns={"adj_close": "close"})
41+
42+
if "date" not in df.columns:
43+
return None
44+
45+
if "volume" not in df.columns:
46+
df["volume"] = 0
47+
for col in ["open", "high", "low", "close"]:
48+
if col not in df.columns:
49+
return None
50+
51+
df["symbol"] = str(symbol or "").strip().upper()
52+
return df[["symbol", *REQUIRED_PRICE_COLUMNS]]
53+
54+
55+
def _fetch_yfinance_daily(symbol):
56+
ticker = yf.Ticker(symbol)
57+
df = ticker.history(period="1y", interval="1d", auto_adjust=False)
58+
return _normalize_prices(symbol, df)
59+
60+
61+
def fetch_candidate_prices(ingestor, symbol):
62+
methods = [
63+
("twelvedata", lambda: ingestor.fetch_twelvedata_daily(symbol)),
64+
("yfinance", lambda: _fetch_yfinance_daily(symbol)),
65+
("stooq", lambda: ingestor.fetch_stooq_data(symbol)),
66+
]
67+
errors = []
68+
for source_name, loader in methods:
69+
try:
70+
df = loader()
71+
except Exception as exc:
72+
errors.append(f"{source_name}:{exc}")
73+
continue
74+
df = _normalize_prices(symbol, df)
75+
if df is not None and not df.empty:
76+
return df, source_name, None
77+
errors.append(f"{source_name}:empty")
78+
return None, None, "; ".join(errors)
79+
80+
1781
def compute_candidate(symbol, prices_df):
1882
df = prices_df.copy()
1983
if df.empty or len(df) < 60:
@@ -80,14 +144,15 @@ def build_candidates(config, tickers):
80144
candidates = []
81145
failures = []
82146
for symbol in tickers:
83-
df = ingestor.fetch_stooq_data(symbol)
147+
df, source_name, error = fetch_candidate_prices(ingestor, symbol)
84148
if df is None or df.empty:
85-
failures.append({"symbol": symbol, "error": "no_price_data"})
149+
failures.append({"symbol": symbol, "error": error or "no_price_data"})
86150
continue
87151
candidate = compute_candidate(symbol, df)
88152
if candidate is None:
89-
failures.append({"symbol": symbol, "error": "insufficient_history"})
153+
failures.append({"symbol": symbol, "error": f"insufficient_history:{source_name}"})
90154
continue
155+
candidate["price_source"] = source_name
91156
candidates.append(candidate)
92157
return candidates, failures
93158

@@ -113,6 +178,7 @@ def main():
113178
"candidate_failures": failures,
114179
"status": status,
115180
"trades": trades,
181+
"price_sources": {c["symbol"]: c.get("price_source") for c in candidates},
116182
}
117183

118184
os.makedirs("results", exist_ok=True)

0 commit comments

Comments
 (0)