Hochfrequenzhandelssimulation mit Stream Analytics

Azure Stream Analytics unterstützt erweiterte Analysen durch die Kombination aus SQL-Sprache, JavaScript benutzerdefinierten Funktionen (UDFs) und benutzerdefinierten Aggregaten (UDAs). Zu den erweiterten Analysen gehören Online-Schulungen zum maschinellen Lernen und Bewertung sowie zustandsbehaftete Prozesssimulationen. In diesem Artikel wird beschrieben, wie Sie eine lineare Regression in einem Azure Stream Analytics Auftrag durchführen, der kontinuierliche Schulungen und Bewertungen in einem Hochfrequenzhandelsszenario durchführt.

Voraussetzungen

Workflow für den Hochfrequenzhandel

Der logische Fluss des Hochfrequenzhandels lautet:

  1. Abrufen von Echtzeitkursen von einer Wertpapierbörse.
  2. Erstellung eines Prognosemodells auf Basis der Kursnotierungen, um die Kursbewegung vorherzusagen.
  3. Kauf- oder Verkaufsaufträge erteilen, um von der korrekten Vorhersage von Kursbewegungen zu profitieren.

Für dieses Szenario ist Folgendes erforderlich:

  • Ein Kursdatenfeed in Echtzeit.
  • Ein Vorhersagemodell, das auf Echtzeit-Kursnotierungen angewendet werden kann.
  • Eine Handelssimulation, die den Gewinn oder Verlust des Handelsalgorithmus veranschaulicht.

Feed mit Echtzeit-Kursnotierungen

Important

Die in diesem Abschnitt referenzierte IEX Trading WebSocket-API (iextrading.com) wurde eingestellt. IEX Cloud bietet jetzt Marktdaten über IEX Cloud mit unterschiedlichen Authentifizierungs- und Endpunkten. Aktualisieren Sie die URL und Authentifizierung in Ihrer Implementierung entsprechend.

Important

Die NuGet-Pakete SocketIoClientDotNet und WindowsAzure.ServiceBus, die in diesem Beispiel verwendet werden, sind veraltet. Verwenden Sie für neue Projekte eine aktuelle Socket.IO Clientbibliothek und das Azure.Messaging.EventHubs-Paket mit EventHubProducerClient anstelle der älteren EventHubClient.

Investors Exchange (IEX) bot früher kostenlose Gebots- und Nachfragekurse in Echtzeit über socket.io an. Sie können ein einfaches Konsolenprogramm schreiben, um Echtzeit-Kurse zu empfangen und sie an Azure Event Hubs als Datenquelle zu pushen. Der folgende Code ist ein Skelett des Programms. Der Code lässt der Kürze halber die Fehlerbehandlung weg. Sie müssen auch die SocketIoClientDotNet Und WindowsAzure.ServiceBus NuGet-Pakete in Ihr Projekt einschließen.

using Quobject.SocketIoClientDotNet.Client;
using Microsoft.ServiceBus.Messaging;
var symbols = "msft,fb,amzn,goog";
var eventHubClient = EventHubClient.CreateFromConnectionString(connectionString, eventHubName);
var socket = IO.Socket("https://ws-api.iextrading.com/1.0/tops");
socket.On(Socket.EVENT_MESSAGE, (message) =>
{
    eventHubClient.Send(new EventData(Encoding.UTF8.GetBytes((string)message)));
});
socket.On(Socket.EVENT_CONNECT, () =>
{
    socket.Emit("subscribe", symbols);
});

Vorsicht

Dieses Codebeispiel dient nur zur Veranschaulichung. Der IEX WebSocket-API-Endpunkt und die hier verwendeten NuGet-Pakete sind nicht mehr verfügbar. Verwenden Sie diesen Code nicht in der Produktion. Aktuelle Alternativen finden Sie weiter oben in den WICHTIGen Hinweisen in diesem Abschnitt.

Hier sind einige generierte Beispielereignisse:

{"symbol":"MSFT","marketPercent":0.03246,"bidSize":100,"bidPrice":74.8,"askSize":300,"askPrice":74.83,"volume":70572,"lastSalePrice":74.825,"lastSaleSize":100,"lastSaleTime":1506953355123,"lastUpdated":1506953357170,"sector":"softwareservices","securityType":"commonstock"}
{"symbol":"GOOG","marketPercent":0.04825,"bidSize":114,"bidPrice":870,"askSize":0,"askPrice":0,"volume":11240,"lastSalePrice":959.47,"lastSaleSize":60,"lastSaleTime":1506953317571,"lastUpdated":1506953357633,"sector":"softwareservices","securityType":"commonstock"}
{"symbol":"MSFT","marketPercent":0.03244,"bidSize":100,"bidPrice":74.8,"askSize":100,"askPrice":74.83,"volume":70572,"lastSalePrice":74.825,"lastSaleSize":100,"lastSaleTime":1506953355123,"lastUpdated":1506953359118,"sector":"softwareservices","securityType":"commonstock"}
{"symbol":"FB","marketPercent":0.01211,"bidSize":100,"bidPrice":169.9,"askSize":100,"askPrice":170.67,"volume":39042,"lastSalePrice":170.67,"lastSaleSize":100,"lastSaleTime":1506953351912,"lastUpdated":1506953359641,"sector":"softwareservices","securityType":"commonstock"}
{"symbol":"GOOG","marketPercent":0.04795,"bidSize":100,"bidPrice":959.19,"askSize":0,"askPrice":0,"volume":11240,"lastSalePrice":959.47,"lastSaleSize":60,"lastSaleTime":1506953317571,"lastUpdated":1506953360949,"sector":"softwareservices","securityType":"commonstock"}
{"symbol":"FB","marketPercent":0.0121,"bidSize":100,"bidPrice":169.9,"askSize":100,"askPrice":170.7,"volume":39042,"lastSalePrice":170.67,"lastSaleSize":100,"lastSaleTime":1506953351912,"lastUpdated":1506953362205,"sector":"softwareservices","securityType":"commonstock"}
{"symbol":"GOOG","marketPercent":0.04795,"bidSize":114,"bidPrice":870,"askSize":0,"askPrice":0,"volume":11240,"lastSalePrice":959.47,"lastSaleSize":60,"lastSaleTime":1506953317571,"lastUpdated":1506953362629,"sector":"softwareservices","securityType":"commonstock"}

Note

Der Zeitstempel des Ereignisses lautet lastUpdated (in Epochenzeit).

Prädiktives Modell für den Hochfrequenzhandel

Für diese Demonstration verwendet das Beispiel ein lineares Modell, das in Order Imbalance Based Strategy in High Frequency Algorithmic Trading beschrieben ist.

„Volume Order Imbalance“ (VOI) ist eine Funktion des Geld-/Briefkurses (aktuell und letzter Tick-Vorgang). Das Papier identifiziert die Korrelation zwischen VOI und zukünftiger Preisbewegung. Damit wird ein lineares Modell zwischen den letzten fünf VOI-Werten und der Preisänderung der nächsten zehn Takten erstellt. Das Modell wird mit den Daten des Vortags per linearer Regression trainiert.

Das trainierte Modell macht dann Kursänderungsvorhersagen auf Kursen am aktuellen Handelstag in Echtzeit. Wenn das Modell eine große Preisänderung vorhersagt, führt es einen Trade aus. Je nach Schwellenwerteinstellung kann ein einzelner Kurs Tausende von Trades während eines Handelstages generieren.

Diagramm, das die Formel zur Definition des Volumen-Ungleichgewichts von Aufträgen zeigt, die im Hochfrequenzhandel verwendet wird.

In den folgenden Abschnitten wird gezeigt, wie Sie die Schulungs- und Vorhersagevorgänge in einem Azure Stream Analytics Auftrag ausdrücken. Die vollständige Abfrage ist eine einzelne WITH Anweisung, die aus allgemeinen Tabellenausdrücken (CTEs) besteht, die eine Pipeline bilden:

CTE-Stufe Purpose
typeconvertedquotes Konvertieren von unformatierten Eingabefeldern in richtige SQL-Typen
timefilteredquotes Kursnotierungen auf die Handelszeiten filtern und ungültige Daten entfernen
shiftedquotes Verwenden Sie LAG, um die Bid-/Ask-Werte des vorherigen Ticks abzurufen.
currentPriceAndVOI Berechnen des Volume Order Imbalance (VOI) aus aktuellem und vorherigem Tick
shiftedPriceAndShiftedVOI Erstellen Sie Sequenzen von 10 aufeinanderfolgenden Mittelpreisen und 2 aufeinanderfolgenden VOI-Werten
modelInput Daten in Merkmalsvektoren umformen (VOI als x, Preisdifferenz als y)
modelagg / modelparambs / model Trainieren eines zweivariablen linearen Regressionsmodells mithilfe von SUMME- und AVG-Aggregaten
shiftedVOI / VOIAndModel / VOIANDModelJoined Verknüpfen aktueller VOI-Werte mit dem trainierten Modell des vorigen Tages
prediction Berechnen der erwarteten zukünftigen Preisänderung (efpc) aus dem Modell
tradeSignal Kauf-/Verkaufssignale generieren, wenn efpc den Schwellenwert von ±0,02 überschreitet

Note

Für diese Abfrage ist Azure Stream Analytics-Kompatibilitätsstufe 1.1 oder höher erforderlich, bei der die Groß-/Kleinschreibung der Feldnamen beibehalten wird, um ein vorhersehbares Verhalten mit UDAs zu gewährleisten.

Eingabefelder für Anführungszeichen bereinigen und konvertieren

Die erste CTE in der Azure Stream Analytics-Abfrage konvertiert die Kursrohdaten aus Event Hubs in korrekt typisierte SQL-Spalten. DATEADD wandelt epochenzeit (Unix Millisekunden) in Datetime um. TRY_CAST erzwingt die Datentypen, ohne dass die Abfrage fehlschlägt. Wandeln Sie Eingabefelder in die erwarteten Datentypen um, um unerwartetes Verhalten bei Manipulation oder Vergleich der Felder zu vermeiden.

WITH
typeconvertedquotes AS (
    /* convert all input fields to proper types */
    SELECT
        System.Timestamp AS lastUpdated,
        symbol,
        DATEADD(millisecond, CAST(lastSaleTime as bigint), '1970-01-01T00:00:00Z') AS lastSaleTime,
        TRY_CAST(bidSize as bigint) AS bidSize,
        TRY_CAST(bidPrice as float) AS bidPrice,
        TRY_CAST(askSize as bigint) AS askSize,
        TRY_CAST(askPrice as float) AS askPrice,
        TRY_CAST(volume as bigint) AS volume,
        TRY_CAST(lastSaleSize as bigint) AS lastSaleSize,
        TRY_CAST(lastSalePrice as float) AS lastSalePrice
    FROM quotes TIMESTAMP BY DATEADD(millisecond, CAST(lastUpdated as bigint), '1970-01-01T00:00:00Z')
),
timefilteredquotes AS (
    /* filter between 7am and 1pm PST, 14:00 to 20:00 UTC */
    /* clean up invalid data points */
	SELECT * FROM typeconvertedquotes
	WHERE DATEPART(hour, lastUpdated) >= 14 AND DATEPART(hour, lastUpdated) < 20 AND bidSize > 0 AND askSize > 0 AND bidPrice > 0 AND askPrice > 0
),

Vorherige Tick-Werte mit LAG abrufen

Die nächste CTE in der Azure Stream Analytics-Abfrage verwendet die LAG-Funktion, um den Geld-/Briefkurs und die Größe des vorherigen Ticks für jedes Wertpapiersymbol abzurufen. Ein Wert von einer Stunde für LIMIT DURATION wird willkürlich gewählt. Anhand der Kursfrequenz können Sie den vorherigen Tick ermitteln, indem Sie eine Stunde zurückblicken.

shiftedquotes AS (
    /* get previous bid/ask price and size in order to calculate VOI */
	SELECT
		symbol,
		(bidPrice + askPrice)/2 AS midPrice,
		bidPrice,
		bidSize,
		askPrice,
		askSize,
		LAG(bidPrice) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS bidPricePrev,
		LAG(bidSize) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS bidSizePrev,
		LAG(askPrice) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS askPricePrev,
		LAG(askSize) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS askSizePrev
	FROM timefilteredquotes
),

