chanmacro: Signal Expectancy Engine V1 — Market Memory System
Phase A-C complete: 4 core factors, regime detection, signal tracking, Bayesian expectancy. chanmacro/ (32 files, ~4000 lines): - models: 12 enums + 15 Pydantic v2 models (DateAwareModel, MarketStateVector, etc.) - fetchers: OHLCV + Breadth (from data_provider) + Derivatives (new endpoint) - scoring: Price Structure / Breadth (quantile buckets) / OI Matrix (5 discrete states) / Volatility Regime - regime_detector: 3-state (TREND/RANGE/PANIC), factor-locked (Price+Breadth+Vol), versioned, 2-day confirmation - expectancy: SignalTracker (record+outcomes), TimeDecay (half-life=180d), BayesianExpectancyEngine (Empirical Bayes, Leveled, SufficiencyGuard) - validation: FactorValidator (IC/ICIR/Hit Ratio), RegimeValidator (MI/KL/ANOVA), TransitionValidator (stability) - CLI: fetch|score|regime|track|backfill|expectancy|validate|serve - tests: 52 passing (models, scoring, regime, expectancy) data_provider: - /api/derivatives endpoint: funding rate, OI, OI change, basis - _derivatives storage: same persist pattern as K-line (merge→lock→snapshot→atomic write) - background refresh every 60s Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
"""
|
||||
fetchers/derivatives.py — Fetches derivatives data from data_provider API.
|
||||
|
||||
Clean consumer: no direct ccxt dependency. Just HTTP GET /api/derivatives.
|
||||
"""
|
||||
|
||||
from datetime import date as Date
|
||||
from typing import Optional
|
||||
|
||||
import requests
|
||||
|
||||
from .base import BaseFetcher
|
||||
from config import config
|
||||
|
||||
|
||||
class DerivativesFetcher(BaseFetcher):
|
||||
"""Fetches derivatives snapshot from data_provider /api/derivatives."""
|
||||
|
||||
def __init__(self, provider_url: Optional[str] = None):
|
||||
super().__init__(timeout=15, max_retries=3)
|
||||
self.provider_url = provider_url or config.provider_url
|
||||
|
||||
def fetch(self, target_date: Optional[Date] = None) -> list[dict]:
|
||||
"""Fetch derivatives data. Returns list with one record dict."""
|
||||
url = f"{self.provider_url}/api/derivatives"
|
||||
params = {"symbol": config.btc_symbol}
|
||||
try:
|
||||
data = self._get(url, params=params)
|
||||
record = {
|
||||
"date": str(target_date or Date.today()),
|
||||
"symbol": config.btc_symbol,
|
||||
"funding_rate": data.get("funding_rate"),
|
||||
"open_interest": data.get("open_interest"),
|
||||
"oi_24h_change_pct": data.get("oi_change_pct"),
|
||||
"basis_annualised_pct": data.get("basis"),
|
||||
"source": "data_provider",
|
||||
}
|
||||
return [record]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
def store(self, db_path: Optional[str] = None, records: Optional[list[dict]] = None) -> int:
|
||||
"""Store derivatives records into SQLite."""
|
||||
import sqlite3
|
||||
db_path = db_path or config.db_path
|
||||
records = records or []
|
||||
conn = sqlite3.connect(db_path)
|
||||
count = 0
|
||||
for r in records:
|
||||
try:
|
||||
conn.execute("""
|
||||
INSERT OR REPLACE INTO derivatives
|
||||
(date, symbol, funding_rate, open_interest, oi_24h_change_pct,
|
||||
long_liquidations, short_liquidations, basis_annualised_pct)
|
||||
VALUES (?, ?, ?, ?, ?, NULL, NULL, ?)
|
||||
""", (
|
||||
r["date"], r.get("symbol", config.btc_symbol),
|
||||
r.get("funding_rate"), r.get("open_interest"),
|
||||
r.get("oi_24h_change_pct"), r.get("basis_annualised_pct"),
|
||||
))
|
||||
count += 1
|
||||
except Exception:
|
||||
continue
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return count
|
||||
Reference in New Issue
Block a user