Pensamento sobre estratégias de negociação de alta frequência (5)

Autora:Ervas daninhas, Criado: 2023-08-09 18:13:16, Atualizado: 2023-09-18 19:51:59

img

O artigo anterior introduziu inicialmente os vários métodos de cálculo dos preços intermediários e deu uma revisão dos preços intermediários, que este artigo continua a aprofundar no tema.

Dados necessários

Os dados do fluxo de pedidos e os dados de profundidade de dez discos, provenientes da coleção de discos reais, são atualizados com uma frequência de 100 ms. Os discos reais contêm apenas dados de compra e venda de um disco e são atualizados em tempo real, para ser simples e temporariamente desnecessários. Considerando que os dados são muito grandes, apenas 100 mil linhas de dados de profundidade são mantidas e os mercados de cada disco também são separados em colunas separadas.

from datetime import date,datetime
import time
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import ast
%matplotlib inline
tick_size = 0.0001
trades = pd.read_csv('YGGUSDT_aggTrade.csv',names=['type','event_time', 'agg_trade_id','symbol', 'price', 'quantity', 'first_trade_id', 'last_trade_id',
       'transact_time', 'is_buyer_maker'])
trades = trades.groupby(['transact_time','is_buyer_maker']).agg({
    'transact_time':'last',
    'agg_trade_id': 'last',
    'price': 'first',
    'quantity': 'sum',
    'first_trade_id': 'first',
    'last_trade_id': 'last',
    'is_buyer_maker': 'last',
})
trades.index = pd.to_datetime(trades['transact_time'], unit='ms')
trades.index.rename('time', inplace=True)
trades['interval'] = trades['transact_time'] - trades['transact_time'].shift()
depths = pd.read_csv('YGGUSDT_depth.csv',names=['type','event_time', 'transact_time','symbol', 'u1', 'u2', 'u3', 'bids','asks'])
depths = depths.iloc[:100000]
depths['bids'] = depths['bids'].apply(ast.literal_eval).copy()
depths['asks'] = depths['asks'].apply(ast.literal_eval).copy()
def expand_bid(bid_data):
    expanded = {}
    for j, (price, quantity) in enumerate(bid_data):
        expanded[f'bid_{j}_price'] = float(price)
        expanded[f'bid_{j}_quantity'] = float(quantity)
    return pd.Series(expanded)
def expand_ask(ask_data):
    expanded = {}
    for j, (price, quantity) in enumerate(ask_data):
        expanded[f'ask_{j}_price'] = float(price)
        expanded[f'ask_{j}_quantity'] = float(quantity)
    return pd.Series(expanded)
# 应用到每一行,得到新的df
expanded_df_bid = depths['bids'].apply(expand_bid)
expanded_df_ask = depths['asks'].apply(expand_ask)
# 在原有df上进行扩展
depths = pd.concat([depths, expanded_df_bid, expanded_df_ask], axis=1)
depths.index = pd.to_datetime(depths['transact_time'], unit='ms')
depths.index.rename('time', inplace=True);
trades = trades[trades['transact_time'] < depths['transact_time'].iloc[-1]]

Primeiro, veja a distribuição desses 20 mercados de arquivos, conforme o esperado, quanto mais longe estiverem as transações, mais listados geralmente haverá, e os listados de compra e venda serão aproximadamente simétricos.

bid_mean_list = []
ask_mean_list = []
for i in range(20):
    bid_mean_list.append(round(depths[f'bid_{i}_quantity'].mean(),0))
    ask_mean_list.append(round(depths[f'ask_{i}_quantity'].mean(),0))
plt.figure(figsize=(10, 5))
plt.plot(bid_mean_list);
plt.plot(ask_mean_list);
plt.grid(True)

img

Combine dados de profundidade e transação para avaliar a precisão das previsões. Aqui, os dados de transação são garantidos depois dos dados de profundidade, sem considerar o atraso, e o erro médio do valor da previsão é calculado diretamente em relação ao preço da transação real.

A partir dos resultados, o erro de um preço médio de compra e venda é o maior, e após a mudança para weight_mid_price, o erro é imediatamente muito menor, melhorando um pouco mais com o ajuste do preço médio ponderado. Após o post de ontem, o feedback foi apenas com I^3/2, aqui verificado e descobrimos resultados melhores. Pensando na seguinte razão, deve ser a diferença de frequência de ocorrência de eventos, I é um evento de baixa probabilidade quando está perto de -1 e 1, para corrigir essas baixas probabilidades, e ao invés disso, a previsão de eventos de alta frequência não é tão precisa, portanto, no caso de eventos de alta frequência, eu ajustei novamente o seguinte:

