Les ressources ont été chargées... Je charge...

Tutoriel d'introduction à la langue PINE de FMZ Quant

Auteur:FMZ~Lydia, Créé: 2022-09-23 15:23:34, Mis à jour: 2024-02-27 16:47:41

Donc nous voyons que dans les trois lignes dessinées a, b et c, la ligne b est un BAR plus lente que la ligne a, et la ligne c est un BAR plus lente que la ligne b. La ligne c est 2 BAR plus lente que la ligne a.

Nous pouvons faire glisser le graphique vers l'extrême gauche et observer que sur la première ligne K, les deux valeurs de b et c sont nulles (na). C'est parce que lorsque le script est exécuté sur la première ligne K BAR, il n'existe pas lorsqu'il se réfère à la valeur historique d'une ou deux périodes en avant, ce qui n'existe pas. Par conséquent, nous devons être prudents lors de l'écriture de stratégies pour vérifier si la référence aux données historiques entraînera des valeurs nulles. Si la valeur nulle est utilisée de manière imprudente, elle causera une série de différences de calcul, et peut même affecter la BAR en temps réel.na, nzIl est également possible de juger dans le code (en fait, nousnzDans nos précédentes vidéos, vous souvenez-vous de quel chapitre il s'agit?), nous avons traité du cas des valeurs nulles, par exemple:

close > nz(close[1], open)    // When referencing the historical value of the previous BAR of the close built-in variable, if it does not exist, the open built-in variable is used

Il s'agit d'une façon de gérer les références possibles à des valeurs nulles (na).

Priorité de l'opérateur

Nous avons appris beaucoup d'opérateurs dans le langage Pine. Ces opérateurs forment des expressions à travers diverses combinaisons avec des opérands. Alors quelle est la priorité de ces opérations lors de l'évaluation dans les expressions? Tout comme l'arithmétique que nous avons apprise à l'école, la multiplication et la division sont calculées en premier, suivie de l'addition et de la soustraction.

La priorité Les opérateurs
9 []
8 +-etnotdans l'opérateur unary
7 */%
6 +-dans l'opérateur binaire
5 ><>=<=
4 ==!=
3 and
2 or
1 ?:

Les expressions à haute priorité sont calculées en premier, et si les priorités sont les mêmes, elles sont évaluées de gauche à droite.()pour envelopper l'expression pour forcer la partie à être évaluée en premier.

Variables

Déclaration variable

Nous avons déjà étudié le concept de marqueur avant, qui est utilisé comme le nom d'une variable, c'est-à-dire qu'une variable est un marqueur qui détient une valeur.

  • Mode de déclaration: La première chose à écrire lors de la déclaration d'une variable est le mode de déclaration.

    1. Utilisez le mot clévar.
    2. Utilisez le mot clévarip.
    3. N'écrivez rien.

    LevaretvaripLes mots clés ont été étudiés dans notre chapitre précédent surAssignment OperatorsSi rien n'est écrit pour le mode de déclaration de la variable, comme l'instruction:i = 1, comme nous l'avons également mentionné précédemment, une telle variable déclarée et attribuée est exécutée sur chaque K-line BAR.

  • Le type Le langage Pine sur FMZ n'est pas strict sur les types, et il peut généralement être omis. Cependant, afin d'être compatible avec la stratégie de scripting sur Trading View, les variables peuvent également être déclarées avec des types. Par exemple:

    int i = 0 
    float f = 1.1
    

    Les exigences en matière de types sont assez strictes et une erreur sera signalée si le code suivant est utilisé dans la vue de négociation:

    baseLine0 = na          // compile time error!
    
  • Marqueur Les marqueurs sont des noms de variables. La dénomination des marqueurs a été mentionnée dans les chapitres précédents, vous pouvez donc la revoir ici:https://www.fmz.com/bbs-topic/9637#markers

En résumé, déclarer une variable peut s'écrire comme suit:

// [<declaration_mode>] [<type>] <marker> = value 
   declaration mode             type     marker      = value

L'opérateur d'attribution est utilisé ici:=attribue une valeur à une variable lorsqu'elle est déclarée. Lors de l'attribution, la valeur peut être une chaîne, un nombre, une expression, un appel de fonction,if, for, while, ouswitchet d'autres structures (ces mots clés structurels et l'utilisation de l'instruction seront expliqués en détail dans les cours suivants.

Ici, nous nous concentrons sur la fonction d'entrée, qui est une fonction que nous utiliserons fréquemment lors de la conception et de l'écriture de stratégies.

fonction d'entrée:

input function, parameters: defval、title、tooltip、inline、group

La fonction d'entrée sur FMZ est quelque peu différente de celle de Trading View, mais cette fonction est utilisée comme entrée d'affectation des paramètres de stratégie.

param1 = input(10, title="name of param1", tooltip="description for param1", group="group name A")
param2 = input("close", title="name of param2", tooltip="description for param2", group="group name A")
param3 = input(color.red, title="name of param3", tooltip="description for param3", group="group name B")
param4 = input(close, title="name of param4", tooltip="description for param4", group="group name B")
param5 = input(true, title="name of param5", tooltip="description for param5", group="group name C")

ma = ta.ema(param4, param1)
plot(ma, title=param2, color=param3, overlay=param5)

La fonction d'entrée est souvent utilisée pour attribuer des valeurs aux variables lors de leur déclaration. La fonction d'entrée sur FMZ dessine des commandes pour définir automatiquement les paramètres de stratégie dans l'interface de stratégie FMZ. Les commandes prises en charge sur FMZ comprennent actuellement des boîtes d'entrée numériques, des boîtes d'entrée de texte, des boîtes déroulantes et des cases à cocher booléennes.

img

Nous introduisons plusieurs paramètres principaux de la fonction d'entrée:

  • defval: la valeur par défaut des options de paramètres de stratégie définies par la fonction d'entrée, prenant en charge les variables intégrées, les valeurs numériques et les chaînes de caractères du langage Pine.
  • Titre: Nom du paramètre de la stratégie affiché sur l'interface de stratégie lors de la négociation en direct/des tests de retour.
  • Conseil d'outil: les informations relatives aux conseils d'outil pour les paramètres de stratégie, lorsque la souris est posée sur le paramètre de stratégie, les informations textuelles du paramètre sont affichées.
  • groupe: nom du groupe de paramètres de stratégie, qui peut être utilisé pour les paramètres de stratégie.

