Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 170 additions & 25 deletions examples/backtest_demo.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# backtest_demo_simple.py
import argparse
import json
import os
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timedelta
from typing import Any, Dict, List, Tuple
from typing import Any, Dict, List, Optional, Tuple

from live_trade_bench.systems.bitmex_system import BitMEXPortfolioSystem
from live_trade_bench.systems.polymarket_system import PolymarketPortfolioSystem
Expand All @@ -17,17 +18,37 @@
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))


def get_backtest_config() -> Dict[str, Any]:
def get_backtest_config(args: Optional[argparse.Namespace] = None) -> Dict[str, Any]:
"""Get backtest configuration from CLI args or defaults."""
if args is None:
# Defaults when no args provided
return {
"start_date": "2025-10-01",
"end_date": "2025-11-01",
"interval_days": 1,
"initial_cash": {"polymarket": 500.0, "stock": 1000.0, "bitmex": 1000.0},
"parallelism": int(os.environ.get("LTB_PARALLELISM", "16")),
"threshold": 0.2,
"market_num": 10,
"stock_num": 15,
"bitmex_num": 12,
}

# Use CLI args with fallback to defaults
return {
"start_date": "2025-10-01",
"end_date": "2025-11-01",
"interval_days": 1,
"initial_cash": {"polymarket": 500.0, "stock": 1000.0, "bitmex": 1000.0}, # Updated to $1,000
"start_date": args.start_date or "2025-10-01",
"end_date": args.end_date or "2025-11-01",
"interval_days": args.interval_days,
"initial_cash": {
"polymarket": args.initial_cash_polymarket,
"stock": args.initial_cash_stock,
"bitmex": args.initial_cash_bitmex,
},
"parallelism": int(os.environ.get("LTB_PARALLELISM", "16")),
"threshold": 0.2,
"market_num": 10,
"stock_num": 15,
"bitmex_num": 12,
"threshold": args.threshold,
"market_num": args.polymarket_count,
"stock_num": args.stock_count,
"bitmex_num": args.bitmex_count,
}


Expand Down Expand Up @@ -58,6 +79,8 @@ def build_systems(
market_num: int = 5,
stock_num: int = 15,
bitmex_num: int = 12,
custom_stocks: Optional[List[str]] = None,
custom_bitmex_symbols: Optional[List[str]] = None,
):
systems: Dict[str, Dict[str, Any]] = {"polymarket": {}, "stock": {}, "bitmex": {}}

Expand All @@ -70,18 +93,26 @@ def build_systems(
)

if run_stock:
print("Pre-fetching stock data...")
from live_trade_bench.fetchers.stock_fetcher import fetch_trending_stocks
if custom_stocks:
print(f"Using custom stock list: {custom_stocks}")
verified_stocks = custom_stocks
else:
print("Pre-fetching trending stock data...")
from live_trade_bench.fetchers.stock_fetcher import fetch_trending_stocks

verified_stocks = fetch_trending_stocks(stock_num)
verified_stocks = fetch_trending_stocks(stock_num)

if run_bitmex:
print("Pre-fetching BitMEX contracts...")
from live_trade_bench.fetchers.bitmex_fetcher import BitMEXFetcher
fetcher = BitMEXFetcher()
trending_contracts = fetcher.get_trending_contracts(limit=bitmex_num)
bitmex_symbols = [c["symbol"] for c in trending_contracts]
print(f" Using {len(bitmex_symbols)} BitMEX contracts: {bitmex_symbols[:5]}...")
if custom_bitmex_symbols:
print(f"Using custom BitMEX symbols: {custom_bitmex_symbols}")
bitmex_symbols = custom_bitmex_symbols
else:
print("Pre-fetching trending BitMEX contracts...")
from live_trade_bench.fetchers.bitmex_fetcher import BitMEXFetcher
fetcher = BitMEXFetcher()
trending_contracts = fetcher.get_trending_contracts(limit=bitmex_num)
bitmex_symbols = [c["symbol"] for c in trending_contracts]
print(f" Using {len(bitmex_symbols)} BitMEX contracts: {bitmex_symbols[:5]}...")

