"""
OF-Trader — Pre-flight checklist (TESTNET ONLY).
================================================
Exercises the full order machinery on Binance Futures *testnet* with fake money:
1. signed /balance (proves API key + HMAC signing)
2. symbol info + set leverage
3. open a tiny position (entry path)
4. place TP + SL bracket (place_tp_market / place_sl_market)
5. market close + cancel all (exit path)
HARD GUARD: refuses to run unless BINANCE_TESTNET=true, so it can never fire
real orders on mainnet.
Run: ./venv/bin/python3 preflight.py [SYMBOL] (default BTCUSDT)
"""
import sys
import time
import logging
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
log = logging.getLogger("preflight")
from src.config import BINANCE_TESTNET, BINANCE_API_KEY
if not BINANCE_TESTNET:
log.error("REFUSING: BINANCE_TESTNET is not true. Pre-flight is testnet-only.")
sys.exit(2)
if not BINANCE_API_KEY:
log.error("No BINANCE_API_KEY set. Add testnet keys to ecosystem/env first.")
sys.exit(2)
from src.exchange import Exchange
SYMBOL = sys.argv[1] if len(sys.argv) > 1 else "BTCUSDT"
def main():
ex = Exchange()
ok = True
# 1. signed balance
try:
bal = ex.get_balance()
log.info(f"[1/5] ✓ signed balance OK: ${bal:.2f} USDT (testnet)")
except Exception as e:
log.error(f"[1/5] ✗ balance failed (signing?): {e}")
return 1
# 2. symbol info + leverage
si = ex.get_symbol_info(SYMBOL)
if not si:
log.error(f"[2/5] ✗ no symbol info for {SYMBOL}")
return 1
ex.set_leverage(SYMBOL)
log.info(f"[2/5] ✓ symbol info + leverage set ({SYMBOL}, tick={si['tick_size']}, minQty={si['min_qty']})")
# 3. open tiny position (market — plumbing test, not strategy)
mark = ex.get_mark_price(SYMBOL)
qty = ex.round_qty(si, max(si["min_qty"], 5.0 / mark)) # ~$5 notional
try:
order = ex.client.futures_create_order(
symbol=SYMBOL, side="BUY", type="MARKET", quantity=qty,
)
log.info(f"[3/5] ✓ opened {qty} {SYMBOL} @ market #{order.get('orderId')}")
except Exception as e:
log.error(f"[3/5] ✗ open failed: {e}")
return 1
time.sleep(1)
# 4. bracket TP + SL (exit side = SELL for a long)
tp = ex.place_tp_market(SYMBOL, "SELL", qty, mark * 1.02, si)
sl = ex.place_sl_market(SYMBOL, "SELL", qty, mark * 0.98, si)
open_orders = ex.get_open_orders(SYMBOL)
if tp and sl and len(open_orders) >= 2:
log.info(f"[4/5] ✓ bracket placed (TP+SL), {len(open_orders)} working orders")
else:
log.error(f"[4/5] ✗ bracket incomplete: tp={bool(tp)} sl={bool(sl)} working={len(open_orders)}")
ok = False
# 5. market close + cancel all
fill = ex.close_position_market(SYMBOL, "SELL", qty, si)
ex.cancel_all_orders(SYMBOL)
remaining = ex.get_open_orders(SYMBOL)
positions = [p for p in ex.get_positions() if p["symbol"] == SYMBOL]
if fill and not remaining and not positions:
log.info(f"[5/5] ✓ closed @ {fill}, no leftover orders/positions")
else:
log.error(f"[5/5] ✗ cleanup incomplete: fill={fill} orders={len(remaining)} pos={len(positions)}")
ok = False
log.info("PRE-FLIGHT PASSED ✓" if ok else "PRE-FLIGHT FAILED ✗")
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())
📜 Git History
227e67bfeat(of-trader): testnet preflight checklist (chunk 4)4 weeks ago