From 7252577a733f8335e32f7cd3b1dd9d96e91c4eb6 Mon Sep 17 00:00:00 2001 From: joeeliang <103452146+joeeliang@users.noreply.github.com> Date: Wed, 12 Aug 2026 01:46:19 -0700 Subject: [PATCH] git ignored nothing? --- .DS_Store | Bin 0 -> 6148 bytes .gitignore | 13 - AGENTS.md | 10 +- README.md | 20 + __pycache__/analytics.cpython-312.pyc | Bin 0 -> 24479 bytes __pycache__/analytics.cpython-314.pyc | Bin 0 -> 28519 bytes __pycache__/bars.cpython-312.pyc | Bin 0 -> 7045 bytes __pycache__/bars.cpython-314.pyc | Bin 0 -> 8547 bytes .../close_legacy_positions.cpython-314.pyc | Bin 0 -> 7053 bytes __pycache__/config.cpython-312.pyc | Bin 0 -> 7823 bytes __pycache__/config.cpython-314.pyc | Bin 0 -> 10022 bytes __pycache__/connection.cpython-314.pyc | Bin 0 -> 3951 bytes __pycache__/daily_report.cpython-314.pyc | Bin 0 -> 8818 bytes __pycache__/dashboard.cpython-314.pyc | Bin 0 -> 36291 bytes __pycache__/main.cpython-314.pyc | Bin 0 -> 16044 bytes __pycache__/orders.cpython-312.pyc | Bin 0 -> 7013 bytes __pycache__/orders.cpython-314.pyc | Bin 0 -> 7959 bytes __pycache__/state.cpython-312.pyc | Bin 0 -> 20399 bytes __pycache__/state.cpython-314.pyc | Bin 0 -> 23668 bytes __pycache__/test_offline.cpython-312.pyc | Bin 0 -> 22100 bytes __pycache__/test_offline.cpython-314.pyc | Bin 0 -> 23316 bytes analytics.py | 445 ++++++++++ dashboard.py | 785 ++++++++++++++++++ dashboard.sh | 5 + orders.py | 17 + state.py | 30 +- strategies/.DS_Store | Bin 0 -> 6148 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 466 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 429 bytes strategies/__pycache__/base.cpython-312.pyc | Bin 0 -> 22762 bytes strategies/__pycache__/base.cpython-314.pyc | Bin 0 -> 27093 bytes strategies/__pycache__/forex.cpython-312.pyc | Bin 0 -> 8896 bytes strategies/__pycache__/forex.cpython-314.pyc | Bin 0 -> 10085 bytes .../__pycache__/ma_cross.cpython-312.pyc | Bin 0 -> 14569 bytes .../__pycache__/ma_cross.cpython-314.pyc | Bin 0 -> 16488 bytes .../mean_reversion.cpython-312.pyc | Bin 0 -> 15583 bytes .../mean_reversion.cpython-314.pyc | Bin 0 -> 17570 bytes .../__pycache__/short_term.cpython-312.pyc | Bin 0 -> 14675 bytes .../__pycache__/short_term.cpython-314.pyc | Bin 0 -> 16656 bytes strategies/base.py | 10 +- strategies/forex.py | 15 +- strategies/ma_cross.py | 22 +- strategies/mean_reversion.py | 24 +- strategies/short_term.py | 24 +- test_offline.py | 302 +++++++ 45 files changed, 1665 insertions(+), 57 deletions(-) create mode 100644 .DS_Store create mode 100644 __pycache__/analytics.cpython-312.pyc create mode 100644 __pycache__/analytics.cpython-314.pyc create mode 100644 __pycache__/bars.cpython-312.pyc create mode 100644 __pycache__/bars.cpython-314.pyc create mode 100644 __pycache__/close_legacy_positions.cpython-314.pyc create mode 100644 __pycache__/config.cpython-312.pyc create mode 100644 __pycache__/config.cpython-314.pyc create mode 100644 __pycache__/connection.cpython-314.pyc create mode 100644 __pycache__/daily_report.cpython-314.pyc create mode 100644 __pycache__/dashboard.cpython-314.pyc create mode 100644 __pycache__/main.cpython-314.pyc create mode 100644 __pycache__/orders.cpython-312.pyc create mode 100644 __pycache__/orders.cpython-314.pyc create mode 100644 __pycache__/state.cpython-312.pyc create mode 100644 __pycache__/state.cpython-314.pyc create mode 100644 __pycache__/test_offline.cpython-312.pyc create mode 100644 __pycache__/test_offline.cpython-314.pyc create mode 100644 analytics.py create mode 100644 dashboard.py create mode 100755 dashboard.sh create mode 100644 strategies/.DS_Store create mode 100644 strategies/__pycache__/__init__.cpython-312.pyc create mode 100644 strategies/__pycache__/__init__.cpython-314.pyc create mode 100644 strategies/__pycache__/base.cpython-312.pyc create mode 100644 strategies/__pycache__/base.cpython-314.pyc create mode 100644 strategies/__pycache__/forex.cpython-312.pyc create mode 100644 strategies/__pycache__/forex.cpython-314.pyc create mode 100644 strategies/__pycache__/ma_cross.cpython-312.pyc create mode 100644 strategies/__pycache__/ma_cross.cpython-314.pyc create mode 100644 strategies/__pycache__/mean_reversion.cpython-312.pyc create mode 100644 strategies/__pycache__/mean_reversion.cpython-314.pyc create mode 100644 strategies/__pycache__/short_term.cpython-312.pyc create mode 100644 strategies/__pycache__/short_term.cpython-314.pyc create mode 100644 test_offline.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..407e6df687bfe8826e3a226002e062d62ce9f539 GIT binary patch literal 6148 zcmeHK%}T>S5T0#wsMbSI;?2d2H=+0fu|z@WK`0(Psr{i98d7XM2p)3lW9WMbo_!C` zzJuTFEHP^vdJ~bEGW#X7Gn+48LZ(YZrt>gt6Sat_0cWgjVOV24&%Ru$b>2MCU)} zXOq&qQF}otHR&3^89mZ~Zs8LeaU@_7)tA+oWlnGVSI@`7sbbwU`)0T~JK!5#FyC#O z(}M27bpfWV*ofu<+j{y@l~?9^dG~=0TU&? zq=Q@Xy>;d0xYt_v4V;bha>1em2d)((ms{~3+!^AQ4}h`9To4hM{t<98=%5Pxssdjq CS8!4Q literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore index 01ce690..e69de29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +0,0 @@ -.venv/ -__pycache__/ -*.pyc - -.env - -bot_state.json -day_pnl.json -recent_sells.json -trades.jsonl -trading_bot.log - -*.log diff --git a/AGENTS.md b/AGENTS.md index c2fec3f..d74c4af 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,6 +21,9 @@ Interactive Brokers 股票自动交易机器人(Python + ib_insync),多策 | `bars.py` | `BarManager`:每个合约一条 `keepUpToDate=True` 实时K线订阅,多策略共享;`to_completed_df` 丢弃未收盘bar;`is_market_active` 通过最新bar时效判断开闭市 | | `state.py` | `PositionTracker`:持仓归属(哪个策略拥有哪笔仓位),持久化到 `bot_state.json`;启动/重连时 `reconcile()` 对账;每笔买卖追加写入 `trades.jsonl` 流水账本(首次运行以当前持仓为 seed) | | `daily_report.py` / `report.sh` | 当日成交明细+盈亏报告(FIFO,数据源 `trades.jsonl`);用法 `./report.sh [YYYY-MM-DD]` | +| `analytics.py` | 全历史往返交易分析引擎(纯离线,不连 IB):FIFO 配对成 `RoundTrip` → 胜率/期望值/盈亏比/profit factor/最大回撤/**保本胜率**,并按策略、标的、入场信号、出场原因、时段归因。`--json` 可供程序消费;`dashboard.py` 依赖它 | +| `dashboard.py` / `dashboard.sh` | 把 `analytics` 结果渲染成自包含 HTML 看板(无 CDN、无外部资源)。图表为手写内联 SVG + 原生 JS;数据以 JSON 内嵌,筛选在浏览器端重算(`summarize()` 是 `analytics.summarize` 的 JS 镜像,**改动统计口径时两处都要改**) | +| `test_offline.py` | 离线单元测试(不连 IB、不下单):账本字段、FIFO 配对、统计口径、指标数学。改策略后必跑 `.venv/bin/python -m unittest test_offline` | | `orders.py` | `execute_market_order`:下单+等待成交(30s超时撤单)+防重复单(`has_open_order`,按 orderRef 匹配) | | `strategies/` | `ma_cross`(SMA20/50+ADX)、`short_term`(EMA5/10+VWAP+max_hold)、`mean_reversion`(RSI/布林/急跌抄底)、`forex`(默认关闭) | | `main.py` | 主循环60s;disconnectedEvent 只注册一次且有并发/关机防护;重连后 `bar_manager.reset()` + 重新 `on_start` | @@ -38,7 +41,8 @@ Interactive Brokers 股票自动交易机器人(Python + ib_insync),多策 8. **MeanRev 趋势过滤**:仅在 `close > SMA(trend_ma_period=50)` 时允许抄底,避免下跌趋势中接飞刀。 9. **信号只用已收盘K线**:最后一根 forming bar 必须丢弃(时间戳比较法)。 10. **休市禁交易**:最新bar年龄 > `bar_seconds*5` 视为休市,跳过全部信号。收盘后约5分钟内 bar 仍"新鲜",此窗口的市价单会隔夜排队——收市停机需提前(参考 15:59 EOD 停止的做法)。 -11. **K线流量**:禁止每轮全量拉历史数据(旧版 5 小时 845MB);用 keepUpToDate 订阅(约 3.5MB/5小时)。`formatDate=2`(epoch,时区无歧义)。 +11. **账本记录归因字段(2026-08-12 起)**:`trades.jsonl` 每条记录附带 `reason`(买入=入场信号名,卖出=出场原因)与 `commission`(IB 实际佣金,`orders.trade_commission` 尽力获取,取不到则不写该字段)。出场原因取值:`SignalExit`/`SoftStop`/`HardStop`/`HardStopDuringCancel`/`HardStopDuringTrail`/`HardStopOffline`/`MaxHold`,MeanRev 另有 `RSI`/`Bollinger`/`Recovery`。**新增出场分支时必须传 reason**,否则归因图出现 `(none)` 桶。字段缺失即视为未知(不是 0),旧账本仍可解析。 +12. **K线流量**:禁止每轮全量拉历史数据(旧版 5 小时 845MB);用 keepUpToDate 订阅(约 3.5MB/5小时)。`formatDate=2`(epoch,时区无歧义)。 ## ⚠️ 已修复的 bug(勿重新引入) @@ -78,6 +82,10 @@ Interactive Brokers 股票自动交易机器人(Python + ib_insync),多策 ./restart_bot.sh # 重启 bot(杀旧进程 + nohup 启动) tail -f trading_bot.log # 运行日志 cat bot_state.json # 当前策略持仓归属与成本 +./report.sh [YYYY-MM-DD] # 单日成交明细 +.venv/bin/python analytics.py # 全历史往返统计(含保本胜率) +./dashboard.sh # 生成并打开 HTML 复盘看板 +.venv/bin/python -m unittest test_offline # 离线测试 ``` 一次性定时任务示例(cron):`close_legacy_positions.sh`(平仓+自动启动bot)、`stop_bot_eod.sh`(15:59 收市前停机)。 diff --git a/README.md b/README.md index db8bb26..2fb87ab 100644 --- a/README.md +++ b/README.md @@ -103,8 +103,28 @@ tail -f trading_bot.log # 3. 查看当天成交明细与盈亏(可指定日期) ./report.sh [YYYY-MM-DD] + +# 4. 复盘:往返交易统计(胜率/期望值/盈亏比/保本胜率,按策略/标的/信号归因) +.venv/bin/python analytics.py +.venv/bin/python analytics.py --since 2026-08-01 + +# 5. 可视化看板(单文件 HTML,含权益曲线、收益分布、完整历史表,可交互筛选) +./dashboard.sh + +# 6. 离线测试(不连 IB、不下单,改动策略后必跑) +.venv/bin/python -m unittest test_offline -v ``` +### 复盘工具说明 + +| 工具 | 用途 | +|------|------| +| `daily_report.py` / `report.sh` | **单日**成交明细与盈亏(原有工具,中文文本输出) | +| `analytics.py` | **全历史**往返交易分析:FIFO 配对 → 胜率、期望值、盈亏比、profit factor、最大回撤、以及按策略/标的/入场信号/出场原因/时段的归因。`--json` 输出可供程序消费 | +| `dashboard.py` / `dashboard.sh` | 把上述分析渲染成自包含 HTML 看板(权益曲线+回撤带、每日盈亏、四张归因图、收益分布直方图、持仓时长散点、完整交易历史表)。支持策略/标的/时间范围交互筛选,深浅色自适应,无 CDN 依赖 | + +`analytics.py` 输出中最关键的一列是 **保本胜率(breakeven win rate)**:按该策略自己实现的平均盈利/平均亏损计算,需要多高的胜率才能不亏。实际胜率低于它,说明这套参数的风险收益结构本身是负期望的,调信号过滤器不会解决问题。 + 启动时如为实盘模式,日志会有醒目的 `LIVE TRADING MODE` 警示。 ## ⚠️ 从旧版本迁移(重要) diff --git a/__pycache__/analytics.cpython-312.pyc b/__pycache__/analytics.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de3547be0192c4821f19edf4f7a728484afb394f GIT binary patch literal 24479 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 literal 0 HcmV?d00001 diff --git a/__pycache__/analytics.cpython-314.pyc b/__pycache__/analytics.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..57404fdbc83185c22b1be591dbe53f8cccbec5c0 GIT binary patch literal 28519 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

~lw*xD z_zM$w?}EPcuD+BC*uSmDhgHniu1~r)Bvx#k5fZk|cMV%`fk^D^f^%iUxe|Au!)Cv0 z%9*HGJzv%|eK=9tHd8&*`@Pk(gNe4zI|X+-?*x9-`_|z*+tCyZ+*2i`Qzb7)<8_H; z4b%D=ey09=#@YHrYbS=Sa3^GTytRvDb_F%}wVa`h$?fQ>W1(L7{WZs0I z4jH)+6hK&#Ac(#J=#;LQhTawTCS3^78A5@m$~^?xN-B4iyldpul2=b2XaY{5{d}EX z-yn~vH{YiaBUT<#=wtGjqO+PRB`pub+zjVR_tuh(TM|o?$Urn~mF~XYE}TYY{BhwH z_!thJzpt_Knhz>DzUJ>Z`%gJD`!l}FHNMNOnd8>{HCOf5Tm1kmH%9B^;N@p7 zJ#%^F(#X`nZU0Qi?~W|Abj`JNC0lwHTK3Pi>`(NaoNqaqv<)PUPfw8AQM)WQp3+oJ zJ(to{OdU^YiendXrm#RYVS|3rS~+1%S?ecEAL#JIKTs#C%pXE1;7miK@5BsInbYib zgl8#sN`aS2LFj{8Tt)lAO1iM|1Fs4TUIC(#XSKQNH$>F3Kf^0*i?!gCtSWc04O>>Z8a*Lz^+ Kswrpv`TqgW)^TzG literal 0 HcmV?d00001 diff --git a/__pycache__/bars.cpython-312.pyc b/__pycache__/bars.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb2c32b863cd750b5038a601ccd8ce2df7636666 GIT binary patch literal 7045 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 J<~+W%{|8eir|kd$ literal 0 HcmV?d00001 diff --git a/__pycache__/close_legacy_positions.cpython-314.pyc b/__pycache__/close_legacy_positions.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d0af792b8d37e6bc214fd379188170310ff5f4a GIT binary patch literal 7053 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 literal 0 HcmV?d00001 diff --git a/__pycache__/config.cpython-312.pyc b/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a12f8644a80843a1fc45c2996b183deac9f9449 GIT binary patch literal 7823 zcmc&(U2GfIl^&AAKT#AVk(5Z0`nN1GP9@n<>L}?t`n4_p$clcFYIcjkh;t>4O>&q! zBiqz^yKL88$jAaw9u`)xSVYNFvgtz}gFY3-?$f?VrGU^=1WnL~Li?mti`^hkd(NHV zk0h&gkt|9}Rvolqj_6vSCo4+*P#u2-nyTZw?hn(#G*lt>sv_inrdO+(vp{=*HHK6sK&^B1w zTG0AWXd5kU9cTk5v_VT-588$k+7PTqsCTZe?{v%}6O|`%l3ShS_>8+o#`WP36@IK2l3uQMgOEV(lNb^2>FltKE*BeEQPMkpp-7I8LY| z0hqBKXC(D<192_e<;I76X_U~A3noH_XKHL*%`7V`hI?vEyn1VHe%*Wi!o~hU_&aa# ztE#3m&CIRYd4nGs9E8rSN_C?ahL7Kvnw*{&rzQ;BdBdAbDI}wdifq(?ZghP7*246> z;aM1ZY3Rkli-U$+N+#8{jE>h7XGUixXJKDRDUBGsPS=RxQ8Y0tWeGK0l;~?TleBz~ zJ*oo`{|`R^c+5TEZnN&AAlvZKsV!$eMenfm{3&{;rFWg8cUyXo?0x!(f8pese|5}p z3E#BxL4f1dLsul7+>>%0Gx%s7eXxWV63Y9i3YJtil)Hp!&L@9-8*1>Y1muV!mtl{c)d^0$(pb@GK+*V0S zkuoa-*HkhvuF48D$5efSkn9{GcLtJd7xZUyMh(7SF(oaL)OxVAJ^Cx1%`mU_8bF@= z!WVdZ_|fnd|7%~o7)%uX4R0?#THHGGYkylY*mqRyIVzqhHneOFy*K>s@YcEQ*LDKE zJD%P{m=vg^(eQ|3MoJS=G-^aKt;%aDY}bn7n`=_a(%4naaIFwz4eA3s!=dTa2#BJT z$*8)7^rC^0L&@lDU1$RgqDa*giN$cH)O&=|2)6kpRZSV5ELGth={ed4ogZWZH_&jdS_}&`LL-F z8m!WVBU{m}#`mJ{Mz=40@UXSZhGTYPtM`@ttdp>-sGW#fmN z$wxmb@FT@=+ve=!#dj9B);|P`7d{IQ?}mp9;a5JryBnU&U)_)k{Nxu-|AF1>?Asdu z0|#G+4$f8g6?T}9flqLRUD28FbJ%f=U1gi#AUtu(c8iT%vg0B5bH2=uZeTbsV=Sla z#4)ln_3b2UXrCt>+z>tyuYOF1=B;! zcJf@|;_K{ne`GTPGFg_^Qo6V-C3ThN47Zj`FR3Zba4gJC7@p)Br6iNg8P2(zqd@K+ zazD8W1VO%Wp6^I28N;(AQBhOY$v)0)_*1HSXGu!l5oIZ-?V}?Zb<2{bi)l#&w^3Ai z{{rjOz_TiyzlT1K8+mWU2*BFOM7%4d)`+;K$)<+;`chp$wO(Wu}L}d{b`?%0XBSamV6B&Wgv