for model_name, model_id in models:
if run_polymarket:
Expand Down Expand Up @@ -277,9 +308,125 @@ def save_models_data(
)


def parse_args() -> argparse.Namespace:
"""Parse command-line arguments for backtest configuration."""
parser = argparse.ArgumentParser(
description="Run backtests for trading models across multiple exchanges",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)

# Time period
parser.add_argument(
"--start-date",
type=str,
default="2025-10-01",
help="Backtest start date (YYYY-MM-DD)",
)
parser.add_argument(
"--end-date",
type=str,
default="2025-11-01",
help="Backtest end date (YYYY-MM-DD)",
)
parser.add_argument(
"--interval-days",
type=int,
default=1,
help="Interval between trading days",
)

# Exchange selection
parser.add_argument(
"--exchanges",
type=str,
default="stock,polymarket,bitmex",
help="Comma-separated list of exchanges to test (stock,polymarket,bitmex)",
)

# Asset counts (for auto-fetching trending assets)
parser.add_argument(
"--stock-count",
type=int,
default=15,
help="Number of trending stocks to fetch",
)
parser.add_argument(
"--polymarket-count",
type=int,
default=10,
help="Number of Polymarket markets to fetch",
)
parser.add_argument(
"--bitmex-count",
type=int,
default=12,
help="Number of BitMEX contracts to fetch",
)

# Custom asset lists (override auto-fetching)
parser.add_argument(
"--stocks",
type=str,
default=None,
help="Custom stock symbols (comma-separated, e.g., AAPL,TSLA,MSFT)",
)
parser.add_argument(
"--bitmex-symbols",
type=str,
default=None,
help="Custom BitMEX symbols (comma-separated, e.g., XBTUSDT,PEPEUSDT)",
)

# Initial cash
parser.add_argument(
"--initial-cash-stock",
type=float,
default=1000.0,
help="Initial cash for stock trading",
)
parser.add_argument(
"--initial-cash-polymarket",
type=float,
default=500.0,
help="Initial cash for Polymarket trading",
)
parser.add_argument(
"--initial-cash-bitmex",
type=float,
default=1000.0,
help="Initial cash for BitMEX trading",
)

# Polymarket specific
parser.add_argument(
"--threshold",
type=float,
default=0.2,
help="Polymarket volume threshold filter",
)

return parser.parse_args()


def main():
args = parse_args()
print("🔮 Parallel Backtest (Per-agent systems)")
cfg = get_backtest_config()

# Parse exchange selection
exchanges = [x.strip().lower() for x in args.exchanges.split(",")]
run_polymarket = "polymarket" in exchanges
run_stock = "stock" in exchanges
run_bitmex = "bitmex" in exchanges

# Parse custom asset lists
custom_stocks = [s.strip() for s in args.stocks.split(",")] if args.stocks else None
custom_bitmex_symbols = (
[s.strip() for s in args.bitmex_symbols.split(",")]
if args.bitmex_symbols
else None
)

cfg = get_backtest_config(args)
all_models = get_base_model_configs()

# Filter to GPT + Anthropic only
Expand All @@ -291,10 +438,6 @@ def main():

print(f"⚡ Using {len(models)} models (GPT + Anthropic only)")
print(f" Filtered from {len(all_models)} total models")

run_polymarket = True
run_stock = True
run_bitmex = True
market_count = sum([run_polymarket, run_stock, run_bitmex])
market_names = []
if run_stock:
Expand All @@ -317,7 +460,9 @@ def main():
threshold=cfg["threshold"],
market_num=cfg["market_num"],
stock_num=cfg["stock_num"],
bitmex_num=cfg["bitmex_num"]
bitmex_num=cfg["bitmex_num"],
custom_stocks=custom_stocks,
custom_bitmex_symbols=custom_bitmex_symbols,
)