Berechnen des Volumenreihenfolgeungleichgewichts (VOI)

Die nächste CTE berechnet den VOI-Wert aus den Daten für den Geld-/Briefkurs des aktuellen und des vorherigen Ticks. Die Abfrage filtert Nullwerte für Fälle heraus, in denen kein vorheriger Tick existiert.

currentPriceAndVOI AS (
    /* calculate VOI */
	SELECT
		symbol,
		midPrice,
		(CASE WHEN (bidPrice < bidPricePrev) THEN 0
            ELSE (CASE WHEN (bidPrice = bidPricePrev) THEN (bidSize - bidSizePrev) ELSE bidSize END)
         END) -
        (CASE WHEN (askPrice < askPricePrev) THEN askSize
            ELSE (CASE WHEN (askPrice = askPricePrev) THEN (askSize - askSizePrev) ELSE 0 END)
         END) AS VOI
	FROM shiftedquotes
	WHERE
		bidPrice IS NOT NULL AND
		bidSize IS NOT NULL AND
		askPrice IS NOT NULL AND
		askSize IS NOT NULL AND
		bidPricePrev IS NOT NULL AND
		bidSizePrev IS NOT NULL AND
		askPricePrev IS NOT NULL AND
		askSizePrev IS NOT NULL
),

Erstellen von Featuresequenzen für Modellschulungen

Die nächste CTE verwendet LAG erneut, um eine Sequenz mit zwei aufeinander folgenden VOI-Werten zu erstellen, gefolgt von 10 aufeinander folgenden Mid-Price-Werten. Diese Sequenzen bilden die Schulungsdaten für das lineare Regressionsmodell.

shiftedPriceAndShiftedVOI AS (
    /* get 10 future prices and 2 previous VOIs */
    SELECT
		symbol,
		midPrice AS midPrice10,
		LAG(midPrice, 1) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS midPrice9,
		LAG(midPrice, 2) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS midPrice8,
		LAG(midPrice, 3) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS midPrice7,
		LAG(midPrice, 4) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS midPrice6,
		LAG(midPrice, 5) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS midPrice5,
		LAG(midPrice, 6) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS midPrice4,
		LAG(midPrice, 7) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS midPrice3,
		LAG(midPrice, 8) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS midPrice2,
		LAG(midPrice, 9) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS midPrice1,
		LAG(midPrice, 10) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS midPrice,
		LAG(VOI, 10) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS VOI1,
		LAG(VOI, 11) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS VOI2
	FROM currentPriceAndVOI
),

Daten in Featurevektoren umformen

Im nächsten CTE werden die Preis- und VOI-Sequenzen in Featurevektoren für ein zweivariables lineares Modell umgestaltet, wobei VOI-Werte die unabhängigen Variablen (x1, x2) und die durchschnittliche zukünftige Preisänderung die abhängige Variable (y) sind. Ereignisse mit unvollständigen Daten werden herausgefiltert.

modelInput AS (
    /* create feature vector, x being VOI, y being delta price */
	SELECT
		symbol,
		(midPrice1 + midPrice2 + midPrice3 + midPrice4 + midPrice5 + midPrice6 + midPrice7 + midPrice8 + midPrice9 + midPrice10)/10.0 - midPrice AS y,
		VOI1 AS x1,
		VOI2 AS x2
	FROM shiftedPriceAndShiftedVOI
	WHERE
		midPrice1 IS NOT NULL AND
		midPrice2 IS NOT NULL AND
		midPrice3 IS NOT NULL AND
		midPrice4 IS NOT NULL AND
		midPrice5 IS NOT NULL AND
		midPrice6 IS NOT NULL AND
		midPrice7 IS NOT NULL AND
		midPrice8 IS NOT NULL AND
		midPrice9 IS NOT NULL AND
		midPrice10 IS NOT NULL AND
		midPrice IS NOT NULL AND
		VOI1 IS NOT NULL AND
		VOI2 IS NOT NULL
),

Trainieren des linearen Regressionsmodells mit SUMME und AVG

Da Azure Stream Analytics keine integrierte lineare Regressionsfunktion hat, verwendet die Abfrage SUM und AVG Aggregate, um die Koeffizienten (a, b1, b2) für das lineare Regressionsmodell mit zwei Variablen zu berechnen. Das Modell wird täglich neu trainiert, wobei ein 24-stündiges Tumbling-Window verwendet wird.

