Files
mmd/__pycache__/dashboard.cpython-314.pyc

247 lines
16 KiB
Plaintext
Raw Normal View History

2026-05-26 22:21:27 +02:00
+
<EFBFBD>j4<00><00>~<00>Rt^RIt^RIt^RIt^RIHtHt^RIHt^RIt ^RI
H t H t ^RI HtHt^RIHtHtHtHtHtRt]!]4P0R, t^<t] !]4tR tRR
R lltR R ltRRRllt ]PCR4R4t"]PCR4R4t#Rt$]R8Xd
]$!4R#R#)up
dashboard.py — MoneyMaker live monitoring dashboard.
Usage:
python dashboard.py # starts on http://localhost:5001
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
N)<02>datetime<6D>timezone)<01>Path)<02>Flask<73>render_template_string)<02>get_conn<6E>DB_TYPE)<05>_c25_day_return<72>_unrealised_pnl<6E> _realised_pnl<6E> _total_fees<65>_signal_accuracyi'<00>logsu<73>
<!DOCTYPE html>
<html lang="da">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="refresh" content="{{ refresh }}">
<title>MoneyMaker Dashboard</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Segoe UI', system-ui, sans-serif; background: #0f1117; color: #e2e8f0; padding: 24px; }
h1 { font-size: 1.4rem; font-weight: 700; color: #7dd3fc; margin-bottom: 4px; }
.subtitle { font-size: 0.8rem; color: #64748b; margin-bottom: 24px; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 24px; }
.card { background: #1e2535; border-radius: 10px; padding: 18px 20px; border: 1px solid #2d3748; }
.card .label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: .06em; color: #64748b; margin-bottom: 6px; }
.card .value { font-size: 1.6rem; font-weight: 700; }
.card .sub { font-size: 0.78rem; color: #94a3b8; margin-top: 4px; }
.pos { color: #4ade80; }
.neg { color: #f87171; }
.neu { color: #94a3b8; }
.warn { color: #fbbf24; }
table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
th { text-align: left; padding: 8px 12px; background: #1e2535; color: #64748b; font-size: 0.72rem; text-transform: uppercase; letter-spacing: .06em; border-bottom: 1px solid #2d3748; }
td { padding: 9px 12px; border-bottom: 1px solid #1a2030; }
tr:hover td { background: #1e2535; }
.section { background: #161c2d; border-radius: 10px; border: 1px solid #2d3748; margin-bottom: 20px; overflow: hidden; }
.section-title { padding: 14px 18px; font-size: 0.8rem; font-weight: 600; color: #7dd3fc; text-transform: uppercase; letter-spacing: .08em; border-bottom: 1px solid #2d3748; background: #1a2236; }
.badge { display: inline-block; padding: 2px 8px; border-radius: 99px; font-size: 0.72rem; font-weight: 600; }
.badge-green { background: #14532d; color: #4ade80; }
.badge-red { background: #450a0a; color: #f87171; }
.badge-gray { background: #1e2535; color: #94a3b8; }
pre { font-family: 'Cascadia Code', 'Fira Mono', monospace; font-size: 0.75rem; color: #94a3b8; padding: 16px 18px; overflow-x: auto; white-space: pre-wrap; line-height: 1.6; }
.footer { font-size: 0.72rem; color: #334155; text-align: center; margin-top: 32px; }
</style>
</head>
<body>
<h1>📈 MoneyMaker</h1>
<div class="subtitle">DB: {{ db_type }} &nbsp;·&nbsp; Opdateret: {{ now }} &nbsp;·&nbsp; Refresh om {{ refresh }}s</div>
<!-- KPI cards -->
<div class="grid">
<div class="card">
<div class="label">Net P&amp;L</div>
<div class="value {{ 'pos' if net_pnl >= 0 else 'neg' }}">{{ "{:+,.0f}".format(net_pnl) }} kr</div>
<div class="sub">{{ "{:+.2f}%".format(net_pct) }}</div>
</div>
<div class="card">
<div class="label">Urealiseret</div>
<div class="value {{ 'pos' if unreal >= 0 else 'neg' }}">{{ "{:+,.0f}".format(unreal) }} kr</div>
<div class="sub">{{ open_count }} åben{{ 'e' if open_count != 1 else '' }} position{{ 'er' if open_count != 1 else '' }}</div>
</div>
<div class="card">
<div class="label">Realiseret</div>
<div class="value {{ 'pos' if realised >= 0 else 'neg' }}">{{ "{:+,.0f}".format(realised) }} kr</div>
<div class="sub">Gebyrer: {{ "{:,.0f}".format(fees) }} kr</div>
</div>
<div class="card">
<div class="label">C25 i dag</div>
{% if c25_ret is not none %}
<div class="value {{ 'pos' if c25_ret >= 0 else 'neg' }}">{{ "{:+.2f}%".format(c25_ret) }}</div>
<div class="sub {{ 'pos' if vs_bench and vs_bench >= 0 else 'neg' }}">
vs benchmark: {{ "{:+.2f}%".format(vs_bench) if vs_bench is not none else "—" }}
</div>
{% else %}
<div class="value neu">—</div>
<div class="sub">Marked lukket?</div>
{% endif %}
</div>
<div class="card">
<div class="label">Signal accuracy</div>
{% if sig.total_trades > 0 %}
<div class="value {{ 'pos' if sig.accuracy_pct >= 50 else 'neg' }}">{{ "{:.0f}%".format(sig.accuracy_pct) }}</div>
<div class="sub">{{ sig.correct }} / {{ sig.total_trades }} handler korrekte</div>
{% else %}
<div class="value neu">—</div>
<div class="sub">Ingen lukkede handler</div>
{% endif %}
</div>
<div class="card">
<div class="label">Kapital</div>
<div class="value neu">{{ "{:,.0f}".format(capital) }} kr</div>
<div class="sub">Kontant: {{ "{:,.0f}".format(cash) }} kr</div>
</div>
</div>
<!-- Open positions -->
<div class="section">
<div class="section-title">📊 Åbne positioner</div>
{% if positions %}
<table>
<tr><th>Ticker</th><th>Antal</th><th>Købt</th><th>Nu</th><th>P&amp;L</th><th>Ændring</th><th>Stop</th><th>Take</th><th>Status</th></tr>
{% for p in positions %}
<tr>
<td><strong>{{ p.ticker }}</strong></td>
<td>{{ "{:.0f}".format(p.shares) }}</td>
<td>{{ "{:,.0f}".format(p.entry) }}</td>
<td>{{ "{:,.0f}".format(p.last) }}</td>
<td class="{{ 'pos' if p.unreal >= 0 else 'neg' }}">{{ "{:+,.0f}".format(p.unreal) }}</td>
<td class="{{ 'pos' if p.pct >= 0 else 'neg' }}">{{ "{:+.1f}%".format(p.pct) }}</td>
<td class="neg">{{ "{:,.0f}".format(p.stop) }}</td>
<td class="pos">{{ "{:,.0f}".format(p.take) }}</td>
<td>
{% if p.stop_hit %}<span class="badge badge-red">🔴 STOP</span>
{% elif p.take_hit %}<span class="badge badge-green">🟡 TAKE</span>
{% else %}<span class="badge badge-gray">⏳ HOLD</span>{% endif %}
</td>
</tr>
{% endfor %}
</table>
{% else %}
<p style="padding:16px 18px; color:#64748b; font-size:0.85rem;">Ingen åbne positioner.</p>
{% endif %}
</div>
<!-- Closed trades -->
<div class="section">
<div class="section-title">🏁 Lukkede handler</div>
{% if trades %}
<table>
<tr><th>Ticker</th><th>Handling</th><th>Antal</th><th>Kurs</th><th>Total</th><th>P&amp;L</th><th>Signal</th><th>Dato</th></tr>
{% for t in trades %}
<tr>
<td><strong>{{ t.ticker }}</strong></td>
<td>{{ t.action.upper() }}</td>
<td>{{ "{:.0f}".format(t.shares) }}</td>
<td>{{ "{:,.0f}".format(t.price) }}</td>
<td>{{ "{:,.0f}".format(t.total_dkk) }}</td>
<td class="{{ 'pos' if t.pnl_dkk and t.pnl_dkk >= 0 else ('neg' if t.pnl_dkk else 'neu') }}">
{{ "{:+,.0f}".format(t.pnl_dkk) if t.pnl_dkk is not none else "—" }}
</td>
<td>
{% if t.signal_correct == 1 %}<span class="badge badge-green">✅ korrekt</span>
{% elif t.signal_correct == 0 %}<span class="badge badge-red">❌ forkert</span>
{% else %}<span class="badge badge-gray">—</span>{% endif %}
</td>
<td>{{ t.event_date }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<p style="padding:16px 18px; color:#64748b; font-size:0.85rem;">Ingen handler endnu.</p>
{% endif %}
</div>
<!-- Signal pipeline stats -->
<div class="section">
<div class="section-title">🔬 NLP signal pipeline</div>
<table>
<tr><th>Analyserede signaler</th><th>Alert-triggers (≥threshold)</th><th>Gns. score</th></tr>
<tr>
<td>{{ sig.total }}</td>
<td>{{ sig.alerts }}</td>
<td>{{ "{:.3f}".format(sig.avg_score) }}</td>
</tr>
</table>
</div>
<!-- Log tail -->
{% if log_tail %}
<div class="section">
<div class="section-title">📋 Seneste log ({{ log_file }})</div>
<pre>{{ log_tail }}</pre>
</div>
{% endif %}
<div class="footer">MoneyMaker · {{ db_type }} · hjess-desktop · auto-refresh {{ refresh }}s</div>
</body>
</html>
c<00>R<00>V^8<>dQhR\R\\\3,/#)<03><00>lines<65>return)<03>int<6E>tuple<6C>str)<01>formats"<22>,/home/hjess/Projects/MoneyMaker/dashboard.py<70> __annotate__r<00>s"<00><00> <1D> <1D>C<EFBFBD> <1D><15>s<EFBFBD>C<EFBFBD>x<EFBFBD><1F> <1D>c<04>f<00>\P4'gR #\\PR4RR7pV'gR #V^,pVP RR7pRP VP 4V)R4pVPV3# \dTPR3u#i;i)
z<Return (filename, last N lines) from most recent runner log.<2E>z runner_*.logT)<01>reverse<73>replace)<01>errors<72>
N)rr) <09>LOG_DIR<49>exists<74>sorted<65>glob<6F> read_text<78>join<69>
splitlines<EFBFBD>name<6D> Exception)rr<00>path<74>content<6E>tails& r<00>_latest_log_tailr,<00>s<><00><00> <12>><3E>><3E> <1B> <1B><15> <0A> <11>'<27>,<2C>,<2C>~<7E>.<2E><04> =<3D>D<EFBFBD> <0F><15> <0A> <0F><01>7<EFBFBD>D<EFBFBD><1D><16>.<2E>.<2E> <09>.<2E>2<><07><13>y<EFBFBD>y<EFBFBD><17>+<2B>+<2B>-<2D>u<EFBFBD>f<EFBFBD>g<EFBFBD>6<>7<><04><13>y<EFBFBD>y<EFBFBD>$<24><EFBFBD><1E><> <14><1D><13>y<EFBFBD>y<EFBFBD>"<22>}<7D><1C><1D>s<00>AB<00>B0<03>/B0c<00>:<00>V^8<>dQhR\\,/#)rr)<02>list<73>dict)rs"rrr<00>s<00><00><12><12><04>T<EFBFBD>
<EFBFBD>rc<04><><00>VPR4P4p.pVEFpVR,p^RIHpVP V/4P RVR,4p\
P !V4PP R4;'g
VR,p\VR,4p\VR,4p Wx,
V, ^d,p
VPRVRV R VR
VR W<>V,
,R V
R \VR,4R\VR,4RWsR,8*RWsR,8<>/
4EK V# \d TR,pL<>i;i)z/Fetch open positions with live yfinance prices.zSELECT * FROM positions<6E>ticker)<01>C25<32> ticker_yahooz.CO<43> lastPrice<63> entry_price<63>shares<65>entry<72>last<73>unreal<61>pct<63>stop<6F> stop_loss<73>take<6B> take_profit<69>stop_hit<69>take_hit) <0B>execute<74>fetchall<6C>reportr2<00>get<65>yf<79>Ticker<65> fast_infor(<00>float<61>append) <0B>db<64>rows<77>result<6C>pr1r2<00> yf_tickerr8r7r6r:s & r<00>_open_positions_liverO<00>sE<00><00> <0A>:<3A>:<3A>/<2F> 0<> 9<> 9<> ;<3B>D<EFBFBD> <0F>F<EFBFBD> <11><01><12>8<EFBFBD><1B><06><1E><17>G<EFBFBD>G<EFBFBD>F<EFBFBD>B<EFBFBD>'<27>+<2B>+<2B>N<EFBFBD>F<EFBFBD>U<EFBFBD>N<EFBFBD>K<> <09> $<24><15>9<EFBFBD>9<EFBFBD>Y<EFBFBD>'<27>1<>1<>5<>5<>k<EFBFBD>B<>V<>V<>a<EFBFBD> <0A>FV<46>D<EFBFBD><16>a<EFBFBD> <0A>&<26>'<27><05><16>q<EFBFBD><18>{<7B>#<23><06><13>|<7C>u<EFBFBD>$<24>s<EFBFBD>*<2A><03><0E> <0A> <0A> <14><06> <14><06> <13><05> <12><04> <14><06><15>,<2C>/<2F> <11><03> <12><05>a<EFBFBD> <0B>n<EFBFBD>-<2D> <12><05>a<EFBFBD> <0A>.<2E>/<2F> <16><04>+<2B><0E>.<2E> <16><04>-<2D> 0<>0<> 
<EFBFBD> <0B><12>. <12>M<EFBFBD><4D>#<19> $<24><14>]<5D>#<23>D<EFBFBD> $<24>s<00>!5E<02>
E<02>E<05>Ec<00>F<00>V^8<>dQhR\R\\,/#)r<00>limitr)rr.r/)rs"rrr<00>s<00><00>#<23>#<23>c<EFBFBD>#<23>4<EFBFBD><04>:<3A>#rc<00><><00>VPRV34P4pVUu.uFp\V4NK up#uupi)z<>
SELECT ticker, action, shares, price, total_dkk, pnl_dkk, signal_correct, event_date
FROM position_events
ORDER BY id DESC LIMIT ?
)rArBr/)rJrQrK<00>rs&& r<00>_closed_tradesrT<00>sF<00><00> <0A>:<3A>:<3A><08> <10><18>  <13><1C>8<EFBFBD>:<3A>  <09>
"<22> "<22>T<EFBFBD><01>D<EFBFBD><11>G<EFBFBD>T<EFBFBD> "<22>"<22><> "s<00>=<04>/c
<00><><00>\4p\V4p\V4p\RV44p\RV44p\ V4p\ V4p\ V4p\4pWS,V,
p V \, ^d,p
Ve W<>,
MRp \V,
p \4wr<>\P!\P4PR4p\\ 3/RVbR\"bR\$bR\bRV bR VbR
VbR VbR V bR V
bRVbRV bRVbR\'V4bRVbR\)RRV4!4bRV bRVbVP+4# TP+4i;i)c3<00>2"<00>TF qR,x<00>K R#5i)r9N<><00><02>.0rMs& r<00> <genexpr><3E>index.<locals>.<genexpr>s<00><00><00>8<>i<EFBFBD><11>8<EFBFBD><1B><1B>i<EFBFBD>s<00>c3<00>N"<00>TFqR,VR,,x<00>K R#5i)r6r7NrXrYs& rr[r\s<00><00><00>E<>9<EFBFBD>a<EFBFBD>8<EFBFBD><1B>q<EFBFBD><17>z<EFBFBD>1<>1<>9<EFBFBD>s<00>#%Nz%d %b %Y %H:%M UTC<54>now<6F>db_type<70>refresh<73>capital<61>cashr9<00>realised<65>fees<65>net_pnl<6E>net_pct<63>c25_ret<65>vs_bench<63> positions<6E>
open_count<EFBFBD>trades<65>sig<69>Sig<69>log_file<6C>log_tailrX)rrOrT<00>sumr r r r <00>CAPITALr,rr^r<00>utc<74>strftimer<00>TEMPLATEr<00>REFRESH<53>len<65>type<70>close)rJrirkr9<00>investedrcrdrlrgrerfrhrbrnror^s r<00>indexrz
s<><00><00> <11><1A>B<EFBFBD>&<13>)<29>"<22>-<2D> <09>#<23>B<EFBFBD>'<27><06><18>8<>i<EFBFBD>8<>8<><06><18>E<>9<EFBFBD>E<>E<><08>"<22>2<EFBFBD>&<26><08> <20><12>_<EFBFBD><04>%<25>b<EFBFBD>)<29><03>$<24>&<26><07><1D>&<26><14>-<2D><07><1C>w<EFBFBD>&<26><13>,<2C><07>,3<>,?<3F>g<EFBFBD>'<27>T<EFBFBD><08><1C>x<EFBFBD>'<27><04>-<2D>/<2F><1A><08><16>l<EFBFBD>l<EFBFBD>8<EFBFBD><<3C><<3C>(<28>1<>1<>2F<32>G<><03>%<25> <14>
<EFBFBD><13>
<EFBFBD><1C>
<EFBFBD><1C> 
<EFBFBD>
<1C> 
<EFBFBD> <16> 
<EFBFBD><1A>
<EFBFBD><1E>
<EFBFBD><16>
<EFBFBD><1C>
<EFBFBD><1C>
<EFBFBD><1C>
<EFBFBD><1E>
<EFBFBD> <20>
<EFBFBD><1B>9<EFBFBD>~<7E>
<EFBFBD> <1A>!
<EFBFBD>"<15>U<EFBFBD>B<EFBFBD><03>$<24>&<26>#
<EFBFBD>$<1E>%
<EFBFBD>&<1E>'
<EFBFBD>, <0B><08><08>
<EFBFBD><EFBFBD><02><08><08>
<EFBFBD>s <00>E
E'<00>'E9z/healthc<00>@<00>RRR\R\P!4/#)<04>status<75>okrJ<00>ts)r<00>timerXrr<00>healthr<68>6s<00><00> <14>d<EFBFBD>D<EFBFBD>'<27>4<EFBFBD><14><19><19><1B> =<3D>=rc<00>4<00>\P!RR7pVPR\RR7VPRRR7VP 4p\ RVP R 24\PVPVP R
R 7R #) zMoneyMaker Dashboard)<01> descriptionz--porti<74>)rw<00>defaultz--hostz0.0.0.0)r<>u-
MoneyMaker Dashboard → http://localhost:rF)<03>host<73>port<72>debugN)
<EFBFBD>argparse<73>ArgumentParser<65> add_argumentr<00>
parse_args<EFBFBD>printr<74><00>app<70>runr<6E>)<02>parser<65>argss r<00>mainr<6E>=sy<00><00> <15> $<24> $<24>1G<31> H<>F<EFBFBD>
<EFBFBD><17><17><08>s<EFBFBD>D<EFBFBD><17>9<>
<EFBFBD><17><17><08>)<29><17>4<> <11> <1C> <1C> <1E>D<EFBFBD> <09> :<3A>4<EFBFBD>9<EFBFBD>9<EFBFBD>+<2B>R<EFBFBD>
H<EFBFBD>I<><07>G<EFBFBD>G<EFBFBD><14><19><19><14><19><19>%<25>G<EFBFBD>8r<00>__main__)<01>()<01>)%<25>__doc__r<5F><00>jsonrrr<00>pathlibr<00>yfinancerE<00>flaskrrrJrrrCr r
r r r rq<00>__file__<5F>parentr ru<00>__name__r<5F>rtr,rOrT<00>routerzr<>r<>rXrr<00><module>r<>s<><00><01> <04><10> <0B> <0B>'<27><18><15>/<2F> <20>a<>a<> <11><07> <0F><08>><3E> <20> <20>6<EFBFBD> )<29><07> <0A><07> <0B>H<EFBFBD>o<EFBFBD><03>i <04><08>\ <1D> <12><#<23><05><19><19>3<EFBFBD><1E>(<13><10>(<13>V<05><19><19>9<EFBFBD><15>><3E><16>><3E> 9<> <0C>z<EFBFBD><19><08>F<EFBFBD>r