PagesDonnées OHLCV et de barres · 10 / 29
Données OHLCV et de barres
Les backtests QuantCraft ont besoin de bougies OHLCV (ouverture, plus haut, plus bas, clôture, volume) pour chaque symbole que vous tradez. Le moteur charge ces données avant l'exécution de votre stratégie, puis déclenche les callbacks du cycle de vie de votre stratégie avec une vue de la barre courante (bar) à chaque pas. À la fin de l'exécution, vous pouvez éventuellement travailler avec la série complète transmise à on_finish(bars, …). Configurez les chargements depuis Test (Exécution du code).
Cette page couvre l'origine des données (chargeurs), le fonctionnement des plages de dates et du préchauffage, et l'utilisation de l'argument bars en série complète dans on_finish. Associez-la aux Données fondamentales lorsque vous mélangez prix et dépôts ; voir Modèle de compte pour le lien entre opérations et prix.
Chargeurs (sources de données)
Dans l'onglet Symbol & data de Run backtest, vous choisissez un flux de prix. Le moteur utilise en coulisses l'un des deux chargeurs :
Flux du courtier connecté
- Charge les bougies depuis votre compte de courtier connecté avec votre clé API et votre secret, ou via une connexion liée par OAuth avec un identifiant de lien valide.
- Prend en charge les actions américaines et les cryptomonnaies (vous choisissez le type d'actif dans la fenêtre).
- Prend en charge de nombreux intervalles de temps — journalier, hebdomadaire, horaire, 4 heures et plusieurs barres de minutes intrajournalières (alignés sur les libellés d'intervalle des graphiques de l'application).
- L'historique est récupéré par morceaux jusqu'à ce que la plage demandée soit couverte (ou jusqu'à atteindre un nombre minimal de barres en mode « sans dates explicites » hérité).
Utilisez le flux du courtier connecté si vous voulez des données issues du courtier ou le jeu complet d'intervalles intrajournaliers (de 1 minute à 4 heures, plus journalier et hebdomadaire).
Stockage QuantCraft (hébergé)
- Charge les bougies depuis le stockage hébergé de QuantCraft lorsque vous êtes connecté (l'application envoie votre jeton de session).
- Les fichiers sont organisés par symbole et par intervalle de temps. Avec un plan gratuit, la fenêtre de backtest propose journalier, hebdomadaire et mensuel ; Premium débloque l'intrajournalier et les intervalles étendus.
- Le fichier complet est récupéré, puis éventuellement filtré sur la plage de dates calendaires choisie.
- Les barres journalières portent un
tcontenant uniquement la date, comme"2024-01-02", lu en UTC.
Codes d'intervalle QuantCraft B2 : 1s, 1m, 2m, m5, 15m, 30m, h1, 2h, 4h, d, w, m, q, y. Attention : m signifie mensuel, pas minutes — 1 minute s'écrit 1m, 5 minutes m5.
Si une plage de dates ne contient aucune barre, le chargement échoue avec un message du type No AAPL bars (d) between 2010-01-01 and 2010-02-01. Available range is … to … — adjust the backtest dates.
Utilisez le stockage QuantCraft lorsque vous êtes connecté sans identifiants de courtier, ou lorsque vous voulez des lots OHLCV hébergés issus du catalogue de symboles QuantCraft (5 minutes, horaire, journalier, hebdomadaire ou mensuel).
Choisir entre les deux
| Besoin | Choix habituel |
|---|---|
| Jeu intrajournalier complet du courtier connecté (1m, 5m, 15m, 30m, 1H, 4H, …) | Flux du courtier connecté |
| 5 minutes ou horaire depuis le catalogue QuantCraft | Stockage QuantCraft |
| Journalier / hebdomadaire / mensuel depuis des fichiers hébergés | Stockage QuantCraft |
| Compte de courtier connecté + clés | Flux du courtier connecté |
| Accès connecté sans identifiants de courtier | Stockage QuantCraft |
Dans les callbacks de stratégie, lisez les prix depuis l'objet
barque le moteur vous transmet — pas depuis un singleton OHLCV global. Dans l'IDE, leqc_ohlcvde niveau module est souvent non défini au chargement de votre fichier ; s'y fier peut casser votre code. Il vaut aussiNonelors d'une exécution ciblant un screener, et lorsqu'il est défini, il couvre la plage complète chargée, préchauffage compris — et non la fenêtre exécutée que dessine le graphique — ce qui en fait la mauvaise source pour les séries de graphique. La vuebarest toujours correcte pour le pas de simulation courant.
Plages de temps
Vous contrôlez les barres qui participent au backtest avec la date de début, la date de fin et, éventuellement, le préchauffage.
Plage calendaire (start_date + end_date)
Lorsque vous définissez les deux dates dans l'onglet Simulation (format YYYY-MM-DD, interprétées comme des jours calendaires UTC) :
- Le moteur ne conserve que les barres comprises dans cette fenêtre.
- La plage est traitée comme un intervalle semi-ouvert : du début du jour de départ jusqu'à la fin du jour d'arrivée selon la convention habituelle du produit (les barres du jour calendaire de fin sont incluses comme configuré par le moteur).
Flux du courtier connecté : les bougies sont demandées pour cette période calendaire.
Stockage QuantCraft : la série complète du fichier est téléchargée, puis filtrée sur la même fenêtre de jours calendaires.
Si vous omettez les deux dates :
- Le flux du courtier connecté utilise une remontée héritée : le moteur remonte dans le temps jusqu'à avoir chargé assez d'historique (dans les limites de l'intervalle), plutôt qu'une fenêtre calendaire saisie par vous.
- Le stockage QuantCraft utilise toutes les barres du fichier (aucun filtre supplémentaire).
Barres de préchauffage (warmup_bars)
Entier non négatif optionnel : barres supplémentaires chargées avant votre date de début afin que les indicateurs puissent regarder en arrière sans manquer d'historique.
- Les barres de préchauffage sont visibles via
bar.close[1],bar.close[2], … etbar.bars_backsur la première barre d'exécution. on_bar/on_tick/on_timerne s'exécutent toujours que dans votre fenêtre d'exécution — le préchauffage ne sert qu'à l'historique, il n'ajoute pas de « jours de trading » simulés au résultat.
bar_index et calendrier
bar_indexcompte uniquement les barres d'exécution (la première barre de votre plage vaut0, puis1, …). Il ne compte pas les barres de simple préchauffage.- Les barres sont simulées de la plus ancienne à la plus récente dans la fenêtre d'exécution.
Données de barre dans les callbacks (bar)
À chaque on_bar et on_tick (et on_timer sur les exécutions forward lorsqu'il est configuré), le moteur transmet bar : une vue de barre OHLC (OhlcvBarView) pour le symbole concerné par le callback. Elle se comporte comme un mapping en lecture seule, mais isinstance(bar, dict) est faux — utilisez bar["close"] ou l'accès par attribut, pas des API réservées aux dict.
En backtest, on_tick reçoit des prix synthétiques issus de l'OHLC de chaque barre (quatre appels par barre, sans véritable historique de ticks). Sur les exécutions forward, on_tick se déclenche à chaque cotation en direct du courtier connecté, tandis que l'argument price suit la dernière valorisation — voir Cycle de vie de la stratégie.
Vous pouvez l'utiliser comme un mapping en lecture seule :
o = float(bar["open"])
h = float(bar["high"])
l = float(bar["low"])
c = float(bar["close"])
v = float(bar["volume"])
t = bar["t"] # ISO timestamp string (QuantCraft B2 daily bars: a date like "2024-01-02", UTC)Vous pouvez aussi lire l'historique relatif à la barre courante par indexation avec décalage (même barre, clôtures précédentes, etc.) :
current_close = bar.close[0]
prev_close = bar.close[1]bar.close[0]est la clôture du contexte de barre courant (même idée quefloat(bar["close"])danson_baren backtest) — voir Indices de barres danson_bareton_tickpour savoir de quelle barre il s'agit dans chaque callback.- Des indices plus grands remontent dans le passé ;
bar.bars_backindique jusqu'où vous pouvez regarder.
Indices de barres dans on_bar et on_tick
Les décalages comme bar.close[0], bar.close[1], … comptent toujours en arrière depuis la barre la plus récente que le callback peut voir — mais la barre « la plus récente » dépend du callback :

on_barse déclenche lorsqu'une barre se clôture. L'indice[0]est cette barre qui vient de se clôturer,[1]la barre précédente,[2]celle d'avant, et ainsi de suite. Chaque indice pointe vers une barre terminée.on_tickse déclenche pendant qu'une barre est encore en formation. L'indice[0]est la barre en formation (ses valeurs changent à chaque tick),[1]est la dernière barre clôturée,[2]celle d'avant, et ainsi de suite.
La même bougie se trouve donc un indice plus haut dans on_tick que dans on_bar :
| Bougie | Dans on_bar | Dans on_tick |
|---|---|---|
| En formation (pas encore clôturée) | non visible | [0] |
| Vient de se clôturer | [0] | [1] |
| Celle d'avant | [1] | [2] |
| Deux avant | [2] | [3] |
def on_bar(bar_index, bar, fundamentals=None, symbol=None):
last_closed = bar.close[0] # the bar that just closed
one_before = bar.close[1]
def on_tick(bar_index, tick_in_bar, price, bar, fundamentals=None, symbol=None):
forming = bar.close[0] # still forming — latest price so far
last_closed = bar.close[1] # same candle on_bar saw as [0]Le même décalage s'applique à chaque série de la vue (open, high, low, close, volume). Si vous déplacez de la logique de on_bar vers on_tick, ajoutez 1 à chaque décalage pour continuer à lire les mêmes bougies.
Backtest :
on_tickrejoue la barre courante sous la forme de quatre ticks synthétiques ;[0]y est donc la barre simulée, lue depuis sa ligne complète (voir Volume en[0]ci-dessous) — ce n'est pas une bougie partiellement construite comme sur une exécution forward.
Exactitude des données en direct (exécutions forward)
Le flux de prix en direct de votre courtier est une approximation, pas un enregistrement complet du marché :
- Les plans de données en direct gratuits ne voient généralement que les transactions d'une seule place — une petite fraction de l'ensemble des échanges américains — de sorte que les bougies construites en direct affichent un volume bien inférieur et peuvent manquer les vrais plus hauts et plus bas.
- Les plans payants à flux consolidé voient toutes les places, mais les transactions peuvent encore arriver en retard par salves, surtout autour de l'ouverture sur les actions très actives.
L'application remplace chaque bougie construite en direct par la barre historique finalisée du courtier peu après sa clôture. Sur de petits intervalles comme 1 minute, cela signifie :
| Indice | De quoi il s'agit | À utiliser pour |
|---|---|---|
bar.close[0] | La bougie en formation, construite à partir de ticks en direct | L'estimation du prix courant |
bar.close[1] | La barre qui vient de se clôturer — peut encore être construite à partir de ticks pendant les ~10–30 premières secondes | À utiliser avec précaution juste après l'ouverture d'une nouvelle barre |
bar.close[2] | Une barre entièrement finalisée | Les signaux qui exigent une clôture confirmée |
Pour les signaux qui dépendent d'une clôture confirmée sur des barres de 1 minute, préférez bar.close[2].
Volume en [0] :
on_tickforward :volume[0]sur la bougie en formation est provisoire (souvent la taille d'un seul tick). Utilisezvolume[1]pour le volume de la dernière barre terminée.- Backtest : les quatre ticks synthétiques utilisent la ligne complète de la barre —
volume[0]est le volume complet de la barre, et non la taille provisoire d'un tick.
C'est le bon endroit pour la logique de trading à chaque pas : entrées, sorties à la clôture de la barre, moyennes mobiles simples, et tout ce qui n'a besoin que de la barre courante plus un historique.
Intervalles principal et supplémentaires (get_bar)
L'intervalle principal de l'exécution est toujours disponible via rt.get_bar (inutile de le lister sous Additional timeframes). Lorsque votre stratégie a aussi besoin de barres à une résolution différente (par exemple des filtres de tendance journaliers sur un backtest en 5 minutes), sélectionnez ces intervalles supplémentaires dans la liste Additional timeframes de la fenêtre Run backtest, onglet Symbol & data, puis lisez-les depuis n'importe quel callback :
from quantcraft import Timeframe
import quantcraft.runtime as rt
def on_bar(bar_index, bar, fundamentals=None, symbol=None):
# Primary TF works without listing it under Additional timeframes
# (use the Timeframe member that matches the modal primary, e.g. M5):
prev_primary = rt.get_bar(Timeframe.M5, shift=1)
daily = rt.get_bar(Timeframe.DAILY, shift=1)
if daily and bar.close[0] > daily.close:
... # primary bar closed above yesterday's daily closeConfigurez Additional timeframes dans les Backtests ; enregistrez les sélections dans les préréglages d'exécution (.qcs) sous secondaryTimeframes.
Champs de BarSnapshot
get_bar renvoie un BarSnapshot (ou None) avec : open, high, low, close, volume, t.
Sémantique de shift
shift | .close | .high / .low / .open |
|---|---|---|
0 | Prix d'exécution courant — identique pour tous les intervalles au sein du même callback (backtest ; voir la note forward ci-dessous) | Depuis la barre stockée de cet intervalle — non unifié avec l'intervalle principal |
>= 1 | Clôture de la barre terminée sur cet intervalle | OHLC complet de la barre terminée — à privilégier pour les mèches de la bougie précédente |
Backtest : get_bar ne voit jamais que les barres jusqu'au pas de simulation courant — jamais les suivantes — sur l'intervalle principal comme sur chaque intervalle supplémentaire. À shift=0, les high/low secondaires sont alignés dans le temps sur l'horloge de simulation pour chaque intervalle. get_bar lit les barres du symbole d'horloge ; dans une exécution multi-symboles, utilisez les bar.close[i] propres à chaque ticker pour son historique.
Exécution forward : la bar principale suit l'intervalle de la barre d'outils du graphique. Les tampons secondaires se mettent à jour lorsqu'une barre se termine dans le flux de cet intervalle — high/low à shift=0 peuvent être en retard jusqu'à la fin de ce créneau ; préférez shift=1 pour un OHLC fiable de la barre précédente. En forward, le .close à shift=0 est la dernière clôture propre à chaque tampon, non unifiée entre intervalles. Dans on_tick, volume[0] sur la barre principale peut être provisoire (encore en formation).
Renvoie None lorsque cet intervalle n'a pas de tampon pour l'exécution (ni l'intervalle principal ni sélectionné sous Additional timeframes), lors d'un backtest Screener (pas de symbole d'horloge — utilisez les décalages bar.close[i] propres à chaque ticker pour l'historique), ou lorsque les données sont indisponibles. Une exception en backtest : shift=0 sur un intervalle que vous n'avez pas sélectionné renvoie l'instantané courant de l'intervalle principal plutôt que None.
tf peut être un membre de Timeframe ou une chaîne comme "1D" / "5Min".
Énumération Timeframe
Importez avec from quantcraft import Timeframe :
| Membre | Valeur chaîne | Description |
|---|---|---|
M1 | "1Min" | Barres de 1 minute |
M5 | "5Min" | Barres de 5 minutes |
M15 | "15Min" | Barres de 15 minutes |
M30 | "30Min" | Barres de 30 minutes |
H1 | "1Hour" | Barres horaires |
H4 | "4Hour" | Barres de 4 heures |
DAILY | "1D" | Barres journalières |
WEEKLY | "1Week" | Barres hebdomadaires |
Certains intervalles QuantCraft B2 (2 minutes, 2 heures, 1 seconde, mensuel m, trimestriel q, annuel y) n'ont pas de membre Timeframe — transmettez-les à get_bar sous forme de chaînes.
Série complète optionnelle : on_finish(bars, …)
À la fin de la simulation, le moteur appelle on_finish une fois. Si votre stratégie définit la signature étendue, vous recevez des lignes OHLC en série complète pour le post-traitement — indicateurs sur toute l'exécution, export de séries, valorisations finales pour refresh_metrics, etc.
def on_finish(bars, symbol=None, bars_by_symbol=None):
...bars — symbole d'horloge, le plus ancien en premier
barsest unelistde dictionnaires pour le symbole d'horloge (leSYMBOLdu script si ce ticker est chargé, sinon le premier symbole chargé — la fenêtre n'a pas de commande de symbole principal). Dans une exécution Screener, il n'y a pas de symbole d'horloge :barsvaut[]etsymbolvaut"", utilisez doncbars_by_symbol.- L'ordre est chronologique : barre la plus ancienne d'abord, la plus récente en dernier — la convention inverse de l'indice
[0] = la plus récentedeQcOhlcvdans les API de type notebook. - Chaque ligne inclut au moins
open,high,low,close,volumeettlorsque des horodatages existent. Lorsque les données fondamentales sont activées, les lignes peuvent aussi inclure un instantanéfundamentalspour cette barre.
Usages typiques :
def on_finish(bars, symbol=None, bars_by_symbol=None):
if not bars:
return
closes = [float(b["close"]) for b in bars]
last = float(bars[-1]["close"])
sym = (symbol or "AAPL").strip().upper()
metrics = account.refresh_metrics({sym: last})
chart_indicators.add(...)Utilisez bars[-1]["close"] (ou les champs de la dernière ligne) comme valorisation finale lors de l'appel de account.refresh_metrics({symbol: last_price}), afin que les positions ouvertes soient valorisées à la fin de l'exécution.
Pour chart_indicators, préférez les valeurs que vous avez accumulées par symbole pendant le parcours des barres et n'utilisez on_finish que pour les transmettre ; ne recalculez à partir de bars que les indicateurs que vous n'avez pas suivis barre par barre — voir Séries d'indicateurs.
bars_by_symbol — exécutions multi-symboles
Lorsque vous faites un backtest sur plus d'un ticker, bars ne désigne toujours que le symbole d'horloge. Pour la série complète alignée de chaque symbole, utilisez bars_by_symbol :
- Un mapping ticker en majuscules → liste de même forme que
bars(dicts chronologiques par symbole). - Utilisez-le lorsque vous avez besoin d'historiques complets transversaux ou par symbole à la fin (corrélation, force relative, indicateurs par symbole).
Quand vous n'avez pas besoin de on_finish
Si votre stratégie n'a besoin que de la barre courante et des décalages d'historique, vous pouvez n'implémenter que on_bar / on_tick et éviter tout traitement lourd de série complète. on_finish est optionnel pour la logique — mais c'est tout de même là que les séries de chart_indicators sont transmises et que refresh_metrics reçoit le dernier prix. Notez que chaque return dans on_finish est silencieux : l'exécution réussit quand même, elle saute simplement ce qui suivait.
Lien avec Test Results
Le résultat structuré du backtest inclut une liste ohlcv.bars (ou équivalent) : une ligne par pas d'exécution simulé, avec les champs OHLC et des instantanés fundamentals optionnels — alignée sur ce que vous avez vu dans les callbacks. on_finish(bars, …) est la même série chronologique que celle utilisée par le moteur pour les calculs sur « toute l'exécution » du symbole d'horloge ; votre code de fin d'exécution correspond donc à ce qui apparaît dans les résultats et les graphiques.
Référence rapide
| Sujet | Résumé pour l'utilisateur |
|---|---|
| Chargeurs | Flux du courtier connecté (clés + large intrajournalier) vs stockage QuantCraft (connecté ; journalier / hebdomadaire / mensuel, davantage avec Premium). |
| Dates | Début + fin YYYY-MM-DD → fenêtre filtrée par calendrier ; omettre les deux → remontée héritée du flux du courtier ou fichier complet du stockage QuantCraft. |
| Préchauffage | Historique supplémentaire avant le début pour les décalages de bar ; callbacks uniquement dans la fenêtre d'exécution. |
| À chaque pas | Utilisez bar / bar["close"] / bar.close[k] dans on_bar / on_tick. [0] = dernière barre clôturée dans on_bar, barre en formation dans on_tick. |
| Intervalles principal / supplémentaires | Principal toujours via rt.get_bar ; supplémentaires via Additional timeframes + rt.get_bar(Timeframe.X, shift). |
| Série complète | Utilisez on_finish(bars, symbol=..., bars_by_symbol=...) — bars du plus ancien au plus récent pour le symbole d'horloge ; bars_by_symbol pour tous les symboles des exécutions multi-symboles. |
| Rendements de facteurs globaux | Pas sur QcOhlcv ni sur bar — utilisez les facteurs Fama-French (fama_french.qc). Les rows mensuelles sont un dict YYYYMM ; utilisez latest_row, pas rows[0]. |

