How to Build a Low Correlation Stock Portfolio, Step by Step
You already have the hard part done: a list of stocks you are willing to own. The problem is combining them. Ten good names that all sell off on the same news day are one bet wearing ten tickers, and no amount of conviction about any single one of them fixes that.
The task, stated precisely: take a candidate list of stocks and allocate capital across it so that the holdings’ returns do not move together — including on the downside — and do it with a process you can repeat next quarter instead of a correlation table you eyeball once.
What you will achieve by the end of this walkthrough is a portfolio whose volatility is lower than the average of its parts, because capital went to candidates that correlate weakly. The steps below run from candidate selection through data configuration, risk model, objective, weight constraints, the optimizer run, and verification. RSJ Portfolio is a Windows desktop application that implements this workflow end to end, so each step maps directly to a screen in the product.
The statistical background — why weak correlation reduces portfolio volatility, and how modern portfolio theory turns a covariance estimate into weights — is covered in the product’s Concept guide. This article stays on the operational path.
Prerequisites: What You Need Before Your First Run
- Windows and the installer. Get it from the Downloads page. The installer filename is versioned and the SHA-512 checksum is published on that page, so verify the file before you run it:
Get-FileHash .\RSJPortfolioSetup-<version>.exe -Algorithm SHA512
Compare the output with the checksum on the Downloads page. If they differ, download again.
- A license. RSJ Portfolio is licensed per single user; what the license includes and the checkout are on the Pricing page.
- The user guide, in English or German, open in a second window while you work.
- A candidate list of symbols you are actually willing to hold. The Selection guide covers searching symbols and building that list.
- Access to a supported data provider for historic end-of-day prices, plus enough disk space for the local cache described on the Data and currencies page.
- Two decisions made in advance: your local currency and your date range. Both feed the price cleaning pipeline and the conversion path for portfolios quoted in several currencies, so deciding late means re-running everything.
Step 1: Define Your Candidate Universe
Open the Selection screen and search for symbols by name or ticker, then add them to a candidate list.
- Make the list broad enough for the optimizer to have room to choose. If every candidate is a bank or every candidate is a semiconductor name, the list will correlate strongly by construction and no objective will rescue it.
- Include candidates whose downside you expect to behave differently from the rest. The product can allocate based on weak downside correlation, not only on overall correlation, but it can only do that if such candidates are in the list.
- Save the candidate list. The same universe has to be reusable across later runs, otherwise you cannot tell whether a change in the allocation came from your parameters or from the candidate set.
The exact search and list-building mechanics are in the Selection guide; there is no reason to re-derive them here.
Step 2: Configure Data, Currency, and Cleaning
On the Configuration screen:
- Choose your data provider and let the local cache populate with historic end-of-day prices.
- Set the date range. A longer history gives the correlation and risk estimates more data to work with, but it must cover the period you care about. A range that stops three years ago tells you about a market that no longer exists.
- Set your local currency. If your portfolio is quoted in several currencies, the product follows the conversion path described on the Data and currencies page.
- Review the price cleaning settings. The cleaning pipeline processes raw provider data — gaps, stale quotes, and similar artefacts — before it reaches the optimizer.
- Confirm the cache is populated before you run. An optimizer run over a half-filled cache produces confident-looking numbers computed on partial data, which is worse than no numbers.
The parameter-by-parameter detail lives in the Configuration guide.
Step 3: Choose a Risk Model and an Objective
Two choices define what “low correlation” means numerically for your run.
The risk model. Pick one of the eight risk models. This determines how the optimizer measures the risk it is trying to reduce — change it and you change what the allocation is optimising for, even with an identical candidate list and date range.
The objective. Five are selectable:
- minimum volatility
- maximum Sharpe ratio
- minimum semi variance
- hierarchical risk parity
- conditional value at risk
For the goal of this article, minimum volatility and minimum semi variance are the natural starting points. Minimum volatility targets overall portfolio volatility; minimum semi variance concentrates on downside risk. If weak downside correlation is specifically what you are after, start with minimum semi variance and re-run with minimum volatility as a comparison — and the other way round if you care about total volatility first.
Return models are selectable as well. The objective and the risk estimator together are the definition of your run, so write down both before you look at the weights; it is easy to rationalise an allocation after the fact. The Methods and features page lists every method, risk model, return model and constraint.
Step 4: Set Weight Constraints and Run the Optimizer
Constraints are what keep a statistically optimal allocation investable. Apply caps per position or per group so the capital does not concentrate in one name or one sector.
Then run the optimizer from the Recommendation screen. It returns the allocation across your candidate list. Read the resulting weights alongside the correlation structure: the interesting question is not only which names got capital, but whether those names got capital because they correlate weakly with the rest.
When you compare runs, change one parameter at a time. Keep the candidate list, the date range and the currency fixed; vary the risk model, or the objective, or the constraints — never two of those at once. A small run log makes that discipline cheap:
run: 2026-09-25-a
universe: core-40 # identical candidate list for every run in this series
date_range: 2016-01-01..2026-06-30
currency: EUR
risk_model: <your chosen estimator>
objective: minimum_volatility
constraints:
max_weight_per_name: 0.08
max_weight_per_group: 0.25
notes: baseline run; next run changes objective only
Doing this the hard way — assembling a return matrix, computing correlations, and solving the optimisation yourself in a spreadsheet or a script — is possible, but the product performs the same allocation in a single run, and the Recommendation guide documents the optimizer parameters and the resulting allocation in detail.
Step 5: Verify the Result and Export It
Before you act on the allocation:
- Compare volatility against a naive baseline. The resulting portfolio volatility should be lower than an equal-weight allocation over the same candidates over the same period. If it is not, something in the setup is off.
- Inspect the pairwise correlations of the selected holdings to confirm capital went to weakly correlated — or weakly downside-correlated — names rather than to the largest positions in your list.
- Re-run with a different risk model or objective as a sanity check. If the allocation is broadly stable, the low-correlation result is robust. If it swings wildly, your estimates are being driven by a handful of observations.
- Export the allocation using the product’s export options, so it can go into your own records or downstream tools.
Treat verification as a loop. If the result is not what you expected, revisit the candidate list and the weight constraints before changing the data — data changes invalidate your comparison across runs.
How to Confirm It Worked
Step 5 gives you the in-product checks. The independent check is to take the exported allocation and price history and compute the two numbers yourself, assuming your export has one row per symbol with a weight column (adjust the column names to match your own export):
import numpy as np
import pandas as pd
w = pd.read_csv("allocation_export.csv").set_index("symbol")["weight"]
px = pd.read_csv("eod_prices.csv", index_col=0, parse_dates=True)
px = px[w.index] # only the allocated holdings
rets = px.pct_change().dropna() # same date range and currency as the run
optimized = rets.mul(w, axis=1).sum(axis=1)
equal = rets.mean(axis=1)
print(f"optimized vol : {optimized.std() * np.sqrt(252):.2%}")
print(f"equal weight : {equal.std() * np.sqrt(252):.2%}")
c = rets.corr().to_numpy()
off_diag = c[~np.eye(len(c), dtype=bool)]
print(f"avg pairwise correlation: {off_diag.mean():.2f}")
print(f"max pairwise correlation: {off_diag.max():.2f}")
The verification passes if the optimized volatility is the lower of the two and the average pairwise correlation is meaningfully below one. The maximum pairwise correlation is worth reading too: a single strongly correlated pair inside an otherwise diversified allocation is usually a position-sizing problem, not a data problem.
Troubleshooting Common Issues
- The optimizer produces a concentrated portfolio. Tighten the weight constraints, or widen the candidate list — with a narrow universe, concentration is often the honest answer.
- Results are unstable between runs. Check the date range and whether the local cache is complete, then try a different risk model. Re-running the same configuration twice should not move the weights.
- Multi-currency portfolios show unexpected values. Confirm the local currency setting and review the conversion path on the Data and currencies page.
- Prices look wrong or missing. Review the cleaning settings and the provider configuration before re-running; do not patch the symptom with a different date range.
- License or installation problems. Work through the Installation and license chapter of the user guide.
FAQ
What does “low correlation” actually mean for a portfolio?
It means the returns of the holdings do not move together. RSJ Portfolio analyses the return correlation between stocks and allocates capital to candidates that correlate weakly — or whose downside correlates weakly — which reduces the volatility of the resulting portfolio.
Do I need to write code to build a low correlation portfolio with RSJ Portfolio?
No. RSJ Portfolio is a Windows desktop application with a managed workflow: you build a candidate list, configure the data provider and cache, choose an objective, risk estimator and weight constraints, and the optimizer returns the allocation. Doing it the hard way — computing correlation matrices and solving the optimisation yourself — is possible but not required.
Which objective should I choose first: minimum volatility or minimum semi variance?
Both are selectable. Minimum volatility targets overall portfolio volatility, while minimum semi variance focuses on downside risk. If your concern is weak downside correlation specifically, minimum semi variance is the natural starting point; you can re-run with minimum volatility and compare.
How much history do I need for the correlation estimates to be meaningful?
The product uses historic end-of-day prices, and the date range is configurable. A longer history gives the correlation and risk estimates more data to work with, but it must cover the period you care about. The Getting started guide lists recommended starting parameters.
Conclusion and Next Steps
The workflow, end to end: define a candidate universe broad enough to choose from, configure the data provider, currency, date range and cleaning before you trust any output, pick a risk model and an objective (minimum volatility or minimum semi variance first), apply weight constraints so the result is investable, run the optimizer, then verify the allocation against an equal-weight baseline and export it. Change one parameter at a time and keep the rest fixed.
If you want recommended starting parameters and guidance on reading the results, start with the Getting started guide. From there, the practical next step is a single-user license from the Pricing page and the installer from Downloads. And if you would rather understand the machinery before you trust it, the product blog carries background articles on the optimizer, the data pipeline, and the methods, written by the team building the product.