Alexander Hübbert
  • Home
  • Research
  • Articles
  • Practical Coding
  • DerivaSteps
  • CV

Articles

Articles

Backtests, data, and equity strategy ideas. Code when relevant. Readers who want to start from the foundations can use Practical Coding, where Python and R are taught.

Oct 13, 2026

How much growth is there in growth stocks?

Sorted at year-ends 2001 to 2020, the most expensive fifth of large US stocks grew its revenue three times as fast as the cheapest fifth over five years, and returned less.

Python Backtesting Analysis Code
Read article
Two panels by years after the sort, 0 to 5. Top: median revenue growth, with the most expensive fifth of large US stocks at 62% after five years, the cheapest fifth at 19% and the three middle fifths between them in order. Bottom: average total return, with the cheapest fifth highest at 77% and the most expensive fifth lowest at 61%.
Oct 9, 2026

The Markowitz curse

When two stocks move almost together, raising one stock's estimated volatility from 20% to 20.2% changes the other stock's minimum-variance weight from 50% to 99.5%.

Python Tutorial Code
Read article
The minimum-variance weight in stock A as stock B's estimated volatility changes from 19.8% to 20.2%, with stock A's at 20%. At a correlation of 0.5, the weight is between 49% and 51%; at 0.99, it changes from 0% to 99.5%. Both lines pass through 50% where both estimated volatilities are 20%.
Oct 8, 2026

How smart are smart beta ETFs?

From November 2015 to September 2026, the funds of all seven smart beta strategies had a lower average Sharpe ratio than their cap-weighted benchmarks.

Python Backtesting Analysis Code
Read article
One dot per smart beta strategy: the average difference between three funds and their cap-weighted benchmarks in annual return, across, and in Sharpe ratio, up, from November 2015 to September 2026. All seven dots are below zero in Sharpe ratio, and only momentum is above zero in return.
Oct 6, 2026

Does announcing trades still hurt index investors?

Over 2012 to 2025, stocks entering and leaving the MSCI USA Minimum Volatility Index returned about as much as its other stocks before the changes were implemented.

Python Backtesting Analysis Code
Read article
Return of the stocks entering and leaving the MSCI USA Minimum Volatility Index relative to its other stocks over 2012 to 2025, from five trading days before the announcement to five after the rebalance. Both lines are close to zero at the rebalance day, and trading volume that day is far above normal.
Oct 2, 2026

How to do factor investing

How to turn value and momentum into one score, select and weight 100 stocks, and rebalance them with trading costs, on simulated data.

Python Backtesting Tutorial Code
Read article
Simulated wealth of one dollar from the end of 2005 to 2025 on a log scale for a value and momentum portfolio before and after trading costs and for a cap-weighted benchmark of the same 500 stocks, with the portfolio lines above the benchmark.
Oct 1, 2026

Testing a stock characteristic as a factor

How to go from an economic idea about a characteristic to the evidence that it is a factor, in five steps, with value on simulated data.

Python Backtesting Tutorial Code
Read article
Ten bars for the five book-to-market groups of half B of the simulated stocks: teal alphas against half A's market return, from -0.20% to 0.19% a month, and red alphas after adding half A's value spread, between -0.02% and 0.02% a month, each with a grey line for its 95% confidence interval.
Sep 30, 2026

Do large stocks crash more often after doubling?

Large US stocks that had doubled crashed more often than large stocks in general, and about as often as large stocks with similar past volatility that had not doubled.

Python Backtesting Analysis Code
Read article
Share of large US stocks that fell at least 40% from a peak within two years, by their gain over the previous two years, against all large stocks and against large stocks with similar past volatility and smaller gains.
Sep 25, 2026

What AUC misses about predicted probabilities

Halving every announcement's odds of a crime leaves the AUC unchanged, while the probabilities then add up to about half the crimes that occur.

