I have started using skfolio using a portfolio given in skfolio.datasets (sp500). My idea is to run a portfolio of equally weighted names using the sp500 as my universe. For this I do the following:
import skfolio
from skfolio.datasets import load_sp500_dataset
from skfolio.preprocessing import prices_to_returns
prices=load_sp500_dataset()
X=prices_to_returns(prices)
X_train, X_test = train_test_split(X, test_size=0.33, shuffle=False)
model = skfolio.optimization.naive.EqualWeighted()
model.fit(X_train)
portfolio = model.predict(X_test)
print(portfolio.annualized_sharpe_ratio)
print(portfolio.summary())
So here I am using the data from the SP500 index and apply an equally weighted portfolio model. It returns various statistics on the portfolio (Skew, Kurtosis,...) and I can plot the cumulative returns with
portfolio.plot_cumulative_returns()
I have a few questions/issues with the above as it seems a lot of parameters have been defined "by default". I believe they could all relate to some kind of set_parameters function (?). In particular:
I’m one of the core developers of Skfolio.
Your number of assets and start period are linked to your input X
, which is your time series of asset returns.
In your example, you use the EqualWeighted allocation model. This model allocates a weight of 1/20 to the 20 assets from your input returns X
. To change it, you need to modify your input by removing columns (assets) or rows (dates).
When using other models, such as MeanRisk, since these models support weight and cardinality constraints, you can control the number of invested assets with the model parameters: MeanRisk Cardinality Constraints.
Another option is through pre-selection transformers: Pre-Selection Transformers.
To use compounded returns for your portfolio evaluation (e.g., plots, risk stats, etc.), you need to set compounded=True
:
from skfolio.optimization import EqualWeighted
from skfolio.datasets import load_sp500_dataset
from skfolio.preprocessing import prices_to_returns
prices = load_sp500_dataset()
X = prices_to_returns(prices)
# Without compounded returns
model = EqualWeighted()
ptf = model.fit_predict(X)
ptf.cumulative_returns_df.tail()
# With compounded returns
model = EqualWeighted(portfolio_params=dict(compounded=True))
ptf = model.fit_predict(X)
ptf.cumulative_returns_df.tail()
In terms of rebalancing, there are two different concepts that are sometimes both called “rebalancing”:
Model Re-Fitting
This changes the target weights, for example, re-fitting an MVO (Mean-Variance Optimization) model every month.
Model re-fittings are performed using cross-validators: Cross-Validators Example.
Rebalancing Towards Target Weights
This refers to performing rebalancing between two re-fittings to avoid weight drift as asset prices diverge.
The second type of rebalancing happens at the same frequency as your input data, so daily in your example.
The reason is that portfolio models and cross-validators in Skfolio are used for model validation, selection, and parameter tuning. Therefore, the model evaluation must be homogeneous to the optimization process. For example, if you call model.fit_predict(X)
with a MeanRisk model that has a 10% volatility constraint, your predicted portfolio on the same training set X
must also have a 10% volatility. Otherwise, you will create issues later in terms of model selection, evaluation, and comparison.
Mathematically, this means that in between re-fittings, portfolio returns are computed as the dot product of returns and weighs.
More details are available here: Portfolio Models.
If you need more granularity to model weight drifts between re-fittings, Skfolio can be used with a backtesting package.
Let me know if you have any other questions!