Simulação de negociação de alta frequência com Stream Analytics

O Azure Stream Analytics suporta análises avançadas através da combinação de linguagem SQL, funções definidas pelo utilizador (UDFs) e agregados definidos pelo utilizador (UDAs). Análises avançadas incluem formação e pontuação online em aprendizagem automática, e simulação de processos com estado. Este artigo descreve como realizar regressão linear num trabalho de Azure Stream Analytics que realiza formação contínua e pontuação num cenário de trading de alta frequência.

Pré-requisitos

Fluxo de trabalho de negociação de alta frequência

O fluxo lógico da negociação de alta frequência é:

  1. Obter cotações em tempo real de uma bolsa de valores mobiliários.
  2. Construir um modelo preditivo em torno das cotações para antecipar o movimento do preço.
  3. Colocar ordens de compra ou venda para ganhar dinheiro com a previsão bem-sucedida dos movimentos de preço.

Este cenário exige:

  • Um fluxo de cotações em tempo real.
  • Um modelo preditivo que pode operar com as cotações em tempo real.
  • Uma simulação de negociação que demonstra o lucro ou perda do algoritmo de negociação.

Feed de cotações em tempo real

Importante

A API WebSocket de negociação IEX (iextrading.com) referida nesta secção foi retirada de serviço. A IEX Cloud fornece agora dados de mercado através da IEX Cloud com diferentes autenticações e endpoints. Atualize a URL e a autenticação na sua implementação em conformidade.

Importante

Os SocketIoClientDotNet pacotes e WindowsAzure.ServiceBus NuGet usados neste exemplo estão obsoletos. Para projetos novos, use uma biblioteca cliente Socket.IO atual e o pacote Azure.Messaging.EventHubs com EventHubProducerClient em vez do legado EventHubClient.

A Investors Exchange (IEX) anteriormente oferecia cotações gratuitas em tempo real utilizando socket.io. Pode escrever um programa simples de consola para receber orçamentos em tempo real e enviá-los para o Hubs de Eventos do Azure como fonte de dados. O código seguinte é um esqueleto do programa. O código omite o tratamento de erros para simplificar. Também precisa de incluir os pacotes NuGet SocketIoClientDotNet e WindowsAzure.ServiceBus no seu projeto.

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);
});

Atenção

Este exemplo de código é apenas para ilustração. O endpoint da API WebSocket do IEX e os pacotes NuGet aqui usados já não estão disponíveis. Não uses este código em produção. Consulte as notas IMPORTANTES anteriores nesta secção para alternativas atuais.

Aqui estão alguns eventos de exemplo gerados:

{"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

O carimbo temporal do evento é lastUpdated, em tempo de época.

Modelo preditivo para negociação de alta frequência

Para esta demonstração, o exemplo utiliza um modelo linear descrito em Order Imbalance Based Strategy in High Frequency Algorithmic Trading.

O desequilíbrio das ordens de volume (VOI) é uma função do preço e volume atuais de compra/venda, e do preço e volume de compra/venda a partir do último tick. O artigo identifica a correlação entre o VOI e os movimentos futuros dos preços. Constrói um modelo linear entre os últimos cinco valores de VOI e a variação de preço nos 10 ticks seguintes. O modelo treina com os dados do dia anterior com regressão linear.

O modelo treinado faz então previsões de variação de preço nas cotações do dia de negociação atual em tempo real. Quando o modelo prevê uma variação de preço suficientemente grande, executa uma operação. Dependendo da definição do limiar, uma única ação pode gerar milhares de operações durante um dia de negociação.

Diagrama que mostra a fórmula de definição do desequilíbrio do volume de ordens usada na negociação de alta frequência.

As secções seguintes mostram como expressar as operações de treino e previsão num trabalho de Azure Stream Analytics. A consulta completa é uma única instrução WITH composta por expressões de tabela comuns (CTEs) que formam uma cadeia de processamento:

Fase CTE Purpose
typeconvertedquotes Converter campos de entrada brutos para tipos SQL adequados
timefilteredquotes Filtre as cotações até ao horário de negociação e remova dados inválidos
shiftedquotes Utilize LAG para obter os valores bid/ask do tick anterior
currentPriceAndVOI Calcular o desequilíbrio de ordem de volume (VOI) entre o tick atual e o anterior
shiftedPriceAndShiftedVOI Crie sequências compostas por 10 preços médios consecutivos e 2 valores consecutivos de VOI
modelInput Remodelar dados em vetores de características (VOI como x, delta de preço como y)
modelagg / modelparambs / model Treine um modelo de regressão linear de duas variáveis usando agregados SUM e AVG
shiftedVOI / VOIAndModel / VOIANDModelJoined Junta os valores atuais do VOI com o modelo treinado do dia anterior
prediction Calcule a alteração futura esperada do preço (EFPC) a partir do modelo
tradeSignal Gerar sinais de compra/venda quando o EFPC ultrapassar o limiar de ±0,02

Note

Esta consulta requer o nível de compatibilidade 1.1 ou posterior do Azure Stream Analytics, que preserva a capitalização dos nomes dos campos para garantir um comportamento previsível com UDAs.

Limpar e converter campos de introdução de citações

O primeiro CTE da consulta do Azure Stream Analytics converte os dados brutos de cotação do Event Hubs em colunas SQL corretamente tipadas. O DATEADD converte o tempo de época (milissegundos Unix) em data-hora. TRY_CAST coage os tipos de dados sem falhar a consulta. Converta os campos de entrada para os tipos de dados esperados para evitar comportamento inesperado na manipulação ou comparação dos campos.

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
),

Recuperar valores de tick anteriores com LAG

O próximo CTE na consulta do Azure Stream Analytics usa a função LAG para obter o preço de compra/venda e o volume do tick anterior para cada símbolo da ação. É arbitrariamente escolhido o valor de uma hora para DURAÇÃO LIMITE. Tendo em conta a frequência das cotações, pode encontrar a variação anterior recorrendo à hora anterior.

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
),

Calcular o desequilíbrio de ordem de volume (VOI)

O próximo CTE calcula o valor do VOI a partir dos dados de bid/ask do tick atual e anterior. A consulta exclui os valores nulos nos casos em que não existe um tick anterior.

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
),

Construir sequências de características para treino de modelos

O próximo CTE usa novamente o LAG para criar uma sequência com 2 valores consecutivos de VOI, seguidos de 10 valores consecutivos de preço médio. Estas sequências formam os dados de treino para o modelo de regressão linear.

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
),

Remodelar dados em vetores de características

A próxima CTE remodela as sequências de preços e VOI em vetores de características para um modelo linear de duas variáveis, onde os valores de VOI são as variáveis independentes (x1, x2) e a variação média futura do preço é a variável dependente (y). Eventos com dados incompletos são filtrados.

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
),

Treine o modelo de regressão linear com SUM e AVG

Como Azure Stream Analytics não tem uma função de regressão linear incorporada, a consulta utiliza agregados SUM e AVG para calcular os coeficientes (a, b1, b2) para o modelo de regressão linear de duas variáveis. O modelo é treinado diariamente usando uma janela de tumbling de 24 horas.

Diagrama que mostra a fórmula matemática de regressão linear para calcular coeficientes de modelos.

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
),

Avaliar as cotações atuais usando o modelo do dia anterior

Para usar o modelo de regressão linear treinado do dia anterior para pontuar o evento atual, a consulta junta as aspas aos coeficientes do modelo. Em vez de usar JOIN, a consulta utiliza UNION para combinar eventos de modelo e citar eventos num único fluxo. Depois, usa LAG para emparelhar os eventos com o modelo do dia anterior, de modo que obténs exatamente uma correspondência. Devido ao fim de semana, a consulta retrocede três dias (72 horas). Se fosse usado um JOIN simples, obter-se-iam três modelos para cada evento de cotação.

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'
),

Gerar sinais comerciais a partir de previsões

Os CTEs finais calculam a alteração futura esperada do preço (EFPC) aplicando a fórmula de regressão linear (a + b1 * x1 + b2 * x2) e depois geram sinais de compra/venda com base num limiar de ±0,02. Um valor de negociação de 10 corresponde a compra. Um valor de transação de -10 corresponde a uma venda.

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
),

Teste a estratégia de negociação com uma simulação

Depois de gerar os sinais de negociação, teste a eficácia da estratégia sem negociar realmente.

Este teste usa um UDA com uma janela de salto que salta a cada minuto. O agrupamento na data e a cláusula HAVING garantem que a janela só contabilize eventos que pertencem ao mesmo dia. Para uma janela com salto que abrange dois dias, a data no GROUP BY separa o agrupamento no dia anterior e no dia atual. A cláusula HAVING filtra as janelas que terminam no dia atual mas se agrupam no dia anterior.

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
)

O JavaScript UDA inicializa todos os acumuladores na init função, calcula a transição de estado com cada evento adicionado à janela e devolve os resultados da simulação no final da janela. A simulação detém ou vende a descoberto 10 ações em cada operação. O custo de transação é fixo $8. A tabela seguinte mostra as quatro ações de negociação que a UDA realiza:

Condition Sinal Action Posição após
Sem participação atual Comprar (10) Comprar para abrir Long
Sem participação atual Vender (-10) Vender para abrir (a descoberto) Short
Posição longa Vender (-10) Vender para fechar, depois vender para abrir (a descoberto) Short
Posição curta Comprar (10) Comprar para fechar, depois comprar para abrir 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

O conector de saída do Power BI do Azure Stream Analytics está programado para descontinuação. Considere usar destinos alternativos de saída, como Azure Data Explorer, Azure Synapse Analytics ou um armazenamento de dados ao qual o Power BI possa ligar-se via DirectQuery ou importar. Para mais informações, consulte saída do Azure Stream Analytics para o Power BI.

Por fim, exporte os resultados para o painel do Power BI para visualização.

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 */

Gráfico que mostra sinais de negociação visualizados num painel de Power BI para a simulação de negociação.

Gráfico que mostra os resultados de lucros e prejuízos visualizados num painel de Power BI para a simulação de negociação.

Resumo

Este artigo mostra como implementar um modelo realista de trading de alta frequência com uma consulta moderadamente complexa no Azure Stream Analytics. O modelo usa duas variáveis de entrada em vez de cinco porque o Azure Stream Analytics não inclui uma função de regressão linear incorporada. No entanto, também pode implementar algoritmos mais sofisticados com dimensões mais elevadas como UDAs em JavaScript.

Pode testar e depurar a maioria das consultas, exceto o JavaScript UDA, usando ferramentas Azure Stream Analytics para Visual Studio Code para desenvolvimento, testes e depuração de consultas.