Dokumentation

Anwendungen

Zuletzt aktualisiert am 4. 8. 2021 von Mark Fric

Beispiel - individuelle Analyse pro Strategie

Wir werden als erstes Beispiel eine benutzerdefinierte Analyse pro Strategie erstellen. Es ist nur eine einfache Demonstration, aber sie zeigt, wie man benutzerdefinierte Analysen auch in Verbindung mit benutzerdefinierten Datenbankspalten verwenden kann.

In diesem Beispiel wird folgendes gemacht:

  • Die benutzerdefinierte Analysemethode geht die nach den Strategie-Backtests gespeicherten Ergebnisse durch und berechnet, wie viele Crosschecks verwendet wurden.
  • Eine neue benutzerdefinierte Datenbankspalte zeigt diese Zahl dann in der Datenbank an

 

Schritt 1 - Neues benutzerdefiniertes Analyse-Snippet erstellen

Öffnen Sie CodeEditorklicken Sie auf Neu erstellen und wählen Sie Benutzerdefinierte Analyse (CA) ganz unten zu wählen. Name CAExampleStr.

Dadurch wird ein neues Snippet erstellt CAExampleStr.java im Ordner Benutzer/Snippets/SQ/BenutzerdefinierteAnalysen

Dieses Snippet sieht derzeit wie folgt aus:

Paket SQ.CustomAnalysis;

import com.strategyquant.lib.*;

import java.util.ArrayList;

import com.strategyquant.datalib.*;
import com.strategyquant.tradinglib.*;

public class CAExampleStr extends CustomAnalysisMethod {

    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    
    public CAExampleStr() {
        super("CAExampleStr", TYPE_FILTER_STRATEGY);
    }
    
    //------------------------------------------------------------------------
    
    @Override
    public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception {
        return true;
    }
    
    
    //------------------------------------------------------------------------
    
    @Override
    public ArrayList processDatabank(String project, String task, String databankName, ArrayList databankRG) throws Exception {
        return databankRG;
    }
}

 

Wie Sie sehen können, hat es drei Methoden:

CAExampleStr()
ein Konstruktor ist, sollten Sie hier nur den Namen Ihrer CA-Methode, der in der Benutzeroberfläche erscheinen wird, und den Typ festlegen.

Standardmäßig ist der Typ TYPE_FILTER_STRATEGY, d.h. es handelt sich um eine Methode pro Strategie.

Andere mögliche Optionen sind:

TYPE_PROCESS_DATABANK - CA, die die gesamte Datenbank verarbeitet, wir werden es in einem anderen Beispiel zeigen.
TYPE_BOTH - CA, die sowohl Methoden für die Analyse pro Strategie als auch pro Datenbank enthält.

Der Typ der benutzerdefinierten Analyse bestimmt, ob dieses Snippet in der Auswahl pro Strategie oder pro Datenbank angezeigt wird.


filterStrategy()

Dies ist eine Methode, die für jede Strategie aufgerufen wird, bevor sie in der Datenbank gespeichert wird - wenn die Verwendung der benutzerdefinierten Analyse konfiguriert ist. Sie erhält eine Strategie mit abgeschlossenen Backtests und Gegenproben (ResultsGroup-Objekt) und muss true/false zurückgeben.


processDatabank()
ist eine Methode, die aufgerufen wird, um die gesamte Datenbank zu verarbeiten - sie erhält ein Array mit allen Strategieergebnissen (ResultsGroups) als Parameter und gibt ein Array mit ResutsGroups zurück.

Da unsere Methode nur für eine Strategie gilt, brauchen wir keine processDatabank() Methode und wir können sie aus dem Snippet löschen.

 

Schritt 2 - Implementierung der Methode filterStrategy()

Die gesamte "Magie" wird in filterStrategy() Methode wird die Anzahl der Gegenproben gezählt, die für die Strategie verwendet wurden. Wie macht man das?

