First commit of the previously-untracked PINK-on-DITAv2 migration system (execution moves to the Rust kernel; policy stays on legacy DITA, so Alpha Engine algorithmic integrity is preserved). BLUE is untouched. Sprint 0 (safety snapshot + flaw-fix verification, MARKET single-leg scope): - Verified Rust FSM fixes (flaws 2,4,10,11,13) by source read of lib.rs. - Hardened 5 vacuous/guarded assertions in test_flaws.py so each flaw test genuinely exercises its fix. Most important: Flaw 5 now asserts capital moves by EXACTLY realized PnL (was entering/exiting at the same price). - Offline suites: 533 passed, 0 failed (35 flaws + 402 kernel/accounting/ bridge + 96 runtime/persistence/multi-exit/restart/seams). - GATE PASS: MARKET-path-critical flaws 1,2,5 confirmed fixed + green. - Added SPRINT0_FLAW_VERIFICATION.md report and _rust_kernel/.gitignore (excludes Rust target/ build artifacts). LIMIT/partial-fill remain explicitly out of scope (MARKET-only bring-up). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1572 lines
92 KiB
Python
1572 lines
92 KiB
Python
#!/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}"
|