Tutoriel d'introduction à la langue PINE de FMZ Quant

Auteur:Je ne sais pas., Créé: 2022-09-23 15:23:34, Mis à jour: 2024-02-27 16:47:41

[TOC] Je vous en prie.

img

Tutoriel d'introduction à la langue PINE de FMZ Quant

Vidéo tutoriel:https://www.youtube.com/watch?v=CA3SwJQb_1g

FMZ Quant Trading Platform prend en charge la rédaction de stratégies en langage Pine, le backtesting et le trading en direct de stratégies en langage Pine, et il est compatible avec des versions inférieures de langage Pine.Place de la stratégiesur la plateforme de négociation quantitative FMZ (FMZ.COM).

FMZ prend en charge non seulement le langage Pine, mais aussi la puissante fonction de dessin du langage Pine. Les différentes fonctions, les outils riches et pratiques, la gestion efficace et pratique sur la plate-forme FMZ améliorent encore la faisabilité de la stratégie (script) Pine. Sur la base de la compatibilité avec le langage Pine, FMZ élargit, optimise et aménage également le langage Pine dans une certaine mesure. Avant d'entrer officiellement dans le tutoriel, jetons un coup d'œil aux changements apportés au langage Pine sur FMZ par rapport à la version originale.

Un bref aperçu de certaines des différences évidentes:

    1. La stratégie Pine sur FMZ, l'identifiant de version au début du code//@versionet lestrategy, indicatorles déclarations au début du code ne sont pas obligatoires à écrire, FMZ ne prend pas en chargeimportà importerlibraryfonctionne pour le moment.

    On peut voir que certaines stratégies sont écrites comme ceci:

    //@version=5
    indicator("My Script", overlay = true)
    src = close
    a = ta.sma(src, 5)
    b = ta.sma(src, 50)
    c = ta.cross(a, b)
    plot(a, color = color.blue)
    plot(b, color = color.black)
    plotshape(c, color = color.red)
    

    Ou écrivez-le comme ceci:

    //@version=5
    strategy("My Strategy", overlay=true)
    
    longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
    if (longCondition)
        strategy.entry("My Long Entry Id", strategy.long)
    
    shortCondition = ta.crossunder(ta.sma(close, 14), ta.sma(close, 28))
    if (shortCondition)
        strategy.entry("My Short Entry Id", strategy.short)
    

    Sur FMZ, il peut être simplifié en:

    src = close
    a = ta.sma(src, 5)
    b = ta.sma(src, 50)
    c = ta.cross(a, b)
    plot(a, color = color.blue, overlay=true)
    plot(b, color = color.black, overlay=true)
    plotshape(c, color = color.red, overlay=true)
    

    Ou bien:

    longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
    if (longCondition)
        strategy.entry("My Long Entry Id", strategy.long)
    
    shortCondition = ta.crossunder(ta.sma(close, 14), ta.sma(close, 28))
    if (shortCondition)
        strategy.entry("My Short Entry Id", strategy.short)
    
    1. Certains paramètres liés au trading de la stratégie (script) sont définis par les paramètres Pine Language Trading Class Library sur l'interface de stratégie FMZ.
    • Modèle de prix de clôture et modèle de prix en temps réel En vue de la négociation, nous pouvons utiliser lecalc_on_every_tickparamètre dustrategyfonction de définir le script de stratégie pour exécuter la logique de la stratégie en temps réel lorsque le prix change à chaque fois.calc_on_every_tickle paramètre doit être réglé surtrueLecalc_on_every_tickle paramètre par défaut estfalse, c'est-à-dire que la logique de stratégie n'est exécutée que lorsque la ligne K actuelle BAR de la stratégie est complètement terminée. Sur FMZ, il est défini par les paramètres du modèle Pine Language Trading Class Library.

      img

    • Le contrôle de précision numérique, tel que le prix et le montant de la commande lors de l'exécution de la stratégie, doit être spécifié sur FMZ Dans la vue de la négociation, il n'y a pas de problème de précision lors du placement d'ordres de négociation réels, car il ne peut être testé que dans la simulation. Sur FMZ, il est possible d'exécuter la stratégie Pine dans le commerce réel. Ensuite, la stratégie doit être en mesure de spécifier la précision du prix et la précision du montant de l'ordre de la variété de négociation de manière flexible. Les paramètres de précision contrôlent le nombre de décimales dans les données pertinentes pour empêcher les données de ne pas répondre aux exigences de l'ordre de l'échange et donc de ne pas passer une commande.

    • Code du contrat à terme Si le produit de trading sur FMZ est un contrat, il a deux attributs, ils sont respectivement Trading Pair et Contract Code. En plus de définir la paire de trading explicitement, il est également nécessaire de définir le code de contrat spécifique dans le paramètre Variety Code du modèle Pine Language Trading Class Library pendant le trading réel et le backtesting.swapPar exemple, certaines bourses ont des contrats trimestriels.quarterCes codes de contrats sont compatibles avec les codes de contrats à terme définis dans le document de l'API du langage Javascript/python/c++ de FMZ.

    Pour d'autres paramètres, tels que le montant minimum de l'ordre, le montant par défaut de l'ordre, etc., veuillez consulter l'introduction du paramètre surArguments de modèle de la bibliothèque de la classe professionnelle de la langue du pindans la documentation de la langue Pine.

    1. Fonctions pour les extensions FMZ:runtime.debug , runtime.log, runtime.errorutilisé pour le débogage.

    3 fonctions ont été ajoutées à la plateforme FMZ pour le débogage.

    • runtime.debug: Imprimer des informations sur les variables sur la console, qui n'est généralement pas utilisée avec cette fonction.

    • runtime.logLes fonctions spécifiques à la langue PINE sur FMZ.

      runtime.log(1, 2, 3, close, high, ...), Multiple parameters can be passed.
      
    • runtime.error: Il en résultera une erreur d'exécution avec le message d'erreur spécifié dans le paramètre de message lors de l'appel.

      runtime.error(message)
      
    1. Leoverlayparamètre est étendu dans certaines des fonctions de dessin

    Dans le langage Pine sur FMZ, les fonctions de dessinplot, plotshape, plotchar, etc. ont ajouté leoverlayle support des paramètres, permettant de spécifier le dessin sur le graphique principal ou le sous-graphique.overlayest réglée surtruepour dessiner sur le graphique principal, etfalseest réglée pour tirer sur le sous-diagramme, ce qui permet à la stratégie Pine sur FMZ de tirer le diagramme principal et le sous-diagramme en même temps.

    1. La valeur de lasyminfo.mintickvariable intégrée

    La variable intégréesyminfo.mintickCette valeur peut être contrôlée par la précision de la devise du paramètre modèle de tarification dans la bibliothèque de classes de négociation de langues de pins sur la FMZ.le robot/test de retourLe paramètre de précision de la devise de prix 2 signifie que le prix est précis à la seconde décimale lors de la négociation et que l'unité minimale de changement de prix est de 0,01.syminfo.mintickest de 0,01.

    1. Le prix moyen dans FMZ PINE Script sont tous inclus de la commission

    Par exemple: le prix de commande est de 8000, la direction de vente, la quantité est de 1 lot (pièce, feuille), le prix moyen après la transaction n'est pas de 8000, mais inférieur à 8000 (le coût inclut les frais de manutention).