img

O resultado é um pouco melhor. O artigo anterior disse que a estratégia deve ser feita com mais dados para fazer previsões, e que o ganho com o engarrafamento e a faturação é muito fraco quando há mais profundidade e dados de transação de pedidos.

df = pd.merge_asof(trades, depths, on='transact_time', direction='backward')
df['spread'] = round(df['ask_0_price'] - df['bid_0_price'],4)
df['mid_price'] = (df['bid_0_price']+ df['ask_0_price']) / 2
df['I'] = (df['bid_0_quantity'] - df['ask_0_quantity']) / (df['bid_0_quantity'] + df['ask_0_quantity'])
df['weight_mid_price'] = df['mid_price'] + df['spread']*df['I']/2
df['adjust_mid_price'] = df['mid_price'] + df['spread']*(df['I'])*(df['I']**8+1)/4
df['adjust_mid_price_2'] = df['mid_price'] + df['spread']*df['I']*(df['I']**2+1)/4
df['adjust_mid_price_3'] = df['mid_price'] + df['spread']*df['I']**3/2
df['adjust_mid_price_4'] = df['mid_price'] + df['spread']*(df['I']+0.3)*(df['I']**4+0.7)/3.8
print('平均值     mid_price的误差:', ((df['price']-df['mid_price'])**2).sum())
print('挂单量加权 mid_price的误差:', ((df['price']-df['weight_mid_price'])**2).sum())
print('调整后的   mid_price的误差:', ((df['price']-df['adjust_mid_price'])**2).sum())
print('调整后的 mid_price_2的误差:', ((df['price']-df['adjust_mid_price_2'])**2).sum())
print('调整后的 mid_price_3的误差:', ((df['price']-df['adjust_mid_price_3'])**2).sum())
print('调整后的 mid_price_4的误差:', ((df['price']-df['adjust_mid_price_4'])**2).sum())
平均值     mid_price的误差: 0.0048751924999999845
挂单量加权 mid_price的误差: 0.0048373440193987035
调整后的   mid_price的误差: 0.004803654771638586
调整后的 mid_price_2的误差: 0.004808216498329721
调整后的 mid_price_3的误差: 0.004794984755260528
调整后的 mid_price_4的误差: 0.0047909595497071375

Considere a segunda profundidade.

Aqui, com a ideia de examinar os diferentes valores que influenciam um parâmetro, a variação do preço do negócio mede a contribuição desse parâmetro para o preço intermediário. Como no gráfico de profundidade do primeiro grau, com o aumento do I, o preço do negócio é mais provável que o preço do negócio mude, o que indica que o I contribuiu positivamente.

O segundo disco foi tratado da mesma forma, descobrindo que, embora o efeito seja um pouco menor do que o primeiro, não pode ser negligenciado. A profundidade do terceiro disco também contribuiu de forma fraca, mas a monotonia é muito pior, e a profundidade mais profunda praticamente não tem valor de referência.

Dependendo do grau de contribuição, os três parâmetros de desequilíbrio são atribuídos diferentes pesos, e o erro de previsão diminui ainda mais para diferentes métodos de cálculo.

bins = np.linspace(-1, 1, 50)
df['change'] = (df['price'].pct_change().shift(-1))/tick_size
df['I_bins'] = pd.cut(df['I'], bins, labels=bins[1:])
df['I_2'] = (df['bid_1_quantity'] - df['ask_1_quantity']) / (df['bid_1_quantity'] + df['ask_1_quantity'])
df['I_2_bins'] = pd.cut(df['I_2'], bins, labels=bins[1:])
df['I_3'] = (df['bid_2_quantity'] - df['ask_2_quantity']) / (df['bid_2_quantity'] + df['ask_2_quantity'])
df['I_3_bins'] = pd.cut(df['I_3'], bins, labels=bins[1:])
df['I_4'] = (df['bid_3_quantity'] - df['ask_3_quantity']) / (df['bid_3_quantity'] + df['ask_3_quantity'])
df['I_4_bins'] = pd.cut(df['I_4'], bins, labels=bins[1:])
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(8, 5))


