diff --git a/MALKHUT/README.md b/MALKHUT/README.md index cdd4212..4623dec 100644 --- a/MALKHUT/README.md +++ b/MALKHUT/README.md @@ -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 | diff --git a/MALKHUT/malkhut/tests/test_order_types.py b/MALKHUT/malkhut/tests/test_order_types.py new file mode 100644 index 0000000..d16f3bc --- /dev/null +++ b/MALKHUT/malkhut/tests/test_order_types.py @@ -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 diff --git a/MALKHUT/malkhut/training/order_types.py b/MALKHUT/malkhut/training/order_types.py new file mode 100644 index 0000000..c66d3cd --- /dev/null +++ b/MALKHUT/malkhut/training/order_types.py @@ -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