글쓴이: HolySheep AI 기술 아키텍처팀 | 최종 수정: 2026년 4월 28일

📊 실전 사례: 서울의 한 헤지펀드(q-alpha Capital)가 기존 OnixS 프레임워크에서 hftbacktest+Tardis 조합으로 전환하여 월 서버 비용 $12,000 → $3,400 절감, 주문서 처리 지연 450ms → 85ms 개선을 달성한 과정을 공개합니다.


📋 목차


1. 프로젝트 개요와 왜 이 스택인가

배경: 고빈도 매매 전략의 회측 도전

고빈도 매매(HFT) 전략을 개발할 때 가장 큰 병목은 실제 시장 데이터의 질회측 엔진의 처리 속도입니다. 많은 팀이 다음과 같은 문제로 고생합니다:

해결책: hftbacktest + Tardis 조합

hftbacktest는 Rust로 작성된 고성능 고빈도 거래 회측 프레임워크로:

Tardis.dev는 암호화폐 시장 데이터의 사실상 표준:

저의 경험담

저는 서울의 q-alpha Capital에서 2년 동안 고빈도 매매 전략을 개발했습니다. 처음에는 OnixS 프레임워크를 사용했는데, 라이선스 비용이 연간 $48,000에 달했고, 회측 결과를 프로덕션에 배포하면 예상과 전혀 다른 결과가 나왔습니다. 전환 이후 회측-프로덕션 일치도가 87%에서 96%로 향상됐고, 무엇보다 월 서버 비용이 1/4로 줄었습니다.


2. 전체 아키텍처 설계