Diagramm, das die lineare Regressionsgleichungsformel für die Berechnung von Modellkoeffizienten zeigt.

modelagg AS (
    /* get aggregates for linear regression calculation,
     http://faculty.cas.usf.edu/mbrannick/regression/Reg2IV.html */
	SELECT
		symbol,
		SUM(x1 * x1) AS x1x1,
		SUM(x2 * x2) AS x2x2,
		SUM(x1 * y) AS x1y,
		SUM(x2 * y) AS x2y,
		SUM(x1 * x2) AS x1x2,
		AVG(y) AS avgy,
		AVG(x1) AS avgx1,
		AVG(x2) AS avgx2
	FROM modelInput
	GROUP BY symbol, TumblingWindow(hour, 24, -4)
),
modelparambs AS (
    /* calculate b1 and b2 for the linear model */
	SELECT
		symbol,
		(x2x2 * x1y - x1x2 * x2y)/(x1x1 * x2x2 - x1x2 * x1x2) AS b1,
		(x1x1 * x2y - x1x2 * x1y)/(x1x1 * x2x2 - x1x2 * x1x2) AS b2,
		avgy,
		avgx1,
		avgx2
	FROM modelagg
),
model AS (
    /* calculate a for the linear model */
	SELECT
		symbol,
		avgy - b1 * avgx1 - b2 * avgx2 AS a,
		b1,
		b2
	FROM modelparambs
),

Bewertung aktueller Kurse mit dem Modell des Vortags

Um das trainierte lineare Regressionsmodell des vorherigen Tages zum Bewerten des aktuellen Ereignisses zu verwenden, verknüpft die Abfrage die Anführungszeichen mit den Modellkoeffizienten. Anstatt JOIN zu verwenden, verwendet die Abfrage UNION , um Modellereignisse und Anführungszeichenereignisse in einem einzigen Datenstrom zu kombinieren. Anschließend wird lag verwendet, um die Ereignisse mit dem Modell des vorigen Tages zu koppeln, sodass Sie genau eine Übereinstimmung erhalten. Aufgrund des Wochenendes sieht die Abfrage drei Tage (72 Stunden) zurück. Wenn eine einfache JOIN verwendet würde, würden Sie für jedes Quote-Ereignis drei Modelle erhalten.

shiftedVOI AS (
    /* get two consecutive VOIs */
	SELECT
		symbol,
		midPrice,
		VOI AS VOI1,		
		LAG(VOI, 1) OVER (PARTITION BY symbol LIMIT DURATION(hour, 1)) AS VOI2
	FROM currentPriceAndVOI
),
VOIAndModel AS (
    /* combine VOIs and models */
	SELECT
		'voi' AS type,
		symbol,
		midPrice,
		VOI1,
		VOI2,
        0.0 AS a,
        0.0 AS b1,
        0.0 AS b2
	FROM shiftedVOI
	UNION
	SELECT
		'model' AS type,
		symbol,
        0.0 AS midPrice,
        0 AS VOI1,
        0 AS VOI2,
		a,
		b1,
		b2
	FROM model
),
VOIANDModelJoined AS (
    /* match VOIs with the latest model within 3 days (72 hours, to take the weekend into account) */
	SELECT
		symbol,
		midPrice,
		VOI1 as x1,
		VOI2 as x2,
		LAG(a, 1) OVER (PARTITION BY symbol LIMIT DURATION(hour, 72) WHEN type = 'model') AS a,
		LAG(b1, 1) OVER (PARTITION BY symbol LIMIT DURATION(hour, 72) WHEN type = 'model') AS b1,
		LAG(b2, 1) OVER (PARTITION BY symbol LIMIT DURATION(hour, 72) WHEN type = 'model') AS b2
	FROM VOIAndModel
	WHERE type = 'voi'
),

Generieren von Handelssignalen aus Vorhersagen

