![Simulink: ein Leitfaden für Expert Advisor-Entwickler](https://c.mql5.com/i/articles/overlay.png)
Simulink: ein Leitfaden für Expert Advisor-Entwickler
Einleitung
Verschiedene Beiträge beschreiben bereits die unendlich vielen Möglichkeiten von Matlab. Genauer gesagt: sie beschreiben die Art und Weise, wie diese Software die Tools eines Programmierers, die er zur Entwicklung eines Expert Advisors benutzt, erweitern kann. In diesem Beitrag versuche ich die Arbeit eines solchen leitungsstarken Matlab-Pakets zu veranschaulichen, nämlich Simulink.
Ich möchte damit eine Alternative in der Entwicklung automatisierter Handelssysteme für Händler anbieten. Warum ich mich dieser Art von Arbeitsmethode zugewandt habe, lag an der Komplexität des Problems, dem sich Händelr gegenübersehen - die Erzeugung, Verfiizierung und das Testen eines automatisierten Handelssystems. Ich bin kein professioneller Programmierer, deshalb ist das Prinzip "vom Einfachen zum Komplexen" bei der Arbeit an Entwicklungen von Handelssystemen für mich von äußerster Wichtigkeit. Was genau heißt "einfach" für mich? Zunächst heißt das die Anschaulichkeit des Erzeugungsprozesses eines Systems und die Logik seiner Funktionsweise. Und es heißt auch möglichst wenig handgeschriebener Code. Diese Erwartungen stimmen mit den Fähigkeiten des Simulink® Pakets, einem MATLAB-Produkt, ziemlich gut überein. MATLAB ist der weltweite Führer unter Instrumenten zur Veranschaulichung mathematischer Berechnungen.
In diesem Beitrag versuche ich ein Handelssystem auf Grundlage des Matlab-Pakets zu erzeugen und zu testen und anschließend einen Expert Advisor für MetaTrader 5 zu schreiben. Darüber hinaus werden für das Backtesting alle historischen Daten von MetaTrader 5 eingesetzt.
Um Verwirrungen zu vermeiden, bezeichne ich das Handelssystem, das in Simulinik funktioniert, mit dem breit gefassten Kürzel MTS, und das, welches in MQL5 funktioniert, einfach mit Expert Advisor.
1. Grundlagen von Simulink und Stateflow
Bevor wir zu spezifischen Taten schreiten, müssen wir uns zunächst mit einem Mindestmaß an Theorie vertraut machen.Mit Hilfe des Simulink®-Pakets, einem Teil von MATLAB, kann der Anwender Modelle von dynamischen Systemen erstellen, diese simulieren und analysieren. Zudem ist es möglich, nach der Art des Systems zu fragen, es zu simulieren und dann zu beobachten, was passiert.
Mit Simulink kann der Anwender von Null ein Modell erstellen oder ein bereits bestehendes verändern. Das Paket unterstützt die Entwicklung linearer und nicht-linearer Systeme, die auf Basis von separatem, kontinuierlichem und hybridem Verhalten erzeugt sind.
Die hauptsächlichen Eigenschaften des Pakets werden auf der Website des Enticklers vorgestellt:
- Umfassende und erweiterbare Libraries vor-definierter Blöcke;
- Interaktiver graphischer Editor zum Zusammenfügen und Verwalten intuitiver Blockdiagramme;
- Möglichkeit, komplexe Designs durch Segmentierungsmodelle in Hierarchien von Designkomponenten zu verwalten;
- Model Explorer zur Naviation, Erzeugung, Konfiguration und Suche nach allen Signalen, Parametern, Eigenschaften im erzeugtem Code in Zusammenhang mit Ihrem Modell;
- Anwendungsprogammschnittstellen (APIs), die Ihnen eine Verknüpfung mit anderen Simulationsprogrammen erlauben und mit denen Sie handgeschriebenen Code mit einschließen können;
- Eingebettete MATLAB™ Funktionsblöcke, um MATLAB Algorithmen in Simulink einzugliedern sowie eingebettete Systemimplementierungen;
- Simulation-Modelle (normal, Accelerator und rascher Accelerator) zum interpretativen Ablauf von Simulationen oder zu erzeugten C-Code Geschwindigkeiten mit Hilfe von Problemlösern mit festen oder variablen Schritten;
- Graphisches Fehlersuchprogramm und Profiler zur Untersuchung von Simulationsergebnissen mit anschließender Diagnose der Leistung und unerwarteten Verhaltens Ihres Designs;
- Voller Zugriff auf MATLAB zur Analyse und Veranschaulichung der Ergebnisse, individuelle Anpassung der Modell-Umgebung sowie Definition von Signal, Parameter und Testdaten;
- Tools zur Modellanalyse und Diagnose zur Sicherstellung der Modellbeständigkeit und Identifikation von Fehlern des Modells.
Beginnen wir also mit der direkten Prüfung der Simulink-Umgebung. Sie wird von einem bereits offenen Matlab-Fenster mit Hilfe einer der beiden folgenden Varianten gestartet:
- via Simulink Befehl im Befehlsfenster
- via Simulink Symbol in der Werkzeugleiste.
Abb. 1 Simulink Initialisierung
Ist der Befehl ausgeführt, erscheint das Fenster mit den verschiedenen Librariers (Simulink Library Browser).
Abb. 2 Library Browser
Dieses Browser-Fenster enthält eine Baumstruktur der Simulink Libraries-Komponenten. Um einen bestimmten Bereich der Library ansehen zu können, wählen Sie ihn einfach mit der Maus aus. Anschließend erscheint rechts im Simulink Library Browser-Fenster eine Reihe an Symbolkomponenten des aktiven Bereichs. Abb. 2 zeigt den Hauptbereich der Simulink Library.
Mit Hilfe des Browser-Menüs oder den Schaltflächen seiner Werkzeugleiste können sie jetzt ein Fenster öffnen, um ein neues Modell zu erzeugen oder ein bestehendes hochzuladen. Hier muss ich anmerken, dass die gesamte Arbeit mit Simulink in einem offenen MATLAB System stattfindet, in dem die Ausführung der Vorgänge kontrolliert werden kann, solange das Modellierungsprogramm ihre Ausgabe vorsieht.
Abb. 3 Das leere Simulink Fenster
Als erstes ändern wir einige Parameter unseres Modells. Öffnen wir Simulation --> Konfigurationsparameter. Das Fenster zeigt einige Registerkarten mit vielen Parametern. Uns interessiert die "Standard-Problemlöser"-Registerkarte, wo man die Parameter des Problemlösers für das Simulink Modellsystem einrichten kann.
In der Simulationszeit wird die Zeit für das Modell via der Anfangszeit - Startzeit (meist 0) und der Endzeit - Stoppzeit - eingerichtet.
Für unsere Zwecke bekommt die Startzeit den Wert 1. Die Stoppzeit bleibt wie sie ist.
In den Optionen des Problemlösers, habe ich seine Art auf festen Schritt geändert, den Problemlöser an sich auf separat eingestellt und den Schritt (hier ja fester Schritt) auf 1.
Abb. 4 Parameter-Konfigurationsfenster
Die Simulink Umgebung wird durch das Stateflow Subsystem erfolgreich ergänzt, ein auf Ereignisse reagierendes Modellierungspaket auf Grundlage der endlichen Automatisierung. Mit seiner Hilfe können wir die Arbeit des Systems auf Grundlage einer Kette von Regeln darstellen, die die Ereignisse und die Handlungen als Reaktion auf diese Ereignisse festlegen.
Die grafische Benutzerschnittstelle des Stateflow-Pakets hat die folgenden Komponenten:
- Ein Grafik-Editor der SF-Charts;
- Stateflow Explorer;
- Stateflow Finder zum Finden der notwendigen Objekte in den SF-Charts;
- Fehlersuchprogramm der SF-Modelle;
- Real Time Workshop, ein Echtzeit Code-Generator
Das am häufigsten gebrauchte Blockdiagramm (Chart), da sich im Stateflow Bereich befindet. Und das sehen wir uns jetzt an.
Verschieben wir den Block von der Library und öffnen das Diagramm mit einem Doppelklick auf den Block. Es erscheint das leere Fenster eines SF-Chart-Editors, mit dem man SF-Charts und ihre Fehlersuche erzeugen kann, um die benötigten Funktionen zu erhalten
Die Werkzeugleiste befindet sich oben links. Sie umfasst 9 Schaltflächen:
- Status;
- History-Kreuzung;
- Standard-Übergang;
- Verbindungskreuzung;
- Truth-Tabelle;
- Funktion;
- eingebettete MATLAB Funktion;
- Feld;
- Simulink Funktionsaufruf.
Es ist im Kontext dieses Beitrag leider unmöglich auf jedes dieser Elemente detailliert einzugehen. Daher beschränke ich mich auf eine Kurzbeschreibung derjenigen Elemente, die wir für unser Modell brauchen. Mehr Einzelheiten zu diesen Elementen finden Sie im Abschnitt Hilfe von Matlab oder auf der Website des Entwicklers.
Abb. 5 Editor-Ansicht des SF-Charts
Das wichtigste Objekt der SF-Charts ist der Status. Er ist in Form eines Rechtecks mit abgerundeten Ecken dargestellt und
kann ausschließlich oder parallel sein. Jeder Zustand kann der übergeordnete Zustand sein und Erben haben und kann zudem aktiv oder nicht aktiv sein. Jeder Zustand kann gewisse Vorgänge ausführen.
Der Übergang ist in Form einer gekrümmten Linie mit Pfeil dargestellt. Er verknüpft die Zustände mit anderen Objekten. Durch linken Mausklick auf das Quellobjekt und Ziehen des Cursors auf das Zielobjekt kann ein Übergang ausgeführt werden. Der Übergang kann seine eigenen Bedingungen haben, die in den Klammern angegeben sind. Der Übergangsvorgang wird innerhalb der Klammern angezeigt und wird dann ausgeführt, wenn die Bedingung erfüllt ist. Der während der Bestätigung des Zielobjekts vollzogene Übergang wird mit einem Slash gekennzeichnet.
Der alternative Knoten (Verbindungskreuzung) hat die Form eines Kreises und erlaubt den Übergang, die verschiedenen Pfade zu benutzen, von denen jeder von einer bestimmten Bedingung festgelegt ist. In so einem Fall wird der Übergang, der der angegebenen Bedingung entspricht, ausgewählt.
Die Funktion wird als Flowgraph mit den Aussagen der Stateflow-prozedularen Programmiersprache dargestellt. Ein Flowgraph spiegelt die logische Struktur der Verwendung von Übergängen und alternativen Knoten wider..
Ein weiteres wichtiges Objekt von Stateflow ist das Ereignis - es gehört zur Gruppe der nicht graphischen Objekte. Dieses Objekt kann die Vorgänge auf dem SF-Chart in Gang setzen.
Sein Vorgang (Handlung) ist ebenfalls ein nicht graphisches Objekt. "Ereignis" kann die Funktion aufrufen, ein spezifisches Ereignis, einen Übergang usw. zuweisen.
Die Daten im SF-Modell werden in numerischen Werten und nicht als graphische Objekte abgebildet. Sie können auf jeder Stufe der Hierarchie des Modells erzeugt werden und besitzen Eigenschaften.
2. Beschreibung der Handelsstrategie
Beschäftigen wir uns kurz mit Handel. Der Expert Advisor wird für unsere Schulungszwecke hier, sehr einfach, wenn nicht sogar primitiv, sein.
Das automatische Handelssystem wird Positions auf Basis von Signalen eröffnen, grundsätzlich nach dem Queren des Exponentiellen Gleitenden Durchschnitts der Abschlusskurse, mit einem Zeitraum von 21 und 55 (Fibonacci-Zahlen). Wenn also der Exponentielle Gleitende Durchschnitt 21 den EGD 55 von unten kommend quert, wird eine Long Position eröffnet - ansonsten eine Short Position.
Zur Filterung des Rauschens wird eine Position am k. Balken durch den Kurs des Balkens eröffnet, der sich nach Auftauchen der 21/55-Querung geöffnet hat. Unseren Handel führen wir mit EURUSD H1 durch. Und wir eröffnen nur eine Position. Sie wird dann geschlossen, wenn die Take Profit oder Stop Loss Schwelle erreicht ist.
Ich möchte hier erwähnen, dass während der Entwicklung eines automatisierten Handelssystems und dem Backtesting der History, gewisse Vereinfachungen des insgesamten Handelsabbilds zulässig sind.
So wird das System z.B. nicht die Ausführung eines Signals durch den Makler nachprüfen. Des Weiteren werden wir dem Kern des Systems in MQL5 Handelsbeschränkungen hinzufügen.
3. Die Modellerzeugung einer Handelsstrategie in Simulink
Zunächst müssen wir die historischen Kursdaten in die Matlab-Umgebung laden. Dies geschieht mit Hilfe des MetaTrader 5 Scripts, die sie speichert (testClose.mq5).
Diese Daten (öffnen, hoch, niedrig, schließen, Differenz/Spread) werden in Matlab auch mit Hilfe eines einfachen m-Scripts (priceTxt.m) geladen.
Mit Hilfe des gleitenden Durchschnitts (Standardfunktion in Matlab) erzeugen wir Arrays Exponentieller Gleitender Durchschnitte:
[ema21, ema55] = movavg(close, 21, 55, 'e');
Sowie ein Hilfs-Arrays mit Balken-Indices:
num=1:length(close);
Erzeugen wir die folgenden Variablen:
K=3; StopLoss=0,0065; TakeProfit=0,0295;
Los geht's mit der Modellerstellung.
Wir legen ein leeres Simulink-Fenster an und speichern es unter dem Namen mts. Alle folgenden Handlungen sind im Video-Format nochmals vorhanden. Sollte also etwas nicht ganz klar, oder sogar vollkommen unklar, sein, dann können Sie sich meine einzelnen Handlungen nochmals im Video ansehen.
Bei der Abspeicherung des Modells kann das System evtl. folgende Fehlermeldung drucken:
??? Datei "C:\\Simulink\\mts.mdl" enthält Zeichen, die mit der aktuellen Zeichenkodierung, windows-1251, inkomaptibel sind. Diese Meldung vermeidet man folgendermaßen:
1) Verwenden Sie die slCharacterEncoding Funktion und ändern die aktuelle Zeichenkodierung in die folgenden: Shift_JIS, windows-1252, ISO-8859-1.
2) Entfernen Sie alle Zeichen, die nicht unterstützt sind. Das erste nicht unterstützte Zeichen befindet sich in Zeile 23, Byte-Abstand 15.
Um dies zu lösen, müssen Sie einfach alle Fenster aller Modelle schließen und die Kodierung mit Hilfe der folgenden Befehle ändern:
bdclose all
set_param(0, 'CharacterEncoding', 'windows-1252');
Legen wir nun die Informationsquelle für unser Modell fest.
Die Rolle einer derartigen Informationsquelle sind die historischen Daten von MetaTrader 5, die die Eröffnungs-, Maximal-, Minimal-, und Abschlusskurse enthalten. Zusätzlich dazu berücksichtigen wir auch noch den Spread, selbst wenn er relativ kürzlich fließend wurde. Und schließlich zeichnen wir die Eröffnungszeit des Balkens auf. Zu Modellerstellungszwecken werden einige Arrays der ursprünglichen Daten als ein Signal interpretiert werden, also als ein Vektor an Werten einer Zeitfunktion an separaten Punkten dieser Zeit.
Legen wir nun ein "FromWorkspace" Subsystem an, um die Daten aus dem Matlab Arbeitsbereich nach dorthin abzurufen. Wählen Sie dazu im Simulink Libraries-Browser den Bereich 'Ports & Subsystems'. Ziehen Sie mit Hilfe der Maus den "Subsystem" Block in das Simulink-Modell Fenster. Und nennen ihn per Klick auf das Subsystem in "FromWorkspace" um. Loggen Sie sich anschließend per Doppelklick der linken Maustaste auf den Block in das Subsystem ein, um die Eingabe- und Ausgabe-Variablen und die Konstanten für das System zu erzeugen.
Um die Signalquellen im Library Browser zu erzeugen, wählen Sie Signal Processing Blockset (Signalverarbeitungs-Blockset) und Signal Processing Sources (Signalverarbeitungsquellen). Mit Hilfe der Maus ziehen Sie den "Signal von Arbeitsplatz" Block in das Subsystem-Fenster des FromWorkspace Modells. Da dieses Modell vier Eingabesignale haben wird, duplizieren wir diesen Block einfach und legen noch drei weitere Kopien an. Legen wir nun gleich fest, welche Variablen der Block verarbeiten soll. Dazu zweimal auf den Block klicken und den Variablennamen in Eigenschaften eingeben. Die Variablen sind: offen, ema21, ema55, num. Daher nennen wir die Blöcke so: offenes Signal, ema21 Signal, ema55 Signal, num Signal.
Aus dem Simulink Bereich "häufig verwendete Blöcke" fügen wir einen Block zur Erzeugung eines Kanals (Bus-Creator) hinzu. Wir öffnen den Block und ändern die Anzahl der Eingaben auf 4 und verknüpfen die Blöcke offenes Signal, ema21 Signal, ema55 Signal, num Signal mit den Eingaben des Bus Creator Blocks.
Zudem haben wir noch 5 weitere Konstanten. Der Block "Konstante" wird ebenfalls aus "häufig verwendete Blöcke" hinzugefügt. Als seinen Wert (konstanter Wert) legen wir die Namen der Variablen fest: Spread, hoch, niedrig, tp, sl:
- Spread - ein Array mit Spread-Werten;
- hoch - ein Array mit maximalen Kurswerten
- niedrig - ein Array mit minimalen Kurswerten;
- tp - Take Profit Wert, absolut;
- sl - Stop Loss Wert, absolut.
Die Blöcke nennen wir wie folgt: Spread-Array, hoch-Array, niedrig-Array, Take Profit, Stop Loss.
Wählen Sie dann im Simulink Bereich "Ports & Subsystems" den Ausgabe-Port Block (Out1) und verschieben ihn in das Subsystem-Fenster. Kopieren Sie den Ausgabe-Port fünfmal. Den ersten verknüpfen wir mit dem Bus Creator Block und alle anderen, abwechselnd, mit den Arrays Spread, hoch, niedrig, Take Profit und Stop Loss Blöcken.
Wir nennen den ersten Port in Kurs um und die anderen erhalten die Namen der Ausgabe-Variablen.
Zur Erzeugung eines Handelssignals fügen wir den Block für Addition (Add) aus dem Simulink Bereich "Mathematische Vorgänge" hinzu und nennen ihn emas differential. Innerhalb des Blocks ändern wir die Liste der Zeichen c + + auf + - und drehen, mit Hilfe von Ctrl+ K den ganzen Block um 90° im Uhrzeigersinn. Dann verknüpfen wir den ema21 Signalblock mit der "+" Eingabe und das ema55 Signal mit "-".
Dann fügen wir den Delay-Block (Verzögerung) aus dem Bereich Signal Processing Blockset der Signalfunktionen hinzu und nennen diesen Block K Delay. Im Delay-Feld (Muster) des Blocks geben wir den Namen der K-Variablen ein und verknüpfen ihn mit dem vorherigen Block.
Die emas differential Blöcke und K Delay formatieren die Front (Differenz der Stufen) des Kontrollsignals für Berechnungen nur in den Phasen der Modellbildung wo eine Veränderung stattgefunden hat. Das Subsystem, das wir etwas später erzeugen werden, wird aktiviert, wenn mind. ein Element in seiner Signalstufe eine Veränderung erfahren hat.
Aus dem Simulink-Bereich "Häufig verwendete Blöcke" fügen wir einen Multiplexer mit und (Mux) Block hinzu und drehen den Block ebenfalls um 90° im Uhrzeigersinn. Wir teilen die Signalzeile des Delay-Blocks in zwei und verknüpfen sie mit den Multiplexes.
Aus dem Stateflow Bereich fügen wir einen Chart-Block ein und geben den Chart ein. Jetzt werden zwei ankommende Ereignisse (Buy und Sell) und zwei ausgehende Ereignisse (OpenBuy und OpenSell) hinzugefügt. Den Auslöserwert (Trigger) für das Buy-Ereignis setzen wir auf Fallend (Aktivierung des Subsystems durch eine negative Front) und den für Sell-Ereignisse auf Steigend (Aktivierung des Subsystems durch eine positive Front). Den Auslöserwert (Trigger) für die Ereignisse OpenBuy und OpenSell setzen wir auf die Position der Funktionsaufrufs (Aufrufen) (Aktivierung des Subsystems wird durch die Logik der Arbeit der gegebenen S-Funktion bestimmt).
Wir erzeugen einen Standard-Übergang mit drei alternativen Knoten. Den ersten Knoten verknüpfen wir mit einem Übergang mit dem zweiten Knoten und setzen die Bedingungen und den Vorgang für beide auf Buy {OpenBuy;}; für den dritten Knoten setzen wir den Vorgang auf Sell {OpenSell;}. Dann verknüpfen wir die Charteingabe mit dem Multiplex, und die zwei Ausgaben mit einem weiteren Multiplex, der vom ersten kopiert werden kann. Der letzte Block wird mit dem Ausgabe-Port verknüpft, den wir von einem analogen Por kopieren und ihn Buy/Sell nennen.
Ach, da hätte ich beinahe vergessen! Damit unser Modell auch ordentlich funktioniert, müssen wir noch ein virtuelles Kanal-Objekt erzeugen, das sich im Matlab Arbeitsbereich befindet. Und dazu geben wir den Bus Editor via des Menüs Werkzeuge ein. Im Editor wählen wir den Punkt Bus hinzufügen und nennen ihn InputBus.
Dann fügen wir die Elemente entsprechend der Namen der Eingabe-Variablen ein: offen, ema21, ema55 und num. Wir öffnen den Bus-Creator und kreuzen das Kästchen neben 'Eigenschaften Angeben' via dem Bus-Objekt an (die Eigenschaften werden mittels des Bus-Objekts eingerichtet). Kurz, wir verknüpfen unseren Block mit dem virtuellen Kanal-Objekts, das wir gerade erzeugt haben. Der virtuelle Kanal heißt, dass die Signale nur graphisch kombiniert werden, ohne irgendeinen Einfluss auf die Memory-Verteilung.
Die Änderungen speichern wir im Subsystem-Fenster. Und damit ist unsere Arbeit mit dem FromWorkspace-Subsystem beendet.
Jetzt müssen wir die "Black Box" erzeugen. Dieser Block beruht auf ankommenden Signalen und verarbeitet die Information und triff Handelsentscheidungen. Er muss natürlich von uns erzeugt werden, anstatt von einem Computerprogramm. Denn schließlich können ja nur wir über die Bedingungen entscheiden unter denen das System einen Handel durchführen sollte. Zudem muss dieser Block die Information über die abgeschlossenen Abschlüssen in Form von Signalen anzeigen.
Der benötigte Block heißt Chart und befindet sich im Stateflow Bereich. Und den kennen wir doch schon, nicht wahr? Per "Drag&Drop" ziehen und platzieren wir ihn in unser Modell-Fenster.
Abb. 6 Die Blöcke des Eingabe-Subsystems und das StateFlow-Chart
Wir öffnen das Chart und geben dort unsere Daten ein Zuerst müssen wir ein Kanal-Objekt erzeugen, ganz genauso wie wir es im FromWorkspace Subsystem gemacht haben. Doch anders als beim vorigen Objekt, das uns Signale aus dem Arbeitsbereich lieferte, wird dieses das erhaltene Ergebnis ausspucken. Also nennen wir es logischerweise AusgabeBus und seine elemente: barOpen, OpenPrice, TakeProfit, StopLoss, ClosePrice, barClose, Comment, PositionDir, posN, AccountBalance.
Jetzt können wir mit dem Zusammenbau beginnen. Im Chartfenster zeigen wir den Standardübergang an (# 1).
Für die Bedingungen und Verfahren geben wir an:
[Input.num>=56 && Input.num>Output.barClose] {Output.barOpen=Input.num;i = Input.num-1;Output.posN++;}
Diese Bedingung besagt, dass die Daten verarbeitet werden, wenn die Anzahl der Eingabe-Balken mind. 56 beträgt und zugleich der Eingabe-Balken höher ist als der Schlussbalken der vorherigen Position. Dann wird dem Eröffnungsbalken (Output.barOpen) die Zahl des ankommenden Balkens mittels einer Index-Variable von i zugewiesen - der Index (beginnt bei 0) und die Anzahl der offenen Positions steigt um 1 an.
Der 2. Übergang wird nur dann ausgeführt, wenn die offene Position nicht die erste ist. Ansonsten wird der dritte Übergang ausgeführt, der der Kontosaldo-Variable (Output.AccountBalance) den wert 100.000 zuweist.
Der 4. Übergang wird ausgeführt, wenn der Chart vom OpenBuy Ereignis in Gang gesetzt worden ist. In so einem Fall wird die Position in Richtung Kaufen zeigen (Output.PositionDir = 1), der Eröffnungspreis wird dem Preis des eröffnenden Balkens entsprechen, wobei der Spread auch noch berücksichtigt wird (Output.OpenPrice = Input.open + spread [i] * 1e-5). Die Werte der Ausgabe-Signale StopLoss und TakeProfit werden ebenfalls festgelegt.
Passiert ein OpenSell Ereignis, folgt der Flow dem fünften Übergang und setzt seine Werte für die Ausgabe-Signale.
Der 6. Übergang wird realisiert, wenn es sich um eine Long Position handelt, andernfalls geht der Flow zum siebten Übergang.
Der 8. Übergang prüft, ob der maximale Kurs des Balken die Take Profit Stufe erreicht hat oder nicht oder ob der minimale Kurs des Balkens die Take Profit Stufe erreicht hat. Andernfalls erhöht sich der Wert der Index-Variable i um 1 (neunter Übergang).
Der 10. Übergang verifiziert die Bedingungen, die bei einem Stop Loss auftauchen: das Kursminimum des Balkens hat die Stop Loss Stufe überschritten. Wird dies bestätigt, geht der Flow zum elften Übergang und dann zum zwölften, wo die Werte der Preisunterschiede der Schluss- und Eröffnungs-Positions, des aktuellen Kontosaldos und der Index des abschließenden Balkens festgelegt sind.
Wird der 10. Übergang nicht bestätigt, wird die Position beim Take Profit geschlossen (13. Übergang). Und danach, vom 14. Übergang an, geht der Flow zum 12.Übergang.
Die Verfahren und Bedingungen für die Übergänge für eine Short Position sind genau umgekehrt.
Und schließlich haben wir neue Variablen im Chart erzeugt. Um diese automatisch in unser Modell zu integrieren, müssen wir es direkt im Chart-Fenster laufen lassen, indem wir auf "Simulation starten" drücken. Diese Schaltfläche sieht fast so aus wie "Play" bei Musikgeräten. Jetzt startet der Stateflow Symbolassistent (SF Master-Objekte) und schlägt vor, die erzeugen Objekte zu speichern. Dazu auf 'Alle auswählen' drücken und dann auf 'Erzeugen' klicken. Die Objekte wurden erzeugt. Öffnen wir nun den Modell-Browser und klicken dort links auf unser Chart in der Modell-Hierarchie. Sortieren wird dort die Objekte nach Datentyp aus (DataType)
und fügen mehr Daten mittels der Menübefehle "Hinzufügen" und "Daten" hinzu. Wir rufen die erste Variable 'Eingabe' auf und verändern den Wert des Bereichs zu Eingabe und die Art zu Bus: <bus object name> . Danach geben wir den Namen des zuvor erzeugten Kanals, InputBus, rechts in dieses Feld ein. Also ist unsere Eingabe-Variable nun eine Variable vom Typ InputBus. Den Wert des Ports stellen wir nun auf 1.
Das gleiche machen wir mit der Ausgabe-Variable. Sie darf nur den Ausgabe-Bereich haben und muss vom Typ OutputBus sein.
Verändern wir nun den Bereich für die Variablen hoch, niedrig, sl, tp und Spread auf den Wert "Input". Entsprechend richten wir die Port-Ziffern in folgender Reihenfolge ein: 3, 4, 6, 5, 2.
Auch den Bereich der Variable Posten zu Konstante müssen wir ändern. Dazu geben wir auf der "Wert-Merkmale" Registerkarte 1, OpenBuy und OpenSell Ereignisse ein - für Eingabe im Feld "Ursprünglich" (rechts). In den Ereignissen verändern wir den Auslöserwert für den "Funktionsaufruf") und
Erzeugen eine interne Variable len mit einem Konstante-Bereich. Auf der "Wert-Merkmale" Registerkarte im Feld "ursprünglicher Wert" geben wir eine m-Funktionslänge ein (schließen). So ist sie gleich der Länge des Abschließen-Arrays, das sich im Matlab Arbeitsbereich befindet.
Für die 'hoch' und 'niedrig' Variablen geben wir einen Wert von [len 1] im Feld 'Größe' ein. Somit haben im Memory die Array-Größen von 'hoch' und 'niedrig' als Werte von [len 1] gespeichert.
Geben wir auch für die Variable K auf der "Wert-Merkmale" Registerkarte im Feld "ursprünglicher Wert" (rechts) die aktuelle Variable für K an, die wir aus dem Arbeitsbereich holen.
Als Ergebnis erhalten wir ein Chart-Subsystem mit 7 Eingabe-Ports und 1 Ausgabe-Port. Platzieren wir den Block so, dass sich der Eingabe-Ereignisse () Port unten befindet und nennen den "Position Handling" Block um. Auch im Chart an sich, lassen wir uns den Namen des Blocks anzeigen Und mittels der entsprechenden Ports vereinen wir dann die Blocks vom FromWorkspace Subsystem und dem "Position Handling" und ändern ihre Farbe.
Hier sei darauf hingewiesen, dass das "Position Handling" Subsystem nur dann funktioniert, wenn es von ankommenden OpenBuy oder OpenSell Ereignissen "aufgeweckt" wird. Auf diese Weise optimieren wir die Funktionsweise des Subsystems, um unnötige Berechnungen zu vermeiden.
Abb. 7 FromWorkspace und Position Handling Subsysteme
Jetzt müssen wir ein Subsystem anlegen, um die Verarbeitungsergebnisse in den Matlab Arbeitsplatz zu drucken und es mit dem "Position Handling" Subsystem verknüpfen. Das wird eine leichte Aufgabe.
Legen wir ein "ToWorkspace" Subsystem an, um die Ergebnisse in den Arbeitsplatz zu bekommen. Dazu wiederholt man alle Schritte der Erzeugung des "FromWorkspace" Subsystems. Im Library Browser wählt man dann den Simulink Bereich Ports & Subsystems. Mit Hilfe der Maus zieht man dann den "Subsystem" Block in das Simulink Modell-Fenster und benennt ihn durch Anklicken von "Subsystem" in "ToWorkplace" um. Anschließend wird der Block mit dem "Position Handling" Subsystem vereint.
Zur Erzeugung der Variablen loggt man sich per Doppelklick mit der linken Maustaste auf den Block in das Subsystem ein.
Da das Subsystem Daten vom OutputBus Objekt erhalten wird, das ein nicht-virtueller Bus ist, müssen wir die Signale aus diesem Kanal auswählen. Dazu wählt man den Simulink Bereich "Häufig verwendete Blöcke" im Library Browser aus und fügt dort einen "Bus Selector" hinzu. Dieser Block hat 1 Eingabe- und 2 Ausgabesignale, wohingegen wir jedoch 10 dieser Signale brauchen.
Verknüpfen wir also den Block mit dem Eingabe-Port. Dazu bitte auf "Simulation starten" drücken (das ist unsere "Play"-Taste). Der Compiler beginnt nun mit dem Bau des Modells. Es wird noch nicht erfolgreich gebaut werden, doch werden die Eingabe-Signale für den Block, der den Bus auswählt, erzeugt. Wenn wir den Block eingeben, dann erscheinen die benötigten Signlae links im Fenster, die via dem OutputBus übertragen werden. Sie alle müssen mit Hilfe der "Auswählen" Taste ausgewählt und dann nach rechts verschoben werden - hin zu "Ausgewählte Signale".
Abb. 8 Bus Selector Blockparameter
Gehen wir abermals in den Bereich "Häufig verwendete Blöcke" des Simulink Libraries Browser, und fügen dort einen Mux Multiplex-Block hinzu, der die Anzahl der Eingaben angibt, die = 10 ist.
Danach loggen wir uns in den Bereich "Sinks" des Simulink Library-Browsers ein und verschieben den ToWorkspace Block in das Subsystem-Fenster Und in diesem Fenster geben wir den neuen Namen der Variable "Kontosaldo" an und verändern das Ausgabeformat (Format Speichern) von "Struktur" zu "Array", und vereinen diesen Block mit dem Multiplex. Anschließend kann der Ausgabe-Port gelöscht werden, da wir ihn nicht mehr brauchen. Passen Sie die Farbe der Blöcke nach Ihren Wünschen an und speichern das Fenster. Unser Subsystem ist nun fertig.
Vor dem Bau unseres Modells sollten wir prüfen, ob im Arbeitsplatz auch Variablen vorhanden sind. Die folgenden Variablen sollten dort vorhanden sein: InputBus, K, OutputBus, schließen, ema21, ema55, hoch, niedrig, num, öffnen, sl, Spread, tp.
Stellen wir den Stoppzeit-Wert als den Parameter ein, der num (Ende) festlegt. Das bedeutet, der verarbeitete Vektor hat die Länge, die vom letzten Element im num Array festgesetzt wurde.
Vor dem Beginn des Modellbaus müssen wir noch einen Compiler wählen. Dies geschieht mit Hilfe des folgenden Befehls:
mex-setup
Bitte wählen Sie Ihren Compiler zum Aufbau externer Interface-Dateien (MEX):
Soll mex bereits installierte Compiler finden [j] / n? j
Compiler wählen:
[1] Lcc-win32 C 2.4.1 in C:\PROGRA~2\MATLAB\R2010a\sys\lcc
[2] Microsoft Visual C++ 2008 SP1 in C:\Program Files (x86)\Microsoft Visual Studio 9.0
[0] keine
Compiler: 2
Wie Sie sehen, habe ich den Microsoft Visual C ++ 2008 Compiler SP1 gewählt.
So, los geht's mit dem Aufbau. Drücken Sie auf "Simulation starten". Ah, eine Fehlermeldung: Stateflow Interface Fehler: Port-Breite stimmt nicht. Eingabe "Spread"(#139) wartet auf einen Skalar. Das Signal ist ein eindimensionaler Vektor mit 59739 Elementen.
Die Variable "Spread" sollte nicht vom Typ 'doppel' sein, sondern stattdessen seinen Typ vom Signal aus Simulink 'erben'.
Daher legen wir im Modell-Browser für diese Variable "Erben: gleich wie Simulink" fest und im Feld 'Größe' "-1". Änderungen speichern.
Fahren wir das Modell noch einmal ab. Jetzt funktioniert der Compiler. Er zeigt nur noch einige kleinere Warnungen. Und in weniger als 40 Sekunden verarbeitet unser Modell die Daten von knapp 60.000 Balken. Der Handel wird von '01.01.2001 00:00 Uhr' - '16.08.2010 11:00 Uhr' ausgeführt. Die Gesamtzahl der Positions beläuft sich auf 461. Im folgenden Videoclip sehen Sie wir das Modell arbeitet.
4. Implementierung der Strategie in MQL5
Unser automatisches Handelssystem ist damit in Simulink erstellt. Jetzt müssen wir dieses Handelskonzept nur noch in die MQL5 Umgebung übertragen. Wir mussten uns mit Simulink Blöcken und Objekten beschäftigen, durch die wir die Logik unseres Handels-Expert Advisors ausgedrückt haben. Unsere Aufgabe jetzt besteht darin, diese logische Handelssystem in den MQL5 Expert Advisor zu bringen.
Hierbei sei allerdings darauf hingewiesen, dass einige Blöcke nicht notwendigerweise im MQL5 Code definiert sein müssen, da ihre Funktionen verborgen werden können. Ich versuche nun so detailliert wie möglich zu erklären, welche Zeile im aktuellen Code mit welchem Block zusammenhängt. Diese Verbindung kann manchmal auch indirekt sein, und kann manchmal eine Schnittstellen-Verknüpfung von Blöcken oder Objekten abbilden.
Bevor ich mit meinen Erklärungen beginne, möchte ich auf einen Beitrag hinweisen: "Schritt-für-Schritt Leitfaden zum Schreiben von Expert Advisors in MQL5 für Anfänger". Dieser Beitrag gibt eine leicht verständliche Beschreibung der wichtigsten Konzepte und grundlegenden Regeln zum Schreiben eines Expert Advisors in MQL5. Doch damit halte ich mich jetzt nicht auf, allerdings verwende ich einige Zeilen der MQL5 Codes von dort.
4.1 Das "FromWorkspace" Subsystem
Wir haben z.B. einen "öffnen Signal" Block im "FromWorkspace" Subsystem. In Simulink ist dies nötig, um während des Backtestings den Preis des Eröffnungbalkens zu bekommen und um zu diesem Preis eine Position eröffnen zu können, falls ein Handelssignal empfangen wird. Dieser Block ist offenbar im MQL5 Code nicht vorhanden, da der Expert Advisor die Information zum Preis unmittelbar nach dem Erhalt des Handelssignals abfragt.
Im Expert Advisor müssen wir Daten verarbeiten, die wir von gleitenden Durchschnitten erhalten haben. Daher müssen wir für sie dynamische Arrays und entsprechende Hilfs-Variablen, wie z.B. Handles anlegen.
int ma1Handle; // Moving Average 1 indicator handle: block "ema21 signal" int ma2Handle; // indicator handle Moving Average 2: block "ema55 signal" ma1Handle=iMA(_Symbol,_Period,MA1_Period,0,MODE_EMA,PRICE_CLOSE); // get handle of Moving Average 1 indicator ma2Handle=iMA(_Symbol,_Period,MA2_Period,0,MODE_EMA,PRICE_CLOSE); // get handle of Moving Average 2 indicator double ma1Val[]; // dynamic array for storing the values of Moving Average 1 for every bar: block "ema21 signal" double ma2Val[]; // dynamic array for storing the values of Moving Average 2 for every bar: block "ema55 signal" ArraySetAsSeries(ma1Val,true);// array of indicator values MA 1: block "ema21 signal" ArraySetAsSeries(ma2Val,true);// array of indicator values MA 2: block "ema55 signal"
Alle anderen Zeilen, die sich ein wenig auf den gleitenden Durchschnitt ema21 und ema55 auswirken, können als Hilfszeilen betrachtet werden.
"Take Profit" und "Stop Loss" sind als Eingabe-Variablen definiert:
input int TakeProfit=135; // Take Profit: Take Profit in the FromWorkspace subsystem input int StopLoss=60; // Stop Loss: Stop Loss in the FromWorkspace subsystemAngesichts der Tatsache, dass es für EURUSD 5 signifikante Ziffern gibt, muss der Wert für TakeProfit und StopLoss folgendermaßen aktualisiert werden:
int sl,tp; sl = StopLoss; tp = TakeProfit; if(_Digits==5) { sl = sl*10; tp = tp*10; }
Die Arrays "Spread", "hoch" und "niedrig" werden dazu benutzt, die Werte in Form einer Matrix der relevanten Kursdaten zu speichern, da sie für die Lieferung historischer Daten zuständig sind, um so die Handelsbedingungen identifizieren zu können.
Sie sind im Code nicht explizit abgebildet. Man kann jedoch argumentieren, dass das Array "Spread" beispielsweise benötigt wird, um einen Stream von Briefkursen zu bilden. Die anderen zwei Arrays sind zur Festlegung der Bedingungen zum Schließen einer Position notwendig, die nicht im Code spezifiziert sind, da sie ja in Meta Trader 5, sobald ein gewisses Preisniveau erreicht ist, automatisch ausgeführt werden.
Der Block "num Signal" ist ein Hilfsblock und erscheint im Code des Expert Advisors nicht.
Der Block "emas Differenzial" prüft die Bedingungen zur Eröffnung einer Short oder Long Position, indem er die Unterschiede findet. "K Delay" erzeugt eine Verzögerung für die Arrays, die dem Durchschnitt des Wertes von K entsprechen.
Das Buy- oder Sell-Ereignis ist erzeugt: es ist ein eingebendes Ereignis für das Subsystem, das die Position eröffnet.
Im Code sieht das alles so aus:
// event Buy (activation by the negative front) bool Buy=((ma2Val[1+K]-ma1Val[1+K])>=0 && (ma2Val[K]-ma1Val[K])<0) || ((ma2Val[1+K]-ma1Val[1+K])>0 && (ma2Val[K]-ma1Val[K])==0); // event Sell (activation by the positive front) bool Sell=((ma2Val[1+K]-ma1Val[1+K])<=0 && (ma2Val[K]-ma1Val[K])>0)|| ((ma2Val[1+K]-ma1Val[1+K])<0 && (ma2Val[K]-ma1Val[K])==0);Das Subsystem, das die Position eröffnet, erzeugt die "OpenBuy" und "OpenSell" Ereignisse selbst, die dann im "Position Handling" Subsystem mit Hilfe der Bedingungen und Verfahren verarbeitet werden.
4.2 Das "Position Handling" Subsystem
Das Subsystem beginnt seine Arbeit, indem es die OpenBuy und OpenSell Ereignisse verarbeitet.
Für den ersten Übergang des Subsystems besteht eine der Bedingungen darin, dass nicht weniger als 56 Balken vorhanden sein müssen. dies ist im Code via der Prüfung folgender Bedingungen angegeben:
if(Bars(_Symbol,_Period)<56) // 1st transition of the «Position handling»subsystem : condition [Input.num>=56] { Alert("Not enough bars!"); return(-1); }
Die zweite Bedingung für den Übergang ist, dass die Zahl des eröffnenden Balkens größer sein muss als die des schließenden Balkens (Input.num; Output.barClose), d.h. die Position wurde geschlossen.
Dies ist im Code so angegeben:
//--- 1st transition of the «Position handling» subsystem: condition [Input.num>Output.barClose] bool IsBought = false; // bought bool IsSold = false; // sold if(PositionSelect(_Symbol)==true) // there is an opened position { if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) { IsBought=true; // long } else if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) { IsSold=true; // short } } // check for opened position if(IsTraded(IsBought,IsSold)) { return; } //+------------------------------------------------------------------+ //| Function of the check for opened position | //+------------------------------------------------------------------+ bool IsTraded(bool IsBought,bool IsSold) { if(IsSold || IsBought) { Alert("Transaction is complete"); return(true); } else return(false); }
Der 4. Übergang ist für das Öffnen einer Long Position zuständig und
wird so angezeigt:
// 4th transition procedures of the «Position handling» subsystem: open long position mrequest.action = TRADE_ACTION_DEAL; // market buy mrequest.price = NormalizeDouble(latest_price.ask,_Digits); // latest ask price mrequest.sl = NormalizeDouble(latest_price.bid - STP*_Point,_Digits); // place Stop Loss mrequest.tp = NormalizeDouble(latest_price.bid + TKP*_Point,_Digits); // place Take Profit mrequest.symbol = _Symbol; // symbol mrequest.volume = Lot; // total lots mrequest.magic = EA_Magic; // Magic Number mrequest.type = ORDER_TYPE_BUY; // order to buy mrequest.type_filling = ORDER_FILLING_FOK; // the specified volume and for a price, // equal or better, than specified mrequest.deviation=100; // slippage OrderSend(mrequest,mresult); if(mresult.retcode==10009 || mresult.retcode==10008) // request completed or order placed { Alert("A buy order has been placed, ticket #:",mresult.order); } else { Alert("A buy order has not been placed; error:",GetLastError()); return; }
Der 5. Übergang ist für das Öffnen einer Short Position zuständig und
wird so angezeigt:
// 5th transition procedures of the «Position handling» subsystem: open a short position mrequest.action = TRADE_ACTION_DEAL; // market sell mrequest.price = NormalizeDouble(latest_price.bid,_Digits); // latest bid price mrequest.sl = NormalizeDouble(latest_price.ask + STP*_Point,_Digits); // place a Stop Loss mrequest.tp = NormalizeDouble(latest_price.ask - TKP*_Point,_Digits); // place a Take Profit mrequest.symbol = _Symbol; // symbol mrequest.volume = Lot; // lots mrequest.magic = EA_Magic; // Magic Number mrequest.type= ORDER_TYPE_SELL; // sell order mrequest.type_filling = ORDER_FILLING_FOK; // in the specified volume and for a price, // equal or better, than specified in the order mrequest.deviation=100; // slippage OrderSend(mrequest,mresult); if(mresult.retcode==10009 || mresult.retcode==10008) // request is complete or the order is placed { Alert("A sell order placed, ticket #:",mresult.order); } else { Alert("A sell order is not placed; error:",GetLastError()); return; }
Andere Übergänge in den Unterkategorien sind im Expert Advisor ganz klar nicht abgebildet, da die entsprechenden Vorgänge (Aktivierung von Stopps oder Erreichen einer Take Profit-Ebene), in MQL5 automatisch ausgeführt werden.
Das "ToWorkspace" Subsystem ist im MQL5 Code auch nicht abgebildet, da seine Aufgabe darin besteht, die Ausgabe in den Matlab Arbeitsplatz abzubilden.
Fazit
Anhand des Beispiels einer einfachen Handelsidee habe ich ein automatisches Handelssystem Simulink erzeugt, in dem ich ein Backtesting vor dem Hintergrund historischer Daten ausgeführt habe. Als erstes beschäftigte mich die Frage: "Lohnt es sich wirklich, sich mit dem ganzen Kram zu beschäftigen, wenn man doch ganz schnell ein Handelssystem mittels des MQL5 Codes implementieren kann?"
Natürlich kann man das ohne Veranschaulichung des Erzeugungsvorgangs des Systems un der Logik hinter seiner Arbeit. Doch dies eignet sich in den meisten Fällen nur für erfahrene Programmierer oder einfach von Natur begabte Menschen. Sobald sich da Handelssystems mit neuen Bedingungen und Funktionen ausweitet, dann sind das Vorhandensein des Blockdiagramms und seiner Arbeit eindeutig die Aufgabe des Händlers.
Ich möchte zudem anführen, dass ich hier nicht beabsichtigt habe, die Fähigkeiten der Simulink-Sprache in Konkurrenz zu denen der MQL5 Sprache zu stellen. Ganz im Gegenteil: ich habe nur versucht zu veranschaulichen, wie die Erzeugung eines automatischen Handelssystems mittels Block-Design geht. Vielleicht schaffen die MQL5-Entwickler in Zukunft einen visuellen Strategie-Constructor, der das Schreiben von Expert Advisors erheblich vereinfachen wird.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/155
![Fehler finden und Protokollierung](https://c.mql5.com/2/0/filter_Log_MQL5.png)
![Ein Beispiel für ein Handelssystem auf der Grundlage des Indikators Heiken-Ashi](https://c.mql5.com/2/0/Heikin_Ashi_MQL5.png)
![Wie man rasch einen Expert Advisor für den Automatisierten Trading-Wettbewerb 2010 erzeugt](https://c.mql5.com/2/0/Fast_Expert_Advisor_Writing_MQL5.png)
![Virtual Order Manager zum Verwalten von Ordern innerhalb der positionszentrischen Umgebung von MetaTrader 5](https://c.mql5.com/2/0/virtual__1.png)
![MQL5 - Sprache von Handelsstrategien, eingebaut ins Kundenterminal MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
Und wieder ein Artikel der durchaus sehr gut ist nur die Übersetzung ist halt etwas kniffelig.
Einfach alles durch ein Programm jagen ist zwar schnell aber im Bereich Computer Befehle sinnlos.
wird übersetzt in
Was natürlich nicht funktionieren kann.
Ich hoffe das ist nur übersehen worden.
Lauffähig sind die Dateien erst wenn ihr die beiden virtuellen Busse (InputBus) mit den 4 Signalen open,ema21,ema55,num
und (OutputBus) mit den anderen 10 Signalen neu erstellt. Die werden nicht in dem Simulink File mitgespeichert da es im Workspace ist .
Also erstellen und dann den Workspace speichern.
Ich konnte das Projekt erfolgreich mit Matlab 2016b erstellen und Simulieren sowie eine DLL daraus erstellen.Aber nur über den embedded coder da die Kommunikation
mit Visual Studio Fehler produziert. Ist sehr wackelig diese Kommunikation. Auf manchen Rechnern geht es glatt und VS startet mit dem geladenen Projekt mal crashed es.
Wenn ich erfolgreich eine Strategie über Simulink als Dll erstelle und die dann im MT5 einbinden kann werde ich berichten.