Files
fegger 19ed77f4a0 Tune model config for price-only training and add walk-forward CV
Downsize GNN hidden channels/heads (128→64, 16→4) to match the 30-stock
price-only universe before alternative data processors are ready.

Add USE_ALTERNATIVE_DATA flag (default False) that skips news/social
branches so the model trains on real data rather than zero-filled stubs.

Standardize target returns per-batch in the data pipeline to stabilize
training.

Introduce walk-forward cross-validation with expanding windows:
configurable fold count and out-of-sample years.

Add IC loss weighting (0.7) to complement MSE, and wire it into the
trainer alongside the new loss function.
2026-05-26 15:36:11 +02:00

226 lines
7.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
from datetime import datetime, timedelta
import torch
from dotenv import load_dotenv
load_dotenv()
def _check_amd_gpu() -> bool:
"""Detect AMD GPU via ROCm device name."""
if not torch.cuda.is_available():
return False
try:
name = torch.cuda.get_device_name(0)
return any(kw in name for kw in ("AMD", "Radeon", "gfx"))
except Exception:
return False
class Config:
# Project settings
PROJECT_NAME = "StockGNN_R9700"
VERSION = "1.0.0"
# Data directories
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_DIR = os.path.join(BASE_DIR, "data")
RAW_DATA_DIR = os.path.join(DATA_DIR, "raw")
PROCESSED_DATA_DIR = os.path.join(DATA_DIR, "processed")
EXTERNAL_DATA_DIR = os.path.join(DATA_DIR, "external")
MODEL_DIR = os.path.join(BASE_DIR, "models")
# Stock universe settings
INITIAL_TICKERS = [
"AAPL",
"MSFT",
"GOOGL",
"AMZN",
"META",
"TSLA",
"NVDA",
"JPM",
"V",
"WMT",
"PG",
"DIS",
"NFLX",
"ADBE",
"PYPL",
"INTC",
"CSCO",
"PEP",
"KO",
"XOM",
"BAC",
"VZ",
"T",
"CRM",
"CMCSA",
"PFE",
"NKE",
"MRK",
"CVX",
"HD",
]
INDEX_TICKER = "^GSPC" # S&P 500
DELISTED_TICKERS_FILE = os.path.join(EXTERNAL_DATA_DIR, "delisted_stocks.csv")
# Date settings
START_DATE = "2015-01-01"
END_DATE = datetime.now().strftime("%Y-%m-%d")
TRAIN_END_DATE = "2022-12-31"
VAL_END_DATE = "2023-06-30"
TEST_END_DATE = END_DATE
# AMD GPU settings (Radeon R9700 AI Pro - 32GB)
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
AMD_GPU = _check_amd_gpu()
GPU_MEMORY_LIMIT = 0.9 # Use 90% of 32GB = 28.8GB
ROCM_OPT_LEVEL = "O2" # Optimization level for ROCm ('O0', 'O1', 'O2')
PIN_MEMORY = True # Enable pinned memory for faster data transfer
MIXED_PRECISION = True # Enable mixed precision training
PRECISION = "bf16" # 'fp16' or 'bf16' for mixed precision
# Model settings
MODEL_NAME = "stock_gnn_r9700"
# 64 hidden / 4 heads gives head_dim=16; appropriate for price-only 30-stock universe.
# Increase once alternative data processors are implemented (see USE_ALTERNATIVE_DATA).
HIDDEN_CHANNELS = 64
NUM_HEADS = 4
DROPOUT = 0.3
LEARNING_RATE = 0.0005
EPOCHS = 200
BATCH_SIZE = 128
SEQUENCE_LENGTH = 60
PREDICTION_HORIZON = 10
# Intraday trading settings
TRADING_FREQUENCY = "5min" # '1min', '5min', '15min', '30min', '1h'
TRADING_HOURS = {
"start": "09:30", # Market open (ET)
"end": "16:00", # Market close (ET)
}
PRE_MARKET_HOURS = {
"start": "04:00", # Pre-market start
"end": "09:30", # Pre-market end
}
AFTER_HOURS = {
"start": "16:00", # After-hours start
"end": "20:00", # After-hours end
}
MAX_POSITION_HOLD_TIME = "4h" # Maximum time to hold a position
MIN_POSITION_HOLD_TIME = "10min" # Minimum time to hold a position
MAX_DAILY_POSITIONS = 50 # Maximum number of positions per day
MAX_POSITION_SIZE = 0.03 # Maximum % of portfolio per position (3%)
# Data pipeline settings
LOOKBACK_WINDOW = 60 # Days for feature calculation
REALTIME_FEATURE_WINDOW = 30 # Number of data points for real-time features
DATA_BUFFER_SIZE = 5000 # Number of data points to keep in memory
DATA_FLUSH_INTERVAL = 300 # seconds - how often to flush data to database
# Alternative data settings (set via .env or environment variables)
NEWS_API_KEY = os.environ.get("NEWS_API_KEY", "")
TWITTER_BEARER_TOKEN = os.environ.get("TWITTER_BEARER_TOKEN", "")
REDDIT_CLIENT_ID = os.environ.get("REDDIT_CLIENT_ID", "")
REDDIT_CLIENT_SECRET = os.environ.get("REDDIT_CLIENT_SECRET", "")
NEWS_LOOKBACK_DAYS = 7 # Number of days to look back for news
SOCIAL_MEDIA_LOOKBACK_DAYS = 3 # Number of days to look back for social media
# Alternative data flag — set to True once NewsProcessor / SocialMediaProcessor
# are implemented; False skips those branches so the model trains on real data
# rather than zeros, which would mislead the attention layers.
USE_ALTERNATIVE_DATA = False
# Feature definitions (referenced by models)
NEWS_FEATURES = [
"sentiment",
"volume",
"recency",
"source_reliability",
"topic_relevance",
]
SOCIAL_FEATURES = [
"twitter_sentiment",
"twitter_volume",
"reddit_sentiment",
"reddit_volume",
"social_momentum",
]
INTRADAY_FEATURES = [
"return",
"volatility",
"momentum",
"volume_momentum",
"bid_ask_spread",
"bid_ask_spread_pct",
"volume_imbalance",
"order_flow",
"vwap_deviation",
]
# Stateful prediction
STATEFUL_PREDICTION = False
REALTIME_UPDATE_INTERVAL = 60 # seconds
# Backtesting settings
INITIAL_CAPITAL = 100000
TRANSACTION_COST = 0.0005 # 0.05% per trade
SLIPPAGE_MODEL = "volume_curve" # 'volume_curve', 'constant', or 'none'
SLIPPAGE_RATE = 0.0002 # 0.02% slippage
# Training loss: weight for (1 Pearson IC) vs MSE.
# 0 = pure MSE, 1 = pure IC loss. 0.7 works well empirically for daily returns.
IC_LOSS_WEIGHT = 0.7
# Walk-forward cross-validation
WALK_FORWARD_FOLDS = 5 # number of folds
WALK_FORWARD_TEST_YEARS = 1 # out-of-sample window per fold (years)
# Live trading settings
LIVE_DATA_ENABLED = True
DATA_PROVIDER = "polygon" # 'polygon', 'alphavantage', 'ib', 'tdameritrade'
POLYGON_API_KEY = os.environ.get("POLYGON_API_KEY", "")
ALPHA_VANTAGE_API_KEY = os.environ.get("ALPHA_VANTAGE_API_KEY", "")
IB_HOST = "127.0.0.1"
IB_PORT = 7497
IB_CLIENT_ID = 1
# WebSocket settings
WEBSOCKET_RECONNECT_DELAY = 5 # seconds
WEBSOCKET_MAX_RETRIES = 20
WEBSOCKET_PING_INTERVAL = 30 # seconds
# Data loading settings
NUM_WORKERS = 8 # Number of data loading workers
PREFETCH_FACTOR = 4 # Number of batches to prefetch
# Online learning settings
ONLINE_LEARNING = True # Enable online learning
ONLINE_LEARNING_RATE = 0.0001 # Learning rate for online updates
ONLINE_LEARNING_INTERVAL = 3600 # seconds - how often to perform online learning
# Risk management settings
MAX_DAILY_LOSS = 0.01 # 1% max daily loss
MAX_DRAWDOWN = 0.05 # 5% max drawdown
VOLATILITY_TARGET = 0.15 # Annualized volatility target
POSITION_SIZING = (
"volatility_target" # 'volatility_target', 'equal_weight', 'kelly'
)
# Execution settings
EXECUTION_ALGORITHM = "vwap" # 'vwap', 'twap', 'pov', 'implementation_shortfall'
EXECUTION_TIME_HORIZON = "5min" # Time to complete execution
MARKET_IMPACT_MODEL = "kyle" # 'kyle', 'almgren_chriss', or 'none'
def __init__(self):
os.makedirs(self.RAW_DATA_DIR, exist_ok=True)
os.makedirs(self.PROCESSED_DATA_DIR, exist_ok=True)
os.makedirs(self.EXTERNAL_DATA_DIR, exist_ok=True)
os.makedirs(self.MODEL_DIR, exist_ok=True)
config = Config()