Les bases de la langue du pin

Lorsque vous commencez à apprendre les bases du langage Pine, il peut y avoir des exemples d'instructions et de grammaire de code que nous ne connaissons pas. Peu importe que vous ne le compreniez pas, nous pouvons d'abord nous familiariser avec les concepts et comprendre le but du test, ou vous pouvez consulter la documentation du langage Pine sur FMZ pour obtenir des instructions. Suivez ensuite le tutoriel étape par étape pour vous familiariser avec diverses grammaires, instructions, fonctions et variables intégrées.

Exécution du modèle

Lorsque vous commencez à apprendre le langage Pine, il est très nécessaire de comprendre les concepts connexes tels que le processus d'exécution du programme de script de langage Pine. La stratégie de langage Pine fonctionne sur la base du graphique. On peut comprendre que la stratégie de langage Pine est une série de calculs et d'opérations, qui sont exécutés sur le graphique dans l'ordre des séries chronologiques à partir des données les plus anciennes qui ont été chargées sur le graphique. La quantité de données que le graphique charge initialement est limitée. Dans le trading réel, la quantité maximale de données est généralement déterminée en fonction du volume de données maximal renvoyé par l'interface d'échange, et la quantité maximale de données pendant le backtesting est déterminée en fonction des données fournies par la source de données du système de backtesting. La barre de ligne K la plus à gauche du graphique, c'est-à-dire le premier ensemble de données du graphique, a une valeur d'index de 0. La valeur de l'index de la barre de donnéesbar_indexdans la langue du pin.

plot(bar_index, "bar_index")

img

Leplotfonction est l'une des fonctions que nous allons utiliser plus dans le futur. l'utilisation est très simple, il est de tracer une ligne sur le graphique selon les paramètres d'entrée, les données d'entrée estbar_index, et la ligne est nomméebar_index. On peut voir que la valeur de la ligne nommée bar_index sur la première barre est 0, et elle augmente de 1 à droite à mesure que la barre augmente.

Parce que les paramètres de la stratégie sont différents, les méthodes d'exécution du modèle de la stratégie sont différentes, elles peuvent être divisées enclosing price modeletreal-time price modelNous avons également présenté brièvement les concepts d'entre eux avant.

  • Modèle de prix de clôture

    Lorsque le code de stratégie est exécuté, la période de la barre de ligne K courante est complètement exécutée, et lorsque la ligne K est fermée, la période de ligne K a été complétée.

  • Modèle de prix en temps réel

    Lorsque le code de stratégie est exécuté, indépendamment du fait que la barre de ligne K actuelle soit fermée ou non, la logique de stratégie Pine sera exécutée chaque fois que le marché change et le signal de trading déclenché sera exécuté immédiatement.

Lorsque la stratégie du langage Pine est exécutée de gauche à droite sur le graphique, les barres de ligne K sur le graphique sont divisées enHistorical BarsetReal-time Bars:

  • Bar historique

    Lorsque la stratégie est réglée sur Tick Model et commence à s'exécuter, toutes les barres de la ligne K sur le graphique à l'exception de la plus à droite sontHistorical BarsLa logique de stratégie n'est exécutée qu'une seule fois sur chaquehistorical barJe suis désolée. Lorsque la stratégie est réglée sur Bar Model et commence à s'exécuter, toutes les barres du graphique sonthistorical barsLa logique de stratégie n'est exécutée qu'une seule fois sur chaquehistorical bar.

    Calcul basé sur les barres historiques: Le code de stratégie est exécuté une fois dans l'état de fermeture de la barre historique, puis le code de stratégie continue d'être exécuté dans la barre historique suivante jusqu'à ce que toutes les barres historiques soient exécutées une fois.

  • Barre en temps réel

    Lorsque la stratégie est exécutée jusqu'à la dernière barre de ligne K à l'extrême droite, la barre est une barre en temps réel.

    Lorsque la stratégie est définie sur Tick Model et commence à s'exécuter, la logique de la stratégie s'exécute une fois pour chaque variation du marché sur la barre en temps réel. Lorsque la stratégie est définie sur Bar Model et commence à s'exécuter, la barre en temps réel ne s'affiche pas sur le graphique.

    Calcul basé sur Bar en temps réel: Si la stratégie est définie sur Bar Model et que le graphique n'affiche pas les barres en temps réel, le code de stratégie ne sera exécuté qu'une seule fois lorsque la barre actuelle se ferme. Si la stratégie est réglée sur Tick Model, le calcul sur la barre en temps réel est complètement différent de la barre historique, et le code de stratégie sera exécuté une fois pour chaque changement de marché sur les barres de trading en direct.high, low, closeCes valeurs peuvent changer à chaque fois que le marché change sur les barres en temps réel. Par conséquent, les données telles que les indicateurs calculés sur la base de ces valeurs changeront également en temps réel.closereprésente toujours le dernier prix actuel, ethighetlowreprésente toujours le point le plus élevé et le point le plus bas atteint depuis le début de la barre en temps réel en cours.

    Mécanisme de rétroaction lors de l'exécution de stratégies sur Bar en temps réel (modèle de prix en temps réel): Lors de l'exécution en temps réel de Bar, la réinitialisation des variables définies par l'utilisateur avant chaque nouvelle itération de la stratégie est appelée rollback.

    Attention!

    /*backtest 
    ...
    ..
    .
    */
    

    Le contenu du paquet est constitué des informations de configuration de backtest enregistrées sous forme de code sur la plateforme FMZ.

    /*backtest
    start: 2022-06-03 09:00:00
    end: 2022-06-08 15:00:00
    period: 1m
    basePeriod: 1m
    exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
    */
    
    var n = 0
    if not barstate.ishistory
        runtime.log("before n + 1, n:", n, " current bar_index:", bar_index)
        n := n + 1
        runtime.log("after n + 1, n:", n, " current bar_index:", bar_index)
      
    plot(n, title="n")
    

    img

