Fix head divisibility validation, benchmark data format, and target computation bugs
- Add embed_dim/feature_dim divisibility checks in attention modules - Auto-select largest valid head count in IntradayGNN when num_features isn't divisible by 8 - Fix benchmark to pass PyG Data objects instead of tuples to model - Fix target computation to use precomputed next trading date and filter valid tickers - Add missing device argument to autocast calls - Remove stale global app_state reference in dashboard API - Clean up various formatting (line wrapping, whitespace)
This commit is contained in:
+33
-31
@@ -309,7 +309,9 @@ class StockDataPipeline:
|
||||
"high": row["High"],
|
||||
"low": row["Low"],
|
||||
"close": row["Close"],
|
||||
"adj_close": row["Close"], # auto_adjust=True, 'Close' is already adjusted
|
||||
"adj_close": row[
|
||||
"Close"
|
||||
], # auto_adjust=True, 'Close' is already adjusted
|
||||
"volume": row["Volume"],
|
||||
}
|
||||
)
|
||||
@@ -581,9 +583,7 @@ class StockDataPipeline:
|
||||
if len(seq_arr) > config.SEQUENCE_LENGTH:
|
||||
seq_arr = seq_arr[-config.SEQUENCE_LENGTH :]
|
||||
elif len(seq_arr) < config.SEQUENCE_LENGTH:
|
||||
pad = np.zeros(
|
||||
(config.SEQUENCE_LENGTH - len(seq_arr), 5), dtype=np.float32
|
||||
)
|
||||
pad = np.zeros((config.SEQUENCE_LENGTH - len(seq_arr), 5), dtype=np.float32)
|
||||
seq_arr = np.vstack([pad, seq_arr])
|
||||
|
||||
# News features (current date, broadcast across all timesteps)
|
||||
@@ -710,9 +710,7 @@ class StockDataPipeline:
|
||||
continue
|
||||
|
||||
# Pre-fetch PIT data once per ticker (avoids O(n²) repeated calls)
|
||||
pit_cache = {
|
||||
t: self.get_point_in_time_data(t, date) for t in valid_tickers
|
||||
}
|
||||
pit_cache = {t: self.get_point_in_time_data(t, date) for t in valid_tickers}
|
||||
|
||||
# Build 3-D node feature matrix
|
||||
node_sequences: List[np.ndarray] = []
|
||||
@@ -834,16 +832,33 @@ class StockDataPipeline:
|
||||
# Compute once per day — stock universe and PIT data don't change intraday
|
||||
pd_date = pd.Timestamp(date)
|
||||
current_tickers = [
|
||||
t for t in tickers
|
||||
t
|
||||
for t in tickers
|
||||
if t in self.price_data
|
||||
and not self.price_data[t].empty
|
||||
and self.price_data[t].index[0] <= pd_date <= self.price_data[t].index[-1]
|
||||
and self.price_data[t].index[0]
|
||||
<= pd_date
|
||||
<= self.price_data[t].index[-1]
|
||||
]
|
||||
if not current_tickers:
|
||||
continue
|
||||
|
||||
date_dt = datetime.strptime(date, "%Y-%m-%d")
|
||||
pit_cache = {t: self.get_point_in_time_data(t, date_dt) for t in current_tickers}
|
||||
pit_cache = {
|
||||
t: self.get_point_in_time_data(t, date_dt) for t in current_tickers
|
||||
}
|
||||
|
||||
# Determine next trading date once per day
|
||||
next_date = self._next_trading_date(pd_date)
|
||||
if next_date is None:
|
||||
continue
|
||||
|
||||
# Pre-filter tickers that have target data on the next trading date
|
||||
target_valid_tickers = [
|
||||
t for t in current_tickers if next_date in self.price_data[t].index
|
||||
]
|
||||
if not target_valid_tickers:
|
||||
continue
|
||||
|
||||
# Get all timestamps for this trading day
|
||||
timestamps = generate_intraday_timestamps(date)
|
||||
@@ -856,7 +871,7 @@ class StockDataPipeline:
|
||||
sequence_features = []
|
||||
valid_tickers = []
|
||||
|
||||
for ticker in current_tickers:
|
||||
for ticker in target_valid_tickers:
|
||||
# Check memory before processing ticker
|
||||
if not self.memory_manager.ensure_memory(50 * 1024**2): # 50MB
|
||||
logger.warning(f"Skipping {ticker} due to memory constraints")
|
||||
@@ -881,8 +896,7 @@ class StockDataPipeline:
|
||||
if isinstance(daily_data, pd.Series):
|
||||
# If only one day of data, create a sequence with the same values
|
||||
features = [
|
||||
daily_data["Close"] / daily_data["Open"]
|
||||
- 1, # Return
|
||||
daily_data["Close"] / daily_data["Open"] - 1, # Return
|
||||
0.2, # Volatility (placeholder)
|
||||
0.0, # Momentum (placeholder)
|
||||
np.log(daily_data["Volume"] + 1), # Log volume
|
||||
@@ -980,26 +994,14 @@ class StockDataPipeline:
|
||||
)
|
||||
|
||||
# Create target (returns over prediction horizon)
|
||||
y = []
|
||||
y_vals = []
|
||||
for ticker in valid_tickers:
|
||||
# For intraday, we would predict the next few minutes
|
||||
# For this example, we'll predict the next day's return
|
||||
next_date = datetime.strptime(date, "%Y-%m-%d") + timedelta(days=1)
|
||||
if (
|
||||
ticker in self.price_data
|
||||
and next_date.strftime("%Y-%m-%d")
|
||||
in self.price_data[ticker].index
|
||||
):
|
||||
current_price = self.price_data[ticker].loc[date]["Close"]
|
||||
future_price = self.price_data[ticker].loc[
|
||||
next_date.strftime("%Y-%m-%d")
|
||||
]["Close"]
|
||||
ret = future_price / current_price - 1
|
||||
y.append(ret)
|
||||
else:
|
||||
y.append(0) # Default value
|
||||
current_price = self.price_data[ticker].loc[date]["Close"]
|
||||
future_price = self.price_data[ticker].loc[next_date]["Close"]
|
||||
ret = future_price / current_price - 1
|
||||
y_vals.append(ret)
|
||||
|
||||
y = torch.tensor(y, dtype=torch.float32).unsqueeze(1)
|
||||
y = torch.tensor(y_vals, dtype=torch.float32).unsqueeze(1)
|
||||
|
||||
# Create Data object
|
||||
data = Data(x=x, edge_index=edge_index, edge_attr=edge_weight, y=y)
|
||||
|
||||
Reference in New Issue
Block a user