malkhut: standardized order types — FIX/CCXT-aligned, multi-exchange mapping
order_types.py: Five-layer taxonomy normalized to industry standards:
Layer 1: Base types (FIX Tag 40) — MARKET, LIMIT
Layer 2: Time-in-force (FIX Tag 59) — GTC, IOC, FOK, GTD
Layer 3: Conditional/Trigger (FIX Tag 3/4+MIT) — STOP_MARKET, STOP_LIMIT,
TRIGGER_MARKET, TRIGGER_LIMIT, TRAILING_STOP
Layer 4: Instructions (FIX Tag 18) — POST_ONLY, REDUCE_ONLY, HIDDEN, ICEBERG
Layer 5: Compound (exchange-specific) — OCO, TP_SL
Cross-exchange mapping: BingX ↔ Binance ↔ Bybit (from CCXT source code).
Standards: FIX 4.4 Tag 40/59/18, CCXT unified API, ISO 10383 (MIC).
Transferability: strategy PARAMETERS transfer. ORDER TYPE NAMES are
venue-specific but semantics identical (LIMIT = LIMIT everywhere).
14 tests. README updated with full mapping table and standards references.
This commit is contained in:
@@ -136,6 +136,7 @@ MALKHUT/
|
||||
│ │ ├── pipeline.py # Bounded continuous learning loop + logger
|
||||
│ │ ├── dsl.py # Strategy DSL v2 (40+ primitives, 40+ sensors)
|
||||
│ │ ├── generator.py # Genetic programming strategy evolution
|
||||
│ │ ├── order_types.py # Standardized order type enum (FIX/CCXT-aligned)
|
||||
│ │ ├── selector.py # Regime → strategy mapping + performance matrix
|
||||
│ │ ├── asset_classification.py # Multi-label taxonomy + exchange registry
|
||||
│ │ ├── asset_behavior.py # 10-dimension behavior DSL, research-validated
|
||||
@@ -468,6 +469,7 @@ Re-measurement at correct fees is required for production deployment.
|
||||
| **Policy Registry** | `training/registry.py` | 14 | CANDIDATE → ACTIVE lifecycle |
|
||||
| **Training Pipeline** | `training/pipeline.py` | 21 | Bounded continuous learning loop |
|
||||
| **Strategy DSL v2** | `training/dsl.py` | 69 | 40+ primitives, 40+ sensors, 16 builtins |
|
||||
| **Order Types** | `training/order_types.py` | (new) | FIX/CCXT-aligned, 5-layer taxonomy, cross-exchange mapping |
|
||||
| **Strategy Generator** | `training/generator.py` | 20 | Genetic programming: crossover, mutation, tournament |
|
||||
| **Strategy Selector** | `training/selector.py` | 24 | Regime → strategy mapping, performance matrix |
|
||||
| **Asset Classification** | `training/asset_classification.py` | 190 | Multi-label taxonomy + exchange registry, 13 assets, system-wide store |
|
||||
@@ -1191,6 +1193,52 @@ zero fidelity loss). CMA-ES training speedup is ~1.5x because multiprocessing ov
|
||||
is amortized across 90 scenarios per eval. Bottleneck is planner (MCTS), not CWM —
|
||||
numba already accelerates the CWM hot path (`_HAS_NUMBA = True`).
|
||||
|
||||
### Order Type Standardization (Multi-Exchange, FIX/CCXT-Aligned)
|
||||
|
||||
Normalized order type taxonomy based on **FIX Protocol Tag 40 (OrdType)** as the
|
||||
universal reference, with **CCXT** de facto crypto standard for practical interop.
|
||||
Neither MiFID II nor MiCA defines a standalone order type taxonomy — both defer to FIX.
|
||||
|
||||
**Five-layer hybrid model:**
|
||||
|
||||
| Layer | Standard Source | Types |
|
||||
|-------|----------------|-------|
|
||||
| 1. Base | FIX Tag 40 | `MARKET`, `LIMIT` |
|
||||
| 2. Time-in-force | FIX Tag 59 | `GTC`, `IOC`, `FOK`, `GTD` |
|
||||
| 3. Conditional/Trigger | FIX Tag 3/4 + MIT | `STOP_MARKET`, `STOP_LIMIT`, `TRIGGER_MARKET`, `TRIGGER_LIMIT`, `TRAILING_STOP` |
|
||||
| 4. Instructions | FIX Tag 18 | `POST_ONLY`, `REDUCE_ONLY`, `HIDDEN`, `ICEBERG` |
|
||||
| 5. Compound | Exchange-specific | `OCO`, `TP_SL` |
|
||||
|
||||
#### Cross-Exchange Mapping (BingX ↔ Binance ↔ Bybit)
|
||||
|
||||
| Normalized | BingX | Binance Spot | Binance Futures | Bybit |
|
||||
|-----------|-------|-------------|-----------------|-------|
|
||||
| `LIMIT` | LIMIT | LIMIT | LIMIT | LIMIT |
|
||||
| `MARKET` | MARKET | MARKET | MARKET | MARKET |
|
||||
| `POST_ONLY` | POST_ONLY | LIMIT_MAKER | LIMIT_MAKER | Post-Only |
|
||||
| `IOC` | IOC | IOC | IOC | IOC |
|
||||
| `FOK` | FOK | FOK | FOK | FOK |
|
||||
| `STOP_MARKET` | TRIGGER_MARKET | STOP_MARKET | STOP_MARKET | Stop-Market |
|
||||
| `STOP_LIMIT` | TRIGGER_LIMIT | STOP_LOSS_LIMIT | STOP | Stop-Limit |
|
||||
| `TP_MARKET` | TRIGGER_MARKET | TAKE_PROFIT | TAKE_PROFIT_MARKET | TP-Market |
|
||||
| `TP_LIMIT` | TRIGGER_LIMIT | TAKE_PROFIT_LIMIT | TAKE_PROFIT | TP-Limit |
|
||||
| `TRAILING_STOP` | TRIGGER_MARKET(trail) | (spot only) | TRAILING_STOP_MARKET | Trailing |
|
||||
|
||||
#### Transferability Principle
|
||||
|
||||
Strategy **PARAMETERS** transfer across exchanges. Order type **NAMES** are venue-specific
|
||||
but semantics are identical. A `LIMIT` on BingX = `LIMIT` on Binance = `LIMIT` on Bybit
|
||||
(same fill behavior). Only the API string differs. The venue adapter translates
|
||||
normalized → exchange-native at submission time.
|
||||
|
||||
#### Standards Referenced
|
||||
|
||||
- FIX 4.4: Tag 40 (OrdType), Tag 59 (TimeInForce), Tag 18 (ExecInst)
|
||||
- CCXT: unified `limit`/`market` + parameter composition (triggerPrice, stopPrice)
|
||||
- ISO 10383: MIC for venue identification
|
||||
- MiFID II / MiCA: defers to FIX for order type classification
|
||||
- FIA: references FIX Tag 40 in digital asset guidelines
|
||||
|
||||
### Prod Tooling
|
||||
|
||||
| Component | Purpose | File |
|
||||
|
||||
83
MALKHUT/malkhut/tests/test_order_types.py
Normal file
83
MALKHUT/malkhut/tests/test_order_types.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
Tests for standardized order types — FIX/CCXT-aligned, multi-exchange.
|
||||
"""
|
||||
import pytest
|
||||
from malkhut.training.order_types import (
|
||||
BaseType, TimeInForce, ConditionalType, OrderInstruction, CompoundOrder,
|
||||
EXCHANGE_ORDER_MAP, normalize_to_exchange, get_supported_types, is_type_available,
|
||||
)
|
||||
|
||||
|
||||
class TestOrderTypeEnums:
|
||||
def test_base_types(self):
|
||||
assert BaseType.MARKET.value == "market"
|
||||
assert BaseType.LIMIT.value == "limit"
|
||||
|
||||
def test_time_in_force(self):
|
||||
assert TimeInForce.GTC.value == "gtc"
|
||||
assert TimeInForce.IOC.value == "ioc"
|
||||
assert TimeInForce.FOK.value == "fok"
|
||||
|
||||
def test_conditional_types(self):
|
||||
assert ConditionalType.STOP_MARKET.value == "stop_market"
|
||||
assert ConditionalType.STOP_LIMIT.value == "stop_limit"
|
||||
assert ConditionalType.TRAILING_STOP.value == "trailing_stop"
|
||||
|
||||
def test_instructions(self):
|
||||
assert OrderInstruction.POST_ONLY.value == "post_only"
|
||||
assert OrderInstruction.REDUCE_ONLY.value == "reduce_only"
|
||||
|
||||
def test_compound(self):
|
||||
assert CompoundOrder.OCO.value == "oco"
|
||||
assert CompoundOrder.TP_SL.value == "tp_sl"
|
||||
|
||||
|
||||
class TestExchangeMapping:
|
||||
def test_binance_has_all_types(self):
|
||||
types = get_supported_types("binance")
|
||||
assert "limit" in types
|
||||
assert "market" in types
|
||||
assert "stop_market" in types
|
||||
assert "post_only" in types
|
||||
|
||||
def test_bingx_has_all_types(self):
|
||||
types = get_supported_types("bingx")
|
||||
assert "limit" in types
|
||||
assert "market" in types
|
||||
assert "trigger_market" in types
|
||||
|
||||
def test_bybit_has_all_types(self):
|
||||
types = get_supported_types("bybit")
|
||||
assert "limit" in types
|
||||
assert "market" in types
|
||||
assert "stop_market" in types
|
||||
|
||||
def test_normalize_limit_all_exchanges(self):
|
||||
"""LIMIT should map to LIMIT on all exchanges."""
|
||||
for ex in ["binance", "bingx", "bybit"]:
|
||||
assert normalize_to_exchange("limit", ex) == "LIMIT"
|
||||
|
||||
def test_normalize_market_all_exchanges(self):
|
||||
"""MARKET should map to MARKET on all exchanges."""
|
||||
for ex in ["binance", "bingx", "bybit"]:
|
||||
assert normalize_to_exchange("market", ex) == "MARKET"
|
||||
|
||||
def test_normalize_post_only_varies(self):
|
||||
"""POST_ONLY maps differently across exchanges."""
|
||||
assert normalize_to_exchange("post_only", "binance") == "LIMIT_MAKER"
|
||||
assert normalize_to_exchange("post_only", "bingx") == "POST_ONLY"
|
||||
assert normalize_to_exchange("post_only", "bybit") == "POST_ONLY"
|
||||
|
||||
def test_normalize_unknown_type(self):
|
||||
assert normalize_to_exchange("invalid_type", "binance") is None
|
||||
|
||||
def test_is_type_available(self):
|
||||
assert is_type_available("limit", "binance")
|
||||
assert is_type_available("stop_market", "bingx")
|
||||
assert not is_type_available("invalid", "binance")
|
||||
|
||||
def test_get_supported_types_unique(self):
|
||||
"""Each exchange should support at least 8 types."""
|
||||
for ex in ["binance", "bingx", "bybit"]:
|
||||
types = get_supported_types(ex)
|
||||
assert len(types) >= 8
|
||||
120
MALKHUT/malkhut/training/order_types.py
Normal file
120
MALKHUT/malkhut/training/order_types.py
Normal file
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
Standardized Order Types — FIX/CCXT-aligned, multi-exchange compatible.
|
||||
|
||||
Five-layer taxonomy normalized to industry standards:
|
||||
Layer 1: Base types (FIX Tag 40 OrdType)
|
||||
Layer 2: Time-in-force (FIX Tag 59)
|
||||
Layer 3: Conditional/Trigger (FIX Tag 3/4 + MIT)
|
||||
Layer 4: Instructions (FIX Tag 18)
|
||||
Layer 5: Compound (exchange-specific)
|
||||
|
||||
Transferability: strategy PARAMETERS transfer across exchanges.
|
||||
Order type NAMES are venue-specific but semantics are identical.
|
||||
The venue adapter translates normalized → exchange-native at submission.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
from typing import Dict, Optional
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# Layer 1: Base Types (FIX Tag 40 OrdType)
|
||||
# ==============================================================================
|
||||
|
||||
class BaseType(str, Enum):
|
||||
MARKET = "market" # FIX: 1 — immediate execution at best available price
|
||||
LIMIT = "limit" # FIX: 2 — execute only at specified price or better
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# Layer 2: Time-in-Force (FIX Tag 59)
|
||||
# ==============================================================================
|
||||
|
||||
class TimeInForce(str, Enum):
|
||||
GTC = "gtc" # FIX: 1 — Good Till Cancel
|
||||
IOC = "ioc" # FIX: 3 — Immediate or Cancel (partial fill OK)
|
||||
FOK = "fok" # FIX: 4 — Fill or Kill (all-or-nothing)
|
||||
GTD = "gtd" # FIX: 6 — Good Till Date
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# Layer 3: Conditional/Trigger (FIX Tag 3/4 + MIT)
|
||||
# ==============================================================================
|
||||
|
||||
class ConditionalType(str, Enum):
|
||||
STOP_MARKET = "stop_market" # FIX: 3 — trigger → market
|
||||
STOP_LIMIT = "stop_limit" # FIX: 4 — trigger → limit
|
||||
TRIGGER_MARKET = "trigger_market" # FIX: J (MIT) — market if touched
|
||||
TRIGGER_LIMIT = "trigger_limit" # FIX: 4 — limit if touched
|
||||
TRAILING_STOP = "trailing_stop" # FIX: P + ExecInst — trailing stop
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# Layer 4: Instructions (FIX Tag 18 ExecInst)
|
||||
# ==============================================================================
|
||||
|
||||
class OrderInstruction(str, Enum):
|
||||
POST_ONLY = "post_only" # FIX: ExecInst — maker-only, reject if crossing
|
||||
REDUCE_ONLY = "reduce_only" # FIX: ExecInst — only reduce existing position
|
||||
HIDDEN = "hidden" # FIX: ExecInst — iceberg/hidden order
|
||||
ICEBERG = "iceberg" # FIX: ExecInst — visible portion only
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# Layer 5: Compound (exchange-specific, no FIX equivalent)
|
||||
# ==============================================================================
|
||||
|
||||
class CompoundOrder(str, Enum):
|
||||
OCO = "oco" # One-Cancels-Other (linked orders)
|
||||
TP_SL = "tp_sl" # Take-Profit / Stop-Loss (attached to position)
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# Exchange-specific mapping tables
|
||||
# ==============================================================================
|
||||
|
||||
EXCHANGE_ORDER_MAP: Dict[str, Dict[str, str]] = {
|
||||
"binance": {
|
||||
"limit": "LIMIT", "market": "MARKET",
|
||||
"stop_market": "STOP_MARKET", "stop_limit": "STOP_LOSS_LIMIT",
|
||||
"trigger_market": "TAKE_PROFIT", "trigger_limit": "TAKE_PROFIT_LIMIT",
|
||||
"trailing_stop": "TRAILING_STOP_MARKET",
|
||||
"post_only": "LIMIT_MAKER", "ioc": "IOC", "fok": "FOK",
|
||||
"reduce_only": "REDUCE_ONLY",
|
||||
},
|
||||
"bingx": {
|
||||
"limit": "LIMIT", "market": "MARKET",
|
||||
"stop_market": "TRIGGER_MARKET", "stop_limit": "TRIGGER_LIMIT",
|
||||
"trigger_market": "TRIGGER_MARKET", "trigger_limit": "TRIGGER_LIMIT",
|
||||
"trailing_stop": "TRIGGER_MARKET",
|
||||
"post_only": "POST_ONLY", "ioc": "IOC", "fok": "FOK",
|
||||
"reduce_only": "REDUCE_ONLY",
|
||||
},
|
||||
"bybit": {
|
||||
"limit": "LIMIT", "market": "MARKET",
|
||||
"stop_market": "STOP_MARKET", "stop_limit": "STOP_LIMIT",
|
||||
"trigger_market": "TAKE_PROFIT_MARKET", "trigger_limit": "TAKE_PROFIT_LIMIT",
|
||||
"trailing_stop": "TRAILING_STOP",
|
||||
"post_only": "POST_ONLY", "ioc": "IOC", "fok": "FOK",
|
||||
"reduce_only": "REDUCE_ONLY",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def normalize_to_exchange(normalized_type: str, exchange_id: str) -> Optional[str]:
|
||||
"""Convert a normalized order type to exchange-native string."""
|
||||
mapping = EXCHANGE_ORDER_MAP.get(exchange_id, {})
|
||||
return mapping.get(normalized_type)
|
||||
|
||||
|
||||
def get_supported_types(exchange_id: str) -> list[str]:
|
||||
"""Get all normalized order types supported by an exchange."""
|
||||
mapping = EXCHANGE_ORDER_MAP.get(exchange_id, {})
|
||||
return list(mapping.keys())
|
||||
|
||||
|
||||
def is_type_available(normalized_type: str, exchange_id: str) -> bool:
|
||||
"""Check if a normalized order type is available on an exchange."""
|
||||
mapping = EXCHANGE_ORDER_MAP.get(exchange_id, {})
|
||||
return normalized_type in mapping
|
||||
Reference in New Issue
Block a user