Joblib: Backtests parallel uitvoeren op meerdere CPU cores
Stel je voor: je hebt een prachtige handelsstrategie gebouwd in Python. Je wilt hem testen op een jaar historische data van de Eurex of CME, met tientallen parametercombinaties.
Je start de backtest en... wacht. En wacht nog maar even. Een enkele CPU-core doet al het werk, terwijl je machine rustig vier, acht of zelfs zestien cores stil laat staan.
Dat voelt zonde, nietwaar? Joblib is de oplossing.
Het is een simpele Python-bibliotheek die je backtests parallel laat draaien. Je versnelt je analyse vaak met een factor 4 tot 8, afhankelijk van je CPU. Dat betekent minder wachten, sneller itereren en uiteindelijk betere trading bots.
Wat is Joblib en waarom gebruik je het voor backtesting?
Joblib is een Python-bibliotheek die speciaal is gebouwd voor simpele parallelisatie. Het is lichtgewicht, werkt met minimale code en is perfect voor numerieke berekeningen.
In algoritmische trading draait het vaak om het herhalen van dezelfde berekening op duizenden datapunten. Denk aan het berekenen van een Sharpe-ratio over 252 handelsdagen, of het optimaliseren van stop-loss en take-profit niveaus. Joblib verdeelt deze taken over alle beschikbare CPU-cores. Je hoeft geen ingewikkelde multiprocessing-code te schrijven.
Je roept één functie aan en Joblib doet de rest. Waarom is dit belangrijk?
Omdat tijd geld is. Als je een strategie test op 5 jaar tickdata van de DAX, kan een enkele run makkelijk 10 tot 30 minuten duren.
Als je 50 parametercombinaties wilt testen, zit je al snel op een dag werk. Met Joblib draai je die 50 runs parallel. Op een 8-core machine ben je in plaats van een dag binnen een uur klaar. Dat betekent dat je sneller kunt leren, sneller kunt optimaliseren en sneller kunt handelen.
Hoe Joblib werkt: de kern in heldere taal
Joblib draait om twee concepten: parallel en delayed. Je schrijft een functie die één backtest uitvoert.
Vervolgens vertel je Joblib: “Draai deze functie voor elke parametercombinatie, en doe dat op al mijn cores.” Dat ziet er zo uit:
from joblib import Parallel, delayed
resultaten = Parallel(n_jobs=-1)(delayed(backtest)(params) for params in lijst_met_parameters)
De parameter n_jobs=-1 betekent: gebruik alle beschikbare CPU-cores. De delayed-functie verpakt je backtestfunctie zodat Joblib hem kan uitvoeren zonder direct te draaien. De lijst met parameters bevat bijvoorbeeld stop-loss percentages, take-profit niveaus, moving-average lengtes of risicopercentages per trade.
Elk element uit de lijst wordt als eigen taak naar een aparte core gestuurd. Joblib is slim. Het maakt geen onnodige kopieën van je data. Het gebruikt memory mapping om grote datasets efficiënt te delen tussen processen.
Dat is handig als je werkt met historische koersen van duizenden instruments, zoals AAPL, TSLA of de Bund-futures.
Je hoeft niet bang te zijn dat je geheugen volloopt. Bovendien is Joblib compatible met NumPy en Pandas, de standaard bibliotheken voor data-analyse in Python.
Geen gepruts met complexe queues of threads. Gewoon helder en rechttoe rechtaan.
Praktijkvoorbeeld: backtesten met Joblib
Laten we een concreet voorbeeld bekijken. Stel, je hebt een eenvoudige moving-average crossover strategie op de DAX (DE40).
Je wilt testen welke combinatie van korte en lange moving averages het beste werkt. Je hebt een lijst met 100 combinaties. Zonder parallelisatie duurt één backtest 20 seconden.
Dat is 2000 seconden, oftewel ruim 33 minuten. Met Joblib op een 8-core machine daalt die tijd naar 4 tot 5 minuten.
params_lijst = [{'short': s, 'long': l} for s in range(5, 50, 5) for l in range(50, 200, 10)]
Je Python-code kan er zo uitzien. Je schrijft een functie backtest_ma(params) die de historische data inlaadt, de moving averages berekent, trades simuleert en een resultaat teruggeeft, zoals de Sharpe-ratio of het maximaal drawdown-percentage. In onze Python voor financiële analyse gids lees je hoe je dit structureert. Je bouwt een lijst met parameters:
Vervolgens roep je Joblib aan. Elk proces krijgt een eigen set parameters en draait de backtest onafhankelijk.
import pandas as pd
df = pd.DataFrame(resultaten)
beste = df.loc[df['sharpe'].idxmax()]
De resultaten komen als een lijst terug. Je kunt ze meteen analyseren, bijvoorbeeld met Pandas:
Je ziet meteen welke combinatie de hoogste Sharpe-ratio heeft, en welke drawdown acceptabel is. Met een paar regels code heb je een volledige parameteroptimalisatie gedraaid. Geen uren wachten, geen complexe threading-code. Gewoon snelle resultaten.
Varianten en modellen met prijsindicaties
Joblib is niet de enige manier om parallel te draaien, maar het is de meest toegankelijke voor algoritmische traders. Er zijn een paar varianten en modellen die je kunt overwegen, afhankelijk van je setup en budget.
- Standaard CPU-parallelisatie (Joblib): gratis, werkt op elke moderne laptop. Een 8-core machine kost tweedehands zo’n €400 tot €700. Een nieuwe laptop met 16 cores (bijvoorbeeld Apple M2 Pro) kost rond de €2000. Je haalt snel een factor 4 tot 8 winst.
- Cloud-parallelisatie (AWS Batch, Google Cloud Run): voor grotere datasets of meer cores. Een c5.9xlarge instance op AWS heeft 36 vCPUs en kost ongeveer €0,70 per uur. Draai je backtest een uur, betaal je €0,70. Handig als je één keer per week een grootschalige optimalisatie draait.
- GPU-acceleratie (Numba, CuPy): voor zeer rekenintensieve modellen, zoals Monte Carlo-simulaties of diepe leermodellen. Een NVIDIA RTX 4090 kost rond de €1600 en kan duizenden simulaties per seconde draaien. Maar pas op: GPU-code is complexer en niet alle trading-bibliotheken zijn GPU-ready.
- Distributie met Dask: als je meerdere machines wilt gebruiken. Dask bouwt voort op Joblib-concepten en schaalt naar clusters. Een kleine Dask-cluster op AWS kost vanaf €200 per maand, afhankelijk van instance-types.
De keuze hangt af van je frequentie en dataset. Voor dagelijkse Eurex-futures is een krachtige laptop met Joblib vaak al voldoende. Voor tickdata van de CME of grote aandelenportefeuilles kan een cloud-instance handig zijn. Voor high-frequency strategieën met microseconden-nauwkeurigheid kijk je naar GPU of FPGA, maar dat is een andere orde van grootte.
Praktische tips voor soepel parallel backtesten
Begin klein en volg onze Backtrader handleiding om je eerste backtest script te schrijven met één parametercombinatie.
Zorg dat hij stabiel loopt en geen geheugenlekken heeft. Schrijf resultaten weg naar een CSV of SQLite, zodat je geen data verliest als een proces crasht.
Gebruik loggen voor debuggen: elke core kan zijn eigen logregels schrijven zonder elkaar te storen. Let op je data-invoer. Als je historische data inlaadt per core, kan dat leiden tot dubbele I/O.
Joblib’s memory mapping helpt, maar je kunt ook de data vooraf inladen en als globale variabele beschikbaar stellen. Test altijd met een kleine dataset voordat je de grote run start. Niets is vervelender dan een uur wachten en dan een foutmelding zien. Bewaak je risico’s, zelfs in backtests.
Gebruik realistische transactiekosten, slippage en spread. Bijvoorbeeld: €2 per trade voor DAX-futures, 0,1% spread voor aandelen.
Zonder deze kosten lijken strategieën vaak te goed. Joblib helpt je snel te testen, maar de kwaliteit van je parameters bepaalt de uitkomst.
Tot slot: vergeet niet te controleren of je broker-API limieten stelt aan het aantal requests per minuut. Parallelle backtests zijn offline, maar als je live gaat, moet je rekening houden met rate limits. Met Joblib maak je je backtestproces sneller, efficiënter en leuker.
Je wint tijd om te focussen op wat echt telt: het verbeteren van je strategie en het beheren van je risico.
Dus pak je Python-omgeving, installeer Joblib via pip, en multiprocessing gebruiken in Python om backtests te versnellen. Je zult versteld staan hoeveel sneller je resultaten boekt.