ErgebnisGruppe ist ein Objekt, das die Strategie und alle Backtest- und Crosscheck-Ergebnisse für die Strategie enthält. Wie der Name schon sagt, handelt es sich um eine Gruppe von Ergebnissen, die unter verschiedenen Schlüsseln gespeichert werden.

Um zu zählen, wie viele Gegenproben verwendet wurden, müssen wir alle Ergebnisse durchgehen und prüfen, ob die Ergebnisse für eine bestimmte Gegenprobe vorhanden sind. Wenn nicht, wurde die Gegenprobe nicht verwendet.

Um die Sache noch komplizierter zu machen, werden einige Ergebnisse (z. B. Monte Carlo) auf besondere Weise gespeichert, so dass wir ihre Verwendung anders bestimmen müssen.

Zum Zeitpunkt der Erstellung dieses Artikels gibt es 8 verschiedene Gegenproben in SQ:

  1. Was wäre, wenn Simulationen
  2. Manipulation von Monte-Carlo-Geschäften
  3. Höhere Backtest-Präzision
  4. Backtests auf zusätzlichen Märkten
  5. Monte-Carlo-Wiederholungstest-Methoden
  6. Profil / Sys. Param. Permutation
  7. Walk-Forward-Optimierung
  8. Walk-Forward-Matrix

Wenn Sie Retester so konfigurieren würden, dass er alle Gegenproben durchführt, und dann alle Schlüssel und Ergebnisse protokollieren würden, die in ResultsGroup gespeichert sind, würden Sie diese Schlüssel erhalten:
Portfolio
Hauptsache: EURUSD_M1/H1
CrossCheck_WasWenn
CrossCheck_HigherPrecision
ZusätzlicherMarkt: GBPUSD_M1/H1
ZusätzlicherMarkt: USDJPY_M1/H1
WF: 10 Läufe : 20 % OOS
WF: 5 Läufe : 10 % OOS
...
WF: 15 Läufe : 40 % OOS
WF: 20 Läufe : 40 % OOS

Beachten Sie, dass die tatsächliche Anzahl der Schlüssel auch von der Konfiguration der Come Cross Checks abhängt. Zum Beispiel wird jeder zusätzliche Backtest unter einem eigenen Schlüssel gespeichert, das gleiche gilt für WF-Läufe + OOS-Kombinationen.

Da es einige zusätzliche Märkte gab, gibt es auch einen speziellen Schlüssel Portfolio, der die Ergebnisse für das Portfolio der Haupt- und zusätzlichen Backtests enthält.

Beachten Sie auch, dass es keine Tasten für Monte Carlo oder Opt. Profil / Sys. Param. Permutationsergebnisse gibt. Sie werden auf eine andere Weise gespeichert, die wir später erklären werden.

 

Schritt 3 - Zählung "einfacher" Gegenkontrollen

Mit einfach meinen wir die Gegenproben, die ihre Ergebnisse als verschiedene Schlüssel in ErgebnisGruppe Objekte.

Die Feststellung, ob die Gegenprobe verwendet wurde, ist in diesem Fall einfach, der Code für filterStrategy() würde wie folgt aussehen:

