From 4aa17c7e989a26a690e40c72a89647427abd84a1 Mon Sep 17 00:00:00 2001 From: Gahow Wang Date: Tue, 7 Apr 2026 00:10:00 +0800 Subject: [PATCH] Zero-config monitor and compare MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `trader.py monitor` — runs ALL 9 strategies by default, $10k capital, $2 fee, integer shares. Just start and forget. - `trader.py compare` — auto-discovers all state files in data/, no --strategy needed. Ranks by return with equity curves. - Monitor defaults: --capital 10000, --fixed-fee 2.0, --integer-shares Co-Authored-By: Claude Opus 4.6 --- trader.py | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/trader.py b/trader.py index 511f229..605323a 100644 --- a/trader.py +++ b/trader.py @@ -595,8 +595,31 @@ def cmd_status(args): def cmd_compare(args): """Compare multiple strategies side-by-side.""" + import glob as _glob + market = args.market - strategy_names = args.strategy + strategy_names = getattr(args, "strategy", None) + + # Auto-discover: find all trader_{market}_*.json state files + if not strategy_names: + pattern = os.path.join("data", f"trader_{market}_*.json") + files = sorted(_glob.glob(pattern)) + strategy_names = [] + for f in files: + # Extract strategy name: trader_{market}_{name}.json + base = os.path.basename(f) # trader_us_foo.json + prefix = f"trader_{market}_" + name = base[len(prefix):-len(".json")] + # Skip sim_ prefixed (historical simulations) unless no live ones + strategy_names.append(name) + + if not strategy_names: + print(f"No state files found for market '{market}' in data/.") + print(f"Run 'monitor' or 'auto' first to generate state.") + return + + print(f" Auto-discovered {len(strategy_names)} strategies: " + f"{', '.join(strategy_names)}") # Load all states states = {} @@ -1277,19 +1300,19 @@ def main(): # Monitor (long-running daemon for tmux) — supports multiple strategies p_monitor = sub.add_parser("monitor", - help="Long-running daemon: auto-runs daily after market close (for tmux)") + help="Long-running daemon: runs ALL strategies daily (for tmux)") p_monitor.add_argument("--market", choices=UNIVERSES.keys(), default="us") p_monitor.add_argument("--strategy", nargs="+", choices=list(STRATEGY_REGISTRY.keys()), - default=["recovery_mom_top10"], - help="One or more strategies to run in parallel") - p_monitor.add_argument("--capital", type=float, default=100_000) + default=list(STRATEGY_REGISTRY.keys()), + help="Strategies to run (default: ALL)") + p_monitor.add_argument("--capital", type=float, default=10_000) p_monitor.add_argument("--tx-cost", type=float, default=0.001, help="Proportional transaction cost (default: 0.001 = 10bps)") - p_monitor.add_argument("--fixed-fee", type=float, default=0.0, - help="Fixed dollar fee per trade") - p_monitor.add_argument("--integer-shares", action="store_true", default=False, - help="Only trade whole shares (no fractional)") + p_monitor.add_argument("--fixed-fee", type=float, default=2.0, + help="Fixed dollar fee per trade (default: $2)") + p_monitor.add_argument("--integer-shares", action="store_true", default=True, + help="Only trade whole shares (default: True)") # Status (strategy is a free-form string to allow sim_ prefixed names) p_status = sub.add_parser("status", help="Show current portfolio") @@ -1316,10 +1339,11 @@ def main(): p_log.add_argument("--end", default=None, help="End date filter (YYYY-MM-DD)") # Compare strategies - p_cmp = sub.add_parser("compare", help="Compare multiple strategies side-by-side") + p_cmp = sub.add_parser("compare", + help="Compare strategies side-by-side (auto-discovers all if no --strategy)") p_cmp.add_argument("--market", choices=UNIVERSES.keys(), default="us") - p_cmp.add_argument("--strategy", nargs="+", required=True, - help="Strategy state names to compare (e.g. recovery_mom_top10 momentum)") + p_cmp.add_argument("--strategy", nargs="+", default=None, + help="Strategy names to compare (default: auto-discover all)") args = parser.parse_args()