← back to Demo Longshot Bot
executor.py
88 lines
"""
executor.py — the money boundary.
Two executors, one interface (open_position / close_position):
PaperExecutor — default. Writes simulated fills to the ledger. No key, $0,
no real order ever leaves the machine.
LiveExecutor — GATED. Refuses to place a real order unless ALL of:
1. env LIVE=1
2. env SAFE_MODE=0 (safe_mode honored — see below)
3. a confirmation token file exists (data/LIVE_CONFIRMED)
4. a real broker key is present
AND — even then — this template does not ship a wired broker
call. Going live is a Steve-only step: draft a go-live memo to
~/.claude/yolo-queue/pending-approval/, implement the broker
call, then remove this guard. This is deliberate: a scaffold
should never be one typo away from spending real money.
Why safe_mode matters: an earlier autoTrader in the Ken/Kalshi dashboard once
placed orders while safe_mode was on. Here safe_mode is a hard precondition, not
a suggestion — LiveExecutor treats SAFE_MODE!=0 as "do not trade".
"""
import os
import ledger
class PaperExecutor:
mode = "paper"
def open_position(self, market, side, size_usd, reason):
return ledger.record_fill(
market["id"], market["title"], side, "open",
market["price"], size_usd, reason, self.mode,
)
def close_position(self, position, mark_price, reason):
return ledger.record_fill(
position["market_id"], position["title"], position["side"], "close",
mark_price, position["size_usd"], reason, self.mode,
)
class LiveExecutor:
mode = "live"
def __init__(self, venue):
self.venue = venue
self._assert_gates()
@staticmethod
def _assert_gates():
problems = []
if os.environ.get("LIVE") != "1":
problems.append("LIVE!=1")
if os.environ.get("SAFE_MODE", "1") != "0":
problems.append("SAFE_MODE!=0 (safe_mode is ON — refusing to trade)")
token = os.path.join(os.path.dirname(__file__), "data", "LIVE_CONFIRMED")
if not os.path.exists(token):
problems.append("missing data/LIVE_CONFIRMED confirmation token")
if not (os.environ.get("POLYMARKET_API_KEY") or os.environ.get("KALSHI_API_KEY")):
problems.append("no broker API key in env")
if problems:
raise PermissionError(
"LIVE trading blocked — unmet safety gates: " + "; ".join(problems)
+ ".\nGoing live is Steve-gated: draft a go-live memo to "
"~/.claude/yolo-queue/pending-approval/, wire the broker call, "
"and clear these gates deliberately. See references/safety-model.md."
)
def open_position(self, market, side, size_usd, reason):
raise NotImplementedError(
"Live broker order not wired in the template — implement the venue "
"order call here, then get Steve's approval. Gates passed, but the "
"money path is intentionally left unimplemented."
)
def close_position(self, position, mark_price, reason):
raise NotImplementedError("Live broker close not wired — see open_position.")
def get_executor(mode, venue):
if mode == "live":
return LiveExecutor(venue)
return PaperExecutor()