Skip to content

Commit

Permalink
Fix fitness function to account for trade date hit rates specifically.
Browse files Browse the repository at this point in the history
  • Loading branch information
Francisco Silva authored and Francisco Silva committed Dec 29, 2024
1 parent 00f6a23 commit 6ae7b1b
Show file tree
Hide file tree
Showing 3 changed files with 11 additions and 53 deletions.
4 changes: 2 additions & 2 deletions stocksense/config/defaults/model_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,14 @@
- 'fwd_return_4Q_hit'
'id_col': 'tic'
'date_col': 'tdq'
'min_train_years': 12
'min_train_years': 10
'max_splits': 3
'ga':
'num_generations': 75
'num_parents_mating': 12
'sol_per_pop': 60
'num_genes': 8
'mutation_percent_genes': 20
'mutation_percent_genes': 15
'crossover_probability': 0.8
'parent_selection_type': "tournament"
'keep_parents': 8
Expand Down
58 changes: 8 additions & 50 deletions stocksense/model/genetic_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,49 +118,6 @@ def plot_fitness(self):
self.ga_instance.plot_fitness()


def evaluate_top_hit_rate_by_date(
y_true: np.array, y_pred: np.array, trade_dates: np.array, k: int = 75
) -> float:
"""
Evaluate predictions by selecting top k stocks for each trade date.
Parameters:
-----------
y_true : np.array
Actual returns
y_pred : np.array
Predicted returns
trade_dates : np.array
Array of trade dates for each observation
k : int
Number of stocks to select per trade date
Returns
-------
float
Average top hit rate.
"""
unique_dates = np.unique(trade_dates)
performance_by_date = []

for date in unique_dates:
date_mask = trade_dates == date
date_true = y_true[date_mask]
date_pred = y_pred[date_mask]

top_k_indices = np.argsort(date_pred)[-k:]
top_hit_rate = np.mean(date_true[top_k_indices] > 0)
performance_by_date.append(
{
"date": date,
"top_hit_rate": top_hit_rate,
}
)

avg_top_hit = np.mean([p["top_hit_rate"] for p in performance_by_date])
return round(avg_top_hit, 4) if avg_top_hit > 0 else 0.0001


def evaluate_top_hit_rate(y_true: np.array, y_pred: np.array, k: int = 25) -> float:
"""
Evaluate across all predictions at once, selecting top k% overall.
Expand Down Expand Up @@ -248,7 +205,7 @@ def fitness_function_wrapper(
Callable[[pygad.GA, list[float], int], float]
Fitness function.
"""
splits = get_train_val_splits(data, min_train_years, 1, 2)
splits = get_train_val_splits(data, min_train_years, 1, 3)

def fitness_function(ga_instance, solution, solution_idx) -> float:
"""
Expand Down Expand Up @@ -292,15 +249,16 @@ def fitness_function(ga_instance, solution, solution_idx) -> float:
for train, val in splits:
X_train = train.select(features).to_pandas()
y_train = train.select(target).to_pandas().values.ravel()
X_val = val.select(features).to_pandas()
y_val = val.select(target).to_pandas().values.ravel()
trade_dates = val.select("tdq").to_pandas().values.ravel()

xgb.train(X_train, y_train)
y_pred = xgb.predict_proba(X_val)

performance = evaluate_top_hit_rate_by_date(y_val, y_pred, trade_dates, 50)
performance_list.append(performance)
for trade_date in np.unique(trade_dates):
val_trade_date = val.filter(pl.col("tdq") == trade_date)
X_val = val_trade_date.select(features).to_pandas()
y_val = val_trade_date.select(target).to_pandas().values.ravel()
y_pred = xgb.predict_proba(X_val)
performance = evaluate_top_hit_rate(y_val, y_pred, 10)
performance_list.append(performance)

avg_performance = round(np.mean(performance_list), 4)
return avg_performance
Expand Down
2 changes: 1 addition & 1 deletion stocksense/model/portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def build_portfolio(
if self.weighting == "equal":
weights = self._equal_weight(portfolio)
elif self.weighting == "market_cap":
weights = self._market_cap_weight(portfolio, score_weight=0.4)
weights = self._market_cap_weight(portfolio, score_weight=0.5)
elif self.weighting == "sector_neutral":
weights = self._sector_neutral_weight(portfolio, trade_date)
else:
Expand Down

0 comments on commit 6ae7b1b

Please sign in to comment.