Bài viết chủ yếu đề cập đến chiến lược giao dịch tần số cao, nghiên cứu tập trung vào mô hình hóa giao dịch tích lũy và cú sốc giá. Bài viết đưa ra một mô hình đầu tiên về vị trí giao dịch ưu việt nhất bằng cách phân tích các giao dịch đơn lẻ, cú sốc giá ở khoảng cách cố định và ảnh hưởng của giao dịch đối với giá. Mô hình này dựa trên sự hiểu biết về khối lượng giao dịch và cú sốc giá, cố gắng tìm vị trí giao dịch tối ưu nhất.
Bài trước đưa ra một biểu thức xác suất giao dịch đơn lớn hơn một giá trị:
Chúng ta cũng quan tâm đến sự phân bố khối lượng giao dịch trong một khoảng thời gian, và trực quan, nó nên liên quan đến số lượng giao dịch và tần suất đặt hàng mỗi lần. Dưới đây, xử lý dữ liệu theo khoảng thời gian cố định.
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']
Kết hợp các giao dịch đơn lẻ với mỗi khoảng thời gian 1s thành số giao dịch, loại bỏ phần không giao dịch, và kết hợp với phân bố giao dịch đơn lẻ trên, kết quả có thể thấy tốt hơn, xem tất cả các giao dịch trong 1s là một đơn lẻ, vấn đề này đã trở thành một vấn đề đã được giải quyết. Nhưng khi chu kỳ kéo dài (so với tần suất giao dịch), sai số được tìm thấy tăng lên, và nghiên cứu cho thấy sai số này chính xác là do sửa đổi phân bố Pareto trước đó. Điều này cho thấy khi chu kỳ kéo dài, chứa nhiều giao dịch đơn lẻ hơn, nhiều giao dịch kết hợp gần với phân bố Pareto tích lũy hơn, tình huống này cần loại bỏ sửa đổi.
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
agg_trade_id | giá cả | số lượng | first_trade_id | Last_trade_id | là_buyer_maker | ngày | Transact_time | khoảng thời gian | khác nhau | |
---|---|---|---|---|---|---|---|---|---|---|
2023-01-27 00:00:00.161 | 1138369 | 2.901 | 54.3 | 3806199 | 3806201 | Không đúng | 2023-01-27 00:00:00.161 | 1674777600161 | N/A | 0.001 |
2023-01-27 00:00:04.140 | 1138370 | 2.901 | 291.3 | 3806202 | 3806203 | Không đúng | 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 | Không đúng | 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 | Không đúng | 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 | Không đúng | 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 | Không đúng | 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 | Không đúng | 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 | Không đúng | 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 | Không đúng | 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 | Không đúng | 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)
Bây giờ để tổng hợp một công thức chung cho phân bố các giao dịch tích lũy theo thời gian khác nhau, hãy sử dụng phân bố giao dịch đơn lẻ để phù hợp, mà không cần phân tích riêng biệt mỗi lần.
Trong đó avg_interval chỉ khoảng cách trung bình của giao dịch đơn lẻ và avg_interval_T chỉ khoảng cách trung bình cần ước tính, nói một chút đi vòng qua. Nếu chúng ta ước tính giao dịch 1s, cần thống kê khoảng cách trung bình của các sự kiện được giao dịch trong 1s. Nếu xác suất đến của đơn hàng phù hợp với phân bố Parsons, nó nên có thể được ước tính trực tiếp ở đây, nhưng độ lệch thực tế rất lớn, không được giải thích ở đây.
Lưu ý rằng xác suất giao dịch lớn hơn một giá trị cụ thể trong khoảng thời gian nào đó ở đây và xác suất giao dịch thực tế ở vị trí đó trong độ sâu nên khác nhau lớn hơn, vì thời gian chờ lâu hơn, khả năng thay đổi sổ hàng sẽ cao hơn và giao dịch cũng dẫn đến sự thay đổi độ sâu, do đó xác suất giao dịch ở cùng một vị trí độ sâu thay đổi trong thời gian thực khi dữ liệu được cập nhật.
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)
Dữ liệu giao dịch là một kho báu, và có rất nhiều dữ liệu để khai thác. Chúng ta nên rất chú ý đến tác động của đơn đặt hàng lên giá cả, ảnh hưởng đến vị trí đặt hàng của chiến lược. Cũng dựa trên dữ liệu tổng hợp transact_time, tính toán chênh lệch giữa giá cuối cùng và giá đầu tiên, nếu chỉ có một đơn đặt hàng, chênh lệch là 0. Điều lạ là vẫn có một số lượng nhỏ dữ liệu kết quả âm, nên là vấn đề thứ tự dữ liệu, không đi sâu ở đây.
Kết quả cho thấy tỷ lệ không gây ra cú sốc lên đến 77%, tỷ lệ một dấu chấm là 16.5%, hai dấu chấm là 3.7%, ba dấu chấm là 1.2, và bốn dấu chấm trở lên là dưới 1%.
Số lượng giao dịch gây ra chênh lệch giá tương ứng đã được thống kê, loại bỏ sự sai lệch quá lớn của cú sốc, cơ bản là phù hợp với mối quan hệ tuyến tính, khoảng 1.000 số lượng gây ra sự biến động giá 1 tick. Nó cũng có thể được hiểu là trung bình khoảng 1.000 đơn vị đeo giá gần mỗi đĩa.
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);
Giá đột phá trong thống kê 2s, khác biệt là ở đây sẽ có âm tính, tất nhiên vì chỉ thống kê thanh toán ở đây, vị trí đối xứng sẽ lớn hơn một dấu chấm. Tiếp tục quan sát mối quan hệ giao dịch và đột phá, chỉ kết quả thống kê lớn hơn 0, kết luận và đơn đặt hàng gần như, cũng là mối quan hệ tuyến tính gần như, mỗi dấu chấm cần khoảng 2000 số lượng.
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);
Trước đây tìm kiếm một giao dịch cần thay đổi tick, nhưng không chính xác, vì nó được xây dựng trong trường hợp giả định cú sốc đã xảy ra. Bây giờ ngược lại, xem cú sốc giá mà giao dịch hoàn thành.
Ở đây, dữ liệu được lấy mẫu theo 1s, một bước dài trên 100 đơn vị, và thống kê sự thay đổi giá trong phạm vi số lượng này.
Trong đó,
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)
số lượng_nhóm | price_diff | quantity_group_center | |
---|---|---|---|
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 |
Với mô hình hóa khối lượng giao dịch và mô hình thô của khối lượng giao dịch tương ứng với cú sốc giá, có vẻ như bạn có thể tính ra vị trí xếp hạng tối ưu.
Bắt đầu bằng cách viết một lợi nhuận mong đợi đơn giản, đó là xác suất thanh toán tích lũy lớn hơn Q trong 1s, nhân với tỷ lệ lợi nhuận mong đợi (tức là giá của cú sốc):
Theo hình ảnh, lợi nhuận mong đợi lớn nhất là khoảng 2500 và khoảng 2,5 lần số lượng giao dịch trung bình. Điều này có nghĩa là đơn bán nên được treo ở vị trí 2500. Một lần nữa cần nhấn mạnh rằng khối lượng giao dịch trong trục ngang đại diện cho 1s không thể đơn giản là tương đương với vị trí sâu.
Phát hiện ra rằng phân bố giao dịch giữa các khoảng thời gian khác nhau là một sự mở rộng đơn giản về phân bố giao dịch đơn lẻ. Cũng dựa trên cú sốc giá và xác suất giao dịch, một mô hình thu nhập mong đợi đơn giản đã được thực hiện, kết quả của mô hình này phù hợp với mong đợi của chúng tôi, nếu số lượng giao dịch bán nhỏ, báo trước giá giảm, cần một lượng nhất định để có khoảng cách lợi nhuận, và khối lượng giao dịch lớn hơn thì xác suất thấp hơn, có một kích thước tối ưu ở giữa, cũng là vị trí chiến lược tìm kiếm.
#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)
Phân tích 🐂🍺