Python Machine Learning Tutorial Code
Read article
Observed crime rate against average predicted probability in six bands. The teal points for the original probabilities are close to the dashed line where the two are equal, and the red points for the halved-odds probabilities are all above it.
Sep 25, 2026

Detecting unusual trading with an isolation forest, part 2: broker records

At a simulated broker, an isolation forest flags 36.3% of insider purchases that resemble prosecuted cases, and 1.1%, no better than chance, when insiders buy like ordinary buyers.

Python Machine Learning Tutorial Code
Read article
Three bars of the share of the 91 insider purchases flagged: 36.3% for insiders like prosecuted cases, 1.1% for insiders like ordinary buyers, and 1.0% for three buyers per takeover chosen at random.
Sep 24, 2026

Detecting unusual trading with an isolation forest, part 1: market data

In a simulated market, an isolation forest flags only 6.4% of the days of illegal insider trading, because most of those days look like ordinary days.

Python Machine Learning Tutorial Code
Read article
Two scatter charts of simulated stock-days, relative volume against standardised return. On the left, the isolation forest's alerts surround the cloud of stock-days on both sides and at the top. On the right, the volume-only rule's alerts are at the top only. Red circles mark the stock-days of insider trading, most of them inside the grey cloud below the alerts.
Sep 23, 2026

A precise option price can still be wrong

More simulations make an estimated option price more precise, but they leave the error from a wrong volatility input unchanged.

Python Tutorial Code
Read article
Estimates of the call option price at 20% volatility for 1,000 to 1,000,000 simulations. Their 95% simulation intervals get shorter around 9.41, the Black–Scholes price at 20%, and an arrow of length 3.87 marks the pricing error from the wrong volatility, the difference between that price and 13.28, the Black–Scholes price at 30%.
Sep 22, 2026

How to validate a daily loss forecast

In a simulation, a daily loss forecast exceeded on 5% of all days, as intended, is exceeded on 20% of volatile days.

Python Backtesting Tutorial Code
Read article
Timeline of 2,000 simulated test days. Daily losses are in grey, a flat red fixed threshold is at 1.74%, and a teal adaptive threshold increases inside the two shaded volatile periods. In a strip below, red marks for the days when the loss is above the fixed threshold are mostly inside the volatile periods, and teal marks for the days when the loss is above the adaptive threshold are spread over the whole test sample.
Sep 20, 2026

Logit and probit: what the coefficients mean

How to read logit and probit coefficients as changes in the probability of a crime.

Python Tutorial Code
Read article
Three curves of predicted values against the information value of an announcement, holding the other two variables fixed: a straight line from a linear regression that is below zero on the left, and two S-shaped curves from a logistic and a probit model that are between 0 and 1 throughout.
Sep 19, 2026

Correlations with US stocks depend on which five years we measure

On monthly returns, US Treasuries had a correlation of -0.31 with US stocks over 2008 to 2013 and +0.53 over 2021 to 2026.

Python Analysis Code
Read article
Six dots, one per asset class, at their correlation with US stocks over August 2021 to July 2026: US Treasuries +0.53, corporate bonds +0.69, gold +0.17, commodities +0.01, developed markets outside the US +0.78, emerging markets +0.64.
Sep 15, 2026

Why the 10-year Treasury yield near 5% is not a clear warning for stocks

After the 10-year Treasury yield was near 5%, the median US stock return over the next year was lower, almost entirely because of 2000 to 2002.

Python Analysis Code
Read article
Median US stock return 1 to 12 months after a month-end, 1964 to 2025. The orange line for month-ends with the 10-year yield near 5 percent is at 9.3 percent after 12 months, and the teal line for all month-ends is at 13.9 percent.
Sep 14, 2026

Two models with 99% accuracy can rank an offender differently

How accuracy and the AUC differ, shown with two models that both have 99% accuracy on 100 traders but AUCs of 0.50 and 0.91.

Python Machine Learning Tutorial Code
Read article
A magnifying glass over rows of white figures, with one red figure in the middle.
Sep 10, 2026