axes[0][0].plot(df.groupby('I_bins')['change'].mean())
axes[0][0].set_title('I')
axes[0][0].grid(True)

axes[0][1].plot(df.groupby('I_2_bins')['change'].mean())
axes[0][1].set_title('I 2')
axes[0][1].grid(True)

axes[1][0].plot(df.groupby('I_3_bins')['change'].mean())
axes[1][0].set_title('I 3')
axes[1][0].grid(True)

axes[1][1].plot(df.groupby('I_4_bins')['change'].mean())
axes[1][1].set_title('I 4')
axes[1][1].grid(True)
plt.tight_layout();

img

df['adjust_mid_price_4'] = df['mid_price'] + df['spread']*(df['I']+0.3)*(df['I']**4+0.7)/3.8
df['adjust_mid_price_5'] = df['mid_price'] + df['spread']*(0.7*df['I']+0.3*df['I_2'])/2
df['adjust_mid_price_6'] = df['mid_price'] + df['spread']*(0.7*df['I']+0.3*df['I_2'])**3/2
df['adjust_mid_price_7'] = df['mid_price'] + df['spread']*(0.7*df['I']+0.3*df['I_2']+0.3)*((0.7*df['I']+0.3*df['I_2'])**4+0.7)/3.8
df['adjust_mid_price_8'] = df['mid_price'] + df['spread']*(0.7*df['I']+0.2*df['I_2']+0.1*df['I_3']+0.3)*((0.7*df['I']+0.3*df['I_2']+0.1*df['I_3'])**4+0.7)/3.8
print('调整后的 mid_price_4的误差:', ((df['price']-df['adjust_mid_price_4'])**2).sum())
print('调整后的 mid_price_5的误差:', ((df['price']-df['adjust_mid_price_5'])**2).sum())
print('调整后的 mid_price_6的误差:', ((df['price']-df['adjust_mid_price_6'])**2).sum())
print('调整后的 mid_price_7的误差:', ((df['price']-df['adjust_mid_price_7'])**2).sum())
print('调整后的 mid_price_8的误差:', ((df['price']-df['adjust_mid_price_8'])**2).sum())
调整后的 mid_price_4的误差: 0.0047909595497071375
调整后的 mid_price_5的误差: 0.0047884350488318714
调整后的 mid_price_6的误差: 0.0047778319053133735
调整后的 mid_price_7的误差: 0.004773578540592192
调整后的 mid_price_8的误差: 0.004771415189297518

Considere os dados de transação

Os dados de transações refletem diretamente o grau de espaços, pois é a opção de participação de ouro e prata, e o custo de um pedido é muito menor, e até mesmo há casos de fraude intencional. Portanto, a estratégia de previsão de preços intermediários deve considerar os dados de transações.

Considerando a forma, define o número médio de chegada de pedidos no desequilíbrio VI, Vb e Vs, representando respectivamente o número médio dentro do evento unitário de compra e venda.

img

Os resultados revelaram que o número de chegadas em curto prazo é mais significativo para as previsões de mudanças de preços, quando VI está entre < 0.1-0.9 e está associado ao preço negativo, enquanto fora do intervalo está rapidamente relacionado ao preço positivo. Isso sugere que os preços retornam ao equilíbrio quando o mercado não é extremo, dominado pela turbulência, e quando o mercado extremo ocorre, como um grande número de compras e vendas, quando sai da tendência. Mesmo sem considerar esses casos de baixa probabilidade, a simples hipótese de que a tendência e VI satisfazem uma relação linear negativa, o diferencial de erro de previsão do preço médio também diminui drasticamente.

img

alpha=0.1
df['avg_buy_interval'] = None
df['avg_sell_interval'] = None
df.loc[df['is_buyer_maker'] == True, 'avg_buy_interval'] = df[df['is_buyer_maker'] == True]['transact_time'].diff().ewm(alpha=alpha).mean()
df.loc[df['is_buyer_maker'] == False, 'avg_sell_interval'] = df[df['is_buyer_maker'] == False]['transact_time'].diff().ewm(alpha=alpha).mean()
df['avg_buy_quantity'] = None
df['avg_sell_quantity'] = None
df.loc[df['is_buyer_maker'] == True, 'avg_buy_quantity'] = df[df['is_buyer_maker'] == True]['quantity'].ewm(alpha=alpha).mean()
df.loc[df['is_buyer_maker'] == False, 'avg_sell_quantity'] = df[df['is_buyer_maker'] == False]['quantity'].ewm(alpha=alpha).mean()
df['avg_buy_quantity'] = df['avg_buy_quantity'].fillna(method='ffill')
df['avg_sell_quantity'] = df['avg_sell_quantity'].fillna(method='ffill')
df['avg_buy_interval'] = df['avg_buy_interval'].fillna(method='ffill')
df['avg_sell_interval'] = df['avg_sell_interval'].fillna(method='ffill')

