Dokumentation

Anwendungen

Zuletzt aktualisiert am 10. 11. 2021 von Mark Fric

Aufruf von Python aus SQ (Java) - Einführung

Python ist eine sehr beliebte Sprache, insbesondere in der wissenschaftlichen und quantitativen Gemeinschaft, dank seiner umfangreichen numerischen und statistischen Bibliotheken.
In diesem Artikel zeigen wir einige Möglichkeiten, wie Python von StrategyQuant Java Snippet aufgerufen werden kann.

Python-Skript

Wir werden ein sehr einfaches Python-Skript verwenden, das in der Datei sqpython.py definiert wird

print("Hallo aus Python!")

Wenn man es mit dem Python-Interpreter aufruft, gibt es die Ausgabe "Hello from Python!"

$ python sqpython.py
Hallo aus Python!

In diesem Beispiel rufen wir dieses Python-Skript aus dem CustomAnalysis-Snippet auf - es ruft das Skript auf und speichert seine Ausgabe in einem speziellen Wert.

Um die Ausgabe des Python-Skripts in SQ zu sehen, erstellen wir eine spezielle Datenbankspalte, die diesen Wert anzeigt. Dieses Beispiel ist eine Variation des Beispiels "Benutzerdefinierte Analyse": Beispiel - individuelle Analyse pro Strategie.

Nach der Schritt 6 Im vorangegangenen Beispiel erstellen wir eine benutzerdefinierte Datenbankspalte, die den in der Ergebnisvariablen der Strategie gespeicherten String-Wert liest und anzeigt (dies wird durch das Snippet Benutzerdefinierte Analyse nach dem Aufruf des Python-Skripts erledigt).

Der gesamte Code der neuen Datenbankspalte lautet:

Paket SQ.Columns.Databanks;

import com.strategyquant.tradinglib.DatabankColumn;
import com.strategyquant.tradinglib.ResultsGroup;
import com.strategyquant.tradinglib.ValueTypes;

public class TestPythonOutput extends DatabankColumn {

    public TestPythonOutput() {
        super("TestPythonOutput", DatabankColumn.Text, ValueTypes.Maximize, 0, 0, 100);
    }
  
  //------------------------------------------------------------------------
    
    @Override
    public String getValue(ResultsGroup rg, String resultKey, byte direction, byte plType, byte sampleType) throws Exception {
        String value = rg.specialValues().getString("PythonOutput", NOT_AVAILABLE);
        
        return value;
    }
}

 

Wenn Sie dieses Snippet kompilieren, SQ neu starten und die neue Spalte zu Ihrer Datenbank hinzufügen, sollten Sie in dieser Spalte die Zeichenfolge "N/A" sehen - das liegt daran, dass wir noch kein Python-Skript aufgerufen haben, um sie zu füllen:

Datenbankspalte Python-Ausgabe NA

Unser Ziel mit Python ist es, diese Spalte mit dem von Python zurückgegebenen Wert zu füllen.

 

Aufrufen von Python-Skripten aus Java mit Jython

1TP6Der wohl einfachste Weg, Python aus SQ aufzurufen, ist die Verwendung von Jython. Jython ist eine Java-Implementierung von Python, die auf der JVM läuft, was bedeutet, dass es eine reine Java-Bibliothek ohne externe Abhängigkeiten ist.

Sie können es (die Standalone-Version JAR) herunterladen von https://www.jython.org/download

und setzen Sie es zu Ihrem (SQ Installation) Ordner.

Dies ist ein Ordner, in dem benutzerdefinierte JAR-Bibliotheken abgelegt werden können, die von SQ beim Start gefunden und geladen werden.

Sobald das jython-standalone-2.7.2.jar vorhanden ist und Sie SQ neu starten, können Sie die Jython-Funktionalität in SQ nutzen.

Wir erstellen nun ein neues benutzerdefiniertes Analyse-Snippet mit dem Namen CAJythonScript mit dem folgenden Code:

Paket SQ.CustomAnalysis;

import java.io.FileReader;
import java.io.StringWriter; import java.io;
import java.util.List;

