From 09bf46c28d35a98ba6b0c1f8c613922e775d7882 Mon Sep 17 00:00:00 2001 From: Henrik Jess Nielsen Date: Mon, 29 Jun 2026 22:33:21 +0200 Subject: [PATCH] Trying out new layout --- __pycache__/saxo_auth.cpython-314.pyc | Bin 12036 -> 12138 bytes logs/dashboard.log | 24 ++++++++++++------------ mmd.nomad | 2 ++ scheduler.py | 24 ++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/__pycache__/saxo_auth.cpython-314.pyc b/__pycache__/saxo_auth.cpython-314.pyc index 132e84dfebfced01fc8b4046287a774a2acdce6d..987b606017ee12384ae2b5a91048db682b496544 100644 GIT binary patch delta 2052 zcmZ`)OKcle6rJb9juU?p$EjUAiGQixOw*c#CZ(UWO+RUxwzOeLOBYQhjy-8i{Nv6f zZE2;3kHn(TDt!VXL|}nbDT_)}-cXh-x+B+#&1tPne6WIdYUABzP1Hkt#nG#&$OsLVh0z$_u3wG?_=D@wr=rE1mes<$V$VP47-u%}|nRd;K;`S6dq)rP&B8EkQ}~1*JlZ(XO=$x}l2F=kwS`s% zW^^h6Tm{8mMA?dRNXStojB>k>V@gL*GU7_-Aifex1UI5$qf3dQX4^zkiSNBANjbbs zhcvM*F7>aoj7kdY565M!xmBI7) z+MN9jAGTX{#dpDusr)0b!glhz!2)aLPlIciiM6fUq5+EAL0)Wd314PCc8jjl*rs+s zD-VRuIYjSw@VU^SV+!?<>LPo z)UD5M-H1L^qdcdTV3~iDT*8QxsaHF@JB7k$WBrh5U57k^$@x>O9jmYZ#R@uJD@*SIK69;2@vc6}|ZZi5nxm zNXx?nLj*LB#A7C3&{WlQpjZ67i_bdTpabH1qR=A8qOnBrMW1aXMC4+XEM^F3LV+$B zw))FCi2nrh1?>=2QHg&ZlKzzY)3=jHKODU~`Z)631OJ2ZFS@IfKMkDve&E#O7u6pJ V)V02;wYJP!^ECfFJyuWN{sH3wx_STr delta 1961 zcmZ`)T}&KR6rQ{6E(^=DEXXdqEV~PAmoC3negupBl$MgFretC%Nekn!JHX1YOU?{G zN+PXMA8LywSJS7)=z}S#FO|fk>8qx%8e>c*`b3Ptn=vsy_~1ErplA$}?03&SckVgg z`I|kMesB78OS!L9!slSC>dlLFlC(>kqsMtD{P8Dye7cW$gI&zljJNa8f>j&7R`1cP zl4RD%9M={Ny0vCenW_1P6kswn|qaXKIS6t(j0m3RiY{amlb;nfb*+7LCPFDU79# zNEqsh21&l_=|OXmk{cI@fq2ga?TVA`pNX3mb9b2 zgOUf$)<|LJB#stUyL>L>pr=k(gCdmEeIG&2QC%^J0*fu;*eE2Qp(J$ z`8iDy49@d?-z}S1k09m)Z5A|RUNH*`T3#@O7s=|0)%v}hsD=HP*+wUE7$xW-c$uJ= z;1zkwl!1-$Un|xz8Z~e(N%OPK z;0!J&bAJ}VPTScFOt>(ugP{kkh5r)LS%6=v`2p?3!pV3wd95WY4!Dks<;2c03Qy8uvt zv)~YDUN-qe;to35O*k1F z<$LuL7w@2fv>T#FcHje&#t5D}z&ePzyNg;;h9k4ZM}2UQzu$1pZi5DD1Gt26auWx$VBrxgEJwkx+;f_#R*_^H^iseRwTHXZuuERQkh)h&iq*rv5sJdvd<6MX|Jfs^M0gWYy;iIstEl)rG r6)fx80DOgehCgGUXWBRYV;{crlRx>rKlxo#-;e%29_$!*(!GBGz}2YN diff --git a/logs/dashboard.log b/logs/dashboard.log index 6de8f93..363227f 100644 --- a/logs/dashboard.log +++ b/logs/dashboard.log @@ -1,13 +1,13 @@ -nohup: ignorerer inddata +Traceback (most recent call last): + File "/home/hjess/Projects/MoneyMaker/dashboard.py", line 779, in + main() + ~~~~^^ + File "/home/hjess/Projects/MoneyMaker/dashboard.py", line 773, in main + init_schema() + ~~~~~~~~~~~^^ + File "/home/hjess/Projects/MoneyMaker/db.py", line 375, in init_schema + conn = psycopg2.connect(DATABASE_URL) + File "/home/hjess/Projects/MoneyMaker/.venv/lib64/python3.14/site-packages/psycopg2/__init__.py", line 122, in connect + conn = _connect(dsn, connection_factory=connection_factory, **kwasync) +psycopg2.OperationalError: could not translate host name "int.i80.dk" to address: Temporary failure in name resolution - MoneyMaker Dashboard -> http://localhost:5001 - - * Serving Flask app 'dashboard' - * Debug mode: off -WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. - * Running on all addresses (0.0.0.0) - * Running on http://127.0.0.1:5001 - * Running on http://192.168.15.40:5001 -Press CTRL+C to quit -127.0.0.1 - - [26/May/2026 22:52:33] "GET /health HTTP/1.1" 200 - -127.0.0.1 - - [26/May/2026 22:52:39] "GET / HTTP/1.1" 200 - diff --git a/mmd.nomad b/mmd.nomad index a879a44..af34a01 100644 --- a/mmd.nomad +++ b/mmd.nomad @@ -85,6 +85,7 @@ ANTHROPIC_API_KEY="{{ key "mmd/anthropic_api_key" }}" SAXO_APP_KEY="{{ key "mmd/SAXO_APP_KEY" }}" SAXO_APP_SECRET_1="{{ key "mmd/SAXO_APP_SECRET_1" }}" SAXO_BASE="{{ key "mmd/SAXO_BASE" }}" +SAXO_TOKEN="{{ key "mmd/SAXO_APP_24_HOUR" }}" HARBOR_ROBOT_TOKEN="{{ key "harbor/robot/token" }}" EOH destination = "secrets/app.env" @@ -132,6 +133,7 @@ ANTHROPIC_API_KEY="{{ key "mmd/anthropic_api_key" }}" SAXO_APP_KEY="{{ key "mmd/SAXO_APP_KEY" }}" SAXO_APP_SECRET_1="{{ key "mmd/SAXO_APP_SECRET_1" }}" SAXO_BASE="{{ key "mmd/SAXO_BASE" }}" +SAXO_TOKEN="{{ key "mmd/SAXO_APP_24_HOUR" }}" EOH destination = "secrets/worker.env" env = true diff --git a/scheduler.py b/scheduler.py index 6e5744f..699c69b 100644 --- a/scheduler.py +++ b/scheduler.py @@ -32,6 +32,23 @@ SCHEDULE = [ (17, 0, frozenset(range(5)), False, True), ] +# Saxo's access token lasts ~20 min and its refresh token rolls on each refresh +# but lapses if unused for ~1h. Trade windows are 2-3h apart, so we roll the +# token forward here independently to keep the refresh chain alive between runs. +SAXO_KEEPALIVE_SEC = 300 + + +def _saxo_keepalive() -> None: + """Roll the Saxo OAuth token forward so its refresh token never lapses in the + gaps between scheduled runs. No-op when there's no stored OAuth token yet + (e.g. only the 24h env token is present).""" + try: + from saxo_auth import _load, get_token + if _load().get("refresh_token"): + get_token() # auto-refreshes when near expiry; rolls the refresh token + except Exception as exc: + print(f"[scheduler] saxo keep-alive: {exc}", flush=True) + def _log_path() -> Path: today = datetime.now(timezone.utc).strftime("%Y-%m-%d") @@ -60,10 +77,17 @@ def _run_job(analyze_only: bool, is_report: bool) -> None: def main() -> None: last_run: dict = {} + last_keepalive = 0.0 print(f"[scheduler] started — LOG_DIR={LOG_DIR}", flush=True) while True: now = datetime.now(timezone.utc) + + # Keep the Saxo refresh-token chain alive between trade windows. + if time.time() - last_keepalive >= SAXO_KEEPALIVE_SEC: + last_keepalive = time.time() + _saxo_keepalive() + dow = now.weekday() for hour, minute, days, analyze_only, is_report in SCHEDULE: