Aufbau von KI-gesteuerten Handelssystemen in MQL5 (Teil 2): Entwicklung eines ChatGPT-integrierten Programms mit Nutzeroberfläche
Einführung
In unserem vorherigen Artikel (Teil 1) haben wir ein JSON (JavaScript Object Notation) Parsing-Framework in MetaQuotes Language 5 (MQL5) entwickelt, das eine Klasse verwendet, die die Serialisierung und Deserialisierung von JSON-Daten mit den Interaktionen des API (Application Programming Interface) und der KI (Künstliche Intelligenz) ermöglicht. In Teil 2 erstellen wir ein in ChatGPT integriertes Programm mit einer Nutzeroberfläche, das das JSON-Framework nutzt, um Aufforderungen an die API für OpenAI zu senden und die Antworten auf einem MetaTrader 5-Chart anzuzeigen, was interaktive KI-gesteuerte Handelseinblicke ermöglicht. Wir werden die folgenden Themen behandeln:
- Verständnis des ChatGPT KI-Programmrahmens
- OpenAI API-Zugang und MetaTrader 5-Konfiguration einrichten
- Implementation in MQL5
- Testen des ChatGPT-Programms
- Schlussfolgerung
Am Ende werden Sie ein funktionierendes MQL5-Programm haben, das ChatGPT für interaktive KI-Abfragen integriert und bereit ist, Handelsstrategien zu verbessern – legen wir los!
Verständnis des ChatGPT KI-Programmrahmens
Das KI-Programm von ChatGPT, das wir entwickeln wollen, integriert die Fähigkeiten eines KI-Modells in MQL5 und ermöglicht es uns, mit der KI zu interagieren, indem wir Aufforderungen senden und Einsichten direkt auf dem Chart des MetaTrader 5 erhalten, aufbauend auf der JSON-Parsing-Grundlage, die im vorherigen Teil geschaffen wurde. Dieses Programm bietet eine nutzerfreundliche Schnittstelle für die Eingabe von Fragen und die Anzeige von KI-Antworten und ist ein praktischer Schritt in Richtung KI-gesteuerter Handelssysteme, die Märkte analysieren oder Strategien vorschlagen können.
Unser Plan ist es, ein Dashboard mit einem Eingabefeld, einer Übermittlungsschaltfläche und einem Antwortanzeigebereich zu erstellen, das es uns ermöglicht, die KI abzufragen und formatierte Antworten anzuzeigen, während gleichzeitig sichergestellt wird, dass alle Interaktionen zur Fehlersuche protokolliert werden. Wir werden das Programm so konfigurieren, dass es mit einer KI-API kommuniziert, Antworten verarbeitet und sie übersichtlich anzeigt. Damit schaffen wir die Voraussetzungen für künftige Programme, die das System mit automatisiertem Handel auf der Grundlage von KI-Erkenntnissen erweitern. Hier ist eine Visualisierung dessen, was wir erreichen wollen.

Fahren wir mit der Einrichtung der API und der MetaTrader 5-Konfiguration fort!
OpenAI API-Zugang und MetaTrader 5-Konfiguration einrichten
Damit das KI Programm von ChatGPT mit der API von OpenAI kommunizieren kann, müssen wir einen gültigen API Schlüssel erhalten und MetaTrader 5 (MT5) so konfigurieren, dass HTTP Anfragen zugelassen werden, um eine nahtlose Integration zu gewährleisten. Wir führen Sie durch die Beschaffung eines OpenAI-API-Schlüssels, die Überprüfung seiner Funktionalität mithilfe von curl-Tests und die Konfiguration des Handelsterminals, um den API-Endpunkt URL (Uniform Resource Locator) für eine zuverlässige Kommunikation zu ermöglichen.
Beziehen eines API-Schlüssels für OpenAI
Erstellen Sie ein OpenAI-Konto: Besuchen Sie „platform.openai.com“ und registrieren Sie sich oder melden Sie sich bei Ihrem Konto an. Navigieren Sie zum Abschnitt API und erstellen Sie einen neuen API-Schlüssel unter dem Dashboard „API Keys“ (API-Schlüssel). Kopieren Sie den Schlüssel (z. B. beginnend mit „sk-“) sicher, da er in der Konfiguration des Programms verwendet wird.
Anfangsinhalt:

Geheime Schlüsselgenerierung:

Sichern Sie den API-Schlüssel: Bewahren Sie den Schlüssel an einem sicheren Ort auf, da er Zugang zu den Diensten von OpenAI gewährt. Beachten Sie, dass sie den Schlüssel sperren können, falls er durchsickert.
Überprüfung des API-Schlüssels mit Curl-Tests
Um sicherzustellen, dass der API-Schlüssel funktioniert, führen wir einen Curl-Test von der Befehlszeile aus durch, um eine API-Anfrage zu simulieren:
- Curl installieren: Vergewissern Sie sich, dass curl auf Ihrem System installiert ist (standardmäßig auf den meisten Linux-/MacOS-Systemen verfügbar oder für Windows herunterladbar).
- Führen Sie eine Testanfrage aus: Öffnen Sie ein Terminal (Eingabeaufforderung unter Windows) und führen Sie den folgenden curl-Befehl aus, wobei Sie <YOUR_API_KEY> durch Ihren tatsächlichen OpenAI-API-Schlüssel ersetzen:
curl -X POST https://api.openai.com/v1/chat/completions -H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json" -d "{\"model\":\"gpt-3.5-turbo\",\"messages\":[{\"role\":\"user\",\"content\":\"Test prompt\"}],\"max_tokens\":50}"- Überprüfen Sie die Antwort: Eine erfolgreiche Antwort (HTTP 200) liefert ein JSON-Objekt mit einem Array „choices“, das die Antwort der KI enthält (z. B. „content“: „This is a test response”). Wenn die Antwort ein „Fehler“-Feld enthält (z. B. ungültiger API-Schlüssel oder Kontingent überschritten), überprüfen Sie die Gültigkeit des Schlüssels oder den Abrechnungsstatus Ihres OpenAI-Kontos. Protokollieren Sie die Antwort, um die Funktionalität zu bestätigen. Nach erfolgreicher Ausführung sollten Sie etwa folgendes Ergebnis erhalten.

