Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,6 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
backend/**/*.json

.claude
.idea
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- never ever commit api keys to git
- never ever create test files out of test directory, never ever add scripts you generte on your own will to project root, always use scripts or tests forlder for that
1 change: 1 addition & 0 deletions backend/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def get_base_model_configs() -> List[Tuple[str, str]]:
TRADING_CONFIG = {
"initial_cash_stock": 1000,
"initial_cash_polymarket": 500,
"initial_cash_bitmex": 10000,
"max_consecutive_failures": 3,
"recovery_wait_time": 3600,
"error_retry_time": 600,
Expand Down
65 changes: 62 additions & 3 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import shutil
from datetime import datetime

from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()

from apscheduler.executors.pool import ThreadPoolExecutor
from apscheduler.schedulers.background import BackgroundScheduler
from fastapi import FastAPI, HTTPException
Expand All @@ -19,7 +24,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,6 +46,7 @@
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,
)
Expand All @@ -47,6 +57,7 @@
# 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 $10,000 each)
for display_name, model_id in get_base_model_configs():
bitmex_system.add_agent(display_name, 10000.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,19 @@ def schedule_background_tasks(scheduler: BackgroundScheduler):
)
logger.info(f"📅 Scheduled trading job for UTC {schedule_hour}:00 ({schedule_desc})")

# Schedule BitMEX cycles 4x daily (00:00, 06:00, 12:00, 18:00 UTC)
for hour in [0, 6, 12, 18]:
scheduler.add_job(
safe_generate_bitmex_cycle,
"cron",
hour=hour,
minute=0,
timezone="UTC",
id=f"bitmex_cycle_{hour:02d}00",
replace_existing=True,
)
logger.info("📅 Scheduled BitMEX cycles 4x daily (00:00, 06:00, 12:00, 18:00 UTC)")

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 +296,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
17 changes: 13 additions & 4 deletions backend/app/models_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,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 +158,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 +206,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