En plus de la déclaration et de l'attribution de variables individuelles, il existe également un moyen de déclarer un groupe de variables et de les attribuer dans le langage Pine:

[Variable A, Variable B, Variable C] = function or structure, such as ```if```, ```for```, ```while``` or ```switch```

Le plus commun est quand on utilise leta.macdla fonction de calcul de l'indicateur MACD, puisque l'indicateur MACD est un indicateur multi-lignes, trois ensembles de données sont calculés.

[dif,dea,column] = ta.macd(close, 12, 26, 9)

plot(dif, title="dif")
plot(dea, title="dea")
plot(column, title="column", style=plot.style_histogram)

Nous pouvons dessiner le graphique MACD en utilisant le code ci-dessus facilement. Non seulement les fonctions intégrées peuvent retourner à plusieurs variables, mais aussi les fonctions personnalisées écrites peuvent retourner à plusieurs données.

twoEMA(data, fastPeriod, slowPeriod) =>
    fast = ta.ema(data, fastPeriod)
    slow = ta.ema(data, slowPeriod)
    [fast, slow]

[ema10, ema20] = twoEMA(close, 10, 20)
plot(ema10, title="ema10", overlay=true)
plot(ema20, title="ema20", overlay=true)

La méthode d'écriture pour utiliser if et d'autres structures comme assignations de variables multiples est également similaire à la fonction personnalisée ci-dessus, et vous pouvez l'essayer si vous êtes intéressé.

[ema10, ema20] = if true
    fast = ta.ema(close, 10)
    slow = ta.ema(close, 20)
    [fast, slow]

plot(ema10, title="ema10", color=color.fuchsia, overlay=true)
plot(ema20, title="ema20", color=color.aqua, overlay=true)

Structure de la condition

Certaines fonctions ne peuvent pas être écrites dans le bloc de code local de la branche conditionnelle, notamment les fonctions suivantes:

couleur à barres (), remplissage (), ligne (), indicateur (), graphique (), bougie de graphique (), graphique graphique (), forme graphique ().

Trading View compilera avec des erreurs, FMZ n'est pas aussi restrictif, mais il est recommandé de suivre les spécifications de Trading View.

strategy("test", overlay=true)
if close > open 
    plot(close, title="close")
else 
    plot(open, title="open")

si déclaration

Exemple d'explication:

var lineColor = na

n = if bar_index > 10 and bar_index <= 20
    lineColor := color.green
else if bar_index > 20 and bar_index <= 30
    lineColor := color.blue
else if bar_index > 30 and bar_index <= 40
    lineColor := color.orange
else if bar_index > 40
    lineColor := color.black
else 
    lineColor := color.red
    
plot(close, title="close", color=n, linewidth=5, overlay=true)
plotchar(true, title="bar_index", char=str.tostring(bar_index), location=location.abovebar, color=color.red, overlay=true)

Point clé: Expressions utilisées pour les jugements qui renvoient des valeurs booléennes. Notez l'indentation. Il peut y avoir au maximum une autre branche. Si toutes les expressions de branche ne sont pas vraies et qu'il n'y a pas d'autre branche, retournez na.

x = if close > open
    close
plot(x, title="x")

déclaration de changement

L'instruction switch est également une instruction structurée par branche, qui est utilisée pour concevoir différents chemins à exécuter selon certaines conditions.

  1. L'instruction switch, comme l'instruction if, peut également renvoyer une valeur.
  2. Contrairement aux instructions de commutation dans d'autres langages, lorsqu'une construction de commutation est exécutée, seul un bloc local de son code est exécuté, de sorte que l'instruction break est inutile (c'est-à-dire qu'il n'est pas nécessaire d'écrire des mots clés comme break).
  3. Chaque branche du commutateur peut écrire un bloc de code local, la dernière ligne de ce bloc de code local est la valeur de retour (il peut s'agir d'une tuple de valeurs). renvoie na si aucun des blocs de code local ramifiés n'a été exécuté.
  4. L'expression dans la structure du commutateur détermine la position peut écrire une chaîne, une variable, une expression ou un appel de fonction.
  5. switch permet de spécifier une valeur de retour qui agit comme valeur par défaut à utiliser lorsqu'il n'y a pas d'autre cas dans la structure à exécuter.

Il existe deux formes de commutation, examinons les exemples un par un pour comprendre leur utilisation.

  1. Unswitchavec des expressions - exemple d'explication:
// input.string: defval, title, options, tooltip
func = input.string("EMA", title="indicator name", tooltip="select the name of the indicator function to be used", options=["EMA", "SMA", "RMA", "WMA"])

// input.int: defval, title, options, tooltip
// param1 = input.int(10, title="period parameter")
fastPeriod = input.int(10, title="fastPeriod parameter", options=[5, 10, 20])
slowPeriod = input.int(20, title="slowPeriod parameter", options=[20, 25, 30])

data = input(close, title="data", tooltip="select the closing price, opening price, highest price...")
fastColor = color.red
slowColor = color.red

[fast, slow] = switch func
    "EMA" =>
        fastLine = ta.ema(data, fastPeriod)
        slowLine = ta.ema(data, slowPeriod)
        fastColor := color.red
        slowColor := color.red
        [fastLine, slowLine]
    "SMA" =>
        fastLine = ta.sma(data, fastPeriod)
        slowLine = ta.sma(data, slowPeriod)
        fastColor := color.green
        slowColor := color.green
        [fastLine, slowLine]
    "RMA" =>
        fastLine = ta.rma(data, fastPeriod)
        slowLine = ta.rma(data, slowPeriod)
        fastColor := color.blue
        slowColor := color.blue
        [fastLine, slowLine]
    =>
        runtime.error("error")
        
plot(fast, title="fast" + fastPeriod, color=fastColor, overlay=true)
plot(slow, title="slow" + slowPeriod, color=slowColor, overlay=true)

