Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
82132c1
chore: updated gitignore
andrewBatutin Oct 25, 2025
613498b
feat: added demo for bitmex integration
andrewBatutin Oct 25, 2025
11701ce
feat: improved logging
andrewBatutin Oct 25, 2025
fa37963
feat: updated backtesting for crypto
andrewBatutin Oct 26, 2025
f9732f9
feat: updated backtesting for crypto
andrewBatutin Oct 26, 2025
72dc177
feat: updated ui
andrewBatutin Oct 26, 2025
e7c4898
feat: added bitmex to home page
andrewBatutin Oct 27, 2025
ab2c367
feat: add subredits for crypo search
andrewBatutin Oct 27, 2025
0cb0e14
feat: testing crypto results
andrewBatutin Oct 28, 2025
53d51a4
feat: fixed issue with news fetching
andrewBatutin Oct 28, 2025
1b2b7c9
test: added smoke test with bullish bearish case for crypto llm
andrewBatutin Nov 1, 2025
041ed92
feat: updated default portfolio size for bitmex
andrewBatutin Nov 2, 2025
f640ca8
feat: config change
andrewBatutin Nov 4, 2025
1bf29a5
Merge branch 'main' of github.com:ulab-uiuc/live-trade-bench into fea…
andrewBatutin Nov 4, 2025
a5f12c8
tests: fixed test cases
andrewBatutin Nov 4, 2025
b20eb93
feat: updated bitmex schedule
andrewBatutin Nov 8, 2025
958ca4c
chore: applied isort
andrewBatutin Nov 8, 2025
7c458d0
chore: code style applied
andrewBatutin Nov 8, 2025
2b93eb8
chore: code style applied
andrewBatutin Nov 8, 2025
b36ef72
feat: updated defaluts crypt protfolio size
andrewBatutin Nov 8, 2025
c16c7ea
chore: remove .DS_Store files and add to .gitignore
andrewBatutin Nov 12, 2025
2c32496
chore: remove package-lock.json
andrewBatutin Nov 12, 2025
d31dbd3
Stop tracking CLAUDE.md (now in .gitignore)
andrewBatutin Nov 12, 2025
92137fa
Remove .DS_Store from git
andrewBatutin Nov 13, 2025
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
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,13 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
backend/**/*.json

# Test data (generated during tests, should not be committed)
tests/fixtures/test_data/

.claude
.idea
CLAUDE.md

# macOS system files
.DS_Store
2 changes: 2 additions & 0 deletions backend/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def get_base_model_configs() -> List[Tuple[str, str]]:
("GPT-o3", "openai/o3-2025-04-16"),
("Claude-Opus-4.1", "anthropic/claude-opus-4-1-20250805"),
("Claude-Opus-4", "anthropic/claude-opus-4-20250514"),
("Claude-Sonnet-4.5", "anthropic/claude-sonnet-4-5-20250929"),
("Claude-Sonnet-4", "anthropic/claude-sonnet-4-20250514"),
("Claude-Sonnet-3.7", "anthropic/claude-3-7-sonnet-latest"),
("Gemini-2.5-Flash", "gemini/gemini-2.5-flash"),
Expand Down Expand Up @@ -63,6 +64,7 @@ def get_base_model_configs() -> List[Tuple[str, str]]:
TRADING_CONFIG = {
"initial_cash_stock": 1000,
"initial_cash_polymarket": 500,
"initial_cash_bitmex": 1000,
"max_consecutive_failures": 3,
"recovery_wait_time": 3600,
"error_retry_time": 600,
Expand Down
66 changes: 63 additions & 3 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

from apscheduler.executors.pool import ThreadPoolExecutor
from apscheduler.schedulers.background import BackgroundScheduler

# Load environment variables from .env file
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
Expand All @@ -19,7 +22,11 @@
MockFetcherPolymarketSystem,
MockFetcherStockSystem,
)
from live_trade_bench.systems import PolymarketPortfolioSystem, StockPortfolioSystem
from live_trade_bench.systems import (
BitMEXPortfolioSystem,
PolymarketPortfolioSystem,
StockPortfolioSystem,
)

