feat: bank forceasts
This commit is contained in:
@@ -235,3 +235,127 @@ def score_text(body: ScoreTextRequest):
|
||||
except Exception as e:
|
||||
logger.error(f"Text scoring error: {e}")
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
# ── Bank Forecasts ─────────────────────────────────────────────────────────────
|
||||
|
||||
class BankSourceUpsert(BaseModel):
|
||||
name: str
|
||||
url: str = ""
|
||||
active: bool = True
|
||||
notes: str = ""
|
||||
|
||||
|
||||
@router.get("/bank-forecasts/sources")
|
||||
def list_bank_sources():
|
||||
from services.database import get_conn
|
||||
conn = get_conn()
|
||||
try:
|
||||
rows = conn.execute(
|
||||
"SELECT id, name, url, active, last_scraped, notes FROM bank_forecast_sources ORDER BY name"
|
||||
).fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@router.put("/bank-forecasts/sources/{source_id}")
|
||||
def upsert_bank_source(source_id: str, body: BankSourceUpsert):
|
||||
from services.database import get_conn
|
||||
conn = get_conn()
|
||||
try:
|
||||
conn.execute(
|
||||
"""INSERT INTO bank_forecast_sources (id, name, url, active, notes)
|
||||
VALUES (?,?,?,?,?)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
name=excluded.name, url=excluded.url,
|
||||
active=excluded.active, notes=excluded.notes""",
|
||||
(source_id, body.name, body.url, int(body.active), body.notes)
|
||||
)
|
||||
conn.commit()
|
||||
return {"saved": source_id}
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@router.get("/bank-forecasts")
|
||||
def list_bank_forecasts(series_id: str = "", event_date: str = ""):
|
||||
from services.database import get_conn
|
||||
conn = get_conn()
|
||||
try:
|
||||
wheres = ["1=1"]
|
||||
params = []
|
||||
if series_id:
|
||||
wheres.append("bf.series_id = ?"); params.append(series_id)
|
||||
if event_date:
|
||||
wheres.append("bf.event_date = ?"); params.append(event_date)
|
||||
rows = conn.execute(
|
||||
f"""SELECT bf.*, bs.name as bank_name
|
||||
FROM bank_forecasts bf
|
||||
JOIN bank_forecast_sources bs ON bs.id = bf.source_id
|
||||
WHERE {' AND '.join(wheres)}
|
||||
ORDER BY bf.event_date DESC, bf.extracted_at DESC
|
||||
LIMIT 500""",
|
||||
params
|
||||
).fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@router.get("/bank-forecasts/consensus")
|
||||
def get_consensus():
|
||||
"""Return one consensus row per (series_id, event_date) = average of bank forecasts."""
|
||||
from services.database import get_conn
|
||||
conn = get_conn()
|
||||
try:
|
||||
rows = conn.execute(
|
||||
"""SELECT series_id, event_name, event_date,
|
||||
ROUND(AVG(forecast_value), 4) as consensus,
|
||||
COUNT(*) as bank_count,
|
||||
MIN(forecast_value) as min_forecast,
|
||||
MAX(forecast_value) as max_forecast
|
||||
FROM bank_forecasts
|
||||
WHERE forecast_value IS NOT NULL
|
||||
GROUP BY series_id, event_date
|
||||
ORDER BY event_date DESC"""
|
||||
).fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@router.post("/bank-forecasts/scrape")
|
||||
def trigger_scrape(source_id: str = ""):
|
||||
"""Scrape one source (source_id) or all active sources (empty)."""
|
||||
from services.database import get_conn
|
||||
from services.bank_forecast_scraper import scrape_source, scrape_all_active
|
||||
conn = get_conn()
|
||||
try:
|
||||
if source_id:
|
||||
s = conn.execute("SELECT * FROM bank_forecast_sources WHERE id=?", (source_id,)).fetchone()
|
||||
if not s:
|
||||
raise HTTPException(404, "Source not found")
|
||||
result = [scrape_source(conn, dict(s))]
|
||||
else:
|
||||
result = scrape_all_active(conn)
|
||||
return {"results": result, "total_sources": len(result)}
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@router.post("/bank-forecasts/push-consensus")
|
||||
def push_consensus_endpoint(series_id: str = "", event_date: str = ""):
|
||||
"""Push bank consensus to macro_series_log. Filters optional."""
|
||||
from services.database import get_conn
|
||||
from services.bank_forecast_scraper import push_consensus_to_log, push_all_consensus
|
||||
conn = get_conn()
|
||||
try:
|
||||
if series_id and event_date:
|
||||
val = push_consensus_to_log(conn, series_id, event_date)
|
||||
return {"pushed": [{"series_id": series_id, "event_date": event_date, "consensus": val}]}
|
||||
else:
|
||||
results = push_all_consensus(conn)
|
||||
return {"pushed": results}
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
Reference in New Issue
Block a user