Add initial implementation of backtesting framework with CLI interface. Introduce core modules for data loading, trade management, performance metrics, and logging. Include Supertrend indicator calculations and slippage estimation. Update .gitignore to exclude logs and CSV files.

This commit is contained in:
2026-01-09 19:53:01 +08:00
parent a25499e016
commit c4aa965a98
15 changed files with 424 additions and 568 deletions

52
trade.py Normal file
View File

@@ -0,0 +1,52 @@
from __future__ import annotations
from dataclasses import dataclass
import pandas as pd
from market_costs import okx_fee, estimate_slippage_rate
from intrabar import entry_slippage_row
@dataclass
class TradeState:
cash: float = 1000.0
qty: float = 0.0
entry_px: float | None = None
max_px: float | None = None
stop_loss_frac: float = 0.02
fee_bps: float = 10.0
slippage_bps: float = 2.0
def enter_long(state: TradeState, price: float) -> dict:
if state.qty > 0:
return {}
px = entry_slippage_row(price, 0.0, state.slippage_bps)
qty = state.cash / px
fee = okx_fee(state.fee_bps, state.cash)
state.qty = max(qty - fee / px, 0.0)
state.cash = 0.0
state.entry_px = px
state.max_px = px
return {"side": "BUY", "price": px, "qty": state.qty, "fee": fee}
def maybe_trailing_stop(state: TradeState, price: float) -> float:
if state.qty <= 0:
return float("inf")
state.max_px = max(state.max_px or price, price)
trail_px = state.max_px * (1.0 - state.stop_loss_frac)
return trail_px
def exit_long(state: TradeState, price: float) -> dict:
if state.qty <= 0:
return {}
notional = state.qty * price
slip = estimate_slippage_rate(state.slippage_bps, notional)
fee = okx_fee(state.fee_bps, notional)
cash_back = notional - slip - fee
event = {"side": "SELL", "price": price, "qty": state.qty, "fee": fee, "slippage": slip}
state.cash = cash_back
state.qty = 0.0
state.entry_px = None
state.max_px = None
return event