Build a portfolio that makes money in any economy with OpenBB

When I was a quant, I had a Bloomberg Terminal.

I had the world’s financial data at my fingertips all in one place.






Now I don’t want to spend $30,000 per year for data so I no longer have my Bloomberg Terminal.

A few months ago, I was trying to build a portfolio, but the data I needed was scattered all over the internet.

I was downloading CSVs, moving columns around, then merging it all together.

It was exhausting.

Then, I discovered OpenBB and everything changed.

OpenBB is a platform that provides free data from hundreds of sources.

All in one place.

It is a game-changer for me.

No more spending an afternoon looking for data.

No more downloading spreadsheets.

No more merging them together.

Instead, you can get it all in a few lines of code.

Using OpenBB, I developed a simple system for investing.

Today, you’ll find out how.

Build your own portfolio that makes money in any economy with OpenBB

Risk parity is a strategy that uses risk to find the allocations of an investment portfolio. It allocates money to stocks based on a target risk level—usually volatility.

In other words, instead of equal dollar weights, risk parity has equal risk weights.

The problem with dollar-weighting is not every stock has the same risk. That means if you equally weigh two stocks, but one has higher risk, returns will be dominated by the higher-risk stock. Investors use risk parity to avoid this problem.

Risk parity is a strategy that works well in volatile markets.

First, let’s import the required libraries and download the stock data using a stock screener.

from openbb_terminal.sdk import openbb
import riskfolio as rp

Then grab the data.

new_highs = openbb.stocks.screener.screener_data("new_high")
port_data = new_highs[
    (new_highs.Price > 15) & (new_highs.Country == "USA")

tickers = port_data.Ticker.tolist()
data = openbb.economy.index(

OpenBB has a function that returns a list of stocks based on a screener. In this case, you’ll find stocks that are at new highs. Then filter this data to include only stocks with prices above $15 and from the US. Store the tickers in a list and download their historical data for a range of dates.

Next, compute the returns and clean the data.

returns = data.pct_change()[1:]
returns.dropna(how="any", axis=1, inplace=True)

Calculate the percentage change in the historical data to get the returns and drop any missing values.

Build the risk parity portfolio

Now, let’s move on to building the risk parity portfolio using RiskFolio.

port = rp.Portfolio(returns=returns)

port.assets_stats(method_mu='hist', method_cov='hist', d=0.94)

port.lowerret = 0.0008
w_rp_c = port.rp_optimization(

A criticism of risk parity is that without leverage, returns lag. So add a constraint to weigh the stocks in a way to reach a minimum portfolio return. This adds weight to higher-risk stocks to push the portfolio returns higher.

Create a Portfolio object and calculate the assets statistics using historical data. Then set a minimum return constraint and perform risk parity optimization using the Classic model and mean-variance optimization.

Visualize the results and calculate the number of shares to buy for each stock. First, plot the optimized portfolio weights as a pie chart. Then, plot the risk contribution.

ax = rp.plot_pie(w=w_rp_c)
Build a portfolio that makes money in any economy with OpenBB

LLY dominates with a 7% weight. That’s because to reach the minimum return threshold, risk parity overweights stocks with higher risk. And with higher risk comes higher return.

ax = rp.plot_risk_con(
Build a portfolio that makes money in any economy with OpenBB

Risk parity figured out how the optimal risk weights to hit the minimum portfolio return constraint. The risk contribution of each stock varies depending on its volatility.

Finally, compute the number of shares you need to invest in the portfolio.

port_val = 10_000
w_rp_c["invest_amt"] = w_rp_c * port_val
w_rp_c["last_price"] =  data.iloc[-1]
w_rp_c["shares"] = (w_rp_c.invest_amt / w_rp_c.last_price).astype(int)

The shares column of the data frame gives you the number of shares to buy to achieve this portfolio. To continue to maintain the portfolio, run the code to re-weight the portfolio on a monthly or quarterly basis.