Réflexion sur la stratégie de trading à haute fréquence (5)

Auteur:Le foin, Créé à partir de: 2023-08-09 18:13:16, mis à jour à partir de: 2023-09-18 19:51:59

img

L'article précédent a introduit les différentes méthodes de calcul des prix intermédiaires et a donné une modification des prix intermédiaires. Cet article continue sur ce sujet.

Données requises

Les données de flux d'ordres et les données de profondeur de dix disques proviennent de la collecte de disques réels, et la fréquence de mise à jour est de 100 ms. Les disques réels ne contiennent que des données de vente et d'achat en temps réel.

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]]

La distribution des vingt fichiers est conforme aux prévisions: plus la distance entre les transactions est longue, plus il y a généralement d'enveloppes, et les enveloppes achetées et vendues sont approximativement symétriques.

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

La combinaison des données de profondeur et des données de transaction permet d'évaluer l'exactitude des prévisions. Ici, les données de transaction sont assurées plus tard que les données de profondeur, et l'erreur moyenne de la valeur des prévisions par rapport au prix réel des transactions est calculée directement sans tenir compte du délai.

Il s'avère que l'erreur de prix moyen moyen d'un achat-vente est la plus grande, et que l'erreur est immédiatement beaucoup plus petite après avoir été changée en weight_mid_price, ce qui améliore encore un peu le prix moyen en ajustant le poids. Après l'article d'hier, les commentaires ont été vérifiés et les résultats sont meilleurs. En pensant à la raison suivante, il devrait y avoir une différence de fréquence d'événements, I étant un événement à faible probabilité à proximité de -1 et 1, pour corriger ces faibles probabilités, les prévisions d'événements à haute fréquence sont moins précises, donc, en prenant davantage en compte les événements à haute fréquence, j'ai ajusté les paramètres suivants:

img

Le résultat est encore un peu meilleur. Comme l'a dit l'article précédent, la stratégie devrait être de prévoir avec plus de données, avec plus de profondeur et de données de transaction d'ordres, l'enchevêtrement et l'amélioration de la facture sont déjà faibles.

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

Considérez la deuxième profondeur.

L'idée ici est d'examiner la variation du prix de transaction pour mesurer la contribution de ce paramètre au prix intermédiaire. Par exemple, le graphique de profondeur de la première rangée montre que, lorsque le prix de transaction augmente, le prix de transaction est plus susceptible de changer, ce qui indique que le prix de transaction a apporté une contribution positive.

Le deuxième temps est traité de la même manière, et on constate que l'effet est légèrement inférieur à celui du premier temps, mais qu'il ne peut être négligé. La profondeur du troisième temps a également une faible contribution, mais la monotonie est beaucoup plus faible, et la profondeur plus profonde n'a pratiquement aucune valeur de référence.

Les trois paramètres de déséquilibre sont répartis en différentes pondérations en fonction de leur contribution, et l'erreur de prévision est encore plus faible pour les différentes méthodes de calcul.

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

Considérez les données de transaction

Les données de transaction reflètent directement le degré d'absence, après tout, il s'agit d'une option pour la participation de l'or et de l'argent authentiques, alors que le coût de l'annonce est beaucoup plus bas, et même dans des cas de fraude intentionnelle.

Compte tenu de la forme, le nombre moyen d'arrivées d'ordres est défini comme VI, Vb et Vs, qui représentent respectivement le nombre moyen d'événements au sein d'unité d'achat et de vente.

img

Les résultats ont montré que le nombre d'arrivées à court terme est le plus significatif sur les prévisions de variation des prix, négativement lié aux prix lorsque VI est entre < 0.1-0.9 et rapidement positif en dehors de la plage. Cela suggère que les prix reviennent à l'équilibre lorsque le marché n'est pas extrême, dominé par la turbulence, et qu'ils sortent de la tendance lorsque le marché extrême se produit, par exemple lorsqu'un grand nombre d'ordres d'achat et de vente sont surchargés. Même en excluant ces situations de faible probabilité, la simple hypothèse selon laquelle la tendance et VI satisfont à une relation linéaire négative, la différence d'erreur de prévision du prix intermédiaire diminue considérablement.

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

Prix intermédiaire intégré

Étant donné que les données de volume et de transaction sont utiles pour les prévisions de prix intermédiaires, il est possible de combiner ces deux paramètres, où l'attribution de la pondération est relativement aléatoire et ne tient pas compte des conditions de la frontière. Dans les cas extrêmes, le prix intermédiaire prévu peut ne pas être entre un achat et un achat, mais aussi longtemps que l'erreur peut être réduite, et ne se soucie pas de ces détails.

L'erreur de prédiction finale est tombée de 0,00487 au début à 0,0043, sans plus approfondir, il y a encore beaucoup de choses à extraire du prix intermédiaire, après tout, le prix intermédiaire de la prédiction est dans le prix de la prédiction, vous pouvez essayer vous-même.

#注意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

Résumé

Cet article combine des données de profondeur et des données de transaction, améliore encore la méthode de calcul du prix intermédiaire, donne une méthode de mesure de l'exactitude, améliore l'exactitude des prévisions de variation des prix. Les paramètres sont généralement moins rigoureux, à titre de référence.


Plus de

mztcoinLe blogueur a écrit: "Je ne suis pas content de ce que j'ai vu.

Je suis Louis.Qui sait?

Je suis désolée.et les veuves et les orphelins,