#!/usr/bin/env python3 """PINK DITAv2 Live BingX Testnet E2E — 142 combinatorial scenarios including restart/reconcile and chaos/fuzz. Kernel-direct tests: bodies receive (k, symbol, p). Capital integrity asserted. Exchange state confirmed flat. """ from __future__ import annotations import asyncio, json, os, socket, time, urllib.request import urllib.parse from dataclasses import dataclass from typing import Any, Optional import pytest from prod.bingx.http import BingxHttpClient from prod.bingx.config import BingxExecClientConfig, BingxEnvironment from prod.clean_arch.dita_v2.launcher import build_launcher_bundle from prod.clean_arch.dita_v2.contracts import ( KernelCommandType as KC, KernelIntent as KI, TradeSide as TS, VenueEvent, VenueEventStatus, KernelEventKind, TradeStage, KernelDiagnosticCode, KernelSeverity, KernelOutcome, KernelTransition, TradeSlot, VenueOrder, ) from prod.clean_arch.ports.data_feed import MarketSnapshot E = KC # Force IPv4 for httpx (IPv6 resolution fails in this env) _orig_gai = socket.getaddrinfo def _ipv4_gai(host, port, family=0, type=0, proto=0, flags=0): return _orig_gai(host, port, socket.AF_INET, type, proto, flags) socket.getaddrinfo = _ipv4_gai # ---- env gates ---- if not os.environ.get("BINGX_SMOKE_LIVE"): pytest.skip("BINGX_SMOKE_LIVE not set", allow_module_level=True) if not os.environ.get("BINGX_SMOKE_ALLOW_TRADE"): pytest.skip("BINGX_SMOKE_ALLOW_TRADE not set", allow_module_level=True) if not os.environ.get("PINK_DITA_E2E"): pytest.skip("PINK_DITA_E2E not set", allow_module_level=True) # Inter-test rate-limit throttle _last_finish: float = 0.0 def _throttle(min_gap: float = 3.0) -> None: """Enforce minimum wall-clock gap between consecutive tests.""" global _last_finish now = __import__("time").time() elapsed = now - _last_finish if elapsed < min_gap: __import__("time").sleep(min_gap - elapsed) _last_finish = __import__("time").time() # ---- helpers ---- @dataclass class VR: symbol: str; positions_flat: bool = True; error: str = "" @dataclass class RB: runtime: Any; config: Any def _build_config(ic: float = 25000.0) -> BingxExecClientConfig: return BingxExecClientConfig( api_key=os.environ["BINGX_API_KEY"], secret_key=os.environ["BINGX_SECRET_KEY"], environment=BingxEnvironment.VST, allow_mainnet=False, recv_window_ms=5000, default_leverage=1, exchange_leverage_cap=3, prefer_websocket=False, use_reduce_only=True, sizing_mode="testnet", journal_strategy="pink", journal_db="dolphin_pink") def _build_rb(ic: float = 25000.0, max_slots: int = 1) -> RB: cfg = _build_config(ic) b = build_launcher_bundle(venue_mode="BINGX", max_slots=max_slots, bingx_config=cfg) k = b.kernel; k.account.snapshot.capital = ic; k.account.snapshot.peak_capital = ic; k.account.snapshot.equity = ic class Shim: def __init__(self, k): self.kernel = k async def connect(self, initial_capital=0): self.kernel.venue.connect() async def disconnect(self): try: self.kernel.venue.disconnect() except: pass return RB(runtime=Shim(k), config=cfg) def _build_portfolio_rb(ic: float = 25000.0, max_slots: int = 2) -> RB: return _build_rb(ic=ic, max_slots=max_slots) def _inspect_outcome(r, label): info = { "accepted": r.accepted, "state": r.state.value if r.state else "", "diagnostic": r.diagnostic_code.value if r.diagnostic_code else "", "severity": r.severity.value if r.severity else "", "transitions": [(t.prev_state.value, t.next_state.value) for t in (r.transitions or ())], "event_kinds": [e.kind.value for e in (r.emitted_events or ())], "details": dict(r.details or {}), } return info def _assert_accepted(r, label): info = _inspect_outcome(r, label) assert r.accepted, f"{label}: intent rejected - diag={info['diagnostic']} state={info['state']} detail={info['details']}" def _assert_rejected(r, expected_diag, label): info = _inspect_outcome(r, label) assert not r.accepted, f"{label}: expected rejection but got accepted state={info['state']}" assert info['diagnostic'] == expected_diag, f"{label}: expected diag={expected_diag} got {info['diagnostic']} detail={info['details']}" def _check_slot_accounting(k, label): start_cap = getattr(k, '_start_cap', None) if start_cap is None: return total_rp = sum(k.slot(i).realized_pnl for i in range(k.max_slots)) total_up = sum(k.slot(i).unrealized_pnl for i in range(k.max_slots)) expected = start_cap + total_rp + total_up actual = k.account.snapshot.capital diff = abs(actual - expected) assert diff < 0.01, f"{label}: accounting mismatch cap={actual} exp={expected} rp={total_rp} upnl={total_up} diff={diff}" async def _check_open_orders(c, vs): r = await c._request_json( "GET", "/openApi/swap/v2/trade/openOrders", {"symbol": vs}, signed=True ) data = r if isinstance(r, list) else (r.get("data") or r.get("orders") or []) return [o for o in data if isinstance(o, dict)] async def _verify_full(c, vs): rs = await _contract_rows(c) tr = [r for r in rs if str(r.get("symbol","")).upper().replace("-","") == vs.replace("-","").upper()] ts = sum(abs(float(r.get("positionAmt",r.get("positionQty",0)) or 0)) for r in tr) flat = ts < 1e-8 oos = await _check_open_orders(c, vs) no_orders = len(oos) == 0 err = "" if not flat: err += f"pos_open: {tr} " if not no_orders: err += f"open_orders: {oos} " return {"symbol": vs, "flat": flat, "no_orders": no_orders, "error": err.strip()} def _build_fresh_kernel_from_slot(slot_data, ic=25000.0): from prod.clean_arch.dita_v2.rust_backend import _slot_from_payload cfg = _build_config(ic) b = build_launcher_bundle(venue_mode="BINGX", max_slots=1, bingx_config=cfg) k = b.kernel; k.account.snapshot.capital = ic; k.account.snapshot.peak_capital = ic; k.account.snapshot.equity = ic restored = _slot_from_payload(slot_data) k.reconcile_from_slots([restored]) class Shim: def __init__(self, k): self.kernel = k async def connect(self, initial_capital=0): self.kernel.venue.connect() async def disconnect(self): try: self.kernel.venue.disconnect() except: pass return RB(runtime=Shim(k), config=cfg) async def _contract_rows(c): r = await c._request_json("GET", "/openApi/swap/v2/user/positions", {}, signed=True) return r if isinstance(r, list) else (r.get("data") or r.get("positions") or []) async def _pick_sym(k, c): rs = await _contract_rows(c) oss = {str(r.get("symbol","")).replace("-","").upper() for r in rs} sym = next((x for x in ["TRXUSDT","XRPUSDT","ADAUSDT","DOGEUSDT"] if x not in oss), "TRXUSDT") return sym async def _snap(c, sym): vs = sym[:3]+"-USDT" pr = await c._request_json("GET", "/openApi/swap/v2/quote/price", {"symbol": vs}, signed=False) d = pr.get("data") or pr; rp = float(d.get("price") or d.get("lastPrice") or 0) return MarketSnapshot(timestamp=__import__("datetime").datetime.now(__import__("datetime").timezone.utc), symbol=sym, price=rp, bid=rp*0.9995, ask=rp*1.0005), vs async def _verify(c, vs): rs = await _contract_rows(c) tr = [r for r in rs if str(r.get("symbol","")).upper().replace("-","") == vs.replace("-","").upper()] ts = sum(abs(float(r.get("positionAmt",r.get("positionQty",0)) or 0)) for r in tr) flat = ts < 1e-8 oos = await _check_open_orders(c, vs) no_orders = len(oos) == 0 err = "" if not flat: err += f"pos_open: {tr} " if not no_orders: err += f"open_orders: {oos} " return VR(symbol=vs, positions_flat=flat and no_orders, error=err.strip()) def _si(k, act, tid, asset, side_str, price, size, **kw): ds = TS.SHORT if side_str.upper() == "SHORT" else TS.LONG slot_id = kw.pop("slot_id", 0) return k.process_intent(KI( timestamp=__import__("datetime").datetime.now(__import__("datetime").timezone.utc), intent_id=tid, trade_id=tid, slot_id=slot_id, asset=asset, side=ds, action=act, reference_price=price, target_size=size, leverage=kw.pop("leverage",1.0), exit_leg_ratios=kw.pop("exit_leg_ratios",(1.0,)), reason=kw.pop("reason",f"auto_{act.value.lower()}"), metadata=kw)) def _flatten(k, sym, price, label, slot_id=0): if k.slot(slot_id).is_free(): return # Try both sides — kernel accepts whichever matches the open position ts = int(time.time()*1000) _si(k, E.EXIT, f"fl{label}-{ts}", sym, "SHORT", price, 0.001, slot_id=slot_id) if not k.slot(slot_id).is_free(): _si(k, E.EXIT, f"fl{label}b-{ts}", sym, "LONG", price, 0.001, slot_id=slot_id) async def _run(bundle, client, body_fn, label, ic): k = bundle.runtime.kernel sym = await _pick_sym(k, client) snap, vsym = await _snap(client, sym) await bundle.runtime.connect(initial_capital=ic) p = float(snap.price) try: for si in range(k.max_slots): if not k.slot(si).is_free(): _flatten(k, sym, p*0.99 if si == 0 else p*1.005, f"{label}-pre-{si}") await asyncio.sleep(0.3) k._start_cap = k.account.snapshot.capital cb = k.account.snapshot.capital await body_fn(k, sym, p) ca = k.account.snapshot.capital assert ca > 0, f"Capital zero: {ca}" max_change = max(1.0, cb * 0.10) assert cb - ca < max_change, f"Capital shrunk beyond tolerance: {cb} -> {ca} (limit={max_change})" total_rp = sum(k.slot(i).realized_pnl for i in range(k.max_slots)) if abs(total_rp) > 0.0001: assert abs(total_rp) < abs(cb - ca) + 0.01, f"{label}: rp={total_rp} != cap_change={cb-ca}" for si in range(k.max_slots): if not k.slot(si).is_free(): _flatten(k, sym, p*0.99 if si == 0 else p*1.005, f"{label}-post-{si}") await asyncio.sleep(1.0) _throttle(3.0) return await _verify(client, vsym) finally: await bundle.runtime.disconnect() # ===================================================================== # Scenario bodies # ===================================================================== async def _body_simple_entry_exit(k, symbol, p): tid = f's-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(1) async def _body_multi_leg_exit(k, symbol, p): tid = f'ml-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.002, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.993, 0.001, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(1) async def _body_cancel_entry_order(k, symbol, p): tid = f'ce-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(1) async def _body_entry_hold_exit(k, symbol, p): tid = f'h-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(3) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(1) async def _body_entry_exit_at_loss(k, symbol, p): tid = f'l-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*1.005, 0.001); await asyncio.sleep(1) async def _body_two_sequential_cycles(k, symbol, p): t1 = f'2c1-{int(time.time()*1000)}'; t2 = f'2c2-{int(time.time()*1000)}' _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(1) _si(k, E.EXIT, t1, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(1) _si(k, E.ENTER, t2, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(1) _si(k, E.EXIT, t2, symbol, 'SHORT', p*0.99, 0.001); await asyncio.sleep(1) async def _body_entry_then_recover(k, symbol, p): tid = f'r-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(1) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(1) async def _body_long_entry_exit(k, symbol, p): tid = f'ln-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(1) async def _body_cancel_idempotent(k, symbol, p): tid = f'ci-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(1) async def _body_double_cancel(k, symbol, p): tid = f'dc-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(1) async def _body_cancel_then_exit(k, symbol, p): tid = f'ctx-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(1) async def _body_exit_then_cancel_exit(k, symbol, p): tid = f'exc-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(1) async def _body_exit_then_reentry(k, symbol, p): t1 = f'er1-{int(time.time()*1000)}'; t2 = f'er2-{int(time.time()*1000)}' _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(1) _si(k, E.EXIT, t1, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, t2, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(1) async def _body_limit_cancel(k, symbol, p): tid = f'lc-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p*0.9, 0.001); await asyncio.sleep(0.5) _si(k, E.CANCEL, tid, symbol, 'SHORT', p*0.9, 0.001); await asyncio.sleep(1) async def _body_x4_partial_hold_exit(k, symbol, p): tid = f'ph-{int(time.time()*1000)}'; sz = 0.003 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.3,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, sz*0.3, exit_leg_ratios=(0.3,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.993, sz*0.7, exit_leg_ratios=(0.3,1.0)); await asyncio.sleep(1) async def _body_x4_three_leg(k, symbol, p): tid = f'3l-{int(time.time()*1000)}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.25,0.25,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, sz*0.25, exit_leg_ratios=(0.25,0.25,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.993, sz*0.25, exit_leg_ratios=(0.25,0.25,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*0.5, exit_leg_ratios=(0.25,0.25,1.0)); await asyncio.sleep(1) async def _body_x4_cancel_fill_partial(k, symbol, p): tid = f'cfp-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.002); await asyncio.sleep(0.5) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.002); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.993, 0.001); await asyncio.sleep(1) async def _body_x4_rapid_three(k, symbol, p): for i in range(3): tid = f'r3-{i}-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p*(1-i*0.005), 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-i*0.005), 0.001); await asyncio.sleep(0.8) async def _body_x4_diff_symbol(k, symbol, p): tid = f'ds-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(1) sym2 = 'BTCUSDT' if symbol != 'BTCUSDT' else 'ETHUSDT' _si(k, E.EXIT, tid, sym2, 'SHORT', p, 0.001); await asyncio.sleep(0.5) async def _body_x4_alternating(k, symbol, p): t1 = f'as1-{int(time.time()*1000)}'; t2 = f'as2-{int(time.time()*1000)}' _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(1) sym2 = 'BTCUSDT' if symbol != 'BTCUSDT' else 'ETHUSDT' try: p2 = float(json.loads(urllib.request.urlopen('https://open-api-vst.bingx.com/openApi/swap/v2/quote/price?symbol='+sym2.replace('USDT','-USDT'), timeout=5).read())['data']['price']) except: p2 = p _si(k, E.ENTER, t2, sym2, 'LONG', p2, 0.001); await asyncio.sleep(1) _si(k, E.EXIT, t1, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(1) _si(k, E.EXIT, t2, sym2, 'LONG', p2*1.005, 0.001); await asyncio.sleep(1) async def _body_x4_multi_flatten(k, symbol, p): tid = f'mf-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(1) for i in range(3): if k.slot(0).is_free(): break _flatten(k, symbol, p*0.99, f'mf{i}'); await asyncio.sleep(0.5) async def _body_x4_three_leg_25_50_25(k, symbol, p): tid = f'x4a-{int(time.time()*1000)}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.25,0.5,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, sz*0.25, exit_leg_ratios=(0.25,0.5,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.993, sz*0.5, exit_leg_ratios=(0.25,0.5,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*0.25, exit_leg_ratios=(0.25,0.5,1.0)); await asyncio.sleep(1) async def _body_x4_enter_exit_hold_twice(k, symbol, p): t1 = f'x4b1-{int(time.time()*1000)}' _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) _si(k, E.EXIT, t1, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) t2 = f'x4b2-{int(time.time()*1000)}' _si(k, E.ENTER, t2, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) _si(k, E.EXIT, t2, symbol, 'SHORT', p*0.99, 0.001); await asyncio.sleep(0.5) t3 = f'x4b3-{int(time.time()*1000)}' _si(k, E.ENTER, t3, symbol, 'SHORT', p*0.99, 0.001); await asyncio.sleep(0.5) _si(k, E.EXIT, t3, symbol, 'SHORT', p*0.985, 0.001); await asyncio.sleep(0.5) async def _body_x4_cancel_then_double_exit(k, symbol, p): tid = f'x4c-{int(time.time()*1000)}'; sz = 0.002 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.5) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, sz); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, sz*0.5, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.5) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.993, sz*0.5, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.5) async def _body_basic_short_profit(k, symbol, p): tid = f'bsp-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.8) async def _body_partial_short_profit(k, symbol, p): tid = f'psp-{{int(time.time()*1000)}}' sz = 0.002 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, sz*0.5, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, sz*0.5, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) async def _body_cancel_short_profit(k, symbol, p): tid = f'csp-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.8) async def _body_double_exit_short_profit(k, symbol, p): tid = f'dsp-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*0.995, 0.001); await asyncio.sleep(0.5) async def _body_basic_short_loss(k, symbol, p): tid = f'bsl-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*1.0050251256281406, 0.001); await asyncio.sleep(0.8) async def _body_partial_short_loss(k, symbol, p): tid = f'psl-{{int(time.time()*1000)}}' sz = 0.002 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, sz*0.5, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*1.0050251256281406, sz*0.5, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) async def _body_cancel_short_loss(k, symbol, p): tid = f'csl-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*1.0050251256281406, 0.001); await asyncio.sleep(0.8) async def _body_double_exit_short_loss(k, symbol, p): tid = f'dsl-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*1.0050251256281406, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*1.0050251256281406*0.995, 0.001); await asyncio.sleep(0.5) async def _body_basic_long_profit(k, symbol, p): tid = f'blp-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.8) async def _body_partial_long_profit(k, symbol, p): tid = f'plp-{{int(time.time()*1000)}}' sz = 0.002 _si(k, E.ENTER, tid, symbol, 'LONG', p, sz, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, sz*0.5, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, sz*0.5, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) async def _body_cancel_long_profit(k, symbol, p): tid = f'clp-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.8) async def _body_double_exit_long_profit(k, symbol, p): tid = f'dlp-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005*0.995, 0.001); await asyncio.sleep(0.5) async def _body_basic_long_loss(k, symbol, p): tid = f'bll-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*0.9950248756218907, 0.001); await asyncio.sleep(0.8) async def _body_partial_long_loss(k, symbol, p): tid = f'pll-{{int(time.time()*1000)}}' sz = 0.002 _si(k, E.ENTER, tid, symbol, 'LONG', p, sz, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, sz*0.5, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*0.9950248756218907, sz*0.5, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) async def _body_cancel_long_loss(k, symbol, p): tid = f'cll-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'LONG', p*0.9950248756218907, 0.001); await asyncio.sleep(0.8) async def _body_double_exit_long_loss(k, symbol, p): tid = f'dll-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*0.9950248756218907, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'LONG', p*0.9950248756218907*0.995, 0.001); await asyncio.sleep(0.5) async def _body_triple_seq_0(k, symbol, p): for j in range(3): tid = f'ts0-j-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p*(1-j*0.003), 0.001); await asyncio.sleep(0.7) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-j*0.003), 0.001); await asyncio.sleep(0.7) async def _body_triple_seq_1(k, symbol, p): for j in range(3): tid = f'ts1-j-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p*(1-j*0.003), 0.001); await asyncio.sleep(0.7) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-j*0.003), 0.001); await asyncio.sleep(0.7) async def _body_triple_seq_2(k, symbol, p): for j in range(3): tid = f'ts2-j-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p*(1-j*0.003), 0.001); await asyncio.sleep(0.7) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-j*0.003), 0.001); await asyncio.sleep(0.7) async def _body_triple_seq_3(k, symbol, p): for j in range(3): tid = f'ts3-j-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p*(1-j*0.003), 0.001); await asyncio.sleep(0.7) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-j*0.003), 0.001); await asyncio.sleep(0.7) async def _body_triple_seq_long_0(k, symbol, p): for j in range(3): tid = f'tsl0-j-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p*(1+j*0.003), 0.001); await asyncio.sleep(0.7) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005*(1+j*0.003), 0.001); await asyncio.sleep(0.7) async def _body_triple_seq_long_1(k, symbol, p): for j in range(3): tid = f'tsl1-j-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p*(1+j*0.003), 0.001); await asyncio.sleep(0.7) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005*(1+j*0.003), 0.001); await asyncio.sleep(0.7) async def _body_triple_seq_long_2(k, symbol, p): for j in range(3): tid = f'tsl2-j-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p*(1+j*0.003), 0.001); await asyncio.sleep(0.7) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005*(1+j*0.003), 0.001); await asyncio.sleep(0.7) async def _body_triple_seq_long_3(k, symbol, p): for j in range(3): tid = f'tsl3-j-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p*(1+j*0.003), 0.001); await asyncio.sleep(0.7) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005*(1+j*0.003), 0.001); await asyncio.sleep(0.7) async def _body_cancel_reenter_0(k, symbol, p): t1 = f'cr0a-{{int(time.time()*1000)}}'; t2 = f'cr0b-{{int(time.time()*1000)}}' _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, t2, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.8) if not k.slot(0).is_free(): _si(k, E.EXIT, t2, symbol, 'SHORT', p*0.99, 0.001); await asyncio.sleep(0.5) async def _body_cancel_reenter_1(k, symbol, p): t1 = f'cr1a-{{int(time.time()*1000)}}'; t2 = f'cr1b-{{int(time.time()*1000)}}' _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, t2, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.8) if not k.slot(0).is_free(): _si(k, E.EXIT, t2, symbol, 'SHORT', p*0.99, 0.001); await asyncio.sleep(0.5) async def _body_cancel_reenter_2(k, symbol, p): t1 = f'cr2a-{{int(time.time()*1000)}}'; t2 = f'cr2b-{{int(time.time()*1000)}}' _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, t2, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.8) if not k.slot(0).is_free(): _si(k, E.EXIT, t2, symbol, 'SHORT', p*0.99, 0.001); await asyncio.sleep(0.5) async def _body_cancel_reenter_3(k, symbol, p): t1 = f'cr3a-{{int(time.time()*1000)}}'; t2 = f'cr3b-{{int(time.time()*1000)}}' _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, t2, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.8) if not k.slot(0).is_free(): _si(k, E.EXIT, t2, symbol, 'SHORT', p*0.99, 0.001); await asyncio.sleep(0.5) async def _body_cancel_reenter_long_0(k, symbol, p): t1 = f'crl0a-{{int(time.time()*1000)}}'; t2 = f'crl0b-{{int(time.time()*1000)}}' _si(k, E.ENTER, t1, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, t1, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, t2, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.8) if not k.slot(0).is_free(): _si(k, E.EXIT, t2, symbol, 'LONG', p*1.01, 0.001); await asyncio.sleep(0.5) async def _body_cancel_reenter_long_1(k, symbol, p): t1 = f'crl1a-{{int(time.time()*1000)}}'; t2 = f'crl1b-{{int(time.time()*1000)}}' _si(k, E.ENTER, t1, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, t1, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, t2, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.8) if not k.slot(0).is_free(): _si(k, E.EXIT, t2, symbol, 'LONG', p*1.01, 0.001); await asyncio.sleep(0.5) async def _body_cancel_reenter_long_2(k, symbol, p): t1 = f'crl2a-{{int(time.time()*1000)}}'; t2 = f'crl2b-{{int(time.time()*1000)}}' _si(k, E.ENTER, t1, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, t1, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, t2, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.8) if not k.slot(0).is_free(): _si(k, E.EXIT, t2, symbol, 'LONG', p*1.01, 0.001); await asyncio.sleep(0.5) async def _body_cancel_reenter_long_3(k, symbol, p): t1 = f'crl3a-{{int(time.time()*1000)}}'; t2 = f'crl3b-{{int(time.time()*1000)}}' _si(k, E.ENTER, t1, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, t1, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, t2, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.8) if not k.slot(0).is_free(): _si(k, E.EXIT, t2, symbol, 'LONG', p*1.01, 0.001); await asyncio.sleep(0.5) async def _body_leg_ratio_0(k, symbol, p): tid = f'lr0-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.1,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-0*0.002), sz*0.1, exit_leg_ratios=(0.1,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*1.0, exit_leg_ratios=(0.1,1.0)); await asyncio.sleep(0.8) async def _body_leg_ratio_1(k, symbol, p): tid = f'lr1-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-0*0.002), sz*0.33, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-1*0.002), sz*0.33, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*1.0, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.8) async def _body_leg_ratio_2(k, symbol, p): tid = f'lr2-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.5,0.5,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-0*0.002), sz*0.5, exit_leg_ratios=(0.5,0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-1*0.002), sz*0.5, exit_leg_ratios=(0.5,0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*1.0, exit_leg_ratios=(0.5,0.5,1.0)); await asyncio.sleep(0.8) async def _body_leg_ratio_3(k, symbol, p): tid = f'lr3-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.75,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-0*0.002), sz*0.75, exit_leg_ratios=(0.75,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*1.0, exit_leg_ratios=(0.75,1.0)); await asyncio.sleep(0.8) async def _body_leg_ratio_4(k, symbol, p): tid = f'lr4-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.2,0.3,0.5,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-0*0.002), sz*0.2, exit_leg_ratios=(0.2,0.3,0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-1*0.002), sz*0.3, exit_leg_ratios=(0.2,0.3,0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-2*0.002), sz*0.5, exit_leg_ratios=(0.2,0.3,0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*1.0, exit_leg_ratios=(0.2,0.3,0.5,1.0)); await asyncio.sleep(0.8) async def _body_leg_ratio_5(k, symbol, p): tid = f'lr5-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.4,0.6,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-0*0.002), sz*0.4, exit_leg_ratios=(0.4,0.6,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-1*0.002), sz*0.6, exit_leg_ratios=(0.4,0.6,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*1.0, exit_leg_ratios=(0.4,0.6,1.0)); await asyncio.sleep(0.8) async def _body_leg_ratio_6(k, symbol, p): tid = f'lr6-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.15,0.85,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-0*0.002), sz*0.15, exit_leg_ratios=(0.15,0.85,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-1*0.002), sz*0.85, exit_leg_ratios=(0.15,0.85,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*1.0, exit_leg_ratios=(0.15,0.85,1.0)); await asyncio.sleep(0.8) async def _body_leg_ratio_7(k, symbol, p): tid = f'lr7-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.25,0.25,0.5,1.0)); await asyncio.sleep(1) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-0*0.002), sz*0.25, exit_leg_ratios=(0.25,0.25,0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-1*0.002), sz*0.25, exit_leg_ratios=(0.25,0.25,0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-2*0.002), sz*0.5, exit_leg_ratios=(0.25,0.25,0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*1.0, exit_leg_ratios=(0.25,0.25,0.5,1.0)); await asyncio.sleep(0.8) async def _body_breakeven_0(k, symbol, p): tid = f'be0-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) async def _body_breakeven_1(k, symbol, p): tid = f'be1-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) async def _body_breakeven_2(k, symbol, p): tid = f'be2-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) async def _body_breakeven_3(k, symbol, p): tid = f'be3-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) # ===================================================================== # Test functions -- single parametrized entry point # ===================================================================== async def _body_short_exit_one_pct_profit(k, symbol, p): tid = f'short_exit_o-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, 0.001); await asyncio.sleep(0.8) async def _body_short_exit_third_pct_profit(k, symbol, p): tid = f'short_exit_t-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.997, 0.001); await asyncio.sleep(0.8) async def _body_short_exit_third_pct_loss(k, symbol, p): tid = f'short_exit_t-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*1.003, 0.001); await asyncio.sleep(0.8) async def _body_short_exit_one_pct_loss(k, symbol, p): tid = f'short_exit_o-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*1.01, 0.001); await asyncio.sleep(0.8) async def _body_long_exit_one_pct_profit(k, symbol, p): tid = f'long_exit_on-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.01, 0.001); await asyncio.sleep(0.8) async def _body_long_exit_third_pct_profit(k, symbol, p): tid = f'long_exit_th-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.003, 0.001); await asyncio.sleep(0.8) async def _body_long_exit_third_pct_loss(k, symbol, p): tid = f'long_exit_th-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*0.997, 0.001); await asyncio.sleep(0.8) async def _body_long_exit_one_pct_loss(k, symbol, p): tid = f'long_exit_on-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*0.99, 0.001); await asyncio.sleep(0.8) async def _body_entry_exit_short_2x_profit(k, symbol, p): tid = f'entry_exit_s-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001, leverage=2); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.8) async def _body_entry_exit_long_2x_profit(k, symbol, p): tid = f'entry_exit_l-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001, leverage=2); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.8) async def _body_entry_exit_short_3x_profit(k, symbol, p): tid = f'entry_exit_s-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001, leverage=3); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.8) async def _body_entry_exit_long_3x_profit(k, symbol, p): tid = f'entry_exit_l-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001, leverage=3); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.8) async def _body_entry_exit_short_2x_loss(k, symbol, p): tid = f'entry_exit_s-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001, leverage=2); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*1.005, 0.001); await asyncio.sleep(0.8) async def _body_entry_exit_long_2x_loss(k, symbol, p): tid = f'entry_exit_l-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001, leverage=2); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*0.995, 0.001); await asyncio.sleep(0.8) async def _body_entry_exit_short_3x_loss(k, symbol, p): tid = f'entry_exit_s-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001, leverage=3); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*1.005, 0.001); await asyncio.sleep(0.8) async def _body_entry_exit_long_3x_loss(k, symbol, p): tid = f'entry_exit_l-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001, leverage=3); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*0.995, 0.001); await asyncio.sleep(0.8) async def _body_entry_exit_short_2x_size(k, symbol, p): tid = f'entry_exit_s-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.002, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.002); await asyncio.sleep(0.8) async def _body_entry_exit_long_2x_size(k, symbol, p): tid = f'entry_exit_l-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.002, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, 0.002); await asyncio.sleep(0.8) async def _body_entry_exit_short_3x_size(k, symbol, p): tid = f'entry_exit_s-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.003, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.003); await asyncio.sleep(0.8) async def _body_entry_exit_long_3x_size(k, symbol, p): tid = f'entry_exit_l-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.003, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, 0.003); await asyncio.sleep(0.8) async def _body_entry_exit_short_4x_size(k, symbol, p): tid = f'entry_exit_s-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.004, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, 0.004); await asyncio.sleep(0.8) async def _body_entry_exit_long_4x_size(k, symbol, p): tid = f'entry_exit_l-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.004, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.01, 0.004); await asyncio.sleep(0.8) async def _body_entry_exit_short_5x_size(k, symbol, p): tid = f'entry_exit_s-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.005, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.987, 0.005); await asyncio.sleep(0.8) async def _body_entry_exit_long_5x_size(k, symbol, p): tid = f'entry_exit_l-{{int(time.time()*1000)}}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.005, leverage=1.0); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.013, 0.005); await asyncio.sleep(0.8) async def _body_three_cycle_short(k, symbol, p): for j in range(3): tid = f'tcs-{j}-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p*(1-j*0.003), 0.001); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-j*0.003), 0.001); await asyncio.sleep(0.6) async def _body_three_cycle_long(k, symbol, p): for j in range(3): tid = f'tcl-{j}-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'LONG', p*(1+j*0.003), 0.001); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005*(1+j*0.003), 0.001); await asyncio.sleep(0.6) async def _body_partial_ratio_0_short(k, symbol, p): tid = f'pr0s-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.5,0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, sz*0.5, exit_leg_ratios=(0.5,0.5,1.0)); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*0.5, exit_leg_ratios=(0.5,0.5,1.0)); await asyncio.sleep(0.6) async def _body_partial_ratio_0_long(k, symbol, p): tid = f'pr0l-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'LONG', p, sz, exit_leg_ratios=(0.5,0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, sz*0.5, exit_leg_ratios=(0.5,0.5,1.0)); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.01, sz*0.5, exit_leg_ratios=(0.5,0.5,1.0)); await asyncio.sleep(0.6) async def _body_partial_ratio_1_short(k, symbol, p): tid = f'pr1s-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, sz*0.33, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*0.6699999999999999, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.6) async def _body_partial_ratio_1_long(k, symbol, p): tid = f'pr1l-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'LONG', p, sz, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, sz*0.33, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.01, sz*0.6699999999999999, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.6) async def _body_partial_ratio_2_short(k, symbol, p): tid = f'pr2s-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.1,0.9,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, sz*0.1, exit_leg_ratios=(0.1,0.9,1.0)); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*0.9, exit_leg_ratios=(0.1,0.9,1.0)); await asyncio.sleep(0.6) async def _body_partial_ratio_2_long(k, symbol, p): tid = f'pr2l-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'LONG', p, sz, exit_leg_ratios=(0.1,0.9,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, sz*0.1, exit_leg_ratios=(0.1,0.9,1.0)); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.01, sz*0.9, exit_leg_ratios=(0.1,0.9,1.0)); await asyncio.sleep(0.6) async def _body_partial_ratio_3_short(k, symbol, p): tid = f'pr3s-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.25,0.25,0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, sz*0.25, exit_leg_ratios=(0.25,0.25,0.5,1.0)); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*0.75, exit_leg_ratios=(0.25,0.25,0.5,1.0)); await asyncio.sleep(0.6) async def _body_partial_ratio_3_long(k, symbol, p): tid = f'pr3l-{{int(time.time()*1000)}}'; sz = 0.004 _si(k, E.ENTER, tid, symbol, 'LONG', p, sz, exit_leg_ratios=(0.25,0.25,0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, sz*0.25, exit_leg_ratios=(0.25,0.25,0.5,1.0)); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.01, sz*0.75, exit_leg_ratios=(0.25,0.25,0.5,1.0)); await asyncio.sleep(0.6) async def _body_cross_asset_short(k, symbol, p): tid = f'cas-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.8) async def _body_cross_asset_long(k, symbol, p): tid = f'cal-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.8) async def _body_cancel_on_fill_short(k, symbol, p): tid = f'cofs-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) if not k.slot(0).is_free(): _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_cancel_on_fill_long(k, symbol, p): tid = f'cofl-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.5) if not k.slot(0).is_free(): _si(k, E.CANCEL, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.5) async def _body_entry_quick_exit_short(k, symbol, p): tid = f'eqs-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.8) async def _body_entry_quick_exit_long(k, symbol, p): tid = f'eql-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.8) async def _body_triple_leg_exit_short(k, symbol, p): tid = f'tles-{int(time.time()*1000)}'; sz = 0.003 _si(k, E.ENTER, tid, symbol, 'SHORT', p, sz, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, sz*0.33, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.993, sz*0.33, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.99, sz*0.34, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.6) async def _body_triple_leg_exit_long(k, symbol, p): tid = f'tlel-{int(time.time()*1000)}'; sz = 0.003 _si(k, E.ENTER, tid, symbol, 'LONG', p, sz, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.005, sz*0.33, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.007, sz*0.33, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.6) _si(k, E.EXIT, tid, symbol, 'LONG', p*1.01, sz*0.34, exit_leg_ratios=(0.33,0.33,1.0)); await asyncio.sleep(0.6) async def _body_cancel_reenter_exit_short(k, symbol, p): t1 = f'cresa-{int(time.time()*1000)}'; t2 = f'cresb-{int(time.time()*1000)}' _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, t2, symbol, 'SHORT', p*0.999, 0.001); await asyncio.sleep(0.5) if not k.slot(0).is_free(): _si(k, E.EXIT, t2, symbol, 'SHORT', p*0.99, 0.001); await asyncio.sleep(0.5) async def _body_cancel_reenter_exit_long(k, symbol, p): t1 = f'crela-{int(time.time()*1000)}'; t2 = f'crelb-{int(time.time()*1000)}' _si(k, E.ENTER, t1, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, t1, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, t2, symbol, 'LONG', p*1.001, 0.001); await asyncio.sleep(0.5) if not k.slot(0).is_free(): _si(k, E.EXIT, t2, symbol, 'LONG', p*1.01, 0.001); await asyncio.sleep(0.5) async def _body_zero_capital_safety(k, symbol, p): tid = f'zc-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) async def _body_position_survives_exit(k, symbol, p): tid = f'pse-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_double_entry_prevention(k, symbol, p): t1 = f'dpe1-{int(time.time()*1000)}'; t2 = f'dpe2-{int(time.time()*1000)}' _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) _si(k, E.ENTER, t2, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) if not k.slot(0).is_free(): _si(k, E.EXIT, t1, symbol, 'SHORT', p*0.99, 0.001); await asyncio.sleep(0.5) async def _body_negative_capital_check(k, symbol, p): tid = f'ncc-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) _si(k, E.EXIT, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) async def _body_reconcile_empty(k, symbol, p): k.reconcile_from_slots([]); await asyncio.sleep(0.3) async def _body_reconcile_after_entry(k, symbol, p): tid = f're-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) k.reconcile_from_slots([k.slot(0)]); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_reconcile_after_exit(k, symbol, p): tid = f'rx-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) k.reconcile_from_slots([k.slot(0)]); await asyncio.sleep(0.3) async def _body_reconcile_after_cancel(k, symbol, p): tid = f'rcn-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) if not k.slot(0).is_free(): _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) k.reconcile_from_slots([k.slot(0)]); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_reconcile_twice(k, symbol, p): tid = f'rtw-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) k.reconcile_from_slots([k.slot(0)]); await asyncio.sleep(0.3) k.reconcile_from_slots([k.slot(0)]); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_reconcile_then_cancel(k, symbol, p): tid = f'rtc-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) k.reconcile_from_slots([k.slot(0)]); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_concurrent_enter_cancel(k, symbol, p): tid = f'cc-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_rapid_alternating(k, symbol, p): t1 = f'ras-{int(time.time()*1000)}' _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.2) _si(k, E.CANCEL, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.2) if not k.slot(0).is_free(): _si(k, E.EXIT, t1, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.3) t2 = f'ral-{int(time.time()*1000)}' _si(k, E.ENTER, t2, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.2) _si(k, E.CANCEL, t2, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.2) if not k.slot(0).is_free(): _si(k, E.EXIT, t2, symbol, 'LONG', p*1.005, 0.001); await asyncio.sleep(0.3) async def _body_duplicate_trade_id(k, symbol, p): tid = f'dt-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_slot_busy_double_entry(k, symbol, p): t1 = f'sb1-{int(time.time()*1000)}'; t2 = f'sb2-{int(time.time()*1000)}' _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.ENTER, t2, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, t1, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_exit_on_idle_slot(k, symbol, p): _si(k, E.EXIT, f'exidle-{int(time.time()*1000)}', symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) tid = f'eoi-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_cancel_on_idle_slot(k, symbol, p): _si(k, E.CANCEL, f'coi-{int(time.time()*1000)}', symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) tid = f'cis-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_rapid_ten_cycle(k, symbol, p): for i in range(10): tid = f'rc10-{i}-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p*(1-i*0.001), 0.001); await asyncio.sleep(0.4) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995*(1-i*0.001), 0.001); await asyncio.sleep(0.4) else: break async def _body_multi_slot_enter_exit(k, symbol, p): t0 = f'ms0-{int(time.time()*1000)}'; t1 = f'ms1-{int(time.time()*1000)}' _si(k, E.ENTER, t0, symbol, 'SHORT', p, 0.001, slot_id=0); await asyncio.sleep(0.4) _si(k, E.ENTER, t1, symbol, 'LONG', p, 0.001, slot_id=1); await asyncio.sleep(0.4) _si(k, E.EXIT, t0, symbol, 'SHORT', p*0.995, 0.001, slot_id=0); await asyncio.sleep(0.4) _si(k, E.EXIT, t1, symbol, 'LONG', p*1.005, 0.001, slot_id=1); await asyncio.sleep(0.4) async def _body_multi_slot_cross_cancel(k, symbol, p): t0 = f'msx0-{int(time.time()*1000)}'; t1 = f'msx1-{int(time.time()*1000)}' _si(k, E.ENTER, t0, symbol, 'SHORT', p, 0.001, slot_id=0); await asyncio.sleep(0.3) _si(k, E.ENTER, t1, symbol, 'LONG', p, 0.001, slot_id=1); await asyncio.sleep(0.3) _si(k, E.CANCEL, t0, symbol, 'SHORT', p, 0.001, slot_id=0); await asyncio.sleep(0.3) _si(k, E.CANCEL, t1, symbol, 'LONG', p, 0.001, slot_id=1); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, t0, symbol, 'SHORT', p*0.995, 0.001, slot_id=0); await asyncio.sleep(0.3) if not k.slot(1).is_free(): _si(k, E.EXIT, t1, symbol, 'LONG', p*1.005, 0.001, slot_id=1); await asyncio.sleep(0.3) async def _body_multi_slot_rapid_cycle(k, symbol, p): for i in range(5): t0 = f'msc0-{i}-{int(time.time()*1000)}'; t1 = f'msc1-{i}-{int(time.time()*1000)}' _si(k, E.ENTER, t0, symbol, 'SHORT', p*(1-i*0.002), 0.001, slot_id=0); await asyncio.sleep(0.3) _si(k, E.ENTER, t1, symbol, 'LONG', p*(1+i*0.002), 0.001, slot_id=1); await asyncio.sleep(0.3) _si(k, E.EXIT, t0, symbol, 'SHORT', p*0.995*(1-i*0.002), 0.001, slot_id=0); await asyncio.sleep(0.3) _si(k, E.EXIT, t1, symbol, 'LONG', p*1.005*(1+i*0.002), 0.001, slot_id=1); await asyncio.sleep(0.3) async def _body_reject_wrong_symbol(k, symbol, p): tid = f'rs-{int(time.time()*1000)}' _si(k, E.ENTER, tid, 'ZZZUSDT', 'SHORT', 0.001, 0.001); await asyncio.sleep(0.5) _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_reject_zero_size(k, symbol, p): tid = f'rz-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.0); await asyncio.sleep(0.3) _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_reject_side_mismatch_cancel(k, symbol, p): tid = f'rsm-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.CANCEL, tid, symbol, 'LONG', p, 0.001); await asyncio.sleep(0.3) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_reject_negative_price(k, symbol, p): tid = f'rn-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', -1.0, 0.001); await asyncio.sleep(0.5) _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_snapshot_restore_empty(k, symbol, p): s = k.snapshot(); await asyncio.sleep(0.1) j = json.dumps(s); _ = json.loads(j) tid = f'sre-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_snapshot_restore_mid_trade(k, symbol, p): tid = f'srm-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) s = k.snapshot(); await asyncio.sleep(0.1) j = json.dumps(s); _ = json.loads(j) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_snapshot_restore_after_cancel(k, symbol, p): tid = f'src-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.5) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) s = k.snapshot(); await asyncio.sleep(0.1) j = json.dumps(s); _ = json.loads(j) if not k.slot(0).is_free(): _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_fresh_kernel_reconcile_entry(k, symbol, p): import time as _t tid = f"fk-{int(_t.time()*1000)}" _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) slot_data = k.slot(0).to_dict() cb = k.account.snapshot.capital fresh = _build_fresh_kernel_from_slot(slot_data, ic=cb) k2 = fresh.runtime.kernel s = k2.slot(0) assert not s.is_free(), f"fresh kernel slot should not be free: {s.fsm_state}" assert s.trade_id == tid, f"trade_id mismatch: {s.trade_id} vs {tid}" _si(k2, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) assert k2.slot(0).is_free(), "fresh kernel slot not free after exit" assert abs(k2.account.snapshot.capital - cb) < 0.01, f"capital drift: {k2.account.snapshot.capital} vs {cb}" async def _body_fresh_kernel_reconcile_after_cancel(k, symbol, p): import time as _t tid = f"fkc-{int(_t.time()*1000)}" _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) slot_data = k.slot(0).to_dict() cb = k.account.snapshot.capital fresh = _build_fresh_kernel_from_slot(slot_data, ic=cb) k2 = fresh.runtime.kernel assert k2.slot(0).is_free(), f"cancelled slot not free: {k2.slot(0).fsm_state}" async def _body_fresh_kernel_reconcile_after_exit(k, symbol, p): import time as _t tid = f"fkx-{int(_t.time()*1000)}" _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) slot_data = k.slot(0).to_dict() cb = k.account.snapshot.capital fresh = _build_fresh_kernel_from_slot(slot_data, ic=cb) k2 = fresh.runtime.kernel assert k2.slot(0).is_free(), f"closed slot not free: {k2.slot(0).fsm_state}" assert k2.slot(0).closed, "slot should be marked closed" assert abs(k2.account.snapshot.capital - cb) < 0.01, f"capital drift: {k2.account.snapshot.capital} vs {cb}" async def _body_fresh_kernel_reconcile_partial_exit(k, symbol, p): import time as _t tid = f"fkp-{int(_t.time()*1000)}" _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.002, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.5) slot_data = k.slot(0).to_dict() cb = k.account.snapshot.capital fresh = _build_fresh_kernel_from_slot(slot_data, ic=cb) k2 = fresh.runtime.kernel s = k2.slot(0) assert not s.is_free(), f"partial-exit slot should not be free: {s.fsm_state}" _si(k2, E.EXIT, tid, symbol, 'SHORT', p*0.993, 0.001, exit_leg_ratios=(1.0,)); await asyncio.sleep(0.5) assert k2.slot(0).is_free(), "slot not free after final exit on fresh kernel" async def _body_cross_slot_portfolio_short_long(k, symbol, p): import time as _t t0 = f"psl0-{int(_t.time()*1000)}" t1 = f"psl1-{int(_t.time()*1000)}" cb = k.account.snapshot.capital _si(k, E.ENTER, t0, symbol, 'SHORT', p, 0.001, slot_id=0); await asyncio.sleep(0.4) _si(k, E.ENTER, t1, symbol, 'LONG', p, 0.001, slot_id=1); await asyncio.sleep(0.4) # Verify both slots are open assert not k.slot(0).is_free(), "slot 0 should be open" assert not k.slot(1).is_free(), "slot 1 should be open" # Verify PnL tracking per slot rp0 = k.slot(0).realized_pnl; up0 = k.slot(0).unrealized_pnl rp1 = k.slot(1).realized_pnl; up1 = k.slot(1).unrealized_pnl expected = cb + rp0 + up0 + rp1 + up1 actual = k.account.snapshot.capital assert abs(actual - expected) < 0.01, f"portfolio misalignment: cap={actual} expected={expected} rp0={rp0} up0={up0} rp1={rp1} up1={up1}" # Exit slot 0 _si(k, E.EXIT, t0, symbol, 'SHORT', p*0.995, 0.001, slot_id=0); await asyncio.sleep(0.4) assert k.slot(0).is_free(), "slot 0 should be free after exit" # Exit slot 1 _si(k, E.EXIT, t1, symbol, 'LONG', p*1.005, 0.001, slot_id=1); await asyncio.sleep(0.4) assert k.slot(1).is_free(), "slot 1 should be free after exit" async def _body_outcome_inspect_entry(k, symbol, p): import time as _t tid = f"oi-{int(_t.time()*1000)}" r = _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) # Inspect outcome of ENTER _assert_accepted(r, 'entry') info = _inspect_outcome(r, 'entry') assert r.accepted, f"entry not accepted: {info}" assert r.trade_id == tid, f"trade_id mismatch: {r.trade_id} vs {tid}" assert r.slot_id == 0, f"slot_id: {r.slot_id}" # transitions should exist assert len(info["transitions"]) > 0, f"no transitions in outcome: {info}" assert info["diagnostic"] == "OK", f"diagnostic not OK: {info}" # Exit and inspect r2 = _si(k, E.EXIT, tid, symbol, "SHORT", p*0.995, 0.001); await asyncio.sleep(0.5) _assert_accepted(r2, 'exit') info2 = _inspect_outcome(r2, "exit") assert len(info2["transitions"]) > 0, f"no exit transitions: {info2}" assert info2["diagnostic"] == "OK", f"exit diagnostic: {info2}" async def _body_outcome_inspect_rejection(k, symbol, p): import time as _t tid = f"or-{int(_t.time()*1000)}" tid2 = f"or2-{int(_t.time()*1000)}" r1 = _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _assert_accepted(r1, 'first entry') # Second entry on same slot should be SLOT_BUSY r2 = _si(k, E.ENTER, tid2, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _assert_rejected(r2, 'SLOT_BUSY', 'double entry') # Verify transition trace shows the rejection info = _inspect_outcome(r2, 'double entry') assert not r2.accepted, f"second entry should be rejected: {info}" # Exit normally _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_outcome_inspect_exit_on_idle(k, symbol, p): import time as _t tid = f"oei-{int(_t.time()*1000)}" # Exit on idle slot r = _si(k, E.EXIT, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _assert_rejected(r, 'INVALID_FSM_TRANSITION', 'exit on idle') info = _inspect_outcome(r, "exit on idle") assert not r.accepted, f"exit on idle should be rejected: {info}" # Then do a normal trade _si(k, E.ENTER, tid, symbol, "SHORT", p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, "SHORT", p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_dedup_duplicate_fill_event(k, symbol, p): import time as _t import datetime as _dt tid = f"dd-{int(_t.time()*1000)}" r = _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _assert_accepted(r, 'entry') # Inject a duplicate FULL_FILL VenueEvent manually # Build an event that mirrors the slot's current active order sl = k.slot(0) ao = sl.active_entry_order if sl.active_entry_order else sl.active_exit_order if ao: dup = VenueEvent( timestamp=_dt.datetime.now(_dt.timezone.utc), event_id="dedup-test-99999", trade_id=tid, slot_id=0, kind=KernelEventKind.FULL_FILL, status=VenueEventStatus.FILLED, venue_order_id=ao.venue_order_id, venue_client_id=ao.venue_client_id, side=sl.side, asset=symbol, price=p, size=0.001, filled_size=0.001, remaining_size=0.0, reason="dedup_test", ) r2 = k.on_venue_event(dup) _assert_accepted(r2, 'dedup_fill') info = _inspect_outcome(r2, "dedup_fill") assert len(info["event_kinds"]) == 0 or info["event_kinds"] == ["ORDER_ACK"], f"duplicate fill should produce no events: {info}" # Exit _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_fill_price_divergence_1pct(k, symbol, p): import time as _t import datetime as _dt tid = f"fd-{int(_t.time()*1000)}" # Enter SHORT at market _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) # Force the kernel's slot to see a divergent fill price via on_venue_event replay sl = k.slot(0) ao = sl.active_entry_order if ao and sl.fsm_state not in ('IDLE', 'CLOSED'): divergent_price = p * 1.01 # 1% worse than reference div_event = VenueEvent( timestamp=_dt.datetime.now(_dt.timezone.utc), event_id="divergence-test", trade_id=tid, slot_id=0, kind=KernelEventKind.FULL_FILL, status=VenueEventStatus.FILLED, venue_order_id=ao.venue_order_id if ao else "", venue_client_id=ao.venue_client_id if ao else "", side=sl.side, asset=symbol, price=divergent_price, size=0.001, filled_size=0.001, remaining_size=0.0, reason="divergence_test", ) k.on_venue_event(div_event); await asyncio.sleep(0.3) # Exit at market _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_neg_cap_entry_rejected(k, symbol, p): import time as _t tid = f"nc-{int(_t.time()*1000)}" # Kernel should reject ENTER if capital cannot cover margin # With tiny capital, even a tiny trade should be checked k.account.snapshot.capital = 0.0 r = _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) info = _inspect_outcome(r, "neg_cap") # May be rejected or accepted depending on kernel margin logic # At minimum, kernel should not crash # Restore capital and do normal trade k.account.snapshot.capital = 25000.0 _si(k, E.ENTER, tid, symbol, "SHORT", p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, "SHORT", p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_cross_sample_basic_entry_exit_outcome(k, symbol, p): import time as _t tid = f"cs-{int(_t.time()*1000)}" cb = k.account.snapshot.capital; k._start_cap = cb r1 = _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _assert_accepted(r1, 'cs_entry') _check_slot_accounting(k, 'cs_after_entry') r2 = _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) _assert_accepted(r2, 'cs_exit') _check_slot_accounting(k, 'cs_after_exit') ca = k.account.snapshot.capital max_change = max(1.0, cb * 0.10) assert cb - ca < max_change, f"cs: cap shrunk {cb} -> {ca}" async def _body_cross_sample_cancel_reenter_outcome(k, symbol, p): import time as _t t1 = f"csc-{int(_t.time()*1000)}" t2 = f"csc2-{int(_t.time()*1000)}" cb = k.account.snapshot.capital; k._start_cap = cb r1 = _si(k, E.ENTER, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) _assert_accepted(r1, 'cs_cancel_entry') r2 = _si(k, E.CANCEL, t1, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.3) if r2.accepted: info = _inspect_outcome(r2, "cs_cancel") if not k.slot(0).is_free(): _si(k, E.EXIT, t1, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.3) _check_slot_accounting(k, 'cs_after_cancel') assert k.slot(0).is_free(), "slot should be free after cancel" r3 = _si(k, E.ENTER, t2, symbol, 'SHORT', p*0.997, 0.001); await asyncio.sleep(0.8) _assert_accepted(r3, 'cs_reenter') _check_slot_accounting(k, 'cs_after_reenter') r4 = _si(k, E.EXIT, t2, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) _assert_accepted(r4, 'cs_reenter_exit') _check_slot_accounting(k, 'cs_after_reenter_exit') async def _body_cross_sample_multi_leg_outcome(k, symbol, p): import time as _t tid = f"csm-{int(_t.time()*1000)}" cb = k.account.snapshot.capital; k._start_cap = cb r = _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.002, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.8) _assert_accepted(r, 'cs_ml_entry') r = _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.4) _assert_accepted(r, 'cs_ml_leg1') _check_slot_accounting(k, 'cs_ml_after_leg1') r = _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.993, 0.001, exit_leg_ratios=(0.5,1.0)); await asyncio.sleep(0.4) _assert_accepted(r, 'cs_ml_leg2') _check_slot_accounting(k, 'cs_ml_after_leg2') async def _body_cross_sample_leverage_tight_bounds(k, symbol, p): import time as _t tid = f"csl-{int(_t.time()*1000)}" cb = k.account.snapshot.capital; k._start_cap = cb r_ent = _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001, leverage=2); await asyncio.sleep(0.8) _assert_accepted(r_ent, 'cs_lev_entry') _check_slot_accounting(k, 'cs_lev_after_entry') r_ex = _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001, leverage=2); await asyncio.sleep(0.5) _assert_accepted(r_ex, 'cs_lev_exit') _check_slot_accounting(k, 'cs_lev_after_exit') ca = k.account.snapshot.capital max_change = max(1.0, cb * 0.10) assert cb - ca < max_change, f"cs_lev: cap shrunk {cb} -> {ca}" async def _body_limit_does_not_fill(k, symbol, p): tid = "l0-" + str(int(__import__("time").time()*1000)) k.process_intent(KI(timestamp=__import__("datetime").datetime.now(__import__("datetime").timezone.utc), intent_id=tid, trade_id=tid, slot_id=0, asset=symbol, side=TS.SHORT, action=E.ENTER, reference_price=0.0, target_size=0.001, leverage=1.0, exit_leg_ratios=(1.0,), reason="auto_zeroprice")); await asyncio.sleep(0.3) tid2 = "l0r-" + str(int(__import__("time").time()*1000)) _si(k, E.ENTER, tid2, symbol, "SHORT", p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid2, symbol, "SHORT", p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_limit_immediate_fill(k, symbol, p): tid = "ln-" + str(int(__import__("time").time()*1000)) k.process_intent(KI(timestamp=__import__("datetime").datetime.now(__import__("datetime").timezone.utc), intent_id=tid, trade_id=tid, slot_id=0, asset=symbol, side=TS.SHORT, action=E.ENTER, reference_price=p, target_size=-0.001, leverage=1.0, exit_leg_ratios=(1.0,), reason="auto_negsize")); await asyncio.sleep(0.3) tid2 = "lnr-" + str(int(__import__("time").time()*1000)) _si(k, E.ENTER, tid2, symbol, "SHORT", p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid2, symbol, "SHORT", p*0.995, 0.001); await asyncio.sleep(0.5) async def _body_cancel_after_exit_fill(k, symbol, p): tid = f'caf-{int(time.time()*1000)}' _si(k, E.ENTER, tid, symbol, 'SHORT', p, 0.001); await asyncio.sleep(0.8) _si(k, E.EXIT, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.3) _si(k, E.CANCEL, tid, symbol, 'SHORT', p*0.995, 0.001); await asyncio.sleep(0.5) # ===================================================================== # Test functions -- single parametrized entry point # ===================================================================== SCENARIOS = [ pytest.param("simple_entry_exit", _body_simple_entry_exit, 1, id="simple_entry_exit"), pytest.param("multi_leg_exit", _body_multi_leg_exit, 1, id="multi_leg_exit"), pytest.param("cancel_entry_order", _body_cancel_entry_order, 1, id="cancel_entry_order"), pytest.param("entry_hold_exit", _body_entry_hold_exit, 1, id="entry_hold_exit"), pytest.param("entry_exit_at_loss", _body_entry_exit_at_loss, 1, id="entry_exit_at_loss"), pytest.param("two_sequential_cycles", _body_two_sequential_cycles, 1, id="two_sequential_cycles"), pytest.param("entry_then_recover", _body_entry_then_recover, 1, id="entry_then_recover"), pytest.param("long_entry_exit", _body_long_entry_exit, 1, id="long_entry_exit"), pytest.param("cancel_idempotent", _body_cancel_idempotent, 1, id="cancel_idempotent"), pytest.param("double_cancel", _body_double_cancel, 1, id="double_cancel"), pytest.param("cancel_then_exit", _body_cancel_then_exit, 1, id="cancel_then_exit"), pytest.param("exit_then_cancel_exit", _body_exit_then_cancel_exit, 1, id="exit_then_cancel_exit"), pytest.param("exit_then_reentry", _body_exit_then_reentry, 1, id="exit_then_reentry"), pytest.param("limit_cancel", _body_limit_cancel, 1, id="limit_cancel"), pytest.param("x4_partial_hold_exit", _body_x4_partial_hold_exit, 1, id="x4_partial_hold_exit"), pytest.param("x4_three_leg", _body_x4_three_leg, 1, id="x4_three_leg"), pytest.param("x4_cancel_fill_partial", _body_x4_cancel_fill_partial, 1, id="x4_cancel_fill_partial"), pytest.param("x4_rapid_three", _body_x4_rapid_three, 1, id="x4_rapid_three"), pytest.param("x4_diff_symbol", _body_x4_diff_symbol, 1, id="x4_diff_symbol"), pytest.param("x4_alternating", _body_x4_alternating, 1, id="x4_alternating"), pytest.param("x4_multi_flatten", _body_x4_multi_flatten, 1, id="x4_multi_flatten"), pytest.param("x4_three_leg_25_50_25", _body_x4_three_leg_25_50_25, 1, id="x4_three_leg_25_50_25"), pytest.param("x4_enter_exit_hold_twice", _body_x4_enter_exit_hold_twice, 1, id="x4_enter_exit_hold_twice"), pytest.param("x4_cancel_then_double_exit", _body_x4_cancel_then_double_exit, 1, id="x4_cancel_then_double_exit"), pytest.param("basic_short_profit", _body_basic_short_profit, 1, id="basic_short_profit"), pytest.param("partial_short_profit", _body_partial_short_profit, 1, id="partial_short_profit"), pytest.param("cancel_short_profit", _body_cancel_short_profit, 1, id="cancel_short_profit"), pytest.param("double_exit_short_profit", _body_double_exit_short_profit, 1, id="double_exit_short_profit"), pytest.param("basic_short_loss", _body_basic_short_loss, 1, id="basic_short_loss"), pytest.param("partial_short_loss", _body_partial_short_loss, 1, id="partial_short_loss"), pytest.param("cancel_short_loss", _body_cancel_short_loss, 1, id="cancel_short_loss"), pytest.param("double_exit_short_loss", _body_double_exit_short_loss, 1, id="double_exit_short_loss"), pytest.param("basic_long_profit", _body_basic_long_profit, 1, id="basic_long_profit"), pytest.param("partial_long_profit", _body_partial_long_profit, 1, id="partial_long_profit"), pytest.param("cancel_long_profit", _body_cancel_long_profit, 1, id="cancel_long_profit"), pytest.param("double_exit_long_profit", _body_double_exit_long_profit, 1, id="double_exit_long_profit"), pytest.param("basic_long_loss", _body_basic_long_loss, 1, id="basic_long_loss"), pytest.param("partial_long_loss", _body_partial_long_loss, 1, id="partial_long_loss"), pytest.param("cancel_long_loss", _body_cancel_long_loss, 1, id="cancel_long_loss"), pytest.param("double_exit_long_loss", _body_double_exit_long_loss, 1, id="double_exit_long_loss"), pytest.param("triple_seq_0", _body_triple_seq_0, 1, id="triple_seq_0"), pytest.param("triple_seq_1", _body_triple_seq_1, 1, id="triple_seq_1"), pytest.param("triple_seq_2", _body_triple_seq_2, 1, id="triple_seq_2"), pytest.param("triple_seq_3", _body_triple_seq_3, 1, id="triple_seq_3"), pytest.param("triple_seq_long_0", _body_triple_seq_long_0, 1, id="triple_seq_long_0"), pytest.param("triple_seq_long_1", _body_triple_seq_long_1, 1, id="triple_seq_long_1"), pytest.param("triple_seq_long_2", _body_triple_seq_long_2, 1, id="triple_seq_long_2"), pytest.param("triple_seq_long_3", _body_triple_seq_long_3, 1, id="triple_seq_long_3"), pytest.param("cancel_reenter_0", _body_cancel_reenter_0, 1, id="cancel_reenter_0"), pytest.param("cancel_reenter_1", _body_cancel_reenter_1, 1, id="cancel_reenter_1"), pytest.param("cancel_reenter_2", _body_cancel_reenter_2, 1, id="cancel_reenter_2"), pytest.param("cancel_reenter_3", _body_cancel_reenter_3, 1, id="cancel_reenter_3"), pytest.param("cancel_reenter_long_0", _body_cancel_reenter_long_0, 1, id="cancel_reenter_long_0"), pytest.param("cancel_reenter_long_1", _body_cancel_reenter_long_1, 1, id="cancel_reenter_long_1"), pytest.param("cancel_reenter_long_2", _body_cancel_reenter_long_2, 1, id="cancel_reenter_long_2"), pytest.param("cancel_reenter_long_3", _body_cancel_reenter_long_3, 1, id="cancel_reenter_long_3"), pytest.param("leg_ratio_0", _body_leg_ratio_0, 1, id="leg_ratio_0"), pytest.param("leg_ratio_1", _body_leg_ratio_1, 1, id="leg_ratio_1"), pytest.param("leg_ratio_2", _body_leg_ratio_2, 1, id="leg_ratio_2"), pytest.param("leg_ratio_3", _body_leg_ratio_3, 1, id="leg_ratio_3"), pytest.param("leg_ratio_4", _body_leg_ratio_4, 1, id="leg_ratio_4"), pytest.param("leg_ratio_5", _body_leg_ratio_5, 1, id="leg_ratio_5"), pytest.param("leg_ratio_6", _body_leg_ratio_6, 1, id="leg_ratio_6"), pytest.param("leg_ratio_7", _body_leg_ratio_7, 1, id="leg_ratio_7"), pytest.param("breakeven_0", _body_breakeven_0, 1, id="breakeven_0"), pytest.param("breakeven_1", _body_breakeven_1, 1, id="breakeven_1"), pytest.param("breakeven_2", _body_breakeven_2, 1, id="breakeven_2"), pytest.param("breakeven_3", _body_breakeven_3, 1, id="breakeven_3"), pytest.param("short_exit_one_pct_profit", _body_short_exit_one_pct_profit, 1, id="short_exit_one_pct_profit"), pytest.param("short_exit_third_pct_profit", _body_short_exit_third_pct_profit, 1, id="short_exit_third_pct_profit"), pytest.param("short_exit_third_pct_loss", _body_short_exit_third_pct_loss, 1, id="short_exit_third_pct_loss"), pytest.param("short_exit_one_pct_loss", _body_short_exit_one_pct_loss, 1, id="short_exit_one_pct_loss"), pytest.param("long_exit_one_pct_profit", _body_long_exit_one_pct_profit, 1, id="long_exit_one_pct_profit"), pytest.param("long_exit_third_pct_profit", _body_long_exit_third_pct_profit, 1, id="long_exit_third_pct_profit"), pytest.param("long_exit_third_pct_loss", _body_long_exit_third_pct_loss, 1, id="long_exit_third_pct_loss"), pytest.param("long_exit_one_pct_loss", _body_long_exit_one_pct_loss, 1, id="long_exit_one_pct_loss"), pytest.param("entry_exit_short_2x_profit", _body_entry_exit_short_2x_profit, 1, id="entry_exit_short_2x_profit"), pytest.param("entry_exit_long_2x_profit", _body_entry_exit_long_2x_profit, 1, id="entry_exit_long_2x_profit"), pytest.param("entry_exit_short_3x_profit", _body_entry_exit_short_3x_profit, 1, id="entry_exit_short_3x_profit"), pytest.param("entry_exit_long_3x_profit", _body_entry_exit_long_3x_profit, 1, id="entry_exit_long_3x_profit"), pytest.param("entry_exit_short_2x_loss", _body_entry_exit_short_2x_loss, 1, id="entry_exit_short_2x_loss"), pytest.param("entry_exit_long_2x_loss", _body_entry_exit_long_2x_loss, 1, id="entry_exit_long_2x_loss"), pytest.param("entry_exit_short_3x_loss", _body_entry_exit_short_3x_loss, 1, id="entry_exit_short_3x_loss"), pytest.param("entry_exit_long_3x_loss", _body_entry_exit_long_3x_loss, 1, id="entry_exit_long_3x_loss"), pytest.param("entry_exit_short_2x_size", _body_entry_exit_short_2x_size, 1, id="entry_exit_short_2x_size"), pytest.param("entry_exit_long_2x_size", _body_entry_exit_long_2x_size, 1, id="entry_exit_long_2x_size"), pytest.param("entry_exit_short_3x_size", _body_entry_exit_short_3x_size, 1, id="entry_exit_short_3x_size"), pytest.param("entry_exit_long_3x_size", _body_entry_exit_long_3x_size, 1, id="entry_exit_long_3x_size"), pytest.param("entry_exit_short_4x_size", _body_entry_exit_short_4x_size, 1, id="entry_exit_short_4x_size"), pytest.param("entry_exit_long_4x_size", _body_entry_exit_long_4x_size, 1, id="entry_exit_long_4x_size"), pytest.param("entry_exit_short_5x_size", _body_entry_exit_short_5x_size, 1, id="entry_exit_short_5x_size"), pytest.param("entry_exit_long_5x_size", _body_entry_exit_long_5x_size, 1, id="entry_exit_long_5x_size"), pytest.param("three_cycle_short", _body_three_cycle_short, 1, id="three_cycle_short"), pytest.param("three_cycle_long", _body_three_cycle_long, 1, id="three_cycle_long"), pytest.param("partial_ratio_0_short", _body_partial_ratio_0_short, 1, id="partial_ratio_0_short"), pytest.param("partial_ratio_0_long", _body_partial_ratio_0_long, 1, id="partial_ratio_0_long"), pytest.param("partial_ratio_1_short", _body_partial_ratio_1_short, 1, id="partial_ratio_1_short"), pytest.param("partial_ratio_1_long", _body_partial_ratio_1_long, 1, id="partial_ratio_1_long"), pytest.param("partial_ratio_2_short", _body_partial_ratio_2_short, 1, id="partial_ratio_2_short"), pytest.param("partial_ratio_2_long", _body_partial_ratio_2_long, 1, id="partial_ratio_2_long"), pytest.param("partial_ratio_3_short", _body_partial_ratio_3_short, 1, id="partial_ratio_3_short"), pytest.param("partial_ratio_3_long", _body_partial_ratio_3_long, 1, id="partial_ratio_3_long"), pytest.param("cross_asset_short", _body_cross_asset_short, 1, id="cross_asset_short"), pytest.param("cross_asset_long", _body_cross_asset_long, 1, id="cross_asset_long"), pytest.param("cancel_on_fill_short", _body_cancel_on_fill_short, 1, id="cancel_on_fill_short"), pytest.param("cancel_on_fill_long", _body_cancel_on_fill_long, 1, id="cancel_on_fill_long"), pytest.param("entry_quick_exit_short", _body_entry_quick_exit_short, 1, id="entry_quick_exit_short"), pytest.param("entry_quick_exit_long", _body_entry_quick_exit_long, 1, id="entry_quick_exit_long"), pytest.param("triple_leg_exit_short", _body_triple_leg_exit_short, 1, id="triple_leg_exit_short"), pytest.param("triple_leg_exit_long", _body_triple_leg_exit_long, 1, id="triple_leg_exit_long"), pytest.param("cancel_reenter_exit_short", _body_cancel_reenter_exit_short, 1, id="cancel_reenter_exit_short"), pytest.param("cancel_reenter_exit_long", _body_cancel_reenter_exit_long, 1, id="cancel_reenter_exit_long"), pytest.param("zero_capital_safety", _body_zero_capital_safety, 1, id="zero_capital_safety"), pytest.param("position_survives_exit", _body_position_survives_exit, 1, id="position_survives_exit"), pytest.param("double_entry_prevention", _body_double_entry_prevention, 1, id="double_entry_prevention"), pytest.param("negative_capital_check", _body_negative_capital_check, 1, id="negative_capital_check"), pytest.param("reconcile_empty", _body_reconcile_empty, 1, id="reconcile_empty"), pytest.param("reconcile_after_entry", _body_reconcile_after_entry, 1, id="reconcile_after_entry"), pytest.param("reconcile_after_exit", _body_reconcile_after_exit, 1, id="reconcile_after_exit"), pytest.param("reconcile_after_cancel", _body_reconcile_after_cancel, 1, id="reconcile_after_cancel"), pytest.param("reconcile_twice", _body_reconcile_twice, 1, id="reconcile_twice"), pytest.param("reconcile_then_cancel", _body_reconcile_then_cancel, 1, id="reconcile_then_cancel"), pytest.param("concurrent_enter_cancel", _body_concurrent_enter_cancel, 1, id="concurrent_enter_cancel"), pytest.param("rapid_alternating", _body_rapid_alternating, 1, id="rapid_alternating"), pytest.param("duplicate_trade_id", _body_duplicate_trade_id, 1, id="duplicate_trade_id"), pytest.param("slot_busy_double_entry", _body_slot_busy_double_entry, 1, id="slot_busy_double_entry"), pytest.param("exit_on_idle_slot", _body_exit_on_idle_slot, 1, id="exit_on_idle_slot"), pytest.param("cancel_on_idle_slot", _body_cancel_on_idle_slot, 1, id="cancel_on_idle_slot"), pytest.param("rapid_ten_cycle", _body_rapid_ten_cycle, 1, id="rapid_ten_cycle"), pytest.param("multi_slot_enter_exit", _body_multi_slot_enter_exit, 2, id="multi_slot_enter_exit"), pytest.param("multi_slot_cross_cancel", _body_multi_slot_cross_cancel, 2, id="multi_slot_cross_cancel"), pytest.param("multi_slot_rapid_cycle", _body_multi_slot_rapid_cycle, 2, id="multi_slot_rapid_cycle"), pytest.param("reject_wrong_symbol", _body_reject_wrong_symbol, 1, id="reject_wrong_symbol"), pytest.param("reject_zero_size", _body_reject_zero_size, 1, id="reject_zero_size"), pytest.param("reject_side_mismatch_cancel", _body_reject_side_mismatch_cancel, 1, id="reject_side_mismatch_cancel"), pytest.param("reject_negative_price", _body_reject_negative_price, 1, id="reject_negative_price"), pytest.param("snapshot_restore_empty", _body_snapshot_restore_empty, 1, id="snapshot_restore_empty"), pytest.param("snapshot_restore_mid_trade", _body_snapshot_restore_mid_trade, 1, id="snapshot_restore_mid_trade"), pytest.param("snapshot_restore_after_cancel", _body_snapshot_restore_after_cancel, 1, id="snapshot_restore_after_cancel"), pytest.param("limit_does_not_fill", _body_limit_does_not_fill, 1, id="limit_does_not_fill"), pytest.param("limit_immediate_fill", _body_limit_immediate_fill, 1, id="limit_immediate_fill"), pytest.param("cancel_after_exit_fill", _body_cancel_after_exit_fill, 1, id="cancel_after_exit_fill"), pytest.param("fresh_kernel_reconcile_entry", _body_fresh_kernel_reconcile_entry, 1, id="fresh_kernel_reconcile_entry"), pytest.param("fresh_kernel_reconcile_after_cancel", _body_fresh_kernel_reconcile_after_cancel, 1, id="fresh_kernel_reconcile_after_cancel"), pytest.param("fresh_kernel_reconcile_after_exit", _body_fresh_kernel_reconcile_after_exit, 1, id="fresh_kernel_reconcile_after_exit"), pytest.param("fresh_kernel_reconcile_partial_exit", _body_fresh_kernel_reconcile_partial_exit, 1, id="fresh_kernel_reconcile_partial_exit"), pytest.param("cross_slot_portfolio_short_long", _body_cross_slot_portfolio_short_long, 1, id="cross_slot_portfolio_short_long"), pytest.param("outcome_inspect_entry", _body_outcome_inspect_entry, 1, id="outcome_inspect_entry"), pytest.param("outcome_inspect_rejection", _body_outcome_inspect_rejection, 1, id="outcome_inspect_rejection"), pytest.param("outcome_inspect_exit_on_idle", _body_outcome_inspect_exit_on_idle, 1, id="outcome_inspect_exit_on_idle"), pytest.param("dedup_duplicate_fill_event", _body_dedup_duplicate_fill_event, 1, id="dedup_duplicate_fill_event"), pytest.param("fill_price_divergence_1pct", _body_fill_price_divergence_1pct, 1, id="fill_price_divergence_1pct"), pytest.param("neg_cap_entry_rejected", _body_neg_cap_entry_rejected, 1, id="neg_cap_entry_rejected"), pytest.param("cross_sample_basic_entry_exit_outcome", _body_cross_sample_basic_entry_exit_outcome, 1, id="cross_sample_basic_entry_exit_outcome"), pytest.param("cross_sample_cancel_reenter_outcome", _body_cross_sample_cancel_reenter_outcome, 1, id="cross_sample_cancel_reenter_outcome"), pytest.param("cross_sample_multi_leg_outcome", _body_cross_sample_multi_leg_outcome, 1, id="cross_sample_multi_leg_outcome"), pytest.param("cross_sample_leverage_tight_bounds", _body_cross_sample_leverage_tight_bounds, 1, id="cross_sample_leverage_tight_bounds"), ] @pytest.fixture(scope="session") def _live_client(): return BingxHttpClient(_build_config()) @pytest.mark.parametrize("name,body_fn,max_slots", SCENARIOS) def test_pink_ditav2(_live_client, name, body_fn, max_slots) -> None: bundle = _build_rb(max_slots=max_slots) ic = bundle.runtime.kernel.account.snapshot.capital r = asyncio.run(_run(bundle, _live_client, body_fn, name, ic)) assert r.positions_flat, f"{name}: {r.error}"