Skip to content
Closed
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
74 changes: 44 additions & 30 deletions src/quant_research_starter/backtest/vectorized.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Dict, Optional

import pandas as pd

from tqdm import tqdm

class VectorizedBacktest:
"""
Expand Down Expand Up @@ -74,40 +74,54 @@ def run(self, weight_scheme: str = "rank") -> Dict:

# Compute daily weights from signals (rebalance only on rebalance dates)
weights_list = []
for date in returns_df.index:
if self._should_rebalance(date, prev_rebalance_date):
# Rebalance: compute new target weights
current_weights = self._calculate_weights(
aligned_signals.loc[date], weight_scheme
)
prev_rebalance_date = date

# Append current weights (maintain between rebalances)
weights_list.append(current_weights)
with tqdm(len(returns_df.index),desc="Backtesting", unit="day") as pbar:
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after comma in function arguments. Should be tqdm(total=len(returns_df.index), desc="Backtesting", unit="day") with a space after the comma following len(returns_df.index).

Suggested change
with tqdm(len(returns_df.index),desc="Backtesting", unit="day") as pbar:
with tqdm(len(returns_df.index), desc="Backtesting", unit="day") as pbar:

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tqdm constructor is called with a positional argument for total, but this should use the total parameter name. Change tqdm(len(returns_df.index),desc="Backtesting", unit="day") to tqdm(total=len(returns_df.index), desc="Backtesting", unit="day").

Suggested change
with tqdm(len(returns_df.index),desc="Backtesting", unit="day") as pbar:
with tqdm(total=len(returns_df.index), desc="Backtesting", unit="day") as pbar:

Copilot uses AI. Check for mistakes.
for date in returns_df.index:
if self._should_rebalance(date, prev_rebalance_date):
# Rebalance: compute new target weights
current_weights = self._calculate_weights(
aligned_signals.loc[date], weight_scheme
)
prev_rebalance_date = date
pbar.set_postfix(rebalance="✓", refresh=False)
else :
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect spacing in else : - there should be no space before the colon. Should be else: according to PEP 8.

Suggested change
else :
else:

Copilot uses AI. Check for mistakes.
pbar.set_postfix(rebalance=" ", refresh=False)

# Append current weights (maintain between rebalances)
weights_list.append(current_weights)
pbar.update(1)

weights = pd.DataFrame(
weights_list, index=returns_df.index, columns=self.prices.columns
).fillna(0.0)

# Previous day weights for PnL calculation
weights_prev = weights.shift(1).fillna(0.0)

# Turnover for transaction costs (L1 change / 2)
turnover = (weights.fillna(0.0) - weights_prev).abs().sum(axis=1) * 0.5
tc_series = turnover * self.transaction_cost

# Strategy returns
strat_ret = (weights_prev * returns_df).sum(axis=1) - tc_series

# Build portfolio value series
portfolio_value = (1 + strat_ret).cumprod() * self.initial_capital
portfolio_value = pd.concat(
[
pd.Series(self.initial_capital, index=[self.prices.index[0]]),
portfolio_value,
]
)
portfolio_value = portfolio_value.reindex(self.prices.index).ffill()
with tqdm(total=4, desc="Calculating performance") as pbar:
# Previous day weights for PnL calculation
pbar.set_description("Calculating weight shifts")
weights_prev = weights.shift(1).fillna(0.0)
pbar.update(1)

# Turnover for transaction costs (L1 change / 2)
pbar.set_description("Calculating transaction costs")
turnover = (weights.fillna(0.0) - weights_prev).abs().sum(axis=1) * 0.5
tc_series = turnover * self.transaction_cost
pbar.update(1)

# Strategy returns
pbar.set_description("Calculating strategy returns")
strat_ret = (weights_prev * returns_df).sum(axis=1) - tc_series
pbar.update(1)

# Build portfolio value series
pbar.set_description("Building portfolio series")
portfolio_value = (1 + strat_ret).cumprod() * self.initial_capital
portfolio_value = pd.concat(
[
pd.Series(self.initial_capital, index=[self.prices.index[0]]),
portfolio_value,
]
)
portfolio_value = portfolio_value.reindex(self.prices.index).ffill()
pbar.update(1)

# Store results
self.positions = weights # interpret as weights positions
Expand Down
Loading
Loading