Why the 60/40 portfolio is not dead.

Measured on daily returns instead of the five annual returns the Financial Times used, a 60/40 portfolio's volatility over 2021 to 2025 is 10.94%, against 14.86%.

Python Backtesting Analysis Code
Read article
Three stock-bond frontiers for 2021 to 2025, measured annually, monthly and daily. The annual curve is a straight line and the daily curve bends.
Sep 2, 2026

Portfolio sorts and Fama-MacBeth regressions

When only the top fifth of simulated stocks earns extra return, five group averages show it, and a straight line predicts more return at every step.

Python Backtesting Tutorial Code
Read article
Five teal bars, the measured average returns of five groups of simulated stocks, at 0.64%, 0.61%, 0.66%, 0.57% and 1.47% a month, and a red fitted straight line that increases across all five groups, below the bars of groups 1 and 5 and above the bars of groups 2, 3 and 4.
Aug 31, 2026

Data cleaning for a backtest: what pandas dropna() deletes

On a simulated four-asset portfolio, dropna() deletes returns that were never missing and cuts the backtest's final value by a third.

Python Backtesting Tutorial Code
Read article
The value of 1.00 invested over twenty years in one simulated history, processed four ways: dropna() on everything ends lowest, stopping the row deletion ends higher, and also compounding the crypto weekend in ends on the truth.
Aug 18, 2026

When a company changes its name, should you sell?

Unprofitable firms that changed their name trailed comparable firms by about 13 percentage points over the next year, and profitable ones did not.

Backtesting Python Analysis Code
Read article
Six estimates of the 12-month return difference between unprofitable renaming firms and comparable firms, from -10.8 to -13.8 percentage points.
Aug 11, 2026

Same data, same trading signal, different answer

On simulated data with a real edge, at least one of ten ordinary changes in how the portfolio is built flips the trading decision in 79% of datasets.

Python Backtesting Tutorial Code
Read article
A quant working at a laptop, for a post on how ordinary choices change a backtest's conclusion.
Jul 26, 2026

Six biases that distort quant research, measured where the truth is known

Five of the six biases make a worthless result look valuable, and one makes a good strategy look bad.

Python Tutorial Code
Read article
An open book beside a laptop showing a chart, for a post on biases in quantitative research.
Jul 25, 2026

In a market with no edge, tuning against the hold-out produced a Sharpe ratio of 2.8

The tuned strategy has a Sharpe ratio of −0.2 on a final test used once, because tuning against a hold-out fits the hold-out's noise.

Python Backtesting Tutorial Code
Read article
A control panel labelled Strategy Parameters, with knobs for lookback, volatility filter, size screen, momentum tilt and weight limit.
Jul 24, 2026

Download US insider trading data from LSEG Workspace in Python

A Python script that downloads insider transactions for about 10,000 US stocks from LSEG and resumes after an interruption.

API Python Tutorial Code
Read article
downloading US insider trading data from LSEG in Python
Jul 24, 2026

Download US IPO data from LSEG Workspace in Python

A Python script that downloads every US IPO since 1988 from LSEG, delisted companies included, and keeps the operating companies.

API Python Tutorial Code
Read article
downloading US IPO data from LSEG in Python
Jul 23, 2026

Buy the dip? US vs. Europe

Buying the dip, net of costs, roughly matched the market over the next year in the US, and in Europe trailed it after falls of 10% or more.

Python Backtesting Analysis Code
Read article
Median 12-month abnormal return after a drawdown, US against Europe, by drawdown threshold.
Jul 20, 2026

Should you wait for new lows or new highs?

Waiting for new S&P 500 highs or lows to invest usually earned less over ten years than investing every month.

Python Backtesting Analysis Code
Read article
Difference in annual IRR for two waiting rules, against the length of the new high or low window in months.
Jul 19, 2026

Connect to IBKR with Python

How to connect Python to Interactive Brokers with ib_async and download a year of daily prices into pandas.

