File size: 33,767 Bytes
e85a672
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
"""
db/database.py
DuckDB schema + connection manager for Equity Workbench ETL.
"""
import duckdb
import os
from pathlib import Path
from loguru import logger


DB_PATH = os.getenv("DB_PATH", "./data/equity.duckdb")


def get_connection() -> duckdb.DuckDBPyConnection:
    Path(DB_PATH).parent.mkdir(parents=True, exist_ok=True)
    conn = duckdb.connect(DB_PATH)
    # ── Extension Setup ───────────────────────────────────────────
    try:
        conn.execute("INSTALL vss;")
        conn.execute("LOAD vss;")
        conn.execute("SET hnsw_enable_experimental_persistence = true;")
    except Exception as e:
        logger.warning(f"Failed to load VSS extension: {e}")
    return conn


def init_db():
    """Create all tables if they don't exist."""
    conn = get_connection()
    try:
        # ── Stocks ────────────────────────────────────────────────────────────────
        conn.execute("""
            CREATE SEQUENCE IF NOT EXISTS stock_quotes_id_seq;
            CREATE TABLE IF NOT EXISTS stock_quotes (
                id          INTEGER PRIMARY KEY DEFAULT nextval('stock_quotes_id_seq'),
                ticker      TEXT    NOT NULL,
                ts          TEXT    NOT NULL,          -- ISO-8601 UTC
                bid         REAL,
                ask         REAL,
                last        REAL,
                "close"     REAL,
                volume      INTEGER,
                "open"      REAL,
                high        REAL,
                low         REAL,
                vwap        REAL,
                created_at  TIMESTAMP DEFAULT now()
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_sq_ticker_ts
                ON stock_quotes(ticker, ts)
        """)

        # ── Options ───────────────────────────────────────────────────────────────
        conn.execute("""
            CREATE SEQUENCE IF NOT EXISTS option_quotes_id_seq;
            CREATE TABLE IF NOT EXISTS option_quotes (
                id              INTEGER PRIMARY KEY DEFAULT nextval('option_quotes_id_seq'),
                ticker          TEXT    NOT NULL,   -- underlying
                expiry          TEXT    NOT NULL,   -- YYYYMMDD
                strike          REAL    NOT NULL,
                "right"         TEXT    NOT NULL,   -- 'C' or 'P'
                ts              TEXT    NOT NULL,
                bid             REAL,
                ask             REAL,
                last            REAL,
                volume          INTEGER,
                open_interest   INTEGER,
                implied_vol     REAL,
                delta           REAL,
                gamma           REAL,
                theta           REAL,
                vega            REAL,
                und_price       REAL,
                pv_dividend     REAL,
                created_at      TIMESTAMP DEFAULT now()
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_oq_ticker_expiry
                ON option_quotes(ticker, expiry, strike, "right")
        """)

        # ── Option Chains (metadata) ───────────────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS option_chains (
                ticker      TEXT    NOT NULL,
                expiry      TEXT    NOT NULL,
                strike      REAL    NOT NULL,
                "right"     TEXT    NOT NULL,
                exchange    TEXT,
                fetched_at  TIMESTAMP DEFAULT now(),
                UNIQUE(ticker, expiry, strike, "right")
            )
        """)

        # ── ETL Run Log ────────────────────────────────────────────────────────
        conn.execute("""
            CREATE SEQUENCE IF NOT EXISTS etl_runs_id_seq;
            CREATE TABLE IF NOT EXISTS etl_runs (
                id          INTEGER PRIMARY KEY DEFAULT nextval('etl_runs_id_seq'),
                run_type    TEXT    NOT NULL,   -- 'stocks' | 'options' | 'chain' | 'polygon_bars_bronze' | ...
                status      TEXT    NOT NULL,   -- 'ok' | 'error'
                message     TEXT,
                rows_written INTEGER DEFAULT 0,
                started_at  TEXT    NOT NULL,
                finished_at TEXT
            )
        """)

        # ── Polygon: OHLCV bars ───────────────────────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS polygon_bars (
                ticker          TEXT    NOT NULL,
                ts              TEXT    NOT NULL,   -- bar open time, ISO-8601 UTC
                timespan        TEXT    NOT NULL,   -- 'day' | 'minute' | 'hour'
                open            REAL,
                high            REAL,
                low             REAL,
                close           REAL,
                volume          REAL,
                vwap            REAL,
                transactions    INTEGER,
                created_at      TIMESTAMP DEFAULT now(),
                UNIQUE(ticker, ts, timespan)
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_pb_ticker_ts
                ON polygon_bars(ticker, ts, timespan)
        """)

        # ── Polygon: real-time / delayed snapshots ────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS polygon_snapshots (
                ticker      TEXT    NOT NULL,
                ts          TEXT    NOT NULL,
                bid         REAL,
                ask         REAL,
                last        REAL,
                prev_close  REAL,
                day_volume  REAL,
                created_at  TIMESTAMP DEFAULT now()
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_ps_ticker_ts
                ON polygon_snapshots(ticker, ts)
        """)

        # ── Polygon: options chain snapshots ──────────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS polygon_option_snapshots (
                underlying      TEXT    NOT NULL,
                expiry          TEXT    NOT NULL,   -- YYYY-MM-DD
                strike          REAL    NOT NULL,
                "right"         TEXT    NOT NULL,   -- 'call' | 'put'
                ts              TEXT    NOT NULL,
                day_open        REAL,
                day_close       REAL,
                day_volume      INTEGER,
                open_interest   INTEGER,
                implied_vol     REAL,
                delta           REAL,
                gamma           REAL,
                theta           REAL,
                vega            REAL,
                created_at      TIMESTAMP DEFAULT now()
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_pos_underlying
                ON polygon_option_snapshots(underlying, expiry, strike, "right")
        """)

        # ── Polygon: ticker reference / metadata ──────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS polygon_tickers (
                ticker           TEXT    NOT NULL UNIQUE,
                name             TEXT,
                market           TEXT,
                primary_exchange TEXT,
                type             TEXT,
                active           INTEGER,
                currency         TEXT,
                description      TEXT,
                updated_at       TEXT    NOT NULL
            )
        """)

        # ── Polygon: historical options OHLCV bars ───────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS polygon_option_bars (
                option_ticker   TEXT    NOT NULL,   -- e.g. O:AAPL240119C00150000
                underlying      TEXT    NOT NULL,
                expiry          TEXT,               -- YYYY-MM-DD
                strike          REAL,
                "right"         TEXT,               -- 'call' | 'put'
                ts              TEXT    NOT NULL,   -- bar open time, ISO-8601 UTC
                timespan        TEXT    NOT NULL,   -- 'day' | 'minute'
                open            REAL,
                high            REAL,
                low             REAL,
                close           REAL,
                volume          REAL,
                vwap            REAL,
                transactions    INTEGER,
                created_at      TIMESTAMP DEFAULT now(),
                UNIQUE(option_ticker, ts, timespan)
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_pob_underlying
                ON polygon_option_bars(underlying, expiry, strike, "right")
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_pob_ticker_ts
                ON polygon_option_bars(option_ticker, ts)
        """)

        # ── Polygon: individual trade ticks ──────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS polygon_trades (
                ticker      TEXT    NOT NULL,
                ts          TEXT    NOT NULL,   -- SIP timestamp, ISO-8601 microsecond UTC
                price       REAL,
                size        REAL,
                conditions  TEXT,               -- comma-separated condition codes
                exchange    INTEGER,
                tape        TEXT,
                created_at  TIMESTAMP DEFAULT now(),
                UNIQUE(ticker, ts, exchange)
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_ptrades_ticker_ts
                ON polygon_trades(ticker, ts)
        """)

        # ── EDGAR: filing metadata ────────────────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS edgar_filings (
                ticker           TEXT    NOT NULL,
                cik              TEXT    NOT NULL,
                form_type        TEXT    NOT NULL,
                filed_date       TEXT,
                accession_number TEXT    NOT NULL,
                primary_doc      TEXT,
                created_at       TIMESTAMP DEFAULT now(),
                UNIQUE(accession_number)
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_ef_ticker_form
                ON edgar_filings(ticker, form_type, filed_date)
        """)

        # ── EDGAR: XBRL financial facts ───────────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS edgar_facts (
                ticker           TEXT    NOT NULL,
                cik              TEXT    NOT NULL,
                taxonomy         TEXT    NOT NULL,
                concept          TEXT    NOT NULL,
                label            TEXT,
                unit             TEXT,
                value            REAL,
                period_start     TEXT,
                period_end       TEXT,
                form_type        TEXT,
                filed_date       TEXT,
                accession_number TEXT,
                created_at       TIMESTAMP DEFAULT now(),
                UNIQUE(ticker, concept, unit, period_end, form_type)
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_edgar_facts_ticker
                ON edgar_facts(ticker, concept, period_end)
        """)

        # ── COT: Commitments of Traders (CFTC) ────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS cot_reports (
                market_name     TEXT    NOT NULL,
                ticker          TEXT,               -- Optional mapping to IBKR ticker
                report_date     TEXT    NOT NULL,   -- ISO-8601
                noncomm_long    INTEGER,
                noncomm_short   INTEGER,
                comm_long       INTEGER,
                comm_short      INTEGER,
                total_long      INTEGER,
                total_short     INTEGER,
                noncomm_spreads INTEGER,
                open_interest   INTEGER,
                created_at      TIMESTAMP DEFAULT now(),
                UNIQUE(market_name, report_date)
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_cot_market_date
                ON cot_reports(market_name, report_date)
        """)

        # ── Vector Storage ────────────────────────────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS ticker_embeddings (
                ticker      TEXT PRIMARY KEY,
                industry    TEXT,
                source      TEXT,
                text        TEXT,
                embedding   FLOAT[384],    -- all-MiniLM-L6-v2 dimension
                updated_at  TIMESTAMP DEFAULT now()
            )
        """)
        try:
            conn.execute("""
                CREATE INDEX IF NOT EXISTS idx_ticker_emb
                ON ticker_embeddings USING HNSW (embedding)
                WITH (metric = 'cosine')
            """)
        except Exception as e:
            logger.warning(f"Failed to create HNSW index on ticker_embeddings: {e}")

        conn.execute("""
            CREATE SEQUENCE IF NOT EXISTS edgar_embeddings_id_seq;
            CREATE TABLE IF NOT EXISTS edgar_embeddings (
                id          INTEGER PRIMARY KEY DEFAULT nextval('edgar_embeddings_id_seq'),
                ticker      TEXT,
                accession   TEXT,
                text        TEXT,
                embedding   FLOAT[384],
                updated_at  TIMESTAMP DEFAULT now()
            )
        """)
        try:
            conn.execute("""
                CREATE INDEX IF NOT EXISTS idx_edgar_emb
                ON edgar_embeddings USING HNSW (embedding)
                WITH (metric = 'cosine')
            """)
        except Exception as e:
            logger.warning(f"Failed to create HNSW index on edgar_embeddings: {e}")

        # ══ SILVER LAYER ═══════════════════════════════════════════════════════
        # Derived, recomputable feature tables built from bronze bars.
        # Grain: one row per entity per trading day. Rebuilt with INSERT OR REPLACE.

        # ── Silver: per-stock daily technical features ─────────────────────────
        # Source: polygon_bars WHERE timespan='day'. Windows are trailing N days.
        conn.execute("""
            CREATE TABLE IF NOT EXISTS silver_stock_features (
                ticker        TEXT    NOT NULL,
                ts            TEXT    NOT NULL,   -- trading day, ISO-8601 (bronze join key)
                trade_date    DATE,              -- typed date for window ordering
                close         DOUBLE,            -- from bronze polygon_bars (day)
                volume        DOUBLE,
                daily_return  DOUBLE,            -- close / prev_close - 1
                -- simple moving averages of close
                ma_20         DOUBLE,
                ma_50         DOUBLE,
                ma_100        DOUBLE,
                -- rolling sample standard deviation of close
                std_20        DOUBLE,
                std_50        DOUBLE,
                std_100       DOUBLE,
                pct_change    DOUBLE,            -- daily_return * 100 (percent)
                -- price z-score = (close - ma_N) / std_N
                zscore_20     DOUBLE,
                zscore_50     DOUBLE,
                zscore_100    DOUBLE,
                -- sigma band flag on price: '+3s' | 'normal' | '-3s' | NULL
                sigma_flag_20  TEXT,
                sigma_flag_50  TEXT,
                sigma_flag_100 TEXT,
                -- return z-score = (pct_change - mean_ret_N) / std_ret_N
                zscore_ret_20  DOUBLE,
                zscore_ret_50  DOUBLE,
                zscore_ret_100 DOUBLE,
                -- sigma band flag on returns
                sigma_flag_ret_20  TEXT,
                sigma_flag_ret_50  TEXT,
                sigma_flag_ret_100 TEXT,
                -- rolling VWAP = sum(typical_price*volume)/sum(volume), typical=(h+l+c)/3
                vwap_20       DOUBLE,
                vwap_50       DOUBLE,
                vwap_100      DOUBLE,
                computed_at   TIMESTAMP DEFAULT now(),
                UNIQUE(ticker, ts)
            )
        """)
        # ── Migrate: add sigma_flag columns if they don't exist yet ───────────
        for col in ("sigma_flag_20", "sigma_flag_50", "sigma_flag_100"):
            try:
                conn.execute(f"ALTER TABLE silver_stock_features ADD COLUMN {col} TEXT")
                logger.info(f"Migrated silver_stock_features: added {col}")
            except Exception:
                pass  # column already exists
        for col in ("pct_change",):
            try:
                conn.execute(f"ALTER TABLE silver_stock_features ADD COLUMN {col} DOUBLE")
                logger.info(f"Migrated silver_stock_features: added {col}")
            except Exception:
                pass
        for col in ("zscore_ret_20", "zscore_ret_50", "zscore_ret_100"):
            try:
                conn.execute(f"ALTER TABLE silver_stock_features ADD COLUMN {col} DOUBLE")
                logger.info(f"Migrated silver_stock_features: added {col}")
            except Exception:
                pass
        for col in ("sigma_flag_ret_20", "sigma_flag_ret_50", "sigma_flag_ret_100"):
            try:
                conn.execute(f"ALTER TABLE silver_stock_features ADD COLUMN {col} TEXT")
                logger.info(f"Migrated silver_stock_features: added {col}")
            except Exception:
                pass
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_ssf_ticker_date
                ON silver_stock_features(ticker, trade_date)
        """)

        # ── View: latest z-score alerts per ticker ─────────────────────────────
        # Query this view to see which tickers are currently outside +-3 std devs.
        # sigma_flag values: '+3s' (above), '-3s' (below), 'normal', NULL (warm-up)
        conn.execute("DROP VIEW IF EXISTS v_zscore_alerts")
        conn.execute("""
            CREATE VIEW v_zscore_alerts AS
            WITH latest AS (
                SELECT *,
                       ROW_NUMBER() OVER (PARTITION BY ticker ORDER BY trade_date DESC) AS rn
                FROM silver_stock_features
            )
            SELECT
                ticker,
                trade_date,
                close,
                pct_change,
                zscore_20,   sigma_flag_20,
                zscore_50,   sigma_flag_50,
                zscore_100,  sigma_flag_100,
                zscore_ret_20,  sigma_flag_ret_20,
                zscore_ret_50,  sigma_flag_ret_50,
                zscore_ret_100, sigma_flag_ret_100,
                CASE
                    WHEN sigma_flag_20      IN ('+3s', '-3s')
                      OR sigma_flag_50      IN ('+3s', '-3s')
                      OR sigma_flag_100     IN ('+3s', '-3s')
                      OR sigma_flag_ret_20  IN ('+3s', '-3s')
                      OR sigma_flag_ret_50  IN ('+3s', '-3s')
                      OR sigma_flag_ret_100 IN ('+3s', '-3s')
                    THEN true ELSE false
                END AS any_breach,
                GREATEST(
                    ABS(COALESCE(zscore_20,      0)),
                    ABS(COALESCE(zscore_50,      0)),
                    ABS(COALESCE(zscore_100,     0)),
                    ABS(COALESCE(zscore_ret_20,  0)),
                    ABS(COALESCE(zscore_ret_50,  0)),
                    ABS(COALESCE(zscore_ret_100, 0))
                ) AS max_abs_zscore
            FROM latest
            WHERE rn = 1
            ORDER BY max_abs_zscore DESC
        """)

        # ── Silver: per-contract daily option greeks (Black-Scholes-Merton) ────
        # Source: polygon_option_bars (option price) JOIN polygon_bars (underlying
        # close). implied_vol solved from the option's market close; greeks analytic.
        conn.execute("""
            CREATE TABLE IF NOT EXISTS silver_option_greeks (
                option_ticker   TEXT    NOT NULL,   -- OPRA symbol
                underlying      TEXT    NOT NULL,
                expiry          TEXT,               -- YYYY-MM-DD
                strike          DOUBLE,
                "right"         TEXT,               -- 'call' | 'put'
                ts              TEXT    NOT NULL,   -- trading day, ISO-8601
                trade_date      DATE,
                option_close    DOUBLE,             -- option price from bronze bar
                und_close       DOUBLE,             -- underlying close (S)
                time_to_expiry  DOUBLE,             -- years to expiry (ACT/365)
                moneyness       DOUBLE,             -- und_close / strike
                risk_free_rate  DOUBLE,             -- r assumption used
                dividend_yield  DOUBLE,             -- q assumption used (default 0)
                implied_vol     DOUBLE,             -- sigma solved from option_close
                delta           DOUBLE,
                gamma           DOUBLE,
                theta           DOUBLE,             -- per calendar day
                vega            DOUBLE,             -- per 1 vol point
                rho             DOUBLE,
                computed_at     TIMESTAMP DEFAULT now(),
                UNIQUE(option_ticker, ts)
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_sog_underlying_date
                
                ON silver_option_greeks(underlying, trade_date)
        """)

        # -- Silver: per-underlying daily options POSITIONING --
        conn.execute("""
            CREATE TABLE IF NOT EXISTS silver_option_positioning (
                underlying      TEXT    NOT NULL,
                ts              TEXT    NOT NULL,
                trade_date      DATE,
                total_volume    DOUBLE,
                call_volume     DOUBLE,
                put_volume      DOUBLE,
                put_call_ratio  DOUBLE,
                atm_iv          DOUBLE,
                call_iv_25d     DOUBLE,
                put_iv_25d      DOUBLE,
                iv_skew_25d     DOUBLE,
                n_contracts     INTEGER,
                computed_at     TIMESTAMP DEFAULT now(),
                UNIQUE(underlying, ts)
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_sop_underlying_date
                ON silver_option_positioning(underlying, trade_date)
        """)

        # ── Silver: COT (Commitments of Traders) positioning features ─────────
        # Source: cot_reports (Bronze, weekly). Grain: one row per (ticker, report_date).
        conn.execute("""
            CREATE TABLE IF NOT EXISTS silver_cot_features (
                report_date          DATE    NOT NULL,
                ticker               TEXT    NOT NULL,
                noncomm_long         BIGINT,
                noncomm_short        BIGINT,
                noncomm_net          BIGINT,
                comm_long            BIGINT,
                comm_short           BIGINT,
                comm_net             BIGINT,
                n_weeks              INTEGER,
                net_pos_mean_52w     DOUBLE,
                net_pos_std_52w      DOUBLE,
                net_pos_zscore_52w   DOUBLE,
                comm_net_mean_52w    DOUBLE,
                comm_net_std_52w     DOUBLE,
                comm_net_zscore_52w  DOUBLE,
                spec_comm_divergence DOUBLE,
                crowd_flag           TEXT,
                computed_at          TIMESTAMP DEFAULT now(),
                PRIMARY KEY (report_date, ticker)
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_scf_ticker_date
                ON silver_cot_features(ticker, report_date)
        """)

        # ── Silver: futures continuous-contract price features ────────────────
        # Source: polygon_bars (Bronze, day bars) for continuous futures tickers
        # like 'ES1:COM'. Grain: one row per (ticker, trade_date).
        conn.execute("""
            CREATE TABLE IF NOT EXISTS silver_futures_features (
                trade_date      DATE    NOT NULL,
                ticker          TEXT    NOT NULL,
                open_price      DOUBLE,
                high_price      DOUBLE,
                low_price       DOUBLE,
                close_price     DOUBLE,
                volume          BIGINT,
                n20             INTEGER,
                ma_20           DOUBLE,
                std_20          DOUBLE,
                zscore_20       DOUBLE,
                n_ret20         INTEGER,
                ret             DOUBLE,
                mean_ret_20     DOUBLE,
                std_ret_20      DOUBLE,
                zscore_ret_20   DOUBLE,
                vx_term_slope   DOUBLE,
                regime_flag     TEXT,
                computed_at     TIMESTAMP DEFAULT now(),
                PRIMARY KEY (trade_date, ticker)
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_sff_ticker_date
                ON silver_futures_features(ticker, trade_date)
        """)

        # ── View: latest COT positioning + futures price context per ticker ───
        conn.execute("DROP VIEW IF EXISTS v_cot_positioning")
        conn.execute("""
            CREATE VIEW v_cot_positioning AS
            WITH latest_cot AS (
                SELECT *, ROW_NUMBER() OVER (PARTITION BY ticker ORDER BY report_date DESC) AS rn
                FROM silver_cot_features
            ),
            latest_fut AS (
                SELECT *, ROW_NUMBER() OVER (PARTITION BY ticker ORDER BY trade_date DESC) AS rn
                FROM silver_futures_features
            )
            SELECT
                c.ticker,
                c.report_date,
                c.noncomm_net,
                c.net_pos_zscore_52w,
                c.comm_net_zscore_52w,
                c.spec_comm_divergence,
                c.crowd_flag,
                f.trade_date AS futures_date,
                f.close_price AS futures_close,
                f.zscore_20 AS futures_zscore_20,
                f.vx_term_slope,
                f.regime_flag
            FROM latest_cot c
            LEFT JOIN latest_fut f ON f.ticker = c.ticker || '1:COM' AND f.rn = 1
            WHERE c.rn = 1
            ORDER BY ABS(COALESCE(c.net_pos_zscore_52w, 0)) DESC
        """)

        # ══ GOLD LAYER ════════════════════
        # Backtest results: runs, trades, portfolio snapshots, metrics, signals.

        # ── Gold: backtest run metadata ───────────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS gold_backtest_runs (
                run_id      TEXT NOT NULL PRIMARY KEY,
                config      TEXT,
                universe    TEXT,
                start_date  DATE NOT NULL,
                end_date    DATE NOT NULL,
                created_at  TIMESTAMP DEFAULT now(),
                fold_id     INTEGER,           -- walk-forward OOS fold (NULL = plain run)
                wf_run_id   TEXT               -- parent walk-forward run (NULL = plain run)
            )
        """)
        # ── Migrate: add fold_id / wf_run_id if the table predates walk-forward
        try:
            conn.execute("ALTER TABLE gold_backtest_runs ADD COLUMN fold_id INTEGER")
            logger.info("Migrated gold_backtest_runs: added fold_id")
        except Exception:
            pass  # column already exists
        try:
            conn.execute("ALTER TABLE gold_backtest_runs ADD COLUMN wf_run_id TEXT")
            logger.info("Migrated gold_backtest_runs: added wf_run_id")
        except Exception:
            pass  # column already exists

        # ── Gold: individual simulated trades ─────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS gold_trades (
                trade_id        TEXT    NOT NULL PRIMARY KEY,
                run_id          TEXT    NOT NULL,
                ticker          TEXT    NOT NULL,
                entry_date      DATE    NOT NULL,
                exit_date       DATE,
                direction       INTEGER NOT NULL,
                shares          DOUBLE,
                entry_price     DOUBLE,
                exit_price      DOUBLE,
                gross_pnl       DOUBLE,
                slippage_cost   DOUBLE,
                commission_cost DOUBLE,
                net_pnl         DOUBLE,
                entry_regime    TEXT,
                exit_regime     TEXT,
                signal_type     TEXT
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_gt_run_ticker
                ON gold_trades(run_id, ticker, entry_date)
        """)

        # ── Gold: daily portfolio snapshots ───────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS gold_portfolio (
                run_id       TEXT NOT NULL,
                trade_date   DATE NOT NULL,
                nav          DOUBLE,
                cash         DOUBLE,
                drawdown_pct DOUBLE,
                n_positions  INTEGER,
                UNIQUE(run_id, trade_date)
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_gp_run_date
                ON gold_portfolio(run_id, trade_date)
        """)

        # ── Gold: aggregated metrics per run ──────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS gold_metrics (
                run_id            TEXT    NOT NULL PRIMARY KEY,
                sharpe            DOUBLE,
                sortino           DOUBLE,
                mdd_pct           DOUBLE,
                mdd_duration_days INTEGER,
                mdd_recovery_days INTEGER,
                calmar            DOUBLE,
                win_rate          DOUBLE,
                profit_factor     DOUBLE,
                expectancy        DOUBLE,
                cost_drag         DOUBLE,
                ann_return        DOUBLE,
                ann_vol           DOUBLE
            )
        """)

        # ── Gold: walk-forward OOS aggregate summary per walk-forward run ─────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS gold_oos_summary (
                run_id TEXT NOT NULL,
                run_ts TIMESTAMPTZ DEFAULT current_timestamp,
                n_folds INTEGER,
                mean_sharpe DOUBLE,
                consistency_ratio DOUBLE,
                combined_sharpe DOUBLE,
                worst_fold_mdd DOUBLE,
                split_type TEXT,
                embargo_days INTEGER,
                PRIMARY KEY (run_id)
            )
        """)

        # ── Gold: immutable signal audit log ──────────────────────────────────
        conn.execute("""
            CREATE TABLE IF NOT EXISTS gold_signals (
                signal_id   TEXT    NOT NULL PRIMARY KEY,
                run_id      TEXT    NOT NULL,
                ticker      TEXT    NOT NULL,
                signal_date DATE    NOT NULL,
                direction   INTEGER NOT NULL,
                strength    DOUBLE,
                signal_type TEXT,
                computed_at TIMESTAMP DEFAULT now()
            )
        """)
        conn.execute("""
            CREATE INDEX IF NOT EXISTS idx_gs_run_date
                ON gold_signals(run_id, signal_date, ticker)
        """)

    finally:
        conn.close()

    logger.info(f"Database initialised at {DB_PATH}")