from .config import (
ALLOWED_ORIGINS,
Expand All @@ -37,16 +44,20 @@
from .news_data import update_news_data
from .price_data import (
get_next_price_update_time,
update_bitmex_prices_and_values,
update_polymarket_prices_and_values,
update_stock_prices_and_values,
)
from .routers import models, news, social, system
from .social_data import update_social_data
from .system_data import update_system_status

load_dotenv()

# Global system instances - Initialize immediately
stock_system = None
polymarket_system = None
bitmex_system = None
# Background scheduler instance; assigned during startup to keep reference alive
scheduler = None

Expand All @@ -68,6 +79,7 @@
# Initialize systems immediately when module loads
stock_system = STOCK_SYSTEMS[STOCK_MOCK_MODE].get_instance()
polymarket_system = POLYMARKET_SYSTEMS[POLYMARKET_MOCK_MODE].get_instance()
bitmex_system = BitMEXPortfolioSystem()

# Add agents for real systems
if STOCK_MOCK_MODE == MockMode.NONE:
Expand All @@ -78,13 +90,18 @@
for display_name, model_id in get_base_model_configs():
polymarket_system.add_agent(display_name, 500.0, model_id)

# Add BitMEX agents (paper trading with $1,000 each)
for display_name, model_id in get_base_model_configs():
bitmex_system.add_agent(display_name, 1000.0, model_id)

# 🆕 加载历史数据到Account内存中
print("🔄 Loading historical data to account memory...")
load_historical_data_to_accounts(stock_system, polymarket_system)
load_historical_data_to_accounts(stock_system, polymarket_system, bitmex_system)
print("✅ Historical data loading completed")

stock_system.initialize_for_live()
polymarket_system.initialize_for_live()
bitmex_system.initialize_for_live()


def get_stock_system():
Expand All @@ -99,6 +116,12 @@ def get_polymarket_system():
return polymarket_system


def get_bitmex_system():
"""Get the BitMEX system instance."""
global bitmex_system
return bitmex_system


logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -194,11 +217,21 @@ def load_backtest_as_initial_data():
def safe_generate_models_data():
if should_run_trading_cycle():
logger.info("🕐 Running trading cycle at market close time...")
generate_models_data(stock_system, polymarket_system)
generate_models_data(stock_system, polymarket_system, bitmex_system)
else:
logger.info("⏰ Skipping trading cycle - not in market time window")


def safe_generate_bitmex_cycle():
"""Run BitMEX trading cycle (24/7 crypto markets)."""
logger.info("🔄 Running BitMEX trading cycle...")
try:
bitmex_system.run_cycle()
logger.info("✅ BitMEX cycle completed")
except Exception as e:
logger.error(f"❌ BitMEX cycle failed: {e}")


def schedule_background_tasks(scheduler: BackgroundScheduler):
from datetime import timedelta

Expand Down Expand Up @@ -227,6 +260,20 @@ def schedule_background_tasks(scheduler: BackgroundScheduler):
)
logger.info(f"📅 Scheduled trading job for UTC {schedule_hour}:00 ({schedule_desc})")

# Schedule BitMEX cycle at same time as stock (3 PM ET, Mon-Fri)
# This prevents file conflicts and ensures all systems update together
scheduler.add_job(
safe_generate_bitmex_cycle,
"cron",
day_of_week="mon-fri",
hour=schedule_hour, # Same as stock (19 or 20 UTC for 3 PM ET)
minute=0,
timezone="UTC",
id="bitmex_daily_cycle",
replace_existing=True,
)
logger.info(f"📅 Scheduled BitMEX cycle for UTC {schedule_hour}:00 ({schedule_desc}), Mon-Fri")

price_interval = UPDATE_FREQUENCY["realtime_prices"]
logger.info(
f"📈 Scheduled stock price update job for every {price_interval} seconds ({price_interval//60} minutes)"
Expand All @@ -250,6 +297,19 @@ def schedule_background_tasks(scheduler: BackgroundScheduler):
id="update_polymarket_prices",
replace_existing=True,
)