Wenn Ihnen dieser Vorgang schwerfällt, können Sie eine einfache Testdatei erstellen und sie wie folgt in Ihrem Browser ausführen.
<!DOCTYPE html> <html> <head> <title>OpenAI API Test</title> <style> body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; } .container { border: 1px solid #ddd; padding: 20px; border-radius: 5px; } input, textarea, button { width: 100%; padding: 10px; margin: 5px 0; } textarea { height: 100px; } .response { background-color: #f5f5f5; padding: 15px; margin-top: 10px; } </style> </head> <body> <div class="container"> <h2>OpenAI API Test</h2> <div> <label for="apiKey">API Key:</label> <input type="text" id="apiKey" placeholder="Enter your API key"> </div> <div> <label for="prompt">Prompt:</label> <textarea id="prompt" placeholder="Enter your prompt">Test prompt</textarea> </div> <div> <label for="maxTokens">Max Tokens:</label> <input type="number" id="maxTokens" value="50"> </div> <button onclick="testAPI()">Test API</button> <div id="response" class="response" style="display: none;"> <h3>Response:</h3> <pre id="responseContent"></pre> </div> </div> <script> async function testAPI() { const apiKey = document.getElementById('apiKey').value; const prompt = document.getElementById('prompt').value; const maxTokens = document.getElementById('maxTokens').value; if (!apiKey) { alert('Please enter your API key'); return; } try { const response = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'gpt-3.5-turbo', messages: [{role: 'user', content: prompt}], max_tokens: parseInt(maxTokens) }) }); const data = await response.json(); // Display response document.getElementById('responseContent').textContent = JSON.stringify(data, null, 2); document.getElementById('response').style.display = 'block'; // Check for errors if (data.error) { console.error('API Error:', data.error); } } catch (error) { console.error('Error:', error); document.getElementById('responseContent').textContent = `Error: ${error.message}`; document.getElementById('response').style.display = 'block'; } } // Pre-fill with your API key (optional - remove if you don't want this) document.getElementById('apiKey').value = 'sk-proj-fb... <YOUR_API_KEY>... X75...'; </script> </body> </html>
Speichern Sie diese Datei als HTML (z. B. api_test.html), öffnen Sie sie in Ihrem Webbrowser, und fügen Sie Ihren Schlüssel hinzu. Sie sollten die folgende Schnittstelle haben.

Wenn wir auf „Test API“ klicken, sollten Sie die folgende Antwort erhalten.

MetaTrader 5 für die API-Kommunikation konfigurieren
Damit das Programm HTTP-Anfragen an den Endpunkt von OpenAI senden kann, konfigurieren wir das Handelsterminal wie folgt:
- Erlauben Sie Webanfragen: Öffnen Sie MT5, navigieren Sie zu Extras > Optionen > Expert Advisors und aktivieren Sie „WebRequest für folgende URLs erlauben“.
- API-Endpunkt hinzufügen: Fügen Sie im Feld „WebRequest URLs“ „https://api.openai.com“ hinzu, um Anfragen an die API von OpenAI zu ermöglichen. Dadurch wird sichergestellt, dass die Funktion WebRequest des Programms kommunizieren kann, ohne durch die Sicherheitseinstellungen von MT5 blockiert zu werden.
Hier ist die vollständige Visualisierung.

Diese Konfiguration ermöglicht es dem Programm, Aufforderungen zu senden und KI-Antworten zu empfangen, was den Weg für die Implementierung ebnet. Fahren wir mit dem Erstellen des Programms fort!
Implementation in MQL5
Um das Programm in MQL5 zu erstellen, öffnen Sie den MetaEditor, gehen zum Navigator, suchen den Ordner „Indicators“, klicken auf die Registerkarte „Neu“ und folgen Sie den Anweisungen, um die Datei zu erstellen. Sobald es erstellt ist, müssen wir in der Programmierumgebung einige globale Variablen und Eingaben deklarieren, die wir im gesamten Programm verwenden werden.
//+------------------------------------------------------------------+ //| a. ChatGPT AI EA.mq5 | //| Copyright 2025, Allan Munene Mutiiria. | //| https://t.me/Forex_Algo_Trader | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Allan Munene Mutiiria." #property link "https://t.me/Forex_Algo_Trader" #property version "1.00" #property strict //--- Input parameters input string OpenAI_Model = "gpt-3.5-turbo"; // OpenAI Model input string OpenAI_Endpoint = "https://api.openai.com/v1/chat/completions"; // OpenAI API Endpoint input int MaxResponseLength = 500; // Max length of ChatGPT response to display input string LogFileName = "ChatGPT_EA_Log.txt"; // Log file name //--- Hardcoded API key (confirmed valid via curl test) //--- Keep your API key private, for us, we added some parts so you can get the actual thing you should be having string OpenAI_API_Key = "sk-proj-vjKCf ... <YOUR FULL ACTUAL API KEY HERE> ... jY..79n...";
Wir beginnen mit der Implementierung des Programms und konzentrieren uns dabei auf die anfängliche Einrichtung der Eingabeparameter, um die Verbindung des Programms mit der API von OpenAI zu konfigurieren. Zunächst definieren wir den Eingabeparameter „OpenAI_Model“ als string mit dem Wert „gpt-3.5-turbo“, der das ChatGPT-Modell angibt, das für API-Anfragen verwendet werden soll, sodass bei Bedarf flexibel zwischen den Modellen gewechselt werden kann. Hier können Sie zu Ihrem Modell wechseln. Dann setzen Sie „OpenAI_Endpoint“ auf „https://api.openai.com/v1/chat/completions“ definieren damit die URL für das Senden von HTTP-POST-Anfragen an die API von OpenAI.
Als Nächstes konfigurieren wir „MaxResponseLength“ als Ganzzahl auf 500, um die Anzeigelänge der ChatGPT-Antwort zu begrenzen und eine überschaubare Ausgabe auf dem MetaTrader 5-Chart zu gewährleisten. Zuletzt definieren wir „LogFileName“ als „ChatGPT_EA_Log.txt“, um die Datei für die Protokollierung von API-Interaktionen und Fehlern festzulegen, und codieren „OpenAI_API_Key“ mit unserem bestätigten gültigen API-Schlüssel (beginnend mit „sk-proj-“) für die Authentifizierung von Anfragen, was die Grundlage für die Funktionalität des Programms bildet. Für die Protokollierung und die Visualisierung der Schaltflächen fügen wir die folgenden Variablen hinzu.
//--- Global variables string conversationHistory = ""; //--- Stores conversation history int logFileHandle = INVALID_HANDLE; //--- Handle for log file bool button_hover = false; //--- Flag for button hover state color button_original_bg = clrRoyalBlue; //--- Original button background color color button_darker_bg; //--- Darkened button background for hover
Wir haben die Variablen mit Kommentaren versehen, damit sie selbsterklärend sind. Als Nächstes müssen wir Hilfsfunktionen definieren, die wir zur Erstellung der Schnittstelle verwenden werden. Stellen Sie jedoch zunächst sicher, dass Sie die JSON-Logik aus dem vorherigen Teil kopieren und einfügen, da wir nicht mehrere Dateien erstellen wollen. Sie könnten die Datei auch erstellen und in das Programm einbinden, aber das werden wir tun, wenn das Programm komplexer wird. Lassen Sie uns erst einmal alles einfach und unkompliziert halten.
//+------------------------------------------------------------------+ //| Creates a rectangle label object | //+------------------------------------------------------------------+ bool createRecLabel(string objName, int xDistance, int yDistance, int xSize, int ySize, color bgColor, int borderWidth, color borderColor = clrNONE, ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID, ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER) { //--- Create rectangle label ResetLastError(); //--- Reset previous errors if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { //--- Attempt creation Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError); //--- Print error return (false); //--- Return failure } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance); //--- Set x distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance); //--- Set y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xSize); //--- Set width ObjectSetInteger(0, objName, OBJPROP_YSIZE, ySize); //--- Set height ObjectSetInteger(0, objName, OBJPROP_CORNER, corner); //--- Set corner ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, bgColor); //--- Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); //--- Set border type ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); //--- Set border style ObjectSetInteger(0, objName, OBJPROP_WIDTH, borderWidth); //--- Set border width ObjectSetInteger(0, objName, OBJPROP_COLOR, borderColor); //--- Set border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); //--- Not background ObjectSetInteger(0, objName, OBJPROP_STATE, false); //--- Not pressed ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); //--- Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); //--- Not selected ChartRedraw(0); //--- Redraw chart return (true); //--- Success } //+------------------------------------------------------------------+ //| Creates a button object | //+------------------------------------------------------------------+ bool createButton(string objName, int xDistance, int yDistance, int xSize, int ySize, string text = "", color textColor = clrBlack, int fontSize = 12, color bgColor = clrNONE, color borderColor = clrNONE, string font = "Arial Rounded MT Bold", ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER, bool isBack = false) { //--- Create button ResetLastError(); //--- Reset errors if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { //--- Attempt creation Print(__FUNCTION__, ": failed to create the button! Error code = ", _LastError); //--- Print error return (false); //--- Failure } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance); //--- Set x distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance); //--- Set y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xSize); //--- Set width ObjectSetInteger(0, objName, OBJPROP_YSIZE, ySize); //--- Set height ObjectSetInteger(0, objName, OBJPROP_CORNER, corner); //--- Set corner ObjectSetString(0, objName, OBJPROP_TEXT, text); //--- Set text ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); //--- Set text color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); //--- Set font size ObjectSetString(0, objName, OBJPROP_FONT, font); //--- Set font ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, bgColor); //--- Set background ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, borderColor); //--- Set border color ObjectSetInteger(0, objName, OBJPROP_BACK, isBack); //--- Set back ObjectSetInteger(0, objName, OBJPROP_STATE, false); //--- Not pressed ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); //--- Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); //--- Not selected ChartRedraw(0); //--- Redraw return (true); //--- Success } //+------------------------------------------------------------------+ //| Creates an edit field object | //+------------------------------------------------------------------+ bool createEdit(string objName, int xDistance, int yDistance, int xSize, int ySize, string text = "", color textColor = clrBlack, int fontSize = 12, color bgColor = clrNONE, color borderColor = clrNONE, string font = "Arial Rounded MT Bold", ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER, int align = ALIGN_LEFT, bool readOnly = false) { //--- Create edit ResetLastError(); //--- Reset errors if (!ObjectCreate(0, objName, OBJ_EDIT, 0, 0, 0)) { //--- Attempt creation Print(__FUNCTION__, ": failed to create the edit! Error code = ", _LastError); //--- Print error return (false); //--- Failure } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance); //--- Set x distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance); //--- Set y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xSize); //--- Set width ObjectSetInteger(0, objName, OBJPROP_YSIZE, ySize); //--- Set height ObjectSetInteger(0, objName, OBJPROP_CORNER, corner); //--- Set corner ObjectSetString(0, objName, OBJPROP_TEXT, text); //--- Set text ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); //--- Set text color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); //--- Set font size ObjectSetString(0, objName, OBJPROP_FONT, font); //--- Set font ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, bgColor); //--- Set background ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, borderColor); //--- Set border color ObjectSetInteger(0, objName, OBJPROP_ALIGN, align); //--- Set alignment ObjectSetInteger(0, objName, OBJPROP_READONLY, readOnly); //--- Set read-only ObjectSetInteger(0, objName, OBJPROP_BACK, false); //--- Not back ObjectSetInteger(0, objName, OBJPROP_STATE, false); //--- Not active ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); //--- Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); //--- Not selected ChartRedraw(0); //--- Redraw return (true); //--- Success } //+------------------------------------------------------------------+ //| Creates a text label object | //+------------------------------------------------------------------+ bool createLabel(string objName, int xDistance, int yDistance, string text, color textColor = clrBlack, int fontSize = 12, string font = "Arial Rounded MT Bold", ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER, ENUM_ANCHOR_POINT anchor = ANCHOR_LEFT_UPPER) { //--- Create label ResetLastError(); //--- Reset errors if (!ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0)) { //--- Attempt creation Print(__FUNCTION__, ": failed to create the label! Error code = ", _LastError); //--- Print error return (false); //--- Failure } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance); //--- Set x distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance); //--- Set y distance ObjectSetInteger(0, objName, OBJPROP_CORNER, corner); //--- Set corner ObjectSetString(0, objName, OBJPROP_TEXT, text); //--- Set text ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); //--- Set color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); //--- Set font size ObjectSetString(0, objName, OBJPROP_FONT, font); //--- Set font ObjectSetInteger(0, objName, OBJPROP_BACK, false); //--- Not back ObjectSetInteger(0, objName, OBJPROP_STATE, false); //--- Not active ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); //--- Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); //--- Not selected ObjectSetInteger(0, objName, OBJPROP_ANCHOR, anchor); //--- Set anchor ChartRedraw(0); //--- Redraw return (true); //--- Success }
Hier schaffen wir wesentliche grafische Elemente für die Interaktion des Händlers auf dem Chart. Zunächst implementieren wir die Funktion „createRecLabel“, die ObjectCreate verwendet, um eine rechteckige Kennzeichnung (OBJ_RECTANGLE_LABEL) an den angegebenen Koordinaten („xDistance“, „yDistance“) mit den Abmessungen („xSize“, „ySize“) zeichnet und dabei Eigenschaften wie Hintergrundfarbe (OBJPROP_BGCOLOR), Randbreite, Farbe („OBJPROP_COLOR“), Typ („OBJPROP_BORDER_TYPE“ als „BORDER_FLAT“), Stil („OBJPROP_STYLE“ als „STYLE_SOLID“) und die Ecke („OBJPROP_CORNER“ als „CORNER_LEFT_UPPER“) über ObjectSetInteger, wobei eine nicht auswählbare Anzeige ohne Hintergrund mit ChartRedraw zur Aktualisierung gewährleistet wird und false zurückgegeben wird, wenn die Erstellung fehlschlägt und ein Fehler über die Funktion Print protokolliert wird.
Dann entwickeln wir die Funktion „createButton“, die eine Schaltfläche (OBJ_BUTTON) mit ähnlichen Koordinaten- und Größenparametern erzeugt und Text (OBJPROP_TEXT), Textfarbe („OBJPROP_COLOR“), Schriftgröße, Schriftart (Arial Rounded MT Bold), Hintergrundfarbe, Rahmenfarbe und Ecke über ObjectSetString und „ObjectSetInteger“, mit einem optionalen Hintergrundflag („isBack“), festlegt, einen nicht auswählbaren, nicht gedrückten Zustand sicherstellt und das Chart auffrischt, wobei im Fehlerfall false zurückgegeben wird und eine Fehlerprotokollierung erfolgt.
Als Nächstes implementieren wir die Funktion „createEdit“, die ein editierbares Textfeld (OBJ_EDIT) mit Parametern für Text, Ausrichtung („OBJPROP_ALIGN“ als „ALIGN_LEFT“), Nur-Lese-Status und ähnlichen Styling-Eigenschaften erstellt, wobei wir „ObjectSetString“ und „ObjectSetInteger“ verwenden, um Aussehen und Verhalten zu konfigurieren, das Chart zu aktualisieren und Fehler ähnlich zu behandeln. Zuletzt fügen wir die Funktion „createLabel“ hinzu, die eine Textbeschriftung (OBJ_LABEL) mit dem angegebenen Text, der Farbe, der Schriftgröße, der Schriftart, der Ecke und dem Ankerpunkt („OBJPROP_ANCHOR“ als „ANCHOR_LEFT_UPPER“) erstellt und eine nicht auswählbare Anzeige und Aktualisierung des Charts gewährleistet. Diese Funktionen bilden die grafische Grundlage für das Dashboard unseres Programms und ermöglichen die Eingabe und Anzeige von KI-Interaktionen. Wir möchten eine schwebende Schaltfläche haben, die sich beim Schweben verdunkelt. Dafür brauchen wir eine Funktion.
//+------------------------------------------------------------------+ //| Darkens a given color by a factor | //+------------------------------------------------------------------+ color DarkenColor(color colorValue, double factor = 0.8) { //--- Darken color function int red = int((colorValue & 0xFF) * factor); //--- Calculate darkened red component int green = int(((colorValue >> 8) & 0xFF) * factor); //--- Calculate darkened green component int blue = int(((colorValue >> 16) & 0xFF) * factor); //--- Calculate darkened blue component return (color)(red | (green << 8) | (blue << 16)); //--- Combine and return darkened color }
Hier implementieren wir die Funktion „DarkenColor“, die einen Farbwert und einen optionalen Faktor (Standard 0.8) nimmt, um die Helligkeit zu reduzieren, und die verdunkelten Rot-, Grün- und Blaukomponenten berechnet, indem sie jede mit bitweisen Operationen extrahiert („colorValue & 0xFF“ für Rot, „(colorValue >> 8) & 0xFF“ für Grün, „(colorValue >> 16) & 0xFF“ für Blau), Multiplikation mit dem Faktor und Kombination mit bitweisen Verschiebungen („red | (green << 8) | (blue << 16)“), um die neue Farbe zu erhalten. Lassen Sie uns nun das Dashboard erstellen, damit wir bereit sind.
//+------------------------------------------------------------------+ //| Creates the dashboard UI | //+------------------------------------------------------------------+ void CreateDashboard() { //--- Create UI createEdit("ChatGPT_InputEdit", 20, 20, 400, 40, "", clrBlack, 12, clrWhiteSmoke, clrDarkGray, "Arial", CORNER_LEFT_UPPER, ALIGN_LEFT, false); //--- Input edit createButton("ChatGPT_SubmitButton", 430, 20, 100, 40, "Send", clrWhite, 12, button_original_bg, clrDarkBlue, "Arial", CORNER_LEFT_UPPER, false); //--- Submit button createRecLabel("ChatGPT_ResponseBg", 20, 70, 510, 300, clrWhite, 2, clrLightGray, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER); //--- Response background ChartRedraw(); //--- Redraw }
Wir fahren fort, indem wir die Funktion „CreateDashboard“ implementieren, um die Nutzeroberfläche auf dem Chart zu erstellen, die es uns ermöglicht, mit der KI zu interagieren. Zuerst rufen wir „createEdit“ auf, um ein editierbares Textfeld mit dem Namen „ChatGPT_InputEdit“ an den Koordinaten (20, 20) mit einer Größe von 400x40 Pixeln zu erstellen. Dabei verwenden wir schwarzen Text, die Schriftart Arial (12 Punkt), einen weißen rauchfarbenen Hintergrund, einen dunkelgrauen Rand, linksbündig und nicht schreibgeschützt, damit wir Aufforderungen eingeben können. Dann fügen wir mit „createButton“ einen „ChatGPT_SubmitButton“ an der Position (430, 20) mit den Maßen 100x40 Pixel hinzu, der mit „Send“ in weißem Text, 12-Punkt-Arial-Schrift, mit königsblauem Hintergrund („button_original_bg“) und dunkelblauem Rand beschriftet ist und in der linken oberen Ecke positioniert wird, um API-Anfragen auszulösen.
Als Nächstes rufen wir „createRecLabel“ auf, um einen Antwort-Hintergrund namens „ChatGPT_ResponseBg“ bei (20, 70) mit den Abmessungen 510x300 Pixel zu erstellen, der einen weißen Hintergrund, einen 2-Pixel-hellgrauen Rand, einen flachen Rahmentyp und einen einfarbigen Stil aufweist und einen klaren Bereich für die Anzeige von AI-Antworten bietet. Zuletzt rufen wir ChartRedraw auf, um das Chart zu aktualisieren und sicherzustellen, dass alle UI-Elemente sichtbar sind. Wir können nun die Funktion in OnInit aufrufen, um zu sehen, was wir erreicht haben.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Initialization button_darker_bg = DarkenColor(button_original_bg); //--- Set darker button color logFileHandle = FileOpen(LogFileName, FILE_READ | FILE_WRITE | FILE_TXT); //--- Open log if(logFileHandle == INVALID_HANDLE) { //--- Check handle Print("Failed to open log file: ", GetLastError()); //--- Print error return(INIT_FAILED); //--- Fail init } FileSeek(logFileHandle, 0, SEEK_END); //--- Seek end FileWriteString(logFileHandle, "EA Initialized at " + TimeToString(TimeCurrent()) + "\n"); //--- Log init CreateDashboard(); //--- Create UI return(INIT_SUCCEEDED); //--- Success } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Deinit ObjectsDeleteAll(0, "ChatGPT_"); //--- Delete objects if(logFileHandle != INVALID_HANDLE) { //--- Check handle FileClose(logFileHandle); //--- Close log } }
In OnInit rufen wir „DarkenColor“ mit „button_original_bg“ auf, um einen dunkleren Farbton für „button_darker_bg“ zu berechnen und einen Hover-Effekt für die Schaltfläche „Submit“ zu ermöglichen. Dann öffnen wir die durch „LogFileName“ angegebene Protokolldatei mit FileOpen mit Lese-, Schreib- und Textflags und speichern das Handle in „logFileHandle“; wenn das Handle ungültig ist, protokollieren wir einen Fehler und geben INIT_FAILED zurück. Auf diese Weise wird sichergestellt, dass wir den Überblick darüber behalten, was wir im Hintergrund tun.
Als Nächstes verwenden wir „FileSeek“, um an das Ende der Protokolldatei zu gelangen und mit FileWriteString eine Initialisierungsmeldung zu schreiben, die über die Funktion TimeToString auch die aktuelle Uhrzeit enthält. Anschließend rufen wir „CreateDashboard“ auf, um die Nutzeroberfläche einzurichten und geben INIT_SUCCEEDED zurück, um die erfolgreiche Initialisierung zu bestätigen. In der Funktion OnDeinit wird aufgeräumt, indem ObjectsDeleteAll aufgerufen wird, um alle Chart-Objekte mit dem Präfix „ChatGPT_“ zu entfernen, und wenn „logFileHandle“ gültig ist, wird die Protokolldatei mit der Funktion FileClose geschlossen. Während der gesamten Zeit, in der das Programm an das Chart angehängt ist, lassen wir die Datei geöffnet. Das bedeutet, dass wir sie nicht öffnen können, während das Programm läuft. Sie können die Datei nach dem Senden und dem Erhalt einer Antwort schließen, aber das ist nicht praktisch, wenn wir aktiv interagieren. Wenn wir das Programm ausführen, erhalten wir folgendes Ergebnis.

Nun, da wir die Schnittstelle erstellt haben, müssen wir einen Weg finden, die Konversation als normalen Chat anzuzeigen, aber das Problem ist, dass MQL5 keine direkte Möglichkeit bietet, dies zu erreichen. Die Kennzeichnungsfunktion allein kann einen Text mit bis zu 63 Zeichen anzeigen. Wir müssen also einen Textumbruch-Mechanismus implementieren, der die Konversation in mehrere Kennzeichen (labels) aufteilt und die maximale Anzahl der Zeichen in einer bestimmten Bezeichnung auf die Höchstgrenze beschränkt. Hier ist die Logik, die wir dafür anwenden.
//+------------------------------------------------------------------+ //| Wraps text respecting newlines and max width | //+------------------------------------------------------------------+ void WrapText(const string inputText, const string font, const int fontSize, const int maxWidth, string &wrappedLines[], int offset = 0) { //--- Wrap text function const int maxChars = 60; //--- Max chars per line ArrayResize(wrappedLines, 0); //--- Clear output array TextSetFont(font, -fontSize * 10, 0); //--- Set font for measurement string paragraphs[]; //--- Array for paragraphs int numParagraphs = StringSplit(inputText, '\n', paragraphs); //--- Split by newline for (int p = 0; p < numParagraphs; p++) { //--- Loop paragraphs string para = paragraphs[p]; //--- Get paragraph if (StringLen(para) == 0) continue; //--- Skip empty string words[]; //--- Array for words int numWords = StringSplit(para, ' ', words); //--- Split by space string currentLine = ""; //--- Current line builder for (int w = 0; w < numWords; w++) { //--- Loop words string testLine = currentLine + (StringLen(currentLine) > 0 ? " " : "") + words[w]; //--- Test add word uint wid, hei; //--- Width, height TextGetSize(testLine, wid, hei); //--- Get size int textWidth = (int)wid; //--- Cast width if (textWidth + offset <= maxWidth && StringLen(testLine) <= maxChars) { //--- Fits currentLine = testLine; //--- Update line } else { //--- Doesn't fit if (StringLen(currentLine) > 0) { //--- Add current if not empty int size = ArraySize(wrappedLines); //--- Get size ArrayResize(wrappedLines, size + 1); //--- Resize wrappedLines[size] = currentLine; //--- Add line } currentLine = words[w]; //--- Start new with word TextGetSize(currentLine, wid, hei); //--- Get size textWidth = (int)wid; //--- Cast if (textWidth + offset > maxWidth || StringLen(currentLine) > maxChars) { //--- Word too long string wrappedWord = ""; //--- Word builder for (int c = 0; c < StringLen(words[w]); c++) { //--- Char loop string testWord = wrappedWord + StringSubstr(words[w], c, 1); //--- Test add char TextGetSize(testWord, wid, hei); //--- Get size int wordWidth = (int)wid; //--- Cast if (wordWidth + offset > maxWidth || StringLen(testWord) > maxChars) { //--- Char exceeds if (StringLen(wrappedWord) > 0) { //--- Add if not empty int size = ArraySize(wrappedLines); //--- Get size ArrayResize(wrappedLines, size + 1); //--- Resize wrappedLines[size] = wrappedWord; //--- Add } wrappedWord = StringSubstr(words[w], c, 1); //--- New with char } else { //--- Fits wrappedWord = testWord; //--- Update } } currentLine = wrappedWord; //--- Set current } } } if (StringLen(currentLine) > 0) { //--- Add remaining line int size = ArraySize(wrappedLines); //--- Get size ArrayResize(wrappedLines, size + 1); //--- Resize wrappedLines[size] = currentLine; //--- Add } } }
Wir implementieren die Funktion „WrapText“, um den Text für die Anzeige im Chart zu formatieren, um lesbare AI-Antworten zu gewährleisten. Zuerst definieren wir eine Konstante „maxChars“, die auf 60 gesetzt wird, um die Zeilenlänge zu begrenzen, aber man könnte sie auch auf 63 begrenzen, das Ausgabe-Array „wrappedLines“ mit ArrayResize leeren und die Schriftart mit TextSetFont mit der angegebenen Schriftart und -größe (skaliert mit -fontSize * 10) setzen. Anschließend wird der Eingabetext mithilfe von StringSplit mit Newline als Trennzeichen in Absätze aufgeteilt, und jeder nicht leere Absatz wird durchlaufen. Jeder Absatz wird mit „StringSplit“ in Wörter aufgeteilt, diesmal jedoch mit einem Leerzeichen als Trennzeichen, wobei eine leere Variable „currentLine“ initialisiert wird.
Als Nächstes gehen wir in einer Schleife durch die Wörter und erstellen eine „testLine“, indem wir jedes Wort mit einem Leerzeichen anhängen, wenn „currentLine“ nicht leer ist, und messen mit TextGetSize die Breite; wenn die Breite plus Offset innerhalb von „maxWidth“ und die Länge unter „maxChars“ liegt, aktualisieren wir „currentLine“, andernfalls fügen wir „currentLine“ zu „wrappedLines“ hinzu, wenn es nicht leer ist, und beginnen eine neue Zeile mit dem aktuellen Wort. Bei übergroßen Wörtern werden die Zeichen durchlaufen, wobei „wrappedWord“ gebildet und seine Breite überprüft wird. Wenn es „maxWidth“ oder „maxChars“ überschreitet, wird es zu „wrappedLines“ hinzugefügt und „currentLine“ mit dem verbleibenden Wort aktualisiert. Abschließend fügen wir alle verbleibenden „currentLine“ zu „wrappedLines“ hinzu und stellen so sicher, dass der gesamte Text für eine übersichtliche Anzeige im Antwortbereich des Programms formatiert ist. Wir können nun diese Logik verwenden und unsere Anzeige mit einem Beispieltext aktualisieren, der dem Nutzer sagt, wie er beginnen soll.
//+------------------------------------------------------------------+ //| Updates the response display | //+------------------------------------------------------------------+ void UpdateResponseDisplay() { //--- Update display int total = ObjectsTotal(0, 0, -1); //--- Get total objects for (int j = total - 1; j >= 0; j--) { //--- Loop backwards string name = ObjectName(0, j, 0, -1); //--- Get name if (StringFind(name, "ChatGPT_ResponseLine_") == 0 || StringFind(name, "ChatGPT_MessageBg_") == 0) { //--- Check prefix ObjectDelete(0, name); //--- Delete } } string displayText = conversationHistory; //--- Get history if (displayText == "") { //--- If empty string objName = "ChatGPT_ResponseLine_0"; //--- Name createLabel(objName, 30, 80, "Type your message above and click Send to chat with the AI.", clrGray, 10, "Arial", CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER); //--- Placeholder ChartRedraw(); //--- Redraw return; //--- Exit } string font = "Arial"; //--- Font int fontSize = 10; //--- Size int padding = 10; //--- Padding int maxWidth = 510 - 2 * padding; //--- Max width string wrappedLines[]; //--- Wrapped lines WrapText(displayText, font, fontSize, maxWidth, wrappedLines, 0); //--- Wrap text TextSetFont(font, -fontSize * 10, 0); //--- Set font uint wid, hei; //--- Size vars TextGetSize("A", wid, hei); //--- Get height int lineHeight = (int)hei; //--- Line height int responseHeight = 300; //--- Response height int maxVisibleLines = (responseHeight - 2 * padding) / lineHeight; //--- Max lines int numLines = ArraySize(wrappedLines); //--- Num lines int startLine = MathMax(0, numLines - maxVisibleLines); //--- Start line int textX = 20 + padding; //--- Text x int textY = 70 + padding; //--- Text y color currentColor = clrWhite; //--- Current color for (int i = startLine; i < numLines; i++) { //--- Loop lines string line = wrappedLines[i]; //--- Get line if (StringFind(line, "You: ") == 0) { //--- User color currentColor = clrGray; //--- Set gray } else if (StringFind(line, "AI: ") == 0) { //--- AI color currentColor = clrBlue; //--- Set blue } string objName = "ChatGPT_ResponseLine_" + IntegerToString(i - startLine); //--- Name createLabel(objName, textX, textY, line, currentColor, fontSize, font, CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER); //--- Create label textY += lineHeight; //--- Next y } ChartRedraw(); //--- Redraw }
Hier implementieren wir die Funktion „UpdateResponseDisplay“, um den Gesprächsverlauf dynamisch darzustellen. Zunächst zählen wir mit ObjectsTotal alle Chart-Objekte und iterieren rückwärts, indem wir ObjectName aufrufen, um den Namen jedes Objekts abzurufen, und ObjectDelete, um Objekte mit den Präfixen „ChatGPT_ResponseLine_“ oder „ChatGPT_MessageBg_“ zu entfernen, wodurch die vorherigen Anzeigeelemente gelöscht werden. Dann prüfen wir, ob „conversationHistory“ leer ist; wenn ja, rufen wir „createLabel“ auf, um eine Platzhalter-Nachricht bei (30, 80) in grauer 10-Punkt-Arial-Schrift anzuzeigen und den Nutzer aufzufordern, eine Nachricht einzugeben, und verlassen die Seite nach dem erneuten Zeichnen.
Wenn „conversationHistory“ Text enthält, setzen wir die Schriftart auf Arial, die Schriftgröße auf 10, die Auffüllung auf 10 und berechnen „maxWidth“ als 510 minus das Doppelte der Auffüllung, dann rufen wir „WrapText“ auf, um den Text in „wrappedLines“ zu teilen. Als Nächstes bestimmen wir mit TextSetFont und TextGetSize die Zeilenhöhe, berechnen „maxVisibleLines“ auf der Grundlage einer Antworthöhe von 300 Pixeln und setzen die Startzeile mit MathMax, um die neuesten Zeilen anzuzeigen. Wir iterieren durch „wrappedLines“ ab „startLine“, setzen „currentColor“ auf grau für Zeilen, die mit „You: “ beginnen, oder blau für „AI: „, und rufen „createLabel“ auf, um jede Zeile an den Koordinaten („textX“, „textY“) anzuzeigen, wobei „textY“ um „lineHeight“ für jede Beschriftung erhöht wird, mit eindeutigen Namen wie „ChatGPT_ResponseLine_0“. Schließlich rufen wir ChartRedraw auf, um das Chart zu aktualisieren und sicherzustellen, dass die Konversation visuell klar und deutlich erkennbar ist. Wenn wir diese Funktion bei der Initialisierung aufrufen, erhalten wir folgendes Ergebnis.

Aus dem Bild geht hervor, dass wir die Anzeige mit einer ersten Nachricht aktualisiert haben und bereit sind, Unterhaltungen zu rendern. Erstellen wir nun eine Funktion, um die Antwort auf die von uns gesendeten Prompts zu erhalten, aber zuerst müssen wir Hilfsfunktionen definieren, die wir benötigen.
//+------------------------------------------------------------------+ //| Escapes string for JSON | //+------------------------------------------------------------------+ string JsonEscape(string value) { //--- JSON escape StringReplace(value, "\\", "\\\\"); //--- Escape backslash StringReplace(value, "\"", "\\\""); //--- Escape quote StringReplace(value, "\n", "\\n"); //--- Escape newline StringReplace(value, "\r", "\\r"); //--- Escape carriage StringReplace(value, "\t", "\\t"); //--- Escape tab StringReplace(value, "\f", "\\f"); //--- Escape form feed for(int i = 0; i < StringLen(value); i++) { //--- Loop chars ushort charCode = StringGetCharacter(value, i); //--- Get char if(charCode < 32 || charCode == 127) { //--- Control chars string hex = StringFormat("\\u%04x", charCode); //--- Hex escape string before = StringSubstr(value, 0, i); //--- Before part string after = StringSubstr(value, i + 1); //--- After part value = before + hex + after; //--- Replace i += 5; //--- Skip added } } return value; //--- Return escaped } //+------------------------------------------------------------------+ //| Logs char array as hex for debugging | //+------------------------------------------------------------------+ string LogCharArray(char &data[]) { //--- Log char array string result = ""; //--- Result string for(int i = 0; i < ArraySize(data); i++) { //--- Loop array result += StringFormat("%02X ", data[i]); //--- Append hex } return result; //--- Return }
Bevor wir die eigentliche Logik für die Verarbeitung von Antworten erstellen, implementieren wir Hilfsprogramme für das Escaping von JSON-Zeichenfolgen und das Debugging für eine stabile API-Kommunikation. Zunächst implementieren wir die Funktion „JsonEscape“, die eine Zeichenketteneingabe annimmt und Sonderzeichen für die JSON-Konformität mithilfe von StringReplace umwandelt, um Backslash in „\\“, Anführungszeichen in „\"“, Zeilenumbruch in „\n“, Wagenrücklauf in „\r“, Tabulator in „\t“ und Seitenvorschub in „\f“ umwandelt; anschließend durchläuft sie jedes Zeichen mit StringGetCharacter und prüft auf Steuerzeichen (ASCII < 32 oder 127), ersetzt sie mit einer Unicode-Escape-Sequenz („\uXXXX“) über StringFormat, aktualisiert die Zeichenfolge mit StringSubstr, um Teile vor und nach der Escape-Sequenz zu verketten, und passt den Index an, um hinzugefügte Zeichen zu überspringen, und gibt die „escapte“ Zeichenfolge zurück.
Dann entwickeln wir die Funktion „LogCharArray“, die ein Zeichenarray in eine hexadezimale Zeichenkette für die Fehlersuche konvertiert, indem sie eine leere Ergebniszeichenkette initialisiert, das Array mit ArraySize durchläuft, den Hex-Wert jedes Zeichens mit „StringFormat“ im Format „%02X“ anhängt und die formatierte Zeichenkette zurückgibt. Diese Funktionen gewährleisten die korrekte Formatierung von Eingabeaufforderungen für OpenAI-API-Anfragen und bieten Debugging-Unterstützung für die Überprüfung von API-Daten. Wir können sie nun verwenden, um die Basisfunktion zum Abrufen der Antworten zu erstellen.
//+------------------------------------------------------------------+ //| Gets ChatGPT response via API | //+------------------------------------------------------------------+ string GetChatGPTResponse(string prompt) { //--- Get AI response string escapedPrompt = JsonEscape(prompt); //--- Escape prompt string requestData = "{\"model\":\"" + OpenAI_Model + "\",\"messages\":[{\"role\":\"user\",\"content\":\"" + escapedPrompt + "\"}],\"max_tokens\":500}"; //--- Build JSON FileWriteString(logFileHandle, "Request Data: " + requestData + "\n"); //--- Log data char postData[]; //--- Post array int dataLen = StringToCharArray(requestData, postData, 0, WHOLE_ARRAY, CP_UTF8); //--- To char array ArrayResize(postData, dataLen - 1); //--- Remove terminator FileWriteString(logFileHandle, "Raw Post Data (Hex): " + LogCharArray(postData) + "\n"); //--- Log hex string headers = "Authorization: Bearer " + OpenAI_API_Key + "\r\n" + //--- Build headers "Content-Type: application/json; charset=UTF-8\r\n" + "Content-Length: " + IntegerToString(dataLen - 1) + "\r\n\r\n"; FileWriteString(logFileHandle, "Request Headers: " + headers + "\n"); //--- Log headers char result[]; //--- Result array string resultHeaders; //--- Result headers int res = WebRequest("POST", OpenAI_Endpoint, headers, 10000, postData, result, resultHeaders); //--- Send request if(res != 200) { //--- Check status string response = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8); //--- To string string errMsg = "API request failed: HTTP Code " + IntegerToString(res) + ", Error: " + IntegerToString(GetLastError()) + ", Response: " + response; //--- Error msg Print(errMsg); //--- Print FileWriteString(logFileHandle, errMsg + "\n"); //--- Log FileWriteString(logFileHandle, "Raw Response Data (Hex): " + LogCharArray(result) + "\n"); //--- Log hex return errMsg; //--- Return error } string response = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8); //--- To string FileWriteString(logFileHandle, "API Response: " + response + "\n"); //--- Log response JsonValue jsonObject; //--- JSON object int index = 0; //--- Index char charArray[]; //--- Char array int arrayLength = StringToCharArray(response, charArray, 0, WHOLE_ARRAY, CP_UTF8); //--- To char if(!jsonObject.DeserializeFromArray(charArray, arrayLength, index)) { //--- Deserialize string errMsg = "Error: Failed to parse API response JSON: " + response; //--- Error Print(errMsg); //--- Print FileWriteString(logFileHandle, errMsg + "\n"); //--- Log return errMsg; //--- Return } JsonValue *error = jsonObject.FindChildByKey("error"); //--- Find error if(error != NULL) { //--- If error string errMsg = "API Error: " + error["message"].ToString(); //--- Get message Print(errMsg); //--- Print FileWriteString(logFileHandle, errMsg + "\n"); //--- Log return errMsg; //--- Return } string content = jsonObject["choices"][0]["message"]["content"].ToString(); //--- Get content if(StringLen(content) > 0) { //--- If content StringReplace(content, "\\n", "\n"); //--- Replace escapes StringTrimLeft(content); //--- Trim left StringTrimRight(content); //--- Trim right return content; //--- Return } string errMsg = "Error: No content in API response: " + response; //--- Error Print(errMsg); //--- Print FileWriteString(logFileHandle, errMsg + "\n"); //--- Log return errMsg; //--- Return }
Wir schließen die Kernfunktionalität unseres Programms ab, indem wir die Funktion „GetChatGPTResponse“ implementieren, um die API-Kommunikation mit OpenAIs ChatGPT abzuwickeln. Zunächst rufen wir „JsonEscape“ auf, um die Eingabeaufforderung zu entschlüsseln und die JSON-Kompatibilität zu gewährleisten, und erstellen eine JSON-Zeichenfolge „requestData“ mit „OpenAI_Model“, einem Nutzernachrichten-Array, das die entschlüsselte Aufforderung enthält, und „max_tokens“, das auf 500 gesetzt ist, und protokollieren es mit FileWriteString in „logFileHandle“. Dann konvertieren wir „requestData“ in ein Zeichenarray „postData“ mit StringToCharArray mit UTF-8 Kodierung, ändern die Größe mit ArrayResize, um den Nullterminator zu entfernen, und protokollieren die Hex-Darstellung mit der Funktion „LogCharArray“.
Als Nächstes erstellen wir HTTP-Header mit dem „OpenAI_API_Key“, dem Inhaltstyp und der Inhaltslänge, protokollieren sie und senden eine POST-Anfrage an „OpenAI_Endpoint“ unter Verwendung von WebRequest mit einer 10-Sekunden-Zeitüberschreitung und speichern die Antwort in „result“. Wenn der Antwortcode nicht 200 ist, wandeln wir „result“ mit CharArrayToString in einen String um, protokollieren einen Fehler mit „Print“ und „FileWriteString“ und geben die Fehlermeldung zurück. Andernfalls wird die Antwort mit „DeserializeFromArray“ in ein „JsonValue“-Objekt geparst, nachdem sie in ein Zeichenarray konvertiert wurde; schlägt das Parsen fehl, wird ein Fehler protokolliert und zurückgegeben. Wir suchen mit „FindChildByKey“ nach einem „Fehler“-Schlüssel, protokollieren ihn und geben eine eventuelle Fehlermeldung zurück.
Schließlich extrahieren wir die AI-Antwort aus „jsonObject['choices'][0]['message']['content']“ mithilfe von „ToString“, ersetzen Zeilenumbrüche mit StringReplace, trimmen Leerzeichen mit StringTrimLeft und StringTrimRight und geben den Inhalt zurück, wenn er nicht leer ist, andernfalls wird ein Fehler protokolliert und zurückgegeben. Mit dieser Funktion kann das Programm nun ChatGPT abfragen und die Antworten zur Anzeige verarbeiten. Jetzt müssen wir nur noch unsere erste Nachricht senden. Wir müssen die Ereignisbehandlung durch OnChartEvent implementieren, um auf die von uns vorgenommenen Bearbeitungen zu reagieren.
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Event handler if(id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_SubmitButton") { //--- Button click string prompt = (string)ObjectGetString(0, "ChatGPT_InputEdit", OBJPROP_TEXT); //--- Get input if(StringLen(prompt) > 0) { //--- If not empty string response = GetChatGPTResponse(prompt); //--- Get AI response Print("User: " + prompt); //--- Print user Print("AI: " + response); //--- Print AI conversationHistory += "You: " + prompt + "\nAI: " + response + "\n\n"; //--- Append history ObjectSetString(0, "ChatGPT_InputEdit", OBJPROP_TEXT, ""); //--- Clear input UpdateResponseDisplay(); //--- Update display FileWriteString(logFileHandle, "Prompt: " + prompt + " | Response: " + response + " | Time: " + TimeToString(TimeCurrent()) + "\n"); //--- Log ChartRedraw(); //--- Redraw } } }
Hier verbessern wir die Interaktivität des Programms, indem wir die Funktion OnChartEvent implementieren, um Nutzerinteraktionen auf dem Chart zu verarbeiten. Zunächst prüfen wir, ob das Ereignis „id“ CHARTEVENT_OBJECT_CLICK und „sparam“ „ChatGPT_SubmitButton“ ist, was auf einen Klick auf die Schaltfläche „Submit“ hinweist. Wenn ja, wird die Nutzereingabe aus dem Textfeld „ChatGPT_InputEdit“ mit ObjectGetString und OBJPROP_TEXT abgerufen und in der Variablen „prompt“ gespeichert.
Wenn „prompt“ nicht leer ist (geprüft mit StringLen), rufen wir „GetChatGPTResponse“ auf, um die KI-Antwort abzurufen, protokollieren die Nutzeraufforderung und die KI-Antwort, fügen sie an „conversationHistory“ mit den Bezeichnungen „You: “ und „AI: “ an, löschen das Eingabefeld mit ObjectSetString mit einem leeren String, aktualisieren die Anzeige durch den Aufruf von „UpdateResponseDisplay“, protokollieren die Interaktion mit Zeitstempel mit FileWriteString an „logFileHandle“ und TimeToString und aktualisieren das Chart. Diese Implementierung ermöglicht es dem Programm, unsere Eingabeaufforderungen zu verarbeiten und die KI-Antworten interaktiv anzuzeigen, wodurch die Kernfunktionalität des Dashboards vervollständigt wird. Nach der Kompilierung erhalten wir folgendes Ergebnis.

Aus dem Bild können wir ersehen, dass wir das KI-Modell erfolgreich in unser Programm integriert haben. Versuchen wir, die Protokolldatei zu öffnen und zu sehen, ob wir dort hineingekommen sind. Sie finden die Datei im Ordner „Allgemeine Dateien“ (siehe unten). Klicken Sie mit der rechten Maustaste und folgen Sie den Anweisungen, um es zu öffnen. Siehe unten.

Wenn wir die Datei öffnen, erhalten wir folgendes Ergebnis.

Auf dem Bild können wir sehen, dass wir die Aktivitätsdaten protokollieren können, um sicherzustellen, dass wir verfolgen können, was wir tun, unsere Aufforderungen und die Antworten, die wir erhalten. Jetzt müssen wir sicherstellen, dass wir der Schaltfläche „Senden“ einen Hover-Effekt hinzufügen. Um dies zu erreichen, müssen wir die Koordinaten der Mausbewegung verfolgen, was voraussetzt, dass wir die Mausbewegung des Charts auf der obersten Ebene aktivieren.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Initialization button_darker_bg = DarkenColor(button_original_bg); //--- Set darker button color logFileHandle = FileOpen(LogFileName, FILE_READ | FILE_WRITE | FILE_TXT); //--- Open log if(logFileHandle == INVALID_HANDLE) { //--- Check handle Print("Failed to open log file: ", GetLastError()); //--- Print error return(INIT_FAILED); //--- Fail init } FileSeek(logFileHandle, 0, SEEK_END); //--- Seek end FileWriteString(logFileHandle, "EA Initialized at " + TimeToString(TimeCurrent()) + "\n"); //--- Log init CreateDashboard(); //--- Create UI UpdateResponseDisplay(); //--- Update display ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); //--- Enable mouse events return(INIT_SUCCEEDED); //--- Success }
Wir verwenden einfach die Funktion ChartSetInteger, um die Mausereignisse im Chart auf true zu setzen. Wir haben die Funktion zur Verdeutlichung hervorgehoben. Dann müssen wir zur Funktion OnChartEvent des Charts übergehen und die Logik hinzufügen, wenn sich die Maus bewegt.
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Event handler if(id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_SubmitButton") { //--- Button click string prompt = (string)ObjectGetString(0, "ChatGPT_InputEdit", OBJPROP_TEXT); //--- Get input if(StringLen(prompt) > 0) { //--- If not empty string response = GetChatGPTResponse(prompt); //--- Get AI response Print("User: " + prompt); //--- Print user Print("AI: " + response); //--- Print AI conversationHistory += "You: " + prompt + "\nAI: " + response + "\n\n"; //--- Append history ObjectSetString(0, "ChatGPT_InputEdit", OBJPROP_TEXT, ""); //--- Clear input UpdateResponseDisplay(); //--- Update display FileWriteString(logFileHandle, "Prompt: " + prompt + " | Response: " + response + " | Time: " + TimeToString(TimeCurrent()) + "\n"); //--- Log ChartRedraw(); //--- Redraw } } else if(id == CHARTEVENT_MOUSE_MOVE) { //--- Mouse move int mouseX = (int)lparam; //--- X coord int mouseY = (int)dparam; //--- Y coord bool isOver = (mouseX >= 430 && mouseX <= 530 && mouseY >= 20 && mouseY <= 60); //--- Check hover if(isOver && !button_hover) { //--- Enter hover ObjectSetInteger(0, "ChatGPT_SubmitButton", OBJPROP_BGCOLOR, button_darker_bg); //--- Darken button_hover = true; //--- Set flag ChartRedraw(); //--- Redraw } else if(!isOver && button_hover) { //--- Exit hover ObjectSetInteger(0, "ChatGPT_SubmitButton", OBJPROP_BGCOLOR, button_original_bg); //--- Restore button_hover = false; //--- Clear flag ChartRedraw(); //--- Redraw } } }
In der Funktion OnChartEvent wird die Implementierung durch Hinzufügen von Hover-Effekten für die Nutzeroberfläche abgeschlossen. In dem Block else-if prüfen wir, ob das Ereignis „id“ CHARTEVENT_MOUSE_MOVE ist, was auf eine Mausbewegung hinweist, und setzen „lparam“ auf „mouseX“ und „dparam“ auf „mouseY“ als Koordinaten. Dann wird geprüft, ob sich die Maus über der Schaltfläche „Submit“ befindet, indem überprüft wird, ob „mouseX“ zwischen 430 und 530 und „mouseY“ zwischen 20 und 60 liegt. Das Ergebnis wird in der Variablen „isOver“ gespeichert.
Wenn „isOver“ wahr und „button_hover“ falsch ist (was bedeutet, dass die Maus in den Schaltflächenbereich eingetreten ist), rufen wir ObjectSetInteger auf, um die Hintergrundfarbe von „ChatGPT_SubmitButton“ auf „button_darker_bg“ für einen Schwebeeffekt zu setzen, setzen „button_hover“ auf wahr und aktualisieren das Chart. Umgekehrt, wenn „isOver“ falsch und „button_hover“ wahr ist (was bedeutet, dass die Maus den Schaltflächenbereich verlassen hat), wird der Hintergrund der Schaltfläche mit „ObjectSetInteger“ auf „button_original_bg“ zurückgesetzt, „button_hover“ auf falsch gesetzt und ChartRedraw aufgerufen, um das Chartzu aktualisieren. Wenn wir jetzt mit dem Mauszeiger über die Schaltfläche fahren, erhalten wir folgendes Ergebnis.

Aus dem Bild können wir ersehen, dass wir in der Lage sind, einen Hover-Effekt auf der erstellten Schaltfläche hinzuzufügen und somit unser Ziel zu erreichen, ein interaktives AI-Dashboard für die Interaktion zu erstellen. Bleiben nur noch die Backtests des Programms, und das wird im nächsten Abschnitt behandelt.
Testen des ChatGPT-Programms
Wir haben die Tests durchgeführt, und unten sehen Sie die kompilierte Visualisierung in einem einzigen Graphics Interchange Format (GIF) Bitmap-Bildformat.

Schlussfolgerung
Abschließend haben wir ein in ChatGPT integriertes Programm in MQL5 entwickelt, das das JSON-Parsing-Framework aus dem vorherigen Teil nutzt, um Aufforderungen an die API von OpenAI zu senden und Antworten über ein interaktives Dashboard anzuzeigen. Durch eine nutzerfreundliche Oberfläche mit Eingabefeld, Übermittlungsschaltfläche und Antwortanzeige, kombiniert mit API-Kommunikation und Protokollierung, ermöglicht das Programm KI-gesteuerte Erkenntnisse in Echtzeit. In den vorangehenden Abschnitten werden wir die Anzeige aktualisieren, um mehr Beantwortungen zu verarbeiten und sie blätterbar zu machen. Bleiben Sie dran.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/19567
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Entwicklung des Price Action Analysis Toolkit (Teil 40): Markt-DNA-Pass
Vom Neuling zum Experten: Animierte Nachrichtenüberschrift mit MQL5 (XI) – Korrelation im Nachrichtenhandel
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Aufbau eines professionellen Handelssystems mit Heikin Ashi (Teil 2): Entwicklung eines EA
- 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.