import javax.script.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.strategyquant.lib.app.MainApp;
import com.strategyquant.tradinglib.CustomAnalysisMethod;
import com.strategyquant.tradinglib.Result;
import com.strategyquant.tradinglib.ResultsGroup; import com.strategyquant.tradinglib.ResultsGroup;

public class CAJythonScript extends CustomAnalysisMethod {
    public static final Logger Log = LoggerFactory.getLogger(CAJythonScript.class);
    
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    
    public CAJythonScript() {
        super("CAJythonScript", TYPE_FILTER_STRATEGY);
    }
    
    //------------------------------------------------------------------------
    
    @Override
    public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception {
        Log.info("Aufruf eines Jython-Skripts");

        StringWriter writer = new StringWriter();
        ScriptContext context = new SimpleScriptContext();
        context.setWriter(writer);

        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine-Engine = manager.getEngineByName("jython");
        String pythonScript = MainApp.getDataPath()+"sqpython.py";
        Log.info("Jython-Skriptpfad: "+pythonScript);
        engine.eval(new FileReader(pythonScript), context);

        String result = writer.toString().trim();

        Log.info("Jython-Skriptausgabe: "+Ergebnis);

        rg.specialValues().set("PythonOutput", result);

        return true;
    }
}

Der Code ist recht einfach, er verwendet die in Java eingebaute Skript-Engine, und wir werden die "jython"-Engine als Namen wählen.

Dann rufen wir einfach engine.eval() und übergeben Sie ihm den Pfad zu unserer Python-Skriptdatei als Parameter.

Dieses Snippet führt das Python-Skript aus, liest seine Ausgabe und speichert diese unter dem Schlüssel "PythonOutput" in der benutzerdefinierten Werte-Map in unserer Strategie (ResultGroup).

Wenn Sie kompiliert haben, starten Sie SQ neu und führen Sie unser neues Custom Analysis Snippet aus.

Sie können ein benutzerdefiniertes Analyse-Snippet als Teil des Builder/Retester-Prozesses oder als benutzerdefinierte Aufgabe in einem benutzerdefinierten Projekt ausführen. In unserem Beispiel werden wir es einfach in Retester ausführen:

Benutzerdefinierte Analyse Python-Schnipsel

Beachten Sie, dass das Skript für die benutzerdefinierte Analyse für jede Strategie nach dem Retest aufgerufen wird, also stellen Sie sicher, dass Sie Start drücken, um den Retest auszuführen.

Sie sollten sehen, dass unsere Kontrolldatenbankspalte TestPythonOutput jetzt den Text "Hello from Python!" enthält. - enthält, der die Ausgabe unseres Python-Skripts ist:

Datenbankspalte python Ausgabe

Das bedeutet, dass das Python-Skript ordnungsgemäß aufgerufen wurde und den Wert zurückgegeben hat, der jetzt in unserer benutzerdefinierten Datenbankspalte angezeigt wird.

Jython Vorteile:

  • Relativ schneller Aufruf - kein externer Prozess muss aufgerufen werden, alles wird intern in der JVM erledigt
  • Einfach zu verwenden - fügen Sie einfach eine .JAR-Bibliothek zu SQ

Benachteiligungen:

  • Jython ist die Python-Implementierung für Java und enthält möglicherweise nicht dieselben Unterpakete wie das native Python.
  • Es wird nicht mehr aktiv entwickelt und ist daher nur für eine ältere Version von Python verfügbar

Jython ist für einfache Skripte geeignet, aber wenn Sie die volle Leistungsfähigkeit von Python mit all seinen unterstützten Bibliotheken benötigen, müssen Sie einen anderen Weg wählen.

 

Aufruf von Python aus Java durch Aufruf einer externen Python-Installation

Wie der Titel schon sagt, wird diese Methode einen nativen Betriebssystemprozess aufrufen, um Python zu starten und unser einfaches Skript auszuführen.

Das bedeutet, dass Sie Python auf Ihrem Computer installiert haben müssen. Die Installation von Python geht über den Rahmen dieses Artikels hinaus, Sie können die Dokumentation auf der https://www.python.org/ Website.