t?V|%5VP>2J*Oq}JMYzVA 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 RedIU$+^72-zd33H`rj;mbEyCT literal 0 HcmV?d00001 diff --git a/__pycache__/connection.cpython-314.pyc b/__pycache__/connection.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3215dce1781ceb5b7664141240af99c12ade20a1 GIT binary patch literal 3951 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

GpGfUT=uAHbEuZnCqyY6S?hq|BY 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 literal 0 HcmV?d00001 diff --git a/__pycache__/dashboard.cpython-314.pyc b/__pycache__/dashboard.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..48ad389a4c4fcb8971de90b49e74265730fa99b3 GIT binary patch literal 36291 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 literal 0 HcmV?d00001 diff --git a/__pycache__/main.cpython-314.pyc b/__pycache__/main.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3c3d77bd1d1b60ec601925ac09b5c1bbecdfab7 GIT binary patch literal 16044 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 Zr!>az4P72u)V!@*(Nuz@qp{IZ{~v!;k?8;c literal 0 HcmV?d00001 diff --git a/__pycache__/orders.cpython-312.pyc b/__pycache__/orders.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d4bd4a7948df137ab4a79a12c4928eee42f268d GIT binary patch literal 7013 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%0DES+%*+gYbsCTIv1?(-$l*0v#~B{^zW|+> z1{`G*`(W|#XnNi@AzV0|l9OsUte?WGX~WdXyS|QNG4d#CG1oLLK@1PE0y@55KSU&H zwXY#}JgF(^gnB~%O_;)ArSb1jm1v5h{vP?@?`P;Y{u%YJq5gkDolp5~i|qw|Tb|#x zdSs37UU2=wMN#ikx!om%zYm)2?`P%yqls%9WTM3RsNm|k+?5Y6l@K%^I5+U8w97>W zSM(BsPcA|}_mmCXD>b!I2Xn0@1fK`?=aW}AaIbU%iQT{O4pUU1$hk{2Zanq1m7KT% v38XhrVo8%j{+1F?nxLx9B@bx|$k(=_E`>`317x=@-=cam{LGJMiD3Q*;4uxp literal 0 HcmV?d00001 diff --git a/__pycache__/orders.cpython-314.pyc b/__pycache__/orders.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd44c30682d717019884a9edba1663cccdcf750d GIT binary patch literal 7959 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 literal 0 HcmV?d00001 diff --git a/__pycache__/state.cpython-312.pyc b/__pycache__/state.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39b6655ba8a666ec70b4cded19aef666d61254b1 GIT binary patch literal 20399 zcmcJ1d2k!onP)c|H$W1+B;IV2qD0Unbx@LJ>LhhpvP{XAYLuil!MFMm;Xo)bG zD2_8jCaMyZOd_h{L{M#NO_eTY~lVK3YR&F zllT#CK+E|7o}HTpOe_}$1eTiz%*ah6mQm|~b<{Fo8MO`Acxoez*hd`$4puggI7eLr zE>`A8+yg}>F31np=>zBTV9C3@b~fOVEJ2544VDg?CEHu(fie?!kdy3hagyU*Q?^d| z1n+fzMkoDVA!C;O(O||-X*4v75~Z<7IOydw=41ZoY2@~!W6@9~>>tUP$8aTMjt0*~ z1KIvquU4c2{_am~5j8P^{lY)bby0j}8*{zpydE6wK_4lxan>5GG!zH=9dMVE9Gr+sI&_BHm zpWnYnub<1~LY&Lhov^fU8##qPYvQ>VIUfHmi06bCxwCu=7cJ9Ut>NT#JhyD-75cqg z9v5h zK`|5-qo;$S;vWr)Qb_Kgv#=OZy5!*4h(8eQaymsE$K>Ga!EjU&0})vsXXGG82DPS9 z&<^WN5kuOgQ<10`kp0T(pdz+KWLE7|`F!m$8kW zQRpNiC{e2Im+=r8a~MyXF-4S&bx?|o1;ZH&n?%Nzedvs1|G7YrJ!Qr^9v%sW&tz;z zkMEb|i0rkwFW0X>sRU(Z{ct2W67q+K)*p@p*YAx; zAr$)}(S5<-*zsWS%zB01T-Vq{rp%`;j*m^lN6RIP=#Gd}c^Aof?h7|pvN~1VpcXg$ zygLzn=Z$M`BwM-{{5Q=1-f`2h@W$yU@$0VnEF*@3bPWjETmjW$kPEs|VlQ!L?*RD`-_sTfyF*tt3A3A$PT zW~l^MOC6lFR`Q@`DgLNMS+bM$7u_c=}jwsR3ivIv`o3wujUbQLE0lPqS{q zU$gOiPN@ZB)k|J!4SPq~_nKG3EVZJKhKWXRd#2=AL~ig@;P zC~#T?AA;zICdA0uuzqbU+eqmU$G|rsu&g9TBcfkCeEeuX6D?UWbf^y!VPrxCpGIUz zBYz+e84pK0MAXK!$(T@_L0npj$&=`pmjEeb4;z&f3fJ~gcp-;v~0-;BILzx&o z6&dkGWZ#%SB;!K!YvcZKG!&g^?hyw^BK~NH*bE6GPx!{oWNV9+4@eR#}_Qw1|=)q@T1N2rvPm^GzgQwAS>7ZR= z>9YIfc{j2}{XU@wjK(k?-9p)=*GA98<|_`pC2AJwwNB?(=*B~NHUJIq3CHwi5b^OP#Ox#j0!SEzHBerX_+s- z8||6}Rigfk85AmIN-;oC@@2b-gabizL3i|iG7eT^z)$O34*Ewzv7iJ+#46^GfX4Z! zUQ@=52N+~?R%neyQEsI9+kL)JI285yVpS`oK$m`}9rcteNX~P2YZ_)_$(r^l^M#Uh zZR6G5S9YgrH>ITe{FrL zdAr)YeR0RpWb-k)-k9Fljn15DyZfSJ+Ht{!LdxEt+8gfJ#dPP!ROfEBb9b_{SGCii z#k1x+Rqg4nO{uQ^YS;c`*TE^Mz?%D*9G9`m!RWXg4jAl&i#Gf*1`IO>1Yk4n79w>J zj=>Dnr*;feFDnk6m~=Lpp^Ftqj!_>Da`pZm#xiHxKy_lAmw2M?sKGp6F)~!E z4r}#U4w{ZH2dxF*_Scz-Bv4N)05Jo!7uFj<*4!`CtIybVLj7O*yYV_RX5zJsg$P%X z*HJYGs!0q*WrU$%RBLOAM!-iIM@WebM&wa{G-F{BU8XnSHOU*PB`@WZwA_JfvHAz7 zIiH!ZALNwFNX~OhCJV5Vr{c2nl5=)*s6z+Hmv}CYPmj+$ z_x;JAo8NI>bN(xLO5Cc7Ta)6p4@`+huC(yErUCQH`Ml`ocbEZRF5E~{p^>=UT%r9N+7#He;i@5FF--Q5 z__q!Hp_3TTO%OpC1ce=rILSwg^)`iMy<~zO%+8c{LlQU1f0Lic=4W|>l!Sh)H9f4n z6-lh-fFGKcp$*0VI<`Rm2@wLjM{HHR4u-)QNy*z#Ib)H=N5>SolUjFCLZp+ijzoqa ztTUD%Z4a*NloZUTBnVOJUYEcHXe$5q9(1cbkA!H->gc)aZb*A7Ql4hj)10NR#J+d> zul3(Br}jLn;(zV&r02wMEkY5nIS7Q&0GgFuJTiS`v3kq=Ybo~=s{4sQ5KZ)asZ^ow zH+#8Wfx9U_A!G zNl9*r572#qNeB5L-J(hg5fG4`ruf`Gzho%aSlt8SBJb`YYTS&x_T-1I=5&4Y)V>S->83SP`|f}MNSoDsXs?+KsP-mkD(!-5uldMc zCwHUEWh_k-WGo#+;W7avMPCI^)u;rOVA%*h}S4h@}-db_lhv1m&{6J>8+TOMM*>pWW&g+x_$ zAv^?>^kE2h#l)41v9iW9XswW#KhJ%tz5KUNTphSF@ROHQ_1$WHcd~xVhpsK@@>S`Q z@>EHaTGF&=Z+h@`8xpkV&khcv1~iECQA48YvQOR*6>eZsJXiM67jqwX(R5J=2qCb_ zE8d*tEU#CgK(7^Xp%9$NL6UC^U|QjarVEdmxqvV!Ofq;7;*Mh+K6~XXNp-_&2B~f) zNlkSO>1^uHn4%0N`S!2}Q zRGmMQ;a^wGD1uWp3X#|2EY`e|C>{j8ub`gtQzV)M&e!Uj)Y?s{+8t``j%4kwq-Xb3 z@7*`~Ko+$m{Jd)t4p`ughks=wg)sw4dM>T4B=(nRmw+SY{sy^1@vUGp1%wdI#v z7ApQ``(pQD==18D*^O7sb0n`(e~IHukwVS4EIBz(*_7j7?mOtl?|)NLWtdw^N~au} z!9`hv8NS);=_}%HmUZ>nthYD;K-!#|+#=hF5%Ow{Qz ztJ4j7&FXYR#}}rquc^~b{jtUsdR*JGh-|CHl(}6~=8T#carNTq=;YaM)!w*d=Nv^Tdn1Y;=}IFXOdG57IXeF;9X*`Co~TDh&g26mWm2GjAHH^l z@RDST;dM?5d3#BNIkizJ&0AT4*d&HJapBB9yy1qzm?F;aeDo^wai|BcIW}_ymM|sC z*cAAHmPdR8aG(w5y})TN%zz@i*a8DVCrNiYA|q0es`)jK zMhLVKc|AA^i;H+R6qX`q#Wqk3Tpnb=3&95gXMi|1E)S7e&>stpOnAHcKVuk>$Pbjq zqys6JjMG0h<_ko^gQ1~}HJdk*aPj30$o zr`#>7yXB6%6+NX(%BGwQ`D857iLszBW7o~68R4}kcGg(Q5cbtDBf);+r%kyt5E&f} zDGCh20Ry&R);9bxxwO)lS!lDV(_mra<$K2VF%GJN1I`?`NW$CJNk`l!TjI93LozeT zW0y;!G%h>h_MB4>HLR?LRklb}-%e-P1RFChEYWf?Hz-AOah}Z58nt`<;h*B85grgd1Ag-wH1Gr+1kmU7#f1Vn79$1K>xS` z9t%am*JL}_Z^E~CYJ395`rtX-(>lm;b68W}UeS;7U(FHHt84;r1mrFm@&Q)!5%?M4 zz(j{<{fg0>|8;Eh{ineEm`_&F5O;Qo@W-8IlHe>&)sIO}L_+}uUO7~SoH#|S%(g72 zCOVA%;9kIMKoy9Pv+zWm&UOk9pGb~UZ4%5?0iRb~j^@;YE=$X7Yu~Yw0pkjt0URPFfC;ylxF<4^kzX zqRez|Zhj%UMw4XolLJ(0pkzN1g+L!YzkX=D=wjVst<5^tuu}XjzyBTT>F>ydl;bW z<-b(O1_k3wUn6(3xvj5UxaG8?e5>5jS7*CbB~ZDJrHzigcHvgrs=b2n_ksoGWssgU zHMVrpaSDEg)2aK zWWAS$_!-k3og!Ki(~{g7i2yl4Fb$5wcL51ZUM9%iFbT>rNHLPI%*#X4tgD~gI8fDg z-NTu2G2yH^He+2U$d8p38t5x1L4uQmN^FHT$RDwD7i^`=sFBu&3{v+uA$f}98AZMV zO$Z|{=u5m(l7S>X0PdKXbo7&n?#OQ9E`gUVD3Qg1>qI#eZR_?9Iy6 zJPZ*e4Kr3~C?%@MFqm*MnT+JKl+d0rV-AG}BZw~(>Z(n-nt|T#xYnd0)|y)1skv5@ zYTBqaZTz^Y8^Ubr$(f;aP1DuRE1f@Ef5|!{%sj(DUW@8!NgSAaaz3!Iabe*7owu8A zKdl}f_{j6pJ%OujhY6y2Gk~SrbFt@p%#Qrj^izvfPu{JnRcrUm?fb>y>xWYvJJpVz zcRF?_Yxg9p_S{&L>U&o0d-k^iUvz?O817XMn#rt~K9f_!f1;dH?94H2?4l+Jrx~cO z&_1Uyhd#_Qoa1euF;31I@fC)O!g3(nK5P6i5Fm^no4^Y><3~B(%ZdZCuwb9y8+wB{ zc$m&$88_zmP^U+%;t2d>;9(YCAP&lrQLt@nV-75kQ%k=~fnlu*VFk8llA{dQjE+So zyj~~s{L0_P3^abond-<&rx{Y-q!I%Sd#SV!Nj?Y5?YI!DSV{9V1)^}}Jd*PqOs*Hd zHT^Bk&2^__UAnp!7V*QNbn>*--L0;_T63iao{*#0j;7Y^RM+fGuGyWe-b0m0=Q5h@ znm7NdAyw(LuAqS>K(VA^?vd2^x;1&Sx`xHlKi~4toL#5 z=HrgOO~Ng%t*=wK)oDR_nY<(VV^sa;be~B)b~@R$*7E{Jwa1pxm9hK446}!X5XC~f ztbDq92ql^OKch)NKwGh=hHQ(8ITB(Xv@C(;NXVNty|tiS9;9TlbUChkfKH&J;5B=Y zQ0VI4qC4d%63x8!3}6=QYgfN@L8`b z-ZyPE@=ci%&!AkEHE`273Kqo)AQ5ZX#0N5fS9fE-a^F2de2Yw;lK zQJZd*8XAe5!d7=Ii^%}F#=Kq21O%x)28hUPV*nypmsqHW0f~%>8!!_9yOtuG0j&$s z0L5-)pIhP?4VWcU7EaXzDsKX$5(rXZRgLBTjK-rie+xNVNElAPMdj~O!Vn4#285#F zlk+%YZ0X9n%ip;4jgKo^XLvmz zVjxkPENMyjKP*}Ig^epJ{**`@?(DP&+fF&zt z6>EINv~N-eMEtPrmXzJ2Iev;@q@F81soD;;wgWDk`xach&sjW9Jc2{BfGu;68tf<}>im$jx6N@*X7j zqPQS&q(hLia9^@%XBnUi46wHl1ITZn0Wkmr@=WMi@)+P*++|$A5W5l8eLjyF)~}?h zY}_b&MFlwFzo1JN&++?}tWec>wdhLG&q^+tXZV?o;D(vK-#a(kdv*Vn{fYKDf3j}F z{KgOKHZKuRObt&zJ=;4ws@8RU40Fi{gQKsmZ_;EzPg{r>}A{Zn=2q%CeM z#4+)lzO={f@*0?SSx_4cz>c^;Vh_eyyG9;y2N4KN!~*4XFYb)FkmCLhc1EqK6n>Em z5f9&5=(^-#n~A95?iwyew=0gEH+-1Fv{oGYM&x1r!_;ktkz1QRH%!=N#i7rVMaj~p zcf}!@bB6P{^*zhG*_Fe$E>(KfVQ8V1hh$~#Fg7uu_ReW0?2M&3!wo`hZ2g)FBIeW8 z5tTDe&FsLUQSDkes$>LMhOe7)HpEy13=$Mk&Nd}1b{w0NbFD+-9>j_*)j_c1Ssrh-sZKv>#7pKbQDD$1!@pM7|~Sj|c~lDl^YZ509Rg1D5jRhQc@ zwWrF~sb%Zt?DNgZvMp1#v}g5Y*Cp5N-lRulU@E=3ZgxOj-8S`Px=KuJQ>z}EI+R}H zo$FNBY+Kl-uGxDdc>A<^;5l{8bBhB3wIwjMf2LD)i)qn|Qu~MQdTjXXThone?lv@E z9lkRBlaW-zX0>5+vSI7px;0miTsiVy)i3I<*Cp$=rt0>nb$gO^efJ!ey5gyWGkd3x zEV<$B%!|CORqeI2`xD#dj?cF(>`%9N=Z9exaMU3*!qLb7#$J5UHSJog*gSvwhH&Hg z+sALdy!h

i$<2eS?cb!>aGh;>d*Ri+yB&gMq>RWfH}S*TW>r?EkMsp?P#O(!fXx?6=AkPy< zWm=boj*rM!a2{*TXJ`e^Te3EQp~?I?>PIAbcxWerFzva$iN>ghs58c`gtv0$2H5Hh ztJ~LLS%qGq#+XPo^+@hliVz^vX+ig^e)BW5RZ>U{?!4S}sVh-6w<%fH^#E^{RFHAb zUNkkFv{%o*pxRrJr?LI1QC)Qj{zF$2n0Z#1apywUU7kf(9dp3e!-_XMo*18dMP0XD zZQQp?>Tdt?<+XQ8aL;-Qhx&0 zyP@TCPYwRE>bo--YFu8oza6iow-9&_hw!PSW?zqt!wV_m1IkW~7|CP@a)j%&Yp4ru zO+`F@;+Rt>To5F7tF$uI{4^O|$URHnUy@Tn3Mp_fhJyk49lh{%LW43FY0ia94g;JS z9cmv|F`p|!B|DFWkC06ej?MtQ5pZAji16jIG02<1M%H#3n#Ysevi`8q8%1D*;6@1H zyD9itgYwxBTI+EFE09j_jxm1rig`a=+vDldD1US z$1#(mgM;|qGYAYF!e~s}_ahc$JDrSE(uX8tJL{K8L}`-f5M3v61aY-O2r;K90Uh}o zB@97O$BG?^zKojqsnjEs^hs%gsC6YuC|K=F>hnD$Fo(IhGV#OG*2Jcir(N~5&z+td zn15}de8IACV)3!Qq-QT8jjQTs4ox}W?7Ul1J^R?4S*_{7exqW;)P9l>#nZ)U_ujO- zVkR)#ov^%LeeIjKO;%tmQZ&Ew;I-EMmH#Yg}+c8x5TXkdM9mB zLpT=nZc=Ot#8cL*iRYq&=P=7c>N&42+E{1=0Fx}NfY8CHJ2}ZZDO?!9PIto0O>rVCXnd6q%d6~c9%6sdEm0N$exd@j@w{gu)qIIp z%&th?BUZ$TxA!%A`#N>#UE=*29xb9|?Z_$J3lco_g6u)P0{$(X1_3>DTvK5CDt!gb zIL5l)NA&O%4_K4w&ufMBP&^ zo(=-7hTzE8zCNDJh4dJbn}H?t#1!p*h(@S3ra1U-ogR%OHO3 zAV@z?kkjldI!2`JBRgjAS8tb_C;JYYMM`Pl0{sA*oa}$Px4-w`etZKG#*@ncB6z8% z5bL1fm)wjGFfT=HzoGV_+4Btmh|tnkLOhL56fsN&&ra=i&_jU?0E`$h#PD_-2!%=^z!iJ*(ulCd%>Rx~p|3)tFEcL}eo zuySUKqZ$MGv?j5QD8z^{fZ1)eILR3Oy7w|>hdKf7xYNnoM z$RW1Npo#40I_lF1+W36{gWE+IjEFEm&LE7TAAP*`pIu$0yJCM~bfn7%jgqn;l1PjN z^P#yuQYZB_jt`#O6mLE?Gwe-qh*`2G@D7?D-40+I_74Rq1KL4U52BdyX&Adnah2HY z#+r_^A5$@2Z#L#8J1Me#^?P0T&KuDvdHZ5Qr1CqI%uzy%o)Hj*ta<9cN7vp&;`M0Y z^g3N2G@7vm5G{@PXDx{DyL6oq2HCXazor`aY8K3sv9JJ2`mi>;HP4vO;8#rO2UA87 zWG!WE+1J8?6cy47uj=47n`;;sJ!B~-R{fPkRYb(oWn#AHr7{jaLEGJ`x>@#N*2h(3 z;aXL5dHB+BqDo!WK6UW!>e|_ZKc1Ws(&g2cdoK0N1`->S<*jq2$@2DeT}xu`l_N9z z?^e`Y?!VNZuJVz2tOXnD0d?IrK-$VxfV7p>sfsSOqHFFPU~=%rVRhHh^!m;7uc+(y z-uT8B0$+KO|Fo(xAedolOi6V*1rFfgDtZkYEbYj@1-OEH-^Sn-+XFTZr@CE$F2va&5%(w2SdC6lT03Gi@b6++L4uAaGa zX0B!KwPeG_`R;|)AJo2Id*k@+rsNYx)b4C>#%~=Kyd1phb@$3RcR4H`^iwn!o-*th zk1tewQ1d>%g51Ysf>$^o|D4G8aU|Hp<;oC}#)&*hcN^(93+Sgme7=m+=Nm=*-3XQ4 zJ|C3hNcN7+=aV7*hC;3G8eU9>_8k?mP^a3joqnZ00&Oa}+S$dYrtd{zO1`y5U`uP|G7FS#wKq`)0&X`bH#zYyvcyDa6i zxP&ZmZr*(DoA;@>By!K=N6_vGRhC{pv3H51e7;-D7mjQBjd3ksJUO5hKxIp;MS9!z zY@z@7eRk#Z%}&b+{_`rBoj+e?wmierTqk8RY znH^m~nBMgGeO6d%F12)0bz}>e7_u7}4q5uK z4NC&$eTQ#vzS(u3%JW@>fHQKLF)X166BYtCDjtRB92_QVp0Y!0i6?00L!W_~~aIma?K G?*9XnpR*0c^Qzz+aTmdv|@~DH&P24x4PTaEdz3# zFrG|CIG#nsnMF8G5J_e%c9RpblR3fJ*|EVp8)wh#jGC6{rb14fGdX9I`DcaY1W$6# zp8b8ds=7-}8#&3&1>L%R>%QvN_kH(wzwf*CSW%&sLl|rQ?5QUn=eU2NA8IftiTS@q z;v9F3^YDAQxP0Z~Jo~MS>)2Hv*R!i3ZopNy*SN1$9(Mzt_CS61T9l zVXt+MEpB6Jey=@V_$=RBt#+He)^pD~I(Yea-03lva~@MU7jnjnmvHr*rvSgr9XjQH ziHC3G+?HR_&mOm4G7S3T0m)2pJUEOL#iNmMz|BjBUVr=~uI7W$crX(7hX$w@r6-P_ zTaUl_)rih0gJ^Z=JSBXHGTztmpbytNl^kMo!=!<5nmIPhiy5 zTu@iX)o^v}-~N@G^)+0O7r3J(YHKEL@G&Y^$1QliGWCA;TF>?Avm-r;zK$+c@1hlh z+)531k_&QjKkC(O8okvo2IB#Nh)3|pBg4S~e<*Zz6EcRQ!cZ_25Q1SLelj4${KElZ zFeo9%!JSGf8L~(=>x)2$Xs}Yg0hGIfc&N?263j?A* zb}|qXRz^ft?0DpiH57>E@iV@8(UoHrOSVXrf)?%ry^;$Uwp0ePrV7OFrU^#-cf$ z-co$eUUXsat9!=>UhA1Nah9TwKQeO8@{c&)Qk=Fs)8@iCBa*+JD?o+c#&%=4e_z+t zz}>7V>2ervw$&ni%VF!Pv)rmOQ@X*{U9P{?y0qJI`DgZOWV>z8s7h=I7l%eC_!9YnO*qi2HJ$Z`xU2jUx| z+e9KfUcmApz){l7^35#A!g8!E$HH=KEXU4rte!&DwXysnmhWKsc9!F0ImIle(6fT& zm9V^0$_w%FB2O9WJ3LDeJ3UJg7qe33tW*W7S;BHESxyzpDP=h>mQ&4g$~-GsUJc8u zr98C0#N%eU0?Vyea+k8a2A0=IdGT`3Dwem56|A0jmkP_{3QuayqQ$MU_giW+567V?l7#pb(D;eqrCC zgFTGnAsO~|fl)(e1+Y*=gn;l53`9o4@svBK~-@&;Z63&-$Wb za3GM$wSpTL2}(U$(NV48X@r&o2U>RQXn6VMm#qtya9Zk!V&?@!TloBo_#yrou8qfR z>qHBRNmjNL`$!tOc_xN%r;zRx`g{W+e=If_9EkgTF>1Zx9FrYvotCO|URJAiDPM)s zv2sKo(=u)`?&0sV_-ea+6=|(nu!Pgd@n@c&z&pnhj#v)a#m~!yv$ z&cmro66#YgEMQ@$C&~$V>t_~U)W;Gghu@>VPSby8d~JXp;SBUZYk8J^6&BPHOet1- z&R!SXFU{twDfK>q6ZDpWCP>rjzyeyXRMXk3S`#IS7Fh7;K{bX^JNRKR5NkaZ13Qh{ z2mK)GaESdJEfxa2C3p<;+O9*!oQ4%J;&Xvs+|~3R;?N{yyi%| z>aJ|Nye;MGNVqz#A4|G+j2ULk73rG#E56IVRL%NC&HC$~OV;e7?5cFb^0(Tqwxt?2 zCmJ?SZ#kH3=%xI+^qO`wW=)&z7cA#3ui21DnQIf~+8MKuZdsFR*_LS8mTc)vn5omE z3Bydqs&wnxRO`+}>&|5B?lHq1bCt|`#7HVL5m7V2u^x#>2@VJk9)G%aT{9tcC6G9| zric@h7>JLEVc;KQd?XqQ$iT+PgrFi=L^ega;zR_*Ycn?4j9h$W;i)hl|+>AX$R3h`f0ex1+3BhX|Ckk?Qe-GJDF9R-`A8Y98WDtzcjjb_wOqadSE& zaHg~DlJ(2hiH?-3CE;pGxgJlr9#6V9CY_tcI@1p4g^}|kC~wL9nZ1ZZQl>w2Zj+_i?gwZ_)9O@C`E zkC?6VxWfWbA6qH^6$wV|DkBqJ=ULT`W@tU%W*15Ys+VV*%eKFnRYUHa;={HvT_($>d-mG2vn=IoNH`l*O4~KLC|}(PBb6Bakwn3DuOK-izu$x)~Uy-UZyzZ8!}P3ev0Q`h_g)BU0CbxXpY% z!uRsc8Q8;dBRXx3-VAig_NI;oY=E^8^VW@fJ#LdsuzT=Bq^Z&npa(zQdPZ^LVY)?y zVk9%MMVG;b-RB?70-A@>w&pH-5BpIWphFgDuiI13P~P*m{2J#NioR zL%OEnwVrhSvauaAAWp&$RkzJm69cp6`dI)EmXR=5-7!~ZmswT3kZ7SHeyRDAQqqUJaIDa zZe@f5jSZ7b^jM4zWMUyf1x_TP#MGt=26YltR=XkSEeW|RUTz6Cz0GLn9QT1t3UBsZ zIr7GluO3a+v?prXlQrvRZ0qipE=@a1Q;zzCqkh_4pPhE~^&8LvjdA`{!bI+2n8=8N z@HK5JwY$wnfWl{K0;&-)A!9a{+~*Ko-Epn-+Tc{>UmavWv!g{p7HB=gXwCD382`UF zCOk@OG7DZli~rPhO;xuJg6j#<^=gERT2B=rPVTVouzp0}2C=gXnX+P8)4u+b+dsib z^+^C|uRb5T%wp6bx#U`o>u)@+dlE0y!qsvk`lvq2uwon6i2m!8oFJ|2v|(ofD?0?T zjQFTrW_{)*T|9=xcL$1zJ1JUC(Jn-iH5dzqp=!WZO?-|rjL`KC$3)WH$&k(<3&VYM zlYd^~UyKpGW%QLIAvdWZm((BL%rSDgio)_5tVMWh6Rx!>*Or89OVYJ9>D)HfdCyTf zQU7LN%F%G!(U5kp`fmBP@^8}uT(O_OR(Z8-viP2>Y104gvXra!BRyAHlWy<$!Mg9Q zyHWNxo7wLP{td&m$`4tEIgT&h&lBuxoU?Myk}=D_&0DCYkALB)$fzzI#bXv(c^RYW zd8MzuF{63j2+)P00X&+;+{n#f>74cf~f!2A@LgZ6la_IRK@meQa--Dq77#*_Ex z0#h!?K+h}#Jw|4X>JH#}#SIkEHbLy6=paSCh!)_w5*gQN_x*XOi-9|BnG@6wt<^+b zddoH-yoq?KZhfL|{ofhhvHsBdZbz#7aH9Kga)bAdz5nBT@|#CF1x@V*z;eV3c+=ecazu~XqI-0-NtcasvW8Fp zeBi8PWQs>%TzWm?c0`M>%EKrXb0K1a*GQ`}qN}kvx_oaP`z6a=8kriDk(%efjhu7X zTPz0i9!--f?d98Zt1=V&6kQtu!3;gBgMlREX~uBx5i%F_45BEuJBH>R-KN261EM!lG7=y)zQCSdoR2UaRnpIX{oU&e*l5Qy?4r*0QQsYB%?BPOe z`n%95R&BfUD=RNGf4TY1Ggm%)`LoHA=2Xf0M9KPO$%eGObo}s~QQv4CGXTp0x=pkv z%yn~S&Qh2%*CBZ)-&#(O#OP_UwOpnrQu=7M5JX?s^o+hl_S{?19x@np70^UHhiGvU zi=$MGSe22O%?SH0qRSvLT&|w^p5d=P$8uyc(+V^iJpSzS0$bkkf{~FMdbNb9+{~(z zv3Jm^OS8Om5_*3$Ip*6f9NW@N)2W20W`WK}kXhsjTkwKOZPt*lSHIya*uMx2FywtU zFK=F)tnoZvM%tre`(Z(d#hLU3N+pUEUIHjx33JH6NjFRWXWgwmjLX1ZjDcwVm#qG1)He_b4+T$1Cgoa3V&13e!6*0N8xDp?@Fegu04s`& z0Tt*#ged9pY?4VXD+p#?Vg`0@gr+LnLN~YYST`#4q3nlN#a|DfQ=w`XoIBh{XFP-r6%-pM~>~_w+WH zB~f_?1nAv%2D-#3HLw?vWE5eeACwH_1S7UmHa54Myu&;|sf92f4UoD-e1#$!oeWO0 zL;Ewz%ZyKanSL&#h^a_OTr5`u?DF!mQKZ@#O%=M9He_d#6iQB}=xZ?Iq(oCpJx<;*Xxr)$5{8cJz=l;nXN2cS%V+O^ZVv>21_S@uXxG$U~1{|#M0$c z`c!fAOmXvdKJ6^NRB*B2O~V!I8`jD8uR5ld%~rI|INRWbQ__M`_Df{-@_U&ww(#iieMlkVaV{NycyT!M3Z7|+yHB)+nt=nRGPj5i_J&T@VyRCbJ z{=M?0-R=7K+Kosr*sv19VFLMG@Z=|H?((6w4pgL&5sYXhqm?tr_q6e&w0cf%*LFp* zQsUJuQW7096-)=jZASEFd-CY|OCkRCT5yct2JP<-1s&FLPr7AA%aefhq;Nr-dB&c3 z#{F!m$e_Y}qT6FUnsFQ;C!_o=<}Lq>Z56|?GlBiWd=mR)W}Tl>guK@m1+ zD=-L=PVNXo6P5*Sk1bmviWX2Uy`y&g%@bs8fT{Bw*T?O`QVHW0=vWTfeZ?8>oDuK3 zM1IE}sL0mogZwqV2O?A-3WTBm)U~b_hfr^UO2V?`L?9eE6BV~g9S_NvT!)9&EzBP& zwzV}=>n&=q0@1H9z-ia^MDw=E7vAiiie9IP)FzT47#@nieRpJ7GQH>zjRa!QQ#0vG5>6_h$pyrhB`g!4MCPQgQ>?Kx zV^aO15Arn>i%lV7U>m5|=1SOHDO*Fr){wF-yKP%`7s9Z<>8+}(RjK+liTX8n>f0d% z$M&SF>aVn1ZuxrKMbo&Rq3gzkvvG3Q)Seq_ZXEe5e zgHfUSR~z+cK1e zm{$sQn3X;*J5oSCa32iC=uu8-AfLWxv}`}T3&7#byGvh8q9N%-?v2T~Pk2+LT7xw+{5B_uH$B}8@b2D~7YyHB$ zSNHvH&WJ*?W%)mMmSSTlUrc5T$(+Gs?2fk4dIR({Dw{_mxCKBn{Vk1NEf zAu`$1AhRq&_uTRjk!2P|3{VM-$TFm^<^rW^h-?x60|Eeam;t&Dh`&)GOHRxQK_Rv* zIrX>|4K>z%Eox=*b|G|qSVL`~HXo)M%Kbi?ixH{GTIi>M+@OZO^7+f3|Gwb|*8gI? z(ecivw>Q1-pIO~|$NrS6gr;JO7JBoIzIS|Y``!o4>%DF7Wian4z&xgmUcZU^p{;X` z{-&|Mvsr($*@!fI{pwm*fS!pj=eHpGlv)YqH>O2JZOoleRv{7tWc7$5Ecc&6clbk; ziY-UPR0&3787ZX8E<=%k%hdsPxpwk<@$ZD$4Owg};smvDS_FUiZJ+--62A`^_3~|; zN{{)#syE{w6V-e588`=*IavSn`D%D@ut;t37{^q97#Wp@c#{=)jUMJ%$9$6qGycjr z2Qd&=m2|D*B|J$Fv@ndocH@Uz(T!zh7~U?lhgDQ#imw4P!%3yN0t&8JY?4X23nesp z7F@JmD!N!Sab~I{>1<9pS0|jSua_pB>&A>fvpL5PUwZoD(-WgpYm!S^uJbcX)}%`- z#`fQJlxEjVm4=8B>1E6dh#o~lBg`#c?VuEfJ^YE%bQZJ=7g(x#??A+MCLqabXxH!77d}Q zuhD?&G;hC$4N!a$<;5_f#o6Y6qEy!0XC}5mbX7k`muv9p!bRpDEk|Uy=NPK{c`g5V zc^r%Z4~1~dC7-(W58$Gg`-mviZDW=**ak&d3B|Q=h8Z8A$Rc}&o2l4H>nCfepb;Ax z9tJL4g!7=>YLROQa?_7sz4!6pTlfs~^?xen+pcPgo%)Ld6P**ov(?Qhd-H93^Uo_*5U;l|UMFby zr?s68{9l(LdZ!VQNPr5D%eJ>68VZs4@*!CPXxEPY;bSk_yNrjLQ5Tp5C)%8)IMMb8 za5`<47^l<9`>4f{S!ZRl53e!%;K(pD^_XC#Ey#sfB%N~Ji zELz|#$R|}afXwX8nqn}!fLP{*%oDZrzfNyZ4N)sfEMOBah;92;hf>EI@)GfBVS6A&v?dLsu&F&pyBsbvHGc;Cm=!I@7H>mo6NA=NW z=oe}1kms~h$O-Bw*>R=voSXRRG4z-8cl3Kb#>bU6kJHPfuVSH>JWX znobDYg{IbyAv)SbI2%Xk0`bAXkbfi;XRzCGT*``5@rT79xwC~R<+r&xyblbA( zHF-0Xk5j)%6~BW>GUIIVi#{xQ@JeA&3CS1{oMOSqXyPQRY(etj0i~*3!JNIv*!wqV z09e0d>7`W{SEWi;BuZ9HnXfk_OV*7Qq@CrLY!_`4-ASjw0Dro?dg91z`AX>GDumgJ z$Hw-im$|1}W|wWav0-*u_q&1FUEbMc-svL)vyB5|JI7lRb|Ede$5-99*Fb<5yC(Le zoi!69X=nADgI7+yaq6p~RBcD1wj)`)KJBb}bI+CiZ|whe#doW(RVS<0r>eIns<$Vr zyXGv$>Y}mT=l9Rq;pL(^3gt?eT@yPeH(XztZgkJ-IZHLU-F5N5Fc)30owrSwbzDFB zZvQ_Vo%S4^-TCaaZ)n!{{B-E-tZ(#=`6VWkdKPSS|3AuM@$c!yeL5ueD-_MjS%)zS zIZH7a$?19xn_IfReOSK!c%6K`TU|G&ryFoza&dhzO;HL_wvKcZt2CP(ICQ@lz28b!azJYR^Ja)L$z83KoxbkBRJtAEd< zJa4q}+-uT$<)|`}NjOVR=lJQ-D-zC-m2i-inpHrp)4xpJUqJ>EnX!;p>uN-s8}jbb zG)e=Fw8K2RO+6ZMM~4|h;(O>}v`H({VsPgnohL*ZnP!IlIKdb`@dWI!=BxrimQ(|Oah-mX2odEGU7=_SLBsL?r}88yk0r#O~w`f0}W)v)1}snt&PddaRu5@5ZqFX2v^j6vs+m_# z4Xg_jBU8`LuGlt&mUnj zs?WHY$1aBjS(cDBpjVwXwHB^kp1#cD)gjicHK^PgHF+}*HLEznh-zztWh5hxW+WN2 z2MGqMUf4pkrDWr2>4V=i_EEH zRROQU8K(6_D;!p0!lAxiE4UQeEutf;c9L}9e>x zrM z!&Y)k48qNysVM3u_qtuyQwpGS>WiQhD8*o3D#arDBi zU#~p+f3}UT%LvZn%FvmONZ}uOLLf>t0CDXQ3{G}o<<3KWdk=K>?cCv(@J$7pIiJdQ zl4Flw96W?U92^?LM_dBnwLinVVi=TMkayFMixiQ>l?qP#MH106aAY!>#4*Hmju8F= zRUsA^|Cyp;im2fj8E$0xt9rLAQQec(LCJ!+DHkT@Eyg(in`jg2H#=7%q>7uSi<>6b zrktx1&Q((py=?W^ zP7;_!=Zn(z?zFvZqWzu9tDk#c_mNR=u`15Y4BZ`^`pk{)Y0oq1#*PfYTmJzaTdPb! z(65_ywO((Wty(u@Ue6A*2??_>S$fCZ{II+w)*;rP-;d}~@|O0}_`})f1zQm&Z;`)1 z_oDT@H~9zRBmNM32@tN&p_?2DySQ*I1c$~}qb3us-Mn!pz#zL^H7Lj8h1GL%&drrf zsLDU&=cVUUgMw!5^WQc=Rfl(mGZ*@-1H+-bN;*}jz-i_$pxH9yFXdg}@Dep zEu2IPCY*oL2rYGRUef}VGr*pr5v#_Q*{hxRXn|(CoDYQQ`gcK?E?5aW@Wd4AIE+6Z z9DAq+T3GN*fVRr0>o6WOM0-^KAVg=B-AH7CXteYe<>3NdNy`jOI0O6)uj8yj4qo#9Opj zi;MTEopa1_Mm?mwKwPrWCGOS0AOHngiZmIZ`6xGLd^4IZ&$0e zC4;g^;Y4OW=6n{8$it4RHa_yy9S8+*85CB^?#Y4*IRKqB?I^>hrULjHTwst8385M` z4>=9u#L_1=xS)J?T zbY})E0S{>vXK+qdCs#O$dNCnPwwV_BiD~?Jqd<=Ay=Hf?eFG|8(Uk~_NN)bWBcf%y zjf--J488c38}jX%Rnmmuh5nb>N%Jg6no`&-$cJ#Tlh1?vz4#WWO0EwXUNi&hXL^l{ z2k3Tn$pCrlL^3z9=LJkyL+Ni&G)NI)K(|pcFyu*yFwM|!sFAsk`a&gyq zce-KaTdS_FnmPn8<%SMmzN)`{dA4E4_^#KVgzH@$_IX@&8x($JRbvMjE*agLArYl# z6*|kLocTOkhK~j4U5QRW$e@!Ge|cy5FKw;GTSvc`X-MUKnXnWjP87%7+fiNZsfF4a z!FQZi#@t$ZW_uO##8(KLHaVc`;mz0>g#9N1bOD@$+bRx4;p+?RCY>aJ#$>eq5c|Aj zW@;7ZQe{x`aa9{WUj=_ljdF!4QfBEz-l2$IxTMDkC)w%j8s$wQayw;&f0r@{(n|#c zID~~`Rr0=`Ah@i4`3|LiN+t059PpB4WJd|;3p0Gg2t?^qlEXN=H7FG*)50A1 zW3&bgv9ClDLCHbZ)1U=~YNX0fBq)Yft)ONQ&Rhux?~j6e71a~?+Q8)_cPdCvytL}l zsf(v3D`uCj8ryxZ+%>WLKfVm_&C<$Cn=Wpe7?@m>ENz-9zEip?UETP#{o_0Dl~rBp zx!9Ag@Il2qIWl!*cEtv43YILzreH~Bs;o6p);e|OM&R9jvs(|Q+d8g4JKNU%?y-;b z{F1}`2NiV__H=E-mv_%E;VPP71FUray4q-|x?jY(-1szCs%mwjYW1C}wdePZb*ItQ zzKi=NVw0W8vgKnt(~h$L@bW$9Qu267m-VH~$|sHJy0`>g*EEb<((ab;R$Z$~xi==< z8}GO`C(4?}_oU0KX3E`nD+G1?uG)za_U)?c?xbtW_>OdKS5KTuRJie3u!(|nSnYkm5p8bdvmI`v-1=Q205KMgja(*`>n!R-Q{Z!j`|&kbXf!Lg4dYqylMYO*bKTpx? z6tz>dmLkTRY=8c@^z*+`^dpM?ks`*;zeGw}f=?$xG6V&Xk zUj7CKL@&2pwxqA1rYHgm=3@VZ=;J@<&T;p3Mx*&b5q(r~j(t?|K^4A|Hb>?I zxamO&KEg9cU*dV-#Aj6I=vyieD)7w=^5iJ_u*_iG_P~xK+~gtG@Sq4MZP{_#2eovp z?n8DA@52tOv5)_-0*00kml}*u@l>IVxzs%}IGzsD4R50jQajs_a)8X|M-$m$EZGQUQIEi8LH!3>3*OAY$ji$;X0d zFDswLE}-vBhl0o1rvWmY literal 0 HcmV?d00001 diff --git a/__pycache__/test_offline.cpython-312.pyc b/__pycache__/test_offline.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1d650f51182450aa0a1eecabbfdc3d351178875 GIT binary patch literal 22100 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 literal 0 HcmV?d00001 diff --git a/__pycache__/test_offline.cpython-314.pyc b/__pycache__/test_offline.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bcdb015647d9de8f6556d5e35447620ea96d6582 GIT binary patch literal 23316 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 str: + return STRATEGY_SHORT.get(name, name[:12]) + + +# --------------------------------------------------------------------------- # +# round-trip reconstruction +# --------------------------------------------------------------------------- # + + +@dataclass +class RoundTrip: + """One closed position slice: a sell matched against an earlier buy lot.""" + + strategy: str + symbol: str + qty: float + entry_ts: Optional[str] + exit_ts: str + entry_price: float + exit_price: float + entry_reason: str + exit_reason: str + gross_pnl: float + commission: float + estimated: bool # exit price was estimated (missed external fill) + seeded_entry: bool # entry lot predates the ledger (basis approximate) + + @property + def pnl(self) -> float: + """Net realized P&L after commissions.""" + return self.gross_pnl - self.commission + + @property + def pnl_pct(self) -> float: + """Net return on the entry notional, in percent.""" + cost = self.entry_price * self.qty + return (self.pnl / cost * 100) if cost else 0.0 + + @property + def hold_minutes(self) -> Optional[float]: + if not self.entry_ts: + return None + try: + a = datetime.fromisoformat(self.entry_ts) + b = datetime.fromisoformat(self.exit_ts) + except ValueError: + return None + return (b - a).total_seconds() / 60 + + @property + def won(self) -> bool: + return self.pnl > 0 + + def as_row(self) -> dict: + d = asdict(self) + d.update( + pnl=self.pnl, + pnl_pct=self.pnl_pct, + hold_minutes=self.hold_minutes, + won=self.won, + strategy_short=short(self.strategy), + ) + return d + + +@dataclass +class OpenLot: + """A buy lot still (partly) unmatched at the end of the ledger.""" + + strategy: str + symbol: str + qty: float + price: float + ts: Optional[str] + reason: str + seeded: bool + + def as_row(self) -> dict: + d = asdict(self) + d["strategy_short"] = short(self.strategy) + d["cost"] = self.qty * self.price + return d + + +def _read_records(path: Path) -> list[dict]: + """Parse the JSONL ledger, skipping blank and malformed lines.""" + records = [] + for lineno, line in enumerate(path.read_text().splitlines(), 1): + line = line.strip() + if not line: + continue + try: + records.append(json.loads(line)) + except json.JSONDecodeError: + print(f"warning: {path.name}:{lineno} is not valid JSON, skipped") + # the ledger is append-only so it is already chronological, but a manual + # repair could have disturbed that; sorting keeps FIFO matching honest + records.sort(key=lambda r: r.get("ts", "")) + return records + + +def build_round_trips(records: Iterable[dict]) -> tuple[list[RoundTrip], list[OpenLot], float]: + """FIFO-match sells against buys per (strategy, symbol). + + Returns (closed round trips, still-open lots, quantity sold with no known + cost basis). A sell that exhausts the available lots is reported in that + last figure rather than being silently priced at zero. + """ + lots: dict[tuple[str, str], deque] = defaultdict(deque) + trips: list[RoundTrip] = [] + unmatched_qty = 0.0 + + for r in records: + rtype = r.get("type") + key = (r.get("strategy", "?"), r.get("symbol", "?")) + qty = float(r.get("qty", 0) or 0) + price = float(r.get("price", 0) or 0) + if qty <= 0: + continue + + if rtype in BUY_TYPES: + lots[key].append({ + "qty": qty, + "price": price, + "ts": r.get("ts"), + "reason": r.get("reason", ""), + # commission is per-fill; carry it per share so partial + # matches take a proportional slice + "comm_per_share": (float(r.get("commission", 0) or 0) / qty), + "seeded": rtype == "seed", + }) + + elif rtype in SELL_TYPES: + remaining = qty + sell_comm_per_share = (float(r.get("commission", 0) or 0) / qty) + dq = lots[key] + while remaining > 1e-9 and dq: + lot = dq[0] + take = min(lot["qty"], remaining) + trips.append(RoundTrip( + strategy=key[0], + symbol=key[1], + qty=take, + entry_ts=lot["ts"], + exit_ts=r.get("ts", ""), + entry_price=lot["price"], + exit_price=price, + entry_reason=lot["reason"], + exit_reason=r.get("reason", "") or ("external" if rtype == "sell_external" else ""), + gross_pnl=(price - lot["price"]) * take, + commission=(lot["comm_per_share"] + sell_comm_per_share) * take, + estimated=bool(r.get("est")), + seeded_entry=lot["seeded"], + )) + lot["qty"] -= take + remaining -= take + if lot["qty"] <= 1e-9: + dq.popleft() + if remaining > 1e-9: + unmatched_qty += remaining + + open_lots = [ + OpenLot(strategy=s, symbol=sym, qty=lot["qty"], price=lot["price"], + ts=lot["ts"], reason=lot["reason"], seeded=lot["seeded"]) + for (s, sym), dq in lots.items() + for lot in dq + if lot["qty"] > 1e-9 + ] + return trips, open_lots, unmatched_qty + + +# --------------------------------------------------------------------------- # +# statistics +# --------------------------------------------------------------------------- # + + +def _mean(xs: list[float]) -> float: + return sum(xs) / len(xs) if xs else 0.0 + + +def _median(xs: list[float]) -> float: + if not xs: + return 0.0 + s = sorted(xs) + mid = len(s) // 2 + return s[mid] if len(s) % 2 else (s[mid - 1] + s[mid]) / 2 + + +def summarize(trips: list[RoundTrip]) -> dict: + """Core performance statistics for a set of round trips.""" + n = len(trips) + if n == 0: + return {"n": 0, "pnl": 0.0, "win_rate": 0.0, "expectancy": 0.0, + "profit_factor": None, "avg_win": 0.0, "avg_loss": 0.0, + "gross_profit": 0.0, "gross_loss": 0.0, "commission": 0.0, + "avg_pnl_pct": 0.0, "median_pnl_pct": 0.0, + "best": 0.0, "worst": 0.0, "avg_hold_minutes": None, + "breakeven_win_rate": None, "payoff_ratio": None} + + pnls = [t.pnl for t in trips] + wins = [p for p in pnls if p > 0] + losses = [p for p in pnls if p <= 0] + gross_profit = sum(wins) + gross_loss = -sum(losses) # positive magnitude + holds = [h for h in (t.hold_minutes for t in trips) if h is not None] + + avg_win = _mean(wins) + avg_loss = _mean([-p for p in losses]) # positive magnitude + payoff = (avg_win / avg_loss) if avg_loss else None + # win rate this strategy would need just to break even, given its own + # realized win/loss sizes - the number to compare the actual win rate against + breakeven_wr = (1 / (1 + payoff) * 100) if payoff else None + + return { + "n": n, + "pnl": sum(pnls), + "win_rate": len(wins) / n * 100, + "expectancy": _mean(pnls), + "profit_factor": (gross_profit / gross_loss) if gross_loss else None, + "avg_win": avg_win, + "avg_loss": avg_loss, + "payoff_ratio": payoff, + "breakeven_win_rate": breakeven_wr, + "gross_profit": gross_profit, + "gross_loss": gross_loss, + "commission": sum(t.commission for t in trips), + "avg_pnl_pct": _mean([t.pnl_pct for t in trips]), + "median_pnl_pct": _median([t.pnl_pct for t in trips]), + "best": max(pnls), + "worst": min(pnls), + "avg_hold_minutes": _mean(holds) if holds else None, + "n_wins": len(wins), + "n_losses": len(losses), + } + + +def equity_curve(trips: list[RoundTrip]) -> list[dict]: + """Cumulative realized P&L in exit order, with running peak and drawdown.""" + ordered = sorted(trips, key=lambda t: t.exit_ts) + curve, cum, peak = [], 0.0, 0.0 + for t in ordered: + cum += t.pnl + peak = max(peak, cum) + curve.append({ + "ts": t.exit_ts, + "cum_pnl": cum, + "peak": peak, + "drawdown": cum - peak, + "pnl": t.pnl, + "symbol": t.symbol, + "strategy_short": short(t.strategy), + }) + return curve + + +def max_drawdown(curve: list[dict]) -> float: + """Largest peak-to-trough decline of the realized equity curve (<= 0).""" + return min((p["drawdown"] for p in curve), default=0.0) + + +def group_stats(trips: list[RoundTrip], keyfn: Callable[[RoundTrip], str]) -> dict[str, dict]: + """Summarize round trips bucketed by an arbitrary key.""" + buckets: dict[str, list[RoundTrip]] = defaultdict(list) + for t in trips: + buckets[keyfn(t) or "(none)"].append(t) + return {k: summarize(v) for k, v in buckets.items()} + + +def daily_pnl(trips: list[RoundTrip]) -> dict[str, float]: + """Net realized P&L per calendar day, keyed by exit date.""" + out: dict[str, float] = defaultdict(float) + for t in trips: + out[t.exit_ts[:10]] += t.pnl + return dict(sorted(out.items())) + + +def _exit_hour(t: RoundTrip) -> str: + try: + return f"{datetime.fromisoformat(t.exit_ts).hour:02d}:00" + except ValueError: + return "(none)" + + +def analyze(path: Path = LEDGER, since: Optional[str] = None, + until: Optional[str] = None) -> dict: + """Full analysis bundle. `since`/`until` are inclusive YYYY-MM-DD exit-date bounds. + + Round trips are always reconstructed from the *whole* ledger so cost basis + stays correct; the date filter is applied to the resulting closed trades. + """ + records = _read_records(path) + trips, open_lots, unmatched = build_round_trips(records) + + if since: + trips = [t for t in trips if t.exit_ts[:10] >= since] + if until: + trips = [t for t in trips if t.exit_ts[:10] <= until] + + curve = equity_curve(trips) + return { + "ledger": str(path), + "generated": datetime.now().isoformat(timespec="seconds"), + "since": since, + "until": until, + "n_records": len(records), + "unmatched_sell_qty": unmatched, + "overall": summarize(trips), + "max_drawdown": max_drawdown(curve), + "by_strategy": group_stats(trips, lambda t: short(t.strategy)), + "by_symbol": group_stats(trips, lambda t: t.symbol), + "by_entry_reason": group_stats(trips, lambda t: t.entry_reason), + "by_exit_reason": group_stats(trips, lambda t: t.exit_reason), + "by_exit_hour": group_stats(trips, _exit_hour), + "by_weekday": group_stats( + trips, + lambda t: (datetime.fromisoformat(t.exit_ts).strftime("%a") + if t.exit_ts else "(none)"), + ), + "daily_pnl": daily_pnl(trips), + "equity_curve": curve, + "trips": [t.as_row() for t in sorted(trips, key=lambda x: x.exit_ts, reverse=True)], + "open_lots": [l.as_row() for l in sorted(open_lots, key=lambda x: (x.symbol, x.strategy))], + } + + +# --------------------------------------------------------------------------- # +# CLI +# --------------------------------------------------------------------------- # + + +def _fmt(v, spec="+.2f", dash="-"): + return dash if v is None else format(v, spec) + + +def _print_table(title: str, stats: dict[str, dict]): + if not stats: + return + print(f"\n {title}") + print(f" {'bucket':<14}{'n':>4}{'net P&L':>11}{'win%':>7}{'exp':>9}{'PF':>7}{'be.win%':>9}") + for k, s in sorted(stats.items(), key=lambda kv: -kv[1]["pnl"]): + print(f" {k[:14]:<14}{s['n']:>4}{s['pnl']:>+11.2f}{s['win_rate']:>7.1f}" + f"{s['expectancy']:>+9.2f}{_fmt(s['profit_factor'], '.2f', ' n/a'):>7}" + f"{_fmt(s['breakeven_win_rate'], '.1f', ' n/a'):>9}") + + +def main(): + ap = argparse.ArgumentParser(description="Round-trip analytics over trades.jsonl") + ap.add_argument("--ledger", default=str(LEDGER), help="path to trades.jsonl") + ap.add_argument("--since", help="only count trades closed on/after YYYY-MM-DD") + ap.add_argument("--until", help="only count trades closed on/before YYYY-MM-DD") + ap.add_argument("--json", action="store_true", help="dump the full bundle as JSON") + args = ap.parse_args() + + path = Path(args.ledger) + if not path.exists(): + raise SystemExit(f"ledger not found: {path}") + + data = analyze(path, args.since, args.until) + + if args.json: + print(json.dumps(data, indent=2, default=str)) + return + + o = data["overall"] + span = " ".join(filter(None, [ + f"since {args.since}" if args.since else "", + f"until {args.until}" if args.until else "", + ])) or "full history" + print(f"========== round-trip performance ({span}) ==========") + if o["n"] == 0: + print(" no closed round trips in range") + else: + print(f" closed trips {o['n']} ({o['n_wins']}W / {o['n_losses']}L)") + print(f" net realized {o['pnl']:+.2f} (commissions {o['commission']:.2f})") + print(f" win rate {o['win_rate']:.1f}% breakeven needs " + f"{_fmt(o['breakeven_win_rate'], '.1f', 'n/a')}%") + print(f" expectancy {o['expectancy']:+.2f} per trade") + print(f" profit factor {_fmt(o['profit_factor'], '.2f', 'n/a')}") + print(f" avg win / loss {o['avg_win']:+.2f} / -{o['avg_loss']:.2f}" + f" (payoff {_fmt(o['payoff_ratio'], '.2f', 'n/a')})") + print(f" best / worst {o['best']:+.2f} / {o['worst']:+.2f}") + print(f" max drawdown {data['max_drawdown']:+.2f}") + if o["avg_hold_minutes"] is not None: + print(f" avg hold {o['avg_hold_minutes']:.0f} min") + + _print_table("by strategy", data["by_strategy"]) + _print_table("by symbol", data["by_symbol"]) + if any(k != "(none)" for k in data["by_entry_reason"]): + _print_table("by entry signal", data["by_entry_reason"]) + if any(k != "(none)" for k in data["by_exit_reason"]): + _print_table("by exit reason", data["by_exit_reason"]) + + if data["unmatched_sell_qty"]: + print(f"\n note: {data['unmatched_sell_qty']:g} unit(s) sold with no known " + f"cost basis (lots predate the ledger) - excluded above") + + if data["open_lots"]: + print(f"\n open lots ({len(data['open_lots'])}):") + for l in data["open_lots"]: + print(f" {l['strategy_short']:<10}{l['symbol']:<6} x{l['qty']:<6g} " + f"@ {l['price']:>9.2f} cost {l['cost']:>10.2f}") + + +if __name__ == "__main__": + main() diff --git a/dashboard.py b/dashboard.py new file mode 100644 index 0000000..644d696 --- /dev/null +++ b/dashboard.py @@ -0,0 +1,785 @@ +#!/usr/bin/env python +"""Render the trade ledger as a self-contained interactive HTML dashboard. + + .venv/bin/python dashboard.py # -> dashboard.html + .venv/bin/python dashboard.py -o /tmp/out.html --open + .venv/bin/python dashboard.py --since 2026-08-01 + +Round trips come from analytics.py. The page embeds them as JSON and does its own +filtering/aggregation client-side, so the strategy, symbol and date-range filters +recompute every chart without regenerating the file. No external assets, no CDN: +the output is one portable HTML file. +""" +from __future__ import annotations + +import argparse +import json +import webbrowser +from pathlib import Path + +import analytics + +# Reference thresholds drawn on the return-distribution chart. Read from config +# when it is importable so the annotations follow the live parameters. +try: + from config import config as _cfg + DEFAULT_MIN_PROFIT = _cfg.short_term.min_profit_pct + DEFAULT_STOP_LOSS = _cfg.short_term.stop_loss_pct + DEFAULT_MAX_DAILY_LOSS = _cfg.max_daily_loss +except Exception: + DEFAULT_MIN_PROFIT, DEFAULT_STOP_LOSS, DEFAULT_MAX_DAILY_LOSS = 1.5, 2.5, 150.0 + + +HTML = r"""Bot Trade History + + +