Nous avons appris la fonction d'entrée avant, ici nous continuons à apprendre deux fonctions similaires à l'entrée:input.string, input.int functions. input.stringest utilisé pour retourner une chaîne, et leinput.intDans l'exemple, il y a une nouvelle utilisation duoptionsParamètre.optionsParamètre peut être passé un tableau de valeurs facultatives, telles queoptions=["EMA", "SMA", "RMA", "WMA"]etoptions=[5, 10, 20]dans l'exemple (notez que l'un est un type de chaîne, l'autre est un type numérique). De cette façon, les commandes de l'interface de stratégie n'ont pas besoin d'entrer des valeurs spécifiques, mais les commandes deviennent des cases déroulantes pour sélectionner ces options fournies dans le paramètre options.

La valeur de la variable func est une chaîne, et la variable func est utilisée comme expression pour le commutateur (qui peut être une variable, un appel de fonction ou une expression) pour déterminer quelle branche du commutateur est exécutée.runtime.error("error")la fonction sera exécutée, provoquant la stratégie de lancer une exception et arrêter.

Dans notre code de test ci-dessus, après la dernière ligne de runtime.error dans le bloc de code de branche par défaut de switch, nous n'avons pas ajouté de code comme [na, na] pour être compatible avec la valeur de retour. Ce problème doit être considéré dans Trading View. Si le type est incohérent, une erreur sera signalée. Mais sur FMZ, puisque le type n'est pas strictement requis, ce code de compatibilité peut être omis. Par conséquent, il n'est pas nécessaire de considérer la compatibilité de type de la valeur de retour de if et switch branches sur FMZ.

strategy("test", overlay=true)
x = if close > open
    close
else
    "open"
plotchar(true, title="x", char=str.tostring(x), location=location.abovebar, color=color.red)

Aucune erreur ne sera signalée sur FMZ, mais une erreur sera signalée sur la vue de trading.

  1. switchsans expressions

Ensuite, examinons une autre façon d'utiliserswitch, c'est à dire sans expressions.

up = close > open     // up = close < open 
down = close < open 
var upOfCount = 0 
var downOfCount = 0 

msgColor = switch
    up  => 
        upOfCount += 1 
        color.green 
    down => 
        downOfCount += 1
        color.red

plotchar(up, title="up", char=str.tostring(upOfCount), location=location.abovebar, color=msgColor, overlay=true)
plotchar(down, title="down", char=str.tostring(downOfCount), location=location.belowbar, color=msgColor, overlay=true)

Comme nous pouvons le voir dans l'exemple de code de test, switch correspondra à l'exécution du bloc de code local qui est vrai sur la condition de branche. En général, les conditions de branche suivant une instruction switch doivent être mutuellement exclusives. C'est-à-dire que le haut et le bas de l'exemple ne peuvent pas être vrais en même temps.up = close > open // up = close < openVous constaterez que la branche de commutation ne peut exécuter que la première branche. En outre, il est nécessaire de faire attention à ne pas écrire d'appels de fonction dans la branche de commutation autant que possible, la fonction ne peut pas être appelée sur chaque BAR peut causer des problèmes de calcul des données (sauf dans l'exemple de "switchavec les expressions", la branche d'exécution est déterministe et ne sera pas modifiée au cours de l'opération de stratégie).

Structure de la boucle

pour la déclaration

return value = for count = start count to final count by step length
    statement                                            // Note: There can be break and continue in the statement
    statement                                            // Note: The last statement is the return value

L'instruction for est très simple à utiliser, la boucle for peut finalement renvoyer une valeur (ou plusieurs valeurs, sous la forme de [a, b, c]). Comme la variable affectée à la position return value dans le pseudo-code ci-dessus. L'instruction for est suivie d'une variable count utilisée pour contrôler le nombre de boucles, se référer à d'autres valeurs, etc. La variable count se voit attribuer le compte initial avant le démarrage de la boucle, puis augmente en fonction du paramètre longueur d'étape, et la boucle s'arrête lorsque la variable count est supérieure au compte final.

Lebreakmot clé utilisé dans la boucle for: la boucle s'arrête lorsque lebreakLa déclaration est exécutée. Lecontinuemot clé utilisé dans la boucle for:continuel'instruction est exécutée, la boucle ignorera le code aprèscontinueL'instruction for renvoie la valeur de retour de la dernière exécution de la boucle. et elle renvoie null si aucun code n'est exécuté.

On le démontre avec un exemple simple:

ret = for i = 0 to 10       // We can increase the keyword by to modify the step length, FMZ does not support reverse loops such as i = 10 to 0 for now
    // We can add condition settings, use continue to skip, use break to jump out
    runtime.log("i:", i)
    i                       // If this line is not written, it will return null because there is no variable to return
    
runtime.log("ret:", ret)
runtime.error("stop")

pour... dans une déclaration

Lefor ... inLa déclaration a deux formes, nous allons les illustrer dans le pseudocode suivant.

return value = for array element in array 
    statement                        // Note: There can be break and continue in the statement
    statement                        // Note: The last statement is the return value
Return value = for [index variable, array element corresponding to index variable] in array
    statement                        // Note: There can be break and continue in the statement
    statement                        // Note: The last statement is the return value 

Nous pouvons voir que la principale différence entre les deux formes est le contenu qui suit le mot clé for, l'un est d'utiliser une variable comme une variable qui se réfère aux éléments du tableau, l'autre est d'utiliser une structure contenant des variables d'index, des tuples de variables d'éléments de tableau comme références.

