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
, nz
Il est également possible de juger dans le code (en fait, nousnz
Dans 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).
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 | + 、- etnot dans 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.
Nous avons déjà étudié le concept de
Mode de déclaration: La première chose à écrire lors de la déclaration d'une variable est le mode de déclaration.
var
.varip
.Levar
etvarip
Les mots clés ont été étudiés dans notre chapitre précédent surAssignment Operators
Si 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
, ouswitch
et 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.
Nous introduisons plusieurs paramètres principaux de la fonction d'entrée:
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.macd
la 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)
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")
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")
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.
Il existe deux formes de commutation, examinons les exemples un par un pour comprendre leur utilisation.
switch
avec 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.string
est utilisé pour retourner une chaîne, et leinput.int
Dans l'exemple, il y a une nouvelle utilisation duoptions
Paramètre.options
Paramè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.
switch
sans expressionsEnsuite, 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 < open
Vous 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 "switch
avec 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).
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
Lebreak
mot clé utilisé dans la boucle for: la boucle s'arrête lorsque lebreak
La déclaration est exécutée.
Lecontinue
mot clé utilisé dans la boucle for:continue
l'instruction est exécutée, la boucle ignorera le code aprèscontinue
L'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")
Lefor ... in
La 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.
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.sma
Il 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.
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.sum
Pour 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?
Lewhile
l'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
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
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))
Utilisationarray<int> a
, float[] b
pour 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.new
etarray.from
Il 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.islast
est vrai, il y a encore un seul élément imprimé avec une valeur de 0.
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).
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.
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)
array.unshift()
, array.insert()
, array.push()
.
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
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évariant
De cette façon, chaque variation de prix sera enregistrée dans le tableau a.
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.join
pour 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")
Le langage Pine peut être conçu avec des fonctions personnalisées.
barcolor(), fill(), hline(), plot(), plotbar(), plotcandle()
) ne peut pas être appelée dans les fonctions personnalisées.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.
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:
str.
series.color.
series.input.
series.ta.
series.plot.
series.array.
series.strategy.
series.math.
series.request.
fonctionnalités de série, fonctionnalités de traitement de type, etc.)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.
strategy.entry
strategy.entry
fonction 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.stop
Le prix stop-loss.Les détails spécifiques de l'exécution dustrategy.entry
Les paramètres sont réglés par les paramètres lorsque lestrategy
fonction est appelée, et elle peut également être contrôlée par le [
Nous nous concentrons sur le lepyramiding
, default_qty_value
paramètres dans lestrategy
Nous 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 lepyramiding
Paramètre comme 3, nous définissons le nombre maximal de transactions dans le même sens à 3.strategy.entry
Les opérations de commande dans l'exemple n'est pas exécuté.default_qty_value
paramètre à 0,1, cestrategy.entry
opération avec IDlong1
a une taille d'ordre par défaut de 0,1.strategy.entry
l'appel de la fonction lorsque nous spécifions ledirection
commestrategy.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.entry
L'opération de commande n'a pas été remplie, et le deuxième appel à lastrategy.entry
Dans un autre cas, par exemple, si la première commande avec ID strategy.entry
fonction de passer des ordres selon l'ID
strategy.close
Lestrategy.close
fonction 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 strategy.close
Il est possible d'établir que les résultats obtenus lors de la clôture d'une position sont les suivants:strategy.close
Cette 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.
strategy.close_all
La fonctionstrategy.close_all
est 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_all
Le paramètre principal de l'opération est le nombre de positions dans la direction actuelle.strategy.close_all
la 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==0
est vrai), donc lorsque les conditions définies par le paramètre when sont remplies, seul lestrategy.entry
la fonction d'entrée avec ID strategy.position_size
est supérieur à 0, alors la fonction d'entrée avec ID 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_all
n'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.
strategy.exit
Lestrategy.exit
Cette fonction est utilisée pour fermer une position d'entrée.strategy.close
etstrategy.close_all
Les fonctions de clôture d'une position immédiatement au prix de marché actuel.strategy.exit
La 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.entry
fonction), etlong1
est intentionnellement réglée aveclimit
paramètre avec un prix de commande en attente de 1, de sorte qu'il ne peut pas être rempli.strategy.exit
Nous avons utilisé strategy.exit
La fonction a également des paramètres d'arrêt de traction plus complexes:trail_price
, trail_points
, trail_offset
peuvent également être testés dans cet exemple pour apprendre leur utilisation.
strategy.cancel
Lestrategy.cancel
Les fonctions sont utilisées pour annuler/arrêter toutes les commandes en attente.strategy.order
, strategy.entry
, strategy.exit
Les 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