English Русский 日本語
preview
Analyse mehrerer Symbole mit Python und MQL5 (Teil I): NASDAQ für Hersteller von integrierten Schaltungen

Analyse mehrerer Symbole mit Python und MQL5 (Teil I): NASDAQ für Hersteller von integrierten Schaltungen

MetaTrader 5Beispiele | 22 November 2024, 10:32
166 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

Es gibt viele Möglichkeiten für einen Anleger, sein Portfolio zu diversifizieren. Darüber hinaus gibt es viele verschiedene Kennzahlen, die als Kriterium für die Optimierung des Portfolios herangezogen werden können. Es ist unwahrscheinlich, dass ein einzelner Anleger genügend Zeit oder Ressourcen hat, um alle Optionen sorgfältig abzuwägen, bevor er eine so wichtige Entscheidung trifft. In dieser Artikelserie werden wir Sie durch die unzähligen Optionen führen, die Ihnen auf Ihrem Weg zum gleichzeitigen Handel mit mehreren Symbolen zur Verfügung stehen. Unser Ziel ist es, Ihnen bei der Entscheidung zu helfen, welche Strategien Sie beibehalten sollten und welche für Sie möglicherweise nicht geeignet sind.


Überblick über die Handelsstrategie

In dieser Diskussion haben wir einen Korb von Aktien ausgewählt, die fundamental miteinander verbunden sind. Wir haben 5 Aktien von Unternehmen ausgewählt, die in ihrem Geschäftszyklus integrierte Schaltungen entwickeln und verkaufen. Diese Unternehmen sind Broadcom, Cisco, Intel, NVIDIA und Comcast. Alle 5 Unternehmen sind an der National Association of Securities Dealers Automated Quotations (NASDAQ) Exchange notiert. Die NASDAQ wurde 1971 gegründet und ist gemessen am Handelsvolumen die größte Börse in den Vereinigten Staaten.


Integrierte Schaltkreise sind aus unserem täglichen Leben nicht mehr wegzudenken. Diese elektronischen Chips durchdringen alle Aspekte unseres modernen Lebens, von den proprietären MetaQuotes-Servern, auf denen diese Website, auf der Sie diesen Artikel lesen, gehostet wird, bis hin zu dem Gerät, mit dem Sie diesen Artikel lesen - all diese Geräte basieren auf einer Technologie, die höchstwahrscheinlich von einem dieser fünf Unternehmen entwickelt wurde. Der erste integrierte Schaltkreis der Welt wurde von Intel entwickelt, trug den Namen Intel 4004 und wurde 1971 auf den Markt gebracht, im selben Jahr, in dem die NASDAQ-Börse gegründet wurde. Der Intel 4004 hatte etwa 2600 Transistoren, was weit entfernt ist von modernen Chips, die leicht Milliarden von Transistoren haben.

Da wir durch die weltweite Nachfrage nach integrierten Schaltkreisen motiviert sind, wollen wir uns auf intelligente Weise auf dem Chipmarkt engagieren. Anhand eines Korbs dieser 5 Aktien zeigen wir Ihnen, wie Sie die Rendite Ihres Portfolios maximieren können, indem Sie Ihr Kapital umsichtig auf diese Aktien verteilen. Ein traditioneller Ansatz, der eine gleichmäßige Verteilung des Kapitals auf alle 5 Aktien vorsieht, reicht auf den modernen, volatilen Märkten nicht mehr aus. Wir werden stattdessen ein Modell erstellen, das uns darüber informiert, ob wir die einzelnen Aktien kaufen oder verkaufen sollten, und welche Mengen wir optimal handeln sollten. Mit anderen Worten: Wir verwenden die uns vorliegenden Daten, um unsere Positionsgrößen und -mengen algorithmisch zu erlernen.


Überblick über die Methodik

Wir begannen mit dem Abruf von 100000 Zeilen M1-Marktdaten für jede der 5 Aktien in unserem Korb von unserem MetaTrader 5-Terminal unter Verwendung der MetaTrader 5 Python-Bibliothek. Nach der Umwandlung der gewöhnlichen Kursdaten in prozentuale Veränderungen führten wir eine explorative Datenanalyse der Marktrenditedaten durch.

Wir haben eine schwache Korrelation zwischen den 5 Aktien festgestellt. Außerdem zeigten unsere Box-Plots deutlich, dass die durchschnittliche Rendite der einzelnen Aktien nahe bei 0 lag. Wir haben auch die Renditen der einzelnen Aktien überlagert und konnten eindeutig feststellen, dass die Renditen der NVIDIA-Aktien am stärksten schwankten. Schließlich haben wir für alle 5 von uns ausgewählten Aktien Paar-Diagramme erstellt, und leider konnten wir keine erkennbare Beziehung feststellen, die wir nutzen könnten.

Von dort aus haben wir die SciPy-Bibliothek verwendet, um die optimale Gewichtung für jede der 5 Aktien in unserem Portfolio zu finden. Wir lassen zu, dass alle 5 Gewichte zwischen -1 und 1 liegen. Immer wenn die Gewichtung unseres Portfolios unter 0 liegt, rät uns der Algorithmus zum Verkauf, und umgekehrt, wenn die Gewichtung über 0 liegt, raten die Daten zum Kauf.

Nach der Berechnung der optimalen Portfoliogewichte haben wir diese Daten in unsere Handelsanwendung integriert, um sicherzustellen, dass sie immer eine optimale Anzahl von Positionen in jedem Markt offen hält. Unsere Handelsanwendung ist so konzipiert, dass alle offenen Positionen automatisch geschlossen werden, wenn sie ein vom Endnutzer festgelegtes Gewinnniveau erreichen.


Abrufen der Daten 

Für den Anfang importieren wir zunächst die benötigten Bibliotheken.

#Import the libraries we need
import pandas              as pd
import numpy               as np
import seaborn             as sns
import matplotlib.pyplot   as plt
import MetaTrader5         as mt5
from   scipy.optimize      import minimize

Lassen Sie uns nun das MetaTrader 5 Terminal initialisieren.

#Initialize the terminal
mt5.initialize()
„True“

Wir definieren den Aktienkorb, den wir handeln möchten

#Now let us fetch the data we need on chip manufacturing stocks
#Broadcom, Cisco, Comcast, Intel, NVIDIA
stocks = ["AVGO.NAS","CSCO.NAS","CMCSA.NAS","INTC.NAS","NVDA.NAS"]

und erstellen einen Datenrahmen, um unsere Marktdaten zu speichern.

#Let us create a data frame to store our stock returns
amount = 100000
returns = pd.DataFrame(columns=stocks,index=np.arange(0,amount))

Jetzt werden wir unsere Marktdaten abrufen.

#Fetch the stock returns
for stock in stocks:
    temp = pd.DataFrame(mt5.copy_rates_from_pos(stock,mt5.TIMEFRAME_M1,0,amount))
    returns[[stock]] = temp[["close"]].pct_change()

Nun formatieren wir unsere Daten.

#Format the data set 
returns.dropna(inplace=True)
returns.reset_index(inplace=True,drop=True)
returns

Zum Schluss multiplizieren wir die Daten mit 100, um sie als Prozentsätze zu speichern.

#Convert the returns to percentages
returns = returns * 100
returns


Explorative Datenanalyse

Manchmal können wir die Beziehung zwischen den Variablen im System visuell erkennen. Analysieren wir die Korrelationsniveaus in unseren Daten, um zu sehen, ob es lineare Kombinationen gibt, die wir uns zunutze machen können. Leider sind unsere Korrelationswerte nicht beeindruckend, und bisher scheint es keine linearen Abhängigkeiten zu geben, die wir ausnutzen könnten.

#Let's analyze if there is any correlation in the data
sns.heatmap(returns.corr(),annot=True)

Abb. 1: Unsere Heatmap der Korrelationen

Analysieren wir die paarweisen Streudiagramme unserer Daten. Bei großen Datenbeständen können nicht-triviale Beziehungen leicht unentdeckt bleiben. Die Wahrscheinlichkeit, dass dies geschieht, wird durch paarweise Parzellen minimiert. Leider gab es in den Daten keine leicht zu beobachtenden Zusammenhänge, die uns durch unsere Diagramme offenbart wurden.

#Let's create pair plots of our data
sns.pairplot(returns)

Abb. 2: Einige unserer paarweisen Streudiagramme

Die Darstellung der in den Daten beobachteten Renditen zeigt, dass NVIDIA die volatilsten Renditen zu haben scheint.

#Lets also visualize our returns
returns.plot()

Abb. 3: Darstellung unserer Markterlöse

Die Visualisierung unserer Markterlöse als Boxplots zeigt uns deutlich, dass der durchschnittliche Markterlös 0 beträgt.

#Let's try creating box-plots 
sns.boxplot(returns)

Abb. 4: Visualisierung unserer Markterlöse als Boxplots



Portfolio-Optimierung

Nun können wir mit der Berechnung der optimalen Gewichtung der Kapitalallokation für jede Aktie beginnen. Zunächst werden wir unsere Gewichte nach dem Zufallsprinzip zuweisen. Darüber hinaus werden wir auch eine Datenstruktur erstellen, um den Fortschritt unseres Optimierungsalgorithmus zu speichern.

#Define random weights that add up to 1
weights = np.array([1,0.5,0,0.5,-1])
#Create a data structure to store the progress of the algorithm
evaluation_history = []

Die Zielfunktion unseres Optimierungsverfahrens ist die Rendite unseres Portfolios unter den gegebenen Gewichten. Beachten Sie, dass unsere Portfoliorenditen anhand des geometrischen Mittels der Renditen der Vermögenswerte berechnet werden. Wir haben uns für das geometrische Mittel und nicht für das arithmetische Mittel entschieden, weil die Berechnung des Mittelwerts bei positiven und negativen Werten nicht mehr trivial ist. Wenn wir dieses Problem ganz locker angehen und das arithmetische Mittel verwenden würden, hätten wir leicht eine Portfoliorendite von 0 berechnen können. Wir können Minimierungsalgorithmen für Maximierungsprobleme verwenden, indem wir die Portfoliorendite mit negativ 1 multiplizieren, bevor wir sie an den Optimierungsalgorithmus zurückgeben.

#Let us now get ready to maximize our returns
#First we need to define the cost function
def cost_function(x):
    #First we need to calculate the portfolio returns with the suggested weights
    portfolio_returns = np.dot(returns,x)
    geom_mean         =  ((np.prod( 1 + portfolio_returns ) ** (1.0/99999.0)) - 1)
    #Let's keep track of how our algorithm is performing
    evaluation_history.append(-geom_mean)
    return(-geom_mean)

Definieren wir nun die Bedingung, die sicherstellt, dass alle Gewichte den Wert 1 ergeben. Beachten Sie, dass nur wenige Optimierungsverfahren in SciPy Gleichheitsbedingungen unterstützen. Gleichheitsbeschränkungen teilen dem SciPy-Modul mit, dass wir möchten, dass diese Funktion gleich 0 ist. Wir wollen also, dass die Differenz zwischen dem absoluten Wert unserer Gewichte und 1 gleich 0 ist.

#Now we need to define our constraints
def l1_norm_constraint(x):
    return(((np.sum(np.abs(x))) - 1))

constraints = ({'type':'eq','fun':l1_norm_constraint})

Alle unsere Gewichte sollten zwischen -1 und 1 liegen. Dies kann durch die Festlegung von Grenzen für unseren Algorithmus erzwungen werden.

#Now we need to define the bounds for our weights
bounds = [(-1,1)] * 5

Durchführung des Optimierungsverfahrens.

#Perform the optimization
results = minimize(cost_function,weights,method="SLSQP",bounds=bounds,constraints=constraints)

Die Ergebnisse unseres Optimierungsverfahrens.

results
message: Optimization terminated successfully
 success: True
  status: 0
     fun: 0.0024308603411499208
       x: [ 3.931e-01  1.138e-01 -5.991e-02  7.744e-02 -3.557e-01]
     nit: 23
     jac: [ 3.851e-04  2.506e-05 -3.083e-04 -6.868e-05 -3.186e-04]
    nfev: 158
    njev: 23

Lassen Sie uns die von uns berechneten optimalen Koeffizientenwerte speichern.

optimal_weights = results.x
optimal_weights
array([ 0.39311134,  0.11379942, -0.05991417,  0.07743534, -0.35573973])

Wir sollten auch die optimalen Punkte aus dem Verfahren speichern.

optima_y = min(evaluation_history)
optima_x = evaluation_history.index(optima_y)
inputs = np.arange(0,len(evaluation_history))

Lassen Sie uns den Leistungsverlauf unseres Optimierungsalgorithmus veranschaulichen. Wie wir aus dem Diagramm ersehen können, scheint unser Algorithmus anfangs mit den ersten 50 Iterationen zu kämpfen zu haben. Es scheint jedoch gelungen zu sein, einen optimalen Punkt zu finden, der unsere Portfoliorenditen maximiert.

plt.scatter(inputs,evaluation_history)
plt.plot(optima_x,optima_y,'s',color='r')
plt.axvline(x=optima_x,ls='--',color='red')
plt.axhline(y=optima_y,ls='--',color='red')
plt.title("Maximizing Returns")

Abb. 5: Die Leistung unseres SLSQP-Optimierungsalgorithmus

Überprüfen wir, ob der absolute Wert unserer Gewichte 1 beträgt, oder anders ausgedrückt, wir wollen überprüfen, ob unsere L1-Norm-Beschränkung nicht verletzt wurde.

#Validate the weights add up to 1
np.sum(np.abs(optimal_weights))
1.0

Es gibt eine intuitive Möglichkeit, die optimalen Koeffizienten zu interpretieren. Wenn wir davon ausgehen, dass wir 10 Positionen eröffnen wollen, multiplizieren wir zunächst die Koeffizienten mit 10. Dann führen wir eine ganzzahlige Division durch 1 durch, um alle Nachkommastellen zu entfernen. Die ganzen Zahlen, die übrig bleiben, können als die Anzahl der Positionen interpretiert werden, die wir in jedem Markt eröffnen sollten. Unsere Daten deuten darauf hin, dass wir 3 Käufe von Broadcom, 1 Kauf von Cisco, 1 Verkauf von Comcast, kein Handel mit Intel und 4 Verkäufe von NVIDIA tätigen sollten, um unsere Rendite zu maximieren. 

#Here's an intuitive way of understanding the data
#If we can only open 10 positions, our best bet may be
#3 buy positions in Broadcom
#1 buy position in Cisco
#1 sell position sell position in Comcast
#No positions in Intel
#4 sell postions in NVIDIA
(optimal_weights * 10) // 1
array([ 3., 1., -1., 0., -4.])


Implementierung in MQL5

Lassen Sie uns nun unsere Handelsstrategie in MQL5 umsetzen. Wir werden den Ball ins Rollen bringen, indem wir zunächst globale Variablen definieren, die wir in unserer Anwendung verwenden werden.

//+------------------------------------------------------------------+
//|                                                 NASDAQ IC AI.mq5 |
//|                                        Gamuchirai Zororo Ndawana |
//|                          https://www.mql5.com/en/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Zororo Ndawana"
#property link      "https://www.mql5.com/en/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
int    rsi_handler,bb_handler;
double bid,ask;
int    optimal_weights[5] = {3,1,-1,0,-4};
string stocks[5]          = {"AVGO.NAS","CSCO.NAS","CMCSA.NAS","INTC.NAS","NVDA.NAS"};
vector current_close      = vector::Zeros(1);
vector rsi_buffer         = vector::Zeros(1);
vector bb_high_buffer     = vector::Zeros(1);
vector bb_mid_buffer      = vector::Zeros(1);
vector bb_low_buffer      = vector::Zeros(1);

Importieren der Handelsbibliothek, um uns bei der Verwaltung unserer Positionen zu helfen.

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include  <Trade/Trade.mqh>
CTrade Trade;

Der Endnutzer unseres Programms kann das Verhalten des Expert Advisors durch die Eingaben, die wir ihm erlauben zu kontrollieren, anpassen.

//+------------------------------------------------------------------+
//| User inputs                                                      |
//+------------------------------------------------------------------+
input double profit_target =  1.0; //At this profit level, our position will be closed
input int    rsi_period    =   20; //Adjust the RSI period
input int    bb_period     =   20; //Adjust the Bollinger Bands period
input double trade_size    =  0.3; //How big should our trades be?

Wenn wir unseren Handelsalgorithmus zum ersten Mal einrichten, müssen wir sicherstellen, dass uns alle 5 Symbole aus unseren vorherigen Berechnungen zur Verfügung stehen. Andernfalls brechen wir den Initialisierungsvorgang ab.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Validate that all the symbols we need are available
   if(!validate_symbol())
     {
      return(INIT_FAILED);
     }
//--- Everything went fine
   return(INIT_SUCCEEDED);
  }

Wenn unser Programm aus der Tabelle entfernt wurde, sollten wir die nicht mehr benötigten Ressourcen freigeben.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Release resources we no longer need
   release_resources();
  }

Immer wenn wir aktualisierte Kurse erhalten, möchten wir zunächst die aktuellen Geld- und Briefkurse in unseren global definierten Variablen speichern, nach Handelsmöglichkeiten suchen und schließlich alle Gewinne, die wir bereithalten, vom Tisch nehmen.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

//--- Update market data
   update_market_data();

//--- Check for a trade oppurtunity in each symbol
   check_trade_symbols();

//--- Check if we have an oppurtunity to take ourt profits
   check_profits();
  }

Die Funktion, die dafür zuständig ist, unsere Gewinne vom Tisch zu nehmen, durchläuft alle Symbole, die wir in unserem Korb haben. Wenn das Symbol erfolgreich gefunden wird, wird geprüft, ob wir Positionen in diesem Markt haben. Unter der Annahme, dass wir offene Positionen haben, werden wir prüfen, ob der Gewinn das vom Nutzer definierte Gewinnziel übersteigt; wenn dies der Fall ist, werden wir unsere Positionen schließen. Andernfalls werden wir weitermachen.

//+------------------------------------------------------------------+
//| Check for opportunities to collect our profits                   |
//+------------------------------------------------------------------+
void check_profits(void)
  {
   for(int i =0; i < 5; i++)
     {
      if(SymbolSelect(stocks[i],true))
        {
         if(PositionSelect(stocks[i]))
           {
            if(PositionGetDouble(POSITION_PROFIT) > profit_target)
              {
               Trade.PositionClose(stocks[i]);
              }
           }
        }
     }
  }

Jedes Mal, wenn wir aktualisierte Preise erhalten, wollen wir sie in unseren global skalierten Variablen speichern, da diese Variablen in verschiedenen Teilen unseres Programms aufgerufen werden können.

//+------------------------------------------------------------------+
//| Update markte data                                               |
//+------------------------------------------------------------------+
void update_market_data(void)
  {
   ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
  }

Wenn unser Expert Advisor nicht in Gebrauch ist, geben wir die nicht mehr benötigten Ressourcen frei, um eine gute Nutzerfreundlichkeit zu gewährleisten.

//+-------------------------------------------------------------------+
//| Release the resources we no longer need                           |
//+-------------------------------------------------------------------+
void release_resources(void)
  {
   ExpertRemove();
  }
//+------------------------------------------------------------------+

Bei der Initialisierung haben wir geprüft, ob alle benötigten Symbole vorhanden sind. Die folgende Funktion ist für diese Aufgabe zuständig. Sie durchläuft alle Symbole, die wir in unserem Array von Aktien haben. Wenn wir kein Symbol auswählen können, gibt die Funktion false zurück und bricht den Initialisierungsvorgang ab. Andernfalls wird die Funktion true zurückgeben.

//+------------------------------------------------------------------+
//| Validate that all the symbols we need are available              |
//+------------------------------------------------------------------+
bool validate_symbol(void)
  {
   for(int i=0; i < 5; i++)
     {
      //--- We failed to add one of the necessary symbols to the Market Watch window!
      if(!SymbolSelect(stocks[i],true))
        {
         Comment("Failed to add ",stocks[i]," to the market watch. Ensure the symbol is available.");
         return(false);
        }
     }

//--- Everything went fine
   return(true);
  }

Diese Funktion ist für die Koordinierung des Prozesses der Eröffnung und Verwaltung von Positionen in unserem Portfolio zuständig. Es wird alle Symbole in unserem Array durchlaufen und prüfen, ob wir offene Positionen in diesem Markt haben und ob wir offene Positionen in diesem Markt haben sollten. Wenn wir das tun sollten, es aber nicht tun, wird die Funktion damit beginnen, nach Möglichkeiten zu suchen, sich auf diesem Markt zu engagieren. Andernfalls wird die Funktion nicht ausgeführt.

//+------------------------------------------------------------------+
//| Check if we have any trade opportunities                         |
//+------------------------------------------------------------------+
void check_trade_symbols(void)
  {
//--- Loop through all the symbols we have
   for(int i=0;i < 5;i++)
     {
      //--- Select that symbol and check how many positons we have open
      if(SymbolSelect(stocks[i],true))
        {
         //--- If we have no positions in that symbol, optimize the portfolio
         if((PositionsTotal() == 0) && (optimal_weights[i] != 0))
           {
            optimize_portfolio(stocks[i],optimal_weights[i]);
           }
        }
     }
  }

Die Funktion zur Optimierung des Portfolios benötigt 2 Parameter, nämlich die betreffende Aktie und die dieser Aktie zugewiesenen Gewichte. Wenn die Gewichte positiv sind, ruft die Funktion ein Verfahren auf, um eine Kaufposition in diesem Markt einzugehen, bis der Gewichtsparameter erfüllt ist; bei negativen Gewichten ist das Gegenteil der Fall.

//+------------------------------------------------------------------+
//| Optimize our portfolio                                           |
//+------------------------------------------------------------------+
void optimize_portfolio(string symbol,int weight)
  {
//--- If the weight is less than 0, check if we have any oppurtunities to sell that stock
   if(weight < 0)
     {
      if(SymbolSelect(symbol,true))
        {
         //--- If we have oppurtunities to sell, act on it
         if(check_sell(symbol, weight))
           {
            Trade.Sell(trade_size,symbol,bid,0,0,"NASDAQ IC AI");
           }
        }
     }

//--- Otherwise buy
   else
     {
      if(SymbolSelect(symbol,true))
        {
         //--- If we have oppurtunities to buy, act on it
         if(check_buy(symbol,weight))
           {
            Trade.Buy(trade_size,symbol,ask,0,0,"NASDAQ IC AI");
           }
        }
     }
  }

Nun müssen wir die Bedingungen festlegen, unter denen wir eine Kaufposition eingehen können. Wir werden uns auf eine Kombination aus technischer Analyse und Kursentwicklung verlassen, um unsere Einstiege zu timen. Wir werden nur dann Kaufposition eingehen, wenn die Kurse über dem oberen Bollinger Band liegen, unser RSI-Wert über 70 liegt und die Kursentwicklung auf höheren Zeitebenen steigt. Wir sind ebenfalls der Meinung, dass dies ein Setup mit hoher Wahrscheinlichkeit darstellt, mit dem wir unsere Gewinnziele sicher erreichen können. Unsere letzte Bedingung ist, dass die Gesamtzahl der Positionen, die wir in diesem Markt geöffnet haben, unsere optimale Allokation nicht überschreitet. Wenn unsere Bedingungen erfüllt sind, geben wir „true“ zurück, was der Funktion „optimize_portfolio“ die Berechtigung gibt, eine Kaufposition einzugehen.

//+------------------------------------------------------------------+
//| Check for oppurtunities to buy                                   |
//+------------------------------------------------------------------+
bool check_buy(string symbol, int weight)
  {
//--- Ensure we have selected the right symbol
   SymbolSelect(symbol,true);

//--- Load the indicators on the symbol
   bb_handler  = iBands(symbol,PERIOD_CURRENT,bb_period,0,1,PRICE_CLOSE);
   rsi_handler = iRSI(symbol,PERIOD_CURRENT,rsi_period,PRICE_CLOSE);
//--- Validate the indicators
   if((bb_handler == INVALID_HANDLE) || (rsi_handler == INVALID_HANDLE))
     {
      //--- Something went wrong
      return(false);
     }

//--- Load indicator readings into the buffers
   bb_high_buffer.CopyIndicatorBuffer(bb_handler,1,0,1);
   rsi_buffer.CopyIndicatorBuffer(rsi_handler,0,0,1);
   current_close.CopyRates(symbol,PERIOD_CURRENT,COPY_RATES_CLOSE,0,1);

//--- Validate that we have a valid buy oppurtunity
   if((bb_high_buffer[0] < current_close[0]) && (rsi_buffer[0] > 70))
     {
      return(false);
     }

//--- Do we allready have enough positions
   if(PositionsTotal() >= weight)
     {
      return(false);
     }
//--- We can open a position
   return(true);
  }

Unsere Funktion „check_sell“ funktioniert ähnlich wie unsere Funktion „check_buy“, mit dem Unterschied, dass sie die Gewichtung zunächst mit dem negativen Wert 1 multipliziert, sodass wir leicht zählen können, wie viele Positionen wir auf dem Markt offen haben sollten. Die Funktion prüft, ob der Kurs unter dem Bollinger Band Low liegt und ob der RSI-Wert unter 30 liegt. Wenn diese 3 Bedingungen erfüllt sind, müssen wir auch sicherstellen, dass die Kursentwicklung auf den höheren Zeitrahmen uns erlaubt, eine Verkaufsposition einzugehen.

//+------------------------------------------------------------------+
//| Check for oppurtunities to sell                                  |
//+------------------------------------------------------------------+
bool check_sell(string symbol, int weight)
  {
//--- Ensure we have selected the right symbol
   SymbolSelect(symbol,true);

//--- Negate the weight
   weight = weight * -1;

//--- Load the indicators on the symbol
   bb_handler  = iBands(symbol,PERIOD_CURRENT,bb_period,0,1,PRICE_CLOSE);
   rsi_handler = iRSI(symbol,PERIOD_CURRENT,rsi_period,PRICE_CLOSE);
//--- Validate the indicators
   if((bb_handler == INVALID_HANDLE) || (rsi_handler == INVALID_HANDLE))
     {
      //--- Something went wrong
      return(false);
     }

//--- Load indicator readings into the buffers
   bb_low_buffer.CopyIndicatorBuffer(bb_handler,2,0,1);
   rsi_buffer.CopyIndicatorBuffer(rsi_handler,0,0,1);
   current_close.CopyRates(symbol,PERIOD_CURRENT,COPY_RATES_CLOSE,0,1);

//--- Validate that we have a valid sell oppurtunity
   if(!((bb_low_buffer[0] > current_close[0]) && (rsi_buffer[0] < 30)))
     {
      return(false);
     }

//--- Do we have enough trades allready open?
   if(PositionsTotal() >= weight)
     {
      //--- We have a valid sell setup
      return(false);
     }

//--- We can go ahead and open a position
   return(true);
  }

Unser System in Aktion

Abb. 6: Vorwärtstest unseres Algorithmus


Schlussfolgerung

In unserer Diskussion haben wir gezeigt, wie Sie Ihre Positionsgröße und Kapitalallokation mithilfe von KI algorithmisch bestimmen können. Es gibt viele verschiedene Aspekte eines Portfolios, die wir optimieren können, z. B. das Risiko (Varianz) eines Portfolios, die Korrelation unseres Portfolios mit der Performance einer Branchenbenchmark (Beta) und die risikobereinigten Renditen des Portfolios. In unserem Beispiel haben wir unser Modell einfach gehalten und nur die Maximierung der Rendite berücksichtigt. Im weiteren Verlauf dieser Serie werden wir viele wichtige Kennzahlen berücksichtigen. Anhand dieses einfachen Beispiels können wir jedoch die Hauptgedanken der Portfolio-Optimierung nachvollziehen, und wenn wir dann zu komplexen Optimierungsverfahren übergehen, kann der Leser das Problem mit Zuversicht angehen, da er weiß, dass sich die hier dargelegten Hauptgedanken nicht ändern werden. Wir können zwar nicht garantieren, dass die in unserer Diskussion enthaltenen Informationen jedes Mal zum Erfolg führen, aber sie sind auf jeden Fall eine Überlegung wert, wenn Sie ernsthaft mit mehreren Symbolen auf algorithmische Weise handeln wollen.

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/15909

Beigefügte Dateien |
NASDAQ_IC_AI.mq5 (9.45 KB)
MQL5-Assistent-Techniken, die Sie kennen sollten (Teil 39): RSI (Relative Strength Index) MQL5-Assistent-Techniken, die Sie kennen sollten (Teil 39): RSI (Relative Strength Index)
Der RSI ist ein beliebter Momentum-Oszillator, der das Tempo und den Umfang der jüngsten Kursveränderungen eines Wertpapiers misst, um über- und unterbewertete Situationen im Kurs des Wertpapiers zu bewerten. Diese Erkenntnisse in Bezug auf Geschwindigkeit und Ausmaß sind der Schlüssel zur Festlegung von Umkehrpunkten. Wir setzen diesen Oszillator in einer anderen nutzerdefinierten Signalklasse ein und untersuchen die Eigenschaften einiger seiner Signale. Wir beginnen jedoch mit dem Abschluss dessen, was wir zuvor über Bollinger-Bänder begonnen haben.
Neuinterpretation klassischer Strategien in MQL5 (Teil III): Prognose des FTSE 100 Neuinterpretation klassischer Strategien in MQL5 (Teil III): Prognose des FTSE 100
In dieser Artikelserie werden wir uns bekannte Handelsstrategien noch einmal ansehen und untersuchen, ob wir diese Strategien mithilfe von KI verbessern können. Im heutigen Artikel werden wir uns mit dem FTSE 100 befassen und versuchen, den Index anhand eines Teils der Einzelwerte, aus denen er sich zusammensetzt, zu prognostizieren.
Selbstoptimierender Expert Advisor mit MQL5 und Python (Teil IV): Stacking-Modelle Selbstoptimierender Expert Advisor mit MQL5 und Python (Teil IV): Stacking-Modelle
Heute werden wir Ihnen zeigen, wie Sie KI-gestützte Handelsanwendungen entwickeln können, die aus ihren eigenen Fehlern lernen. Wir werden eine Technik demonstrieren, die als Stacking bekannt ist und bei der wir 2 Modelle verwenden, um eine Vorhersage zu treffen. Das erste Modell ist in der Regel ein schwächerer Lerner, und das zweite Modell ist in der Regel ein leistungsfähigeres Modell, das die Residuen unseres schwächeren Lerners lernt. Unser Ziel ist es, ein Ensemble von Modellen zu erstellen, um hoffentlich eine höhere Genauigkeit zu erreichen.
Scalping Orderflow für MQL5 Scalping Orderflow für MQL5
Dieser MetaTrader 5 Expert Advisor implementiert die Strategie für ein Scalping-OrderFlow mit fortschrittlichem Risikomanagement. Es verwendet mehrere technische Indikatoren, um Handelsmöglichkeiten auf der Grundlage von Ungleichgewichten im Auftragsfluss zu identifizieren. Das Backtesting zeigt die potenzielle Rentabilität, macht aber auch deutlich, dass weitere Optimierungen erforderlich sind, insbesondere beim Risikomanagement und beim Verhältnis der Handelsergebnisse. Es ist für erfahrene Händler geeignet und muss vor dem Live-Einsatz gründlich getestet und verstanden werden.