Nous examinons seulement la scène exécutée pendant les bars en temps réel, donc nous utilisons lenot barstate.ishistoryexpression pour limiter l'accumulation de la variable n uniquement dans la barre en temps réel, et utiliserruntime.logfonction de sortie de l'information dans le journal de stratégie avant et après l'opération d'accumulation.plot, on peut voir que n est toujours égal à 0 lorsque la stratégie est exécutée dans les Bars historiques. Lorsque la barre en temps réel est exécutée, l'opération d'addition de 1 à n est déclenchée, et l'opération d'addition de 1 à n est exécutée lorsque la stratégie est exécutée à chaque tour de la barre en temps réel. On peut observer à partir du message de journal que n sera réinitialisé à la valeur finalement soumise par la stratégie d'exécution de la barre précédente lorsque le code de stratégie est ré-exécuté à chaque tour. La mise à jour de la valeur n sera soumise lorsque le code de stratégie est exécuté sur la barre en temps réel pour la dernière fois, vous pouvez donc voir que la valeur de la courbe n augmente de 1 à chaque augmentation de Bar à partir de la barre en temps réel sur le graphique.

Résumé:

  1. Le code de stratégie est exécuté une fois chaque fois que le marché est mis à jour lorsque la stratégie commence à s'exécuter en temps réel.
  2. Lorsqu'elles sont exécutées dans une barre en temps réel, les variables sont inversées à chaque fois avant l'exécution du code de stratégie.
  3. Lorsqu'elles sont exécutées dans une barre en temps réel, les variables sont soumises une fois lorsque la clôture est mise à jour.

En raison du retour en arrière des données, les opérations de dessin, telles que les courbes sur le graphique, peuvent également entraîner un redessin.

var n = 0
if not barstate.ishistory
    runtime.log("before n + 1, n:", n, " current bar_index:", bar_index)
    n := open > close ? n + 1 : n
    runtime.log("after n + 1, n:", n, " current bar_index:", bar_index)
  
plot(n, title="n")

Capture d'écran du temps Aimg

Capture d'écran du temps Bimg

On a seulement modifié la phrase:n := open > close ? n + 1 : n, il suffit d'ajouter 1 à n lorsque la barre en temps réel actuelle est une ligne négative (c'est-à-dire que le prix d'ouverture est supérieur au prix de clôture). On peut voir que dans le premier graphique (temps A), puisque le prix d'ouverture était supérieur au prix de clôture (ligne négative) à ce moment-là, n a été accumulé de 1, et la valeur de n affichée sur la courbe du graphique était de 5. Ensuite, le marché a changé et le prix a été mis à jour comme indiqué dans le deuxième graphique (temps B). À ce moment-là, le prix d'ouverture est inférieur au prix de clôture (ligne positive), et la valeur de n retombe sans augmentation de 1.

  • Contextes variables dans les fonctions

    Étudions ensemble les variables de la fonction de langage Pine. Selon certaines descriptions sur les tutoriels Pine, les variables de la fonction ont les différences suivantes par rapport aux variables en dehors de la fonction:

    L'historique des variables de série utilisées dans la fonction Pine est créé à chaque appel successif à la fonction. Si la fonction n'est pas appelée sur chaque barre sur laquelle le script s'exécute, cela entraînera une divergence entre les valeurs historiques de la série à l'intérieur et à l'extérieur du bloc local de la fonction. Par conséquent, si la fonction n'est pas appelée sur chaque barre, la série référencée à l'intérieur et à l'extérieur de la fonction avec la même valeur d'index ne fera pas référence au même point historique.

    C'est un peu difficile à comprendre? On va le trouver avec un code de test qui fonctionne sur FMZ:

    /*backtest
    start: 2022-06-03 09:00:00
    end: 2022-06-08 15:00:00
    period: 1m
    basePeriod: 1m
    exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
    */
      
    f(a) => a[1]
    f2() => close[1]  
    
    oneBarInTwo = bar_index % 2 == 0
    plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")   
    plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")   
    plot(close[2], title = "close[2]", color = color.red, overlay = true)
    plot(close[1], title = "close[1]", color = color.green, overlay = true)
    

    Capture d'écran de l'exécution du backtest

    img

    Le code d'essai est relativement simple, principalement pour examiner les données référencées par deux méthodes:f(a) => a[1]etf2() => close[1].

    • f(a) => a[1]: Utiliser la méthode de transmission des paramètres, la fonction renvoie àa[1] finally.

    • f2() => close[1]: Utiliser la variable intégréeclosedirectement, et la fonction retourne àclose[1] finally.

