Skip to content

Commit ad51d4f

Browse files
committed
Handle pandas-datareader import-time TypeError gracefully
1 parent 4346c94 commit ad51d4f

File tree

8 files changed

+1779
-1660
lines changed

8 files changed

+1779
-1660
lines changed

.github/workflows/ci.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
test:
9+
runs-on: ubuntu-latest
10+
strategy:
11+
matrix:
12+
python-version: ["3.10", "3.11", "3.12"]
13+
steps:
14+
- uses: actions/checkout@v4
15+
- uses: actions/setup-python@v5
16+
with:
17+
python-version: ${{ matrix.python-version }}
18+
- run: python -m pip install --upgrade pip
19+
- run: pip install -e .[dev]
20+
- run: ruff check .
21+
- run: pytest -q

README.md

Lines changed: 94 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -1,213 +1,94 @@
1-
# Markowitzify
2-
3-
(C) 2020 Mark M. Bailey, PhD
4-
5-
## About
6-
7-
Markowitzify will implement a variety of portfolio and stock/cryptocurrency analysis methods to optimize portfolios or trading strategies. The two primary classes are portfolio and stonks.<br>
8-
9-
The portfolio class will implement portfolio optimization based on the theory described by Harry Markowitz (University of California, San Diego), and elaborated by Marcos M. Lopez de Prado (Cornell University). In 1952, Harry Markowitz posited that the investment problem can be represented as a convex optimization algorithm. Markowitz's Critial Line Algorithm (CLA) estimates an "efficient frontier" of portfolios that maximize an expected return based on portfolio risk, where risk is measured as the standard deviation of the returns. However, solutions to these problems are often mathematically unstable. Lopez de Prado developed a machine-learning solution called Nested Cluster Optimization (NCO) that addresses this instability. This repository applies both the CLA algorithm, as well as the improved NCO algorithm, to a stock portfolio. Additionally, this repository simulates portfolio performance over time using Monte Carlo methods, and calculates various other measures of portfolio performance, including the Hurst Exponent and Sharpe Ratio.<br>
10-
11-
The stonks class will create a stock or cryptocurrency object containing OHLC data. The Relative Strength Indicator (RSI), a Fractal Indicator (as defined by Kaabar), Bollinger Bands, and Bullish/Bearish signals (based on RSI and the Fractal Indicator) can be calculated. Simulated trading strategies can also be backtested to elucidate an optimal strategy based on maximized profit.
12-
13-
## References
14-
* Carr, Michael. "Measure Volatility With Average True Range," *Investopedia,* Nov 2019, Link: https://www.investopedia.com/articles/trading/08/average-true-range.asp#:~:text=The%20average%20true%20range%20%28ATR%29%20is%20an%20exponential,signals%2C%20while%20shorter%20timeframes%20will%20increase%20trading%20activity.
15-
* Hall, Mary. "Enter Profitable Territory With Average True Range," *Investopedia," Sep 2020, Link: https://www.investopedia.com/articles/trading/08/atr.asp.
16-
* Kaabar, Sofien. "Coding Different Facets of Volatility," *Medium,* Oct 2020, Link: https://medium.com/python-in-plain-english/coding-different-facets-of-volatility-bd1a49282df4.
17-
* Kaabar, Sofien. "Developing a Systematic Indicator to Trade Cryptocurrencies With Python," *Medium,* Dec 2020, Link: https://medium.com/python-in-plain-english/a-technical-indicator-that-works-for-cryptocurrencies-python-trading-258963c7e9c7.
18-
* Kaabar, Sofien. "The Fractal Indicator — Detecting Tops & Bottoms in Markets," *Medium,* Dec 2020, Link: https://medium.com/swlh/the-fractal-indicator-detecting-tops-bottoms-in-markets-1d8aac0269e8.
19-
* Lopez de Prado, Marcos M. *Machine Learning for Asset Managers,* Cambridge University Press, 2020.
20-
* Markowitz, Harry. "Portfolio Selection," *Journal of Finance,* Vol. 7, pp. 77-91, 1952.
21-
* Melul, Elias. "Monte Carlo Simulations for Stock Price Predictions [Python]," *Medium,* May 2018, Link: https://medium.com/analytics-vidhya/monte-carlo-simulations-for-predicting-stock-prices-python-a64f53585662.
22-
* Tavora, Marco. "How the Mathematics of Fractals Can Help Predict Stock Markets Shifts," *Medium,* June 2019, Link: https://towardsdatascience.com/how-the-mathematics-of-fractals-can-help-predict-stock-markets-shifts-19fee5dd6574.
23-
24-
## Updates
25-
* 2020-12-17: Added stonks class and methods for individual stock/crypto analysis.
26-
* 2020-12-01: Added Hurst Exponent, Sharpe Ratio, and separated NCO and Markowitz optimization methods.
27-
* 2020-11-28: Added Monte Carlo simulation capability.
28-
* 2020-11-27: Initial commit.
29-
30-
## Installation
31-
`pip install markowitzify`
32-
33-
## Import
34-
`import markowitzify`
35-
36-
## The Portfolio Class
37-
38-
### Object Instantiation
39-
`portfolio_object = markowitzify.portfolio(**options)`<br>
40-
41-
Attributes:<br>
42-
* `portfolio_object.portfolio` = Portfolio (Pandas DataFrame).
43-
* `portfolio_object.cov` = Portfolio covariance matrix (Numpy array).
44-
* `portfolio_object.optimal` = Optimal portfolio configuration calculated using the Markowitz (CLA) algorithm (Pandas DataFrame).
45-
* `portfolio_object.nco` = Optimal portfolio configuration calculated using nco algorithm (Pandas DataFrame).
46-
* `portfolio_object.sharpe` = Sharpe ratio for the portfolio (float).
47-
* `portfolio_object.H` = Hurst Exponents for each stock in the portfolio (Pandas DataFrame).
48-
* `portfolio_object.help_()` = View instructions.
49-
* `portfolio_object.about()` = View about.
50-
51-
Parameters:<br>
52-
* `API_KEY` (optional) = (str) API Key from Market Stack (only requried if using this method to build portfolio).
53-
* `verbose` (optional, default = False) = (bool) Turn on if you like Zelda jokes.
54-
55-
### Updating Parameters
56-
Set API Key:<br>
57-
`portfolio_object.set_API_KEY(<STR>)`<br>
58-
59-
Set verbose:<br>
60-
`portfolio_object.set_verbose(<BOOL>)`<br>
61-
62-
### Building Portfolio
63-
Portfolio objects can be instantiated by uploadng a CSV of portfolio performance, or using the Market Stack API (https://marketstack.com/, API key required - note that access may be limited if using an unpaid account).<br><br>
64-
65-
Market Stack API:<br>
66-
`portfolio_object.build_portfolio(TKR_list, time_delta, **options)`<br>
67-
68-
Parameters:<br>
69-
* `datareader` (optional, default = True) = (bool) If True, will use Pandas data_reader to find stock data. If False, will use Market Stack API (requires API Key).
70-
* `TKR_list` (required) = (list) List of ticker symbols for portfolio.
71-
* `time_delta` (required) = (int) Number of days to collect price data (either from today or from end_date).
72-
* `end_date` (optional, default = today's date) = (str, %m-%d-%Y) Specify the end date for the time delta calculation.<br><br>
73-
74-
Upload CSV:<br>
75-
`portfolio_object.import_portfolio(input_path, **options)`<br>
76-
77-
Parameters:<br>
78-
* `input_path` (required) = (str) Location of CSV file.
79-
* `filename` (optional, default = 'portfolio.csv') = (str) Optional file name for portfolio CSV file.
80-
* `dates_kw` (optional, default = 'date') = (str) Name of column in portfolio that contains the date of each closing price.<br><br>
81-
82-
Build TSP:<br>
83-
Builds a portfolio based on Thrift Savings Plan funds with a lookback of 5 years from the current date.<br>
84-
`portfolio_object.build_TSP()`<br>
85-
86-
Export Portfolio:<br>
87-
`portfolio_object.save(file_path, **options)`<br>
88-
89-
Parameters:<br>
90-
* `file_path` (required) = (str) Location of CSV file.
91-
* `filename` (optional, default = 'portfolio.csv') = (str) Optional file name for portfolio CSV file.
92-
93-
### Finding Optimal Weights
94-
Implements the NCO algorithm.<br>
95-
96-
`portfolio_object.nco(**options)`<br>
97-
98-
Parameters:<br>
99-
* `mu` (optional, default = None) = (float) When not None, algorithm will return the Sharpe ratio portfolio; otherwise will return the NCO portfolio.
100-
* `maxNumClusters` (optional, default = 10 or number of stocks in portfolio - 1) = (int) Maximum number of clusters. Must not exceed the number of stocks in the portfolio - 1.<br>
101-
102-
Implements the Markowitz optimization algorithm.<br>
103-
104-
`portfolio_object.markowitz()`
105-
106-
### Hurst Exponent and Sharpe Ratios
107-
Calculate the Hurst Exponent for each stock in the portfolio.<br>
108-
109-
`portfolio_object.hurst(**options)`<br>
110-
111-
Parameters:<br>
112-
* lag1, lag2 (optional, default = (2, 20)) = (int) Lag times for fractal calculation.<br>
113-
114-
Calculate the Sharpe ratio for the portfolio.<br>
115-
116-
`H = portfolio_object.sharpe_ratio(**options)`<br>
117-
118-
Parameters:<br>
119-
* w (optional, dafault = Markowitz optimal weights) = (Numpy array) Weights for each stock in the portfolio.
120-
* risk_free (optional, dafault = 0.035) = (float) Risk-free rate of return.
121-
122-
### Trend Analysis
123-
Trend analysis can be performed on securities within the portfolio. Output is a Pandas DataFrame.<br>
124-
125-
`trend_output = portfolio_object.trend(**options)`<br>
126-
127-
Parameters:<br>
128-
* `exclude` (optional, default = []) = (list) List of ticker symbols in portfolio to exclude from trend analysis. Default setting will include all items in portfolio.
129-
130-
### Monte Carlo Simulation
131-
Simulated market returns. Output is a Pandas DataFrame with metrics for all included ticker symbols.<br>
132-
133-
`simulation_output = portfolio_object.simulate(threshold=0.2, days=100, **options)`<br>
134-
135-
Parameters:<br>
136-
* `threshold` (required, dafault = 0.2) = (float) Probability of a 'threshold' return, e.g., 0.2 would calculate the probability of a 20% return.
137-
* `days` (required, default = 100) = (int) Number of days in Monte Carlo simulation.
138-
* `on` (optional, default = 'return') = (str) Predicted return will be calculated on percent return if 'return' or on raw price if 'value'.
139-
* `exclude` (optional, default = []) = (list) List of ticker symbols in portfolio to exclude from trend analysis. Default setting will include all items in portfolio.
140-
* `iterations` (optional, default = 10000) = (int) Number of iterations in Monte Carlo simulation.
141-
142-
## The Stonks Class
143-
144-
### Object Instantiation
145-
`stock_object = markowitzify.stonks(TKR, **options)`<br>
146-
147-
Attributes:<br>
148-
* `stock_object.TKR` = Ticker symbol (str).
149-
* `stock_object.stonk` = OHLC array (Pandas DataFrame).
150-
* `stock_object.bands` = OHLC with Bollinger Bands (Pandas DataFrame).
151-
* `stock_object.fract` = OHLC with Fractal Indicator (Pandas DataFrame).
152-
* `stock_object.rsi` = OHLC with RSI Indicator (Pandas DataFrame).
153-
* `stock_object.sig` = OHLC with Bullish/Bearish signals based on Fractal Indicator and RSI (-1 == oversold, 1 == overbought) (Pandas DataFrame).
154-
* `stock_object.strategies` = Traading strategies (Buy/Risk coefficients of exponential average true range) and backtesting outcomes (Pandas DataFrame).
155-
* `stock_object.best_strategy` = Optimal strategy that maximizes profit (dictionary).
156-
* `stock_object.help_()` = View instructions.
157-
* `stock_object.about()` = View about.
158-
159-
Parameters:<br>
160-
* `TKR` (required) = (str) Ticker symbol.
161-
* `start` (optional, default = 10 years from today) = (str) Start date to collect OHLC data.
162-
* `verbose` (optional, default = False) = (bool) Turn on if you like Zelda jokes.
163-
164-
### Updating Parameters
165-
Set verbose:<br>
166-
`portfolio_object.set_verbose(<BOOL>)`<br>
167-
168-
### Fractal Indicator
169-
Calculates the Fractal Indicator, as defined by Kaabar (see "The Fractal Indicator — Detecting Tops & Bottoms in Markets").<br>
170-
171-
`stock_object.fractal(**options)`<br>
172-
173-
Parameters:<br>
174-
* `n` (optional, default = 20) = (int) EMA and Rolling Volatility lookback.
175-
* `lookback` (optional, default = 14) = (int) Fractal Indicator lookback.<br>
176-
177-
### Bollinger Bands
178-
Calculates trending price, as well as upper and lower Bollinger Bands.<br>
179-
180-
`stock_object.bollinger(**options)`<br>
181-
182-
Parameters:<br>
183-
* `n` (optional, default = 20) = (int) Number of days in smoothing period.
184-
* `m` (optional, default = 2) = (int) Number of standard deviations.
185-
* `log_returns` (optional, default = True) = (bool) Use Log Returns for calculation of bands. If False, uses Adjusted Close raw values.<br>
186-
187-
### Relative Strength Indicator
188-
Calculates the RSI for the dataset.<br>
189-
190-
`stock_object.RSI(**options)`<br>
191-
192-
Parameters:<br>
193-
* `initial_lookback` (optional, default = 14) = (int) Lookback period for initial RSI value.
194-
* `lookback` (optional, default = 14) = (int) Lookback period for subsequent RSI values.<br>
195-
196-
### Bullish / Bearish Signal Generator
197-
Determines Bullish or Bearish signal based on Fractal Indicator and RSI signals. (-1 == oversold, 1 == overbought).<br>
198-
199-
`stock_object.signal(**options)`<br>
200-
201-
Parameters:<br>
202-
* `lookback` (optional, default = 14) = (int) Sets all lookback periods for RSI and Fractal Indicator calculations.<br>
203-
204-
### Simulate Trading Strategies
205-
Simulates and backtests a set of strategies (Buy/Risk coefficients) to find the optimum trading strategy that maximizes profit within a range of "Buy" and "Risk" values. Uses exponential average true range to quantify risk. "Buy" and "Risk" parameters are multiples of eATR for entry and exit criteria, respectively. For example, if buy = 1, then the entry criterion is defined as 1x eATR plus the previous day's closing price. If risk = 2, then a stop loss of 2x the eATR is defined as the exit criterion.<br>
206-
207-
`stock_object.strategize(**options)`<br>
208-
209-
Parameters:<br>
210-
* `eATR_lookback` (optional, default = 10) = (int) Sets lookback periods for exponential average true range.
211-
* `buy_range` (optional, default = (1.0, 4.0, 0.25)) = (tuple) Range of "Buy" coefficients to consider in the model, in the format (start, stop, interval).
212-
* `risk_range` (optional, default = (1.0, 4.0, 0.25)) = (tuple) Range of "Risk" coefficients to consider in the model, in the format (start, stop, interval).
213-
* `chandelier` (optional, default = False) = (bool) If True, uses the chandelier method for determining stop loss (high price - risk*eATR). If False, uses the closing price instead of high price.<br>
1+
# Markowitzify
2+
3+
Markowitzify is a lightweight Python library for portfolio optimization (`portfolio`) and technical-analysis helpers (`stonks`).
4+
5+
It includes:
6+
- **Portfolio analytics**: Markowitz optimization, NCO optimization, Sharpe ratio, Hurst exponent, Monte Carlo simulation, trend scan.
7+
- **Stonks analytics**: Fractal indicator, Bollinger bands, RSI, signal generation, and basic strategy backtesting.
8+
9+
## Installation
10+
11+
```bash
12+
pip install markowitzify
13+
```
14+
15+
For local development:
16+
17+
```bash
18+
pip install -e ".[dev]"
19+
```
20+
21+
Optional market-data provider extras:
22+
23+
```bash
24+
pip install -e ".[data]"
25+
```
26+
27+
## Quickstart (offline-safe)
28+
29+
```python
30+
import numpy as np
31+
import pandas as pd
32+
import markowitzify
33+
import helper_monkey as hm
34+
35+
rng = np.random.default_rng(42)
36+
returns = rng.normal(0.0005, 0.01, size=(200, 4))
37+
prices = 100 * np.exp(np.cumsum(returns, axis=0))
38+
df = pd.DataFrame(prices, columns=["AAA", "BBB", "CCC", "DDD"])
39+
40+
p = markowitzify.portfolio()
41+
p.portfolio = df
42+
p.cov = hm.cov_matrix(df)
43+
44+
p.markowitz()
45+
print(p.optimal)
46+
47+
p.NCO()
48+
print(p.nco)
49+
```
50+
51+
## API Overview
52+
53+
### `portfolio`
54+
55+
```python
56+
p = markowitzify.portfolio(API_KEY=None, verbose=False)
57+
```
58+
59+
Key methods/attributes:
60+
- `build_portfolio(TKR_list, time_delta, end_date=None, datareader=True, provider="auto")`
61+
- `provider="auto"` prefers `yfinance` if installed, otherwise `pandas_datareader`.
62+
- `build_TSP()` (depends on external endpoint availability).
63+
- `import_portfolio(input_path, filename="portfolio.csv", dates_kw="date")`
64+
- `markowitz()` → sets `p.optimal`
65+
- `NCO()` / `optimize_nco()` → sets `p.nco`
66+
- `hurst()`, `sharpe_ratio()`, `simulate()`, `trend()`
67+
68+
### `stonks`
69+
70+
```python
71+
s = markowitzify.stonks("AAPL", provider="auto")
72+
```
73+
74+
Key methods/attributes:
75+
- `fractal()`
76+
- `bollinger()`
77+
- `RSI()`
78+
- `signal()`
79+
- `strategize()`
80+
81+
## Testing / Contributing
82+
83+
Run checks locally:
84+
85+
```bash
86+
ruff check .
87+
pytest -q
88+
```
89+
90+
## Notes and limitations
91+
92+
- External market APIs/providers may change or break over time.
93+
- Tests are intentionally offline and do not rely on Yahoo/MarketStack/TSP network calls.
94+
- MarketStack-based portfolio building requires a valid API key.

0 commit comments

Comments
 (0)