public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception {
        List keys = rg.getResultKeys();
        
        int crosschecksCount = 0;
        boolean additionalMarketUsed = false;
        int wfCount = 0;
        
        for(int i=0; i 1) {
            // enthält Walk-Forward-Matrix
            crosschecksCount++;
            
            // das ist knifflig, wenn die WF-Matrix ausgeführt wurde, können wir nicht feststellen
            // ob auch eine einzelne Walk-Forward-Optimierung gelaufen ist,
            // also zählen wir sie auch mit
            crosschecksCount++;
        }

        // enthält Monte-Carlo-Handelsmanipulationen
        // enthält Monte-Carlo-Retest-Methoden
        // enthält Opt. Profil / Sys. Param. Permutation
        // FERTIGKEIT
        ...

Bestimmung der Verwendung von Was wäre wenn und Höhere Präzision Quervergleiche sind einfach - es wird nur geprüft, ob die Liste der Schlüssel die Konstante enthält.

Bestimmung der Verwendung von Backtests auf zusätzlichen Märkten ist ähnlich, wir müssen uns nur bewusst sein, dass jeder zusätzliche Backtest mit einem eigenen Schlüssel gespeichert wird.

Bestimmung der Verwendung von Walk-Forward-Optimierung und Matrix ist ebenfalls einfach, wir prüfen nur die Anzahl (falls vorhanden) der Schlüssel mit dem Präfix "WF:".

Der einzige Haken an der Sache ist, dass, wenn Walk-Forward-Matrix Crosscheck verwendet wurde, können wir nicht erkennen, ob auch einzelne Walk-Forward-Optimierung Gegenprobe verwendet, da sie denselben Schlüssel zur Speicherung ihres Ergebnisses verwendet.

Unser crosschecksCount enthält nun die tatsächliche Anzahl der verwendeten Gegenproben, außer bei Monte Carlo und Opt. Profil Gegenkontrollen, die wir im nächsten Schritt erkennen werden.

 

Schritt 4 - Erkennen der Verwendung eines "besonderen" Gegenchecks

Wie bereits gesagt, Monte Carlo und Opt. Profil / Sys. Param. Permutation Die Ergebnisse werden auf besondere Weise gespeichert.

Anstatt sie unter ihrem eigenen Schlüssel in ResultsGroup zu speichern, werden sie in speziellen Objekten im Hauptergebnis gespeichert.

Wir können feststellen, ob die Ergebnisse der Monte-Carlo-Manipulation auf diese Weise gegengeprüft wurden:

// Hauptergebnis abrufen, hier werden die Monte-Carlo-Simulationen gespeichert
Ergebnis mainResult = rg.mainResult();
        
if(mainResult.getInt("MonteCarloManipulation_NumberOfSimulations") > 0) {
// enthält das Ergebnis der Monte-Carlo-Handelsmanipulation
        crosschecksCount++;
}

Hauptergebnis ist das wichtigste Backtest-Ergebnis, hier werden die MC-Ergebnisse gespeichert. Wir versuchen einfach, eine Anzahl von MC-Simulationen zu erhalten, und wenn diese größer als 0 ist, wissen wir, dass sie einige MC-Manipulationsergebnisse enthält.

Wir können die Verwendung von Monte-Carlo-Wiederholungstests auf ähnliche Weise überprüfen:

if(mainResult.getInt("MonteCarloRetest_NumberOfSimulations") > 0) {
    // enthält das Ergebnis der Monte-Carlo-Handelsmanipulation
    crosschecksCount++;
}

Die letzte zu prüfende Option ist Opt. Profil / Sys. Param. Permutation. Es ist in einem eigenen Objekt in ResutsGroup gespeichert, wir werden einfach prüfen, ob das Objekt dort existiert:

if(rg.getOptimizationProfile() != null) {
        // enthält Opt. Profil / Sys. Param. Permutation Ergebnis
        crosschecksCount++;
    }

 

Schritt 5 - Speichern der Zählung in den Strategieergebnissen

Da wir nun die Anzahl der verwendeten Gegenproben haben, wollen wir sie in einer neuen Datenbankspalte anzeigen (die später erstellt wird)

Im Moment haben wir die Anzahl der Gegenproben in einer Variablen croschecksCount gespeichert, aber wir müssen sie als benutzerdefinierte Daten in ResultsGroup speichern, damit das Datenbankspalten-Snippet sie abrufen kann, wenn es ihren Wert anzeigt.

Glücklicherweise ist dies recht einfach - wir können jedes Objekt in ResultsGroup spezielle Werte speichern, indem wir es aufrufen:

rg.specialValues().set(String key, Object value);

in unserem Fall:

rg.specialValues().set("CA_NumberOfCrosschecks", crosschecksCount);

Wir werden diesen Wert im nächsten Schritt abrufen, um ihn in der Datenbank anzuzeigen.

Als Letztes geben wir true zurück, weil wir diese benutzerdefinierte Analyse nicht zum Filtern verwenden werden.


Hinweis -
Wenn Sie eine benutzerdefinierte Analyse zum Filtern verwenden möchten, geben Sie in dieser Methode einfach true oder false zurück, basierend auf den Berechnungen aus den Strategie-Backtests, und je nachdem wird die Strategie verworfen oder in der Datenbank gespeichert.

Der vollständige Code unserer filterStrategy() Methode ist:

    @Override
public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception {
    List keys = rg.getResultKeys();
    
    int crosschecksCount = 0;
    boolean additionalMarketUsed = false;
    int wfCount = 0;
    
    for(int i=0; i 1) {
        // enthält Walk-Forward-Matrix-Ergebnisse
        crosschecksCount++;
        
        // dies ist knifflig, wenn die WF-Matrix ausgeführt wurde, können wir nicht feststellen
        // ob auch eine einzelne Walk-Forward-Optimierung durchgeführt wurde,
        // also zählen wir sie auch mit
        crosschecksCount++;
    }

    // Hauptergebnis holen, hier werden die Monte-Carlo-Simulationen gespeichert
    Ergebnis mainResult = rg.mainResult();
    
    if(mainResult.getInt("MonteCarloManipulation_NumberOfSimulations") > 0) {
        // enthält das Ergebnis der Monte-Carlo-Handelsmanipulation
        crosschecksCount++;
    }

    if(mainResult.getInt("MonteCarloRetest_NumberOfSimulations") > 0) {
        // enthält das Ergebnis der Monte-Carlo-Handelsmanipulation
        crosschecksCount++;
    }
    
    if(rg.getOptimizationProfile() != null) {
        // enthält Opt. Profil / Sys. Param. Permutation Ergebnis
        crosschecksCount++;
    }
    
    
    rg.specialValues().set("CA_NumberOfCrosschecks", crosschecksCount);
    
    return true;
}

 

Schritt 6 - Neue Datenbanksäule hinzufügen

Wir haben die Anzahl der im benutzerdefinierten Analyseschnipsel verwendeten Gegenproben berechnet und möchten sie nun in der Datenbank anzeigen. Zu diesem Zweck müssen wir eine neue Datenbankspalte erstellen.

Klicken Sie erneut auf Neu erstellen in CodeEditor und erstellen Sie diesmal eine neue Datenbankspalte. Benennen Sie sie NumberOfCC.

Der Standardcode für das Datenbankspalten-Snippet sieht wie folgt aus:

Paket SQ.Columns.Databanks;

import com.strategyquant.lib.*;
import com.strategyquant.datalib.*;
import com.strategyquant.tradinglib.*;

public class NumberOfCC extends DatabankColumn {
    
  public NumberOfCC() {
    super("NumberOfCC",
          DatabankColumn.Decimal2, // Format der Wertanzeige
          ValueTypes.Maximize, // ob Wert maximiert / minimiert / auf einen Wert angenähert werden soll
          0, // Zielwert, falls Annäherung gewählt wurde
          0, // durchschnittliches Minimum dieses Wertes
          100); // durchschnittliches Maximum dieses Wertes
    
    setWidth(80); // Standardspaltenbreite in Pixel
    
    setTooltip("Ihr Tooltip hier");
    
    /* Wenn diese neue Spalte von anderen Spalten abhängt, die zuerst berechnet werden müssen, fügen Sie sie hier ein.
       Achten Sie darauf, dass Sie keine zirkulären Abhängigkeiten erstellen, wie z.B. A hängt von B ab und B hängt von A ab.
       Spalten (=Statistikwerte) werden durch den Namen der Klasse identifiziert)
    */
    //setDependencies("DrawdownPct", "NumberOfTrades");
  }
  
  //------------------------------------------------------------------------

  /**
   * Diese Methode sollte den berechneten Wert dieser neuen Spalte zurückgeben. Normalerweise sollten Sie ihn aus der Liste der Aufträge berechnen
   * oder aus einigen bereits berechneten statistischen Werten (andere Datenbankspalten).
   */
  @Override
  public double compute(SQStats stats, StatsTypeCombination combination, OrdersList ordersList, SettingsMap settings, SQStats statsLong, SQStats statsShort) throws Exception {
    
    /* ein Beispiel - so können Sie andere Werte erhalten, von denen dieser neue Wert abhängt */
    //int numberOfTrades = stats.getInt("NumberOfTrades");
    //double drawdownPct = stats.getDouble("DrawdownPct");
    
    /* ein Beispiel - Berechnung des Nettogewinns aus der Liste der Trades */
    double netProfit = 0;

    for(int i = 0; i<ordersList.size(); i++) {
      Order order = ordersList.get(i);
      
      if(order.isBalanceOrder()) {
        // Saldoaufträge (Einzahlungen, Abhebungen) nicht mitzählen
        fortfahren;
      }
      
      /* Methode getPLByStatsType gibt automatisch PL zurück, abhängig vom angegebenen Statistiktyp - in Geld, % oder Pips */
      double PL = getPLByStatsType(Auftrag, Kombination);
      
      netProfit += PL;
    }

    /* Runden Sie den Wert und geben Sie ihn zurück. Er wird in der Statistik unter dem Schlüssel "NumberOfCC" gespeichert */
    return round2(NettoGewinn);
  }
}

Sie hat eine Berechnen() Methode, die Statistiken und Aufträge durchgehen kann, um die jeweilige Metrik zu berechnen.

In unserem Fall brauchen wir das nicht, wir werden nichts aus Bestellungen verwenden, stattdessen werden wir den Wert für CA_NumberOfCrossChecks die wir in einer benutzerdefinierten Analyse berechnet haben.

Wir können also die gesamte Berechnen() Methode und verwenden eine spezielle Methode getValue() stattdessen. Dieser wird nicht standardmäßig generiert, sondern wird verwendet, um spezielle Werte abzurufen, z. B. Symbol und Zeitrahmen.

getValue() erhält die ErgebnisGruppe als Parameter und gibt einen String zurück, der in der Datenbank für diese Spalte angezeigt wird.

Unsere vereinfachte Datenbanksäule sieht wie folgt aus:

Paket SQ.Columns.Databanks;

import com.strategyquant.lib.*;
import com.strategyquant.datalib.*;
import com.strategyquant.tradinglib.*;

public class NumberOfCC extends DatabankColumn {
    
  public NumberOfCC() {
    super("NumberOfCC", DatabankColumn.Decimal2, ValueTypes.Maximize, 0, 0, 100);
  }
  
  //------------------------------------------------------------------------
  
  @Override
  public String getValue(ResultsGroup rg, String resultKey, byte direction, byte plType, byte sampleType) throws Exception {
    int value = rg.specialValues().getInt("CA_NumberOfCrosschecks", -1);
    
    if(wert == -1) {
      // dies bedeutet, dass der Wert nicht gesetzt wurde
      return NOT_AVAILABLE;
    } sonst {
      // Rückgabe des in String konvertierten Wertes
      return java.lang.Integer.toString(value);
    }
  }
}

 

Beachten Sie, dass wir einen Aufruf verwendet haben:

rg.specialValues().getInt("CA_NumberOfCrosschecks", -1);

um den Wert zu erhalten, der zuvor von der benutzerdefinierten Analyse als int (Zahl) gespeichert wurde.

Der Wert -1 ist der Standardwert und wird verwendet, wenn kein Wert für den Schlüssel CA_NumberOfCrossChecks gefunden werden. Dies geschieht, wenn wir die Benutzeroberfläche nicht für die Ausführung unserer benutzerdefinierten Analysemethode konfigurieren.

In diesem Fall wird N/A string zurückgegeben.

 

Besonderer Hinweis zur Filterung nach dieser Spalte

Da es sich bei dieser Datenbankspalte um eine spezielle Spalte handelt, deren Wert von der benutzerdefinierten Analyse berechnet wird, kann sie NICHT in benutzerdefinierten Filtern in Ranking verwendet werden. Dies wird nicht funktionieren, da diese Filter vor dem Start der benutzerdefinierten Analyse ausgewertet werden und diese Spalte dann keinen Wert hat.

Um nach Werten zu filtern, die durch eine benutzerdefinierte Analyse berechnet wurden, verwenden Sie bitte die true/false-Rückgabe der Option filterStrategy() Methode im benutzerdefinierten Analyse-Snippet.

 

Schritt 7 - Damit alles funktioniert

Nach erfolgreicher Kompilierung unserer neuen Snippets in CodeEditor und Neustart von StrategyQuant können wir nun versuchen, sie zu verwenden. Wir können dies in Retester tun, um zu sehen, wie es funktioniert.

Erstellen Sie zunächst eine neue Datenbankansicht, nennen Sie sie CA View und fügen Sie einige Spalten sowie unsere neue Spalte hinzu NumberOfCC dort.

Wenn wir zu dieser Ansicht wechseln, werden wir sehen, dass unsere neue Spalte N/A ist:

Das liegt daran, dass der angezeigte Wert noch nicht berechnet ist.

Verwenden wir also das neue benutzerdefinierte Analyse-Snippet in der Wiederholungsprüfer - müssen wir es nur in der Registerkarte Rangordnung wie folgt konfigurieren:

Unser Beispiel für eine benutzerdefinierte Analyse wird nun auf jede Strategie angewendet, die in Retester erneut getestet wird, bevor sie in einer Datenbank gespeichert wird.

Lassen Sie uns also versuchen, einige Gegenkontrollen in Wiederholungsprüfer und führen Sie es aus.

Je nachdem, wie viele Gegenproben Sie tatsächlich ausgewählt haben, sollten Sie nun eine Zahl in der neuen Spalte sehen:

 

Schlussfolgerung

Dies war eine einfache Demonstration der neuen benutzerdefinierten Analysefunktionen, die pro Strategie angewendet werden. Wie Sie sehen können:

  • es kann zur Berechnung neuer Metriken verwendet werden, die die Grenzen eines einzelnen Backtests oder eines Crosschecks überschreiten
  • es kann verwendet werden, um neue Metriken zu implementieren, die in der Datenbank angezeigt werden
  • er kann zum Filtern verwendet werden (indem er false zurückgibt)

 

War dieser Artikel hilfreich? Der Artikel war nützlich Der Artikel war nicht nützlich

Abonnieren
Benachrichtigen Sie mich bei
5 Kommentare
Älteste
Neuestes Meistgewählt
Inline-Rückmeldungen
Alle Kommentare anzeigen
tan8858
tan8858
21. 8. 2021 3:32 pm

Bitte veröffentlichen Sie den vollständigen Code für Schritt 5 (am besten als Beispiel für Schritt 6)
In meinem Code-Editor meldet er einen Fehler
"Symbol nicht gefunden symbol:class List location:class SQ.CustomAnalysis.CAExampleStr"

List keys = rg.getResultKeys();

Sorry, ich bin neu hier und konnte den Fehler nicht finden

Emmanuel
Antwort an  tan8858
26. 11. 2021 4:17 pm

Ich auch, ich bekomme den gleichen Fehler.

Emmanuel
Antwort an  tan8858
26. 11. 2021 4:54 Uhr

Ich denke, wir müssen hinzufügen: importieren java.verwenden.*;  
am Anfang des Codes
Es sollte funktionieren

tan8858
tan8858
Antwort an  Emmanuel
28. 11. 2021 10:00 Uhr

Ich danke Ihnen!

Emmanuel
25. 11. 2021 20:59 Uhr

Ausgezeichnet!