Skip to content

Commit 312749e

Browse files
author
Dennis Donaghy
committed
Solidify validation and add executive + freshness dashboard views
1 parent 8e02a5c commit 312749e

7 files changed

Lines changed: 113 additions & 6 deletions

File tree

docs/graphql_query_pack.graphql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,20 @@ query SelectionSetV1Dashboard {
6060
avg_theta
6161
}
6262
}
63+
64+
query SelectionSetV1Executive {
65+
analytics_selection_set_v1_executive(order_by: [{block: asc}, {symbol: asc}]) {
66+
block
67+
symbol
68+
source
69+
last_ts
70+
last_close
71+
last_volume
72+
}
73+
analytics_ingestion_freshness_sla(order_by: [{source: asc}]) {
74+
source
75+
last_run_at
76+
minutes_since_last_run
77+
sla_status
78+
}
79+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
create schema if not exists analytics;
2+
3+
create or replace view analytics.ingestion_freshness_sla as
4+
select
5+
source,
6+
max(started_at) as last_run_at,
7+
extract(epoch from (now() - max(started_at))) / 60.0 as minutes_since_last_run,
8+
case
9+
when extract(epoch from (now() - max(started_at))) / 60.0 > 90 then 'warn'
10+
else 'ok'
11+
end as sla_status
12+
from ingestion.runs
13+
group by source;
14+
15+
create or replace view analytics.selection_set_v1_executive as
16+
select 'rates'::text as block, symbol, source, last_ts, last_close, last_volume
17+
from analytics.selection_set_v1_latest_bars
18+
where symbol in ('SOFR','MORTGAGE30US','DGS10','DGS2')
19+
union all
20+
select 'volatility'::text, symbol, source, last_ts, last_close, last_volume
21+
from analytics.selection_set_v1_latest_bars
22+
where symbol in ('VIXCLS','VXX','UVXY','SVXY')
23+
union all
24+
select 'equities_etf'::text, symbol, source, last_ts, last_close, last_volume
25+
from analytics.selection_set_v1_latest_bars
26+
where symbol in ('SPY','QQQ','IWM','DIA','VTI','TLT','IEF','XLF','XLK')
27+
union all
28+
select 'crypto'::text, symbol, source, last_ts, last_close, last_volume
29+
from analytics.selection_set_v1_latest_bars
30+
where symbol in ('BTCUSD','ETHUSD','SOLUSD');

services/ingestion/pull_crypto_coingecko.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from datetime import datetime, timezone
55
from urllib import parse, request
66
import psycopg
7+
from validation_models import MarketBarIn
78

89
DB_URL = os.getenv("DATABASE_URL", "postgresql://quant:quant_dev_change_me@127.0.0.1:5432/quant")
910
COINS = [c.strip().lower() for c in os.getenv("CRYPTO_COINS", "bitcoin,ethereum,solana").split(",") if c.strip()]
@@ -47,14 +48,18 @@ def main():
4748
""",
4849
(symbol, ts, price, mcap, vol, json.dumps(r)),
4950
)
51+
try:
52+
v = MarketBarIn(symbol=symbol, ts=ts, open=price, high=price, low=price, close=price, volume=vol or 0, source='coingecko')
53+
except Exception:
54+
continue
5055
cur.execute(
5156
"""
5257
insert into market.bars(symbol, ts, open, high, low, close, volume, source)
5358
values (%s,%s,%s,%s,%s,%s,%s,'coingecko')
5459
on conflict (symbol, ts) do update
5560
set close=excluded.close, volume=excluded.volume, source=excluded.source
5661
""",
57-
(symbol, ts, price, price, price, price, vol or 0),
62+
(v.symbol, v.ts, v.open, v.high, v.low, v.close, v.volume),
5863
)
5964
written += 1
6065
cur.execute("update ingestion.runs set status='success', completed_at=now(), rows_written=%s where id=%s", (written, run_id))

services/ingestion/pull_equities_bars_stooq.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from datetime import datetime, timezone
66
from urllib import request
77
import psycopg
8+
from validation_models import MarketBarIn
89

910
DB_URL = os.getenv("DATABASE_URL", "postgresql://quant:quant_dev_change_me@127.0.0.1:5432/quant")
1011
SYMBOLS = [s.strip().upper() for s in os.getenv("EQUITY_SYMBOLS", "SPY,QQQ,AAPL,MSFT").split(",") if s.strip()]
@@ -44,14 +45,18 @@ def main():
4445
row = fetch_stooq_daily(sym)
4546
if not row:
4647
continue
48+
try:
49+
v = MarketBarIn(**{**row, "source": "stooq"})
50+
except Exception:
51+
continue
4752
cur.execute(
4853
"""
4954
insert into market.bars(symbol, ts, open, high, low, close, volume, source)
5055
values (%s,%s,%s,%s,%s,%s,%s,'stooq')
5156
on conflict (symbol, ts) do update
5257
set open=excluded.open, high=excluded.high, low=excluded.low, close=excluded.close, volume=excluded.volume, source=excluded.source
5358
""",
54-
(row["symbol"], row["ts"], row["open"], row["high"], row["low"], row["close"], row["volume"]),
59+
(v.symbol, v.ts, v.open, v.high, v.low, v.close, v.volume),
5560
)
5661
written += 1
5762

services/ingestion/pull_macro_fred.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from datetime import datetime, timezone
66
from urllib import request
77
import psycopg
8+
from validation_models import MarketBarIn
89

910
DB_URL = os.getenv("DATABASE_URL", "postgresql://quant:quant_dev_change_me@127.0.0.1:5432/quant")
1011
FRED_SERIES = [s.strip().upper() for s in os.getenv("FRED_SERIES", "SOFR,MORTGAGE30US,DGS10,DGS2,VIXCLS").split(",") if s.strip()]
@@ -34,14 +35,18 @@ def main():
3435
ts, val = fetch_latest(s)
3536
if ts is None:
3637
continue
38+
try:
39+
v = MarketBarIn(symbol=s, ts=ts, open=val, high=val, low=val, close=val, volume=0, source='fred')
40+
except Exception:
41+
continue
3742
cur.execute(
3843
"""
3944
insert into market.bars(symbol, ts, open, high, low, close, volume, source)
4045
values (%s,%s,%s,%s,%s,%s,0,'fred')
4146
on conflict (symbol, ts) do update
4247
set close=excluded.close, source=excluded.source
4348
""",
44-
(s, ts, val, val, val, val),
49+
(v.symbol, v.ts, v.open, v.high, v.low, v.close),
4550
)
4651
written += 1
4752
cur.execute("update ingestion.runs set status='success', completed_at=now(), rows_written=%s where id=%s", (written, run_id))

services/ingestion/pull_tradier_greeks.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from datetime import datetime, timezone
55
from urllib import parse, request
66
import psycopg
7+
from validation_models import GreeksIn
78

89
DB_URL = os.getenv("DATABASE_URL", "postgresql://quant:quant_dev_change_me@127.0.0.1:5432/quant")
910
TRADIER_BASE = os.getenv("TRADIER_SANDBOX_BASE_URL") or os.getenv("TRADIER_PAPER_BASE_URL") or "https://sandbox.tradier.com/v1"
@@ -55,6 +56,22 @@ def main():
5556

5657
for o in options[:150]:
5758
greeks = o.get("greeks") or {}
59+
try:
60+
g = GreeksIn(
61+
underlying=sym,
62+
option_symbol=o.get("symbol") or "",
63+
ts=now,
64+
price=o.get("last") or o.get("bid") or o.get("ask") or 0,
65+
iv=greeks.get("mid_iv") or greeks.get("smv_vol"),
66+
delta=greeks.get("delta"),
67+
gamma=greeks.get("gamma"),
68+
vega=greeks.get("vega"),
69+
theta=greeks.get("theta"),
70+
rho=greeks.get("rho"),
71+
model='tradier'
72+
)
73+
except Exception:
74+
continue
5875
cur.execute(
5976
"""
6077
insert into options.greeks_snapshot(
@@ -64,9 +81,9 @@ def main():
6481
set price=excluded.price, iv=excluded.iv, delta=excluded.delta, gamma=excluded.gamma,
6582
vega=excluded.vega, theta=excluded.theta, rho=excluded.rho, model=excluded.model
6683
""",
67-
(sym, o.get("symbol"), now, o.get("last") or o.get("bid") or o.get("ask"),
68-
greeks.get("mid_iv") or greeks.get("smv_vol"), greeks.get("delta"), greeks.get("gamma"),
69-
greeks.get("vega"), greeks.get("theta"), greeks.get("rho")),
84+
(g.underlying, g.option_symbol, g.ts, g.price,
85+
g.iv, g.delta, g.gamma,
86+
g.vega, g.theta, g.rho),
7087
)
7188
written += 1
7289

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from datetime import datetime
2+
from decimal import Decimal
3+
from pydantic import BaseModel, Field
4+
5+
6+
class MarketBarIn(BaseModel):
7+
symbol: str = Field(min_length=1, max_length=32)
8+
ts: datetime
9+
open: Decimal
10+
high: Decimal
11+
low: Decimal
12+
close: Decimal
13+
volume: Decimal = Decimal("0")
14+
source: str = Field(min_length=1, max_length=32)
15+
16+
17+
class GreeksIn(BaseModel):
18+
underlying: str = Field(min_length=1, max_length=32)
19+
option_symbol: str = Field(min_length=1, max_length=96)
20+
ts: datetime
21+
price: Decimal
22+
iv: Decimal | None = None
23+
delta: Decimal | None = None
24+
gamma: Decimal | None = None
25+
vega: Decimal | None = None
26+
theta: Decimal | None = None
27+
rho: Decimal | None = None
28+
model: str = Field(default="unspecified", max_length=32)

0 commit comments

Comments
 (0)