66from functools import cached_property
77from typing import TYPE_CHECKING , Any
88
9+ from backtest_lib .market import (
10+ get_pastview_type_from_backend ,
11+ get_timeseries_type_from_backend ,
12+ )
13+ from backtest_lib .market .timeseries import Timeseries
14+
915if TYPE_CHECKING :
1016 from backtest_lib .market import MarketView , PastView
1117 from backtest_lib .market .timeseries import Comparable
@@ -28,12 +34,12 @@ class BacktestResults[IndexT: Comparable]:
2834 asset_returns : PastView [float , IndexT ] = field (repr = False )
2935 initial_capital : float
3036
31- portfolio_returns : list [float ]
32- nav : list [float ]
33- drawdowns : list [float ]
34- gross_exposure : list [float ]
35- net_exposure : list [float ]
36- turnover : list [float ]
37+ portfolio_returns : Timeseries [float , IndexT ]
38+ nav : Timeseries [float , IndexT ]
39+ drawdowns : Timeseries [float , IndexT ]
40+ gross_exposure : Timeseries [float , IndexT ]
41+ net_exposure : Timeseries [float , IndexT ]
42+ turnover : Timeseries [float , IndexT ]
3743
3844 total_return : float
3945 annualized_return : float
@@ -43,7 +49,9 @@ class BacktestResults[IndexT: Comparable]:
4349 avg_turnover : float
4450
4551 market : MarketView [IndexT ]
46- _backend : type [PastView ]
52+ _backend : str
53+ _backend_pastview_type : type [PastView ]
54+ _backend_timeseries_type : type [Timeseries ]
4755
4856 @staticmethod
4957 def from_weights_and_returns (
@@ -54,7 +62,7 @@ def from_weights_and_returns(
5462 initial_capital : float = 1.0 ,
5563 periods_per_year : float = 252.0 ,
5664 risk_free_annual : float | None = None ,
57- backend : type [ PastView ] ,
65+ backend : str = "polars" ,
5866 ) -> BacktestResults [Any ]:
5967 """
6068 Build results from pre-computed per-security simple returns.
@@ -158,18 +166,20 @@ def _std(xs: list[float]) -> float:
158166 )
159167 sharpe = (annualized_return - risk_free_annual ) / annualized_volatility
160168
169+ timeseries_type = get_timeseries_type_from_backend (backend )
170+
161171 return BacktestResults (
162172 periods = periods ,
163173 securities = securities ,
164174 weights = weights ,
165175 asset_returns = returns ,
166176 initial_capital = initial_capital ,
167- portfolio_returns = portfolio_returns ,
168- nav = nav ,
169- drawdowns = drawdowns ,
170- gross_exposure = gross_exposure ,
171- net_exposure = net_exposure ,
172- turnover = turnover ,
177+ portfolio_returns = timeseries_type . from_vectors ( portfolio_returns , periods ) ,
178+ nav = timeseries_type . from_vectors ( nav , periods ) ,
179+ drawdowns = timeseries_type . from_vectors ( drawdowns , periods ) ,
180+ gross_exposure = timeseries_type . from_vectors ( gross_exposure , periods ) ,
181+ net_exposure = timeseries_type . from_vectors ( net_exposure , periods ) ,
182+ turnover = timeseries_type . from_vectors ( turnover , periods ) ,
173183 total_return = total_return ,
174184 annualized_return = annualized_return ,
175185 annualized_volatility = annualized_volatility ,
@@ -178,6 +188,8 @@ def _std(xs: list[float]) -> float:
178188 avg_turnover = avg_turnover ,
179189 market = market ,
180190 _backend = backend ,
191+ _backend_pastview_type = get_pastview_type_from_backend (backend ),
192+ _backend_timeseries_type = timeseries_type ,
181193 )
182194
183195 @staticmethod
@@ -188,7 +200,7 @@ def from_weights_market_initial_capital(
188200 * ,
189201 periods_per_year : float = 252.0 ,
190202 risk_free_annual : float | None = 0.02 ,
191- backend : type [ PastView ] ,
203+ backend : str ,
192204 ) -> BacktestResults [Any ]:
193205 """
194206 Convenience constructor that derives per-security returns from
@@ -244,7 +256,9 @@ def from_weights_market_initial_capital(
244256 .collect ()
245257 )
246258
247- asset_returns : PastView = backend .from_dataframe (asset_returns_df )
259+ backend_pastview_type = get_pastview_type_from_backend (backend )
260+
261+ asset_returns : PastView = backend_pastview_type .from_dataframe (asset_returns_df )
248262
249263 results = BacktestResults .from_weights_and_returns (
250264 weights = weights ,
@@ -273,17 +287,16 @@ def quantities_held(self) -> PastView[float, IndexT]:
273287 )
274288 if dtype .is_numeric ()
275289 ]
276- nav_series = pl .Series (self .nav )
277290
278291 qtys = joined .select (
279292 "date" ,
280293 * [
281- (pl .col (c ) * nav_series / pl .col (f"{ c } _p" )).alias (c )
294+ (pl .col (c ) * self . nav . to_series () / pl .col (f"{ c } _p" )).alias (c )
282295 for c in numeric_cols
283296 ],
284297 )
285298
286- return self ._backend .from_dataframe (qtys .collect ())
299+ return self ._backend_pastview_type .from_dataframe (qtys .collect ())
287300
288301 @cached_property
289302 def values_held (self ) -> PastView [float , IndexT ]:
@@ -297,11 +310,10 @@ def values_held(self) -> PastView[float, IndexT]:
297310 )
298311 if dtype .is_numeric ()
299312 ]
300- nav_series = pl .Series (self .nav )
301313
302314 values = weights .select (
303315 "date" ,
304- * [(pl .col (c ) * nav_series ).alias (c ) for c in numeric_cols ],
316+ * [(pl .col (c ) * self . nav . to_series () ).alias (c ) for c in numeric_cols ],
305317 )
306318
307- return self ._backend .from_dataframe (values .collect ())
319+ return self ._backend_pastview_type .from_dataframe (values .collect ())
0 commit comments