Die endgültigen CTEs berechnen die erwartete zukünftige Preisänderung (efpc), indem die lineare Regressionsformel (a + b1 * x1 + b2 * x2) angewendet und dann Kauf-/Verkaufssignale basierend auf einem ±0,02-Schwellenwert generiert werden. Ein Handelswert von 10 bedeutet Kauf. Ein Handelswert von -10 bedeutet „Verkaufen“.

prediction AS (
    /* make prediction if there is a model */
	SELECT
		symbol,
		midPrice,
		a + b1 * x1 + b2 * x2 AS efpc
	FROM VOIANDModelJoined
	WHERE
		a IS NOT NULL AND
		b1 IS NOT NULL AND
		b2 IS NOT NULL AND
        x1 IS NOT NULL AND
        x2 IS NOT NULL
),
tradeSignal AS (
    /* generate buy/sell signals */
	SELECT
        DateAdd(hour, -7, System.Timestamp) AS time,
		symbol,		
		midPrice,
        efpc,
		CASE WHEN (efpc > 0.02) THEN 10 ELSE (CASE WHEN (efpc < -0.02) THEN -10 ELSE 0 END) END AS trade,
		DATETIMEFROMPARTS(DATEPART(year, System.Timestamp), DATEPART(month, System.Timestamp), DATEPART(day, System.Timestamp), 0, 0, 0, 0) as date
	FROM prediction
),

Testen der Handelsstrategie mit einer Simulation

Testen Sie nach dem Generieren der Handelssignale, wie effektiv die Handelsstrategie ist, ohne real zu handeln.

Dieser Test verwendet einen UDA mit einem Hopping-Fenster, das minütlich einen Hop durchführt. Die Gruppierung am Datum und die HAVING-Klausel stellen sicher, dass das Fenster nur Ereignisse enthält, die zum selben Tag gehören. Bei einem Hopping-Fenster über zwei Tage hinweg teilt das Datum in GROUP BY die Gruppierung in den vorherigen und den aktuellen Tag auf. Die HAVING-Klausel filtert die Fenster heraus, die am aktuellen Tag enden, aber zum Vortag gruppiert sind.

simulation AS
(
    /* perform trade simulation for the past 7 hours to cover an entire trading day, and generate output every minute */
	SELECT
        DateAdd(hour, -7, System.Timestamp) AS time,
		symbol,
		date,
		uda.TradeSimulation(tradeSignal) AS s
	FROM tradeSignal
	GROUP BY HoppingWindow(minute, 420, 1), symbol, date
	Having DateDiff(day, date, time) < 1 AND DATEPART(hour, time) < 13
)

Die JavaScript UDA initialisiert alle Akkumulatoren in der init Funktion, berechnet den Zustandsübergang mit jedem Ereignis, das dem Fenster hinzugefügt wird, und gibt die Simulationsergebnisse am Ende des Fensters zurück. Bei der Simulation werden pro Handel 10 Aktien einer Aktie gehalten oder geshortet. Die Transaktionskosten betragen pauschal $8. In der folgenden Tabelle sind die vier Handelsaktionen aufgeführt, die von der UDA ausgeführt werden:

Zustand Signal Action Position danach
Keine aktuelle Holding Kaufen (10) Kaufen, um zu öffnen Long
Keine aktuelle Holding Verkaufen (-10) Sell to open (short) Short
Long-Position Verkaufen (-10) Sell to close, dann Sell to open (short) Short
Short-Position Kaufen (10) Buy to close, dann Buy to open Long
function main() {
	var TRADE_COST = 8.0;
	var SHARES = 10;
	this.init = function () {
		this.own = false;
		this.pos = 0;
		this.pnl = 0.0;
		this.tradeCosts = 0.0;
		this.buyPrice = 0.0;
		this.sellPrice = 0.0;
		this.buySize = 0;
		this.sellSize = 0;
		this.buyTotal = 0.0;
		this.sellTotal = 0.0;
	}
	this.accumulate = function (tradeSignal, timestamp) {
		if(!this.own && tradeSignal.trade == 10) {
		  // Buy to open
		  this.own = true;
		  this.pos = 1;
		  this.buyPrice = tradeSignal.midprice;
		  this.tradeCosts += TRADE_COST;
		  this.buySize += SHARES;
		  this.buyTotal += SHARES * tradeSignal.midprice;
		} else if(!this.own && tradeSignal.trade == -10) {
		  // Sell to open
		  this.own = true;
		  this.pos = -1
		  this.sellPrice = tradeSignal.midprice;
		  this.tradeCosts += TRADE_COST;
		  this.sellSize += SHARES;
		  this.sellTotal += SHARES * tradeSignal.midprice;
		} else if(this.own && this.pos == 1 && tradeSignal.trade == -10) {
		  // Sell to close
		  this.own = false;
		  this.pos = 0;
		  this.sellPrice = tradeSignal.midprice;
		  this.tradeCosts += TRADE_COST;
		  this.pnl += (this.sellPrice - this.buyPrice)*SHARES - 2*TRADE_COST;
		  this.sellSize += SHARES;
		  this.sellTotal += SHARES * tradeSignal.midprice;
		  // Sell to open
		  this.own = true;
		  this.pos = -1;
		  this.sellPrice = tradeSignal.midprice;
		  this.tradeCosts += TRADE_COST;
		  this.sellSize += SHARES;		  
		  this.sellTotal += SHARES * tradeSignal.midprice;
		} else if(this.own && this.pos == -1 && tradeSignal.trade == 10) {
		  // Buy to close
		  this.own = false;
		  this.pos = 0;
		  this.buyPrice = tradeSignal.midprice;
		  this.tradeCosts += TRADE_COST;
		  this.pnl += (this.sellPrice - this.buyPrice)*SHARES - 2*TRADE_COST;
		  this.buySize += SHARES;
		  this.buyTotal += SHARES * tradeSignal.midprice;
		  // Buy to open
		  this.own = true;
		  this.pos = 1;
		  this.buyPrice = tradeSignal.midprice;
		  this.tradeCosts += TRADE_COST;
		  this.buySize += SHARES;		  
		  this.buyTotal += SHARES * tradeSignal.midprice;
		}
	}
	this.computeResult = function () {
		var result = {
			"pnl": this.pnl,
			"buySize": this.buySize,
			"sellSize": this.sellSize,
			"buyTotal": this.buyTotal,
			"sellTotal": this.sellTotal,
			"tradeCost": this.tradeCost
			};
		return result;
	}
}

Note

Der Power BI-Ausgabekonnektor für Azure Stream Analytics wird eingestellt. Erwägen Sie die Verwendung alternativer Ausgabeziele wie Azure Data Explorer, Azure Synapse Analytics oder eines Datenspeichers, mit dem Power BI über DirectQuery eine Verbindung herstellen oder importieren können. Weitere Informationen finden Sie unter Azure Stream Analytics-Ausgabe an Power BI.

Abschließend geben Sie die Daten zur Visualisierung an das Power BI-Dashboard aus.

SELECT * INTO tradeSignalDashboard FROM tradeSignal /* output tradeSignal to PBI */
SELECT
    symbol,
    time,
    date,
    TRY_CAST(s.pnl as float) AS pnl,
    TRY_CAST(s.buySize as bigint) AS buySize,
    TRY_CAST(s.sellSize as bigint) AS sellSize,
    TRY_CAST(s.buyTotal as float) AS buyTotal,
    TRY_CAST(s.sellTotal as float) AS sellTotal
    INTO pnlDashboard
FROM simulation /* output trade simulation to PBI */

Chart, das Handelssignale in einem Power BI Dashboard für die Handelssimulation darstellt.

Diagramm, das im Power BI-Dashboard für die Handelssimulation visualisierte Gewinn- und Verlustergebnisse zeigt.

Zusammenfassung

In diesem Artikel wird gezeigt, wie Sie ein realistisches Hochfrequenzhandelsmodell mit einer moderat komplexen Abfrage in Azure Stream Analytics implementieren. Das Modell verwendet zwei Eingabevariablen anstelle von fünf, da Azure Stream Analytics keine integrierte lineare Regressionsfunktion enthält. Sie können jedoch auch komplexere Algorithmen mit höheren Dimensionen als JavaScript UDAs implementieren.

Mit Azure Stream Analytics Tools für Visual Studio Code für die Abfrageentwicklung, Tests und Debugging können Sie die meisten Abfragen testen und debuggen, außer javaScript UDA.