df['avg_buy_rate'] = 1000 / df['avg_buy_interval']
df['avg_sell_rate'] =1000 / df['avg_sell_interval']

df['avg_buy_volume'] = df['avg_buy_rate']*df['avg_buy_quantity']
df['avg_sell_volume'] = df['avg_sell_rate']*df['avg_sell_quantity']
df['I'] = (df['bid_0_quantity']- df['ask_0_quantity']) / (df['bid_0_quantity'] + df['ask_0_quantity'])
df['OI'] = (df['avg_buy_rate']-df['avg_sell_rate']) / (df['avg_buy_rate'] + df['avg_sell_rate'])
df['QI'] = (df['avg_buy_quantity']-df['avg_sell_quantity']) / (df['avg_buy_quantity'] + df['avg_sell_quantity'])
df['VI'] = (df['avg_buy_volume']-df['avg_sell_volume']) / (df['avg_buy_volume'] + df['avg_sell_volume'])
bins = np.linspace(-1, 1, 50)
df['VI_bins'] = pd.cut(df['VI'], bins, labels=bins[1:])
plt.plot(df.groupby('VI_bins')['change'].mean());
plt.grid(True)

img

df['adjust_mid_price'] = df['mid_price'] + df['spread']*df['I']/2
df['adjust_mid_price_9'] = df['mid_price'] + df['spread']*(-df['OI'])*2
df['adjust_mid_price_10'] = df['mid_price'] + df['spread']*(-df['VI'])*1.4
print('调整后的mid_price   的误差:', ((df['price']-df['adjust_mid_price'])**2).sum())
print('调整后的mid_price_9 的误差:', ((df['price']-df['adjust_mid_price_9'])**2).sum())
print('调整后的mid_price_10的误差:', ((df['price']-df['adjust_mid_price_10'])**2).sum())
调整后的mid_price   的误差: 0.0048373440193987035
调整后的mid_price_9 的误差: 0.004629586542840461
调整后的mid_price_10的误差: 0.004401790287167206

Preço intermediário integrado

Considerando que tanto a quantidade de pedidos como os dados de transações são úteis para a previsão do preço intermediário, é possível combinar os dois parâmetros, onde a atribuição de peso é relativamente arbitrária e não considera as condições da fronteira. Em casos extremos, o preço intermediário previsto pode não estar entre um comprador e um vendedor, mas, desde que o erro possa ser reduzido, não se importa com esses detalhes.

O erro da previsão final caiu de 0,00487 para 0,0043 no início, e não podemos continuar a aprofundar, o preço intermediário ainda tem muito a ser extraído, afinal, o preço intermediário é o preço da previsão, todos podem experimentá-lo.

#注意VI需要延后一个使用
df['price_change'] = np.log(df['price']/df['price'].rolling(40).mean())
df['CI'] = -1.5*df['VI'].shift()+0.7*(0.7*df['I']+0.2*df['I_2']+0.1*df['I_3'])**3 + 150*df['price_change'].shift(1)
df['adjust_mid_price_11'] = df['mid_price'] + df['spread']*(df['CI'])
print('调整后的mid_price_11的误差:', ((df['price']-df['adjust_mid_price_11'])**2).sum())
调整后的mid_price_11的误差: 0.00421125960463469

Resumo

Este artigo combina dados de profundidade e dados de transações, melhorando ainda mais o método de cálculo do preço intermediário, fornecendo um método para medir a precisão, melhorando a precisão das previsões de mudanças de preço. Em geral, os parâmetros não são tão rigorosos, apenas para referência.


Mais.

mztcoinO que é que ele tem a ver com isso?

Louis?Quem sabe?

XukittyE não há, nem há, senão admiração.