Constant Rebalanced Portfolios (CRP)
Relevant source files
The following files were used as context for generating this wiki page:
Constant Rebalanced Portfolios (CRP) represent a fundamental class of investment strategies within the universal-portfolios library. A CRP maintains a fixed distribution of wealth across a set of assets, rebalancing at every time step to ensure the portfolio weights remain constant regardless of price fluctuations. This approach serves as a critical benchmark for more complex Online Portfolio Selection (OLPS) algorithms. Sources: universal/algos/crp.py:11-15, README.md
Within this framework, the library implements several variations, including the standard uniform CRP, the Best Constant Rebalanced Portfolio (BCRP) calculated in hindsight, and specialized versions like the hindsight-optimal Markowitz portfolio. These algorithms inherit from the base Algo class, which provides the necessary structure for sequential weight calculation and performance evaluation. Sources: universal/algo.py:18-24, universal/algos/bcrp.py:9-14, universal/algos/best_markowitz.py:9-13
Core Architecture and Logic
The CRP implementation follows a simplified version of the online portfolio selection flow. Unlike adaptive algorithms, the standard CRP does not update its weight vector b based on historical price relatives.
The diagram shows the data flow where a constant weight vector is applied to the input price data to generate results. Sources: universal/algo.py:70-90, universal/algos/crp.py:24-27
Standard CRP Implementation
The CRP class is the primary implementation. If no weights are provided during initialization, it defaults to a uniform distribution (UCRP), where each asset receives an equal share of wealth. Sources: universal/algos/crp.py:18-27
| Component | Description |
|---|---|
__init__(b=None) | Initializes the CRP with a specific weight vector b. Defaults to uniform if None. |
step(x, last_b, history) | Returns the constant weight vector self.b for each time step. |
weights(X) | Generates a full DataFrame of weights for the entire duration of the price data X. |
plot_crps(data) | Static method to visualize the performance of all possible CRP combinations (useful for 2 or 3 assets). |
Sources: universal/algos/crp.py:18-45
Hindsight Optimization Variants
The project includes specialized versions of CRP that determine the "best" constant weights after observing the full price history.
Best Constant Rebalanced Portfolio (BCRP)
The BCRP class calculates the optimal weights that would have maximized total wealth over a specific period. This is a theoretical benchmark used to evaluate the efficiency of online algorithms. It utilizes the tools.opt_weights function to perform the optimization. Sources: universal/algos/bcrp.py:9-25, universal/tools.py:126-140
Best Markowitz Portfolio
The BestMarkowitz class is a variant that constructs the optimal Markowitz portfolio in hindsight. It calculates the mean and covariance matrix of annualized returns to find weights that optimize the Sharpe ratio or specific return targets. Sources: universal/algos/best_markowitz.py:9-35
The diagram illustrates how hindsight algorithms utilize the full history to solve an optimization problem for a single best weight vector. Sources: universal/algos/bcrp.py:21-25, universal/algos/best_markowitz.py:21-35
Implementation Details
The weights method in the CRP class ensures that the weight vector is properly broadcasted across the entire timeline of the input price data.
# universal/algos/crp.py:29-37
def weights(self, X):
if self.b is None:
b = X * 0 + 1
b.loc[:, "CASH"] = 0
b = b.div(b.sum(axis=1), axis=0)
return b
elif self.b.ndim == 1:
return np.repeat([self.b], X.shape[0], axis=0)
else:
return self.b
Sources: universal/algos/crp.py:29-37
Performance Benchmarking
CRP results are typically processed into an AlgoResult object, which provides access to various financial metrics used to compare the CRP against other strategies. Sources: universal/result.py:32-45
| Metric | Calculation / Context |
|---|---|
| Total Wealth | Cumulative product of periodic returns generated by the constant weights. |
| Sharpe Ratio | Annualized risk-adjusted return relative to the risk-free rate. |
| Turnover | In a CRP, turnover is generated by rebalancing to maintain the fixed ratio as prices change. |
| Information Ratio | Measured against a uniform CRP (UCRP) by default. |
Sources: universal/result.py:130-145, universal/result.py:190-205, universal/result.py:230-240
Conclusion
Constant Rebalanced Portfolios serve as the baseline for the universal-portfolios package. While simple in implementation, the variants like BCRP and BestMarkowitz provide essential theoretical limits for performance. The rebalancing logic inherent in CRP ensures that wealth is redistributed from winning assets to losing ones to maintain the target allocation, a behavior that is explored and optimized by more advanced algorithms in the library. Sources: universal/algos/crp.py:11-15, universal/algos/bcrp.py:9-11