글쓴이: HolySheep AI 기술 아키텍처팀 | 최종 수정: 2026년 4월 28일
📊 실전 사례: 서울의 한 헤지펀드(q-alpha Capital)가 기존 OnixS 프레임워크에서
hftbacktest+Tardis조합으로 전환하여 월 서버 비용 $12,000 → $3,400 절감, 주문서 처리 지연 450ms → 85ms 개선을 달성한 과정을 공개합니다.
📋 목차
- 1. 프로젝트 개요와 왜 이 스택인가
- 2. 전체 아키텍처 설계
- 3. 개발 환경 구축
- 4. Tardis 데이터 연동
- 5. hftbacktest 핵심 구현
- 6. HolySheep AI로 시장 신호 생성
- 7. 성능 최적화 기법
- 8. 스택 비교표
- 9. 이런 팀에 적합 / 비적합
- 10. 가격과 ROI 분석
- 11. 왜 HolySheep를 선택해야 하나
- 12. 자주 발생하는 오류와 해결책
- 13. 시작하기
1. 프로젝트 개요와 왜 이 스택인가
배경: 고빈도 매매 전략의 회측 도전
고빈도 매매(HFT) 전략을 개발할 때 가장 큰 병목은 실제 시장 데이터의 질과 회측 엔진의 처리 속도입니다. 많은 팀이 다음과 같은 문제로 고생합니다:
- 샘플 편향: aggregated 데이터로 실제 체결 경험을 재현하지 못함
- 호가창 처리: Level-2 주문서를 정확히 시뮬레이션하지 못함
- 시장'impact: 자신의 주문이 시장에 미치는 영향을 무시
- 데이터 비용: 고품질 시장 데이터의 천문학적 비용
해결책: hftbacktest + Tardis 조합
hftbacktest는 Rust로 작성된 고성능 고빈도 거래 회측 프레임워크로:
- 네이티브 Level-2 주문서 시뮬레이션
- sub-millisecond 타임스탬프 정밀도
- tick-by-tick 실행 시뮬레이션
- CSV/Parquet/SQLite 데이터 소스 지원
Tardis.dev는 암호화폐 시장 데이터의 사실상 표준:
- Binance, Bybit, OKX 등 40+ 거래소
- Level-2 주문서 원시 데이터
- 실시간 및 역사적 데이터 모두 제공
- websocket과 HTTP API 모두 지원
저의 경험담
저는 서울의 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 시스템 요구사항
구성 요소 최소 사양 권장 사양
CPU 8 cores 32+ cores (AMD EPYC)
RAM 32 GB 128 GB DDR5
Storage 500 GB SSD 2 TB NVMe Gen4
OS Ubuntu 22.04 Ubuntu 24.04 LTS
Network 1 Gbps 10 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,