+ + + +""" + + +def build(data: dict, min_profit: float, stop_loss: float, max_daily_loss: float) -> str: + """Inject the analysis bundle into the page template.""" + payload = { + "ledger": data["ledger"], + "generated": data["generated"], + "n_records": data["n_records"], + "unmatched_sell_qty": data["unmatched_sell_qty"], + "overall": data["overall"], + "trips": data["trips"], + "open_lots": data["open_lots"], + "min_profit_pct": min_profit, + "stop_loss_pct": stop_loss, + "max_daily_loss": max_daily_loss, + } + return HTML.replace("__DATA__", json.dumps(payload, default=str)) + + +def main(): + ap = argparse.ArgumentParser(description="Render trades.jsonl as an HTML dashboard") + ap.add_argument("--ledger", default=str(analytics.LEDGER)) + ap.add_argument("-o", "--out", default="dashboard.html") + ap.add_argument("--since", help="only include trades closed on/after YYYY-MM-DD") + ap.add_argument("--until", help="only include trades closed on/before YYYY-MM-DD") + ap.add_argument("--min-profit", type=float, default=DEFAULT_MIN_PROFIT, + help="min-profit annotation on the distribution chart") + ap.add_argument("--stop-loss", type=float, default=DEFAULT_STOP_LOSS, + help="stop-loss annotation on the distribution chart") + ap.add_argument("--max-daily-loss", type=float, default=DEFAULT_MAX_DAILY_LOSS, + help="daily circuit-breaker reference line") + ap.add_argument("--open", action="store_true", help="open the result in a browser") + args = ap.parse_args() + + path = Path(args.ledger) + if not path.exists(): + raise SystemExit(f"ledger not found: {path}") + + data = analytics.analyze(path, args.since, args.until) + out = Path(args.out) + out.write_text(build(data, args.min_profit, args.stop_loss, args.max_daily_loss)) + print(f"wrote {out} ({data['overall']['n']} closed trips, " + f"net {data['overall']['pnl']:+.2f})") + if args.open: + webbrowser.open(out.resolve().as_uri()) + + +if __name__ == "__main__": + main() diff --git a/dashboard.sh b/dashboard.sh new file mode 100755 index 0000000..a448857 --- /dev/null +++ b/dashboard.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# 生成可视化交易复盘看板(单文件 HTML,无外部依赖)并在浏览器打开 +# 用法: ./dashboard.sh [--since YYYY-MM-DD] [--until YYYY-MM-DD] +DIR="$(cd "$(dirname "$0")" && pwd)" +exec "$DIR/.venv/bin/python" "$DIR/dashboard.py" -o "$DIR/dashboard.html" --open "$@" diff --git a/orders.py b/orders.py index ce86fe8..f489963 100644 --- a/orders.py +++ b/orders.py @@ -13,6 +13,23 @@ def create_stock_contract(symbol: str, exchange: str = "SMART", currency: str = return Contract(symbol=symbol, secType="STK", exchange=exchange, currency=currency) +def trade_commission(trade: Optional[Trade]) -> float: + """Total commission reported for a trade's fills, 0.0 if none yet. + + Best effort: IB delivers commissionReport asynchronously, sometimes a moment + after the fill, so a freshly filled trade may still report 0. The ledger + treats a missing commission as unknown rather than as free, so an occasional + miss understates costs rather than corrupting anything. + """ + if trade is None: + return 0.0 + total = 0.0 + for f in getattr(trade, "fills", []) or []: + report = getattr(f, "commissionReport", None) + total += float(getattr(report, "commission", 0) or 0) + return total + + def has_open_order(ib: IB, order_ref: str) -> bool: """True if an unfinished order with this orderRef already exists.""" for trade in ib.openTrades(): diff --git a/state.py b/state.py index e3b2600..af01a4d 100644 --- a/state.py +++ b/state.py @@ -129,12 +129,23 @@ class PositionTracker: # ---------- trade ledger (append-only JSONL, used by daily_report.py) ---------- def _ledger_append(self, type_: str, strategy: str, key: str, qty: float, price: float, - estimated: bool = False): + estimated: bool = False, reason: str = "", commission: float = 0.0): + """Append one fill to the ledger. + + `reason` is the signal that caused it (entry signal on buys, exit reason + on sells) and `commission` the IB commission for the fill when it was + available at record time. Both are what analytics.py attributes P&L by, + so they are worth recording even when only partly populated. + """ rec = { "ts": datetime.now().isoformat(timespec="seconds"), "type": type_, "strategy": strategy, "symbol": key, "qty": qty, "price": price, } + if reason: + rec["reason"] = reason + if commission: + rec["commission"] = round(commission, 4) if estimated: rec["est"] = True try: @@ -198,7 +209,8 @@ class PositionTracker: if k == key and e.get("quantity", 0) > 0 ) - def record_buy(self, strategy: str, key: str, quantity: float, price: float): + def record_buy(self, strategy: str, key: str, quantity: float, price: float, + reason: str = "", commission: float = 0.0): entries = self._data.setdefault(strategy, {}) entry = entries.get(key) if entry: @@ -214,10 +226,12 @@ class PositionTracker: "entry_ts": datetime.now(timezone.utc).isoformat(), } self.save() - self._ledger_append("buy", strategy, key, quantity, price) + self._ledger_append("buy", strategy, key, quantity, price, + reason=reason, commission=commission) logger.info("Tracker: %s owns %s x%g @ %.4f", strategy, key, entries[key]["quantity"], entries[key]["entry_price"]) - def record_sell(self, strategy: str, key: str, quantity: float, price: float = None): + def record_sell(self, strategy: str, key: str, quantity: float, price: float = None, + reason: str = "", commission: float = 0.0): entries = self._data.get(strategy, {}) entry = entries.get(key) if not entry: @@ -227,8 +241,9 @@ class PositionTracker: del entries[key] self.save() if price is not None: - self._add_day_pnl((price - entry["entry_price"]) * quantity) - self._ledger_append("sell", strategy, key, quantity, price) + self._add_day_pnl((price - entry["entry_price"]) * quantity - commission) + self._ledger_append("sell", strategy, key, quantity, price, + reason=reason, commission=commission) self._record_recent_sell(key, price) logger.info("Tracker: %s sold %s x%g, remaining=%s", strategy, key, quantity, entry.get("quantity", 0)) @@ -246,7 +261,8 @@ class PositionTracker: "Tracker: backfilling external sell %s %s x%g @ %.2f%s", strategy, key, qty, price, " (ESTIMATED)" if estimated else "", ) - self._ledger_append("sell_external", strategy, key, qty, price, estimated=estimated) + self._ledger_append("sell_external", strategy, key, qty, price, estimated=estimated, + reason="HardStopOffline") self._add_day_pnl((price - entry_price) * qty) self._record_recent_sell(key, price) diff --git a/strategies/.DS_Store b/strategies/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..47c1619acded7b06bd1634878b599ae7bebc93b2 GIT binary patch literal 6148 zcmeHKOHKnZ47E!aBX!diVwo#cTp(1#33`Ajog%tuHM8l0T{hf-BeCTaT!ZHipAiER zLP7}HQv966PCQSVBqkzW9=8jkIT2N8f-J^}$nfaY#GR)=)-_tXqLy}aP0h$ce{o21 zpCQ|Zdb&mKZ}~U-o5iZ_4!x#5xx4?|^U&4xX5Ve#Q9Z7o+Vj(gi?6)5-|+5t@3J`2 zTtWo{!9Xw&3Q0;uLk9!Fz>op2Cg*bh zU*eY;E%JvU(F+EGfq%vTPwQ2^#7Ftv`r-5Bu1#obG!gMDVnARI9s%gcIdZ0xHlM_X YUv?~pvWkprI4~XpN=T?+;1?Kp2Cm{WJ^%m! literal 0 HcmV?d00001 diff --git a/strategies/__pycache__/__init__.cpython-312.pyc b/strategies/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a83e5b3548e2253a6a11234750eb38d596d1abe GIT binary patch literal 466 zcmYjNJxc>I7*2Y3z3bUp97J3k1cxGcxU1MIB0@zxKj4;d_F}y@xs)V|1s8vXyPLno ze-OAuCpV#+lS#Ct@9^-vU(fR41bvWIUttoC{Z{a$eLxGUdM6b)mOPcXKh}A( z+m0pc4d+WojN?aBpG~dAcZ|ym%xSlMb=^LjYb|$iTAbqt%tg+MS;OOwHsr1W&p_3H zui><{pxhgMg`H3VO;L|CA?&R3ZjHHJQK2xdNNQWVt`@2GLVRjw<&=+5N(e%4V4~H* z&u(<7wM8^wnB-~Ek4_kl4p^3J+F|k#<57(9FcN>y%dvl-#mlkr- zlQ*G1;O=Urb9lU&c{6XAZfns870vF;-5vq>)X6Mre`I>7$SYisghUV$BpuCbQ7yF4 zGTe&n&_Ty=JF15+x`sQE7dEibfgU98F+o46Z$jXGDJKYsZ0=`*rN}d6Hxre&<3Wn$ z@P58@P;&OH>;J0N^tR-9%6JhE&dvuXbFJl=W#fcBF)s3QJZl^U4!4vew>3B#>Ka@H z!Gg3e)fGk{y&|JSUJ7BP(z({=W~<<;Ye?eZlh)9(;#3cf8uNCoQ^Y*nyPLBE3S4$aN8%F_Nl6xEQM4qR4?QhYvMAXWC5MGLLkbiKFf*Vf z!cd~P-d!qnYbwpIs3xx9ZF)@CZo_o98>ZcE=_cJMP0v{jsVrbrJxNd7vzveRFp+vT zkNsnR-yIA9f{>GTd(Mu;J9qBmyRYxw?|Jdvl9FN$&%N&GlR8eew4?zAz+v`jvA*;qb8oxjREttWz;fl9yL$%qeau!Q7cO~1#HvBqs1)F z2kfH`lr;xRrb|alr=6qDY1gQ0x@@$JDJkN?7)dho`0Rm5}H)rA(qPETyazhW*o0%EUq&g`yM)`@DS0dU_`8 z4+VVz6gQk0NLdF%K?Wpc8wrPIPRpVsr_ASMpD3Yp@qkZ$!58#RN;1AI^zo~bJgyB* z3+?HTM=k@cp~TU;jT%H=GKvPtBpN02ghe#HX&SXiMWXpl?t^rNQ7gVJIo~$1=uK|4 zShOOvi#EwIVZdyfMoTcC4$&c%BFza{B}j9zG@Dd9VG>L6T_)~gDNdBqN|s9%1i^#W zCaj|CP4lQ*ssw~8KrMUIl<7saxSQ2PY0)j!NL8$cMXW>(59OeI6N72 z1zNAccRk=%pqvNshIF0-dA0a%%qd%k@1}Iwdc*~m*ML4Z;;%eX#BseCRy*&*nT@vuOBefZ$YL;5idP~lzTE$k(VjHW`Cbf(0Qb!tc2kLn9)3vtIt{1sG za&s}?JCL?BleQB*?PPuILV71lw@KwvDgISV7}@NZ#BStu&6Ruiq#Of2MH&G(lh9oThVkz{4pR7eW=eZk3HCqvS%!I0=j zbRZNSlBAgtNxHg=P9rRjUs85m#%guX%u!GMK^z2Qk;>c!?ADVj(1vmn!A&k%?w&Wj zRg|o2n>RhM)+DPN@##pqYwir+9!|J-s_vaguqC^9Q@-b6bKCFiqgY*XZ~EQYJ#YGT z=OaMl#xpC64_en>#or?bg6BCNxG=~4zVR9_*COG4Ot;*Oh(dJ)xdmaWC=?tE`^T?F z+?%?cPHjMuN3?qXll#d{?gv)KPan}_y{DHf|Ek{iX?Q*NOG4lH+?x2@Csrm5b(*uYrep&Bp^e;1{P6f+8-45c)MkS z`a|k2NWLpg%vs^FSw&14r+wE`Mt?BuHL?98_o1Zx41$!ImCBqT+MaT1%);2GXMMr2 zKRg$y--P0Mx!r)H^dP_nbGZ|)R@K!S8((+rm^XiHcP*S>eDMnx9#&K>eEQ4Hi&AX% zZg9P}YrUd7?&wZBU2I9l(~|-i+7d973FG4qN1^si<(AUZG83(r0)@yfkpWADzY$u@ z%z2+JS%^*m3Hiv8GnjjAiZ{*tL`W8Vg0?8n2$O-(WnX}nBfU;S=ru`}{aBc5{-DzB zHOoX=QdL@ot;>SdP8r81CR0w<)>vl6yvFQ0rK~zsg{B3^JX<9BFg_v;n-?Yz%sv!R zb|JXQ{e6WeQL$UC*qx~8Q7d|uN7gG2%v(RMYo5OzxArf&zgqKtO~Tu+di(LI?GroW z7`0A~n*c?B?1-=x3L<11MbF83Pf>}yCue0$QPbysKf6t)a^a_IWvH=9Xy=aW`I9g0mvuOU3Nwj=&9h5A3$1*|KVic`xQ(xNDPKNGh zhsD}C5jBZ6>Y-Si(*lvOyhpTC?`a$n;Oz|y4hPHCOXz7wx~C4bT!NmK{sK5olyPNR zr5a^X@X2_Wzf(*j$f;kN*9m;e{9HxhgLtZJYiWHQZWKj}a^RM4EotYjf=(9ddz1s4Sg^c@&1@C=QvJ#0uF0=186CyxYya88&6 zE2jtw5fV|jJSX@90U^uabr%;CYe>&oMxPa-10_P&B(YPFC7%Kk@1GdE90-9c7rlL% z(p>^r&A?bF7?=~LLpamA4hzAM5R|S7GimWq5qx24E~k#@^P@cA2K*6pClFE;;fg;j zfC7!NyfKzk2v#^G`sQ*#fg>KDm1PVPV8U52+13j|0U`Bc{Gn-3RWv2#0Mw4*b_L{i z`m#TW?&+Y?g!NizHXsVp^>GP#)Z_HQojI4c07FBlfl2QlzMxO~98Ul66%b}Nf8!yI zj~@f6o~4D-IxS0GZ1r*=UY6!UL6OmZy^_$83(7w|BZC%9qv99{45R}csJBz_VbZ%U z&t@l35d4b3RxiEWf`3BrV;&jjLz#yUXPMz?f&h6Y%u1)Lcg)Dr zYv>3N4{}YH26H0dpXr(igl2#k<1#IM77x7{U@1db`QbrqkE0QbHh&Rg_wI>Ek3igv zZ!*M?I2b|)WF5eq(4~`GnpLczHmkzVOjL{UKa8P~4*~EGMFf%3^o*o?j7FZCh**Kc zVH!C;_e?~}ThpV>jxS;pI)a1%2Za>x^%{;w>Z!@D>_+Ga_&+`ChwcWfnm5v~+=7?^5e$+@e(8Jkk>Ed_uUDH}_r zX-K)SQ^AO9n|y307)X_F-r(S9LSbKEOy6Kis)Pz;KOMPSE9EY<*}S$a6C|IYK{1Zi zh_R-Mpy(pxhgglJN)=Im&<%MM#T1hF*nvlZ#?h9Kjr*ViBR!bjhe*?A-f2sLK7#cl zFx5A?jatrG@pj-=AW_<;mbS%|_0pa5#-zP;{@I7sb$8}&&;91>v5_VByBE~z?&a-j zRnPpfq_gVFRSC~N)w6HSxi48=A8U?P#wM0tULIL`VT0rQ_CMTzD6xM;-9Hk4`ds|n zD;q{$80C>ubCJTbi~J9q?uAc(es1yQue_FMe?e`3VXfhXWOeQ0{@c;mr$2l;vGb_9 z^XUBwb>}Iy{ghh$!h$JTQL{Mog+s|YAvPX6ztp`_xuUGtS9>3}btc;S)wcfC=6LJT z_|aFAZQiB!cLU3Nz{&fA@d@?C2AuJ!Ug^F>K}`4?YHRClV?o$;#9HG3y|5U+n`<=E=b%HjK!fAw18 zw5XmIM9Zgj4R;za}SFBg>n|CDb)r&h-d+UdmM8}}o zF}P+QBoO0^=VM*Vj+NKd-6!w&#CM&+q`t^EJ#1=CH1(-XeJefj#^*5U^#e5B?m-Gm z1~KW?noxq}&pgCz5<~lF9sz`sjAU3yD9K@KGfZj*QbrvbCNyP_$bV?~APtc*5*bEw zssIz2l^`KM9y<~gyFwFPz<=Q~gh|%nFBy zmg__a0wf)q(ELI#J*Rk!Wa@XSC`~7260%ZeR#7I!SjsFieYKH}UZw?TL#i%2QtP1g zb7_r5p6s|e_cH1!I}zOEfN8FpgtJL?HhsnPUh%udzvK9DZpHUt*K?}y+_zf4?fuBR z?mQK@o?@#rOzH+=UQ%X8@iz%5)JL{jRhU%>D8}o&vXB1-tCnFZGa}_LFEoCb5jko7 zZa}y}-OMT?Dg8uwqI*%JY!D5>nqMA1SXARXh8yN^1|Oy}N=qF!#1CrKmHpOZP^a=y zbJXxb8XI{i)x73mEO!wWkeHH)O=FvmoF0;2K($CytI{X55~q}=vn2WAyQ5X<#M_Ir znW2a=7`nz#LOzOWsY<5Z)GO)S3Dbt9EP>D@tP?4dKL}u?-SJv-4)`lbko^S2B3+;H z%X7d*Nf}qD8SOCF@T(M>Wcex#7+S|7b(>G|O_>v@t4t#xZBW%(*{8nTd+*@ggI_xQ z;fWRT&#rvqO5*S(_3)(!ov*5Oug+VN&E9!a!djlO!9tMGAS^R3sHhQ50I{o{$w zlWOP5_|E5Xka*9`n;u$A-?rbf$KCsu!(X5K+T3dMs&aoQe)g5`Subd(N3OEMgf1|A zq`mTF&63FLdz-D0VK`!?`RoeKhP}Kjqwf2L?2IYYf==Hmv{~6S{Sl$S)NCpd00f2P zILwdLp^rRP`#o#t)|b00Bnq zqW%dJ{y?BpU?NH8i~=hQmK0p8#yALskfbJi`S|HmM+6)<8UC^$YpOBF;7hf}f=pGz zPR6cDuwt2c?v9jDKe7otLrR$P*aFCt7?w;rsZG;gqfI~6)V29(^lUA#23w1T2;g=E zU^Oh9tt7J)O)I8E?*=ivL(@rrk3Ns!pQ(<4lI$iw$NzGaD{5e( zbtx7aE#^N_{t!3ElXi3X=s74Hx1oT1gaX1KnQ$&wy~UrX6QR$5Q+K=()?sDYPBl2Pf#Wjs~$*dxcWfuJ?tinam{Q1IRnVp(j zl-#9I+U8WqKjJoJeikjJ72Xe>RSV%e*Kc2sH7&QUJA32S-mS=zIf~j)@kz)6as!fq z(D05at(5PJnEFP}osoM{Z7crUwB?eeiAdGvm15@&(%+zdk|G5w1SZmZ+q+P=*w1*h zmbkM$Zf)P%*9>CQIsT-+7CLKkv@<|#k(=eO^D=w^qK2sqaR~VWeoOx|7#MPiG00mUiQBN^t4_!>2M_+4XUFd;n=1+ zw#Ay)9c}YG6yuAg`5SA_U9lJ6d*$6%erGh+7`N`)iklgm1tEVfZf30!z*1WDyeAh+ zb>353>N3JFD3bjFh_`GsM`2VjT?WCGqkvKvXUiGRZmO0qU~Pf>Yk;QIA-KssbXI)v z*y7N=<9Cn8+YYSkTX*-bI|t&{0XF26o#{%(n3f@B%h*FVF?|{O^b}+IvMf58XbNxD zf)m3u%({+NH0mT7OdU5x)1CL|*aJI*Q87fZUzo`U%pbIZcBZig&XLCY34I`DHSb|s zWdB$R>!V)gg8;e?^CVg~RdXujl6E`F&e$pdiuc!4Un$1I(nsd~N zNB!4PkP+-d0$U&`Y1n=zUC0<|lAvk427!VRYUqVRvbQ@!rcVdQC6+x>g|JX#i8I;_&2*R^9a%Q>H{X^5zX2f1T?CN4 zO1ZMCg{!d_bu-Yjs`uH|X4QK{Z9Sq^9lg)5yH0|JIx7;+Hr3hoz}fLN8>r>{sbpPC z>~*#7spYF`ZU5?^T6=8XnY7m~p1b$T-B%I~J!(VGn!Se=eF%x%6Z0*#e6{2Kj>L{3 zb;r=EqV71Uww+Wx&)z2fw|^`WzTp?C58y0bZMZPvEFi7;c6oi;-juAuk-OtN5% z8b1e#C~9PCc^A2u6y{|}UIRD7dkvB2&qDP&eHjQtd?$5Ff<&z$t|SE+N9xnr(+&vV z0j3BRNUKLG=b3;M#)g4+fVX=XocwlxW;AA#RrcQ^6_U56_*UrTrlrOLMfK!)`^o@jl zmjjY88-!L_68vHskywwJ7r^uh5z-G%M)(x@C&_LBgTsjdZ#Vpb%$i%D1x9ETgEtI$ z`5|KPi;x+=jIQUptQ4nh(9m8-sy8!gthq5{yhn995inWARr&Vow_Z;;x2eu;YtAFF z^Y2}J_hO>8S8eUZ=YJkpnf;R!aqE$-oC!#u=E^ew!mPRSV9Ij&Y80SHC!h*;nV-Ay zBvSnejN1_RYAZ;rA!mvWg^a?Y0py%c4?I{D0~|h$@ZvBk{9D^_0`O5I?e@3YO|-ZL zi0V{^bTSYu(0f0`Ef`+Ke!B#OxnTmi=cWo6UJIF5QL=z3HA|XdCg!Hx*%tL!L0Bf8 zmm_2C&2v${U@2~jHcZ}w7SpSvzi+^_`MIZ})~NypP<`BrF>1Y06l_8tiIdBFqE;Oi z^6T?5@%b7Lwh8s4MGO4r7NSL;)Y1Ykf3`4`siv)vpU|Hoy+8UJ8@Xso*jj&zPUpPm zhV2PuTk;CxGmT%S8^uwZXujHk<8CopELs@7`OTapxwEW!v1;zhLh z3BA~nt*ysCNbeeUTouB{F1=*-{V&n3XbqFw345ZMIsed>)j*;Z@*K;1vN*_d!4}NY z_MBPTn^zd0V(}AuYx}^Ym1koww13DuChvhuMUD;*?GI&E&#bSW(uX$p=T*cfbiU@O zML%DD6(aQOIBWi$;g0cNo6Xz}J8B(xQZ3O=TP$kV`gg_m08$MJB2T-rW_YXLp z&PwDJ;0L+%2fM(Hh2*(iu-9LKudAf-{fq!A8t28>A&^U5cu{hsm$S1$=Dt9@9O)ck z86ja{4wRO>I)(AM@qm=;4M7qSDJB7rJ+R}$5{c#*J4m)s^ohl_W@XUsw4VeS+b2nh zP^?uIAdwI_QSylbI7QHQg1gHf)N}}5;o21%W2Qd*fYB^#v1=ya11rExJgkp-Fv-T5 zY2~os6GJm$nEizgse7_pSAW7*6h`lvOR4u60m8n58HER8hz5{>sKeRx#^{)8AnmTG zc@;o;tH4X-HS}_JCR4vl&^pdUtq>csFF1D%c75heqK(AITuLxInQFsuc_TLFtTZ8? z0UrMk9hXycS#{q?Z=-wzfSF5n z%EGuPMbncI_nooCjT7Oa&4GrZWl_k}2%Hex$lBVViiGC^a2EdRsZvb-WlCM#rqH8`mi2kRHGohK3 zJHu-;309L2Q}!{oC`^z{RgN*l(4P98|$f7I@%nNjl1tWfidTlvUoTzFmE%@pj{XFWWZX|FGP%xb0s1-S$}D zlDuC2)LMD(^5yx#l(Rh9*tB3-P`+T_DB{YuZxnNl+wXnq?xzwBd(?(KiH5zmUYH+T zs3EWSQ@2kg+%2lRC3fz;3-4Z7Hm)ELidq);zt-BS@|AEWWZ<(Yn;Oyyy3Jd|X}su%_{z{jNP(|6H*41LQ@A>;Lt=FDQxX;Di z=RS7TU|Qo&A?fX2Flo)iU{PoTcviJXt%U>aO2_Jv_1cpO_sO{XBD_8ED*PDhDo?%RBRc)fGO|638$kOp;Y2~E)^s)O@>s4nGt}|<{Ge3Hj=Nfx9 zh7gYMKS{dJ{ba+zRX)%E1jDR7jTG0JpDB~T?Kk>MPc`u$l^yTI>pO?|QVu?sdN{eFvl6g=I ztdw4BgaZpu%~$|qq`$Cwl4y8_R?5uX0cW((p{eu8P?)A!aC9I=8(qw+%IX-#nBH-W z=^f=SB7c)%Ix`z7Gb@oYk!DUIb+dM8vExeqBYa1;ZI-EZZAQ)s`@c|389<<2NO4sz zOslRIDAy`IiOSt-C45?zudJHaD~~3eN9Ro$of}Z+$a0Tn)q#4(b&UTRJ2&5`@8y4+ zf7*1frkCHeT*Upd<*LGgs4bW6Np~7D-X|Hp)0pO3$xYi4H5F28z=@a_c}Tj(LTYi? zz|sf-`PCxV*to2}OCi}B?#spxGwNtkm*4Dr0%UUrGUP_N{TD*UrNb=bLNYwG0(xJm zU5pDOE+8#IIw)d%x|!^sqE)+WNK$g4`B6OKj59S#RA4>w9$kjkTOWbUz|j3qo4GJn zO?z56*~oJjz#CuVt{X3K*Z90#GxQi(hYQ?WU;2e5X7mzlL)junj_0Z|!2@s7Q-VEW z8u?B8>l!#cVlv5p7#^iy=(@qMv8^!%F2{KO+#ozl4B<$sSW6_TA93|hOJGiXyN0D} zV;4g5RpwMAleW`aoGKcFL3B)0u_xr77mra;sQ~(DJcr3>!+kjBq82;8Ufq=_>zW^cN#9XA-?vzPujaREk`>k9 zLBNPq)X?Rm#nJVO4qU{rS8N!JC5;a~b@%G;*2k_a?|a}muwbF8<@1N(y>iEM+p~CZ zy{uz?;A3YQ3YTqv*wA$E(%nl-+v9r%A2bXl8cwSXr{ibNuQ$B3a8$2}=3M1>EVnHS z@?zWVNbDuGdgszh@#?**s|Ux9xBIJgzgPFx?85mw7j9qp&5P^KZE@@2l@f_ z>`?r}%%KPxgv9)(FkR9&Dmi$44)-rU$(l^ZBVbdDdCwNAxLHwP&K{I#p@15)pc<17 z+43CQ9#j!z{nL9anwU)yeF|p|wnC~wk!W%uwG$6(p?Q-!sWNX^_{`efGlV3q{ zf1~Jzl~ib=W_0QQ1wFA~o{DrXfGKeb-0>)+Fnr<&ilSDr=tFDHD2&>@ooErw6;yS_ z=vytAA6h$`vOTAV1(cglR6g<9UNamZIWO;FI8clkO)IJL=L^13P=ze;Z`r{8#PCcy$qLmvWsa#c=!sgl$;pQEVCJKzHy=olK70EDNA;y zrE!U=U_b{H(q7=sU6?c~AY%&DC&4goqFlw5XOh|d;T+R5=#}1Mrb?K6-Tep0{x%Clx#D=s1G6#yWo;iIMG_i7hRH=1W7jlQhkP6S%Lun4z_> zTIUIY?9M53^;$e~@6_E>Umhm0zq(HM7H6Dw_0dGx(Rm9A{;l_R-rX6CEbmz_-?vuY zw^BA=^fCO|mBpuH#~^^OS9dOj*Q)mdNZQ{s>F8Z)`7`e~ysIPk+v6h_9vcm|Vo=(i z-o)M^b??yX$ok$BiLMi>b;rDYVKnAR+N&1JKL6}PNBP^&-+De?Gq5^%zwUwKJluZX zK6UFo`5o&8|%OIjamzUXex;tr?~mh7r~@4PK(cQ5jvKaBo-?5rV} z!?%W$uG0|v@7QkJ7WYCpE-8i7SW=NJEl-p-s-=yKO6>TOf2C@@^tpufIo0~y#!jyM z82{6aQm*?XM9g~5)(=sFEV^&>n~zlU9~JROT&9n#{X6jS=TGrRDolUAcc2;{e^JaI zsWknC-9qsSp2A8B84AlOKp4+8($P{gTHB2?P7^?KyjgM-?M!+EeaL^Z)C7*t8OS)@ zN%YElaFV7OX8C^?E-_`xtAbCR0UzStwu~DlxpBj*GUxzkX(75>$QeJXnRZ_OzSttdI0d+&e|L4R3`_M2#)s3`H$OM;R{wYMB2J zUB(<2Fy}qN5LXa}zFvSNoDE;`GIPcR7z2=em|?UYvd-$=L|Y#D*D*->CIxiSL$f**Q@_DfphGel z9ptZ2@COu-vK=aUHXkQMjX4NX2AK^QBamZ{Xrwe!#%s3+i@4vxeJaF=g1e(_S`;<$Qo zeEp&nADvipO(s2E8)mMgY6I%-(#J%ji6cn5LK*5Evr*J>+mUbys!NEu-}AidSsGk- z?PhxYTx~ZPoyRsaXuSh)9=be>ZLyA}lk2Yi^X7;4vbPW1I*_o}ui5J#!q5PXeWJcg zt?!yY^41GUPyM|4tW^<;bNO6(eRb{jh6b1-Jw7 zQ)$`=Y1&Z8iC`+j59qKmjx2DNELfu^TT+juU0=+iWdc9x^Mrc;vMm)+OOTS^X4J## zyMm=pOUXnd*fFY|>vWjn_kVJP-^{Nf=oVevrGITw^ELPC7XP$mUlW-i)s0Ii1(6uG zY&5_;LNmguRw#h$;kdFy+R}_91W#kMFA0$DGqOTH?lujRwX86zbMGmX2T3VsA3faZh%t|YLYl5C#=$mW7N zM!$DJ!W*buXqLPXiNJchg;SCb^$23du0k`H^*YGY(l1A8+ zvTKcLWQoy1+60>1%cNRnZ)aqY%+K=I5aeF#(7YvSi)19zBt9aZ&E!XGh0&kWM1N8! zqbYw~TLjXj9-Q8p+eh1A{CzumEBe-FNc=l?>sZ3kq&k{n=J)LH+8;PNA9^~LDq%hL z^x#K&;uSp)T~&8VZMlG^*yTJNjz(O2VRV!T?6yF|2|a0)OMC!Fo7vwh9k4eO^lvAbX0 z-JjS!tnMCO+kHNM_I&*1i*e`0xb@}u&R+TfM2Q`!w-eb2I%MR9AotBQ3378?_@gl|3WbmWpN$VKOSagK6#ZANo&CrB{E@2 z-Zf~MIDHln4l1`HoW99{%$j#CKDEK&wRDobR+}Et7km{g?jIR=bIBtkVpwr=3D}e^ zX+O3gg9RIi35(5-IJ`D2W^-9=?*@n0(ky$eR6L@ujZ&j|fR7D=>A-vG$RqatQE`j8 zCN{po;k9CBuT_b?;xAo{U;35!Whs71QeT>QL?t%P@SLT5!(cGKz%Rb?V~*a`%XHF@ zEGF|PA8X#==)I(9@0H%wd?98y2Ja48Jr;JvqZ~6c?G@v5^f9 zucdR3=(SPBn-9cHRQLc5vSSh1c*RC2sbj-pFz==T;k7jMh+YJKkdNVm-W!%ua|;a) zFB%+P_gmvQU1r)=Xmpr{Kk;MWInKj%6P53!d~}9_KLrJiNGJ&aCpqmE*m#EyjHHWSNzO5 z?S0`>ai64KkRDOy#?wvaem0ZzUb(Wt(ffY*5qob8@QvoPJWU$Cmyc`j`xq*Hv++@6 z~7bzR8gQwW-adyyXM>^YK9aNqqdCh0YdQdxn4$D7B8ct{E z3lV_Ti&lFe4iUqxuL(|w*A0X iJ71l!)vC7I#ol#W%T3E;%Zq0I62GwbA2}9bP5xg25Ws){ literal 0 HcmV?d00001 diff --git a/strategies/__pycache__/base.cpython-314.pyc b/strategies/__pycache__/base.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95d6b8f429299a358efa66960dd4ecdbfeaa9084 GIT binary patch literal 27093 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%0Dl*(i(w5YuX?n({cos8eC6-4VKa8i0 z>7T$@n!#cl+Yl*BjvL6GiMld`?k8qJno%}e7VfxiTGhl&tGZv~xL3zAfsRbafSpVT zEUQi&hVa^{d)}CICIF%1i({f-nT}M_fy^hQodJd*v>Kr^fDfgfoO z>;1anIB>`W7Z~8d_BGuM;VRZlN&-(1=&{rHwSZh`4mQkadvAz6LGqP zgM%FmSTwS$N|210;A#D`EMyiF4S`mFx-(Frf`&8+Tqy0v-l>?^=5fwNvegE|bv z$bv{2NVLf)k5CE|oyrM&&U*v6Si`HmPtpu@9XTSY zQOLpBRs^6!R5A`?`MBc(OOhDNLQA0@p&JAocMaf%IY;n}oryZffP;W=Acd7n$O@|z z6M5OhchSu%ZX9q-z%?b&4fKqqa?9+ot=r##IQla(`-T zyxji%;_ntmx3-73w%-{DZ|(V1&E=ipFK@lN^UBU>L3OyGI+S0t(7xb$d-BF)w5}~& z*A}YXeS7Dftsm^XyEED`5bhWV9l(_*FNaPJM^BE1PmV@TdO|OcMJ(eV*%}{exttQc9Ji3ebj1|42w{t`V13(m!?xHFv20<&zGNX7klm+QT3{R#QdzOs z=Ia*fBbJ?W+7)BorCk?yMUCb73mMB-AcKSuFIwIhE^nOcdh6)2t^BRrWvgvYiwl0s zD(AGg{`aBEkmcY>#(D6{!Kk$^Y^_^3AF=L)9J6+#e0FJmG;CTQLvG8KL-7(XZu}?0 zTEF_ljynDF(=D9EMvNf`PhkP;LbkoPgMTvf?##XFsOiNIO)vfn@rxY%zXrO{?{Bmh z_0&M>RerEmb9YyJ86NLhJJj&|vyyh>!HwJp)q1>qu+i94rT$=hRZqG4UXczy_sX^K zxmQI&Q(zWx9vIBp5681PICLXL>|foiPTu#%&%{9^y8~&7F0u$-v9n3f##qNdm+Ybq zhOw;LVPGt>Nn4qoH$C6&dUyNXnovvkQqw{Cn+&hwJYQr+lkyh{ULAn%XXoZ5w4MBk zQ@widXrAl1-1s*>8@NKM8F)7e#|3w0^=Fvif%ZHP1yi+t_aIe~H|Cp?55G8{fdzb~-%a5wjfFlg;W%1))3b zXrGQ1erlT3!wFZ|n}j1C7s69Yak(qCOhVBLtP*h9NYgI3>^ldfVu=p+4_e>oSn=CWyx7!*vpVOhg- zSZu+16+2VETu>DFLL=@_u{&gOqYP2A5H<@@^MB}N4NBaxAaA~91r2oaXd71GGsm(GMs#5=B(R)6`)Pj8?Bv{{2#Tz zy1SY0tkB%ul11*ey|DkZbsg;YimS-pMeg@Ybg=CP`Cv~y;>%YZFo;E3+wv{`tak{2 zf%(PydOf*oMB0Af?*9?oBRM@mKOle~VZczFi2a;^d1M5Pu0RkcW<9oHkG9}K(6)KrLHm|)qB&~*Y{Xa{-%s8n9kZ%~lpWpd&-VV;!zTXP2Qr&ZWz(<*Th9XX8@gPv~ahhmgJN@*k_{#xhDh{~>ce z*3q3(pFqpxW&W`V_u)`tjgNJ7g9|0m&p*z_B^~Uhj>ko~w1K|wqHOEgNu|d+oRepV zl;OM&?hpNO%(w!(tH~)!YKd*g}f842p#tTz)d2E8> z0aG`4yn_TrvbgB+0iM`5GB@Y(KEfaW#83!B9ISoqm6D$BP$FQiM_WTV@${&ursijq zMH^LFKKli787PP@I76E%c0-q>^JDgR1w;)^AX(nixTu4)_vGIoAvTe`w;&9FYoA?W zK>lG8GfPOGl|{Cx$)m8;-H^tDY&`6@OQbAFgO9K~p4jk^=Qu_kq?0b!gCqu$VUIj) z_K9sM3I4diF<8KzJpWf*?q6}n2VC_7Zf}I!``29A&$xmITuX#&`8%%cuQ}TTt~1Pa n{xw(iah4^TRr+C8>3nM>tLB34Q{9VNewe@9{7)Qnunhk%#fS_b literal 0 HcmV?d00001 diff --git a/strategies/__pycache__/forex.cpython-312.pyc b/strategies/__pycache__/forex.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62468ef5423f835b23bed0da4c08b88efb6dcdf7 GIT binary patch literal 8896 zcmb6;Yfv0VdNVsS`+l*oJS}M@z-A=@5+@{~HxD5sA%qSjOPjMd%g%sywd~@a#XP*+ zIkJ*0WnBqUbpe&@$~cv}Ls3%3zpPxUQs}PE_Emm_G0F`$k?WkgR3-X{jGfp@D#`cF z?Cc`6#Jbtq?ytMQ{<^38d(S^O999aRU)80eav??i4jcNzWq`H60$_&XDV~l|0j;J3 z251{%%pe5pWDT15UMV zjJXEg0k;a9Vg-YqfQP0Glt4va)Hjl=g`&H()(UtH)G3NL-=KJl;JM3WdW*+szx5F| zy8H&k+?t3dNhq9D45wTCtYU2mk#nJVXh0x}J0b=LL*$x}3<5&rbwMc}3K3BV_Jv4L zjEoCGQ3xmEyr{U6iC{P}I203-0w3i2{j_3)k^aa4EKwwk3gO|Tpwmha9*M)MynwR? zBcd2d#9@xBJ0V7r0QbU@6Wt=mFfMFfbO@UO_7@na0{r0QgfleSr$lJO!st z2N>QUuskDhJS!OcjcKZ%=D8d8fQdKWpaN#zBv^Q}V2u{9C#b>wEN{8N2JE~QFde*2 za3VJJ+M(CQd-NXOAvpVuyz>SZaPx)PUYAgyj+l8jjClA0pi-EoM(IFN59KdXTr{G*A=0PV z@LC5mcW;7i;1z_~bw`Bkj%>7c23YJz^bD2E4oW46%qEm(xqca;#CA0IR#w>Cc zNKwJ;c#4XevjbTmstRAWsRA%7OKX!^&ZlQ!3ua~MZ!*i1cUE=>$r62hlli6kc&^rk z5mtBTy%F3?c86os9lFc!QaEvV2;{;X3`XLSWH3mO!4<9_`9)#E{R4^!PgPzAn!e5ME?}r0C}qRup3W#0}`A z01G@N#S#pLV#&FQV-3JNt0wQo%PKA!Td`P%nc6< z%Vv&DA4#(}%_|k#-aUBxpj^=)RWzj82jIgJ%hQ)-uV3=|VUqP>#omRg4{JWC zksDg2hSueZHf?6zo;3SI=Nha^g)=%8VG1wiE%>h;17SEz(V%;Bf)GUk+3@f*Fie)w zbA$OsBA*z*{45*h=0mSN*V3U)*2*%Rjz>{Ls7Hj+W)HbtuK^J9rB$?i+9) ze{Psz{)FS;rc!LQT*u4RV#&Ju+mh?oA)ZUocj0aDp{bjHl)~L?)!OUJ6nzAmFy>p| ztHIIAXZut-`S_wL4{rV&dhuDzcPQTYV*_u}&+ge%SogO1xYZ0woQl>wOF6ph**?JB zou@Z@UO7`RMc>@YQe)Hs>gGrAKlwMz2<^9YjbF%Gz^v^PlOY10#3A2MC_==e)#7f| zj2*4!cdLd=1ynVrb?HX$Xm#D*{_1Llu2b07=AI64XiT8fddG$Y#VC#q_9bG9IXp~= z5D$+jW??jZ71We4UaM`P8fpp+Ft9N{BZn8MsCx%I+Pk?Ww|Czx5G(JMkze(SO%8xbv26(zap`uGrnTIwm`Abxn57w#hH-mR{IB&&zek zq`G5^?8nyo)=%30vEx@Aa%Z2^+4m*KSgfmN3iNl!bjM8BkGj%rE9G0?-FbWGT+ci8 zlV{UyQ`HYkw#^$Cl8eI9rGR|tRq4{J@}&Xk(!hg~tLcsvPw~u4(=W-MddXA2U|#k# z$@Zotd(%o`#muokJND3BJY%1>FKy}kgn!`fS#g)HRPIW*sk_@J*?llw_Uw>6J7mvp z$+H{AXRF?=xm_cd?~=-QEwCS&KQPOC+NC}1OZBG~$x`i^PtL3{hC=6mQU-@}6^k#8 zl%w$0*~zm@r3dd-!8_XT+vOA8(uwY+3m4@J0qH_u>3e^;d?NH~d*9;{%3HY#6Apd7 zYK6H^M3naHt?u?M^e;SgdztYUg)IhX{L)LeR~Ua;+{{AbqY4^Iatv?ANQC4tiH9?~ z3%V!@b^crMUu%Ku(5x58Fpg|^@>w!fGvw1)8B4OB1W)U-KVe82-n_8MD!L!PZtCx| z3Q*0rpFGwQ^r8XuBE6YjoG>J_E*jV=Dw@w?W=^sPwOF~T!I&;m2YV;AQ25Q7i`0Z2 zDCerkkpl=F!~#7~;)BA^sB)%V6NniRMHF*zqcoW^1wkYsk{T~jwM#BwsLKrO0c>#+ z3RR+3T!pf0o8;OiyDBADz@?}?Nnq9HE(nnUxw#a3Esmw2z zeMc($&hxTxIkIq(Gu;OK>)tk-oa>dV8>Q;TW!L^C^Zr#6j6D(2F}&VFwQQq*ZlGJX zus>%i0Dj7KLZ?$@zJCSa_hr7jFbAJD>J=YFsqO_mVX&aVMUAxK!AL|m+F29G=#Ie6 z9T`((s-%)x=B=X0c5qm-aYEF#fhaV=nWNW7I9I^Qm|(uo+&rt*BlHM0%3PsFXq7z- zUE_s01EDJCLq4zyMpZ>CTqG$Bii#sRL=s^E!#32rmXo7lVF)8(QV&Zi7O+%W+s|rz za~Qfc=Ai`<1pqiF!#YM#y1-E5#Tz+C>qkBm2cXaxXX}o4_mQn*^OY zRkLbf9L@Afak*SvBNf-o7tN0@7ax?}2POBx#~ih-@pJdVZypxD@C8K!9;i*(?UUTT zrS1FgvG+o<`-J2^@n5S94*s`z6xO=Fy?GD)m)>T^@V9g`ZTuMv(BCuAcb}#Kyw6yg zYq|UOlI9xjekF(fH5~TTA`Zcq4WoGIPnL~p;MKEiwBAS0U;+zddb2E$@de?UvlQVv zGg$ex2~Up$y=x%*gdU@kxVrkNzHF9R^j`3*Gg?9f>=cuaiF8(;U{a8)Kqoeu)jc^n zo9Lj-Uh{e1I$fa3sJ`g`Pd$(3)KB7DsIwaNXr7Rn=kR^NV`uV?8r4xQ1^F3-pk}eY z)&_HoDK66snVW2{DP&Bbny8XDJUP#P z!kV&@pQNn3N!_6hcBbA;%Ep^vl{|4n6g{^M4{sR!ikHmCs_MT7x~>aF4uQ$KJ@@H2B#m3pw^JD?8McpSlN~;&ZL<)Uore z4?8fErf=DS>sxkEo&N-M3Cq9k0f7z3po2@8)<-X8H#jMufF|L)1lzMkIm-L;+PdwP65Cp$Y) zo%>Xh5PmfF`_!o_sF=Z3k0&EZh&6f={mCAPQ^t2?XuwRISfl6X)VW9Wyqpi4e#O#? zNx?RVCC2w_^I9%md3KtbI)tbt)R%Z&c$$R2khJ1UGch5qMH*=BR3AiP!-=6W#Y7UZ z7^G;_U_@aDg-{$Kl~^LI#s;Jb(GOvvIy4Okp;02}fhZj0fMYR*4fYL>`RyBRlY#G7 zSV$8=a4|%L*A=UJ)ZD~WgY0~kWXrH?ZZjDTiOp(bqz+HQ);Vql>PY36c^QYhYf#ltv zwnE@DJ9fK%zHOoG&UvZ)aN4y}xaHHr#<|4Oj>fckrL1!5)la<#=3Qy)=k6j5Ycv}@ zSM<}e_sZtmmfd^P+`rh|D@A3qwNg<{`Xu;}Q>P{;=D7KS`QG<0-?=Q;wo0|Fi(#qu zwB$c6`ObXmI)fpSZQ8a{)V5Lt(a-G2?b>-S1ZD*u2+T?j3=7NO#!Tk4(n_{up2<`?2XTz@HfC4mbM=V?x+YW9d{&HTbcJQw zGP`fiD{pU*wl~Pzo22bc_ZsEHozme>`S2C#@RgO4Ef01cy4Se8vw4-H993UY45T+0 zs<Z^J zayezpCpr1#A|nIGfU}V+Hawd+?i;YM2{Q29Yo^#-@_QW@2QyquP{tG%-4BhCX_c@1 zrj5)y2zVrp1$yd=EyJv6v;lboYvT2&bjWK65hz5wp^UZF*z*QZ;la&fQh?)_HYtdi_ibG<*k^4^sNc0rK$pOW^UlJ|E>`@5E2er3sC^-uO! z*D>HlodN4wJoEzH;$}Z+_5t`*E&>&Rs*_Q%X*zj~!t*5((p0cbgo(T=K!SqUi{RDDc4|7u?B;K34S<+ zuss;W1aD@<6b$l-FnkR}G6Wf#;C9u|WdxlXH9M>rhIqx&7D|TN2~bo$K5_;|6$WzZ zs((aKt-$BRSR#}pc!vb{3W*2pJ6;&BFBpVZu7qCol}~8bjT)X{fq(H`D89ZyJvK0$ zdDUv-_Rcn}Qc%sGQLDvmUt#MDkDCkAt5rtsz}(eU3aW*M5089sWDUVD+#J_TuR3eE zJ@d>e1=Ygn8di^cX%pw16_Jv29_B%{$gE-Y*u!!`dJfRBo*&if#YU}O`tHRwb>Khj zH0L1I`i>#r^xUizOVeqpe{Hdx>n#s>2Yeqd~4jF62SC_#~>Ya=2g?Y-9lv)L&n zoz~uT7ns={%yiO_>0`}IcjEjC)9w%0-89M0bZTM9b#BQdo&B+s{=$%1mbTOO`_9#s zWmztyXGY&S=R41P&i6jr=CE4`q|X}PIwuzq@>{HEiAxur{yh{flQYE6^btwT8A%6q zU7x;}mDpZR;;Im@?=$ooC1bBiGWD7zbFW3RP(0gb?X^l)TITv}y>`jo>yR9@Zs>FN zx+E7Z8~X}+3#Gy;@*44N*0!;nZzQi3w=!y7a=Xb!;y0BNzxlPoR(%><;%BOf*YXWk z2faGQ)Rl-!LMSXLy1q^?t5`Zi!qHGXG|mf(D=G%hhlDqHDF_{+Z}Lh>G9-w6a5N+Y z#poqIDDvS%JR&MiDG>}O&L?BM#7BaWF)yPSpk*vN4oejC7y0mn#A|aUga}R!t44Sn zbv`PJ(L@}^IDH8*DnWS|cJu}>0Ls=G68RzMEAiu#Vf76D|1qhV%ScMvLXp#i%6i@th3_5$K#q zdSW;%BzYRJ7Q|XG8J6lj3BfZK5~a-4(fGJ$l#eAYctiw8I~pxvB$YI|5CfF)NT1dN z*awFhAZK*#e@aLMu$AOEa~6(si1{NRYJhN%0Ci1z_$>At9Sl))(NV>UlyN>yK1TQ! zdW(92g$@F;i(mr)G+`J^22|Dr@8x%oCv)2b$B=36zOcu^G~3 z0Z&NSfX!vAX;A$tN`Fb{0`&LDWS=&sOl32Lvs)gRS{@aZ-`ID3-xT{7rp0wt@3r4- zm)EtV*0oHr4@_lwFz)gjuU>yuc6(E9FATCgTDN_!=A*g~>*SWMR7=;wx^8u3WAhaI zV~0=yi&D0RoF+Wqw~?BAfu=W+Dw0jlKEUNBKir%zL$S-P`Dr^JJ;V46JuQz1Eb@L% zT4SvYqe^A*c(zt zPV(z_rB5sg62e!K(+pK#`D$E%)mA@D{Omcni;K)<{QzfyTMDo~yvm%kC0!qf*($T- zl5c%ZN2vO_0Mm-{6jBvA@@F8m4*HO6b5elW3>et8C#M0bzUtLDm?a-|)U58g#`)oL zImt%+h8CUQ*qWYs^?5AI;Jh?z8gcpRawdhUZ`*1d>CDet3p&8Tcq3JGBT?C~T+Q9;~aE7nsDyuUV5PjxOWP_d+< zG=06lwsHGdZLPvIDr{HBP%l_vMvzg%lSy7Nh?D0>6EVdUo)82+9-dT8{KfFuP<)&p zywsrXp%(0cu}~Ck1u5bA-b5%C9h>xM!VT7bBbrR|k^j(nx)syWzHZ;KzQN&38r5G(Zq!~AA!AlOBF*bF^*;+8;y^FH3284aP-pzRILKuxPX_b*jJbyAmfv< zs3f3l5q2Ozsj9fs8|%`9ZihG-4=Wt(voE4>ydWe5!HeCI2^Di@Qvy=GVvi*fZ;XOH z5sZW;MO8fO$9YL%&i-jdOwp`yL7dE8|o90Xlg}Y_j?s?np#iDgL z4%|3!?Z6{f$qn0e+x+^0Pa_XpLtnVc7OS_>owcTH9vChcZb}tyk_+ooh4qWBviIt4 z*2xvyQWe|g*pEyfn&jr5RCCXK)8V_qd_(`I{ZI9}BF8hLvpXIm_|`z|MOTkZADJ&} zzf<#x?Ve3O=t~{+%^yD@A3v2kero>o>k9`%vTgKfDREaXLH9jN78v+kL@vA1<>=YO z{K^GV*zl_YHnBU%sy=SXy1qXC`Q%6X~_%S2{oOy2xTgvsIU+YVD%LQXbuag8Q|7YLa3Ag0zwc)q=LfpfN~-ad zyA=!0fhl&;>iprp#q#xXxi?krmCJXe%6GgR`&A(O6+5Q7f%jZhGm>1}ma1)AaPFKp z?OZZK%X1M0>XlB?xq2%g$Th3k-( zP=w~BkwVSN0If~3Zx^f%O@D&@54zLhA*T)6a07{e8mS~n{ppO);dZiOi^4@E{=5ioXHrOn zdGwW0VVDnH4D(40JP12sWyK6SR;_zkm25fzt z#n%B)y4kSlz4*4rZ~WLN2LGmDJ4^FO;;)uJ-$`_NN@F zYWp`=`yU<^Rennt=mpYVc6m}R&-})ncUakV@V@Kde?Hb@(|?KkVUa7IjvdTj7k99_ zkC+af;SLL>Ul;&)Psc#<9&7Dr=I%L5I<|55YAJpjhnQwP^vN%QQN_Pl0%uFx%wgTu zDS9xEsJwl#fJKHC*8!jcwvKCFA^>jdA7l36MOssZD{`)uGKAg?#F-&<5VW}|pvyA1 zR}de?9*fOi@7tpZ0oTdI8lv{po@DN_d?T1b3VsNV-z%KFpa~il|aVTB%sen z`WhQ1^#Q$~sn2oOH+&V&&5n@@nIzb2piqs+qIea+N&lNISC^tfE&}@uB?6 zNq%eg_kkOw&)dtkq8Arn{km)n)b-hQj9(=U0WPN(m#3F;*szz;ZYBaEsS@2VqlVc^ zlBR%Rxt7(J-eMIQHYUw*2gNxb2321ssIuG!J@bT8mfet~C1BB3i+oO1xy8?Be_PTD zU%P@T0@gDO%$r3g+Iqf9t+bqfbm97^GtQ$A#CmS>b0fvM zyV2A>b=j+PN6;{u#c#;|rWG#W3LdRe`NW(#lm3_3lL_|pf6~YQV~1vK%xZOqoJ_gd zl-Xs+OYG9}opxE9?;JJBvi6>gcIjOp|4}u2^bH?^r~lQ_?WL@;MmZRXXwzt0BTkb9S%Nh+n9$Aphd+5?p|Dq zO5m^#CB~#7aCR?kOV0r#abTSmpQdB?X>n-}R=tY33j^Za;E!I~sgCPBadP!A8a4;d zH#C}flV3T-H~$0aoBsyjEfT<9Ac{W5$5p=-%@ArADr`6b!DXY6h{Yg8PF-7tJ4&FgxF@+6|PE2}jxdwv{j7k<_+~BGv1^!LN zLJykmS8*aHn1wLW?+H#M6^mMmzyT;6^~%9X#tRWKj0TZN^kivL{S-g$WPPp;vck@x z3f8Vmn<7P}7>K-OS}5|$cJGw#k<~FJytC`tul!tM?0A;Uj0&EuCD` znyP7)oo)A>ZGijQ?z}oOJ@U@UDcxdO#mugEt#aA6RN1!KZn=J6s(#->*?zgKGga2P zP}V)Ao7z2Xf5~oNI}5H}n7(jL_|f;lie6E1m(O^A8oVXV_WkW2r`$WIEZ|4aOupMR z+dWtQ^JA%sy;IJ`qV;l7+k8>mt;B;(ZBwSj^6Ho% z=zHkxd+6zZ;Os}w+j`x)Slqo>TsCvz-G*5=`1%Ef;Om#JyK(ONxm(3^?zu}3w|Cy{ zUnn^yyN=y=9s8GM@&Y6Gy%35QOER^_Um+!vIw`3)SmCqc+RhF4zV67>l z*Q2-Cg_0)O)pXz01S4H#PYyA}v304H6!@4W8!0%>EICL)^K)?wDCdg1zl6EL^xN5) zZU+>9y#*1!-ro%Hr3{Exw~%9xvg%uV3?k^o~PE&3#zWnSiutp{{| zRNQzXISFFKX=MzeU6<`$CAk2XQ8VAC1_GDQ=*$<3KSSDo0&4vX9hEmO?{nsC1gfyZ z)krepe%Vz6`S<5qJ!wF{9ElHbnsEtH_VfvXF#y3G?FY(iiYhZ!VfH9`P!sU5EnI+U zL5MCtFI!Xrju)uD^K8Z*R{9z}1h0w{BD_jQV-QAFql*>+9=C-E3U$R6dUL!7+I*-B z1vHz$q1=iyWr$E+P%rRf3P&HJMBFSs8Knb`!W+0=@hS-o^HYj^51OfuSm3@j%4T@0#nq!`~hL0eG6I0w*R@b!?vU_vN zy;*iQq}&a2%mep!j8YCv56HIal&yLZ1fTi3d8Sp~*pk}VB5&NC+PM2po4j`*wRb?? zdos26-La%6B`wo!kikRua}lFsSK7Nc<{y|YJ^PPV zDBUx5?1yXxYokL?S-S-aFZLbPw`4zyuggskYKLepjoK}r?dvcPJzxmqoWe5B4$p?M zL`Zru!wb+^Yz9CbOO^{VO9bKj;96pS`=XlPYg6--|Ag8>W&bQ|7@`@e{@4yJYv z&X2q{Z>#y-b|#C(P5_Hx6#7a-=Q`#Crn8)#vqI??9UB0y2=9Rrr>Y@z`BgQ9o(G<- zfWF>s0*8tMN(4G3o7EQ&BSfU6X!woQG0t?uMI3<4i%0N<$@ZLvx~}Ls2E<(*;MEu_ zeWlri)fxmSN8z73#-kn7WQQX0y6}k~UuNL18kS)2d?GRt!!rDd1K)wtEzV$Y44%p) zu)O(r0-BA%U?dTSzXhPLDfA#f!(9j{Rk91deir{_%1Eb#Y{ zr}!U|CxtF9%si`s$UhDLKXGwf2lLDUfhroUdN#~}5%B~qiD!iC??_;Q1imEYUyf!za{hlfw7Uz!^j5w#9%kL&BGW!n>K!6+-{A~y#ir@n0tk7h?wbk zx-ueh%8;o9IoYYf`=@c-sm5EiJ51G91^bR{f{aZl|WigVBNJMZtUpPJ1E3c?SXVx#}Qoud8;GjdR86EFS}5(^YVF?5(3 z%zWuV1>_ZB<(O(vHKra^(^#$yYsR#L+A-arZcIO@A2SRZNVzI(95W6YNm?B?jhP3{ zW0pY+$!o&aG25Vxq_yFaG5esMrW6!Qjn>H%$$wereL9mHbSbD~6r=l)V)U&2zA{@{ zIz@X8&oFbwtB`bEk?|o;GOP&4 zAaEF!VX@a(i04C*ap+_1i}0Z+q|d>OuCg2ynL7PE+YfD{?C?||vxH2n&t_gIV7=dl z-DjymnxSCV=|Lr%*gsScI-$fYd_TapSNI#FK(KF)b z7#Jlo;R)k-{-~!Bb3V-ZCIV4Uy+3^2KgD}SBIA)sj%B>Lt~g~6Onr#OX_L91?r88t z#sjQpf{O%0QP1Ia&sb>Om&qYG(pgf3h9o0i&#~+s*1|IJDg{*QjuW3PK8F`hxH^AI zFHq53<*X`3{V~+X=$Im=lphUEMRWZ}HMz#QM2zy~>SNTXE?1FDj1nZ0%aR22%E8*8 zm*v$Sn1fz9_)&*kzd?VeT%Z5m&!Pu_W0Xrn2-PI#rl@=L8LwGV^OFOdwXl6*E}!Gl|0N%EOWPK%&)G9t;GnT$>JfI9Hl zqP$lvsdzRVGWVNSNOne|-7GuN&$3r{Wp93n<#)l%$!lnunBwpaPrD1AP?Nm&GN|SK5Pk6h^+ac$ z)e1Uy!k)OiwEeMe?-NJ)Lfd>>Ty;yA+En$u);q1@roFZoJo%{8YC!6*x*L=VJ zLA|)QOW50$+|-?E*t9#Y`eVxr7=#LB4++y0UY&2j|BDN7X-`r#;JW;U9mR`W5EvS+ zL+(Q6D~n&ei?5-~Ct0>$F#MW_p?sPoz6J+Q>)!9MHT&9)4M7Z0-(%vV`l7H{t$o%n#y>h z5zp5r{ozm$Xl)i90_>}yi3yhZQsL>AH0RECbRX@X-jeUiPY&?`E;Pi#?|PZ zDPZ7$&C9L?@UlEtg0Ur6b_rcFLhsU z61h4A^xMY(qmV)U%3(GtsmA?dtXIi-u$3mW7D*?M&Er)9lE7`lYF%X9N6zte^`zu^SAX6 z>&4DNp>r@LH{2$ewk>vl)O)vAta(eQd28j|Ul06XKx`in+6P|h)p|qPM3rq?7?~dt zOY0t&*2Q~Lkp0H|Hx`4*vc`CC%3iv#eSW)W-zC_0E$fo@gQDr+n(1K5v1#G(pB#Q- zD_tM0)Qc56 zg^Hcas_*L_=)~PU!tS26UB_0rwZ;>VdS56N4$H47h1rtE=%t1-J8qwxJ-Oy?U8#db zHau()JI@N8XV>~Kiv8~i{qL>4&m=q9pP7QsT~yijr>28nqz%yXOCGVn&92QoUiwE> zbWg43N1jdvWIo+S_iWdETHT?9%-?LMA(j+OP|{?11r5hWC%N%J7PSEoib56vPx1u= z@d6+-hDICUL1y@& zjrxskly7b`tYlOlt7SAkDho2}MkV#~%r!tNa-3SekvyvzZA|%_>W$QlE~bs?u6D!T zfdGWxX@EUy&cl*Z(Hw!6_bsNK1`caR9|IT!g6_A5pd%0rG0iAG@`AvqV)Qhuj?sRs z%k4;xtIapgt=7osWBT6(B%D>uh-1%yz!x+8j$xVsrYUBA(|sYcz;>|IVvGl)iCX1d zU<~r9$U8-5Z;4rc7jp*@(l}j>f*_Fy0cDOeD8gL~fz-%(__0VNIs&|OlcX964Ub4l z;O+sFgE+@N!>bA}TIIhH;yHZ7Js|h1{o#obzohXqqmz8}83VV1#QU}8ktY)u|Nvbi{KQ3uGb|UN#uwE;7 z5MXkLFlxu>Fh*}fBpD_+_8Q0tFgZyP|llhB~4o} ze?l;C5lz*CsXA$@O_^QxrUuXL2NY<^#&V{YGf z-b(8!y)kW|9AyjMdGB|2#`7d^b7+Z;2a}flqUE4qIheG(6<4P0u7vh`#yiHPtxLgsJ66soj7fKw=DstzyiY9eZ_F;sQ9ftx>mir^e zBdL&&ewFRh`hZacRpfNpYi}}B(E~vES%?VJm$EuUYn5QF60OyOwR*94iTPyY-iX-r zw$SwUsyk^t0k6|&jkl%BH;d(7q1-E$?-R=ReO3Od0{JVaCEg9pm8~ifT|6h&HVd`Q zN$Y_%-GQ_g>b~Srl)c$Wbym?oRM4H9RXCE>!eVw|YyhL56$(2%^nRevGA|hrS z@_;O)l}dalfq*~&nn4X@Q4Fx4S;`6(`lB}j>;&qhx$Uq!lAe|G zUKJ5PxC2m}xrH>#<4Zyck)aG45HR3oPL~#5!A$uXXw2V)D02fXcF|HJSZb1%I#E}* zrmIVNYCf{uwTPbmf@lAK^8j~W8Sk0vO)Hcjn50T8#L^u?>5ioX%dN@MHqq85*xH_} zsjV$(C8gi~1(2DN_Fq47RDMpOAOmuPX!8g*&)U`lE1FeWv~>!$&VNlSvGU*e6EKaN z&Q1gUXLLuoYM};F|JBv8S@E!h?%1SxXjfsnR0YKkH_@2htnbvSAJ&z1D%20#)lmLJ zg&InJs8vFmY)T-)*^y&nRPHT|mL3*gXa4#gJZhuEp3i`HcZoCDGaZX6Qvw zwY(E=fUe4Fgo-fN>vmJgs57$|b;^5_|1z44zy&*0tX4~E)iJQVU<$q}{G!+}qk)|% zrZqzxUl52uQv`lYYoy2sy5i$wJ)=X0o3sLvB|8sYejYby&Ir0UeYQrQtpP2_j3H)# zS}-QXG&4q+yPB+__^hFQVIZpnL1M+^(`+lrLtG?6nPg3Qs}wisB%$lhCF?inH7isuot5)sct z1VI}>LsDN5@)}m2%lBq<8>|pHhJwJDa1h;}mE25Yw)|Ih9`_4cf<;hM$e$UKD#3yX8LlFS!$4Z&;6w ztml>22V-RK^8GA}`IS~zvhnHwpXPTJnX-bw*kKmG#meN7a93boj9fZq;{JEc%>6U$ zPICcp$maKvYgjjvov*iK959pOJB(DjAjr%%IUQ>BU_mux^CQoc0Nl5VwTP9-H%f*_ z_$K>|BWCB_ZvT@YXtMa(I$AjbvftR8s6_}4ClAtd zvE%r;<7bW$&{>;zW;UKSG$;UAG%if<&UEQKf63E-@_1k0(eBJ-kil(?Oh&iLOh1Qcg0S)&A~8t~J}lv|d#F%GRslA2+MCWk?80B-{#HOSSH>y<1{3Y=Vc7_!iiVh07$xDRFr{t8(?2@o`h z^x?~0ER}C)a?0x}kSU0UOHzTY8;l;P(~=D2MfitMnWYE}NlK0lN;-%@a=~hHT)yZC zJ1(IeJ#%Q15nOqQe+O)_WF%Y0g?P-UiJ=H=F=)*}y&1Y!5QC4Jn1UolqEsY-kf2+LG>e(cLMyJCpA2xFUXV*8EkQIo%6q=FcqdO*-qKWAm*3 zDQJwY&t9M7W`7UV0k33SrGRBGEj5RW>-`t_Y?3<3wQ>dvmE&ZVh)r^SZDLc`%z)!*tK>ck_Lg(H{O-oCu{zE61D zCp3IbsQ6m^o{anbov`Q7rRo}_JGT>XzmTgq9UXcV0F@uMjKd;L!1Qms(2 zYq?n{Z(RWe$>I135Ja7B(NQlr>X%9%J9aOhTiO2L$}0Ql>%^RNS?Iqk_J@T2&{{Ym z^hbnuB0|r^XU!96=`zk6Q_d?XXGNlWvFy(2rRL=wFV)HtJ8Ui(%|_=(7fYAi%grkl z!oKd+(PZhnqV3%^+q)^7Q?%6!HW=_{w%tE>RXufVhKalHy2Y)n!q(Q$9Ia2B6$>Zl zPo~_ylzYpfYVp!t>#}n>^q^{W$0KH~f8ZxqVIWs23JMQdQys$K|Jh?I`wDdk| zNLE}Fofie?#s2|NPR*Z6xdG6YMZ;3}C%yN2#fC1Sq3f5VyMrqAJg=l|<##UMyPR^A zi4L#e@GjLocI?T{V9j$j<*8dzeWJUk6YG0~`kt3c+SN-xbylR@m7=>*a5pXmlkVm@ zb!zigar1s*^Zw-vE9Y0cAC)~ko!oqO-Y}<_>-l+Ec@ALPN^mv!*m)ZEZM~Xc|BCiO zd$P1+PMO=cJmG9!1yAUHQp3*S^NAv7^G*a4+Od56y=bHA}kX z>SXBw(RM(v9Y|MEO$QT~<5Ov#rYt*tojyxbC1>ehr|pz$06q;=Nz0dfFFg61u09jJ zK=;wA#Bm2Cf3lPAQ>lK^(9r~$pH|R)TGdZCb#I2u-?h?x2G!pk)I)krMPsZbu>oUl z0)TOU0#RVS+A61HK$a_~WPnzM0W_JQQ2Gn%yUO);L12%Hh}SU%I9-5MPZkaenHmsn zHQe8Wbt}tWuOF+Js)13W_{Dt+qv3oD!Ujr(f>xLXR_iN|xk1Y!s*0E@nzPcyG_q>p zW7R9dLlG5%tj6F5U|mHWXukR!7)oX!K@~uaqU%*bAZ+GWU%gfqF$&KvD{@dpop-=x zRA#TS9WbcBg8xlfcM*v`Bq@RIBId4p3UV2N`sUnOr%i;wXWcoxW)Arg4o^Je8et^W z{ZW5(l9x2#bqP)%84m{o2VS)tgZzMG@?RV7fx14z)Pi_RNfvO!4gF_ZcdG z6H2C?uTgoJ^IfRn9iZ_dr2T1$YoUC;e9j2QpbLrKHA`(=`2>`f`#)*9*RtHZ!mbWH z;*+KQqOCu!{<+aMcV2YW39dTP)hM_cm+8-3d(gAu)aOY_RB*19R32LC26u=p_BjL4t+Gwvqal>mVWZwfR;+6g>RQCQ!$RHR zRRc6Dvx8Q@#Qq#3u&mi?(@x48(`BIr@vV6PkRHv@MvdA>9z0~ zya%mk)H51*Zfb@m=&5I&!)s1^numpcXn+#4qY!Bj??^!?=9U0+E9Nf<&|(@!35X`I zgF#mcc>F};N)vq!YG<|JBbP(dqiB^a2n=|Qt$(=1M+6d}$&vuwI+RC^q6AS8fCPY( zMNB8pt#JRqJwnf22e*VGTFYYko$MZ8ftmgP?%G!#bA#CxmHnfe2vN4zup!0;Sqyf-mqq{o5gJRj>$tD`N2*xdsjh@uuPVw+N!r^z;`Yt37Uldy}3Z~}x$wb9k zZF9<6k$CqHznSF{*DAYLgO9F0whbckC4vB9t(~8g-z#74SvmQrBDK2*j7}xqmlRzB zoGbX8ofK@_=X5D+Sz^y0&ZKOmKX+7v<+G%0!7^_F&pKBpo$5L+bCo4!sW*^`;OfX) zUw^XnoM=1uMmkX=m}(Z!eRS#WrDe^ET5LTov>q2*`-IlMRP7G2_JB}(K&-mE=pq(7pMYE(;| zRgnCVmOiRi{YcmGHe^24(-6O6tp$vov3eoO%P^!<6uuS?F~vRbnISg=*k(A_jLlFB zw!vs9aP@V&b+$r)wmjPWGorK3^lM^uys1#puem88etv4zG zEp9C?!t8#mkl8G7B~qS|l>O&Uf{_&Y3{c;(fph52qXYZt2sr0MDlo~xFHQne&<5w3 zQSv;%)PI7=tH?hZ9_K3G_a;aebF|62m_CMT9<}e_r*ADwIm$smO}Wa^#U;a8ypjQj zOq(H1fmbGR_VCirkSF%i)7E3;$1+{`TbZ4p#{%yr%3FcIg+SL+pHDLQz>A2P3}f2l zLp@G7Tch>)m`DJAE`d~vLvqI9@sl4IaIKJ%OyFDvXFSG6M zG#-^S{qTzlmM2QE%=VxHjSFI1CHN;3H4AqW%T8m2Sb+O0jBtM>bM^-kfXc!?A6&Do zP)e@ZkqrNabd$pf|M}+-eeognT%lI$(iV++PhxMHg3r#9=CFVZ2m8gg5bRDI! zKUb8f+vkQB!t>!Y1<9o@@>#L}b@>m;{eIhnwilTE z+@?`?(P`UpTHS)(@w?Lcf_&2h3ToTF=SqjVb?MD8f#mV%E`(h#c0XD9+;*x?5=@WUx`2PA9$ z$0tog@XI#NjD7ULzMmpK+T4FeXxlL&C-gC<5c6b^gRU)e=MHm!17-N4`6Cd45uc|2 zJ7xJN%JC1>w*N_8NKzMmL6!dlb?_I|)?ZNdPxTe?GErY4=qnPfNqx-+nwOfBO4=|N L{w0N9GQ$4@CY@^^ literal 0 HcmV?d00001 diff --git a/strategies/__pycache__/ma_cross.cpython-314.pyc b/strategies/__pycache__/ma_cross.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d783c8e81b7ad059f519055f1b473af8d6631b24 GIT binary patch literal 16488 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 z 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 EACx|hQvd(} literal 0 HcmV?d00001 diff --git a/strategies/__pycache__/mean_reversion.cpython-312.pyc b/strategies/__pycache__/mean_reversion.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd9732099ab39b4a21b42997bccc2b974fa1225b GIT binary patch literal 15583 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 literal 0 HcmV?d00001 diff --git a/strategies/__pycache__/short_term.cpython-314.pyc b/strategies/__pycache__/short_term.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..082aa49ab0b979dd90ce3ce1d9d51af6f044b8b2 GIT binary patch literal 16656 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 literal 0 HcmV?d00001 diff --git a/strategies/base.py b/strategies/base.py index 9368545..c405193 100644 --- a/strategies/base.py +++ b/strategies/base.py @@ -9,7 +9,7 @@ from ib_insync import IB, Contract, StopOrder, Trade from bars import BarManager from config import config -from orders import has_open_order, wait_trade_done +from orders import has_open_order, trade_commission, wait_trade_done from state import PositionTracker logger = logging.getLogger(__name__) @@ -208,7 +208,10 @@ class BaseStrategy(ABC): "%s HARD STOP filled: %s x%g @ %.2f", self.name, key, filled, trade.orderStatus.avgFillPrice, ) - self.tracker.record_sell(self.name, key, filled, trade.orderStatus.avgFillPrice) + self.tracker.record_sell(self.name, key, filled, + trade.orderStatus.avgFillPrice, + reason="HardStop", + commission=trade_commission(trade)) self._mark_hard_stop_cooldown(key) elif owned: logger.warning( @@ -285,7 +288,8 @@ class BaseStrategy(ABC): stop_filled, stop_price, confirmed = await self._cancel_stop(key) if stop_filled > 0: logger.info("%s: stop filled %g during trailing raise", key, stop_filled) - self.tracker.record_sell(self.name, key, stop_filled, stop_price) + self.tracker.record_sell(self.name, key, stop_filled, stop_price, + reason="HardStopDuringTrail") remaining = owned["quantity"] - stop_filled if remaining <= 0: return diff --git a/strategies/forex.py b/strategies/forex.py index cf0b584..4b41d39 100644 --- a/strategies/forex.py +++ b/strategies/forex.py @@ -5,7 +5,7 @@ from ib_insync import Contract, IB from bars import BarManager, is_market_active, parse_bar_size_seconds, to_completed_df from config import config -from orders import execute_market_order +from orders import execute_market_order, trade_commission from state import PositionTracker from strategies.base import BaseStrategy @@ -87,13 +87,13 @@ class ForexMAStrategy(BaseStrategy): "FOREX STOP-LOSS SELL: %s close=%.5f entry=%.5f", pair, last["close"], entry, ) - await self._sell(pair, contract, owned["quantity"]) + await self._sell(pair, contract, owned["quantity"], "SoftStop") elif cross_down: logger.info( "FOREX SELL %s (fast MA %.5f < slow MA %.5f)", pair, last["fast_ma"], last["slow_ma"], ) - await self._sell(pair, contract, owned["quantity"]) + await self._sell(pair, contract, owned["quantity"], "CrossDown") else: if cross_up: logger.info( @@ -102,19 +102,20 @@ class ForexMAStrategy(BaseStrategy): ) await self._buy(pair, contract) - async def _buy(self, pair: str, contract: Contract): + async def _buy(self, pair: str, contract: Contract, reason: str = "MACross"): ref = f"{self.name}:{pair}" trade = await execute_market_order(self.ib, contract, "BUY", self.units, ref) if trade and trade.orderStatus.filled > 0: self.tracker.record_buy( - self.name, pair, trade.orderStatus.filled, trade.orderStatus.avgFillPrice + self.name, pair, trade.orderStatus.filled, trade.orderStatus.avgFillPrice, + reason=reason, commission=trade_commission(trade), ) - async def _sell(self, pair: str, contract: Contract, quantity: float): + async def _sell(self, pair: str, contract: Contract, quantity: float, reason: str = ""): ref = f"{self.name}:{pair}" trade = await execute_market_order(self.ib, contract, "SELL", quantity, ref) if trade and trade.orderStatus.filled > 0: - self.tracker.record_sell(self.name, pair, trade.orderStatus.filled) + self.tracker.record_sell(self.name, pair, trade.orderStatus.filled, reason=reason) async def on_tick(self): pass diff --git a/strategies/ma_cross.py b/strategies/ma_cross.py index 6dfcd37..d4ad3ac 100644 --- a/strategies/ma_cross.py +++ b/strategies/ma_cross.py @@ -5,7 +5,7 @@ from ib_insync import Contract, IB from bars import BarManager, is_market_active, parse_bar_size_seconds, to_completed_df from config import config -from orders import execute_market_order +from orders import execute_market_order, trade_commission from state import PositionTracker from strategies.base import BaseStrategy @@ -125,7 +125,7 @@ class MAStockStrategy(BaseStrategy): "STOCK STOP-LOSS SELL (soft fallback): %s close=%.2f entry=%.2f (%.2f%%)", symbol, last["close"], entry, profit_pct, ) - await self._sell(symbol, contract, owned["quantity"]) + await self._sell(symbol, contract, owned["quantity"], "SoftStop") self._mark_stop_cooldown(symbol) elif not fast_above: # re-checked every cycle: exits as soon as profit requirement is met @@ -134,7 +134,7 @@ class MAStockStrategy(BaseStrategy): "STOCK SELL: %s (fast MA %.2f < slow MA %.2f, profit=%.2f%%, ADX=%.1f)", symbol, last["fast_ma"], last["slow_ma"], profit_pct, last["adx"], ) - await self._sell(symbol, contract, owned["quantity"]) + await self._sell(symbol, contract, owned["quantity"], "SignalExit") else: logger.debug( "STOCK SELL WAITING: %s profit %.2f%% < min %.1f%%", @@ -158,12 +158,14 @@ class MAStockStrategy(BaseStrategy): ) await self._buy(symbol, contract, qty) - async def _buy(self, symbol: str, contract: Contract, quantity: int): + async def _buy(self, symbol: str, contract: Contract, quantity: int, + reason: str = "GoldenCross+ADX"): ref = f"{self.name}:{symbol}" trade = await execute_market_order(self.ib, contract, "BUY", quantity, ref) if trade and trade.orderStatus.filled > 0: self.tracker.record_buy( - self.name, symbol, trade.orderStatus.filled, trade.orderStatus.avgFillPrice + self.name, symbol, trade.orderStatus.filled, trade.orderStatus.avgFillPrice, + reason=reason, commission=trade_commission(trade), ) if self._use_hard_stop(): stop_price = self._target_stop_price( @@ -177,13 +179,15 @@ class MAStockStrategy(BaseStrategy): # cool down to avoid retrying every cycle self._mark_cooldown(symbol, "order failed") - async def _sell(self, symbol: str, contract: Contract, quantity: float): + async def _sell(self, symbol: str, contract: Contract, quantity: float, + reason: str = ""): # cancel the hard stop first; it may have filled in the cancel race if self._use_hard_stop(): stop_filled, stop_price, cancel_confirmed = await self._cancel_stop(symbol) if stop_filled > 0: logger.info("%s: stop order filled %g during cancel", symbol, stop_filled) - self.tracker.record_sell(self.name, symbol, stop_filled, stop_price) + self.tracker.record_sell(self.name, symbol, stop_filled, stop_price, + reason="HardStopDuringCancel") quantity -= stop_filled if quantity <= 0: return @@ -198,7 +202,9 @@ class MAStockStrategy(BaseStrategy): ref = f"{self.name}:{symbol}" trade = await execute_market_order(self.ib, contract, "SELL", quantity, ref) if trade and trade.orderStatus.filled > 0: - self.tracker.record_sell(self.name, symbol, trade.orderStatus.filled, trade.orderStatus.avgFillPrice) + self.tracker.record_sell(self.name, symbol, trade.orderStatus.filled, + trade.orderStatus.avgFillPrice, + reason=reason, commission=trade_commission(trade)) async def on_tick(self): pass diff --git a/strategies/mean_reversion.py b/strategies/mean_reversion.py index f22d4cf..ecbcf85 100644 --- a/strategies/mean_reversion.py +++ b/strategies/mean_reversion.py @@ -5,7 +5,7 @@ from ib_insync import Contract, IB from bars import BarManager, is_market_active, parse_bar_size_seconds, to_completed_df from config import config -from orders import execute_market_order +from orders import execute_market_order, trade_commission from state import PositionTracker from strategies.base import BaseStrategy @@ -145,7 +145,7 @@ class MeanReversionStrategy(BaseStrategy): "MeanRev BUY: %s x%d [%s] RSI=%.1f, close=%.2f, BB_lower=%.2f", symbol, qty, buy_signal, last["rsi"], last["close"], last["bb_lower"], ) - await self._buy(symbol, contract, qty) + await self._buy(symbol, contract, qty, buy_signal) else: entry = owned["entry_price"] profit_pct = (last["close"] - entry) / entry * 100 @@ -157,7 +157,7 @@ class MeanReversionStrategy(BaseStrategy): "MeanRev STOP-LOSS SELL (soft fallback): %s close=%.2f entry=%.2f (%.2f%%)", symbol, last["close"], entry, profit_pct, ) - await self._sell(symbol, contract, owned["quantity"]) + await self._sell(symbol, contract, owned["quantity"], "SoftStop") self._mark_stop_cooldown(symbol) return @@ -181,14 +181,16 @@ class MeanReversionStrategy(BaseStrategy): "MeanRev SELL: %s [%s] RSI=%.1f, close=%.2f, entry=%.2f, profit=%.2f%%", symbol, sell_signal, last["rsi"], last["close"], entry, profit_pct, ) - await self._sell(symbol, contract, owned["quantity"]) + await self._sell(symbol, contract, owned["quantity"], sell_signal) - async def _buy(self, symbol: str, contract: Contract, quantity: int): + async def _buy(self, symbol: str, contract: Contract, quantity: int, + reason: str = ""): ref = f"{self.name}:{symbol}" trade = await execute_market_order(self.ib, contract, "BUY", quantity, ref) if trade and trade.orderStatus.filled > 0: self.tracker.record_buy( - self.name, symbol, trade.orderStatus.filled, trade.orderStatus.avgFillPrice + self.name, symbol, trade.orderStatus.filled, trade.orderStatus.avgFillPrice, + reason=reason, commission=trade_commission(trade), ) if self._use_hard_stop(): stop_price = self._target_stop_price( @@ -202,13 +204,15 @@ class MeanReversionStrategy(BaseStrategy): # cool down to avoid retrying every cycle self._mark_cooldown(symbol, "order failed") - async def _sell(self, symbol: str, contract: Contract, quantity: float): + async def _sell(self, symbol: str, contract: Contract, quantity: float, + reason: str = ""): # cancel the hard stop first; it may have filled in the cancel race if self._use_hard_stop(): stop_filled, stop_price, cancel_confirmed = await self._cancel_stop(symbol) if stop_filled > 0: logger.info("%s: stop order filled %g during cancel", symbol, stop_filled) - self.tracker.record_sell(self.name, symbol, stop_filled, stop_price) + self.tracker.record_sell(self.name, symbol, stop_filled, stop_price, + reason="HardStopDuringCancel") quantity -= stop_filled if quantity <= 0: return @@ -223,7 +227,9 @@ class MeanReversionStrategy(BaseStrategy): ref = f"{self.name}:{symbol}" trade = await execute_market_order(self.ib, contract, "SELL", quantity, ref) if trade and trade.orderStatus.filled > 0: - self.tracker.record_sell(self.name, symbol, trade.orderStatus.filled, trade.orderStatus.avgFillPrice) + self.tracker.record_sell(self.name, symbol, trade.orderStatus.filled, + trade.orderStatus.avgFillPrice, + reason=reason, commission=trade_commission(trade)) async def on_tick(self): pass diff --git a/strategies/short_term.py b/strategies/short_term.py index 7c5cc65..d3aee0a 100644 --- a/strategies/short_term.py +++ b/strategies/short_term.py @@ -6,7 +6,7 @@ from ib_insync import Contract, IB from bars import BarManager, is_market_active, parse_bar_size_seconds, to_completed_df from config import config -from orders import execute_market_order +from orders import execute_market_order, trade_commission from state import PositionTracker from strategies.base import BaseStrategy @@ -119,7 +119,7 @@ class ShortTermMAVWAPStrategy(BaseStrategy): "ShortTerm STOP-LOSS SELL (soft fallback): %s close=%.2f entry=%.2f (%.2f%%)", symbol, last["close"], entry, profit_pct, ) - await self._sell(symbol, contract, owned["quantity"]) + await self._sell(symbol, contract, owned["quantity"], "SoftStop") self._mark_stop_cooldown(symbol) return @@ -128,7 +128,7 @@ class ShortTermMAVWAPStrategy(BaseStrategy): hold_days = (date.today() - entry_date).days if hold_days >= self.cfg.max_hold_days: logger.info("ShortTerm SELL %s: max hold reached (%d days)", symbol, hold_days) - await self._sell(symbol, contract, owned["quantity"]) + await self._sell(symbol, contract, owned["quantity"], "MaxHold") return if not fast_above: @@ -138,7 +138,7 @@ class ShortTermMAVWAPStrategy(BaseStrategy): "ShortTerm SELL: %s (fast EMA %.2f < slow EMA %.2f, profit=%.2f%%)", symbol, last["fast_ema"], last["slow_ema"], profit_pct, ) - await self._sell(symbol, contract, owned["quantity"]) + await self._sell(symbol, contract, owned["quantity"], "SignalExit") else: logger.debug( "ShortTerm SELL WAITING: %s profit %.2f%% < min %.1f%%", @@ -162,12 +162,14 @@ class ShortTermMAVWAPStrategy(BaseStrategy): ) await self._buy(symbol, contract, qty) - async def _buy(self, symbol: str, contract: Contract, quantity: int): + async def _buy(self, symbol: str, contract: Contract, quantity: int, + reason: str = "EMACross+VWAP"): ref = f"{self.name}:{symbol}" trade = await execute_market_order(self.ib, contract, "BUY", quantity, ref) if trade and trade.orderStatus.filled > 0: self.tracker.record_buy( - self.name, symbol, trade.orderStatus.filled, trade.orderStatus.avgFillPrice + self.name, symbol, trade.orderStatus.filled, trade.orderStatus.avgFillPrice, + reason=reason, commission=trade_commission(trade), ) if self._use_hard_stop(): stop_price = self._target_stop_price( @@ -181,13 +183,15 @@ class ShortTermMAVWAPStrategy(BaseStrategy): # cool down to avoid retrying every cycle self._mark_cooldown(symbol, "order failed") - async def _sell(self, symbol: str, contract: Contract, quantity: float): + async def _sell(self, symbol: str, contract: Contract, quantity: float, + reason: str = ""): # cancel the hard stop first; it may have filled in the cancel race if self._use_hard_stop(): stop_filled, stop_price, cancel_confirmed = await self._cancel_stop(symbol) if stop_filled > 0: logger.info("%s: stop order filled %g during cancel", symbol, stop_filled) - self.tracker.record_sell(self.name, symbol, stop_filled, stop_price) + self.tracker.record_sell(self.name, symbol, stop_filled, stop_price, + reason="HardStopDuringCancel") quantity -= stop_filled if quantity <= 0: return @@ -202,7 +206,9 @@ class ShortTermMAVWAPStrategy(BaseStrategy): ref = f"{self.name}:{symbol}" trade = await execute_market_order(self.ib, contract, "SELL", quantity, ref) if trade and trade.orderStatus.filled > 0: - self.tracker.record_sell(self.name, symbol, trade.orderStatus.filled, trade.orderStatus.avgFillPrice) + self.tracker.record_sell(self.name, symbol, trade.orderStatus.filled, + trade.orderStatus.avgFillPrice, + reason=reason, commission=trade_commission(trade)) async def on_tick(self): pass diff --git a/test_offline.py b/test_offline.py new file mode 100644 index 0000000..5aea141 --- /dev/null +++ b/test_offline.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python +"""Offline tests — no IB connection, no orders, safe to run against the live repo. + + .venv/bin/python -m unittest test_offline -v + +Covers the pure layers: ledger writing (PositionTracker), round-trip +reconstruction and statistics (analytics), and the indicator maths. Indicator +tests are skipped when pandas is unavailable. +""" +import json +import tempfile +import unittest +from pathlib import Path + +import analytics +from state import PositionTracker + +try: + import pandas as pd + HAVE_PANDAS = True +except ImportError: + HAVE_PANDAS = False + + +class LedgerTest(unittest.TestCase): + """PositionTracker must record the attribution fields the dashboard needs.""" + + def setUp(self): + self.dir = tempfile.TemporaryDirectory() + self.tracker = PositionTracker(str(Path(self.dir.name) / "bot_state.json")) + + def tearDown(self): + self.dir.cleanup() + + def _ledger(self): + return [json.loads(l) for l in + self.tracker.ledger_file.read_text().splitlines() if l.strip()] + + def test_buy_records_reason_and_commission(self): + self.tracker.record_buy("S", "AAPL", 10, 100.0, reason="RSI", commission=0.35) + rec = self._ledger()[-1] + self.assertEqual(rec["type"], "buy") + self.assertEqual(rec["reason"], "RSI") + self.assertEqual(rec["commission"], 0.35) + + def test_sell_records_reason_and_commission(self): + self.tracker.record_buy("S", "AAPL", 10, 100.0, reason="RSI") + self.tracker.record_sell("S", "AAPL", 10, 103.0, + reason="SignalExit", commission=0.4) + rec = self._ledger()[-1] + self.assertEqual(rec["reason"], "SignalExit") + self.assertEqual(rec["commission"], 0.4) + + def test_absent_fields_are_omitted(self): + """A record with no reason/commission stays byte-compatible with the old + schema, so historical ledgers keep parsing.""" + self.tracker.record_buy("S", "AAPL", 10, 100.0) + rec = self._ledger()[-1] + self.assertNotIn("reason", rec) + self.assertNotIn("commission", rec) + + def test_day_pnl_is_net_of_commission(self): + self.tracker.record_buy("S", "AAPL", 10, 100.0) + self.tracker.record_sell("S", "AAPL", 10, 101.0, commission=0.5) + self.assertAlmostEqual(self.tracker.day_realized_pnl(), 10.0 - 0.5, places=6) + + def test_external_sell_is_tagged(self): + self.tracker.record_buy("S", "AAPL", 10, 100.0) + self.tracker.reconcile({}) # account holds none -> backfill an external sell + rec = self._ledger()[-1] + self.assertEqual(rec["type"], "sell_external") + self.assertEqual(rec["reason"], "HardStopOffline") + self.assertTrue(rec["est"]) + + +class RoundTripTest(unittest.TestCase): + """FIFO reconstruction has to survive partial fills and pre-ledger lots.""" + + @staticmethod + def _trips(records): + return analytics.build_round_trips(records) + + def test_simple_round_trip(self): + trips, open_lots, unmatched = self._trips([ + {"ts": "2026-08-01T10:00:00", "type": "buy", "strategy": "S", "symbol": "X", + "qty": 10, "price": 100.0, "reason": "Entry", "commission": 0.35}, + {"ts": "2026-08-01T11:00:00", "type": "sell", "strategy": "S", "symbol": "X", + "qty": 10, "price": 102.0, "reason": "SignalExit", "commission": 0.35}, + ]) + self.assertEqual(len(trips), 1) + self.assertEqual(open_lots, []) + self.assertEqual(unmatched, 0) + t = trips[0] + self.assertAlmostEqual(t.gross_pnl, 20.0) + self.assertAlmostEqual(t.commission, 0.70) + self.assertAlmostEqual(t.pnl, 19.30) + self.assertAlmostEqual(t.pnl_pct, 19.30 / 1000 * 100) + self.assertEqual(t.hold_minutes, 60) + self.assertEqual(t.entry_reason, "Entry") + self.assertEqual(t.exit_reason, "SignalExit") + self.assertTrue(t.won) + + def test_partial_sell_splits_the_lot(self): + trips, open_lots, _ = self._trips([ + {"ts": "2026-08-01T10:00:00", "type": "buy", "strategy": "S", "symbol": "X", + "qty": 10, "price": 100.0, "commission": 1.0}, + {"ts": "2026-08-01T11:00:00", "type": "sell", "strategy": "S", "symbol": "X", + "qty": 4, "price": 105.0}, + ]) + self.assertEqual(len(trips), 1) + self.assertEqual(trips[0].qty, 4) + self.assertAlmostEqual(trips[0].gross_pnl, 20.0) + # buy commission is allocated pro rata: 4/10 of $1.00 + self.assertAlmostEqual(trips[0].commission, 0.4) + self.assertEqual(len(open_lots), 1) + self.assertEqual(open_lots[0].qty, 6) + + def test_sell_spanning_two_lots_is_fifo(self): + trips, _, _ = self._trips([ + {"ts": "2026-08-01T10:00:00", "type": "buy", "strategy": "S", "symbol": "X", + "qty": 5, "price": 100.0}, + {"ts": "2026-08-01T10:30:00", "type": "buy", "strategy": "S", "symbol": "X", + "qty": 5, "price": 110.0}, + {"ts": "2026-08-01T11:00:00", "type": "sell", "strategy": "S", "symbol": "X", + "qty": 8, "price": 120.0}, + ]) + self.assertEqual(len(trips), 2) + # oldest lot consumed first, in full + self.assertEqual((trips[0].qty, trips[0].entry_price), (5, 100.0)) + self.assertEqual((trips[1].qty, trips[1].entry_price), (3, 110.0)) + self.assertAlmostEqual(sum(t.gross_pnl for t in trips), 5 * 20 + 3 * 10) + + def test_sell_without_basis_is_reported_not_guessed(self): + trips, _, unmatched = self._trips([ + {"ts": "2026-08-01T11:00:00", "type": "sell", "strategy": "S", "symbol": "X", + "qty": 7, "price": 120.0}, + ]) + self.assertEqual(trips, []) + self.assertEqual(unmatched, 7) + + def test_positions_are_owned_per_strategy(self): + """Two strategies holding the same symbol must not cross-match.""" + trips, open_lots, unmatched = self._trips([ + {"ts": "2026-08-01T10:00:00", "type": "buy", "strategy": "A", "symbol": "X", + "qty": 5, "price": 100.0}, + {"ts": "2026-08-01T10:01:00", "type": "buy", "strategy": "B", "symbol": "X", + "qty": 5, "price": 200.0}, + {"ts": "2026-08-01T11:00:00", "type": "sell", "strategy": "B", "symbol": "X", + "qty": 5, "price": 210.0}, + ]) + self.assertEqual(len(trips), 1) + self.assertEqual(trips[0].strategy, "B") + self.assertAlmostEqual(trips[0].gross_pnl, 50.0) + self.assertEqual(unmatched, 0) + self.assertEqual([(l.strategy, l.qty) for l in open_lots], [("A", 5)]) + + def test_seed_lot_is_flagged(self): + trips, _, _ = self._trips([ + {"ts": "2026-08-01T10:00:00", "type": "seed", "strategy": "S", "symbol": "X", + "qty": 5, "price": 100.0}, + {"ts": "2026-08-01T11:00:00", "type": "sell", "strategy": "S", "symbol": "X", + "qty": 5, "price": 101.0}, + ]) + self.assertTrue(trips[0].seeded_entry) + + +class StatsTest(unittest.TestCase): + """The statistics that parameter decisions get made on.""" + + @staticmethod + def _trip(pnl, entry=100.0, qty=1.0, ts="2026-08-01T10:00:00"): + return analytics.RoundTrip( + strategy="S", symbol="X", qty=qty, entry_ts=ts, exit_ts=ts, + entry_price=entry, exit_price=entry + pnl, entry_reason="", exit_reason="", + gross_pnl=pnl, commission=0.0, estimated=False, seeded_entry=False) + + def test_summary_of_empty_set(self): + s = analytics.summarize([]) + self.assertEqual(s["n"], 0) + self.assertEqual(s["pnl"], 0.0) + self.assertIsNone(s["profit_factor"]) + + def test_core_statistics(self): + # 3 wins of +10, 2 losses of -20 + trips = [self._trip(10)] * 3 + [self._trip(-20)] * 2 + s = analytics.summarize(trips) + self.assertEqual(s["n"], 5) + self.assertAlmostEqual(s["pnl"], 30 - 40) + self.assertAlmostEqual(s["win_rate"], 60.0) + self.assertAlmostEqual(s["expectancy"], -2.0) + self.assertAlmostEqual(s["profit_factor"], 30 / 40) + self.assertAlmostEqual(s["avg_win"], 10.0) + self.assertAlmostEqual(s["avg_loss"], 20.0) + self.assertAlmostEqual(s["payoff_ratio"], 0.5) + # payoff 0.5 => must win 2 of every 3 just to break even + self.assertAlmostEqual(s["breakeven_win_rate"], 100 / 1.5) + + def test_breakeven_win_rate_matches_actual_at_zero_expectancy(self): + """Sanity check on the headline diagnostic: when a set nets exactly zero, + its win rate must equal its own breakeven win rate.""" + trips = [self._trip(10)] * 2 + [self._trip(-20)] + s = analytics.summarize(trips) + self.assertAlmostEqual(s["pnl"], 0.0) + self.assertAlmostEqual(s["win_rate"], s["breakeven_win_rate"], places=6) + + def test_profit_factor_is_none_without_losses(self): + self.assertIsNone(analytics.summarize([self._trip(5)])["profit_factor"]) + + def test_max_drawdown(self): + trips = [ + self._trip(100, ts="2026-08-01T10:00:00"), + self._trip(-40, ts="2026-08-01T11:00:00"), + self._trip(-30, ts="2026-08-01T12:00:00"), + self._trip(50, ts="2026-08-01T13:00:00"), + ] + curve = analytics.equity_curve(trips) + self.assertEqual([p["cum_pnl"] for p in curve], [100, 60, 30, 80]) + self.assertAlmostEqual(analytics.max_drawdown(curve), -70.0) + + def test_grouping(self): + a = self._trip(10); a.symbol = "AAA" + b = self._trip(-5); b.symbol = "BBB" + g = analytics.group_stats([a, b], lambda t: t.symbol) + self.assertAlmostEqual(g["AAA"]["pnl"], 10) + self.assertAlmostEqual(g["BBB"]["pnl"], -5) + + def test_analyze_end_to_end_with_date_filter(self): + recs = [ + {"ts": "2026-08-01T10:00:00", "type": "buy", "strategy": "S", "symbol": "X", + "qty": 1, "price": 100.0}, + {"ts": "2026-08-01T11:00:00", "type": "sell", "strategy": "S", "symbol": "X", + "qty": 1, "price": 110.0}, + {"ts": "2026-08-05T10:00:00", "type": "buy", "strategy": "S", "symbol": "X", + "qty": 1, "price": 100.0}, + {"ts": "2026-08-05T11:00:00", "type": "sell", "strategy": "S", "symbol": "X", + "qty": 1, "price": 90.0}, + ] + with tempfile.TemporaryDirectory() as d: + p = Path(d) / "trades.jsonl" + p.write_text("\n".join(json.dumps(r) for r in recs) + "\n") + allt = analytics.analyze(p) + self.assertEqual(allt["overall"]["n"], 2) + self.assertAlmostEqual(allt["overall"]["pnl"], 0.0) + # filtering the window must not corrupt the cost basis of what remains + late = analytics.analyze(p, since="2026-08-05") + self.assertEqual(late["overall"]["n"], 1) + self.assertAlmostEqual(late["overall"]["pnl"], -10.0) + + +@unittest.skipUnless(HAVE_PANDAS, "pandas not installed") +class IndicatorTest(unittest.TestCase): + """Guards on the two indicator bugs AGENTS.md warns against reintroducing.""" + + def test_rsi_is_100_on_a_pure_uptrend(self): + from strategies.mean_reversion import MeanReversionStrategy + rsi = MeanReversionStrategy._calc_rsi(pd.Series(range(1, 40), dtype=float), 14) + # no losses -> rs is inf -> RSI must saturate at 100, never NaN + self.assertFalse(pd.isna(rsi.iloc[-1])) + self.assertAlmostEqual(rsi.iloc[-1], 100.0, places=6) + + def test_adx_direction_on_a_clean_uptrend(self): + from strategies.ma_cross import MAStockStrategy + n = 60 + close = pd.Series([100 + i for i in range(n)], dtype=float) + df = pd.DataFrame({"high": close + 0.5, "low": close - 0.5, "close": close}) + adx = MAStockStrategy._calc_adx(df, 14) + # a monotonic uptrend must read as strongly trending + self.assertGreater(adx.iloc[-1], 50) + + def test_vwap_resets_each_day(self): + from strategies.short_term import ShortTermMAVWAPStrategy + ts = (list(pd.date_range("2026-08-03 13:30", periods=3, freq="1min", tz="UTC")) + + list(pd.date_range("2026-08-04 13:30", periods=3, freq="1min", tz="UTC"))) + df = pd.DataFrame({ + "date": ts, + "high": [10, 10, 10, 20, 20, 20], "low": [10, 10, 10, 20, 20, 20], + "close": [10, 10, 10, 20, 20, 20], "volume": [1, 1, 1, 1, 1, 1], + }) + vwap = ShortTermMAVWAPStrategy._calc_vwap(df) + # day two must not be dragged toward day one's prices + self.assertAlmostEqual(vwap.iloc[2], 10.0) + self.assertAlmostEqual(vwap.iloc[3], 20.0) + + def test_forming_bar_is_dropped(self): + from datetime import datetime, timedelta, timezone + from ib_insync import BarData + from bars import to_completed_df + + def bar(date, close): + return BarData(date=date, open=close, high=close, low=close, + close=close, volume=1) + + now = datetime.now(timezone.utc) + bars = [bar(now - timedelta(seconds=120), 1.0), bar(now - timedelta(seconds=60), 2.0), + bar(now, 3.0)] # the last bar is still forming + df = to_completed_df(bars, 60) + self.assertEqual(len(df), 2) + self.assertEqual(df["close"].iloc[-1], 2.0) + + +if __name__ == "__main__": + unittest.main(verbosity=2)