for i, d in enumerate(days, 1):
Expand Down
24 changes: 21 additions & 3 deletions frontend/src/components/charts/AreaChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@ const AreaChart: React.FC<AreaChartProps> = ({
title = "Portfolio Composition Over Time",
size = 'medium'
}) => {
// Format price with adaptive precision for small crypto values
const formatPrice = (value: number): string => {
if (value >= 1.0) {
// Standard formatting for larger values
return value.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 4
});
} else if (value >= 0.0001) {
// Show 8 decimals for small values
return value.toFixed(8).replace(/\.?0+$/, '');
} else if (value > 0) {
// Scientific notation for very small values (micro-cap tokens)
return value.toExponential(2);
}
return '0';
};

if (!portfolioHistory || portfolioHistory.length === 0) {
return (
<div className={`area-chart-container ${size}`}>
Expand Down Expand Up @@ -157,11 +175,11 @@ const AreaChart: React.FC<AreaChartProps> = ({
label: (context: any) => {
const label = context.dataset.label || '';
const value = context.parsed.y;
return `${label}: $${value.toFixed(2)}`;
return `${label}: $${formatPrice(value)}`;
},
footer: (tooltipItems: any[]) => {
const total = tooltipItems.reduce((sum, item) => sum + item.parsed.y, 0);
return `Total: $${total.toFixed(2)}`;
return `Total: $${formatPrice(total)}`;
},
},
},
Expand Down Expand Up @@ -193,7 +211,7 @@ const AreaChart: React.FC<AreaChartProps> = ({
size: size === 'small' ? 8 : 10,
},
callback: (value: any) => {
return `$${value}`;
return `$${formatPrice(value)}`;
},
},
},
Expand Down
20 changes: 14 additions & 6 deletions live_trade_bench/agents/base_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,15 @@ def _format_price_history(
for i, h in enumerate(reversed(recent_history)):
hist_price = h.get("price", 0.0)
hist_date = h.get("date", "Unknown Date")
price_str = (
f"close price ${hist_price:,.2f}"
if is_stock
else f"{hist_price:.4f}"
)
# LLM-friendly formatting: scientific notation for small values
if is_stock:
price_str = f"close price ${hist_price:,.2f}"
else:
# Crypto: use scientific notation for micro-cap tokens
if hist_price >= 1.0:
price_str = f"{hist_price:.4f}"
else:
price_str = f"{hist_price:.2e}"
change_str = "N/A"
original_index = len(recent_history) - 1 - i
if original_index > 0:
Expand All @@ -192,7 +196,11 @@ def _format_price_history(
if prev_day_price > 0:
change = hist_price - prev_day_price
change_pct = (change / prev_day_price) * 100
change_str = f"{change:+.2f} ({change_pct:+.2f}%)"
# Use scientific notation for small crypto price changes
if not is_stock and abs(change) < 0.01:
change_str = f"{change:+.2e} ({change_pct:+.2f}%)"
else:
change_str = f"{change:+.2f} ({change_pct:+.2f}%)"
lines.append(f" - {hist_date}: {price_str} (Change: {change_str})")
else:
current_price = self.history_tail(ticker, 1)
Expand Down
10 changes: 8 additions & 2 deletions live_trade_bench/agents/bitmex_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,15 @@ def _prepare_market_analysis(self, market_data: Dict[str, Dict[str, Any]]) -> st
price = data.get("current_price", 0.0)
price_history = data.get("price_history", [])

# Format price with crypto-specific styling
# Format price with crypto-specific styling (LLM-friendly)
# Use scientific notation for small values to save tokens and enable math
if "USD" in symbol or "USDT" in symbol:
analysis_parts.append(f"{symbol}: Current price is ${price:,.2f}")
if price >= 1.0:
# Standard formatting for larger prices
analysis_parts.append(f"{symbol}: Current price is ${price:,.4f}")
else:
# Scientific notation for micro-cap tokens (PEPE, BONK, etc.)
analysis_parts.append(f"{symbol}: Current price is ${price:.2e}")
else:
analysis_parts.append(f"{symbol}: Current price is {price:.6f}")

Expand Down
Loading
Loading