Files
lowkey_backtest/engine/market.py

157 lines
4.5 KiB
Python

"""
Market type definitions and configuration for backtesting.
Supports different market types with their specific trading conditions:
- SPOT: No leverage, no funding, long-only
- PERPETUAL: Leverage, funding rates, long/short
"""
from dataclasses import dataclass
from enum import Enum
class MarketType(Enum):
"""Supported market types for backtesting."""
SPOT = "spot"
PERPETUAL = "perpetual"
@dataclass(frozen=True)
class MarketConfig:
"""
Configuration for a specific market type.
Attributes:
market_type: The market type enum value
maker_fee: Maker fee as decimal (e.g., 0.0008 = 0.08%)
taker_fee: Taker fee as decimal (e.g., 0.001 = 0.1%)
max_leverage: Maximum allowed leverage
funding_rate: Funding rate per 8 hours as decimal (perpetuals only)
funding_interval_hours: Hours between funding payments
maintenance_margin_rate: Rate for liquidation calculation
supports_short: Whether short-selling is supported
"""
market_type: MarketType
maker_fee: float
taker_fee: float
max_leverage: int
funding_rate: float
funding_interval_hours: int
maintenance_margin_rate: float
supports_short: bool
# OKX-based default configurations
SPOT_CONFIG = MarketConfig(
market_type=MarketType.SPOT,
maker_fee=0.0008, # 0.08%
taker_fee=0.0010, # 0.10%
max_leverage=1,
funding_rate=0.0,
funding_interval_hours=0,
maintenance_margin_rate=0.0,
supports_short=False,
)
PERPETUAL_CONFIG = MarketConfig(
market_type=MarketType.PERPETUAL,
maker_fee=0.0002, # 0.02%
taker_fee=0.0005, # 0.05%
max_leverage=125,
funding_rate=0.0001, # 0.01% per 8 hours (simplified average)
funding_interval_hours=8,
maintenance_margin_rate=0.004, # 0.4% for BTC on OKX
supports_short=True,
)
def get_market_config(market_type: MarketType) -> MarketConfig:
"""
Get the configuration for a specific market type.
Args:
market_type: The market type to get configuration for
Returns:
MarketConfig with default values for that market type
"""
configs = {
MarketType.SPOT: SPOT_CONFIG,
MarketType.PERPETUAL: PERPETUAL_CONFIG,
}
return configs[market_type]
def get_ccxt_symbol(symbol: str, market_type: MarketType) -> str:
"""
Convert a standard symbol to CCXT format for the given market type.
Args:
symbol: Standard symbol (e.g., 'BTC/USDT')
market_type: The market type
Returns:
CCXT-formatted symbol (e.g., 'BTC/USDT:USDT' for perpetuals)
"""
if market_type == MarketType.PERPETUAL:
# OKX perpetual format: BTC/USDT:USDT
quote = symbol.split('/')[1] if '/' in symbol else 'USDT'
return f"{symbol}:{quote}"
return symbol
def calculate_leverage_stop_loss(
leverage: int,
maintenance_margin_rate: float = 0.004
) -> float:
"""
Calculate the implicit stop-loss percentage from leverage.
At a given leverage, liquidation occurs when the position loses
approximately (1/leverage - maintenance_margin_rate) of its value.
Args:
leverage: Position leverage multiplier
maintenance_margin_rate: Maintenance margin rate (default OKX BTC: 0.4%)
Returns:
Stop-loss percentage as decimal (e.g., 0.196 for 19.6%)
"""
if leverage <= 1:
return 1.0 # No forced stop for spot
return (1 / leverage) - maintenance_margin_rate
def calculate_liquidation_price(
entry_price: float,
leverage: float,
is_long: bool,
maintenance_margin_rate: float = 0.004
) -> float:
"""
Calculate the liquidation price for a leveraged position.
Args:
entry_price: Position entry price
leverage: Position leverage
is_long: True for long positions, False for short
maintenance_margin_rate: Maintenance margin rate (default OKX BTC: 0.4%)
Returns:
Liquidation price
"""
if leverage <= 1:
return 0.0 if is_long else float('inf')
# Simplified liquidation formula
# Long: liq_price = entry * (1 - 1/leverage + maintenance_margin_rate)
# Short: liq_price = entry * (1 + 1/leverage - maintenance_margin_rate)
margin_ratio = 1 / leverage
if is_long:
liq_price = entry_price * (1 - margin_ratio + maintenance_margin_rate)
else:
liq_price = entry_price * (1 + margin_ratio - maintenance_margin_rate)
return liq_price