Überlegungen zur Hochfrequenz-Handelstrategie (5)

Schriftsteller:Das Gras, Erstellt: 2023-08-09 18:13:16, Aktualisiert: 2023-09-18 19:51:59

img

Der vorherige Artikel gab einen ersten Einblick in die verschiedenen Mittelpreisberechnungsmethoden und gab eine Revision des Mittelpreises an, die in diesem Artikel weiter vertieft wird.

Daten benötigt

Die Orderflussdaten und die Tiefendaten der zehn Datensätze stammen aus der Festplattenerhebung und werden mit einer Update-Frequenz von 100 ms aktualisiert. Die Festplatten enthalten nur Kauf- und Verkaufsdaten, die in Echtzeit aktualisiert werden.

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

Schauen wir uns zunächst die Verteilung dieser 20 Aktienmärkte an, die den Erwartungen entspricht. Je weiter die Abstand zwischen den Aufschriften ist, desto mehr Aufschriften gibt es im Allgemeinen, und die Aufschriften für Käufe und Verkäufe sind in etwa symmetrisch.

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

Die Kombination von Tiefen- und Transaktionsdaten erleichtert die Bewertung der Prognose-Genauigkeit. Hier wird sichergestellt, dass die Transaktionsdaten später als die Tiefendaten liegen und ohne Berücksichtigung von Verzögerungen direkt die durchschnittliche Verzerrung des Prognosewerts mit dem tatsächlichen Transaktionspreis berechnet wird.

Die Ergebnisse zeigten, dass der Fehler bei einem durchschnittlichen Kauf-Verkauf-Mid_price der größte war. Nach der Änderung zum weight_mid_price wurde der Fehler sofort viel kleiner und wurde durch die Anpassung des gewichteten Mittelfrisses ein wenig verbessert. Nach der Veröffentlichung des gestrigen Artikels wurden die Rückmeldungen nur mit I^3/2 überprüft und die Ergebnisse sind besser.

img

Das Ergebnis ist etwas besser. Wie ich in einem früheren Artikel sagte, sollte die Strategie mit mehr Daten prognostizieren, und mit mehr Tiefe und Ordertransaktionsdaten ist die Effizienz, die mit der Auflistung erzielt werden kann, schwach.

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

Betrachten Sie die zweite Tiefe

Hier ist ein Gedanke, der die unterschiedlichen Abwägungsschwerpunkte untersucht, die ein Parameter beeinflussen, die Veränderungen des Transaktionspreises, um den Beitrag dieses Parameters zum Zwischenpreis zu messen. Wie in der ersten Tiefendiagramm, wenn der Transaktionspreis zunimmt, ist der Transaktionspreis dann wahrscheinlicher, dass der Transaktionspreis sich ändert, was bedeutet, dass I einen positiven Beitrag leistet.

Der zweite Satz wurde in derselben Weise behandelt, und es wurde festgestellt, dass die Effekte, obwohl sie etwas kleiner sind als der erste Satz, nicht ignoriert werden können. Die Tiefe des dritten Satzes hat auch einen geringen Beitrag geleistet, aber die Monotonie ist viel schlechter, und die tiefere Tiefe hat kaum einen Referenzwert.

Je nach Beitragsschwerpunkt werden die Ungleichgewichtsparameter der drei Klassen unterschiedlich gewichtet, und die Prognosefehler sinken weiter, wenn die tatsächlichen Prüfungen für verschiedene Berechnungsmethoden durchgeführt werden.

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

Betrachten Sie Transaktionsdaten

Die Transaktionsdaten spiegeln direkt den Grad der Überschüssigkeit wider. Schließlich ist dies die Wahl der Beteiligung von Echtgold und Silber, während die Kosten für die Aufstellung viel niedriger sind, und es gibt sogar Fälle von absichtlichem Aufstellungsbetrug. Daher ist die Strategie der Prognose von Zwischenpreisen auf Transaktionsdaten ausgerichtet.

Unter Berücksichtigung der Form wird definiert, dass die durchschnittliche Ankunftszahl der Bestellungen VI, Vb und Vs die durchschnittliche Anzahl in den Einheitsereignissen der Auftrags- und Verkaufsbestellungen darstellen.

img

Die Ergebnisse zeigen, dass die Anzahl der Ankünfte in kurzer Zeit am deutlichsten für die Prognose von Preisänderungen ist, wenn VI zwischen <0.1-0.9 liegt und negativ mit den Preisen und außerhalb der Bandbreite schnell positiv mit den Preisen zusammenhängt. Dies deutet darauf hin, dass die Preise normalisiert werden, wenn die Märkte nicht extrem sind und von der Erschütterung geprägt sind, und wenn die Märkte extrem sind, wie z. B. wenn eine große Anzahl von Überschreibungen stattfindet, wenn sie aus dem Trend gehen. Selbst wenn diese niedrigen Wahrscheinlichkeitsszenarien nicht berücksichtigt werden, sinkt die einfache Hypothese, dass der Trend und VI eine negative lineare Beziehung erfüllen, die Prognosefehlerdifferenz zum mittleren Preis erheblich.

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

Gesamtmittelpreis

Angesichts der Tatsache, dass sowohl die Anzahl der Aufträge als auch die Transaktionsdaten hilfreich für die Vorhersage von Mittelkosten sind, können die beiden Parameter zusammengefasst werden, wobei die Gewichtung hier eher willkürlich ist und keine Grenzbedingungen berücksichtigt werden. In extremen Fällen kann der vorhergesagte Mittelkurs nicht zwischen Kauf und Verkauf liegen, aber solange der Fehler reduziert werden kann, sind diese Details egal.

Der Fehler bei der letzten Prognose sank von 0.00487 auf 0.0043, und hier geht es nicht weiter. Der Zwischenpreis kann noch viel abgebaut werden, schließlich ist der Zwischenpreis bei der Prognose, und jeder kann es selbst versuchen.

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

Zusammenfassung

Dieser Artikel kombiniert tiefgreifende Daten und Transaktionsdaten, verbessert die Berechnungsmethode für die Zwischenpreise weiter, gibt eine Methode zur Messung der Genauigkeit an und verbessert die Genauigkeit der Prognose von Preisänderungen. Im Allgemeinen sind die verschiedenen Parameter nicht so streng, nur zur Referenz.


Mehr

mztcoinIch bin zu viel auf Gras gespannt und freue mich auf die nächste Aktualisierung.

LouisWer weiß?

- Ich weiß nicht.Und die hohen und die niedrigen, nur zu bewundern.