# BitMEX price updates (every 10 minutes, 24/7)
bitmex_interval = 600 # 10 minutes
logger.info(
f"📈 Scheduled BitMEX price update job for every {bitmex_interval} seconds ({bitmex_interval//60} minutes)"
)
scheduler.add_job(
update_bitmex_prices_and_values,
"interval",
seconds=bitmex_interval,
id="update_bitmex_prices",
replace_existing=True,
)
scheduler.add_job(
update_news_data,
"interval",
Expand Down
18 changes: 14 additions & 4 deletions backend/app/models_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def _create_model_data(agent, account, market_type):
"timestamp": snapshot["timestamp"],
"profit": snapshot["profit"],
"totalValue": snapshot["total_value"],
"performance": snapshot.get("performance", 0),
}
for snapshot in allocation_history
],
Expand All @@ -122,7 +123,7 @@ def _serialize_positions(model_data):
return model_data


def load_historical_data_to_accounts(stock_system, polymarket_system):
def load_historical_data_to_accounts(stock_system, polymarket_system, bitmex_system=None):
"""Load historical data to account memory on every startup.

This function ALWAYS loads data to restore account state, regardless of whether
Expand Down Expand Up @@ -158,7 +159,14 @@ def load_historical_data_to_accounts(stock_system, polymarket_system):
print(f" 📊 {model_name}: Benchmark model (will be preserved)")
continue

system = stock_system if category == "stock" else polymarket_system
if category == "stock":
system = stock_system
elif category == "polymarket":
system = polymarket_system
elif category == "bitmex" and bitmex_system is not None:
system = bitmex_system
else:
continue

account = None
for agent_name, acc in system.accounts.items():
Expand Down Expand Up @@ -199,15 +207,17 @@ def restore_account_from_historical_data(account, historical_model_data):
account.total_fees = historical_model_data.get("total_fees", 0.0)


def generate_models_data(stock_system, polymarket_system) -> None:
def generate_models_data(stock_system, polymarket_system, bitmex_system=None) -> None:
"""Generate and save model data for all systems"""
try:
print("🚀 Starting data generation for both markets...")
print("🚀 Starting data generation for all markets...")
all_market_data = []

existing_benchmarks = _preserve_existing_benchmarks()

systems = {"stock": stock_system, "polymarket": polymarket_system}
if bitmex_system is not None:
systems["bitmex"] = bitmex_system

for market_type, system in systems.items():
print(f"--- Processing {market_type.upper()} market ---")
Expand Down
14 changes: 11 additions & 3 deletions backend/app/news_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@
def update_news_data() -> None:
print("📰 Updating news data...")

all_news_data = {"stock": [], "polymarket": []}
all_news_data = {"stock": [], "polymarket": [], "bitmex": []}

try:
from .main import get_polymarket_system, get_stock_system
from .main import get_bitmex_system, get_polymarket_system, get_stock_system

stock_system = get_stock_system()
polymarket_system = get_polymarket_system()
bitmex_system = get_bitmex_system()

if not stock_system or not polymarket_system:
if not stock_system or not polymarket_system or not bitmex_system:
print("❌ Failed to get system instances")
return

Expand All @@ -23,23 +24,30 @@ def update_news_data() -> None:
stock_system.initialize_for_live()
if not polymarket_system.universe:
polymarket_system.initialize_for_live()
if not bitmex_system.universe:
bitmex_system.initialize_for_live()

# Fetch market data
stock_market_data = stock_system._fetch_market_data(for_date=None)
polymarket_market_data = polymarket_system._fetch_market_data(for_date=None)
bitmex_market_data = bitmex_system._fetch_market_data(for_date=None)

# Fetch news data
stock_news = stock_system._fetch_news_data(stock_market_data, for_date=None)
polymarket_news = polymarket_system._fetch_news_data(
polymarket_market_data, for_date=None
)
bitmex_news = bitmex_system._fetch_news_data(bitmex_market_data, for_date=None)

all_news_data["stock"] = [
item for sublist in stock_news.values() for item in sublist
]
all_news_data["polymarket"] = [
item for sublist in polymarket_news.values() for item in sublist
]
all_news_data["bitmex"] = [
item for sublist in bitmex_news.values() for item in sublist
]

with open(NEWS_DATA_FILE, "w") as f:
json.dump(all_news_data, f, indent=4)
Expand Down
Loading
Loading