diff --git a/Makefile b/Makefile index 73e9ac0..eb5d347 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ PY := .venv/bin/python -.PHONY: help run signals buy fetch rss analyze force dry company saxo saxo-buy saxo-sell saxo-login saxo-status +.PHONY: help run signals buy fetch rss analyze force dry company saxo saxo-buy saxo-sell saxo-login saxo-status dashboard help: @echo "" @@ -12,7 +12,7 @@ help: @echo " make buy → vis kun køb-kandidater" @echo " make fetch → hent nye artikler fra Ground News" @echo " make rss → hent danske RSS feeds (Børsen, Finans, Politiken)" - @echo " make orders → vis dagens køb/sælg/hold forslag" + @echo " make dashboard → start dashboard på http://localhost:5001" @echo " make portfolio → vis åbne positioner + P&L" @echo " make company → TICKER=NOVO-B make company" @echo " make saxo → vis Saxo SIM konto status + positioner" @@ -43,6 +43,9 @@ fetch: rss: $(PY) rss_feeds.py +dashboard: + $(PY) dashboard.py + orders: $(PY) portfolio.py orders diff --git a/dashboard.py b/dashboard.py index 1a7771d..160fe14 100644 --- a/dashboard.py +++ b/dashboard.py @@ -6,11 +6,12 @@ Usage: python dashboard.py --port 5002 Auto-refreshes every 60 seconds. Shows: - • Portfolio P&L + C25 benchmark - • Open positions with live prices - • Closed trades (win/loss) - • Signal accuracy - • Recent runner log tail + - Portfolio P&L + C25 benchmark + - Equity curve, win/loss, position charts (Apache ECharts) + - Open positions with live prices + - Closed trades (win/loss) + - Signal accuracy + - Recent runner log tail """ import argparse import json @@ -20,7 +21,7 @@ from datetime import datetime, timezone from pathlib import Path import yfinance as yf -from flask import Flask, render_template_string +from flask import Flask, jsonify, render_template_string from db import get_conn, DB_TYPE from report import _c25_day_return, _unrealised_pnl, _realised_pnl, _total_fees, _signal_accuracy @@ -31,175 +32,496 @@ REFRESH = 60 # seconds app = Flask(__name__) -# ── HTML template ──────────────────────────────────────────────────────────── +# ── HTML template ───────────────────────────────────────────────────────────── -TEMPLATE = """ - +TEMPLATE = """
- -| Ticker | Antal | Købt | Nu | P&L | Ændring | Stop | Take | Status | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Ticker | Antal | +Kob | Nu | +P&L | Aendring | +Stop | Take | Status | +|||||||
| {{ p.ticker }} | -{{ "{:.0f}".format(p.shares) }} | -{{ "{:,.0f}".format(p.entry) }} | -{{ "{:,.0f}".format(p.last) }} | -{{ "{:+,.0f}".format(p.unreal) }} | -{{ "{:+.1f}%".format(p.pct) }} | -{{ "{:,.0f}".format(p.stop) }} | -{{ "{:,.0f}".format(p.take) }} | +{{ "{:.0f}".format(p.shares) }} | +{{ "{:,.0f}".format(p.entry) }} | +{{ "{:,.0f}".format(p.last) }} | +{{ "{:+,.0f}".format(p.unreal) }} | +{{ "{:+.1f}%".format(p.pct) }} | +{{ "{:,.0f}".format(p.stop) }} | +{{ "{:,.0f}".format(p.take) }} | - {% if p.stop_hit %}🔴 STOP - {% elif p.take_hit %}🟡 TAKE - {% else %}⏳ HOLD{% endif %} + {% if p.stop_hit %}STOP + {% elif p.take_hit %}TAKE + {% else %}HOLD{% endif %} |
Ingen åbne positioner.
+| Ticker | Handling | Antal | Kurs | Total | P&L | Signal | Dato | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Ticker | Type | Antal | Kurs | Total | P&L | Signal | Dato | ||||||
| {{ t.ticker }} | -{{ t.action.upper() }} | -{{ "{:.0f}".format(t.shares) }} | -{{ "{:,.0f}".format(t.price) }} | -{{ "{:,.0f}".format(t.total_dkk) }} | -+ | {{ t.action.upper() }} | +{{ "{:.0f}".format(t.shares) }} | +{{ "{:,.0f}".format(t.price) }} | +{{ "{:,.0f}".format(t.total_dkk) }} | +{{ "{:+,.0f}".format(t.pnl_dkk) if t.pnl_dkk is not none else "—" }} | - {% if t.signal_correct == 1 %}✅ korrekt - {% elif t.signal_correct == 0 %}❌ forkert + {% if t.signal_correct == 1 %}korrekt + {% elif t.signal_correct == 0 %}forkert {% else %}—{% endif %} | -{{ t.event_date }} | +{{ t.event_date }} |
Ingen handler endnu.
+| Analyserede signaler | Alert-triggers (≥threshold) | Gns. score | ||||
|---|---|---|---|---|---|---|
| {{ sig.total }} | -{{ sig.alerts }} | -{{ "{:.3f}".format(sig.avg_score) }} | +Analyserede signaler | +Alert-triggers | +Gns. score | +Korrekte handler | +
| {{ sig.total }} | +{{ sig.alerts }} | +{{ "{:.3f}".format(sig.avg_score) }} | +{{ sig.correct }} / {{ sig.total_trades }} |
| Selskab | +Pris | +Analytiker | +Nyheder | +Dækning | +Signalstyrke | +Vurdering | +
|---|---|---|---|---|---|---|
| + {{ s.ticker }} + {{ s.name }} + {% if s.alerts %}{{ s.alerts }} alert{% endif %} + | +{{ s.price }} kr | ++ {{ s.rec_clean }} + {{ s.rec_count }} + | +{{ s.sent_text }} | +{{ s.n_articles }} art · {{ s.n_sources }} src + {% if s.n_sources < 3 %} lav{% endif %} + | +
+
+
+
+
+
+ {{ s.signal_str }}
+ |
+ {{ s.agree_text }} | +
{{ log_tail }}
+ {{ log_tail }}
+