Files
quant-trading/tests/test_ibkr_trader.py
Gahow Wang fd44842570 live: IBKR order layer for trend_rider_v10 (guarded MOC orders via IB Gateway)
ibkr_trader.py computes the strategy's target for the close the way trader.py's
morning phase does, sizes whole-share orders on min(NAV, $10.5k) less a 1%
cash buffer against the account's real positions, and sends MOC orders,
sells before buys. Guards: the login must manage IBKR_ACCOUNT; a live account
needs --live and data/ibkr_live/ARMED; the account may hold only the
strategy's universe; MOC whole-share orders only; one submission per session.
After the close it records executions and commissions, scores each order
against the primary exchange's closing cross (SIP condition 6) and compares
NAV with the simulated account, with a preregistered verdict on the first
20 orders.

A what-if run on the paper Gateway (no orders) accepted GLD/SHY MOC orders
at $1.00 commission and rejected TQQQ/UPRO: the account lacks the Complex or
Leveraged ETP permission. The live service is defined but not enabled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:17:11 -07:00

75 lines
3.3 KiB
Python

"""Guards of the IBKR order layer (ibkr_trader.py): which accounts it may trade,
how much it sizes, and which orders it will send."""
import unittest
import ibkr_trader as it
class AccountGuard(unittest.TestCase):
def test_paper_account_trades_without_flags(self):
self.assertFalse(it.check_account(["DU123"], "DU123", live=False, armed=False))
def test_account_must_be_managed_by_the_login(self):
with self.assertRaises(SystemExit):
it.check_account(["DU123"], "U999", live=True, armed=True)
with self.assertRaises(SystemExit):
it.check_account(["U999"], "", live=True, armed=True)
def test_live_account_needs_flag_and_armed_file(self):
with self.assertRaises(SystemExit):
it.check_account(["U999"], "U999", live=False, armed=True)
with self.assertRaises(SystemExit):
it.check_account(["U999"], "U999", live=True, armed=False)
self.assertTrue(it.check_account(["U999"], "U999", live=True, armed=True))
def test_read_only_use_skips_the_trading_flags(self):
self.assertTrue(it.check_account(["U999"], "U999", live=False, armed=False, trading=False))
def test_live_account_number_is_masked(self):
self.assertEqual(it.mask("U1234567"), "U***567")
self.assertEqual(it.mask("DU123"), "DU123")
class Sizing(unittest.TestCase):
def test_orders_are_sized_on_the_capped_equity(self):
trades = it.size_orders({"TQQQ": 1.0}, {}, {"TQQQ": 100.0}, nav=972_000.0, resize_band=0.1)
self.assertEqual([t["ticker"] for t in trades], ["TQQQ"])
self.assertLessEqual(trades[0]["shares_delta"] * 100.0, it.MAX_EQUITY * (1 - it.CASH_BUFFER))
self.assertGreater(trades[0]["shares_delta"], 95)
def test_a_switch_sells_the_old_leg_and_buys_the_new(self):
trades = it.size_orders({"GLD": 1.0}, {"TQQQ": 100}, {"TQQQ": 100.0, "GLD": 300.0}, nav=10_000.0,
resize_band=0.1)
by = {t["ticker"]: t["shares_delta"] for t in trades}
self.assertEqual(by["TQQQ"], -100)
self.assertLessEqual(by["GLD"] * 300.0, 10_000.0 * (1 - it.CASH_BUFFER))
self.assertGreaterEqual(by["GLD"], 32)
def test_missing_price_stops_the_run(self):
with self.assertRaises(SystemExit):
it.size_orders({"TQQQ": 1.0}, {"GLD": 3}, {"TQQQ": 100.0}, nav=10_000.0, resize_band=0.1)
class OrderWhitelist(unittest.TestCase):
def test_only_universe_symbols_and_whole_shares(self):
with self.assertRaises(SystemExit):
it.validate_orders([{"ticker": "AAPL", "shares_delta": 1}], {"TQQQ"}, {})
with self.assertRaises(SystemExit):
it.validate_orders([{"ticker": "TQQQ", "shares_delta": 1.5}], {"TQQQ"}, {})
def test_never_sells_more_than_held(self):
with self.assertRaises(SystemExit):
it.validate_orders([{"ticker": "TQQQ", "shares_delta": -5}], {"TQQQ"}, {"TQQQ": 3})
it.validate_orders([{"ticker": "TQQQ", "shares_delta": -3}], {"TQQQ"}, {"TQQQ": 3})
def test_orders_are_market_on_close(self):
o = it.moc_order(-7, "DU123", "ibkr_trader 2026-10-13")
self.assertEqual((o.action, o.totalQuantity, o.orderType, o.tif, o.account), ("SELL", 7, "MOC", "DAY", "DU123"))
with self.assertRaises(ValueError):
it.moc_order(0, "DU123", "x")
if __name__ == "__main__":
unittest.main()