testArray = array.from(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
for ele in testArray            // Modify it to the form of [i, ele]: for [i, ele] in testArray, runtime.log("ele:", ele, ", i:", i)
    runtime.log("ele:", ele)

runtime.error("stop")

Quand il doit utiliser l'index, utilisez la grammairefor [i, ele] in testArray.

Application des boucles pour

Nous pouvons utiliser les fonctions intégrées fournies dans le langage Pine pour compléter certains des calculs logiques de la boucle, soit écrits en utilisant directement la structure de la boucle, soit traités en utilisant les fonctions intégrées.

  1. Calculer la valeur moyenne

Lors de la conception avec une structure en boucle:

length = 5
var a = array.new(length)
array.push(a, close)

if array.size(a) >= length
	array.remove(a, 0)
	
sum = 0 	
for ele in a
    sum += ele 

avg = sum / length
plot(avg, title="avg", overlay=true)

L'exemple utilise une boucle for pour calculer la somme, puis calculer la valeur moyenne.

Calculer la moyenne mobile directement en utilisant la fonction intégrée:

plot(ta.sma(close, length), title="ta.sma", overlay=true)

Utiliser la fonction intégréeta.smaIl est évident qu'il est plus simple d'utiliser la fonction intégrée pour calculer la moyenne mobile. En comparant sur le graphique, on peut voir que les résultats calculés sont exactement les mêmes.

  1. Résumé

Nous utilisons toujours l'exemple ci-dessus pour illustrer.

Lors de la conception avec une structure en boucle:

length = 5
var a = array.new(length)
array.push(a, close)

if array.size(a) >= length
	array.remove(a, 0)
	
sum = 0 	
for ele in a
    sum += ele 

avg = sum / length
plot(avg, title="avg", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)

Pour calculer la somme de tous les éléments d'un tableau, nous pouvons utiliser une boucle pour le traiter, ou utiliser la fonction intégréearray.sumPour calculer. Calculer la somme directement en utilisant la fonction intégrée:

length = 5
var a = array.new(length)
array.push(a, close)

if array.size(a) >= length
	array.remove(a, 0)
	
plot(array.sum(a) / length, title="avg", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)

Nous pouvons voir les données calculées est exactement le même que celui affiché sur le graphique en utilisant le graphique.

Alors pourquoi concevoir des boucles quand on peut faire tout cela avec des fonctions intégrées?

  1. Pour certaines opérations et calculs pour les tableaux.
  2. Pour revoir l'historique, par exemple, pour savoir combien de points élevés passés sont plus élevés que le point élevé actuel de BAR.
  3. Lorsque les fonctions intégrées du langage Pine ne peuvent pas compléter les calculs pour les BAR passées.

pendant que statemnet

Lewhilel'instruction maintient le code dans la section de boucle en cours d'exécution jusqu'à ce que la condition de jugement dans la structure while soit false.

return value = while judgment condition
    statement                    // Note: There can be break and continue in the statement
    statement                    // Note: The last statement is the return value

D'autres règles de while sont similaires à celles de la boucle for. La dernière ligne du bloc de code local du corps de la boucle est la valeur de retour, qui peut renvoyer plusieurs valeurs. Exécuter la boucle lorsque la condition loop est vraie, et arrêter la boucle lorsque la condition est fausse. Les instructions break et continue peuvent également être utilisées dans le corps de la boucle.

Nous utiliserons encore l'exemple du calcul des moyennes mobiles pour démontrer:

length = 10

sma(data, length) => 
    i = 0 
    sum = 0 
    while i < 10 
        sum += data[i]
        i += 1
        sum / length

plot(sma(close, length), title="sma", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)

Nous pouvons voir que la boucle while est également très simple à utiliser, et il est également possible de concevoir une logique de calcul qui ne peut pas être remplacée par les fonctions intégrées, telles que le calcul factoriel:

counter = 5
fact = 1

ret = while counter > 0
    fact := fact * counter
    counter := counter - 1
    fact

plot(ret, title="ret")  // ret = 5 * 4 * 3 * 2 * 1

Les tableaux

La définition des tableaux dans le langage Pine est similaire à celle des autres langages de programmation. Les tableaux Pine sont des tableaux unidimensionnels. Ils sont généralement utilisés pour stocker une série continue de données. Les données stockées dans le tableau sont appelées l'élément du tableau, et les types de ces éléments peuvent être: entier, virgule flottante, chaîne, valeur de couleur, valeur booléenne. Le langage Pine sur FMZ n'est pas très strict en ce qui concerne les types, et il peut même stocker des chaînes et des nombres dans un tableau en même temps.[]pour faire référence à un élément dans le tableau, nous devons utiliser les fonctionsarray.get()etarray.set()L'ordre d'indexation des éléments du tableau est que l'index du premier élément du tableau est 0, et l'index de l'élément suivant est augmenté de 1.

Nous l'illustrons avec un code simple:

var a = array.from(0)
if bar_index == 0 
    runtime.log("current value a on BAR:", a, ", a on the last BAR, i.e. the value of a[1]:", a[1])
else if bar_index == 1 
    array.push(a, bar_index)
    runtime.log("current value a on BAR:", a, ", a on the last BAR, i.e. the value of a[1]:", a[1])
else if bar_index == 2
    array.push(a, bar_index)
    runtime.log("current value a on BAR:", a, ", a on the last BAR, i.e. the value of a[1]:", a[1], ", a on the last second BAR, i.e. the value of a[2]:", a[2])
else if bar_index == 3 
    array.push(a, bar_index)
    runtime.log("current value a on BAR:", a, ", a on the last BAR, i.e. the value of a[1]:", a[1], ", a on the last second BAR, i.e. the value of a[2]:", a[2], ", a on the last third BAR, i.e. the value of a[3]:", a[3])
else if bar_index == 4 
    // Obtain elements by index using array.get, modify elements by index using array.set
    runtime.log("Before array modification:", array.get(a, 0), array.get(a, 1), array.get(a, 2), array.get(a, 3))
    array.set(a, 1, 999)
    runtime.log("After array modification:", array.get(a, 0), array.get(a, 1), array.get(a, 2), array.get(a, 3))

Déclarer un tableau

Utilisationarray<int> a, float[] bpour déclarer un tableau ou simplement déclarer une variable qui peut être attribuée à un tableau, par exemple:

array<int> a = array.new(3, bar_index)
float[] b = array.new(3, close)
c = array.from("hello", "fmz", "!")
runtime.log("a:", a)
runtime.log("b:", b)
runtime.log("c:", c)
runtime.error("stop")

Les variables d'array sont initialisées en utilisant learray.newetarray.fromIl y a aussi beaucoup de fonctions liées à des types similaires àarray.newdans la langue du pin:array.new_int(), array.new_bool(), array.new_color(), array.new_string(), etc.

Le mot clé var fonctionne également avec le mode de déclaration de tableau. Les tableaux déclarés avec le mot clé var ne sont initialisés que sur le premier BAR.

var a = array.from(0)
b = array.from(0)

if bar_index == 1
    array.push(a, bar_index)
    array.push(b, bar_index)
else if bar_index == 2 
    array.push(a, bar_index)
    array.push(b, bar_index)
else if barstate.islast
    runtime.log("a:", a)
    runtime.log("b:", b)
    runtime.error("stop")

On peut voir que les changements de la matrice a ont été déterminés en continu et n'ont pas été réinitialisés.barstate.islastest vrai, il y a encore un seul élément imprimé avec une valeur de 0.

Lire et écrire des éléments dans un tableau

Utilisez array.get pour obtenir l'élément à la position d'index spécifiée dans le tableau, et utiliser array.set pour modifier l'élément à la position d'index spécifiée dans le tableau.

Le premier paramètre de array.get est le tableau à traiter, et le deuxième paramètre est l'index spécifié. Le premier paramètre de array.set est le tableau à traiter, le deuxième paramètre est l'index spécifié et le troisième paramètre est l'élément à écrire.

Nous utilisons l'exemple simple suivant pour illustrer:

lookbackInput = input.int(100)
FILL_COLOR = color.green

var fillColors = array.new(5)
if barstate.isfirst
    array.set(fillColors, 0, color.new(FILL_COLOR, 70))
    array.set(fillColors, 1, color.new(FILL_COLOR, 75))
    array.set(fillColors, 2, color.new(FILL_COLOR, 80))
    array.set(fillColors, 3, color.new(FILL_COLOR, 85))
    array.set(fillColors, 4, color.new(FILL_COLOR, 90))

lastHiBar = - ta.highestbars(high, lookbackInput)
fillNo = math.min(lastHiBar / (lookbackInput / 5), 4)

bgcolor(array.get(fillColors, int(fillNo)), overlay=true)
plot(lastHiBar, title="lastHiBar")
plot(fillNo, title="fillNo")

L'exemple initie la couleur de base verte, déclare et initie un tableau pour stocker les couleurs, puis assigne différentes valeurs de transparence aux couleurs (en utilisant lecolor.newLe niveau de couleur est calculé en calculant la distance de la barre actuelle de la valeur maximale de haut dans 100 périodes de rétrospective. Plus la distance est proche de la valeur maximale de haut dans les 100 derniers cycles de rétrospective, plus le rang est élevé et plus la valeur de couleur correspondante est sombre (moins transparente).

Iterer à travers les éléments du tableau

Comment itérer à travers un tableau, nous pouvons utiliser les instructions for/for in/while que nous avons apprises avant.

a = array.from(1, 2, 3, 4, 5, 6)

for i = 0 to (array.size(a) == 0 ? na : array.size(a) - 1)
    array.set(a, i, i)
    
runtime.log(a)
runtime.error("stop")
a = array.from(1, 2, 3, 4, 5, 6)

i = 0
while i < array.size(a)
    array.set(a, i, i)
    i += 1

runtime.log(a)
runtime.error("stop")
a = array.from(1, 2, 3, 4, 5, 6)

for [i, ele] in a 
    array.set(a, i, i)

runtime.log(a)
runtime.error("stop")

Ces trois méthodes de traversée ont les mêmes résultats d'exécution.

Les tableaux peuvent être déclarés dans la portée globale d'un script, ou dans la portée locale d'une fonction ou d'une branche.

Références de données historiques

Pour l'utilisation d'éléments dans les tableaux, les moyens suivants sont équivalents. Nous pouvons voir par l'exemple suivant que deux ensembles de lignes sont dessinés sur le diagramme, deux dans chaque ensemble, et les deux lignes dans chaque ensemble ont exactement la même valeur.

a = array.new_float(1)
array.set(a, 0, close)
closeA1 = array.get(a, 0)[1]
closeB1 = close[1]
plot(closeA1, "closeA1", color.red, 6)
plot(closeB1, "closeB1", color.black, 2)

ma1 = ta.sma(array.get(a, 0), 20)
ma2 = ta.sma(close, 20)
plot(ma1, "ma1", color.aqua, 6)
plot(ma2, "ma2", color.black, 2)

Fonctions pour ajouter et supprimer des tableaux

  1. Fonctions liées à l'addition de tableaux:

array.unshift(), array.insert(), array.push().

  1. Fonctions liées à l'opération de suppression des tableaux:

array.remove(), array.shift(), array.pop(), array.clear().

Nous utilisons l'exemple suivant pour tester ces fonctions d'addition et de suppression d'opérations de tableau.

a = array.from("A", "B", "C")
ret = array.unshift(a, "X")
runtime.log("array a:", a, ", ret:", ret)

ret := array.insert(a, 1, "Y")
runtime.log("array a:", a, ", ret:", ret)

ret := array.push(a, "D")
runtime.log("array a:", a, ", ret:", ret)

ret := array.remove(a, 2)
runtime.log("array a:", a, ", ret:", ret)

ret := array.shift(a)
runtime.log("array a:", a, ", ret:", ret)

ret := array.pop(a)
runtime.log("array a:", a, ", ret:", ret)

ret := array.clear(a)
runtime.log("array a:", a, ", ret:", ret)

runtime.error("stop")

Application des ajouts, suppressions: tableaux comme files d'attente

Nous pouvons construire une structure de données queue en utilisant des tableaux et certaines fonctions d'ajout et de suppression de tableaux. Les files d'attente peuvent être utilisées pour calculer la moyenne mobile des prix des ticks.

Une file d'attente est une structure souvent utilisée dans le domaine de la programmation.

L'élément qui entre dans la file d'attente en premier, quitte la file d'attente en premier.

De cette façon, il s'assure que les données de la file d'attente sont les dernières données et que la longueur de la file d'attente ne s'étendra pas indéfiniment.

Dans l'exemple suivant, nous utilisons une structure de file d'attente pour enregistrer le prix de chaque tick, calculer le prix moyen mobile au niveau du tick, puis le comparer à la moyenne mobile au niveau de la ligne K de 1 minute.

strategy("test", overlay=true)

varip a = array.new_float(0)
var length = 10

if not barstate.ishistory
    array.push(a, close)

    if array.size(a) > length
        array.shift(a)

sum = 0.0
for [index, ele] in a 
    sum += ele

avgPrice = array.size(a) == length ? sum / length : na

plot(avgPrice, title="avgPrice")
plot(ta.sma(close, length), title="ta.sma")

Notez que lorsque vous déclarez un tableau, nous spécifions le mode de déclaration et utilisons le mot clévariantDe cette façon, chaque variation de prix sera enregistrée dans le tableau a.

Fonctions couramment utilisées de calcul et d'opération de matrice

Calculer les fonctions de corrélation:

array.avg()Calcule la valeur moyenne de tous les éléments d'un tableau,array.min()calcule le plus petit élément d'un tableau,array.max()calcule le plus petit élément d'un tableau,array.stdev()Calcule l'écart type de tous les éléments d'un tableau,array.sum()Calcule l'écart type de tous les éléments d'un tableau.

Fonctions liées au fonctionnement:array.concat()pour fusionner ou concatener deux tableaux.array.copy()pour copier le tableau.array.joinpour concatenates tous les éléments d'un tableau dans une chaîne.array.sort()à trier par ordre croissant ou décroissant.array.reverse()pour inverser le tableau.array.slice()pour couper la matrice.array.includes()pour juger de l'élément.array.indexof()pour retourner à l'indice de la première occurrence de la valeur passée comme paramètre. Si la valeur n'est pas trouvée, -1 sera renvoyé.array.lastindexof()pour trouver la dernière occurrence de la valeur.

Exemples d'essais de fonctions liées au calcul d'un tableau:

a = array.from(3, 2, 1, 4, 5, 6, 7, 8, 9)

runtime.log("Arithmetic average of the array a:", array.avg(a))
runtime.log("The minimum element in the array a:", array.min(a))
runtime.log("The maximum element in the array a:", array.max(a))
runtime.log("Standard deviation in array a:", array.stdev(a))
runtime.log("Sum of all elements of the array a:", array.sum(a))
runtime.error("stop")

Ce sont des fonctions de calcul de tableau couramment utilisées.

Exemples de fonctions liées au fonctionnement:

a = array.from(1, 2, 3, 4, 5, 6)
b = array.from(11, 2, 13, 4, 15, 6)

runtime.log("array a: ", a, ", array b: ", b)
runtime.log("array a, array b is concatenated with:", array.concat(a, b))
c = array.copy(b)

runtime.log("Copy an array b and assign it to the variable c, variable c:", c)

runtime.log("use array.join to process the array c, add the symbol + to the middle of each element, concatenating all elements results in a string:", array.join(c, "+"))
runtime.log("Sort the array b, in order from smallest to largest, using the parameter order.ascending:", array.sort(b, order.ascending))     // array.sort function modifies the original array
runtime.log("Sort the array b, in order from largest to smallest, using the parameter order.descending:", array.sort(b, order.descending))   // array.sort function modifies the original array

runtime.log("array a:", a, ", array b:", b)
array.reverse(a)   // This function modifies the original array
runtime.log("reverse the order of all elements in the array a, after reversing, the array a is:", a)    

runtime.log("Intercept array a, index 0~index 3, and follow the rule of left-closed and right-open interval:", array.slice(a, 0, 3))
runtime.log("Search for element 11 in array b:", array.includes(b, 11))
runtime.log("Search for element 100 in array a:", array.includes(a, 100))
runtime.log("Connect array a and array b, and search the index position of the first occurrence of element 2:", array.indexof(array.concat(a, b), 2), " , observe array.concat(a, b):", array.concat(a, b))
runtime.log("Connect array a and array b, and search the index position of the last occurrence of element 6:", array.lastindexof(array.concat(a, b), 6), " , observe array.concat(a, b):", array.concat(a, b))

runtime.error("stop")

Les fonctions

Fonctions personnalisées

Le langage Pine peut être conçu avec des fonctions personnalisées.

  1. Toutes les fonctions sont définies dans le champ global du script. Une fonction ne peut pas être déclarée dans une autre fonction.
  2. Les fonctions ne sont pas autorisées à s'appeler dans leur propre code (recursion).
  3. En principe, toutes les fonctions de dessin intégrées au langage PINE (barcolor(), fill(), hline(), plot(), plotbar(), plotcandle()) ne peut pas être appelée dans les fonctions personnalisées.
  4. Les fonctions peuvent être écrites sous forme de ligne unique ou de lignes multiples. La valeur de retour de la dernière instruction est la valeur de retour de la fonction en cours, qui peut être retournée sous forme de tuple.

Nous avons également utilisé les fonctions personnalisées à plusieurs reprises dans nos tutoriels précédents, comme celles conçues comme une seule ligne:

barIsUp() => close > open

Si le BAR actuel est une droite positive lorsque la fonction renvoie.

Fonctions personnalisées conçues pour être multiples lignes:

sma(data, length) => 
    i = 0 
    sum = 0 
    while i < 10 
        sum += data[i]
        i += 1
        sum / length

plot(sma(close, length), title="sma", overlay=true)
plot(ta.sma(close, length), title="ta.sma", overlay=true)

Nous utilisons une fonction personnalisée pour réaliser une fonction de calcul moyen de SMA.

En outre, deux exemples de fonctions personnalisées que nous pouvons retourner:

twoEMA(data, fastPeriod, slowPeriod) =>
    fast = ta.ema(data, fastPeriod)
    slow = ta.ema(data, slowPeriod)
    [fast, slow]

[ema10, ema20] = twoEMA(close, 10, 20)
plot(ema10, title="ema10", overlay=true)
plot(ema20, title="ema20", overlay=true)

Une fonction peut calculer la ligne rapide, la ligne lente et deux moyennes EMA.

Fonctions intégrées

Les fonctions intégrées sont faciles à trouver dans leDocument de scénario FMZ PINE.

Classification des fonctions intégrées dans la langue Pine:

  1. Fonction de traitement de chaînestr. series.
  2. La fonction de traitement des valeurs de couleurcolor. series.
  3. Fonction d'entrée de paramètresinput. series.
  4. Fonction de calcul des indicateursta. series.
  5. Fonction de dessinplot. series.
  6. Fonction de traitement des tableauxarray. series.
  7. Les fonctions liées aux échangesstrategy. series.
  8. Fonctions liées aux opérations mathématiquesmath. series.
  9. Autres fonctions (traitement du temps, fonctions de dessin de séries non graphiques,request.fonctionnalités de série, fonctionnalités de traitement de type, etc.)

Fonctions de négociation

Lestrategy.Les séries de fonctions sont des fonctions que nous utilisons souvent dans la conception de stratégies, et ces fonctions sont étroitement liées à l'exécution des opérations de trading lorsque la stratégie est exécutée spécifiquement.


  1. strategy.entry

strategy.entryfonction est une fonction plus importante lorsque nous écrivons une stratégie pour passer une commande, plusieurs paramètres importants pour la fonction sont:id, direction, qty, when, etc.

Paramètres:

  • id: Ceci peut être compris comme donnant un nom à une position de négociation à des fins de référence.
  • direction: Si la direction de l'ordre est longue (acheter), passer dans la variable intégréestrategy.long, et si vous voulez vendre, passez la variablestrategy.short.
  • qty: spécifier le montant des ordres à passer, si ce paramètre n'est pas passé, le montant par défaut des ordres sera utilisé.
  • when: Condition d'exécution, vous pouvez spécifier ce paramètre pour contrôler si cette opération d'ordre en cours est déclenchée ou non.
  • limit: Indiquer le prix limite de commande.
  • stopLe prix stop-loss.

Les détails spécifiques de l'exécution dustrategy.entryLes paramètres sont réglés par les paramètres lorsque lestrategyfonction est appelée, et elle peut également être contrôlée par le [Pine Language Trade-Class Library Template Arguments](https://www.fmz.com/bbs-topic/9293#template-les arguments-of-pine-language-trade-class-library) régler le contrôle, les arguments du modèle de bibliothèque de la classe de commerce de la langue Pine contrôlent plus de détails de la transaction, vous pouvez consulter la documentation liée pour plus de détails.

Nous nous concentrons sur le lepyramiding, default_qty_valueparamètres dans lestrategyNous utilisons le code suivant pour le test:

/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/

strategy(title = "open long example", pyramiding = 3, default_qty_value=0.1, overlay=true)

ema10 = ta.ema(close, 10)

findOrderIdx(idx) =>
    if strategy.opentrades == 0 
        false 
    else 
        ret = false 
        for i = 0 to strategy.opentrades - 1 
            if strategy.opentrades.entry_id(i) == idx
                ret := true 
                break
        ret 
        

if not findOrderIdx("long1")
    strategy.entry("long1", strategy.long)

if not findOrderIdx("long2")
    strategy.entry("long2", strategy.long, 0.2, when = close > ema10)

if not findOrderIdx("long3")
    strategy.entry("long3", strategy.long, 0.2, limit = low[1])
    strategy.entry("long3", strategy.long, 0.3, limit = low[1])

if not findOrderIdx("long4")
    strategy.entry("long4", strategy.long, 0.2)

plot(ema10, title="ema10", color=color.red)

La partie au début du code/* backtest... */est un réglage de backtest, qui est utilisé pour enregistrer le temps de réglage de backtest et d'autres informations à ce moment-là pour le débogage, pas le code de démarrage.

Dans le code:strategy(title = "open long example", pyramiding = 3, default_qty_value=0.1, overlay=true), lorsque nous spécifions lepyramidingParamètre comme 3, nous définissons le nombre maximal de transactions dans le même sens à 3.strategy.entryLes opérations de commande dans l'exemple n'est pas exécuté.default_qty_valueparamètre à 0,1, cestrategy.entryopération avec IDlong1a une taille d'ordre par défaut de 0,1.strategy.entryl'appel de la fonction lorsque nous spécifions ledirectioncommestrategy.long, donc les ordres de test backtest sont tous des ordres d'achat.

Notez que l'opération de commandestrategy.entry("long3", ...dans le code est appelé deux fois, pour le même identifiant:long3, le premierstrategy.entryL'opération de commande n'a pas été remplie, et le deuxième appel à lastrategy.entryDans un autre cas, par exemple, si la première commande avec ID long3 est remplie, continuez à utiliser lestrategy.entryfonction de passer des ordres selon l'ID long3, alors les positions des ordres seront accumulées sur l'ID long3.


  1. strategy.close

Lestrategy.closefonction est utilisée pour fermer la position d'entrée avec l'identifiant d'identification spécifié. Les principaux paramètres sont:id, when, qty, qty_percent.

Paramètres:

  • id: L'ID d'entrée qui doit être fermé est l'ID que nous spécifions lorsque nous ouvrons une position en utilisant une fonction d'ordre d'entrée, telle questrategy.entry.
  • when: conditions d'exécution.
  • qty: Nombre de positions fermées.
  • qty_percent: Pourcentage de positions fermées.

Nous allons nous familiariser avec les détails de l'utilisation de cette fonction à travers un exemple: Le/*backtest ... */dans le code sont les informations de configuration pourFMZ.COMinternational site web backtest, vous pouvez le supprimer et définir le marché, la variété, la plage de temps et d'autres informations dont vous avez besoin pour tester.

/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/

strategy("close Demo", pyramiding=3)

var enableStop = false 
if enableStop
    runtime.error("stop")

strategy.entry("long1", strategy.long, 0.2)
if strategy.opentrades >= 3 
    strategy.close("long1")                   // Multiple entry orders, no qty parameters specified, close all
    // strategy.close()                          // Without specifying the id parameter, the current position will be closed
    // strategy.close("long2")                   // If a non-existent id is specified then nothing is done
    // strategy.close("long1", qty=0.15)         // Specify qty parameters to close a position
    // strategy.close("long1", qty_percent=50)   // qty_percent is set to 50 to close 50% of the positions marked by long1
    // strategy.close("long1", qty_percent=80, when=close<open)  // Specify the parameter when, change it to close>open and it won't trigger
    enableStop := true

La stratégie d'essai montre trois entrées longues consécutives avec l'ID d'entrée long1, puis utilise différents paramètres de lastrategy.closeIl est possible d'établir que les résultats obtenus lors de la clôture d'une position sont les suivants:strategy.closeCette fonction n'a pas de paramètres permettant de spécifier le prix de l'ordre de clôture de la position, elle est principalement utilisée pour clôturer la position immédiatement au prix de marché actuel.


  1. strategy.close_all

La fonctionstrategy.close_allest utilisé pour fermer toutes les positions actuelles, parce que les positions du langage de l'écriture Pine ne peut avoir qu'une direction, c'est-à-dire, s'il y a un signal déclenché dans la direction opposée de la position actuelle fermera la position actuelle, puis l'ouvrir selon le déclencheur de signal.strategy.close_allLe paramètre principal de l'opération est le nombre de positions dans la direction actuelle.strategy.close_allla fonction est:when.

Paramètres:

  • when: conditions d'exécution.

Utilisons un exemple pour observer:

/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/

strategy("closeAll Demo")

var enableStop = false 
if enableStop
    runtime.error("stop")

strategy.entry("long", strategy.long, 0.2, when=strategy.position_size==0 and close>open)
strategy.entry("short", strategy.short, 0.3, when=strategy.position_size>0 and close<open)

if strategy.position_size < 0 
    strategy.close_all()
    enableStop := true 

Le code d'essai commence par un numéro de position de 0 (c'est-à-direstrategy.position_size==0est vrai), donc lorsque les conditions définies par le paramètre when sont remplies, seul lestrategy.entryla fonction d'entrée avec ID long est exécutée.strategy.position_sizeest supérieur à 0, alors la fonction d'entrée avec ID short peut être exécutée, puisque la position longue actuelle est maintenue, ce signal de renversement de shorting à ce moment-là entraînera la fermeture de la position longue, puis l'ouverture de la position courte dans la direction opposée.strategy.position_size < 0, c'est-à-dire que lorsque l'on détient une position courte, toutes les positions dans la direction de détention actuelle seront fermées.enableStop := true. Arrête l'exécution de la stratégie afin que le journal puisse être observé.