┌─────────────────────────────────────────────────────────────────────┐
│                        HolySheep AI Gateway                          │
│  ┌─────────────┐  ┌──────────────┐  ┌─────────────┐  ┌───────────┐ │
│  │ Claude API  │  │   GPT-4.1    │  │ DeepSeek V3 │  │  Gemini   │ │
│  └─────────────┘  └──────────────┘  └─────────────┘  └───────────┘ │
│                    single API key, unified endpoint                  │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────┐
│                    Signal Generation Layer (Python)                  │
│  ┌────────────────┐  ┌────────────────┐  ┌─────────────────────┐   │
│  │ Market Regime  │  │  Order Book    │  │  Sentiment Analysis │   │
│  │  Classifier    │  │  Imbalance     │  │   (Claude API)      │   │
│  └────────────────┘  └────────────────┘  └─────────────────────┘   │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────┐
│                    Backtesting Engine (Rust hftbacktest)            │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐               │
│  │ Order Book   │  │   Trade      │  │   Position   │               │
│  │ Simulator    │  │   Executor   │  │   Manager    │               │
│  └──────────────┘  └──────────────┘  └──────────────┘               │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────┐
│                    Data Source: Tardis.dev                           │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐               │
│  │ Level-2 OBQ  │  │  Trades      │  │  Funding     │               │
│  │  (40+ exchanges) │ │  (full depth)│  │  (perp only) │               │
│  └──────────────┘  └──────────────┘  └──────────────┘               │
└─────────────────────────────────────────────────────────────────────┘
```

이 아키텍처의 핵심은 역할 분리입니다:

  • Rust hftbacktest: 초저지연 주문서 처리, 정확한 체결 시뮬레이션
  • Python + HolySheep AI: 복잡한 신호 생성, 머신러닝 모델 추론
  • Tardis: 고품질 시장 데이터 공급

3. 개발 환경 구축

3.1 시스템 요구사항

구성 요소최소 사양권장 사양
CPU8 cores32+ cores (AMD EPYC)
RAM32 GB128 GB DDR5
Storage500 GB SSD2 TB NVMe Gen4
OSUbuntu 22.04Ubuntu 24.04 LTS
Network1 Gbps10 Gbps

3.2 Rust 환경 설치

# Rust 설치 (hftbacktest 컴파일 필요)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env

nightly toolchain for better optimization

rustup default nightly rustup component add rustfmt clippy

메모리 할당자 최적화

apt-get install -y libtinfo-dev libgoogle-perftools-dev

Rust 프로젝트 생성

cargo new hft_backtest_project cd hft_backtest_project

3.3 Python 환경 설정

# Python 3.11+ 권장
python3.11 -m venv venv
source venv/bin/activate

핵심 의존성 설치

pip install --upgrade pip pip install \ py-tardis-client \ pandas numpy \ asyncpg motor \ websockets aiohttp \ holy-sheep-python-sdk \ # HolySheep AI SDK redis \ pydantic

HolySheep AI SDK 설정

export HOLYSHEEP_API_KEY="YOUR_HOLYSHEEP_API_KEY" export HOLYSHEEP_BASE_URL="https://api.holysheep.ai/v1"

3.4 Cargo.toml 설정

[package]
name = "hft_backtest"
version = "1.0.0"
edition = "2021"

[dependencies]

고성능 회측 엔진

hftbacktest = { git = "https://github.com/nkocua/hftbacktest", branch = "main", features = ["full"] }

직렬화

serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_bytes = "1.0" parquet = "51.0" arrow = "51.0"

비동기 처리

tokio = { version = "1.36", features = ["full"] } futures = "0.3"

데이터 처리

csv = "1.3" byteorder = "1.5"

로깅

tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] }

메모리 최적화

ahash = "0.8" parking_lot = "0.12" smallvec = "1.13" [profile.release] opt-level = 3 lto = true codegen-units = 1 strip = true

4. Tardis 데이터 연동

4.1 Tardis API 설정

Tardis는 다양한 데이터 유형을 제공합니다. 고빈도 매매 전략에는 Level-2 주문서체결 데이터가 필수입니다.

# tardis_config.json
{
  "exchange": "binance",
  "symbols": ["BTCUSDT", "ETHUSDT"],
  "data_types": ["book_snapshot", "book_update", "trade"],
  "start_date": "2024-01-01",
  "end_date": "2024-12-31",
  "timeframe": "tick",
  "format": "parquet"
}

4.2 Python으로 Tardis 데이터 다운로드

#!/usr/bin/env python3
"""
Tardis.dev에서 Level-2 주문서 및 체결 데이터 다운로드
"""
import asyncio
from tardis_client import TardisClient, Message
from holy_sheep_client import HolySheep
import pyarrow as pa
import pyarrow.parquet as pq
from datetime import datetime, timedelta
import os

TARDIS_API_KEY = os.getenv("TARDIS_API_KEY")
HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY")

class TardisDataDownloader:
    def __init__(self, exchange: str, symbol: str):
        self.exchange = exchange
        self.symbol = symbol
        self.client = TardisClient()
        self.holy_sheep = HolySheep(api_key=HOLYSHEEP_API_KEY)
        
    async def download_book_snapshot(self, timestamp: datetime) -> dict:
        """Level-2 주문서 스냅샷 조회"""
        response = await self.client.book_snapshot(
            exchange=self.exchange,
            symbol=self.symbol,
            timestamp=timestamp
        )
        return response
    
    async def download_trades(self, start: datetime, end: datetime):
        """체결 데이터 다운로드"""
        trades = []
        async for message in self.client.trades(
            exchange=self.exchange,
            symbol=self.symbol,
            from_timestamp=start,
            to_timestamp=end
        ):
            trades.append({
                "timestamp": message.timestamp,
                "price": float(message.price),
                "side": message.side.value if hasattr(message.side, 'value') else message.side,
                "size": float(message.amount),
                "trade_id": message.id
            })
        return trades
    
    async def process_realtime_stream(self):
        """실시간 스트림 처리 및 HolySheep AI 연동"""
        latest_book = {}
        
        async for message in self.client.realtime(self.exchange, self.symbol):
            if message.type == "book_snapshot":
                latest_book["snapshot"] = message.data
                latest_book["timestamp"] = message.timestamp
                
            elif message.type == "book_update":
                # 주문서 업데이트:apply
                for bid in message.data.get("b", []):
                    price, size = float(bid[0]), float(bid[1])
                    if size == 0:
                        latest_book["snapshot"]["bids"].pop(price, None)
                    else:
                        latest_book["snapshot"]["bids"][price] = size
                        
                for ask in message.data.get("a", []):
                    price, size = float(ask[0]), float(ask[1])
                    if size == 0:
                        latest_book["snapshot"]["asks"].pop(price, None)
                    else:
                        latest_book["snapshot"]["asks"][price] = size
                
                # HolySheep AI로 주문서 불균형 분석
                imbalance = self._calculate_book_imbalance(latest_book["snapshot"])
                
                # DeepSeek V3으로 급격한 변화 탐지
                analysis = await self.holy_sheep.completions.create(
                    model="deepseek-v3.2",
                    messages=[{
                        "role": "user",
                        "content": f"""Analyze this order book imbalance: {imbalance}
                        Current timestamp: {latest_book['timestamp']}
                        Should we adjust our market making spread? Reply with JSON format:
                        {{"action": "WIDEN|NORMAL|NARROW", "confidence": 0.0-1.0}}"""
                    }]
                )
                
                # 분석 결과 로깅
                print(f"[{latest_book['timestamp']}] Imbalance: {imbalance:.3f}, AI: {analysis.choices[0].message.content}")
    
    def _calculate_book_imbalance(self, book: dict) -> float:
        """주문서 불균형 계산"""
        bid_volume = sum(book["bids"].values())
        ask_volume = sum(book["asks"].values())
        total = bid_volume + ask_volume
        if total == 0:
            return 0.0
        return (bid_volume - ask_volume) / total

async def main():
    downloader = TardisDataDownloader("binance", "BTCUSDT")
    
    # 과거 데이터 다운로드
    start = datetime(2024, 6, 1)
    end = datetime(2024, 6, 2)
    
    print(f"Downloading trades from {start} to {end}...")
    trades = await downloader.download_trades(start, end)
    print(f"Downloaded {len(trades)} trades")
    
    # 실시간 스트림 시작
    await downloader.process_realtime_stream()

if __name__ == "__main__":
    asyncio.run(main())

4.3 Tardis 데이터를 Parquet로 변환

#!/usr/bin/env python3
"""
Tardis CSV/JSON 데이터를 hftbacktest 호환 Parquet로 변환
"""
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq
from pathlib import Path
import struct
import os

class TardisConverter:
    """Tardis 데이터를 hftbacktest 친화적 형식으로 변환"""
    
    # Tardis raw format columns
    TARDIS_COLUMNS = [
        "timestamp",      # nanoseconds since epoch
        "exchange",       # exchange code
        "symbol",         # trading pair
        "side",           # bid/ask
        "price",          # price
        "size",           # quantity
        "order_id",       # order identifier
        "trade_id"        # trade identifier
    ]
    
    def __init__(self, output_dir: str):
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(parents=True, exist_ok=True)
    
    def convert_csv_to_parquet(self, csv_path: str, symbol: str):
        """CSV를 Parquet로 변환"""
        print(f"Reading {csv_path}...")
        
        df = pd.read_csv(csv_path)
        
        # Tardis 타임스탬프 처리 (마이크로초 단위)
        if "local_timestamp" in df.columns:
            df["timestamp"] = pd.to_datetime(df["local_timestamp"])
        elif "timestamp" in df.columns:
            df["timestamp"] = pd.to_datetime(df["timestamp"])
        
        df["timestamp"] = df["timestamp"].astype("int64")  # nanoseconds
        
        # 바이너리 직렬화를 위한 형식 변환
        df["price"] = df["price"].astype("float64")
        df["size"] = df["size"].astype("float64")
        
        # 심볼 필터링
        if "symbol" in df.columns:
            df = df[df["symbol"].str.contains(symbol, case=False)]
        
        # Parquet 저장
        output_path = self.output_dir / f"{symbol.replace('/', '_')}.parquet"
        df.to_parquet(output_path, compression="snappy", engine="pyarrow")
        print(f"Saved to {output_path}")
        
        return output_path
    
    def create_orderbook_snapshot(self, trades_df: pd.DataFrame) -> pd.DataFrame:
        """체결 데이터에서 주문서 스냅샷 재구성"""
        # 이 방법은 approximation이므로 주의
        snapshots = []
        
        for timestamp, group in trades_df.groupby(
            pd.to_datetime(trades_df["timestamp"], unit="ns").floor("1s")
        ):
            bids = group[group["side"] == "buy"].nlargest(20, "price")
            asks = group[group["side"] == "sell"].nsmallest(20, "price")
            
            snapshots.append({
                "timestamp": timestamp.value,
                "best_bid": bids["price"].max() if len(bids) > 0 else None,
                "best_ask": asks["price"].min() if len(asks) > 0 else None,
                "bid_volume": bids["size"].sum() if len(bids) > 0 else 0,
                "ask_volume": asks["size"].sum() if len(asks) > 0 else 0,
            })
        
        return pd.DataFrame(snapshots)

def convert_tardis_data():
    converter = TardisConverter("./data/parquet")
    
    # Tardis에서 다운로드한 CSV 파일들
    csv_files = [
        "./data/raw/binance_btcusdt_trades_2024.csv",
        "./data/raw/binance_ethusdt_trades_2024.csv"
    ]
    
    for csv_file in csv_files:
        if os.path.exists(csv_file):
            symbol = "BTCUSDT" if "btc" in csv_file else "ETHUSDT"
            converter.convert_csv_to_parquet(csv_file, symbol)

if __name__ == "__main__":
    convert_tardis_data()

5. hftbacktest 핵심 구현

5.1 Rust 기본 구조

// src/lib.rs
use hftbacktest::{
    BacktestEngine, DataSource, DepthManager, Error,
    indicators::{atr, sma, rsi},
    types::{OrderBook, Trade, Side, Position},
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use parking_lot::RwLock;

/// 시장 Regime 분류
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MarketRegime {
    LowVolatility,    // 박스권
    HighVolatility,   // 변동성 확대
    Trending,         // 트렌드
    MeanReverting,    // 평균 회귀
}

/// 매매 신호 구조체
#[derive(Debug, Clone)]
pub struct MarketSignal {
    pub regime: MarketRegime,
    pub book_imbalance: f64,      // [-1, 1]
    pub volatility_ratio: f64,
    pub momentum_score: f64,
    pub timestamp: i64,
}

/// 매매 파라미터
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketMakingParams {
    pub base_spread_bps: f64,      // 베이직 스프레드 (bps)
    pub skew_multiplier: f64,      // 가격 왜곡 계수
    pub inventory_target: f64,     // 목표库存 (0 = 중립)
    pub inventory_skew: f64,       //库存 기울기 계수
    pub order_size: f64,           // 주문 크기
    pub max_position: f64,         // 최대 포지션
    pub quote_interval_ms: u64,    // 호가 갱신 주기
}

impl Default for MarketMakingParams {
    fn default() -> Self {
        Self {
            base_spread_bps: 5.0,
            skew_multiplier: 0.3,
            inventory_target: 0.0,
            inventory_skew: 0.1,
            order_size: 0.01,
            max_position: 1.0,
            quote_interval_ms: 100,
        }
    }
}

/// 메인 회측 엔진
pub struct HFTBacktester {
    engine: BacktestEngine,
    params: MarketMakingParams,
    signals: Arc>>,
    last_quote_time: i64,
}

impl HFTBacktester {
    pub fn new(data_path: &str) -> Result {
        let mut engine = BacktestEngine::new();
        
        // Tardis Parquet 데이터 로드
        engine.load_data(DataSource::Parquet(data_path))?;
        
        Ok(Self {
            engine,
            params: MarketMakingParams::default(),
            signals: Arc::new(RwLock::new(Vec::new())),
            last_quote_time: 0,
        })
    }
    
    pub fn set_params(&mut self, params: MarketMakingParams) {
        self.params = params;
    }
    
    /// 주문서 불균형 계산
    pub fn calculate_book_imbalance(&self, depth: &DepthManager) -> f64 {
        let mut bid_volume = 0.0;
        let mut ask_volume = 0.0;
        
        // 상위 10 레벨 집계
        for level in 0..10 {
            if let Some(bid) = depth.bid(level) {
                bid_volume += bid.size as f64;
            }
            if let Some(ask) = depth.ask(level) {
                ask_volume += ask.size as f64;
            }
        }
        
        let total = bid_volume + ask_volume;
        if total == 0.0 {
            return 0.0;
        }
        
        (bid_volume - ask_volume) / total
    }
    
    /// 최적 호가 가격 계산
    pub fn calculate_quote_prices(
        &self,
        mid_price: f64,
        book_imbalance: f64,
        position: f64,
    ) -> (f64, f64) {
        // 베이직 스프레드 계산
        let base_spread = mid_price * (self.params.base_spread_bps / 10000.0);
        
        //库存 조정
        let inventory_adjustment = position * self.params.inventory_skew;
        
        // 주문서 불균형 조정
        let imbalance_adjustment = mid_price * book_imbalance * 0.001;
        
        let bid_price = mid_price 
            - base_spread / 2.0 
            + inventory_adjustment 
            - imbalance_adjustment;
            
        let ask_price = mid_price 
            + base_spread / 2.0 
            + inventory_adjustment 
            + imbalance_adjustment;
        
        (bid_price, ask_price)
    }
    
    /// 메인 회측 루프
    pub fn run(&mut self) -> Result {
        let mut total_pnl = 0.0;
        let mut total_trades = 0;
        let mut max_drawdown = 0.0;
        let mut equity = 0.0;
        let mut peak_equity = 0.0;
        
        // 인디케이터 초기화
        let mut position = Position::new();
        let mut order_id = 0u64;
        
        // 지표 버퍼
        let mut atr_buffer = Vec::with_capacity(100);
        let mut prices = Vec::with_capacity(100);
        
        while let Some(tick) = self.engine.next_tick()? {
            let timestamp = tick.timestamp;
            let mid_price = (tick.bid[0].price + tick.ask[0].price) / 2.0;
            
            // 주문서 불균형 업데이트
            let book_imbalance = self.calculate_book_imbalance(&self.engine.depth());
            
            // 시장 Regime 분류 (간단한 휴리스틱)
            let regime = self.classify_regime(&prices, &atr_buffer);
            
            // 신호 저장
            let signal = MarketSignal {
                regime: regime.clone(),
                book_imbalance,
                volatility_ratio: 1.0,
                momentum_score: 0.0,
                timestamp,
            };
            self.signals.write().push(signal);
            
            // 호가 갱신 주기에 따른 주문 제출
            if timestamp - self.last_quote_time >= self.params.quote_interval_ms * 1_000_000 {
                let (bid_price, ask_price) = self.calculate_quote_prices(
                    mid_price,
                    book_imbalance,
                    position.net_position(),
                );
                
                // Bid 주문
                self.engine.submit_order(
                    order_id,
                    Side::Bid,
                    bid_price,
                    self.params.order_size,
                    timestamp,
                )?;
                order_id += 1;
                
                // Ask 주문
                self.engine.submit_order(
                    order_id,
                    Side::Ask,
                    ask_price,
                    self.params.order_size,
                    timestamp,
                )?;
                order_id += 1;
                
                self.last_quote_time = timestamp;
            }
            
            // 체결 처리
            for fill in self.engine.process_fills(timestamp)? {
                total_trades += 1;
                
                let pnl = if fill.side == Side::Ask {
                    fill.price * fill.size
                } else {
                    -(fill.price * fill.size)
                };
                
                total_pnl += pnl;
                position.update(fill.side, fill.price, fill.size);
            }
            
            // PnL 업데이트
            equity = total_pnl + position.unrealized_pnl(mid_price);
            peak_equity = peak_equity.max(equity);
            max_drawdown = max_drawdown.max(peak_equity - equity);
            
            // 가격 이력 업데이트
            prices.push(mid_price);
            if prices.len() > 100 {
                prices.remove(0);
            }
        }
        
        Ok(BacktestResult {
            total_pnl,
            total_trades,
            max_drawdown,
            sharpe_ratio: self.calculate_sharpe(),
            win_rate: self.calculate_win_rate(),
            final_equity: equity,
        })
    }
    
    fn classify_regime(&self, prices: &[f64], _atr: &[f64]) -> MarketRegime {
        if prices.len() < 20 {
            return MarketRegime::LowVolatility;
        }
        
        let recent_std = self.stddev(&prices[prices.len()-20..]);
        let volatility_threshold = 0.002; // 0.2%
        
        if recent_std < volatility_threshold {
            MarketRegime::LowVolatility
        } else if recent_std > volatility_threshold * 3.0 {
            MarketRegime::HighVolatility
        } else {
            MarketRegime::Trending
        }
    }
    
    fn stddev(&self, values: &[f64]) -> f64 {
        if values.is_empty() {
            return 0.0;
        }
        let mean = values.iter().sum::() / values.len() as f64;
        let variance = values.iter()
            .map(|v| (v - mean).powi(2))
            .sum::() / values.len() as f64;
        variance.sqrt()
    }
    
    fn calculate_sharpe(&self) -> f64 {
        // Annualized Sharpe Ratio 계산
        1.5 // 예시값
    }
    
    fn calculate_win_rate(&self) -> f64 {
        0.62 // 예시값
    }
}

/// 회측 결과
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BacktestResult {
    pub total_pnl: f64,
    pub total_trades: usize,
    pub max_drawdown: f64,
    pub sharpe_ratio: f64,
    pub win_rate: f64,
    pub final_equity: f64,
}

5.2 Python Bindings와 HolySheep AI 연동

# src/py_bindings.rs
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
use hftbacktest::{HFTBacktester, MarketMakingParams, MarketSignal};
use std::sync::Arc;
use parking_lot::RwLock;

/// Python 래퍼 클래스
#[pyclass]
pub struct PyHFTBacktester {
    inner: Arc>>,
    signals: Arc>>,
}

#[pymethods]
impl PyHFTBacktester {
    #[new]
    fn new(data_path: String) -> PyResult {
        let backtester = HFTBacktester::new(&data_path)
            .map_err(|e| PyErr::new::(format!("{}", e)))?;
        
        Ok(Self {
            inner: Arc::new(RwLock::new(Some(backtester))),
            signals: Arc::new(RwLock::new(Vec::new())),
        })
    }
    
    fn set_params(&self, params: PyMarketMakingParams) -> PyResult<()> {
        let mut guard = self.inner.write();
        if let Some(ref mut bt) = *guard {
            bt.set_params(params.into());
            Ok(())
        } else {
            Err(PyErr::new::("Engine not initialized"))
        }
    }
    
    fn run(&self) -> PyResult {
        let mut guard = self.inner.write();
        if let Some(ref mut bt) = *guard {
            let result = bt.run()
                .map_err(|e| PyErr::new::(format!("{}", e)))?;
            
            Ok(result.into())
        } else {
            Err(PyErr::new::("Engine not initialized"))
        }
    }
    
    fn get_signals(&self) -> Vec {
        self.signals.read()
            .iter()
            .map(|s| s.clone().into())
            .collect()
    }
}

#[pyclass]
pub struct PyMarketMakingParams {
    #[pyo3(get, set)]
    pub base_spread_bps: f64,
    #[pyo3(get, set)]
    pub skew_multiplier: f64,
    #[pyo3(get, set)]
    pub inventory_target: f64,
    #[pyo3(get, set)]
    pub inventory_skew: f64,
    #[pyo3(get, set)]
    pub order_size: f64,
    #[pyo3(get, set)]
    pub max_position: f64,
    #[pyo3(get, set)]
    pub quote_interval_ms: u64,
}

impl From for MarketMakingParams {
    fn from(p: PyMarketMakingParams) -> Self {
        MarketMakingParams {
            base_spread_bps: p.base_spread_bps,
            skew_multiplier: p.skew_multiplier,
            inventory_target: p.inventory_target,
            inventory_skew: p.inventory_skew,
            order_size: p.order_size,
            max_position: p.max_position,
            quote_interval_ms: p.quote_interval_ms,
        }
    }
}

impl From for PyMarketMakingParams {
    fn from(p: MarketMakingParams) -> Self {
        Self {
            base_spread_bps: p.base_spread_bps,
            skew_multiplier: p.skew_multiplier,
            inventory_target: p.inventory_target,
            inventory_skew: p.inventory_skew,
            order_size: p.order_size,
            max_position: p.max_position,
            quote_interval_ms: p.quote_interval_ms,
        }
    }
}

#[pyclass]
pub struct PyMarketSignal {
    #[pyo3(get, set)]
    pub regime: String,
    #[pyo3(get, set)]
    pub book_imbalance: f64,
    #[pyo3(get, set)]
    pub volatility_ratio: f64,
    #[pyo3(get, set)]
    pub momentum_score: f64,
    #[pyo3(get, set)]
    pub timestamp: i64,
}

impl From for PyMarketSignal {
    fn from(s: MarketSignal) -> Self {
        Self {
            regime: format!("{:?}", s.regime),
            book_imbalance: s.book_imbalance,
            volatility_ratio: s.volatility_ratio,
            momentum_score: s.momentum_score,
            timestamp: s.timestamp,
        }
    }
}

#[pyclass]
pub struct PyBacktestResult {
    #[pyo3(get, set)]
    pub total_pnl: f64,
    #[pyo3(get, set)]
    pub total_trades: usize,
    #[pyo3(get, set)]
    pub max_drawdown: f64,
    #[pyo3(get, set)]
    pub sharpe_ratio: f64,
    #[pyo3(get, set)]
    pub win_rate: f64,
    #[pyo3(get, set)]
    pub final_equity: f64,
}

impl From for PyBacktestResult {
    fn from(r: hftbacktest::BacktestResult) -> Self {
        Self {
            total_pnl: r.total_pnl,