Dokumentation
Anwendungen
Zuletzt aktualisiert am 16. 11. 2021 von Mark Fric
Beispiel - benutzerdefinierte Analyse pro Datenbank
Inhalt der Seite
In diesem Beispiel geht es um die benutzerdefinierte Analyse pro Datenbank. Im Gegensatz zur benutzerdefinierten Analyse pro Strategie, die eine Strategie verarbeitet, läuft dieses CA-Snippet über die gesamte Datenbank und kann auf jedes Strategieergebnis in einer Datenbank zugreifen.
Die benutzerdefinierte Analyse pro Datenbank kann nur über eine benutzerdefinierte Aufgabe im benutzerdefinierten Projekt ausgeführt werden.
In diesem Beispiel werden wir ein ziemlich komplexes Beispiel für ein benutzerdefiniertes Analyse-Snippet erstellen, das ein Portfolio aus den Walk-Forward-Ergebnissen der Strategien in der Datenbank erstellt.
Dies war eine Bitte aus unserer ersten Coding-Sitzung.
Schritt 1 - Neues benutzerdefiniertes Analyse-Snippet erstellen
Öffnen Sie CodeEditor, klicken Sie auf Neu erstellen und wählen Sie ganz unten die Option Benutzerdefinierte Analyse (CA). Benennen Sie sie WFPortfolio.
Dadurch wird ein neues Snippet erstellt WFPortfolio.java im Ordner Benutzer/Snippets/SQ/BenutzerdefinierteAnalysen
Diesmal setzen wir sie als TYPE_PROCESS_DATABANK im Konstruktor der Klasse:
public WFPortfolio() { super("WFPortfolio", TYPE_PROCESS_DATABANK); }
Schritt 2 - Implementierung der Methode processDatabank()
Diese CA ruft die Methode processDatabank() die 4 Parameter hat:
- String-Projekt - Name des Projekts, in dem die CA-Aufgabe platziert ist
- String-Aufgabe - Name der CA-Aufgabe
- String databankName - Name der Datenbank, mit der das CA-Snippet arbeitet
- ArrayList databankRG - Liste aller Strategien (ResultsGroups) aus dieser Datenbank.
Sie gibt auch eine Liste ArrayList zurück, die nur die Strategien aus der Eingabeliste enthalten sollte, die in der Datenbank erhalten bleiben sollen.
Auf diese Weise kann sie als Filter verwendet werden - Sie können die Strategien in der databankRG-Liste durchgehen und eine neue Liste erstellen, in die Sie nur die Strategien aufnehmen, die einige Ihrer Kriterien erfüllen. Strategien, die nicht in der Ausgabeliste enthalten sind, werden aus der Datenbank gelöscht.
In unserem Fall wollen wir nicht etwas aus der Datenbank löschen, sondern stattdessen eine neue Strategie (unser neu erstelltes Portfolio) hinzufügen. Wir werden sehen, wie es gemacht wird.
Zusammengefasst funktioniert der Code folgendermaßen:
- Erstellt eine neue Ergebnisgruppe für das Portfolio - sie wird zu Beginn leer sein
- Geht jede Strategie in der Datenbank durch und prüft, ob sie ein WF-Ergebnis hat. Wenn ja, wird dieses Ergebnis in ein neues Portfolio mit allen WF-Aufträgen kopiert.
- Berechnung des Portfolio-Ergebnisses aus den Teilergebnissen - standardmäßig wird es nicht aus den WF-Ergebnissen berechnet, so dass wir dies im Code selbst tun müssen
- Hinzufügen der fertigen Portfolio ResultsGroup zur Datenbank
Der Code ist lang und komplex, wir werden ihn hier nicht vollständig erklären. Er wird am Ende dieses Artikels vollständig kommentiert dargestellt.
Schritt 3 - Ausführen unseres benutzerdefinierten CA-Snippets
Um ein CA-Snippet "pro Datenbank" auszuführen, müssen wir ein benutzerdefiniertes Projekt und eine benutzerdefinierte Aufgabe des Typs BenutzerdefinierteAnalyse.
Wählen Sie die WFPortfolio in der benutzerdefinierten Aufgabe.
Fügen Sie dann einige Strategien mit Walk-Forward-Ergebnissen zur Ergebnisdatenbank hinzu und führen Sie die Aufgabe aus.
Es sollte eine neue Strategie mit dem Namen WF-Aktienportfolio erstellt werden, die WF-Aktien aus allen Strategien in Results enthält.
und das Portfolio-Eigenkapital aus allen WF-Ergebnissen sieht wie folgt aus:
Vollständig kommentierter Code des Snippets
package SQ.CustomAnalysis; import com.strategyquant.lib.*; import java.util.ArrayList; import com.strategyquant.tradinglib.*; import com.strategyquant.tradinglib.project.ProjectEngine; import com.strategyquant.tradinglib.results.SpecialValues; import com.strategyquant.tradinglib.results.stats.comparator.OrderComparatorByOpenTime; public class WFPortfolio extends CustomAnalysisMethod { //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ public WFPortfolio() { super("WFPortfolio", TYPE_PROCESS_DATABANK); } //------------------------------------------------------------------------ @Override public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception { return true; } //------------------------------------------------------------------------ @Override public ArrayList<ResultsGroup> processDatabank(String project, String task, String databankName, ArrayList<ResultsGroup> databankRG) throws Exception { // create new ResultsGroup for our new portfolio ResultsGroup portfolioRG = new ResultsGroup("WF Equity portfolio"); // get OrderList from new portfolio (empty so far), // we will insert WF orders there OrdersList portfolioOrdersList = portfolioRG.orders(); // go through all strategy results in databank for(ResultsGroup strategyRG : databankRG) { // skip strategies whose name starts with "WF Equity portfolio" // this will ensure that we'll ignore our portfolios created // in previous runs String strName = strategyRG.getName(); if(strName.startsWith("WF Equity portfolio")) { continue; } // get best Walk-Forward result from a strategy // If it doesn't exist it will return null Result wfResult = getWFResult(strategyRG); if(wfResult == null) { continue; } // --------------------- // wfResult is the best Walk-Forward result from existing strategy // clone it to a new result with all its settings String wfResultKey = wfResult.getResultKey(); // we'll name the new subresult as "StrategyName "+ "existing WF result name" String newSubResultKey = String.format("%s-%s", strategyRG.getName(), wfResultKey); // clone settings to use them in the new result SettingsMap settingsMapCopy = wfResult.getSettings().clone(); // create new result that will be added to the portfolio Result wfResultCopied = new Result(newSubResultKey, portfolioRG, settingsMapCopy); portfolioRG.addSubresult(newSubResultKey, settingsMapCopy, wfResultCopied); // --------------------- // copy WF orders from original strategy to new results group OrdersList filteredOL = strategyRG.orders().filterWithClone(wfResultKey, Directions.Both, SampleTypes.FullSample); for(int i=0; i<filteredOL.size(); i++) { Order order = filteredOL.get(i); // change setup name to match the new result name in the new portfolio order.SetupName = newSubResultKey; order.IsInPortfolio = 1; // add the order to the portfolio OrdersList portfolioOrdersList.add(order); } // copy also symbol definitions from old strategy // this is needed for proper cancullation of PL and metrics portfolioRG.symbols().add(strategyRG.symbols()); } // --------------------- // compute portfolio result // normally we'd call portfolioRG.createPortfolioResult() but the default version // ignores WF results, so we have to implement it by ourselves createPortfolioResult(portfolioRG); // portfolio ResultsGroup is complete, now add it to the databank Databank databank = ProjectEngine.get(project).getDatabanks().get(databankName); databank.add(portfolioRG, true); return databankRG; } //------------------------------------------------------------------------ /** * returns best Walk-Forward result - if it exists */ private Result getWFResult(ResultsGroup rg) throws Exception { String wfKey = rg.getBestWFResultKey(); if(wfKey == null) { return null; } return rg.subResult(wfKey); } //------------------------------------------------------------------------ /** * creates a Portfolio result from all the existing sub-results in the ResultsGroup * This is function from standard SQ library, with only exception that this works * also on WF results - standard ResultsGroup.createPortfolioResult() * ignores WF results when creating portfolio. * * @param portfolioRG * @throws Exception */ public void createPortfolioResult(ResultsGroup portfolioRG) throws Exception { Result firstResult = null; String tf = null; int resultsInPortfolio = 0; // go through all existing sub-results and get their initial capital and from-to dates for(String existingResultKey : portfolioRG.getResultKeys()) { Result result = portfolioRG.subResult(existingResultKey); resultsInPortfolio++; if(firstResult == null) { firstResult = result; } } if(resultsInPortfolio <= 1) { throw new Exception("ResultGroup doesn't contain more Results, cannot create a portfolio!"); } if(firstResult == null) { throw new Exception("No Result in ResultGroup to create portfolio!"); } SettingsMap settings = firstResult.getSettings(); Result portfolioResult = new Result(ResultsGroup.Portfolio, portfolioRG, settings); portfolioRG.removeSubresult(ResultsGroup.Portfolio, true); portfolioRG.addSubresult(ResultsGroup.Portfolio, settings, portfolioResult); portfolioRG.specialValues().setString(SpecialValues.Symbol, ResultsGroup.Portfolio); portfolioRG.specialValues().setString(SpecialValues.Timeframe, "N/A"); // for portfolio sort orders by their open time OrdersList ordersList = portfolioRG.orders(); ordersList.sort(new OrderComparatorByOpenTime()); // compute all metrics on the portfolio portfolioResult.computeAllStats(portfolioRG.specialValues(), portfolioRG.getOOS()); } }
War dieser Artikel hilfreich? Der Artikel war nützlich Der Artikel war nicht nützlich
Mark, vielen Dank für diese beeindruckende Arbeit !!!!!! Es ist genau das, was ich brauche !!!!
SQX ist wirklich einzigartig.
Anhand eines Beispiels kann ich eine neue Idee entwickeln.
Der vollständig kommentierte Code ist wirklich hilfreich
Es funktioniert gut !!!!!!! Dankeschön