157 lines
4.5 KiB
Python
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
|