Python API Tutorial Code
Read article
Python code connects to the Trader Workstation desktop app, which connects to Interactive Brokers.
Jul 19, 2026

Trading costs more than halved Swedish momentum's ending value

From 2001 to 2025, trading costs cut one krona's growth in Swedish momentum from 358 kronor to 156, still ahead of the market's 55.

Python Backtesting Analysis Code
Read article
Momentum on Swedish stocks against the market, 2001 to 2025, before and after costs.
Jul 19, 2026

Currency risk matters, especially if you are Swedish or Swiss

From 1988 to 2026, the S&P 500 multiplied an unhedged investment 109.7 times in Swedish kronor and 41.1 times in Swiss francs.

Python Backtesting Analysis Code
Read article
Growth of one unit in the S&P 500 total return, in six home currencies, 1988 to 2026.
Jul 18, 2026

The Magic Formula worked. Just not lately.

Greenblatt's Magic Formula returned 11.1% a year after trading costs, against 8.7% for the S&P 500 from 2001 to 2026, and has trailed it since about 2015.

Python Backtesting Analysis Code
Read article
Magic Formula portfolio against the S&P 500, 2001 to 2026, with a rolling five-year premium.
Jul 17, 2026

The longer you hold a stock, the less likely you are to beat the market

From 2000 to 2026, 45.7 percent of US stocks beat the S&P 500 over one year, and 27.0 percent over fifteen years.

Python Backtesting Analysis Code
Read article
Share of US stocks that beat the S&P 500, decreasing from 45.7 percent at one year to 27.0 percent at fifteen, 2000 to 2026.
Jul 16, 2026

Purged cross-validation: why a model with no predictive power can score 90% accuracy

Training labels that reach into the test period raise a model's accuracy on returns with no signal to 90.5%, and dropping those training days gives 50.3%.

Python Machine Learning Backtesting Tutorial Code
Read article
Diagram showing how purging prevents training outcomes from overlapping the test period.
Jul 13, 2026

Does your backtested Sharpe ratio show a real edge?

In a simulated market with no edge, the best of 216 strategies has a Sharpe ratio of 0.72, which searching alone matches or beats 41% of the time.

Python Backtesting Tutorial Code
Read article
Does your backtested Sharpe ratio show a real edge?
Jul 12, 2026

Download tick data from LSEG Tick History in Python

A Python script that downloads every trade and quote for a list of stocks from LSEG Tick History and restarts where it stopped.

API Python Tutorial Code
Read article
Download tick data from LSEG Tick History in Python
Jul 10, 2026

Does an IPO boom precede a weak year for the S&P 500?

From 1988 to 2025, an IPO boom was no stable warning: in the following year the S&P 500 returned within a few points of its average.

Python Backtesting Analysis Code
Read article
Next-12-month S&P 500 abnormal return by prior-year IPO intensity threshold.
Jul 8, 2026

Butter prices can appear related to the S&P 500

Two unrelated simulated price series have a correlation of 0.93 because both trend upward, and a cointegration test finds no evidence of a stable relation.

Python Backtesting Tutorial Code
Read article
Butter prices can appear related to the S&P 500
Jul 5, 2026

How to write better

Five rules from Jacobsen and McCloskey for stating your point directly, each with an example before and after.

Tutorial
Read article
How to write better
Jul 3, 2026

The priciest profitable IPOs pop 13.2% and then return -2.1% over their first year

Sorted by price-to-earnings ratio at the offer, the cheapest quarter of profitable US IPOs returned a median 13.0% in its first year, against −2.1% for the priciest.

Python Backtesting Analysis Code
Read article
Median first-day pop, day-one open-to-close, one-month and one-year return by US IPO size bucket, 2000 to 2025.
Jun 30, 2026

Can a chess rating system predict World Cup match winners?

At the 2014, 2018 and 2022 World Cups, Elo ratings predicted how often the favourite won, counting a draw as half, to within 3 percentage points.