On peut constater que la fonctionstrategy.close_alln'a pas de paramètres permettant de spécifier le prix de clôture de l'ordre, cette fonction est principalement utilisée pour clôturer immédiatement la position au prix de marché actuel.


  1. strategy.exit

Lestrategy.exitCette fonction est utilisée pour fermer une position d'entrée.strategy.closeetstrategy.close_allLes fonctions de clôture d'une position immédiatement au prix de marché actuel.strategy.exitLa fonction va fermer la position selon les paramètres.

Paramètres:

  • id: L'identifiant de commande de l'ordre en cours à condition de clôture.
  • from_entry: Utilisé pour spécifier l'identifiant d'entrée de la position à fermer.
  • qty: Nombre de positions fermées.
  • qty_percent: Pourcentage de positions fermées, fourchette: 0 ~ 100.
  • profit: Objectif de bénéfice, exprimé en points.
  • loss: cible de stop-loss, exprimée en points.
  • limit: Objectif de profit, déterminé par prix.
  • stop: cible de stop-loss, spécifiée par prix.
  • when: conditions d'exécution.

Utiliser une stratégie de test pour comprendre l'utilisation des paramètres.

/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
args: [["RunMode",1,358374],["ZPrecision",0,358374]]
*/

strategy("strategy.exit Demo", pyramiding=3)