Le[]Le symbole est utilisé pour faire référence à la valeur historique de la variable de la série de données, et close[1] fait référence aux données de prix de clôture sur la barre avant le prix de clôture actuel. Notre code de test dessine un total de 4 types de données sur le graphique:

  • plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")Dessinez un caractère A, la couleur est rouge, il est dessiné lorsque oneBarInTwo est vrai, et la position dessinée (sur l'axe Y est: la valeur renvoyée parf(close).

  • plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")Dessinez un caractère B, la couleur est verte, il est dessiné uniquement lorsque oneBarInTwo est vrai, et la position dessinée (sur l'axe Y) est: la valeur renvoyée parf2().

  • plot(close[2], title = "close[2]", color = color.red, overlay = true)Tracez une ligne, la couleur est rouge, et la position dessinée (sur l'axe Y) est:close[2], qui est le prix de clôture de la deuxième barre avant la barre courante (comptant 2 barres à gauche).

  • plot(close[1], title = "close[1]", color = color.green, overlay = true)Tracez une ligne, la couleur est verte, et la position dessinée (sur l'axe Y) est:close[1], qui est le prix de clôture de la première barre avant la barre courante (en comptant 1 barre à gauche).

Il peut être vu à partir de la capture d'écran de la stratégie backtesting que bien que les deux fonctionf(a) => a[1]utilisé pour dessiner le marqueur A et la fonctionf2() => close[1]utilisé pour dessiner le marqueur B [1] pour faire référence aux données historiques sur la série de données, les positions du marqueur A et B sur le graphique sont complètement différentes.plot(close[2], title = "close[2]", color = color.red, overlay = true), les données utilisées pour tracer la ligne sontclose[2].

img

La raison est de calculer si de tracer les marqueurs A et B à travers l'indice de la barre de ligne K, c'est-à-dire la variable intégréebar_index. Les marqueurs A et B ne sont pas dessinés sur chaque ligne K Bar (le calcul de la fonction est appelé lors du dessin). La valeur référencée par la fonctionf(a) => a[1]ne sera pas la même que la valeur référencée par la fonctionf2() => close[1]si la fonction n'est pas appelée sur chaque barre (même si les deux utilisent le même index comme [1]).

  • Certaines fonctions intégrées doivent être calculées sur chaque barre afin de calculer leurs résultats correctement

    Pour illustrer cette situation avec un exemple simple:

    res = close > close[1] ? ta.barssince(close < close[1]) : -1
    plot(res, style = plot.style_histogram, color=res >= 0 ? color.red : color.blue)
    

    Nous écrivons le code d'appel de la fonctionta.barssince(close < close[1])dans un opérateur ternairecondition ? value1 : value2. Cela provoque l'appel de la fonction ta.barssince uniquement lorsqueclose > close[1]Mais leta.barssincefonction est de calculer le nombre de lignes K depuis la dernière foisclose < close[1]Lorsque la fonction ta.barssince est appelée, elle est toujours close > close[1], c'est-à-dire que le prix de clôture actuel est supérieur au prix de clôture de la barre précédente.

    ta.barssince: Lorsqu'elle est appelée, la fonction renvoie na si la condition n'a jamais été remplie avant la ligne K actuelle.

Comme le montre le tableau:

img

Donc, lorsque le graphique est dessiné, seules les données avec une valeur pour la variable res (-1) sont dessinées.

Pour éviter ce problème, nous prenons simplement leta.barssince(close < close[1])la fonction appelle l'opérateur ternaire et l'écrit à l'extérieur de toutes les branches conditionnelles possibles, ce qui lui permet d'effectuer des calculs sur chaque K-line Bar.

a = ta.barssince(close < close[1])
res = close > close[1] ? a : -1
plot(res, style = plot.style_histogram, color=res >= 0 ? color.red : color.blue)

Séries chronologiques

Le concept de série temporelle est très important dans le langage Pine, et c'est un concept que nous devons comprendre lorsque nous apprenons le langage Pine. La série temporelle n'est pas un type mais une structure de base pour stocker des valeurs continues de variables au fil du temps. Nous savons que les scripts Pine sont basés sur des graphiques, et le contenu le plus basique affiché dans le graphique est le graphique en ligne K.openest une variable intégrée du langage Pine, et sa structure est de stocker la série temporelle du prix d'ouverture de chaque K-line Bar.openreprésente les prix d'ouverture de toutes les barres de ligne K de la première barre au début du graphique de ligne K actuel à la barre où le script actuel est exécuté.opendans le code de stratégie Pine, c'est le prix d'ouverture de la barre de la ligne K lorsque le code de stratégie est actuellement exécuté.[]Lorsque la stratégie Pine est exécutée sur une certaine K-line Bar, utiliseropen[1]pour faire référence au prix d'ouverture de la barre de ligne K précédente (c'est-à-dire le prix d'ouverture de la période de ligne K précédente) qui fait référence auopensérie temporelle sur laquelle cette barre de lignes K est actuellement exécutée par le script.

  • Les variables sur les séries temporelles sont très pratiques pour l'informatique Prenons la fonction intégréeta.cumà titre d'exemple:

    ta.cum
    
    Cumulative (total) sum of `source`. In other words it's a sum of all elements of `source`.
    ta.cum(source) → series float
    RETURNS
    Total sum series.
    ARGUMENTS
    source (series int/float)
    SEE ALSO
    math.sum
    

    Code de test:

    v1 = 1
    v2 = ta.cum(v1)
    plot(v1, title="v1")
    plot(v2, title="v2")
    plot(bar_index+1, title="bar_index")
    

    Il y a beaucoup de fonctions intégrées commeta.cumqui peut traiter les données sur les séries temporelles directement.ta.cumest l'accumulation des valeurs correspondant aux variables transmises sur chaque K-line Bar, et ensuite nous utilisons un graphique pour le rendre plus facile à comprendre.

    Processus de mise en œuvre de la stratégie Variable intégrée bar_index v1 V2
    La stratégie fonctionne sur la première barre de la ligne K. 0 1 1
    La stratégie fonctionne sur la deuxième barre de la ligne K. 1 1 2
    La stratégie fonctionne sur la troisième barre de la ligne K. 2 1 3
    La stratégie s'exécute sur la barre de ligne K N + 1 N 1 N+1

    On peut voir que v1, v2 et même bar_index sont toutes des structures de séries temporelles, et il y a des données correspondantes sur chaque barre.

    img

    Parce que la variable v1 est 1 sur chaque Bar, lorsque leta.cum(v1)fonction est exécutée sur la première ligne K Bar, il n'y a que la première Bar, donc le résultat du calcul est 1 et attribué à la variable v2. Quand?ta.cum(v1)est exécuté sur la deuxième K-line Bar, il y a déjà 2 K-line Bars (la variable intégrée bar_index correspondant à la première est 0, et la seconde correspondant à la variable intégrée bar_index est 1), donc le résultat du calcul est 2, qui est attribué à la variable v2, et ainsi de suite.bar_indexest augmenté de 0, alorsbar_index + 1sur le graphique, on peut aussi voir que les lignesv2etbar_indexIls se chevauchent en effet.

    img

    De même, je peux également utiliser leta.cumLa fonction intégrée pour calculer la somme des prix de clôture pour toutes les barres sur le graphique actuel.ta.cum(close), Lorsque la stratégie s'exécute à la barre en temps réel à l'extrême droite, le résultat calculé parta.cum(close)est la somme des prix de clôture de toutes les barres du graphique (s'il n'est pas à l'extrême droite, il ne s'accumule que jusqu'à la barre actuelle).

    Les variables des séries chronologiques peuvent également être calculées à l'aide d'opérateurs, tels que le code:ta.sma(high - low, 14), soustraire la variable intégréehigh(le prix le plus élevé de la barre de la ligne K)low(le prix le plus bas de K-line Bar), et enfin utiliser leta.smafonction pour calculer la valeur moyenne.

  • Le résultat d'un appel de fonction laissera également des traces de valeurs dans la série temporelle.

    v1 = ta.highest(high, 10)[1]
    v2 = ta.highest(high[1], 10)
    plot(v1, title="v1", overlay=true)
    plot(v2, title="v2", overlay=true)
    

    Le code de test s'exécute pendant le backtesting, et on peut observer que les valeurs dev1etv2Le résultat calculé par l'appel de la fonction laissera des traces de la valeur dans la série chronologique, comme leta.highest(high, 10)dans le codeta.highest(high, 10)[1]Le résultat calculé par l'appel de la fonction peut également utiliser [1] pour faire référence à sa valeur historique.ta.highest(high, 10)correspondant à la barre précédente de la barre actuelle, le résultat du calcul estta.highest(high[1], 10)Alors...ta.highest(high[1], 10)etta.highest(high, 10)[1]sont exactement équivalents.

    Utiliser une autre fonction de dessin pour produire la vérification des informations:

    a = ta.highest(close, 10)[1]
    b = ta.highest(close[1], 10)
    plotchar(true, title="a", char=str.tostring(a), location=location.abovebar, color=color.red, overlay=true)
    plotchar(true, title="b", char=str.tostring(b), location=location.belowbar, color=color.green, overlay=true)
    

    Nous pouvons voir que les valeurs de la variable a et de la variable b dans la série temporelle sont affichées au-dessus et en dessous des Bars correspondantes. Nous pouvons conserver ce code de dessin pendant le processus d'apprentissage, car nous pouvons souvent avoir besoin de produire des informations sur le graphique pour l'observation pendant les tests et l'expérimentation.

    img

Structure du scénario

Structure générale

Dans la première partie du tutoriel, nous avons résumé certaines différences dans l'utilisation du langage Pine sur FMZ et Trading View.indicator(), strategy(), etlibrary()Bien sûr, afin d'être compatible avec les versions antérieures des scripts Pine, des stratégies telles que://@version=5, indicator(), strategy()Certains paramètres de stratégie peuvent également être définis en passant des paramètres dans lestrategy() function.

<version>
<declaration_statement>
<code>

Le<version>Les informations relatives au contrôle de version peuvent être omises.

Commentaires

La langue du pin utilise//comme symbole de commentaire d'une seule ligne, puisque la langue Pine n'a pas de symbole de commentaire de plusieurs lignes. FMZ étend le symbole de commentaire/**/pour les commentaires de plusieurs lignes.

Le code

Les lignes du script qui ne sont pas des commentaires ou des directives du compilateur sont des instructions, qui mettent en œuvre l'algorithme du script.

  • Déclaration variable
  • Réattribution des variables
  • Déclaration de fonction
  • Appels de fonction intégrés, appels de fonction définis par l'utilisateur
  • if, for, whileouswitchla structure

Les déclarations peuvent être organisées de différentes manières.

  • Certaines instructions peuvent être exprimées en une seule ligne, comme la plupart des déclarations de variables, les lignes contenant un seul appel de fonction ou les déclarations de fonction en une seule ligne.
  • Les instructions dans la portée globale d'un script (c'est-à-dire les parties qui ne font pas partie d'un bloc local) ne peuvent pas commencer par unspaceLes lignes commençant à la première position, par définition, font partie de la portée globale du script.
  • Une déclaration de structure ou de fonction en plusieurs lignes nécessite toujours unelocal blockUn bloc local doit être indenté par un onglet ou quatre espaces (sinon, il sera analysé comme le code concaténé de la ligne précédente, c'est-à-dire jugé comme le contenu continu de la ligne précédente de code), et chaque bloc local définit une portée locale différente.
  • Plusieurs énoncés d'une seule ligne peuvent être concaténés dans une seule ligne en utilisant des virgules (,) comme séparateurs.
  • Une ligne peut contenir des commentaires ou ne contenir que des commentaires.
  • Les lignes peuvent également être enroulées (continuées sur plusieurs lignes).

Par exemple, il comprend trois blocs locaux, un dans la déclaration de fonction personnalisée et deux dans la déclaration de variable en utilisant la structure if, comme suit:

indicator("", "", true)             // declaration statement (global scope), can be omitted

barIsUp() =>                        // function declaration (global scope)
    close > open                    // local block (local scope)

plotColor = if barIsUp()            // variable declaration (global scope)
    color.green                     // local block (local scope)
else
    color.red                       // local block (local scope)

runtime.log("color", color = plotColor)  // Call a built-in function to output the log (global scope)

Code de rupture de ligne

Les lignes longues peuvent être divisées en plusieurs lignes, ou enveloppées. Une ligne enveloppée doit être indentée par n'importe quel nombre d'espaces, tant qu'elle n'est pas un multiple de 4 (ces limites sont utilisées pour indenter des blocs locaux).

a = open + high + low + close

Il peut être emballé comme suit (notez que le nombre d'espaces en retrait par ligne ne peut pas être un multiple de 4):

a = open +
      high +
          low +
             close

Un appel long peut être décrit comme suit:

close1 = request.security(syminfo.tickerid, "D", close)      // syminfo.tickerid daily level closing price data series for the current trading pair
close2 = request.security(syminfo.tickerid, "240", close)    // syminfo.tickerid 240-minute level closing price data series for the current trading pair
plot(ta.correlation(close, open, 100),                       // line-long plot() calls can be wrapped
   color = color.new(color.purple, 40),
   style = plot.style_area,
   trackprice = true)

Cependant, comme un bloc local doit commencer par une indentation en grammaire (4 espaces ou 1 onglet), lors de sa division sur la ligne suivante, la continuation d'une instruction doit commencer par plus d'une indentation (pas égale à 4 multiples d'espaces).

test(c, o) =>
    ret = c > o ?
       (c > o+5000 ? 
          1 :
              0):
       (c < o-5000 ? 
          -1 : 
              0)
           
                   
a = test(close, open)
plot(a, title="a")

Marqueurs et opérateurs

Marqueurs

Avant de reconnaître les variables, nous devons d'abord comprendre le concept de marqueurs.fonctionetVariable(utilisé pour nommer les variables et les fonctions).Les fonctionsOn verra dans nos tutoriels ultérieurs, apprenons d'abord sur les marqueurs.

    1. Les marqueurs doivent commencer par une majuscule(A-Z)ou en minuscules(a-z)lettre ou soulignement(_)comme premier caractère du marqueur.
    1. Le prochain caractère après le premier caractère d'un marqueur peut être unlettre, sous-traiter, ou unNuméro.
    1. La dénomination des marqueurs est sensible à la case.

Par exemple, les marqueurs nommés suivants:

fmzVar
_fmzVar
fmz666Var
funcName
MAX_LEN
max_len
maxLen
3barsDown  // Wrong naming! It used a numeric character as the leading character of the marker

Comme la plupart des langages de programmation, le langage Pine a également des suggestions d'écriture.

    1. Toutes les lettres majuscules sont utilisées pour nommer les constantes.
    1. Utilisez lecas de chameau inférieurpour les autres noms de marqueurs.
// name variables, constants
GREEN_COLOR = #4CAF50
MAX_LOOKBACK = 100
int fastLength = 7

// name functions
zeroOne(boolValue) => boolValue ? 1 : 0

Les opérateurs

Les opérateurs sont des symboles d'opération utilisés dans les langages de programmation pour construire des expressions, et les expressions sont des règles de calcul conçues pour certains objectifs de calcul lorsque nous écrivons des stratégies.

opérateurs d'affectation, opérateurs arithmétiques, opérateurs de comparaison, opérateurs logiques,? :les opérateurs ternaires,[]les opérateurs de référence historiques.

Prendre l'opérateur arithmétique*par exemple, il est différent du problème de type causé par le résultat de retour de l'opérateur de la langue Pine sur Trading View.

//@version=5
indicator("")
lenInput = input.int(14, "Length")
factor = year > 2020 ? 3 : 1
adjustedLength = lenInput * factor
ma = ta.ema(close, adjustedLength)  // Compilation error!
plot(ma)

Lorsque vous exécutez ce script sur Trading View, une erreur de compilation se produira.adjustedLength = lenInput * factor, le résultat estseries inttype (série), mais le deuxième paramètre de la fonctionta.emaMais il n'y a pas de restrictions aussi strictes sur FMZ, le code ci-dessus peut fonctionner normalement.

Examinons ensemble l'utilisation de différents opérateurs.

Les opérateurs de mission

Il existe 2 types d'opérateurs d'affectation:=, :=, que nous avons vu dans plusieurs exemples au début du tutoriel.

Le=L'opérateur est utilisé pour attribuer une valeur à une variable lorsqu'elle est initialisée ou déclarée.=Il s'agit de déclarations de variables valides:

a = close           // Use built-in variables to assign values to a
b = 10000           // Use numerical assignment
c = "test"          // Use string assignment
d = color.green     // Use color value assignment
plot(a, title="a")
plot(b, title="b")
plotchar(true, title="c", char=str.tostring(c), color=d, overlay=true)

Notez que l'énoncé de missiona = close, la variable a sur chaque barre est le prix de clôture (close) actuel de la barre.b, c, dsont inchangés et peuvent être testés dans le système de backtest sur FMZ et les résultats sont visibles sur le graphique.

:=On peut comprendre simplement que la valeur de la variable est la valeur de l'indicateur.:=L'opérateur est utilisé pour modifier les valeurs des variables qui ont été déclarées et initialisées. Si nous utilisons le:=l'opérateur pour attribuer une valeur à une variable non initialisée ou déclarée, il provoquera une erreur, par exemple:

a := 0

Par conséquent, le:=l'opérateur d'attribution est généralement utilisé pour réattribuer des variables existantes, par exemple:

a = close > open 
b = 0 
if a
    b := b + 1

plot(b)

Jugez siclose > open(c'est-à-dire que le BAR actuel est une ligne positive), la variable a est vraie. Le code dans le bloc local de l'instruction ifb := b + 1est exécuté, et l'opérateur d'affectation:=Ensuite, nous utilisons la fonction graphique pour dessiner la valeur de la variable b sur chaque BAR de la série temporelle sur le graphique, et les connecter en une ligne.

Est-ce que nous pensons que quand une ligne positive BAR apparaît, b continuera à s'accumuler de 1? Bien sûr que non, ici nous déclarons et initialisons la variable b comme 0 sans utiliser de désignation de mot clé.b=0est exécuté sur chaque BAR, donc nous pouvons voir que le résultat de ce code est de réinitialiser la variable b à 0 à chaque fois, si la variable a est vraie, c'est-à-dire en ligne avecclose > open, alors b sera augmenté de 1 lorsque le code est exécuté dans ce round, et b est 1 lorsque la fonction de tracé dessine, mais b est réaffecté à 0 lorsque le code est exécuté dans le round suivant. C'est aussi l'endroit où les débutants de la langue Pine sont sujets aux pièges.

En ce qui concerne les opérateurs d'affectation, nous devons développer sur deux mots clés:var, varip

  • - Je ne sais pas.

    En fait, nous avons vu et utilisé ce mot clé dans les tutoriels précédents, mais nous ne l'avons pas discuté en détail à ce moment-là.

    var est un mot-clé utilisé pour l'allocation et l'initialisation ponctuelle de variables. En général, la grammaire de l'attribution de variables qui ne contient pas le mot-clé var provoque la valeur de la variable s à être écrasée à chaque fois que les données sont mises à jour. En revanche, lorsque les variables sont affectées en utilisant le mot-clé var, elles peuvent maintenir l'état malgré les mises à jour de données.

    Nous utilisons toujours cet exemple, mais nous utilisons levarmot clé lors de l'attribution d'une valeur à b ici.

    a = close > open 
    var b = 0 
    if a
        b := b + 1
    
    plot(b)
    

    Levarmot clé permet à la variable b d'effectuer l'affectation initiale seulement, et alors il ne réinitialisera pas b à 0 à chaque fois que la logique de stratégie est exécutée, de sorte qu'il peut être observé à partir de la ligne dessinée à l'exécution que b est le nombre de lignes positives BARs qui sont apparus lorsque la ligne actuelle K BAR a été backtesté.

    Les variables déclarées par var peuvent être écrites non seulement dans la portée globale, mais aussi dans des blocs de code, comme dans cet exemple:

    strategy(overlay=true)
    var a = close
    var b = 0.0
    var c = 0.0
    var green_bars_count = 0
    if close > open
        var x = close
        b := x
        green_bars_count := green_bars_count + 1
        if green_bars_count >= 10
            var y = close
            c := y
    plot(a, title = "a")
    plot(b, title = "b")
    plot(c, title = "c")
    

    La variable a contient le prix de clôture de la première barre de la série. La variable b contient le prix de clôture de la première barre de prix verde de la série. La variable c détient le prix de clôture de la dixième barre verde de la série.

  • variété

    Nous voyons le mot clévaripPour la première fois, nous pouvons regarder la description de ce mot clé:

    varip (var intrabar persist) est un mot-clé pour l'attribution et l'initialisation unique de variables. Il est similaire au mot-clé var, mais les variables déclarées avec varip conservent leurs valeurs lors des mises à jour en temps réel de la ligne K.

    Est-ce difficile à comprendre? Peu importe, nous l'expliquons par un exemple, c'est facile à comprendre.

    strategy(overlay=true)
    
    // test var varip
    var i = 0
    varip ii = 0  
    
    // Print the i and ii changed in each round of the strategy logic on the chart
    plotchar(true, title="ii", char=str.tostring(ii), location=location.abovebar, color=color.red)
    plotchar(true, title="i", char=str.tostring(i), location=location.belowbar, color=color.green)  
    
    // Increment i and ii by 1 for each round of logic execution
    i := i + 1
    ii := ii + 1
    

    Ce code de test a des performances différentes sur le modèle Bar et le modèle Tick:

    Modèle de barre: Vous souvenez-vous que l'exécution de la stratégie que nous avons expliqué plus tôt est divisé en étape BAR historique et en temps réel BAR étape?i, iidéclaré envar, varipeffectuer des opérations incrémentielles à chaque tour d'exécution du code de stratégie. Par conséquent, on peut voir que les nombres affichés sur la ligne K BAR du résultat du backtest sont augmentés de 1 un par un. Lorsque l'étape historique de la ligne K se termine, l'étape de la ligne K en temps réel commence. Les variables déclarées par var et varip commencent à subir des changements différents. Parce que c'est un modèle de barre, le code de stratégie sera exécuté une fois pour chaque changement de prix dans une ligne K BAR,i := i + 1etii := ii + 1La différence est que ii est modifié à chaque fois. Bien que i soit modifié à chaque fois, la valeur précédente sera restaurée lorsque la logique de stratégie est exécutée dans le tour suivant (rappelez-vous le mécanisme de retour que nous avons expliqué dans le chapitre précédent Execution de modèle?), et la valeur de i ne sera pas mise à jour jusqu'à ce que la ligne K actuelle BAR soit terminée (c'est-à-dire que la valeur précédente ne sera pas restaurée lorsque la logique de stratégie est exécutée dans le tour suivant). On peut donc voir que la variable i est toujours augmentée de 1 pour chaque BAR. Mais la variable ii est accumulée plusieurs fois pour chaque BAR.

    Modèle de tique: Puisque le modèle Tick n'exécute la logique de stratégie qu'une seule fois par K-line BAR. Ainsi, dans le modèle de prix de clôture, les variables déclarées par var et varip se comportent exactement de la même manière dans l'exemple ci-dessus en augmentant de 1 pour chaque K-line BAR pendant l'étape historique de la ligne K et l'étape de la ligne K en temps réel.

Opérateurs arithmétiques
Les opérateurs Définition
+ Ajout
- Soustraction
* Multiplication
/ La division
% Moduleur

Le+et-Les autres opérateurs arithmétiques ne peuvent être utilisés que comme opérateurs binaires et il signalera une erreur s'il a été utilisé comme opérateur unitaire.

  1. Les deux côtés de l'opérateur arithmétique sont de type numérique, le résultat est un type numérique, entier ou virgule flottante selon le résultat de l'opération.
  2. Si l'un des opérandes est une chaîne et que l'opérateur est+, le résultat du calcul est une chaîne, la valeur sera convertie en forme de chaîne, puis les chaînes sont cousues ensemble.
  3. Si l'un des opérandes est na, le résultat du calcul est la valeur nullena, et il affichera NaN lorsqu'il est imprimé sur FMZ.
a = 1 + 1 
b = 1 + 1.1
c = 1 + "1.1"
d = "1" + "1.1"
e = 1 + na 

runtime.log("a:", a, ", b:", b, ", c:", c, ", d:", d, ", e:", e)   
// a: 2 , b: 2.1 , c: 11.1 , d: 11.1 , e: NaN

Le langage Pine sur FMZ est un peu différent du langage Pine sur Trading View, le langage Pine sur FMZ n'est pas très strict sur les types de variables.

a = 1 * "1.1"
b = "1" / "1.1"
c = 5 % "A" 

plot(a)
plot(b)
plot(c)

Il fonctionne sur FMZ, mais il signale une erreur de type sur la vue de trading. Si les deux opérandes de l'opérateur arithmétique sont des chaînes, le système convertit les chaînes en valeurs numériques et les calcule ensuite. Si une chaîne non numérique ne peut pas être calculée, le résultat de l'opération du système est une valeur nullena.

Opérateurs de comparaison

Les opérateurs de comparaison sont tous des opérateurs binaires.

Les opérateurs Définition
< <
> >
<= <=
>= >=
== ==
!= !=

Exemple de test:

a = 1 > 2 
b = 1 < 2 
c = "1" <= 2 
d = "1" >= 2 
e = 1 == 1 
f = 2 != 1 
g = open > close 
h = na > 1 
i = 1 > na

runtime.log("a:", a, ", b:", b, ", c:", c, ", d:", d, ", e:", e, ", f:", f, ", g:", g, ", h:", h, ", i:", i)   
// a: false , b: true , c: true , d: false , e: true , f: true , g: false , h: false , i: false

Comme nous pouvons le voir, l'opérateur de comparaison est très simple à utiliser, mais c'est aussi l'opérateur que nous utilisons le plus lorsque nous écrivons des stratégies.close, open, etc. Comme pour l'opérateur, il existe une différence sur le langage Pine entre FMZ et Trading View.d = "1" >= 2ne signalera pas d'erreur sur FMZ, et il sera exécuté en convertissant d'abord la chaîne en une valeur, puis en comparant l'opération.

Opérateurs logiques
Les opérateurs Symboles de code Définition
Je ne sais pas. Je ne sais pas. Opérateur unarien, pas opérations
et et Opérateurs binaires et opérations
ou ou Opérateurs binaires ou opérations

Quand il s'agit d'opérateurs logiques, alors nous devons parler de tables de vraies valeurs.

a = 1 == 1  // An expression formed by using comparison operators, the result is a Boolean value
b = 1 != 1
c = not b   // Logical not operators
d = not a   // Logical not operators

runtime.log("test the logical operator:and", "#FF0000")
runtime.log("a:", a, ", c:", c, ", a and c:", a and c)
runtime.log("a:", a, ", b:", b, ", a and b:", a and b)
runtime.log("b:", b, ", c:", c, ", b and c:", b and c)
runtime.log("d:", d, ", b:", b, ", d and b:", d and b)

runtime.log("test the logical operator:or", "#FF0000")
runtime.log("a:", a, ", c:", c, ", a or c:", a or c)
runtime.log("a:", a, ", b:", b, ", a or b:", a or b)
runtime.log("b:", b, ", c:", c, ", b or c:", b or c)
runtime.log("d:", d, ", b:", b, ", d or b:", d or b)

runtime.error("stop")

Afin de ne pas surimpression des messages, nous jetons une erreur avec leruntime.error("stop")Après cela, nous pouvons observer les informations de sortie, et nous pouvons trouver que le contenu imprimé est en fait le même que la table de valeur réelle.

Opérateur ternaire

Expressions ternaires utilisant l'opérateur ternaire? :combiné avec des opérandescondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalseNous les avons également utilisés dans les leçons précédentes. l'expression dite ternaire, opérateur ternaire signifie qu'il y a trois opérandes en elle.

Dans lecondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse, conditionest la condition de jugement. Si elle est vraie, la valeur de l'expression est:valueWhenConditionIsTrueSi.conditionest false, alors la valeur de l'expression estvalueWhenConditionIsFalse.

Exemple d'une démonstration pratique, bien que peu pratique:

a = close > open
b = a ? "positive line" : "negative line"
c = not a ? "negative line" : "positive line"
plotchar(a, location=location.abovebar, color=color.red, char=b, overlay=true)
plotchar(not a, location=location.belowbar, color=color.green, char=c, overlay=true)

Que faire si nous rencontrons un doji? Cela n'a pas d'importance! Les expressions ternaires peuvent également être imbriquées, comme nous l'avons fait dans le tutoriel précédent.

a = close > open
b = a ? math.abs(close-open) > 30 ? "positive line" : "doji" : math.abs(close-open) > 30 ? "negative line" : "doji"
c = not a ? math.abs(close-open) > 30 ? "negative line" : "doji" : math.abs(close-open) > 30 ? "positive line" : "doji"
plotchar(a, location=location.abovebar, color=color.red, char=b, overlay=true)
plotchar(not a, location=location.belowbar, color=color.green, char=c, overlay=true)

En fait, cela revient à remplacervalueWhenConditionIsTrueetvalueWhenConditionIsFalsedanscondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalseavec une autre expression ternaire.

Opérateur historique

Utiliser l'opérateur historique[]Ces valeurs historiques sont les valeurs de la variable sur la barre de ligne K avant la barre de ligne K actuelle lorsque le script était en cours d'exécution.[]L'opérateur est utilisé après les variables, les expressions et les appels de fonctions.[]Par exemple, si je veux citer le prix de clôture du dernier K-line BAR, nous l'écrivons comme:close[1].

Nous avons vu quelque chose comme ça dans les leçons précédentes:

high[10]
ta.sma(close, 10)[1]
ta.highest(high, 10)[20]
close > nz(close[1], open)

Le[]L'opérateur ne peut être utilisé qu'une seule fois sur la même valeur, il est donc erroné de l'écrire comme ceci, et une erreur sera signalée:

a = close[1][2]   // error

Ici, on peut dire que l'opérateur[]est utilisé pour la structure de série, il semble que la structure de série (série) est similaire à array! Utilisons un exemple pour illustrer la différence entre les séries et les tableaux dans la langue Pine.

strategy("test", overlay=true)

a = close
b = close[1]
c = b[1]

plot(a, title="a")
plot(b, title="b")
plot(c, title="c")

a = close[1][2]signalera une erreur, mais:

b = close[1]
c = b[1]

Mais si elle est écrite séparément, elle ne rapportera pas d'erreur.b = close [1], b devrait être une valeur, maisc = b[1], b peut encore être utilisé pour faire référence à la valeur historique à nouveau en utilisant l'opérateur historique. On peut voir que le concept de série dans le langage Pine n'est pas aussi simple qu'un tableau. Il peut être compris comme la valeur historique sur la dernière barre de close (assignée à b), b est également une structure de séries chronologiques (séries chronologiques), et son h


Plus de