From 4dcfc79635552c593996f4c3814fba7cd3ae3e11 Mon Sep 17 00:00:00 2001 From: drjones Date: Fri, 27 Feb 2026 02:35:22 -0800 Subject: [PATCH] Fix forced-paper execution semantics and add minimum forced notional control --- .env.example | 1 + bot.py | 30 ++++++++++++++++-------------- config.py | 1 + 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/.env.example b/.env.example index ce033e0..6975ea2 100644 --- a/.env.example +++ b/.env.example @@ -14,6 +14,7 @@ TRADE_INTERVAL_HOURS=2 CURATE_INTERVAL_MINUTES=30 TIMEZONE=America/Los_Angeles FORCE_PAPER_TRADES=true +FORCE_PAPER_MIN_USD=10 OLLAMA_URL=http://10.30.20.110:11434 OLLAMA_CURATOR_MODEL=gemma3:latest diff --git a/bot.py b/bot.py index d45aaa3..3d2d0ea 100644 --- a/bot.py +++ b/bot.py @@ -69,6 +69,12 @@ def run_cycle(): memory_hits = qdrant_similar(symbol, json.dumps(news)[:2000], limit=5) decision = llm_final_decision(symbol, news, strat, memory_hits) + if settings.paper_mode and settings.force_paper_trades and decision["action"] == "hold": + decision["action"] = "buy" + decision["confidence"] = max(decision.get("confidence", 0.0), settings.min_confidence) + decision["order_usd"] = max(decision.get("order_usd", 0.0), settings.force_paper_min_usd) + decision["reason"] = f"{decision.get('reason','')} | force_paper_trades" + drow = BotDecision( symbol=symbol, action=decision["action"], @@ -82,11 +88,6 @@ def run_cycle(): db.commit() db.refresh(drow) - if settings.paper_mode and settings.force_paper_trades and decision["action"] == "hold": - decision["action"] = "buy" - decision["confidence"] = max(decision.get("confidence", 0.0), settings.min_confidence) - decision["reason"] = f"{decision.get('reason','')} | force_paper_trades" - should_trade = decision["action"] in {"buy", "sell"} and decision["confidence"] >= settings.min_confidence if should_trade: @@ -103,15 +104,16 @@ def run_cycle(): drow.status = "executed" if ok else "failed" db.add(drow) - trade = TradeExecution( - symbol=symbol, - side=decision["action"], - qty=float((res or {}).get("json", {}).get("qty", 0) or 0), - notional=notional, - alpaca_order_id=(res or {}).get("json", {}).get("id", ""), - raw=json.dumps({"decision": decision, "strategy": strat, "memory": memory_hits, "broker": res})[:60000], - ) - db.add(trade) + if ok: + trade = TradeExecution( + symbol=symbol, + side=decision["action"], + qty=float((res or {}).get("json", {}).get("qty", 0) or 0), + notional=notional, + alpaca_order_id=(res or {}).get("json", {}).get("id", ""), + raw=json.dumps({"decision": decision, "strategy": strat, "memory": memory_hits, "broker": res})[:60000], + ) + db.add(trade) db.commit() # learning memory + notes + orchestration signal diff --git a/config.py b/config.py index 64707fd..54b6dfb 100644 --- a/config.py +++ b/config.py @@ -21,6 +21,7 @@ class Settings: curate_interval_minutes = int(os.getenv("CURATE_INTERVAL_MINUTES", "30")) timezone = os.getenv("TIMEZONE", "America/Los_Angeles") force_paper_trades = os.getenv("FORCE_PAPER_TRADES", "true").lower() == "true" + force_paper_min_usd = float(os.getenv("FORCE_PAPER_MIN_USD", "10")) ollama_url = os.getenv("OLLAMA_URL", "http://10.30.20.110:11434") ollama_curator_model = os.getenv("OLLAMA_CURATOR_MODEL", "gemma3:latest")