varip isExit = false 

findOrderIdx(idx) =>
    ret = -1 
    if strategy.opentrades == 0 
        ret
    else 
        for i = 0 to strategy.opentrades - 1 
            if strategy.opentrades.entry_id(i) == idx
                ret := i 
                break
        ret

strategy.entry("long1", strategy.long, 0.1, limit=1, when=findOrderIdx("long1") < 0)
strategy.entry("long2", strategy.long, 0.2, when=findOrderIdx("long2") < 0)
strategy.entry("long3", strategy.long, 0.3, when=findOrderIdx("long3") < 0)

if not isExit and strategy.opentrades > 0
    // strategy.exit("exitAll")          // If only one id parameter is specified, the exit order is invalid, and the parameters profit, limit, loss, stop and other exit conditions also need to be set at least one, otherwise it is also invalid
    strategy.exit("exit1", "long1", profit=50)                    // Since the long1 entry order is not filled, the exit order with ID exit1 is also on hold until the corresponding entry order is filled before exit1 is placed
    strategy.exit("exit2", "long2", qty=0.1, profit=100)          // Specify the parameter qty to close 0.1 positions in the position with ID long2
    strategy.exit("exit3", "long3", qty_percent=50, limit=strategy.opentrades.entry_price(findOrderIdx("long3")) + 1000)   // Specify the parameter qty_percent to close 50% of the positions in the position with ID long3
    isExit := true 

