[TOC]
Unterstützendes Video-Tutorial:https://www.youtube.com/watch?v=CA3SwJQb_1g
FMZ Quant Trading Platform unterstützt das Schreiben, Backtesting und Live-Handel von Pine-Strategien in der Pine-Sprache und ist kompatibel mit niedrigeren Versionen der Pine-Sprache.Strategieplatzauf der FMZ Quant Trading Plattform (FMZ.COM).
FMZ unterstützt nicht nur die Pine-Sprache, sondern auch die leistungsstarke Zeichnungsfunktion der Pine-Sprache. Die verschiedenen Funktionen, reiche und praktische Werkzeuge, effiziente und bequeme Verwaltung auf der FMZ-Plattform verbessern die Praktikabilität der Pine-Strategie (Skript). Basierend auf der Kompatibilität mit der Pine-Sprache erweitert, optimiert und trimmt FMZ auch die Pine-Sprache bis zu einem gewissen Grad. Bevor wir offiziell in das Tutorial eintreten, werfen wir einen Blick darauf, welche Änderungen an der Pine-Sprache auf FMZ im Vergleich zur Originalversion vorgenommen wurden.
Ein kurzer Überblick über einige der offensichtlichen Unterschiede:
//@version
und derstrategy
, indicator
Anweisungen am Anfang des Codes sind nicht zwingend zu schreiben, FMZ unterstützt nichtimport
zu importierenlibrary
Funktionieren für jetzt.Man kann sehen, dass einige Strategien so geschrieben sind:
//@version=5
indicator("My Script", overlay = true)
src = close
a = ta.sma(src, 5)
b = ta.sma(src, 50)
c = ta.cross(a, b)
plot(a, color = color.blue)
plot(b, color = color.black)
plotshape(c, color = color.red)
Oder schreiben Sie es so:
//@version=5
strategy("My Strategy", overlay=true)
longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
if (longCondition)
strategy.entry("My Long Entry Id", strategy.long)
shortCondition = ta.crossunder(ta.sma(close, 14), ta.sma(close, 28))
if (shortCondition)
strategy.entry("My Short Entry Id", strategy.short)
Auf FMZ kann es vereinfacht werden:
src = close
a = ta.sma(src, 5)
b = ta.sma(src, 50)
c = ta.cross(a, b)
plot(a, color = color.blue, overlay=true)
plot(b, color = color.black, overlay=true)
plotshape(c, color = color.red, overlay=true)
Oder:
longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
if (longCondition)
strategy.entry("My Long Entry Id", strategy.long)
shortCondition = ta.crossunder(ta.sma(close, 14), ta.sma(close, 28))
if (shortCondition)
strategy.entry("My Short Entry Id", strategy.short)
Schlusskursmodell und Echtzeitpreismodell
In der Handelsansicht können wir diecalc_on_every_tick
Parameter derstrategy
Funktion, um das Strategie-Skript zu setzen, um die Strategie-Logik in Echtzeit auszuführen, wenn sich der Preis jedes Mal ändert.calc_on_every_tick
Parameter sollte auftrue
. Diecalc_on_every_tick
Standardparameter istfalse
, d. h. die Strategie-Logik wird erst ausgeführt, wenn die aktuelle K-Line BAR der Strategie vollständig abgeschlossen ist.
Auf FMZ wird es durch die Parameter der
Numerische Präzisionskontrolle, wie Preis und Auftragsbetrag bei der Ausführung der Strategie, muss auf FMZ angegeben werden. In der Handelsansicht gibt es beim Platzieren von realen Handelsordern kein Genauigkeitsproblem, da es nur in der Simulation getestet werden kann. Auf FMZ ist es möglich, die Pine-Strategie im realen Handel auszuführen. Dann muss die Strategie in der Lage sein, die Preisgenauigkeit und die Auftragsbetragsgenauigkeit der Handelsvariante flexibel anzugeben. Die Genauigkeitseinstellungen steuern die Anzahl der Dezimalstellen in den relevanten Daten, um zu verhindern, dass die Daten die Auftragsvoraussetzungen der Börse nicht erfüllen und somit keine Bestellung aufgeben.
Futures-Kontraktcode
Wenn das Handelsprodukt auf FMZ ein Vertrag ist, hat es zwei Attribute, sie sind swap
, und der Vertragscode hängt davon ab, ob die betriebliche Börse einen solchen Vertrag hat.quarter
Diese Vertragscodes entsprechen den Futures-Kontraktcodes, die in FMZ
Für andere Einstellungen, wie z. B. Mindestbestellbetrag, Standardbestellbetrag usw., lesen Sie bitte die Parametereinführung auf
runtime.debug
, runtime.log
, runtime.error
für das Debugging verwendet.3 Funktionen wurden zur Debugging-Plattform des FMZ hinzugefügt.
runtime.debug
: Drucken Sie Variableninformationen auf der Konsole, die in der Regel nicht mit dieser Funktion verwendet wird.
runtime.log
: Ausgabe im Protokoll. PINE-Sprachenspezifische Funktionen auf FMZ.
runtime.log(1, 2, 3, close, high, ...), Multiple parameters can be passed.
runtime.error
: Es wird bei Aufruf zu einem Laufzeitfehler mit der im Nachrichtenparameter angegebenen Fehlermeldung führen.
runtime.error(message)
overlay
Parameter wird in einigen der Zeichnungsfunktionen erweitertIn der Pine-Sprache auf FMZ sind die Zeichnungsfunktionenplot
, plotshape
, plotchar
, etc. haben dieoverlay
Parameterunterstützung, die es ermöglicht, die Zeichnung auf dem Haupt- oder Teildiagramm anzugeben.overlay
ist auftrue
auf der Hauptkarte zu zeichnen undfalse
ist so eingestellt, dass sie auf dem Unterdiagramm beruht, wodurch die Pine-Strategie auf FMZ das Hauptdiagramm und das Unterdiagramm gleichzeitig erstellen kann.
syminfo.mintick
eingebaute VariableDie eingebaute Variable vonsyminfo.mintick
ist definiert als der Mindest-Tick-Wert für das aktuelle Symbol. Dieser Wert kann durch die Vorlageparameter-Präzisionswährung in der syminfo.mintick
ist 0,01.
Beispielsweise beträgt der Auftragspreis 8000, die Verkaufsrichtung, die Menge beträgt 1 Los (Stück, Blatt), der durchschnittliche Preis nach der Transaktion beträgt nicht 8000, sondern unter 8000 (die Kosten beinhalten die Bearbeitungsgebühr).
Wenn Sie mit dem Lernen der Grundlagen der Pine-Sprache beginnen, können einige Beispiele für Anweisungen und Code-Grammatik vorliegen, mit denen Sie nicht vertraut sind. Es spielt keine Rolle, ob Sie sie nicht verstehen, wir können uns zuerst mit den Konzepten vertraut machen und den Zweck des Tests verstehen, oder Sie können die Pine-Sprachdokumentation auf FMZ nach Anweisungen überprüfen. Folgen Sie dann dem Schritt für Schritt-Tutorial, um sich mit verschiedenen Grammatik, Anweisungen, Funktionen und integrierten Variablen vertraut zu machen.
Wenn man anfängt, die Pine-Sprache zu lernen, ist es sehr notwendig, die damit verbundenen Konzepte wie den Ausführungsvorgang des Pine-Sprach-Skriptprogramms zu verstehen. Die Pine-Sprachstrategie läuft auf der Grundlage des Charts. Man kann verstehen, dass die Pine-Sprachstrategie eine Reihe von Berechnungen und Operationen ist, die in der Reihenfolge der Zeitreihen auf dem Chart aus den frühesten Daten, die auf dem Chart geladen wurden, ausgeführt werden. Die Datenmenge, die das Chart zunächst lädt, ist begrenzt. Im realen Handel wird die maximale Datenmenge normalerweise auf der Grundlage des maximalen Datenvolumens bestimmt, das von der Austauschoberfläche zurückgegeben wird, und die maximale Datenmenge während des Backtests wird auf der Grundlage der Datenquelle des Backtesting-Systems bestimmt. Die linkste K-Zeilenecke auf dem Chart, das heißt die erste Datensammlung des Chartes, hat einen Indexwert von 0. Der Wert der K-Zeilenecke, in der dasbar_index
in der Pine-Sprache.
plot(bar_index, "bar_index")
Dieplot
Die Verwendung ist sehr einfach, es ist eine Linie auf dem Diagramm nach den Eingabeparametern zu zeichnen, die Eingabedaten istbar_index
, und die Linie heißt:bar_index
. Es kann gesehen werden, dass der Wert der Zeile mit dem Namen bar_index auf der ersten Bar 0 beträgt und mit zunehmender Bar um 1 nach rechts steigt.
Da die Einstellungen der Strategie unterschiedlich sind, unterscheiden sich die Modelldurchführungsmethoden der Strategie.closing price model
undreal-time price model
Wir haben auch kurz die Konzepte von ihnen vorgestellt.
Modell für den Schlusspreis
Wenn der Strategiecode ausgeführt wird, wird die Periode der aktuellen K-Line Bar vollständig ausgeführt, und wenn die K-Line geschlossen wird, ist die K-Line Periode abgeschlossen.
Echtzeitpreismodell
Wenn der Strategiecode ausgeführt wird, unabhängig davon, ob die aktuelle K-Line Bar geschlossen ist oder nicht, wird die Pine-Strategie-Logik jedes Mal ausgeführt, wenn sich der Markt ändert, und das ausgelöste Handelssignal wird sofort ausgeführt.
Wenn die Pine-Sprachstrategie von links nach rechts auf dem Diagramm ausgeführt wird, werden die K-Linienbalken auf dem Diagramm inHistorical Bars
undReal-time Bars
:
Historische Kneipe
Wenn die Strategie auf Historical Bars
. Die Strategie Logik wird nur einmal auf jedem ausgeführthistorical bar
- Ich weiß nicht.
Wenn die Strategie auf historical bars
. Die Strategie Logik wird nur einmal auf jedem ausgeführthistorical bar
.
Berechnung auf der Grundlage historischer Bars: Der Strategie-Code wird einmal im Schließzustand der historischen Bar ausgeführt, und dann wird der Strategie-Code in der nächsten historischen Bar ausgeführt, bis alle historischen Bars einmal ausgeführt werden.
Bar in Echtzeit
Wenn die Strategie bis zur letzten K-Linienbalke ganz rechts ausgeführt wird, ist die Balke eine Echtzeitbalke. Nachdem die Echtzeitbalke geschlossen ist, wird die Balke zu einer überschrittenen Echtzeitbalke (wird zu einer historischen Balke). Eine neue Echtzeitbalke wird ganz rechts im Chart generiert.
Wenn die Strategie auf
Berechnung auf Basis von Echtzeit-Bar:
Wenn die Strategie auf high
, low
, close
Diese Daten werden in Echtzeit berechnet und werden in Echtzeit berechnet. Auf einer Echtzeit-Bar können sich diese Werte jedes Mal ändern, wenn sich der Markt in Echtzeit-Bars ändert.close
stets den aktuellen letzten Preis darstellt undhigh
undlow
Diese eingebauten Variablen stellen den Endwert des Echtzeitbalkens dar, als er zuletzt aktualisiert wurde.
Rollback-Mechanismus bei der Ausführung von Strategien in Echtzeit Bar (Echtzeit-Preismodell): Während der Echtzeit-Bar-Ausführung wird das Zurücksetzen benutzerdefinierter Variablen vor jeder neuen Iteration der Strategie als Rollback bezeichnet.
Aufmerksamkeit:
/*backtest
...
..
.
*/
Der Inhalt des Pakets ist die in Form eines Codes auf der FMZ-Plattform gespeicherte Backtest-Konfigurationsinformation.
/*backtest
start: 2022-06-03 09:00:00
end: 2022-06-08 15:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
*/
var n = 0
if not barstate.ishistory
runtime.log("before n + 1, n:", n, " current bar_index:", bar_index)
n := n + 1
runtime.log("after n + 1, n:", n, " current bar_index:", bar_index)
plot(n, title="n")
Wir untersuchen nur die Szene während Echtzeit Bars ausgeführt, so dass wir dienot barstate.ishistory
Ausdruck, um die Akkumulation der Variablen n nur in der Echtzeit-Bar zu begrenzen, undruntime.log
Funktion zur Ausgabe der Informationen im Strategieprotokoll vor und nach dem Akkumulationsvorgang.plot
, kann man sehen, dass n immer 0 ist, wenn die Strategie in historischen Bars ausgeführt wird. Wenn die Echtzeit-Bar ausgeführt wird, wird die Operation der Hinzufügung von 1 zu n ausgelöst, und die Operation der Hinzufügung von 1 zu n wird ausgeführt, wenn die Strategie in jeder Runde der Echtzeit-Bar ausgeführt wird. Aus der Log-Nachricht kann beobachtet werden, dass n auf den Wert zurückgesetzt wird, der von der vorherigen Bar-Ausführungsstrategie endgültig eingereicht wurde, wenn der Strategie-Code in jeder Runde erneut ausgeführt wird. Die n-Wert-Aktualisierung wird eingereicht, wenn der Strategie-Code zum letzten Mal auf der Echtzeit-Bar ausgeführt wird, so dass Sie sehen können, dass der Wert der Kurve n mit jeder Erhöhung von Bar um 1 steigt, die von der Echtzeit-Bar auf dem Diagramm beginnt.
Zusammenfassung:
Aufgrund des Daten-Rollbacks können Zeichnungsvorgänge wie Kurven auf dem Diagramm auch zu einer Neuaufzeichnung führen.
var n = 0
if not barstate.ishistory
runtime.log("before n + 1, n:", n, " current bar_index:", bar_index)
n := open > close ? n + 1 : n
runtime.log("after n + 1, n:", n, " current bar_index:", bar_index)
plot(n, title="n")
Bildschirmaufnahme der Zeit A
Bildschirmaufnahme der Zeit B
Wir haben nur den Satz geändert:n := open > close ? n + 1 : n
, nur 1 zu n hinzufügen, wenn die aktuelle Echtzeit-Bar eine negative Linie ist (d. h. der Eröffnungspreis höher ist als der Schlusskurs). Es kann gesehen werden, dass im ersten Diagramm (Zeit A), da der Eröffnungspreis zu diesem Zeitpunkt höher als der Schlusskurs (negative Linie) war, n um 1 angesammelt wurde und der Wert von n auf der Chartkurve 5 war. Dann änderte sich der Markt und der Preis wurde wie im zweiten Diagramm (Zeit B) angezeigt aktualisiert. Zu diesem Zeitpunkt ist der Eröffnungspreis niedriger als der Schlusskurs (positive Linie) und der n-Wert rollt zurück, ohne um 1 zu steigen. Die Kurve von n im Diagramm wird ebenfalls sofort neu gezeichnet, und der Wert von n auf der Kurve ist 4. Daher sind die Signale, wie Crossup und Crossdown, die in Echtzeit-Bars angezeigt werden, unsicher und können sich ändern.
Variabler Kontext in Funktionen
Lassen Sie uns die Variablen in der Pine-Sprachfunktion zusammen studieren. Gemäß einigen Beschreibungen in Pine-Tutorials haben die Variablen in der Funktion folgende Unterschiede zu den Variablen außerhalb der Funktion:
Die Historie der in der Funktion Pine verwendeten Serienvariablen wird bei jedem aufeinanderfolgenden Aufruf der Funktion erstellt. Wenn die Funktion nicht auf jeder Stange aufgerufen wird, auf der das Skript ausgeführt wird, führt dies zu einer Diskrepanz zwischen den historischen Werten der Serie innerhalb und außerhalb des lokalen Blocks der Funktion. Wenn daher die Funktion nicht auf jeder Stange aufgerufen wird, beziehen sich die innerhalb und außerhalb der Funktion referenzierten Reihen mit dem gleichen Indexwert nicht auf den gleichen historischen Punkt.
Wir werden es mit einem Testcode herausfinden, der auf FMZ läuft:
/*backtest
start: 2022-06-03 09:00:00
end: 2022-06-08 15:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
*/
f(a) => a[1]
f2() => close[1]
oneBarInTwo = bar_index % 2 == 0
plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")
plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")
plot(close[2], title = "close[2]", color = color.red, overlay = true)
plot(close[1], title = "close[1]", color = color.green, overlay = true)
Bildschirmfoto des Backtest-Führens
Der Testcode ist relativ einfach und dient hauptsächlich der Untersuchung der Daten, auf die mit zwei Methoden verwiesen wird, nämlich:f(a) => a[1]
undf2() => close[1]
.
f(a) => a[1]
: Mit der Methode der Übergabe von Parametern kehrt die Funktion zua[1]
finally.
f2() => close[1]
: Verwenden Sie die eingebaute Variableclose
direkt und die Funktion kehrt zuclose[1]
finally.
Die[]
Das Symbol wird verwendet, um auf den historischen Wert der Datenreihe zu verweisen, und close[1] bezieht sich auf die Schlusskursdaten auf der Bar vor dem aktuellen Schlusskurs.
plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")
Zeichnen Sie ein Zeichen f(close)
.
plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")
Zeichnen Sie ein Zeichen f2()
.
plot(close[2], title = "close[2]", color = color.red, overlay = true)
Zeichnen Sie eine Linie, die Farbe ist rot, und die gezeichnete Position (auf der Y-Achse) ist:close[2]
, der der Schlusskurs des zweiten Balkens vor dem aktuellen Balken ist (bei Zählung von 2 Balken links).
plot(close[1], title = "close[1]", color = color.green, overlay = true)
Zeichnen Sie eine Linie, die Farbe ist grün, und die gezeichnete Position (auf der Y-Achse) ist:close[1]
, der der Schlusskurs des ersten Balkens vor dem aktuellen Balken ist (mit 1 Balken nach links gezählt).
Aus dem Screenshot des Strategie-Backtesting ist zu sehen, dass zwar sowohl die Funktionf(a) => a[1]
verwendet, um den A-Marker und die Funktion zu zeichnenf2() => close[1]
Die Markerposition von plot(close[2], title = "close[2]", color = color.red, overlay = true)
, die Daten zur Zeichnung der Linie sindclose[2]
.
Der Grund ist zu berechnen, ob die bar_index
Die f(a) => a[1]
wird nicht gleich dem von der Funktion angegebenen Wert seinf2() => close[1]
wenn die Funktion nicht auf jedem Bar aufgerufen wird (auch wenn beide den gleichen Index verwenden wie [1]).
Einige eingebaute Funktionen müssen auf jedem Bar berechnet werden, um ihre Ergebnisse richtig zu berechnen
Um diese Situation mit einem einfachen Beispiel zu veranschaulichen:
res = close > close[1] ? ta.barssince(close < close[1]) : -1
plot(res, style = plot.style_histogram, color=res >= 0 ? color.red : color.blue)
Wir schreiben den Funktionscode.ta.barssince(close < close[1])
in einem ternären Operatorcondition ? value1 : value2
. Dadurch wird die Ta.barssince-Funktion nur dann aufgerufen, wennclose > close[1]
Aber dieta.barssince
Funktion ist es, die Anzahl der K-Linien seit dem letzten Mal zu berechnenclose < close[1]
Wenn die Funktion ta.barssince aufgerufen wird, ist sie immer close > close[1], das heißt, der aktuelle Schlusskurs ist größer als der Schlusskurs des vorherigen Bar. Wenn die Funktion ta.barssince aufgerufen wird, wird die Bedingung close < close [1] nicht festgelegt und es gibt keine aktuelle Position, an der sie stattfindet.
ta.barssince: Bei Aufruf gibt die Funktion na zurück, wenn die Bedingung noch nie vor der aktuellen K-Zeile erfüllt wurde.
Wie in der Tabelle gezeigt:
Wenn also das Diagramm gezeichnet wird, werden nur die Daten mit einem Wert für die Res-Variable (-1) gezeichnet.
Um dieses Problem zu vermeiden, nehmen wir einfach dieta.barssince(close < close[1])
Funktion aus dem ternären Operator aufrufen und schreiben Sie es außerhalb aller möglichen bedingten Zweige, so dass es Berechnungen auf jeder K-Line Bar durchführen.
a = ta.barssince(close < close[1])
res = close > close[1] ? a : -1
plot(res, style = plot.style_histogram, color=res >= 0 ? color.red : color.blue)
Das Konzept der Zeitreihe ist in der Pine-Sprache sehr wichtig, und es ist ein Konzept, das wir verstehen müssen, wenn wir die Pine-Sprache lernen. Zeitreihe ist kein Typ, sondern eine grundlegende Struktur für die Speicherung kontinuierlicher Werte von Variablen im Laufe der Zeit. Wir wissen, dass Pine-Skripte auf Diagrammen basieren, und der grundlegendste Inhalt, der im Diagramm angezeigt wird, ist das K-Liniendiagramm. Zeitreihe, bei der jeder Wert mit einem Zeitstempel einer K-Linienbalke verknüpft ist.open
ist eine eingebaute Variable (eingebaute) der Pine-Sprache, und ihre Struktur besteht darin, die Zeitreihe des Eröffnungspreises jedes K-Line Bar zu speichern.open
repräsentiert die Eröffnungspreise aller K-Line Bars von der ersten Bar am Anfang des aktuellen K-Line-Charts bis zur Bar, wo das aktuelle Skript ausgeführt wird.open
Wenn Sie sich auf historische Werte in einer Zeitreihe beziehen möchten, müssen Sie die[]
Wenn die Pine-Strategie auf einem bestimmten K-Line-Bar ausgeführt wird, verwendenopen[1]
der Eröffnungspreis des vorherigen K-Linienbalkens (d. h. der Eröffnungspreis des vorherigen K-Linienzeitraums), der sich auf dieopen
Zeitreihe, auf der dieses K-Linienzeichen gerade vom Skript ausgeführt wird.
Variablen auf Zeitreihen sind sehr praktisch für die Berechnung
Nehmen wir die eingebaute Funktion.ta.cum
Zum Beispiel:
ta.cum
Cumulative (total) sum of `source`. In other words it's a sum of all elements of `source`.
ta.cum(source) → series float
RETURNS
Total sum series.
ARGUMENTS
source (series int/float)
SEE ALSO
math.sum
Prüfcode:
v1 = 1
v2 = ta.cum(v1)
plot(v1, title="v1")
plot(v2, title="v2")
plot(bar_index+1, title="bar_index")
Es gibt viele eingebaute Funktionen wieta.cum
Das ist ein System, das Daten über Zeitreihen direkt verarbeiten kann.ta.cum
ist die Akkumulation der Werte, die den Variablen entsprechen, die auf jeder K-Linienbalke übergeben werden, und als nächstes verwenden wir ein Diagramm, um es einfacher zu verstehen.
Strategiebetriebsprozess | Eingebettete Variable bar_index | V1 | V2 |
---|---|---|---|
Die Strategie läuft auf der ersten K-Line Bar | 0 | 1 | 1 |
Die Strategie läuft auf der zweiten K-Line Bar | 1 | 1 | 2 |
Die Strategie läuft auf der dritten K-Line Bar | 2 | 1 | 3 |
… | … | … | … |
Die Strategie läuft auf der N + 1 K-Linienleiste | N | 1 | N+1 |
Es kann gesehen werden, dass v1, v2 und sogar bar_index alle Zeitreihenstrukturen sind, und es gibt entsprechende Daten für jeden Balken. Ob der Testcode das
Weil die Variable v1 ist 1 auf jedem Bar, wenn dieta.cum(v1)
Wenn die Funktion auf der ersten K-Linie Bar ausgeführt wird, gibt es nur die erste Bar, so dass das Berechnungsresultat 1 ist und der Variablen v2 zugeordnet wird.
Wann?ta.cum(v1)
Wenn die Berechnung auf der zweiten K-Linie Bar ausgeführt wird, gibt es bereits 2 K-Linie Bars (die eingebaute Variable bar_index, die der ersten entspricht, ist 0, und die zweite, die der eingebauten Variable bar_index entspricht, ist 1), so dass das Berechnungsresultat 2 ist, das der Variable v2 zugeordnet wird, und so weiter.bar_index
wird von 0 erhöht, dannbar_index + 1
Wir können auch sehen, dass die Linienv2
undbar_index
Sie überschneiden sich tatsächlich.
Auch ich kann dieta.cum
Ich habe eine eingebaute Funktion, um die Summe der Schlusskurs für alle Bars auf dem aktuellen Diagramm zu berechnen.ta.cum(close)
, Wenn die Strategie auf die Echtzeit-Bar in der äußersten rechten Seite läuft, wird das Ergebnis berechnet durchta.cum(close)
ist die Summe der Schlusskurse aller Balken auf dem Diagramm (wenn sie nicht nach rechts läuft, wird sie nur bis zum aktuellen Balken angesammelt).
Variablen der Zeitreihen können auch mit Hilfe von Operatoren berechnet werden, z. B. mit dem Code:ta.sma(high - low, 14)
, subtrahieren Sie die eingebaute Variablehigh
(der höchste Preis der K-Line Bar) vonlow
(der niedrigste Preis von K-Line Bar), und schließlich dieta.sma
Funktion zur Berechnung des Durchschnittswerts.
Das Ergebnis eines Funktionsanrufs hinterlässt auch Spuren von Werten in der Zeitreihe.
v1 = ta.highest(high, 10)[1]
v2 = ta.highest(high[1], 10)
plot(v1, title="v1", overlay=true)
plot(v2, title="v2", overlay=true)
Der Testcode wird während des Backtests ausgeführt, und es kann beobachtet werden, daß die Werte vonv1
undv2
Das Ergebnis, das durch den Funktionsanruf berechnet wird, hinterlässt Spuren des Wertes in der Zeitreihe, wie z.B. dieta.highest(high, 10)
in dem Codeta.highest(high, 10)[1]
Das durch den Funktionsruf berechnete Ergebnis kann auch [1] verwendet werden, um sich auf seinen historischen Wert zu beziehen.ta.highest(high, 10)
entspricht dem vorherigen Balken des aktuellen Balkens, ist das Berechnungsergebnista.highest(high[1], 10)
Also...ta.highest(high[1], 10)
undta.highest(high, 10)[1]
sind exakt gleichwertig.
Verwenden Sie eine andere Zeichnungsfunktion zur Ausgabe der Informationsüberprüfung:
a = ta.highest(close, 10)[1]
b = ta.highest(close[1], 10)
plotchar(true, title="a", char=str.tostring(a), location=location.abovebar, color=color.red, overlay=true)
plotchar(true, title="b", char=str.tostring(b), location=location.belowbar, color=color.green, overlay=true)
Wir können sehen, dass die Werte der Variablen a und b in der Zeitreihe über und unter den entsprechenden Bars angezeigt werden. Wir können diesen Zeichnungscode während des Lernprozesses behalten, da wir oft Informationen auf dem Diagramm für Beobachtungen während des Backtests und des Experimentierens ausgeben müssen.
Im Anfangsteil des Tutorials haben wir einige Unterschiede bei der Verwendung der Pine-Sprache auf FMZ und Trading View zusammengefasst.indicator()
, strategy()
, undlibrary()
Natürlich, um mit früheren Versionen von Pine-Skripten kompatibel zu sein, müssen Strategien wie://@version=5
, indicator()
, strategy()
Einige Strategie-Einstellungen können auch eingestellt werden, indem Parameter in diestrategy()
function.
<version>
<declaration_statement>
<code>
Die<version>
Versionskontrollinformationen können weggelassen werden.
Die Kiefersprache verwendet//
als ein einzeliges Kommentarzeichen, da die Pine-Sprache kein mehrzeliges Kommentarzeichen hat. FMZ erweitert das Kommentarzeichen/**/
für mehrzeilige Kommentare.
Zeilen im Skript, die keine Kommentare oder Compilerrichtlinien sind, sind Anweisungen, die den Algorithmus des Skript implementieren.
if
, for
, while
oderswitch
StrukturDie Auskünfte können auf verschiedene Weise angeordnet werden
space
oder ```tab` (Tab-Taste). Ihr erster Zeichen muss auch der erste Zeichen der Zeile sein. Zeilen, die an der ersten Position beginnen, werden per Definition Teil des globalen Bereichs des Skriptes.local block
Ein lokaler Block muss durch einen Tab oder vier Leerzeichen eingelegt werden (andernfalls wird er als der verknüpfte Code der vorherigen Zeile analysiert, d.h. als der kontinuierliche Inhalt der vorherigen Zeile beurteilt), und jeder lokale Block definiert einen anderen lokalen Bereich.Zum Beispiel enthält es drei lokale Blöcke, einen in der kundenspezifischen Funktionsdeklaration und zwei in der Variablendeklaration mit der if-Struktur, wie folgt:
indicator("", "", true) // declaration statement (global scope), can be omitted
barIsUp() => // function declaration (global scope)
close > open // local block (local scope)
plotColor = if barIsUp() // variable declaration (global scope)
color.green // local block (local scope)
else
color.red // local block (local scope)
runtime.log("color", color = plotColor) // Call a built-in function to output the log (global scope)
Lange Linien können über mehrere Linien aufgeteilt oder
a = open + high + low + close
Es kann so verpackt werden (beachten Sie, dass die Anzahl der durchgeschlagenen Leerzeichen pro Zeile kein Vielfaches von 4 sein kann):
a = open +
high +
low +
close
Ein langer Plot ((() Anruf kann wie folgt verpackt werden:
close1 = request.security(syminfo.tickerid, "D", close) // syminfo.tickerid daily level closing price data series for the current trading pair
close2 = request.security(syminfo.tickerid, "240", close) // syminfo.tickerid 240-minute level closing price data series for the current trading pair
plot(ta.correlation(close, open, 100), // line-long plot() calls can be wrapped
color = color.new(color.purple, 40),
style = plot.style_area,
trackprice = true)
Behauptungen in benutzerdefinierten Funktionsdeklarationen können ebenfalls eingewürft werden. Da jedoch ein lokaler Block mit einer Einzugslösung in der Grammatik (4 Leerzeichen oder 1 Tab) beginnen muss, muss die Fortsetzung einer Behauptung bei der Aufteilung auf die nächste Zeile mit mehr als einer Einzugslösung beginnen (nicht gleich 4 Vielfachen von Leerzeichen). Zum Beispiel:
test(c, o) =>
ret = c > o ?
(c > o+5000 ?
1 :
0):
(c < o-5000 ?
-1 :
0)
a = test(close, open)
plot(a, title="a")
Bevor wir Variablen erkennen, müssen wir zuerst das Konzept von
(A-Z)
oder kleinbuchstaben(a-z)
Brief oder Unterstriche(_)
als erstes Zeichen des Marker.Wie beispielsweise folgende benannte Marker:
fmzVar
_fmzVar
fmz666Var
funcName
MAX_LEN
max_len
maxLen
3barsDown // Wrong naming! It used a numeric character as the leading character of the marker
Wie die meisten Programmiersprachen hat auch die Pine-Sprache Schreibvorschläge.
// name variables, constants
GREEN_COLOR = #4CAF50
MAX_LOOKBACK = 100
int fastLength = 7
// name functions
zeroOne(boolValue) => boolValue ? 1 : 0
Operatoren sind einige Operationssymbole, die in Programmiersprachen verwendet werden, um Ausdrücke zu konstruieren, und Ausdrücke sind Rechenregeln, die für bestimmte Rechenzwecke entwickelt wurden, wenn wir Strategien schreiben.
Zuweisungsoperatoren, arithmetische Operatoren, Vergleichsoperatoren, logische Operatoren,? :
Drittlandbetreiber,[]
historische Referenzoperatoren.
Die Arithmetikoperatoren nehmen*
als Beispiel unterscheidet es sich von dem Typproblem, das durch das Rückgabeergebnis des Pine-Sprachoperators in der Trading View verursacht wird. Der folgende Testcode wird bereitgestellt:
//@version=5
indicator("")
lenInput = input.int(14, "Length")
factor = year > 2020 ? 3 : 1
adjustedLength = lenInput * factor
ma = ta.ema(close, adjustedLength) // Compilation error!
plot(ma)
Bei der Ausführung dieses Skripts in der Trading View tritt ein Kompilierungsfehler auf.adjustedLength = lenInput * factor
Das Ergebnis ist:series int
Typ (Serie), aber der zweite Parameter der Funktionta.ema
Aber es gibt keine solchen strengen Einschränkungen für FMZ, der obige Code kann normal ausgeführt werden.
Lassen Sie uns die Verwendung verschiedener Operatoren zusammen betrachten.
Es gibt zwei Arten von Zuweisungsoperatoren:=
, :=
, die wir in mehreren Beispielen am Anfang des Tutorials gesehen haben.
Die=
Variablen, die mit einer Variablen initialisiert, deklariert und zugewiesen werden=
Dies sind gültige Variablerdeklarationen:
a = close // Use built-in variables to assign values to a
b = 10000 // Use numerical assignment
c = "test" // Use string assignment
d = color.green // Use color value assignment
plot(a, title="a")
plot(b, title="b")
plotchar(true, title="c", char=str.tostring(c), color=d, overlay=true)
Beachten Sie, dass die Zuweisunga = close
, ist die Variable a für jeden Bar der aktuelle Schlusskurs (Schlusskurs) des Bar. Sonstige Variablenb
, c
, d
sind unverändert und können im Backtestsystem auf FMZ getestet werden, wobei die Ergebnisse auf der Tabelle zu sehen sind.
:=
Es kann einfach verstanden werden, dass die:=
Der Operator wird verwendet, um die Werte von Variablen zu ändern, die deklariert und initialisiert wurden.
Wenn wir die:=
Wenn der Operator einen Wert einer nicht initialisierten oder deklarierten Variablen zuweist, verursacht er einen Fehler, zum Beispiel:
a := 0
Daher ist die:=
der Zuweisungsoperator wird im Allgemeinen verwendet, um vorhandene Variablen neu zuzuweisen, z. B.:
a = close > open
b = 0
if a
b := b + 1
plot(b)
Beurteilen, obclose > open
Der Code im lokalen Block der if-Anweisung ist der Code, der in der Anweisung verwendet wird.b := b + 1
wird ausgeführt, und der Zuordnung Operator:=
Wir verwenden dann die Plot-Funktion, um den Wert der Variablen b auf jede BAR der Zeitreihen auf dem Diagramm zu ziehen und sie in eine Linie zu verbinden.
Wir denken, dass wenn eine positive Linie BAR erscheint, b sich weiterhin um 1 ansammeln wird? Natürlich nicht, hier erklären und initialisieren wir die Variable b als 0 ohne Schlüsselwortbezeichnung.b=0
Wir können also sehen, dass das Ergebnis dieses Codes darin besteht, die Variable b jedes Mal auf 0 zurückzusetzen, wenn die Variable a wahr ist, d.h. im Einklang mitclose > open
, dann wird b um 1 erhöht, wenn der Code in dieser Runde ausgeführt wird, und b ist 1 wenn die Plot-Funktion zieht, aber b wird bei der Ausführung des Codes in der nächsten Runde auf 0 neu zugewiesen.
Wenn es um Zuweisungsoperatoren geht, müssen wir zwei Schlüsselwörter erweitern:var
, varip
Ver-
In der Tat haben wir dieses Schlüsselwort in früheren Tutorials gesehen und verwendet, aber wir haben es damals nicht ausführlich diskutiert.
var ist ein Schlüsselwort, das zur Zuordnung und einmaligen Initialisierung von Variablen verwendet wird. Im Allgemeinen führt die Variablenzuweisungsgrammatik, die das Schlüsselwort var nicht enthält, dazu, dass der Variablenwert jedes Mal überschrieben wird, wenn die Daten aktualisiert werden. Im Gegensatz dazu können Variablen, die mit dem Schlüsselwort var zugewiesen werden, trotz Datenaktualisierungen
zustand behalten.
Wir verwenden immer noch dieses Beispiel, aber wir verwenden dievar
Schlüsselwort bei der Zuweisung eines Wertes an b hier.
a = close > open
var b = 0
if a
b := b + 1
plot(b)
Dievar
Das Schlüsselwort erlaubt der Variablen b, nur die anfängliche Zuordnung auszuführen, und dann wird sie b nicht jedes Mal, wenn die Strategie-Logik ausgeführt wird, auf 0 zurücksetzen, so dass aus der Linie, die zur Laufzeit gezogen wurde, beobachtet werden kann, dass b die Anzahl der positiven Linien-BARs ist, die erschienen sind, wenn die aktuelle K-Linie-BAR zurück getestet wurde.
Variablen, die von var deklariert werden, können nicht nur im globalen Bereich geschrieben werden, sondern auch in Codeblöcken, wie in diesem Beispiel:
strategy(overlay=true)
var a = close
var b = 0.0
var c = 0.0
var green_bars_count = 0
if close > open
var x = close
b := x
green_bars_count := green_bars_count + 1
if green_bars_count >= 10
var y = close
c := y
plot(a, title = "a")
plot(b, title = "b")
plot(c, title = "c")
Die Variable
Variation
Wir sehen das Schlüsselwortvarip
Zum ersten Mal können wir uns die Beschreibung dieses Schlüsselworts ansehen:
varip (var intrabar persist) ist ein Schlüsselwort für die Zuordnung und einmalige Initialisierung von Variablen.
Ist es schwierig zu verstehen? Es spielt keine Rolle, wir erklären es durch ein Beispiel, es ist leicht zu verstehen.
strategy(overlay=true)
// test var varip
var i = 0
varip ii = 0
// Print the i and ii changed in each round of the strategy logic on the chart
plotchar(true, title="ii", char=str.tostring(ii), location=location.abovebar, color=color.red)
plotchar(true, title="i", char=str.tostring(i), location=location.belowbar, color=color.green)
// Increment i and ii by 1 for each round of logic execution
i := i + 1
ii := ii + 1
Dieser Testcode hat unterschiedliche Leistungen auf
Strichmodell:
Erinnern Sie sich, dass die Strategie-Ausführung, die wir früher erklärt in historische BAR-Stufe und Echtzeit-BAR-Stufe unterteilt ist?i
, ii
in dervar
, varip
Es kann daher gesehen werden, dass die auf der K-Linie BAR des Backtest-Ergebnisses angezeigten Zahlen um 1 nach einem erhöht werden. Wenn die historische K-Linie-Stufe endet, beginnt die Echtzeit-K-Linie-Stufe. Die von var und varip deklarierten Variablen beginnen unterschiedliche Veränderungen zu erleiden. Da es sich um ein Barmodell handelt, wird der Strategie-Code für jede Preisänderung in einer K-Linie BAR einmal ausgeführt.i := i + 1
undii := ii + 1
Der Unterschied besteht darin, dass ii jedes Mal geändert wird. Obwohl i jedes Mal geändert wird, wird der vorherige Wert wiederhergestellt, wenn die Strategie-Logik in der nächsten Runde ausgeführt wird (erinnern Sie sich an den Rollback-Mechanismus, den wir im vorherigen Kapitel
Tick-Modell: Da das Tick-Modell die Strategie-Logik nur einmal pro K-Line BAR ausführt, verhalten sich die durch var und varip deklarierten Variablen im Schlusskursmodell im obigen Beispiel genau gleich, wobei sie für jede K-Line BAR während der historischen K-Line-Phase und der Echtzeit-K-Line-Phase um 1 zunehmen.
Betreiber | Beschreibung |
---|---|
+ | Ergänzung |
- | Subtraktion |
* | Multiplikation |
/ | Abteilung |
% | Modul |
Die+
und-
Andere arithmetische Operatoren können nur als binäre Operatoren verwendet werden und es wird einen Fehler melden, wenn es als unare Operatoren verwendet wurde.
+
, das Ergebnis der Berechnung ist eine Zeichenfolge, der Wert wird in die Zeichenfolgeform umgewandelt, und dann werden die Zeichenfolge zusammengefügt. Wenn es sich um einen anderen arithmetischen Operator handelt, wird versucht, die Zeichenfolge in einen Wert umzuwandeln und dann die Operation fortzuführen.a = 1 + 1
b = 1 + 1.1
c = 1 + "1.1"
d = "1" + "1.1"
e = 1 + na
runtime.log("a:", a, ", b:", b, ", c:", c, ", d:", d, ", e:", e)
// a: 2 , b: 2.1 , c: 11.1 , d: 11.1 , e: NaN
Die Pine-Sprache auf FMZ unterscheidet sich etwas von der Pine-Sprache auf Trading View, die Pine-Sprache auf FMZ ist nicht sehr streng in Bezug auf Variabletypen.
a = 1 * "1.1"
b = "1" / "1.1"
c = 5 % "A"
plot(a)
plot(b)
plot(c)
Es funktioniert auf FMZ, meldet jedoch einen Typfehler in der Trading-Ansicht. Wenn beide Operanden des arithmetischen Operators Zeichenfolgen sind, wandelt das System die Zeichenfolgen in numerische Werte um und berechnet sie dann. Wenn eine nicht-numerische Zeichenfolge nicht berechnet werden kann, ist das Ergebnis der Systemoperation ein Nullwert
Die Vergleichsoperatoren sind alle binäre Operatoren.
Betreiber | Beschreibung |
---|---|
< | < |
> | > |
<= | <= |
>= | >= |
== | == |
!= | != |
Prüfbeispiel:
a = 1 > 2
b = 1 < 2
c = "1" <= 2
d = "1" >= 2
e = 1 == 1
f = 2 != 1
g = open > close
h = na > 1
i = 1 > na
runtime.log("a:", a, ", b:", b, ", c:", c, ", d:", d, ", e:", e, ", f:", f, ", g:", g, ", h:", h, ", i:", i)
// a: false , b: true , c: true , d: false , e: true , f: true , g: false , h: false , i: false
Wie wir sehen können, ist der Vergleichsoperator sehr einfach zu bedienen, aber dies ist auch der Operator, den wir am häufigsten verwenden, wenn wir Strategien schreiben.close
, open
, usw.
Wie bei dem Betreiber gibt es einen Unterschied bezüglich der Pine-Sprache zwischen FMZ und Trading View.d = "1" >= 2
wird keinen Fehler auf FMZ melden, und es wird ausgeführt, indem die Zeichenfolge zuerst in einen Wert umgewandelt und dann die Operation verglichen wird.
Betreiber | Codezeichen | Beschreibung |
---|---|---|
- Nein. | - Nein. | Unarer Operator, nicht Operationen |
und | und | Binäre Operatoren und Operationen |
oder | oder | Binäre Operatoren oder Operationen |
Wenn es um logische Operatoren geht, dann müssen wir über echte Werte Tabellen sprechen. dasselbe wie wir in der Highschool gelernt haben, hier testen und lernen wir einfach in unserem Backtesting-System:
a = 1 == 1 // An expression formed by using comparison operators, the result is a Boolean value
b = 1 != 1
c = not b // Logical not operators
d = not a // Logical not operators
runtime.log("test the logical operator:and", "#FF0000")
runtime.log("a:", a, ", c:", c, ", a and c:", a and c)
runtime.log("a:", a, ", b:", b, ", a and b:", a and b)
runtime.log("b:", b, ", c:", c, ", b and c:", b and c)
runtime.log("d:", d, ", b:", b, ", d and b:", d and b)
runtime.log("test the logical operator:or", "#FF0000")
runtime.log("a:", a, ", c:", c, ", a or c:", a or c)
runtime.log("a:", a, ", b:", b, ", a or b:", a or b)
runtime.log("b:", b, ", c:", c, ", b or c:", b or c)
runtime.log("d:", d, ", b:", b, ", d or b:", d or b)
runtime.error("stop")
Um keine Nachrichten zu überdrucken, wir werfen einen Fehler mit derruntime.error("stop")
Danach können wir die Ausgabeinformationen beobachten, und wir können feststellen, dass der gedruckte Inhalt tatsächlich mit der wahren Werttabelle identisch ist.
Ternäre Ausdrücke mit dem ternären Operator? :
kombiniert mit Operandencondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
Wir haben sie auch in den vorherigen Lektionen benutzt. Der sogenannte ternäre Ausdruck, ternärer Operator bedeutet, dass es drei Operanden gibt.
In dercondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
, condition
ist die Urteilsbedingung. Wenn sie wahr ist, ist der Wert des Ausdrucks:valueWhenConditionIsTrue
Wenn.condition
ist falsch, dann ist der Wert des AusdrucksvalueWhenConditionIsFalse
.
Beispiel für eine praktische Demonstration:
a = close > open
b = a ? "positive line" : "negative line"
c = not a ? "negative line" : "positive line"
plotchar(a, location=location.abovebar, color=color.red, char=b, overlay=true)
plotchar(not a, location=location.belowbar, color=color.green, char=c, overlay=true)
Was tun, wenn wir auf einen Doji stoßen? Es spielt keine Rolle! Ternäre Ausdrücke können auch verschachtelt werden, wie wir es in dem vorherigen Tutorial getan haben.
a = close > open
b = a ? math.abs(close-open) > 30 ? "positive line" : "doji" : math.abs(close-open) > 30 ? "negative line" : "doji"
c = not a ? math.abs(close-open) > 30 ? "negative line" : "doji" : math.abs(close-open) > 30 ? "positive line" : "doji"
plotchar(a, location=location.abovebar, color=color.red, char=b, overlay=true)
plotchar(not a, location=location.belowbar, color=color.green, char=c, overlay=true)
In Wirklichkeit entspricht es dem Ersatz vonvalueWhenConditionIsTrue
undvalueWhenConditionIsFalse
incondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
mit einem anderen dreifachen Ausdruck.
Verwenden Sie den historischen Operator[]
Diese historischen Werte sind die Werte der Variablen auf der K-Linienleiste vor der aktuellen K-Linienleiste, als das Skript ausgeführt wurde.[]
Der Operator wird nach Variablen, Ausdrücken und Funktionsanrufen verwendet.[]
Wenn ich zum Beispiel den Schlusskurs der letzten K-Line BAR zitieren möchte, schreiben wir es wie folgt:close[1]
.
Wir haben in den vorherigen Lektionen so etwas gesehen:
high[10]
ta.sma(close, 10)[1]
ta.highest(high, 10)[20]
close > nz(close[1], open)
Die[]
Der Operator kann nur einmal für denselben Wert verwendet werden, also ist es falsch, ihn so zu schreiben, und ein Fehler wird gemeldet:
a = close[1][2] // error
Hier könnte jemand sagen, dass der Betreiber[]
ist für die Seriestruktur verwendet, scheint es, dass die Seriestruktur (Serie) ist ähnlich wie Array!
Lassen Sie uns ein Beispiel verwenden, um den Unterschied zwischen Reihen und Arrays in der Pine-Sprache zu veranschaulichen.
strategy("test", overlay=true)
a = close
b = close[1]
c = b[1]
plot(a, title="a")
plot(b, title="b")
plot(c, title="c")
a = close[1][2]
wird einen Fehler melden, aber:
b = close[1]
c = b[1]
Aber wenn es getrennt geschrieben wird, wird es keinen Fehler melden.b = close [1]
, b sollte ein Wert sein, aberc = b[1]
, b kann immer noch verwendet werden, um den historischen Wert erneut mit dem History-Operator zu beziehen. Es kann gesehen werden, dass das Konzept der Reihe in der Pine-Sprache nicht so einfach ist wie ein Array. Es kann als der historische Wert auf der letzten Bar von close (zu b zugewiesen) verstanden werden, b ist auch eine Zeitreihenstruktur (Zeitreihen) und seine h