El artículo se centra en la estrategia de operaciones de alta frecuencia, con un enfoque en el modelado acumulativo de la transacción y el impacto de los precios. El artículo propone un modelo preliminar de la posición óptima suspendida mediante el análisis de transacciones individuales, los impactos de precios de intervalos fijos y el efecto de las transacciones en los precios.
El artículo anterior muestra una expresión de probabilidad de que una transacción sea mayor que un determinado valor:
También nos preocupa la distribución del volumen de transacciones durante un período de tiempo, que intuitivamente debería estar relacionada con el volumen de transacciones y la frecuencia de pedidos por transacción. A continuación, procesamos los datos en intervalos fijos.
from datetime import date,datetime
import time
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
trades = pd.read_csv('HOOKUSDT-aggTrades-2023-01-27.csv')
trades['date'] = pd.to_datetime(trades['transact_time'], unit='ms')
trades.index = trades['date']
buy_trades = trades[trades['is_buyer_maker']==False].copy()
buy_trades = buy_trades.groupby('transact_time').agg({
'agg_trade_id': 'last',
'price': 'last',
'quantity': 'sum',
'first_trade_id': 'first',
'last_trade_id': 'last',
'is_buyer_maker': 'last',
'date': 'last',
'transact_time':'last'
})
buy_trades['interval']=buy_trades['transact_time'] - buy_trades['transact_time'].shift()
buy_trades.index = buy_trades['date']
Si se combinan las transacciones por 1s en cada intervalo, se elimina la parte que no se realizó y se ajusta a la distribución de transacciones individuales de arriba, el resultado es mejor, se considera que todas las transacciones dentro de 1s se consideran como una sola transacción, el problema se convierte en un problema resuelto. Pero cuando el ciclo se extiende (relativamente a la frecuencia de transacciones), se encuentra que el error aumenta, y el estudio encontró que este error es causado por la corrección de la distribución de Pareto anterior.
df_resampled = buy_trades['quantity'].resample('1S').sum()
df_resampled = df_resampled.to_frame(name='quantity')
df_resampled = df_resampled[df_resampled['quantity']>0]
buy_trades
el nombre del operador. | precio | cantidad | el nombre del operador | el número de transacción | es_comprador_fabricante | fecha | tiempo de la transacción | el intervalo | Diferencia | |
---|---|---|---|---|---|---|---|---|---|---|
2023-01-27 00:00:00.161 | 1138369 | 2.901 | 54.3 | 3806199 | 3806201 | No es cierto. | 2023-01-27 00:00:00.161 | 1674777600161 | No | 0.001 |
2023-01-27 00:00:04.140 | 1138370 | 2.901 | 291.3 | 3806202 | 3806203 | No es cierto. | 2023-01-27 00:00:04.140 | 1674777604140 | 3979.0 | 0.000 |
2023-01-27 00:00:04.339 | 1138373 | 2.902 | 55.1 | 3806205 | 3806207 | No es cierto. | 2023-01-27 00:00:04.339 | 1674777604339 | 199.0 | 0.001 |
2023-01-27 00:00:04.772 | 1138374 | 2.902 | 1032.7 | 3806208 | 3806223 | No es cierto. | 2023-01-27 00:00:04.772 | 1674777604772 | 433.0 | 0.000 |
2023-01-27 00:00:05.562 | 1138375 | 2.901 | 3.5 | 3806224 | 3806224 | No es cierto. | 2023-01-27 00:00:05.562 | 1674777605562 | 790.0 | 0.000 |
… | … | … | … | … | … | … | … | … | … | … |
2023-01-27 23:59:57.739 | 1544370 | 3.572 | 394.8 | 5074645 | 5074651 | No es cierto. | 2023-01-27 23:59:57.739 | 1674863997739 | 1224.0 | 0.002 |
2023-01-27 23:59:57.902 | 1544372 | 3.573 | 177.6 | 5074652 | 5074655 | No es cierto. | 2023-01-27 23:59:57.902 | 1674863997902 | 163.0 | 0.001 |
2023-01-27 23:59:58.107 | 1544373 | 3.573 | 139.8 | 5074656 | 5074656 | No es cierto. | 2023-01-27 23:59:58.107 | 1674863998107 | 205.0 | 0.000 |
2023-01-27 23:59:58.302 | 1544374 | 3.573 | 60.5 | 5074657 | 5074657 | No es cierto. | 2023-01-27 23:59:58.302 | 1674863998302 | 195.0 | 0.000 |
2023-01-27 23:59:59.894 | 1544376 | 3.571 | 12.1 | 5074662 | 5074664 | No es cierto. | 2023-01-27 23:59:59.894 | 1674863999894 | 1592.0 | 0.000 |
#1s内的累计分布
depths = np.array(range(0, 3000, 5))
probabilities = np.array([np.mean(df_resampled['quantity'] > depth) for depth in depths])
mean = df_resampled['quantity'].mean()
alpha = np.log(np.mean(df_resampled['quantity'] > mean))/np.log(2.05)
probabilities_s = np.array([((1+20**(-depth/mean))*depth/mean+1)**(alpha) for depth in depths])
plt.figure(figsize=(10, 5))
plt.plot(depths, probabilities)
plt.plot(depths, probabilities_s)
plt.xlabel('Depth')
plt.ylabel('Probability of execution')
plt.title('Execution probability at different depths')
plt.grid(True)
df_resampled = buy_trades['quantity'].resample('30S').sum()
df_resampled = df_resampled.to_frame(name='quantity')
df_resampled = df_resampled[df_resampled['quantity']>0]
depths = np.array(range(0, 12000, 20))
probabilities = np.array([np.mean(df_resampled['quantity'] > depth) for depth in depths])
mean = df_resampled['quantity'].mean()
alpha = np.log(np.mean(df_resampled['quantity'] > mean))/np.log(2.05)
probabilities_s = np.array([((1+20**(-depth/mean))*depth/mean+1)**(alpha) for depth in depths])
alpha = np.log(np.mean(df_resampled['quantity'] > mean))/np.log(2)
probabilities_s_2 = np.array([(depth/mean+1)**alpha for depth in depths]) # 无修正
plt.figure(figsize=(10, 5))
plt.plot(depths, probabilities,label='Probabilities (True)')
plt.plot(depths, probabilities_s, label='Probabilities (Simulation 1)')
plt.plot(depths, probabilities_s_2, label='Probabilities (Simulation 2)')
plt.xlabel('Depth')
plt.ylabel('Probability of execution')
plt.title('Execution probability at different depths')
plt.legend()
plt.grid(True)
Ahora, para la distribución de transacciones acumuladas en diferentes tiempos, se resume una fórmula general, que se adapta a la distribución de transacciones individuales, sin tener que hacer estadísticas separadas cada vez.
Donde avg_interval indica el intervalo medio de una transacción, y avg_interval_T indica el intervalo medio del intervalo que se necesita estimar, es decir, un poco de vuelta. Si queremos estimar el intervalo de 1s, necesitamos contar el intervalo medio de los eventos que se incluyen en el intervalo de 1s. Si la probabilidad de llegada de la orden se ajusta a la distribución de Parsons, aquí se debe estimar directamente, pero el desvío real es muy grande, no se explica aquí.
Tenga en cuenta que la probabilidad de que el volumen de transacciones sea mayor que el valor específico en un intervalo de tiempo aquí y la probabilidad de transacciones en la ubicación real en la profundidad deben diferir mucho, ya que cuanto más tiempo se espera, mayor es la probabilidad de que el libro de pedidos cambie, y las transacciones también causan cambios en la profundidad, por lo que la probabilidad de transacciones en la misma ubicación de profundidad cambia en tiempo real a medida que se actualizan los datos.
df_resampled = buy_trades['quantity'].resample('2S').sum()
df_resampled = df_resampled.to_frame(name='quantity')
df_resampled = df_resampled[df_resampled['quantity']>0]
depths = np.array(range(0, 6500, 10))
probabilities = np.array([np.mean(df_resampled['quantity'] > depth) for depth in depths])
mean = buy_trades['quantity'].mean()
adjust = buy_trades['interval'].mean() / 2620
alpha = np.log(np.mean(buy_trades['quantity'] > mean))/0.7178397931503168
probabilities_s = np.array([((1+20**(-depth*adjust/mean))*depth*adjust/mean+1)**(alpha) for depth in depths])
plt.figure(figsize=(10, 5))
plt.plot(depths, probabilities)
plt.plot(depths, probabilities_s)
plt.xlabel('Depth')
plt.ylabel('Probability of execution')
plt.title('Execution probability at different depths')
plt.grid(True)
Los datos de transacciones son un tesoro, y hay muchos datos para extraer. Debemos estar muy atentos al impacto que los pedidos producen en el precio, lo que afecta la posición de la estrategia de lista. También se calcula la diferencia entre el último precio y el primer precio según los datos agregados de transact_time, si solo hay un pedido, la diferencia es 0.
Los resultados muestran que el porcentaje de ticks sin causar impactos es de hasta un 77%, el porcentaje de ticks 1 es del 16.5%, los ticks 2 son del 3.7%, los ticks 3 son del 1.2%, y los ticks 4 o más son de menos del 1%.
El volumen de transacciones causado por el diferencial correspondiente, eliminando la falta de verdad de un impacto demasiado grande, se ajusta básicamente a una relación lineal, con una fluctuación de precios de aproximadamente 1 tick por cada 1000 volúmenes. También se puede entender que la cantidad media de ticks en el precio cercano a cada plato es de aproximadamente 1000.
diff_df = trades[trades['is_buyer_maker']==False].groupby('transact_time')['price'].agg(lambda x: abs(round(x.iloc[-1] - x.iloc[0],3)) if len(x) > 1 else 0)
buy_trades['diff'] = buy_trades['transact_time'].map(diff_df)
diff_counts = buy_trades['diff'].value_counts()
diff_counts[diff_counts>10]/diff_counts.sum()
0.000 0.769965
0.001 0.165527
0.002 0.037826
0.003 0.012546
0.004 0.005986
0.005 0.003173
0.006 0.001964
0.007 0.001036
0.008 0.000795
0.009 0.000474
0.010 0.000227
0.011 0.000187
0.012 0.000087
0.013 0.000080
Name: diff, dtype: float64
diff_group = buy_trades.groupby('diff').agg({
'quantity': 'mean',
'diff': 'last',
})
diff_group['quantity'][diff_group['diff']>0][diff_group['diff']<0.01].plot(figsize=(10,5),grid=True);
La diferencia es que aquí habrá un impacto negativo, por supuesto, ya que aquí solo se estadísticas de pagos, la posición simétrica será mayor de una tick. Continuando observando la relación entre el volumen de transacciones y el impacto, solo se obtendrán resultados estadísticos mayores de 0, la conclusión y el orden individual son similares, también una relación lineal aproximada, cada tick requiere aproximadamente una cantidad de 2000.
df_resampled = buy_trades.resample('2S').agg({
'price': ['first', 'last', 'count'],
'quantity': 'sum'
})
df_resampled['price_diff'] = round(df_resampled[('price', 'last')] - df_resampled[('price', 'first')],3)
df_resampled['price_diff'] = df_resampled['price_diff'].fillna(0)
result_df_raw = pd.DataFrame({
'price_diff': df_resampled['price_diff'],
'quantity_sum': df_resampled[('quantity', 'sum')],
'data_count': df_resampled[('price', 'count')]
})
result_df = result_df_raw[result_df_raw['price_diff'] != 0]
result_df['price_diff'][abs(result_df['price_diff'])<0.016].value_counts().sort_index().plot.bar(figsize=(10,5));
result_df['price_diff'].value_counts()[result_df['price_diff'].value_counts()>30]
0.001 7176
-0.001 3665
0.002 3069
-0.002 1536
0.003 1260
0.004 692
-0.003 608
0.005 391
-0.004 322
0.006 259
-0.005 192
0.007 146
-0.006 112
0.008 82
0.009 75
-0.007 75
-0.008 65
0.010 51
0.011 41
-0.010 31
Name: price_diff, dtype: int64
diff_group = result_df.groupby('price_diff').agg({ 'quantity_sum': 'mean'})
diff_group[(diff_group.index>0) & (diff_group.index<0.015)].plot(figsize=(10,5),grid=True);
El anterior busca el volumen de transacción necesario para un cambio de tick, pero no es preciso, ya que se basa en el supuesto de que el impacto ya ha ocurrido; ahora, a su vez, se observa el impacto de precio que trae el cambio de tick.
Aquí los datos se muestran por 1s, con un paso por cada 100 cantidades, y se estadísticas el movimiento de los precios dentro de este rango de cantidades.
En este caso, el valor C representa el cambio en el precio y el valor Q representa el volumen de compra y venta.
df_resampled = buy_trades.resample('1S').agg({
'price': ['first', 'last', 'count'],
'quantity': 'sum'
})
df_resampled['price_diff'] = round(df_resampled[('price', 'last')] - df_resampled[('price', 'first')],3)
df_resampled['price_diff'] = df_resampled['price_diff'].fillna(0)
result_df_raw = pd.DataFrame({
'price_diff': df_resampled['price_diff'],
'quantity_sum': df_resampled[('quantity', 'sum')],
'data_count': df_resampled[('price', 'count')]
})
result_df = result_df_raw[result_df_raw['price_diff'] != 0]
df = result_df.copy()
bins = np.arange(0, 30000, 100) #
labels = [f'{i}-{i+100-1}' for i in bins[:-1]]
df.loc[:, 'quantity_group'] = pd.cut(df['quantity_sum'], bins=bins, labels=labels)
grouped = df.groupby('quantity_group')['price_diff'].mean()
grouped_df = pd.DataFrame(grouped).reset_index()
grouped_df['quantity_group_center'] = grouped_df['quantity_group'].apply(lambda x: (float(x.split('-')[0]) + float(x.split('-')[1])) / 2)
plt.figure(figsize=(10,5))
plt.scatter(grouped_df['quantity_group_center'], grouped_df['price_diff'],s=10)
plt.plot(grouped_df['quantity_group_center'], np.array(grouped_df['quantity_group_center'].values)/2e6-0.000352,color='red')
plt.xlabel('quantity_group_center')
plt.ylabel('average price_diff')
plt.title('Scatter plot of average price_diff by quantity_group')
plt.grid(True)
grouped_df.head(10)
cantidad_grupo | precio_diferencia | cantidad_grupo_centro | |
---|---|---|---|
0 | 0-199 | -0.000302 | 99.5 |
1 | 100-299 | -0.000124 | 199.5 |
2 | 200-399 | -0.000068 | 299.5 |
3 | 300-499 | -0.000017 | 399.5 |
4 | 400-599 | -0.000048 | 499.5 |
5 | 500-699 | 0.000098 | 599.5 |
6 | 600-799 | 0.000006 | 699.5 |
7 | 700-899 | 0.000261 | 799.5 |
8 | 800-999 | 0.000186 | 899.5 |
9 | 900-1099 | 0.000299 | 999.5 |
Con el modelo de transacciones completas y el modelo de transacciones correspondientes a los impactos de precios, parece que se puede calcular la posición óptima de la lista suspendida.
Primero escriba un rendimiento esperado simple, es decir, la probabilidad de que el pago acumulado sea mayor que Q en 1s, multiplicado por el rendimiento esperado (es decir, el precio del impacto):
De acuerdo con la imagen, la ganancia esperada es máxima alrededor de 2500, aproximadamente 2.5 veces el volumen promedio de transacciones. Es decir, los pedidos de venta deben colgar en la posición de 2500.
Descubrimos que la distribución de entradas en diferentes intervalos de tiempo es una simple escalada de la distribución de entradas en una sola transacción. También se hizo un modelo de rendimiento esperado simple basado en el impacto del precio y la probabilidad de transacción, el resultado de este modelo se ajusta a nuestras expectativas, si la cantidad de entradas vendidas es pequeña, lo que predice una caída de los precios, se necesita un cierto volumen para obtener un margen de beneficio, mientras que el mayor volumen de transacciones, la probabilidad es menor, hay un tamaño óptimo en el medio, y también la estrategia de búsqueda de la posición suspendida.
#1s内的累计分布
df_resampled = buy_trades['quantity'].resample('1S').sum()
df_resampled = df_resampled.to_frame(name='quantity')
df_resampled = df_resampled[df_resampled['quantity']>0]
depths = np.array(range(0, 15000, 10))
mean = df_resampled['quantity'].mean()
alpha = np.log(np.mean(df_resampled['quantity'] > mean))/np.log(2.05)
probabilities_s = np.array([((1+20**(-depth/mean))*depth/mean+1)**(alpha) for depth in depths])
profit_s = np.array([depth/2e6-0.000352 for depth in depths])
plt.figure(figsize=(10, 5))
plt.plot(depths, probabilities_s*profit_s)
plt.xlabel('Q')
plt.ylabel('Excpet profit')
plt.grid(True)
Cuantificación de orcs 🐂🍺