Overview
The engine.py module provides a paper trading engine that simulates order fills using real market data. It manages bot accounts, positions, and settlement without making actual trades.
PaperPosition Class
Represents a single open or settled paper position.
Fields
Unique 12-character position ID (auto-generated)
ID of the bot that owns this position
Number of contracts purchased
Price paid per contract in dollars
Total cost: contracts × entry_price
Market close time (for settlement scheduling)
Whether position has been settled
Market outcome: “yes”, “no”, or “timeout”
Payout from winning contracts (0 if lost)
Net profit: payout - cost
When position was settled
from genetic.engine import PaperPosition
from datetime import datetime, timezone
position = PaperPosition(
bot_id="bot_abc123",
market_ticker="KXBTC15M-24DEC05-T1030",
side="yes",
contracts=10,
entry_price=0.55,
cost=5.50,
close_time=datetime.now(timezone.utc)
)
print(f"Position: {position.contracts} {position.side} @ ${position.entry_price}")
print(f"Cost: ${position.cost:.2f}")
BotAccount Class
Paper trading account for one bot with P&L tracking.
Fields
Available cash for new trades
Dict of ticker → position for open positions
List of settled positions
Trades executed today (resets daily)
Today’s realized P&L (resets daily)
Last trade date YYYY-MM-DD for daily reset
Properties
equity
Total account value: cash + open position costs.
acct = BotAccount(bot_id="bot_123", initial_bankroll=100.0)
print(f"Equity: ${acct.equity:.2f}")
realized_pnl
Sum of all settled position profits.
print(f"Realized P&L: ${acct.realized_pnl:+.2f}")
roi_pct
Return on investment percentage based on realized P&L.
print(f"ROI: {acct.roi_pct:+.2f}%")
win_rate
Percentage of settled positions that were profitable.
print(f"Win rate: {acct.win_rate:.1%}")
n_settled
Number of settled positions.
print(f"Settled trades: {acct.n_settled}")
n_open
Number of currently open positions.
print(f"Open positions: {acct.n_open}")
Methods
unrealized_pnl(feed)
Estimate P&L of open positions using current bid prices.
Market data feed for current prices
unrealized = acct.unrealized_pnl(feed)
print(f"Unrealized P&L: ${unrealized:+.2f}")
total_pnl(feed)
Realized + unrealized P&L.
total = acct.total_pnl(feed)
print(f"Total P&L: ${total:+.2f}")
total_roi_pct(feed)
ROI including unrealized positions.
roi = acct.total_roi_pct(feed)
print(f"Total ROI: {roi:+.2f}%")
PaperTradingEngine Class
Simulates fills locally using real market data.
Constructor
Market data feed instance
from genetic.engine import PaperTradingEngine
from genetic.feed import MarketDataFeed
from kalshi_client import KalshiClient
client = KalshiClient()
feed = MarketDataFeed(client)
engine = PaperTradingEngine(feed)
Methods
create_account(bot_id, bankroll=100.0)
Create a new paper trading account for a bot.
acct = engine.create_account("bot_abc123", bankroll=100.0)
print(f"Created account with ${acct.cash:.2f} cash")
try_buy(bot_id, market_ticker, side, usd_amount)
Attempt to buy a position. Returns position if successful, None if rejected.
Target USD amount to invest
Created position, or None if rejected
Fill logic:
- Fill at displayed ask price (yes_ask or no_ask)
- Calculate integer contracts only
- Check available cash
- No duplicate positions in same market
position = engine.try_buy(
bot_id="bot_abc123",
market_ticker="KXBTC15M-24DEC05-T1030",
side="yes",
usd_amount=10.0
)
if position:
print(f"Bought {position.contracts} @ ${position.entry_price:.2f}")
print(f"Total cost: ${position.cost:.2f}")
else:
print("Order rejected (no cash, bad price, or duplicate)")
get_open_tickers()
Get all unique tickers with open positions across all accounts.
tickers = engine.get_open_tickers()
print(f"Open positions in {len(tickers)} markets")
get_closeable_tickers()
Get tickers whose markets have passed their close time.
Set of closeable market tickers
closeable = engine.get_closeable_tickers()
if closeable:
print(f"Markets ready to settle: {closeable}")
total_open_positions()
Total open positions across all accounts.
open_count = engine.total_open_positions()
print(f"{open_count} total open positions")
total_settled()
Total settled positions across all accounts.
Number of settled positions
settled_count = engine.total_settled()
print(f"{settled_count} settled positions")
settle_markets()
Check all open positions against cached settlement data.
Called each tick by main loop. Uses cached results from feed.
# Each tick
engine.settle_markets()
settle_with_targeted_check()
Check specific closeable tickers via API, then settle.
More efficient - only checks markets that are ready to settle.
# Every N ticks
engine.settle_with_targeted_check()
force_close_remaining()
Force-close all open positions as total losses.
Called after settlement timeout. Treats unsettled positions as losses for fitness calculation.
# After generation ends + settlement wait period
engine.force_close_remaining()
print(f"Forced closure of {engine.total_open_positions()} positions")
Example: Full Trading Cycle
from genetic.engine import PaperTradingEngine
from genetic.feed import MarketDataFeed
from kalshi_client import KalshiClient
import time
# Setup
client = KalshiClient()
feed = MarketDataFeed(client)
engine = PaperTradingEngine(feed)
# Create bot account
acct = engine.create_account("bot_test", bankroll=100.0)
print(f"Starting bankroll: ${acct.cash:.2f}")
# Update market data
feed.update()
# Place some trades
for ticker in list(feed.get_open_markets().keys())[:3]:
pos = engine.try_buy("bot_test", ticker, "yes", 10.0)
if pos:
print(f"Opened: {ticker} {pos.contracts} @ ${pos.entry_price:.2f}")
print(f"\nCash after trades: ${acct.cash:.2f}")
print(f"Open positions: {acct.n_open}")
print(f"Equity: ${acct.equity:.2f}")
# Wait and settle
time.sleep(60)
engine.settle_with_targeted_check()
print(f"\nSettled positions: {acct.n_settled}")
print(f"Win rate: {acct.win_rate:.1%}")
print(f"Realized P&L: ${acct.realized_pnl:+.2f}")
print(f"ROI: {acct.roi_pct:+.2f}%")