Skip to content
← All articles
Article

Experimenting with technical indicators using Python and backtesting

Listen to the audio story

The strategy that gained 200 percent by being wrong way round · 2:03 · AI-written script, synthetic voices

Transcript

Ava: Antoine ran a simple trading rule on hundreds of stocks and crypto pairs, and one coin came out with roughly two hundred percent return. Sounds great, until you see what buy-and-hold did on the same coin.

Andrew: Let me guess. It lost a lot.

Ava: About eighty percent. The rule didn't discover a secret. It happened to be short while the price collapsed.

Andrew: Okay, back up. What's the rule?

Ava: It's an EMA crossover. An EMA is a moving average that weighs recent prices more. When the twenty-period one crosses above the eighty-period one, the strategy buys. When it crosses back, it sells. Each trade has a ten percent stop loss and a twenty-five percent take profit.

Andrew: And he ran that across a whole market?

Ava: Yes, with a tool called BatchBacktesting. It pulls daily prices from Financial Modeling Prep for the S and P five hundred, or from Binance for crypto, runs the strategy on each ticker using backtesting dot py, and writes CSV results and optional charts. He ran it on twelve threads.

Andrew: So what did the leaderboard look like?

Ava: The top was Bitcoin against the BUSD stablecoin at plus two hundred ninety-four percent. The bottom was a token called BTT at minus ninety-nine point nine percent. And the stocks UAL and NCLH lost over eighty percent, because the COVID period punishes naive trend rules.

Andrew: So you can't just buy the winners tomorrow.

Ava: Definitely not. Antoine says the top and bottom lists are diagnostics, not buy and sell lists. The best result is probably a rule fitted to that window, and the backtest ignores things like fees, slippage, and liquidity.

Andrew: So what's it good for?

Ava: Seeing the distribution of outcomes under a simple rule. He calls it invaluable for humility and dangerous as autopilot. And always open the chart for a ticker before telling a story about it.

Andrew: A screening hammer, not a portfolio manager.

Ava: His own words, nearly. The full write-up has the code, the leaderboard and the buy-and-hold comparison chart.

Experimenting with technical indicators using Python and backtesting
Table of contents

I batch-ran an EMA crossover strategy across the S&P 500 and a list of crypto pairs with BatchBacktesting[1], using Financial Modeling Prep for equities and Binance for crypto. This post shows what the runner does, how to read the outrageous rows at the top and bottom of the leaderboard, and why none of it is a live trading system. The original article is in French on Medium.

1. What BatchBacktesting does

  1. Pulls daily OHLCV from Financial Modeling Prep (S&P 500 list) or Binance (crypto list).
  2. Applies a strategy class to each ticker with backtesting.py[2].
  3. Writes CSV results and optional HTML charts under output/.

It is a screening hammer, not a portfolio manager.

2. Setup

pip install numpy httpx rich backtesting pandas_ta

The notebook defines the API helpers itself (make_api_request, get_SP500, get_all_crypto, price history fetchers). It only needs the two base URLs and your keys:

BASE_URL_FMP = "https://financialmodelingprep.com/api/v3"
BASE_URL_BINANCE = "https://fapi.binance.com/fapi/v1/"
FMP_API_KEY = "YOUR_FMP_API_KEY"
BINANCE_API_KEY = "YOUR_BINANCE_API_KEY"

Keep real keys in environment variables, not in the notebook you commit.

3. The EMA strategy

Buy when the 20-period EMA crosses above the 80-period EMA, sell on the opposite cross. Each entry carries a 10 % stop loss and a 25 % take profit.

class EMA(Strategy):
    n1 = 20
    n2 = 80

    def init(self):
        close = self.data.Close
        self.ema20 = self.I(taPanda.ema, close.s, self.n1)
        self.ema80 = self.I(taPanda.ema, close.s, self.n2)

    def next(self):
        price = self.data.Close
        if crossover(self.ema20, self.ema80):
            self.position.close()
            self.buy(sl=0.90 * price, tp=1.25 * price)
        elif crossover(self.ema80, self.ema20):
            self.position.close()
            self.sell(sl=1.10 * price, tp=0.75 * price)

4. Running the batch

tickers = get_SP500()
run_backtests(tickers, strategy=EMA, num_threads=12, generate_plots=True)

tickers = get_all_crypto()
run_backtests(tickers, strategy=EMA, num_threads=12, generate_plots=True)

run_backtests spreads the tickers over a thread pool and writes one chart per instrument, such as this AAPL chart[3]. The repository does not ship precomputed results: run it locally to get your own output/ folder.

5. Reading the leaderboard

Top five EMA returns: BTCBUSD +293.78 %, ALB +205.97 %, OMGUSD +199.62 %, BBWI +196.82 %, GRMN +193.47 %. Bottom five: BTTBUSD −99.93 %, UAL −82.63 %, NCLH −81.51 %, LNC −78.02 %, CHRW −76.38 %.

TickerReturnGut check
BTCBUSD+293.78 %Crypto volatility plus a parameter fit to the period
BTTBUSD−99.93 %A token collapse; liquidity is not modeled
UAL / NCLH−82 % / −81 %COVID-era windows punish naive trend rules

Top and bottom lists are diagnostics, not buy and sell lists. Open the per-ticker chart before telling a story about a row.

Highest EMA returns next to buy-and-hold for the same instruments

The chart puts the EMA return next to buy-and-hold. OMGUSD made about 200 % with the rule while buy-and-hold lost about 80 %: the gain came from being short through a collapse, not from a reliable edge.

6. Where the full code lives

The Medium article pastes every helper: HTTP calls, threading, CSV writers and plotting. Those belong in the BatchBacktesting repository[1], where they are maintained. The follow-up post adds a MACD strategy to the same runner.

7. When not to use this

MisuseWhy it hurts
Deploying the top ticker liveOverfit to the window
Ignoring fees and slippageThe backtest inflates
Skipping out-of-sample datesA regime change breaks the rule

8. Takeaway

Batch runs teach the distribution of outcomes under a simple rule: invaluable for humility, dangerous as autopilot.


Originally published in French on Medium[4].

References

  1. AlgoETS. (). BatchBacktesting. [Software repository] github.com.
  2. backtesting.py. (). [Web page] kernc.github.io.
  3. this AAPL chart. (). [Web page] algoets.github.io.
  4. Medium. (). [Article] medium.com.

References use this article’s publication date.