English 日本語
preview
Aufbau von KI-gesteuerten Handelssystemen in MQL5 (Teil 2): Entwicklung eines ChatGPT-integrierten Programms mit Nutzeroberfläche

Aufbau von KI-gesteuerten Handelssystemen in MQL5 (Teil 2): Entwicklung eines ChatGPT-integrierten Programms mit Nutzeroberfläche

MetaTrader 5Handelssysteme |
33 0
Allan Munene Mutiiria
Allan Munene Mutiiria

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:

  1. Verständnis des ChatGPT KI-Programmrahmens
  2. OpenAI API-Zugang und MetaTrader 5-Konfiguration einrichten
  3. Implementation in MQL5
  4. Testen des ChatGPT-Programms
  5. 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.

PROGRAMMRAHMEN

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:

ERSTE LANDING PAGE

Geheime Schlüsselgenerierung:

GENERIERUNG GEHEIMER API-SCHLÜSSEL

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.

API CURL TEST AUF CMD

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.

PROGRAMMSCHNITTSTELLE

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

TEST-ANTWORT

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.

MT5 WEB ANFRAGE LINK ENDPUNKT

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.

UI CREATED

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.

AKTUALISIERTE ANZEIGE MIT ANFANGSMELDUNG

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.

UNSER ERSTER PROMPT; HALLO WELT

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.

LOG DATEI

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

ERGEBNIS DER PROTOKOLLDATEI

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.

HOVER-EFFEKT DER MAUS

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.

VOLLSTÄNDIGER TEST, GIF


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

Beigefügte Dateien |
a._ChatGPT_AI_EA.mq5 (116.72 KB)
Entwicklung des Price Action Analysis Toolkit (Teil 40): Markt-DNA-Pass Entwicklung des Price Action Analysis Toolkit (Teil 40): Markt-DNA-Pass
In diesem Artikel wird die einzigartige Identität der einzelnen Währungspaare anhand ihrer historischen Kursentwicklung untersucht. Inspiriert vom Konzept der genetischen DNA, die den individuellen Bauplan eines jeden Lebewesens kodiert, wenden wir einen ähnlichen Rahmen auf die Märkte an, indem wir die Kursentwicklung als „DNA“ eines jeden Paares betrachten. Durch die Aufschlüsselung struktureller Verhaltensweisen wie Volatilität, Schwankungen, Rückschritte, Ausschläge und Sitzungsmerkmale zeigt das Tool das zugrunde liegende Profil, das ein Paar von einem anderen unterscheidet. Dieser Ansatz bietet einen tieferen Einblick in das Marktverhalten und gibt Händlern eine strukturierte Methode an die Hand, um ihre Strategien auf die natürlichen Tendenzen der einzelnen Instrumente abzustimmen.
Vom Neuling zum Experten: Animierte Nachrichtenüberschrift mit MQL5 (XI) – Korrelation im Nachrichtenhandel Vom Neuling zum Experten: Animierte Nachrichtenüberschrift mit MQL5 (XI) – Korrelation im Nachrichtenhandel
In diesem Beitrag werden wir untersuchen, wie das Konzept der Finanzkorrelation angewendet werden kann, um die Entscheidungseffizienz beim Handel mit mehreren Symbolen während der Ankündigung wichtiger wirtschaftlicher Ereignisse zu verbessern. Der Schwerpunkt liegt dabei auf der Bewältigung des erhöhten Risikos, das durch die erhöhte Volatilität bei der Veröffentlichung von Nachrichten entsteht.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Aufbau eines professionellen Handelssystems mit Heikin Ashi (Teil 2): Entwicklung eines EA Aufbau eines professionellen Handelssystems mit Heikin Ashi (Teil 2): Entwicklung eines EA
Dieser Artikel erklärt, wie man einen professionellen Heikin Ashi-basierten Expert Advisor (EA) in MQL5 entwickelt. Sie werden lernen, wie man Eingabeparameter, Enumerationen, Indikatoren und globale Variablen einrichtet und die zentrale Handelslogik implementiert. Sie können auch einen Backtest mit Gold durchführen, um Ihre Arbeit zu überprüfen.