if bar_index == 0 
    runtime.log("The price per point:", syminfo.mintick)    // The price per point is related to the "Pricing Currency Precision" parameter setting on the Pine language template parameters

Nous utilisons le modèle de prix en temps réel pour le backtest, la stratégie de test commence par 3 opérations d'entrée (strategy.entryfonction), etlong1est intentionnellement réglée aveclimitparamètre avec un prix de commande en attente de 1, de sorte qu'il ne peut pas être rempli.strategy.exitNous avons utilisé take profit par point et take profit par prix, la clôture d'un nombre fixe de positions et les positions clôturées par pourcentage.strategy.exitLa fonction a également des paramètres d'arrêt de traction plus complexes:trail_price, trail_points, trail_offsetpeuvent également être testés dans cet exemple pour apprendre leur utilisation.


  1. strategy.cancel

Lestrategy.cancelLes fonctions sont utilisées pour annuler/arrêter toutes les commandes en attente.strategy.order, strategy.entry , strategy.exitLes principaux paramètres de cette fonction sont les suivants:id, when.

Paramètres:

  • id: L'identifiant d'entrée à annuler.
  • when: conditions d'exécution.

Cette fonction est facile à comprendre, et elle est utilisée pour annuler les commandes d'entrée qui ne sont pas remplies.

/*backtest
start: 2022-07-03 00:00:00
end: 2022-07-09 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/

strategy("strategy.cancel Demo", pyramiding=3)

var isStop = false 
if isStop 
    runtime.error("stop")

strategy.entry("long1", strategy.long, 0.1, limit=1)
strategy.entry("long2", strategy.long, 0.2, limit=2)
strategy.entry("long3", strategy.long, 0

Plus de