Files
siloqy/prod/tests/test_pink_bingx_dita_live_e2e.py

1572 lines
92 KiB
Python
Raw Normal View History

#!/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}"