Sie sollten Python auf Ihrem Computer installieren, damit Sie normalerweise Skripte durch den Aufruf von

$ python myscript.py

über die Befehlszeile.

Wir erstellen ein neues benutzerdefiniertes Analyseschnipsel namens CAPythonScript mit dem folgenden Code:

Paket SQ.CustomAnalysis;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.strategyquant.lib.app.MainApp;
import com.strategyquant.tradinglib.CustomAnalysisMethod;
import com.strategyquant.tradinglib.Result;
import com.strategyquant.tradinglib.ResultsGroup; import com.strategyquant.tradinglib.ResultsGroup;

public class CAPythonScript extends CustomAnalysisMethod {
    public static final Logger Log = LoggerFactory.getLogger(CAPythonScript.class);
    
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    
    public CAPythonScript() {
        super("CAPythonScript", TYPE_FILTER_STRATEGY);
    }
    
    //------------------------------------------------------------------------
    
    @Override
    public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception {
    
        String pythonScript = MainApp.getDataPath()+"sqpython.py";
        
        Log.info("Aufruf des Python-Skripts: "+pythonScript);
        
        ProcessBuilder processBuilder = new ProcessBuilder("py", pythonScript);
        processBuilder.redirectErrorStream(true);

        Process process = processBuilder.start();
        String result = readProcessOutput(process.getInputStream());

        int exitCode = process.waitFor();
        
        Log.info("Python-Skriptausgabe: "+Ergebnis);

        rg.specialValues().set("PythonOutput", Ergebnis);
        
        return true;
    }

    //------------------------------------------------------------------------

    private String readProcessOutput(InputStream stream) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));

        StringBuilder stringBuilder = new StringBuilder();

        try {
        String line;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
        }

        stream.close();

        } catch (IOException ex) {
        Log.error("Fehler: ", ex);
        }
        
        return stringBuilder.toString();
    }
}

In diesem Schnipsel wird der Aufruf des externen Skripts mit ProcessBuilder durchgeführt, aber auch hier ist es einfach zu verstehen.

Sie müssen einen ProcessBuilder mit den angegebenen Parametern erstellen und ihn dann durch Aufruf der .start() Methode und sammeln ihre Ausgabe.

Dadurch wird das extern installierte Python aufgerufen, so dass es alle Bibliotheken und Module enthalten kann, die Sie in Ihrem Standard-Python verwenden können.

 

Aufruf von Python über HTTP

In den beiden vorangegangenen Beispielen ging es um die direkte Ausführung von Python-Skripten, wie man sie von der Kommandozeile aus durchführen kann.

Eine andere Möglichkeit, Python von SQ aus aufzurufen, wäre die Verwendung eines HTTP-Protokolls als Abstraktionsschicht zwischen den beiden verschiedenen Sprachen.

Python wird standardmäßig mit einem einfachen eingebauten HTTP-Server geliefert, den wir für den Austausch von Inhalten oder Dateien über HTTP verwenden können:

python -m http.server 9000

 

Wenn Sie zu http://localhost:9000wird der Inhalt des Verzeichnisses aufgelistet, in dem wir den vorherigen Befehl gestartet haben.

Sie können gängige Web-Frameworks verwenden wie Flachmann oder Django um Web-Endpunkte in Python zu erstellen.
Sobald Sie die Web-Endpunkte in Python haben, können Sie sie von SQ aus mit einer der Java-HTTP-Bibliotheken aufrufen.

Wir erstellen ein neues benutzerdefiniertes Analyse-Snippet namens CAPythonHttp, das Python über HTTP aufruft:

Paket SQ.CustomAnalysis;

import com.strategyquant.lib.SQUtils;
import com.strategyquant.lib.app.MainApp;
import com.strategyquant.tradinglib.CustomAnalysisMethod;
import com.strategyquant.tradinglib.ResultsGroup; import com.strategyquant.tradinglib.ResultsGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.script.ScriptContext;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.SimpleScriptContext; import javax.script;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;

public class CAPythonHttp extends CustomAnalysisMethod {
    public static final Logger Log = LoggerFactory.getLogger(CAPythonHttp.class);

    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------

