""" web/app.py — ChanMacro dashboard (Flask, port 8124). """ import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from datetime import date as Date, timedelta from flask import Flask, render_template, jsonify, request from database import get_connection from config import config from scoring.price_structure import PriceStructureScorer from scoring.breadth_scorer import BreadthScorer from scoring.oi_matrix import OIMatrixScorer from scoring.volatility_regime import VolatilityRegimeScorer from regime_detector import RegimeDetector from models import MarketStateVector from expectancy.engine import BayesianExpectancyEngine app = Flask(__name__) def _build_state(target: Date): """Shared: build MarketStateVector for a date.""" ps = PriceStructureScorer().compute(target) br = BreadthScorer().compute(target) oi = OIMatrixScorer().compute(target) vol = VolatilityRegimeScorer().compute(target) detector = RegimeDetector() detector.load_state(config.db_path) r = detector.detect(ps.score, br.breadth_top50, vol.vol_regime.value, target) state = MarketStateVector( date=target, regime=r.regime, regime_confidence=r.confidence, regime_version=r.regime_version, regime_maturity_score=r.maturity_score, breadth_top20=br.breadth_top20, breadth_top30=br.breadth_top30, breadth_top50=br.breadth_top50, breadth_bucket=br.breadth_bucket, breadth_divergence=br.breadth_divergence, oi_state=oi.oi_state, volatility_regime=vol.vol_regime, price_structure_score=ps, breadth_score=br, oi_matrix_score=oi, volatility_regime_score=vol, ) state.market_state_hash = state.compute_hash() return state @app.route("/") def dashboard(): return render_template("index.html") @app.route("/api/state") def api_state(): """Current market state with all factor scores.""" try: target = Date.today() state = _build_state(target) return jsonify({ "date": str(state.date), "regime": state.regime.value, "regime_confidence": state.regime_confidence, "regime_maturity": state.regime_maturity_score, "breadth": { "score": state.breadth_score.score, "bucket": state.breadth_bucket.value, "top20": state.breadth_top20, "top30": state.breadth_top30, "top50": state.breadth_top50, "divergence": state.breadth_divergence, "narrative": state.breadth_score.narrative, }, "oi_state": state.oi_state.value, "oi_score": state.oi_matrix_score.score, "oi_narrative": state.oi_matrix_score.narrative, "volatility": state.volatility_regime.value, "price_structure": { "score": state.price_structure_score.score, "trend": state.price_structure_score.trend_strength, "vol_comp": state.price_structure_score.volatility_compression, "momentum": state.price_structure_score.momentum, "label": state.price_structure_score.label, "narrative": state.price_structure_score.narrative, }, }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route("/api/history") def api_history(): """Regime and factor score history.""" days = request.args.get("days", 60, type=int) conn = get_connection() # Regime history regimes = conn.execute( "SELECT date, regime, confidence, maturity_score FROM regime_history ORDER BY date DESC LIMIT ?", (days,) ).fetchall() # Breadth history breadth = conn.execute( "SELECT date, advance_top50, decline_top50, above_ema20_top50 FROM breadth_daily ORDER BY date DESC LIMIT ?", (days,) ).fetchall() conn.close() return jsonify({ "regimes": [{"date": r["date"], "regime": r["regime"], "confidence": r["confidence"], "maturity": r["maturity_score"]} for r in reversed(regimes)], "breadth": [{"date": b["date"], "advance": b["advance_top50"], "decline": b["decline_top50"], "above_ema20": b["above_ema20_top50"]} for b in reversed(breadth)], }) @app.route("/api/expectancy") def api_expectancy(): """Query signal expectancy.""" signal = request.args.get("signal", "B3") try: target = Date.today() state = _build_state(target) engine = BayesianExpectancyEngine(level_min_samples=5) report = engine.estimate(state, signal_type=signal, target_date=target) layers = [] for l in report.layers: layers.append({ "name": l.name, "samples": l.samples, "effective_samples": l.effective_samples, "raw_winrate": l.raw_winrate, "posterior_winrate": l.posterior_winrate, "avg_return": l.avg_return, }) return jsonify({ "signal": signal, "final_estimate": report.final_estimate, "sufficiency": report.sufficiency.value, "source": report.source, "avg_return_7d": report.avg_return_7d, "profit_factor": report.profit_factor, "max_adverse": report.max_adverse_excursion, "layers": layers, }) except Exception as e: return jsonify({"error": str(e)}), 500 if __name__ == "__main__": app.run(host="0.0.0.0", port=8124, debug=True)