Development Guidelines
Relevant source files
The following files were used as context for generating this wiki page:
The Development Guidelines for the universal-portfolios project provide a structured framework for implementing and testing Online Portfolio Selection (OLPS) algorithms. The project aims to consolidate various state-of-the-art algorithms and provide unified tools for performance analysis, wealth computation, and visualization.
These guidelines cover the core architectural patterns, including the base Algo class for strategy implementation, the AlgoResult class for metric evaluation, and the standardized development environment managed via Poetry.
Sources: README.md:10-15, universal/algo.py:10-15
Environment Setup and Tooling
The project utilizes modern Python development tools to ensure code quality and consistency. Only Python 3.8+ is supported.
Dependency Management
The project uses Poetry for managing dependencies and virtual environments. Key dependencies include numpy, pandas, scipy, and matplotlib for numerical computation and visualization.
| Tool | Purpose | Command |
|---|---|---|
| Poetry | Dependency and environment management | poetry install |
| Ruff | Linting and formatting | Configured in pyproject.toml |
| Pre-commit | Automatic formatting/linting before commits | pre-commit install |
| Pytest | Running the test suite | poetry run python -m pytest |
Sources: pyproject.toml:13-33, README.md:57-81
Code Formatting
A pre-commit hook is required to maintain a consistent style. The project enforces a line length of 88 characters and uses Ruff for linting.
Sources: pyproject.toml:53-56, README.md:73-81
Core Architecture
The architecture revolves around two primary components: the algorithm logic (universal.algo) and the performance results (universal.result).
Algorithm Implementation Flow
All new portfolio selection algorithms must inherit from the Algo base class. Developers can either implement a sequential step method for real-time weight calculation or a batch weights method for optimized matrix operations.
This diagram shows the execution flow of the run method in the Algo base class, which converts prices and iterates through time steps to generate portfolio weights.
Sources: universal/algo.py:17-105
Base Class: Algo
Key methods in the Algo class that require implementation or consideration during development:
init_weights(columns): Defines initial portfolio weights (e.g., uniform distribution).init_step(X): Initializes persistent variables before the sequential loop.step(x, last_b, history): Computes the weight vector for the next time step._convert_prices(S, method): Standardizes input data into "ratio", "log", or "raw" formats.
Sources: universal/algo.py:46-68, universal/algo.py:186-218
Performance Analysis and Results
The AlgoResult class is the standard output for all strategy executions. It provides automated calculation of financial metrics and visualization utilities.
Key Metrics
The following metrics are automatically calculated from the generated weights and price relatives:
| Metric | Property/Function | Description |
|---|---|---|
| Sharpe Ratio | self.sharpe | Annualized risk-adjusted return using log returns. |
| Max Drawdown | self.max_drawdown | Highest peak-to-trough decline in portfolio wealth. |
| Turnover | self.turnover | Trading volume relative to total portfolio value. |
| Total Wealth | self.total_wealth | Final cumulative product of all period returns. |
| Information Ratio | self.information | Risk-adjusted excess return over a benchmark (UCRP). |
Sources: universal/result.py:145-215, universal/result.py:270-275
Data Flow for Results
The data transformation from raw prices to performance metrics follows a strict pipeline to account for transaction costs and risk-free rates.
The sequence illustrates how AlgoResult processes raw algorithm output into finalized metrics.
Sources: universal/result.py:40-100, universal/tools.py:476-490
Testing Guidelines
Reliable algorithm development requires rigorous testing to prevent look-ahead bias and ensure numerical stability.
Mandatory Tests
- Forward Bias Test: Ensure weights at time $t$ do not change when additional future data points are added to the dataset.
- Fee Consistency: Verify that algorithms like Buy-And-Hold (BAH) do not incur unnecessary transaction costs.
- Numerical Stability: Algorithms should handle
NaNvalues or zero-volatility periods without crashing.
Sources: tests/test_algos.py:20-40, universal/algo.py:18-25
Sample Test Implementation
def test_bias(algo_class, S):
"""Test forward bias of algo."""
m = 10
B1 = algo_class().run(S.iloc[:-m]).B
B2 = algo_class().run(S).B
assert (B1 == B2.iloc[:-m]).all().all()
Sources: tests/test_algos.py:26-31
Utilities and Best Practices
- Quick Running: Use
tools.quickrun(algo, data)to execute an algorithm and immediately display a summary and performance plots. - Parallelization: The
runmethod supportsn_jobsfor parallelized step execution if the algorithm does not depend on the previous weight vector. - Price Normalization: Always use
_convert_pricesto ensure the input data matches the algorithm's expected mathematical model (ratio vs log).
Sources: universal/tools.py:72-84, universal/algo.py:108-142
The universal-portfolios framework emphasizes modularity and standardized evaluation. By adhering to the Algo and AlgoResult structures, developers can ensure their implementations are compatible with the library's extensive analysis and visualization tools.