Python Backtesting Analysis Code
Read article
Can a chess rating system predict World Cup match winners?
Jun 24, 2026

The most profitable US companies beat the S&P 500, and the least profitable lost money

From 2000 to 2026, the 30 most profitable US companies returned 9.7% a year after costs, against 8.1% for the S&P 500 and −0.4% for the 30 least profitable.

Python Backtesting Analysis Code
Read article
Growth of one dollar and drawdown for high-quality against low-quality US companies, 2000 to 2026.
Jun 8, 2026

How to backtest a strategy

A monthly momentum strategy backtested end to end in Python with the bt library, on simulated data, before and after trading costs.

Python Backtesting Tutorial Code
Read article
How to backtest a strategy
Jun 3, 2026

How did diversified portfolios do when US stocks rose or fell?

From 1988 to 2026, three diversified portfolios typically lost less than the S&P 500 in its falling months and gained less in its rising months.

Python Backtesting Analysis Code
Read article
How did diversified portfolios do when US stocks rose or fell?
May 27, 2026

Estimating the frequency of market crimes

From a prosecution rate of 5%, Detection-Controlled Estimation estimates a crime rate of 23%, where the simulated true rate is 25%.

Python Tutorial Code
Read article
Three bars for the crime rate among 10,000 simulated announcements: 5.0% counted from prosecutions, 22.9% estimated by Detection-Controlled Estimation, and a true rate of 25.3%.
May 24, 2026

How persistent is a firm's cost of capital relative to other firms?

Across US firms, the correlation between a firm's cost of capital today and one year later averages 0.67, and five years later 0.23.

Python Analysis Code
Read article
How the cross-sectional correlation of WACC and its components decays over one to five years.
May 20, 2026

Do the highest-yielding stocks beat the S&P 500?

The 30 highest-yielding US stocks returned 7.9% a year after trading costs from 2012 to 2026, against 15.0% for the S&P 500.

Python Backtesting Analysis Code
Read article
Do the highest-yielding stocks beat the S&P 500?
May 11, 2026

Does a large one-day move in the S&P 500 predict next week's return?

The S&P 500's median next-week return was far from zero only after one-day moves of 5% or more, and opposite in sign to those moves.

Python Backtesting Analysis Code
Read article
Does a large one-day move in the S&P 500 predict next week's return?
Apr 24, 2026

About 5% of stocks created all the net wealth

From 2000 to 2026, 5.4% of US stocks and 4.4% of European stocks created all the net wealth, the total gain above Treasury bills, repeating Bessembinder's finding.

Python Analysis Code
Read article
Cumulative share of net wealth created by the top few percent of stocks, US and Europe.
Apr 22, 2026

Does following insider buyers beat the S&P 500 after trading costs?

Following insider buyers beat the S&P 500 from 2001 to 2026 after costs, 15.1% a year against 9.1%, and trailed it over the last five years.

Python Backtesting Analysis Code
Read article
Does following insider buyers beat the S&P 500 after trading costs?
Apr 20, 2026

Classify insiders as routine or opportunistic

How to label an insider routine or opportunistic in Python, from the calendar months of their trades over the three previous years.

Code Python Tutorial
Read article
Four panels, one per insider, with one dot per trade at its year and calendar month. Alexander trades every March and his dots are teal, Routine, from 2018; Beatrice never repeats a month and her dots are red, Opportunistic, from 2018; Cecilia's June trades in odd years and Diana's single trade are grey, NonClassified.
Apr 10, 2026

Download stock data from LSEG Workspace in Python

A Python script that downloads daily data since 2000 for about 1,650 Swedish stocks from LSEG, delisted ones included, into one CSV.

API Python Tutorial Code
Read article
Download stock data from LSEG Workspace in Python

Categories

No matching items
Alexander Hübbert

Postdoctoral Researcher in Finance.

Home Research Articles Practical Coding DerivaSteps CV
LinkedIn Email
© 2026 Alexander Hübbert