    public CAPythonHttp() {
        super("CAJythonScript", TYPE_FILTER_STRATEGY);
    }
    
    //------------------------------------------------------------------------
    
    @Override
    public boolean filterStrategy(String project, String task, String databankName, ResultsGroup rg) throws Exception {
        HttpURLConnection connection = null;

        try {
            Log.info("Aufruf von Python über HTTP");

            URL url = new URL("http://localhost:9000");
            connection = (HttpURLConnection)url.openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Content-Type", "text/html");
            connection.setRequestProperty("Content-Language", "en-US");

            connection.setUseCaches(false);
            connection.setDoOutput(true);

            // Antwort abrufen
            InputStream is = connection.getInputStream();
            BufferedReader rd = new BufferedReader(new InputStreamReader(is));
            StringBuilder response = new StringBuilder(); // oder StringBuffer wenn nicht Java 5+
            String Zeile;
            while((Zeile = rd.readLine()) != null) {
                response.append(line);
                response.append('\r');
            }
            rd.close();

            String result = response.toString();

            // HTML-Tags aus der Antwort entfernen
            result = result.replaceAll("\\", "");

            Log.info("PythonHTTP-Skriptausgabe: "+Ergebnis);

            // wir werden nur die ersten 10 Zeichen des Ergebnisses speichern
            rg.specialValues().set("PythonOutput", result.substring(0, 10));

        } catch (Exception e) {
            throw new Exception("Anfragefehler: GET, " + e.getMessage());
        } finally {
            if(Verbindung != null) {
                connection.disconnect();
            }
        }

        return true;
    }
}

Auch hier sollte es einfach zu folgen sein. Das Skript erstellt eine HTTP-Anfrage an eine bestimmte URL und liest die Antwort.

In diesem Beispiel wäre die Antwort ein langes HTML-Dokument mit einer Verzeichnisliste. Daher entfernen wir die HTML-Tags und kürzen die Antwort auf 10 Zeichen.

Sie sollten die folgende Ausgabe sehen:

Datenbankspalte Python http

 

Übergabe von Parametern

An dieser Stelle werden die Dinge interessant und komplizierter. Der Aufruf von Python-Skripten aus SQ ohne Übergabe von Parametern hat nur sehr begrenzten Nutzen.

Normalerweise möchten Sie einige (oder viele) Parameter von SQ an Python übergeben - den Namen der Strategie, die Liste der Trades, die Testergebnisse usw., damit Sie in Python mit ihnen arbeiten und möglicherweise etwas zurückgeben können.

Die Art und Weise, wie Sie diese Parameter übergeben und die Ergebnisse von Python zurückbekommen, hängt von der Python-Aufrufmethode ab, die Sie verwenden.

Im Falle eines HTTP-Anrufs ist die Situation einfach.

Sie können sogar sehr große Datenmengen zwischen SQ und Python unter Verwendung von Standard-HTTP-Anfrage/Antwort übertragen.

Sie können Daten als Standard-GET-Parameter oder POST-Daten an Ihren Python-Web-Endpunkt senden. Sie können Daten als HTTP-Antwort an SQ zurückgeben.

Im Falle eines externen Python-Skriptaufrufs (mit Jython oder Python-Aufruf)

Wenn die übergebenen Daten klein sind (z. B. nur der Name der Strategie), können Sie sie als Argumente des Skripts übergeben.

Beim Senden größerer Daten besteht eine Möglichkeit darin, die Daten in einer temporären Datei in einem beliebigen Format in SQ zu speichern und dann die Daten aus dieser temporären Datei in Python zu laden. Auf die gleiche Weise können Sie größere Daten von Python an SQ senden.

 

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

Abonnieren
Benachrichtigen Sie mich bei
2 Kommentare
Älteste
Neuestes Meistgewählt
Inline-Rückmeldungen
Alle Kommentare anzeigen
Bienenhändler
10. 11. 2021 13:24 Uhr

Vielen Dank

Emmanuel
13. 1. 2022 1:36 Uhr

Dankeschön

Verwandte Beiträge