Dokumentation

Anwendungen

Zuletzt aktualisiert am 21. 6. 2022 von Mark Fric

Filter nach Korrelation - Beispiel für ein Plugin

Hinweis

  • Dieses Beispiel und die dafür benötigte Funktionalität ist nur in SQ Build 136 Dev 2 oder höher enthalten.
  • Die beschriebene Funktionalität ist nur für "fortgeschrittene" und engagierte Benutzer gedacht. Sie ist nicht vollständig dokumentiert, dies ist das erste Beispiel, das diese Möglichkeit zeigt.

 

Dies ist ein Beispiel dafür, wie Sie die SQX-Funktionalität mit Ihrem eigenen Plugin erweitern können.

StrategyQuant X ist als Plugin-basiertes System konzipiert, jeder Teil des Programms ist ein Plugin. Die Erweiterung von SQX mit einem eigenen Plugin war jedoch bis jetzt (Build 136 Dev 2) nicht möglich, da man Zugang zum vollständigen Quellcode benötigte und keine Möglichkeit hatte, sein Plugin zu kompilieren.

SQX UI ist im Grunde eine in AngularJS geschriebene Webanwendung, deren Backend aus Java-Servlets besteht.

In diesem Beispiel zeigen wir Schritt für Schritt, wie man ein einfaches Plugin mit einer einfachen Benutzeroberfläche erstellt.

Das vollständige Plugin ist auch als ZIP-Datei im Anhang zu diesem Artikel verfügbar.

 

Dieses Plugin wird eine Funktion zum Filtern von Strategien nach Korrelation implementieren. Es funktioniert wie folgt:

  • Plugin wird eine neue Schaltfläche hinzugefügt Nach Korrelation filtern zur Datenbank
  • Beim Klicken auf die Schaltfläche wird ein Popup-Dialog angezeigt, in dem der Benutzer die maximal zulässige Korrelation konfiguriert
  • Wenn dies bestätigt wird, ruft das Plugin seine Backend-Methode auf und geht alle Strategien in der aktuellen Datenbank durch und filtert (entfernt) diejenigen heraus, die eine höhere Korrelation als Max mit anderen Strategien in dieser Datenbank haben.

 

Credits - dieses Plugin basiert auf dem Code, der von AgentPot auf Discord als Teil unserer Live-Coding-Sitzungenwird der Code mit seiner Zustimmung verwendet.

 

Speicherort der Benutzer-Plugins

Benutzerentwickelte Plugins sollten im Ordner {SQ-Installation}/Benutzer/extend/Plugins. Sie können die ZIP-Datei dieses Plugins gerne herunterladen und in diesen Ordner entpacken.

Wenn Sie CodeEditor öffnen, sollten Sie dort den Ordner dieses Plugins sehen:

 

Dieses Plugin besteht aus drei Dateien:

  • FilterByCorrelationServlet.java - java-Datei, die die Funktionalität der Filterung von Strategien nach Korrelation implementiert.
  • modul.js - JavaScript-Datei mit der Definition des Moduls in Angular
  • popup.html - HTML-Datei, die die Benutzeroberfläche des neuen Plugins enthält

Gehen wir sie der Reihe nach durch, beginnend mit den einfachsten.

 

 

Definition der Benutzeroberfläche für das Plugin - popup.html

Das Plugin UI wird in der Datei popup.html. Es handelt sich um eine Standard-HTML-Datei, die in einem modalen Fenster angezeigt wird.

Es gibt DIVs, um das Fenster zu dekorieren, das eigentliche "Formular" ist im DIV modal-body definiert:

<!-- Modal window-->
<div class="modal centered" id="filterByCorrelationButtonModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title" id="myModalLabel" tsq>Nach Korrelation filtern</h4>
            </div>
            <div class="modal-body" style="height: auto;">
                Funktionalität - alle Strategien, deren Korrelation größer als Max ist, werden aus der aktuellen Datenbank entfernt.
                <label tsq>Einstellungen zur Filterung</label>
                <div class="row">
                    <div class="col-sm-12">
                        <div class="col-sm-6">
                            <label class="sqn-label" tsq>Korrelationszeitraum:</label>
                        </div>
                        <div class="col-sm-6">
                            <select id="fbc-correlation-period" class="setting-control">    
                                <option value="5" tsq>Stunde</option>
                                <option value="10" selected tsq>Tag</option>
                                <option value="20" tsq>Woche</option>
                                <option value="30" tsq>Monat</option>
                            </select>
                        </div>
                    </div>
                </div>
                <div class="row">
                    <div class="col-sm-12">
                        <div class="col-sm-6">
                            <label class="sqn-label" tsq>Maximale Korrelation:</label>
                        </div>
                        <div class="col-sm-6">
                            <div class="sqn-spinner setting-control">
                                <input type="text" class="sqn-input" id="fbc-max-correlation" min="0" max="1" step="0.01" value="0.5" />
                                <div class="sqn-spinner-btns">
                                    <div class="sqn-spinner-minus" onclick="sqnSpinnerMinus(this)"></div>
                                    <div class="sqn-spinner-plus" onclick="sqnSpinnerPlus(this)"></div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="modal-footer">
                <a data-dismiss="modal" tsq>Schließen Sie</a>
                <button type="button" class="btn btn-primary" onclick="onFilter()" tsq>Filter</button>
            </div>
        </div>
    </div>
</div>

<style>
    #filterByCorrelationButtonModal .setting-control {
        width: 100px;
    }
</style>

<script>
    function onFilter(){
        let period = document.getElementById("fbc-correlation-period").value;
        let maxCorrelation = document.getElementById("fbc-max-correlation").value;

        const urlParams = new URLSearchParams({
            projectName: window.parent.filterByCorrelationData.projectName,
            databankName: window.parent.filterByCorrelationData.databankName,
            period,
            maxCorrelation
        });

        CustomPluginController.sendRequest("filterByCorrelation/filter?" + urlParams.toString(), "GET", null, function(response){
            let responseJSON = JSON.parse(response);
            console.error(responseJSON);
            if(responseJSON.error){
                alert(responseJSON.error);
            }
            else {
                alert(responseJSON.success);
                window.parent.hidePopup("#filterByCorrelationButtonModal");
            }
        });
    }
</script>

 

Das Wichtigste ist hier die id filterByCorrelationButtonModal die in der ersten Zeile verwendet wird. Das ist wichtig, weil dieses Popup dann über diese ID in der JavaScript-Funktion in module.js referenziert wird.

Wenn Sie Ihr eigenes Plugin entwickeln, müssen Sie eine eigene eindeutige ID für das Popup erstellen und diese ID in modul.js - siehe später.

 

Eine weitere Besonderheit ist, dass wir onClick() JavaScript-Ereignis, um die Übermittlung des Formulars zu erfassen, und wir werden dann einfache JS verwenden, um die konfigurierten Werte abzurufen.

Sie werden dann mit einer speziellen Methode an das Backend gesendet CustomPluginController.sendRequest() die eine Anfrage an /filterByCorrelation/filter wobei die Parameter aus dem Formular übergeben werden und der Rückgabewert in einer Meldung angezeigt wird.

 

 

Definition des Plugins in module.js

Das Plugin benötigt eine spezielle modul.js Datei, damit sie richtig erkannt und in die Benutzeroberfläche integriert werden kann.

Es ist JavaScript Angular-Code, aber Sie brauchen nicht zu detaillierten Kenntnissen der Angular haben, können Sie dieses Beispiel Plugin und einfach kopieren & einfügen + ändern, wenn Sie ae Implementierung Ihrer eigenen Plugin.

Der gesamte Code von module.js ist:

angular.module('app.resultsdatabankactions.filterByCorrelation', ['sqplugin']).config(function(sqPluginProvider, $controllerProvider) {
    
    function callback(projektname, aufgabenname, datenbankname, selectedStrategies){
        window.parent.filterByCorrelationData = {
            ProjektName,
            Aufgabenname,
            datenbankName
        };
        window.parent.showPopup("#filterByCorrelationButtonModal");
    }

    let buttonController = new CustomPluginController('app.resultsdatabankactions.filterByCorrelation', $controllerProvider, callback).init();

    sqPluginProvider.plugin("ResultsDatabankAction", 100, {
        Titel: Ltsq("Tools:Filter by correlation"),
        class: 'btn btn-normal btn-default',
        controller: buttonController,
        id: "databank-action-filterbycorrelation"
    });
    
    sqPluginProvider.addPopupWindow("plugins/FilterByCorrelation/popup.html", null, 'SQUANT');

});

 

Lassen Sie uns den Code Stück für Stück durchgehen.

Der erste Teil ist die Registrierung unseres neuen Plugins als Angular-Modul:

angular.module('app.resultsdatabankactions.filterByCorrelation', ['sqplugin']).config(function(sqPluginProvider, $controllerProvider) {

 

Im Allgemeinen müssen Sie ihr nur einen eindeutigen Namen geben, z. B. app.resultsdatabankactions.filterByCorrelation.
Wir verwenden intern die Konvention app.resultsdatabankaction.YourUniqueName

 

Der zweite Teil ist die Erstellung eines Button-Controllers und eines Callbacks, der den Popup-Dialog anzeigt.

function callback(projektname, aufgabenname, databankname, selectedStrategies){
    window.parent.filterByCorrelationData = {
        Projektname,
        Aufgabenname,
        datenbankName
    };
    window.parent.showPopup("#filterByCorrelationButtonModal");
}

let buttonController = new CustomPluginController('app.resultsdatabankactions.filterByCorrelation', $controllerProvider, callback).init();

 

Das Wichtigste ist hier der Aufruf der Methode window.parent.showPopup() dem Sie eine eindeutige ID des Popups geben, das zuvor in popup.html definiert wurde. Wie der Name schon sagt, zeigt der Aufruf das Popup und unser Formular an,

und der Button-Controller ruft es auf, wenn der Button angeklickt wird.

 

Der dritte Teil besteht darin, dieses Plugin zu registrieren und festzulegen, wo es angezeigt werden soll:

sqPluginProvider.plugin("ResultsDatabankAction", 100, {
    title: Ltsq("Filter nach Korrelation"),
    class: 'btn btn-normal btn-default',
    controller: buttonController,
    id: "databank-action-filterbycorrelation"
});

 

Hier sagen wir, es ist verbunden mit ErgebnisseDatabankAktion Erweiterungspunkt, d.h. er wird in der Datenbank als Schaltfläche angezeigt.

 

Das Interessante hier ist der Titel - wenn Sie nicht möchten, dass er als separate Schaltfläche angezeigt wird, sondern als weiteres Element in der bestehenden Werkzeuge können Sie einfach Titel verwenden:

Titel: Ltsq("Werkzeuge:Filter nach Korrelation"),

Sie können auch sehen, dass wir einen Button-Controller definiert und ihn auf unsere buttonController vorher definiert.

 

Der letzte Teil ist die Registrierung unserer UI-Datei in SQX pugin system - dies wird sicherstellen, dass die pooup.html-Datei geladen wird und Teil der UI wird.

sqPluginProvider.addPopupWindow("plugins/FilterByCorrelation/popup.html", null, 'SQUANT');

 

 

Implementierung des Backends - FilterByCorrelationServlet.java

Nun, da wir die Benutzeroberfläche haben, müssen wir die gewünschten Funktionen im Backend implementieren.

Das Backend besteht aus einem HTTP-Servlet, das HTTP-Anfragen vom Frontend (UI) verarbeitet und die erforderlichen Funktionen ausführt. Wenn Sie sich erinnern, haben wir diese Anfrage in der Datei popup.html definiert, sie verwendet eine spezielle Methode CustomPluginController.sendRequest() die eine Anfrage an /filterByCorrelation/filter.

Wir müssen also ein Servlet implementieren, das diese Anfrage bearbeitet und das tut, was wir wollen.

Das Servlet muss Folgendes implementieren IServletPlugin Schnittstelle. Die wichtigste Methode ist hier getHandler()die aufgerufen wird, wenn das Backend eine Anfrage für /filterByCorrelation/.

Die Anfrage wird dann von unserem FilterByCorrelation innere Klasse, die die Funktionalität implementiert.

 

Der Teil des Schnipsels, der nur die Anfrage bearbeitet und ihr einen Handler zuweist, ist also:

@PluginImplementierung
public class FilterByCorrelationServlet implements IServletPlugin {

    private static final Logger Log = LoggerFactory.getLogger(FilterByCorrelationServlet.class);
    private ServletContextHandler connectionContext;

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

    @Override
    public String getProduct() {
        return SQConst.CODE_SQ;
    }

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

    @Override
    public int getPreferredPosition() {
        return 0;
    }

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

    @Override
    public void initPlugin() throws Exception {
    }

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

    @Override
    public Handler getHandler() {
        if(connectionContext == null){
            connectionContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
            connectionContext.setContextPath("/filterByCorrelation/");
            connectionContext.addServlet(new ServletHolder(new FilterByCorrelation()),"/*");
        }
        return connectionContext;
    }

 

FilterByCorrelation innere Klasse

Dies ist die Klasse, die die Anfrage bearbeitet /filterByCorrelation/filter von der Benutzeroberfläche.

Der erste Teil dieser Klasse überprüft, welcher Befehl genau aufgerufen wurde - in unserem Fall gibt es nur Filter Befehl.

class FilterByCorrelation extends HttpJSONServlet {
    @Override
    protected String execute(String command, Map args, String method){
        switch(command) {
            case "filter": return onFilter(args);
            default:
                return apiErrorJSON("Execution failed. Unknown command '"+command+"'.", null);
        }
    }

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

    private String onFilter(Map args) {
        JSONObject response = new JSONObject();
        try {
            String projectName = tryGetParam(args, "projectName")[0];
            String databankName = tryGetParam(args, "databankName")[0];
            int period = Integer.parseInt(tryGetParam(args, "period")[0]);
            double maxCorrelation = Double.parseDouble(tryGetParam(args, "maxCorrelation")[0]);

            int removedCount = filterByCorrelation(projectName, databankName, period, maxCorrelation);

            response.put("success", "Strategien gefiltert, "+removedCount+" Strategien entfernt.");
            return response.toString();
        }
        catch(Exception e){
            Log.error("Fehler beim Filtern von Strategien nach Korrelation", e);
            response.put("error", "Filterung fehlgeschlagen - " + e.getMessage());
            return response.toString();
        }
    }

 

Nach Erhalt dieses Befehls führt es Folgendes aus onFilter() Methode, die:

  • erhält Parameter wie Projektname, Datenbankname, Zeitraum und maxCorrelation aus den Anfrageparametern
  • führt die eigentliche Filterung durch Korrelation aus, indem es die Methode filterByCorrelation(projektname, datenbankname, zeitraum, maxCorrelation)
  • sendet es dann eine Nachricht mit der tatsächlichen Anzahl der entfernten Strategien oder eine Fehlermeldung im Falle eines Fehlers zurück.

 

 

filterByCorrelation()-Methode

Diese Methode führt die eigentliche Filterung durch - sie geht durch alle Strategien in der aktuellen Datenbank, berechnet die Korrelationen zwischen den Strategien und entfernt die Strategien, bei denen die Korrelation > maxCorrelation ist.

Die Rechenkorrelation verdient einen eigenen Artikel und ein eigenes Beispiel, Sie können verfolgen, wie sie im Code umgesetzt wird:

    private int filterByCorrelation(String projectName, String databankName, int period, double maxCorrelation) throws Exception {
        Log.info("Filtern nach Korrelation (Projekt: " + Projektname + ", Datenbank: " + Datenbankname + ")...");
        SQProject Projekt = ProjectEngine.get(Projektname);
        Datenbank databank = project.getDatabanks().get(databankName);
        int removedCount = 0;

        // Prüfung auf korrelierte Strategien unter Verwendung der Fitnessbewertung als Rang-/Prioritätsliste
        ArrayList rgSortedCandidates = new ArrayList(databank.getRecords());

        rgSortedCandidates.sort((o1, o2) -> Double.valueOf(o1.getFitness()).compareTo(Double.valueOf(o2.getFitness())));

        ArrayList rgSortedCandidatesRanked = new ArrayList();

        for(ErgebnisGruppe rgSC : rgSortedCandidates) {
            rgSortedCandidatesRanked.add(0, rgSC);
        }

        ArrayList rgUncorrelated = new ArrayList();
        // todo: mehrere strategien könnten die gleiche fitness haben. dies sollte durch zusätzliche sortierung berücksichtigt werden (d.h. fitness > pf > ret/dd .. etc)

        while (rgSortedCandidatesRanked.size() > 0) {
            ErgebnisGruppe focusStrat = rgSortedCandidatesRanked.get(0).clone();
            String focusStratName = focusStrat.getName();

            Log.info("Zu vergleichende Fokusstrategie: {}", focusStratName);

            rgUncorrelated.add(focusStrat.clone()); // Hinzufügen der besten Strategie im Pool zur unkorrelierten Liste als Startpunkt
            rgSortedCandidatesRanked.remove(0);

            Iterator it = rgSortedCandidatesRanked.iterator();

            while(it.hasNext()) {
                ResultsGroup subStrat = it.next();

                String subStratName = subStrat.getName();

                if (subStratName != focusStratName) {
                    CorrelationPeriods corrPeriods = new CorrelationPeriods();

                    CorrelationType corrType = CorrelationTypes.getInstance().findClassByName("GewinnVerlust");
                    CorrelationComputer correlationComputer = new CorrelationComputer();

                    // Berechnung der Korrelationszeiträume
                    CorrelationPeriods correlationPeriods = precomputePeriodsAP(focusStrat, subStrat, period, corrType);
                    double corrValue = SQUtils.round2(correlationComputer.computeCorrelation(false, focusStratName, subStratName, focusStrat.orders(), subStrat.orders(), correlationPeriods));

                    Log.info(" - Korrelation von '{}'' zur Fokusstrategie: {}", subStratName, corrValue);

                    if (corrValue > maxCorrelation) {
                        it.remove();
                        removedCount++;
                        Log.info(" - Entfernen der Strategie " + subStratName + " (Korrelation " + corrValue + " > " + maxCorrelation + ")...");
                        databank.remove(subStratName, true, true, true, true, true, null);
                    }
                } else {
                    it.remove();
                }
            }
        }

        return removedCount;
    }

    //------------------------------------------------------------------------
    
    private CorrelationPeriods precomputePeriodsAP(ResultsGroup paramResultsGroup1, ResultsGroup paramResultsGroup2, int paramInt, CorrelationType paramCorrelationType) throws Exception {
        CorrelationPeriods correlationPeriods = new CorrelationPeriods();
        TimePeriod timePeriod = CorrelationLib.getPeriod(paramResultsGroup1.orders());
        long l1 = timePeriod.from;
        long l2 = timePeriod.to;
        timePeriod = CorrelationLib.getPeriod(paramResultsGroup2.orders());
        if (timePeriod.from  l2) {
            l2 = timePeriod.to;
        }

        TimePeriods timePeriods1 = CorrelationLib.generatePeriods(paramInt, l1, l2);
        TimePeriods timePeriods2 = timePeriods1.clone();

        paramCorrelationType.computePeriods(paramResultsGroup1.orders(), paramInt, timePeriods1);
        correlationPeriods.put(paramResultsGroup1.getName(), timePeriods1);

        paramCorrelationType.computePeriods(paramResultsGroup2.orders(), paramInt, timePeriods2);
        correlationPeriods.put(paramResultsGroup2.getName(), timePeriods2);

        Rückgabe von correlationPeriods;
    }        
}

 

 

Arbeiten mit Plugins im Code-Editor

In SQX Build 136 Dev 2 haben wir eine neue Funktionalität zur Arbeit mit benutzerdefinierten Plugins hinzugefügt.

Wenn Sie das angehängte Plugin in den richtigen Ordner entpackt haben ({SQ-Installation}/user/extend/Plugins) und öffnen Sie den Code-Editor, dann sehen Sie einen neuen Abschnitt Plugins mit unserem neuen Plugin:

 

Sie können dort wie gewohnt Dateien und Ordner erstellen und bearbeiten. Die neue Funktion ist die Möglichkeit, das Plugin zu kompilieren.

Klicken Sie mit der rechten Maustaste auf den Plugin-Ordner und wählen Sie Plugin kompilieren:

 

Das Plugin wird kompiliert und eine neue Datei FilterByCorrelation.jar wird in dem Ordner erscheinen.

Starten Sie nun StrategyQuant neu und Sie sollten eine neue Schaltfläche für die Datenbank sehen:

 

Wenn Sie darauf klicken, sollte sich der unten abgebildete Dialog öffnen:

Konfigurieren Sie die Filtereinstellungen und klicken Sie auf die Schaltfläche Filter. Wenn Sie einige Strategien in der Datenbank haben, werden Sie feststellen, dass einige von ihnen entfernt werden und eine Warnung angezeigt wird:

 

Unser Plugin hat also funktioniert - es hat die Korrelationen zwischen allen Strategien in der aktuellen Datenbank berechnet und verglichen und 5 Strategien entfernt, deren Korrelation größer war als die konfigurierte maximale Korrelation.

 

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

Abonnieren
Benachrichtigen Sie mich bei
8 Kommentare
Älteste
Neuestes Meistgewählt
Inline-Rückmeldungen
Alle Kommentare anzeigen
Emmanuel
22. 6. 2022 12:53 Uhr

Ausgezeichnet !!!!!!!!!!!!!!!!!!!!!!!!!!!! Vielen Dank für diese Verbesserung. !!!
Sie öffnet die Tür für neue Entwicklungen!

Emmanuel
24. 6. 2022 1:58 pm

Dies ist das erste Mal, dass wir ein Plug-in-Beispiel sehen. Es ist wirklich hilfreich, um zu verstehen, wie Plug-in und Korrelation funktionieren. Wir brauchen mehr Plug-in-Beispiele. Mit Beispielen sind wir in der Lage, Plug-ins zu entwickeln und weiterzugeben. Vielen Dank für dieses Beispiel

binhsir
3. 7. 2022 7:42 Uhr

Ich muss sagen, dass die Skalierbarkeit von SQX beeindruckend ist, auch wenn ich kein sehr guter Entwickler bin.

Jordanien
22. 9. 2022 2:42 Uhr

Lieber Mark, vielen Dank, ich weiß das zu schätzen.

Jordanien
22. 9. 2022 14:50 Uhr

Gleichzeitig hoffe ich, dass diese Funktion als Aufgabe in den benutzerdefinierten Projekten verwendet werden kann.

tnickel
17. 2. 2023 9:47 pm

Ich habe dieses Plugin mit SQ 4.137 Dev2 getestet, es funktioniert einwandfrei. Ich bin glücklich, diese Funktion sollte in der nächsten Version der SQ sein.

tnickel
Antwort an  tnickel
26. 4. 2023 2:22 pm

Es wäre hilfreich, wenn dieser "Filter nach Korrelation" als neue Aufgabe in einem Workflow als Block verfügbar wäre. So können viele Dinge automatisiert werden.

ytu
ytu
Antwort an  tnickel
24. 6. 2023 5:29 Uhr

Fantastische Idee!!