В этом посте будет описано, как обрабатывать и хранить высокочастотные финансовые данные для поддержки вашего алгоритма машинного обучения на основе статистических свойств данных.

Пост напрямую основан на содержании из книги «Достижения в области финансового машинного обучения» Маркоса Лопеса де Прадо.

Физическое значение:

Высокочастотные финансовые данные объемны и нуждаются в данных. Например. Финансовые данные с высокой периодичностью (тик за тиком или сделка за сделкой) за 20 дней составляют 5 миллионов записей (у Excel есть внутренний лимит в 1 миллион записей) и составляют файл размером 300 МБ.

Если данные не будут обобщены, мы быстро столкнемся с ограничениями обработки данных, поскольку мы обрабатываем данные за месяцы/годы.

Данные, которые появляются на финансовых графиках, на популярных веб-сайтах, являются такими обобщенными данными. Визуально мы не теряем ничего существенного, работая с такими обобщенными данными, хотя есть некоторые последствия, когда мы работаем над тестированием (обратным тестированием) высокочастотных торговых алгоритмов.

Описание алгоритма:

Основная идея состоит в том, чтобы нарезать необработанные высокочастотные данные на срезы на основе согласованного правила. Как только мы подготовили срез, мы суммируем его, используя статистику. Распространенной статистикой, используемой многими, является OHLC [цена открытия, высокая цена, низкая цена, цена закрытия]. В частности, в только что взятом срезе, какова цена, с которой начался срез, какова была самая высокая цена, достигнутая в срезе, и т. д.

Затем эти сводные статистические данные сохраняются и составляют основу для обучения модели машинного обучения.

1.1.1 Временные полосы: мы берем срезы с постоянной частотой, т.е. каждые 10 минут

1.1.2 Тиковые бары: мы берем срезы после того, как прошло постоянное количество сделок, т.е. каждые 10 000 сделок

1.1.3 Бары объема: мы берем срезы после обмена постоянным торговым объемом, например. каждые 10 000 акций

1.1.4 Долларовые слитки: мы берем кусочки после обмена постоянной финансовой суммы, например, каждые 10 000 долларов акций

Исходный временной ряд имел 5 миллионов записей. Выбранный ряд содержит всего 800 записей, но мы практически не потеряли дискриминацию. Выбранный ряд показывает графики по тиковым барам, барам объема и долларовым барам, которые немного отличаются, но примерно совпадают.

Ежедневное количество баров остается примерно одинаковым для разных типов баров.

Ряд возвратов, сформированный из баров, остается одинаковым для всех типов баров. Всплеск возвратов связан с переносом контракта

Мы можем рассчитать степень нормальности доходности, используя тест нормальности Жака-Бера, который может быть реализован прямым образом с использованием функциональных возможностей модуля scipy. Если p-значение результата меньше 0,05, мы можем сделать вывод, что распределение нашей тестовой переменной отличается от нормального распределения. Значение p также можно использовать для ранжирования двух разных переменных с точки зрения степени ненормальности. Объяснение можно найти в этом отличном сообщении в блоге: https://www.statology.org/jarque-bera-test-python/

Мы можем рассчитать степень последовательной корреляции, которая представляет собой корреляцию переменной с ее запаздывающими версиями. Это можно легко реализовать с помощью функциональности модуля statsmodel, граница уровня значимости 5% уже нанесена на график, поэтому необходимо учитывать только значения корреляции за пределами этой границы. Объяснение можно найти в этом отличном сообщении в блоге: https://machinelearningmastery.com/gentle-introduction-autocorrelation-partial-autocorrelation/

Код Python:

Есть ли какие-нибудь советы по кодированию для улучшения скорости/выполнения алгоритма? Дай мне знать в комментариях…..

def thresh(df, freq):
def thresh(df, freq):
    tick_thresh = pd.DataFrame(pd.pivot_table(df,index='Date',values='Symbol',aggfunc='count'))
    volume_thresh = pd.DataFrame(pd.pivot_table(df,index='Date',values='Volume',aggfunc='sum'))
    dollar_thresh = pd.DataFrame(pd.pivot_table(df,index='Date',values='Dollar',aggfunc='sum'))
    tick_thresh = np.round((1/freq)*np.average(tick_thresh['Symbol']))
    volume_thresh = np.round((1/freq)*np.average(volume_thresh['Volume']))
    dollar_thresh = np.round((1/freq)*np.average(dollar_thresh['Dollar']))
    return tick_thresh, volume_thresh, dollar_thresh

def bar_count(tick_bar, volume_bar, dollar_bar):
    tick_temp = pd.DataFrame(pd.pivot_table(tick_bar, index='Date', values='Open', aggfunc='count'))
    volume_temp = pd.DataFrame(pd.pivot_table(volume_bar, index='Date', values='Open', aggfunc='count'))
    dollar_temp = pd.DataFrame(pd.pivot_table(dollar_bar, index='Date', values='Open', aggfunc='count'))
    return tick_temp, volume_temp, dollar_temp

def return_series(tick_bar, volume_bar, dollar_bar):
    tick_bar['Return'] = tick_bar['Close'].diff()/tick_bar['Close'].shift(1)
    volume_bar['Return'] = volume_bar['Close'].diff()/volume_bar['Close'].shift(1)
    dollar_bar['Return'] = dollar_bar['Close'].diff()/dollar_bar['Close'].shift(1)
    return tick_bar, volume_bar, dollar_bar

def bar_gen(df, tick_thresh, volume_thresh, dollar_thresh):
    count, cum_vol_tick, vol_tick, vwap, open, low, high, close, vol, vol_volume, cum_vol_volume, dol, vol_dollar, cum_vol_dollar = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    buffer_tick, set_tick, buffer_volume, set_volume,buffer_dollar, set_dollar = [], [], [], [], [], []
    for i, (price, volume, dollar, date) in enumerate(zip(df['Price'], df['Volume'], df['Dollar'], df['Date'])):

        count = count + 1
        vol = vol + volume
        dol = dol + dollar
        buffer_tick.append(price)
        buffer_volume.append(price)
        buffer_dollar.append(price)
        vol_tick = vol_tick + (volume*price)
        vol_volume = vol_volume + (volume*price)
        vol_dollar = vol_dollar + (volume*price)
        cum_vol_tick = cum_vol_tick + volume
        cum_vol_volume = cum_vol_volume + volume
        cum_vol_dollar = cum_vol_dollar + volume

        if count >= tick_thresh:
            open = buffer_tick[0]
            low = np.min(buffer_tick)
            high = np.max(buffer_tick)
            close = buffer_tick[-1]
            vwap = vol_tick/cum_vol_tick
            set_tick.append((date, i, open, low, high, close, vwap))
            count, cum_vol_tick, vol_tick, vwap, open, low, high, close = 0, 0, 0, 0, 0, 0, 0, 0
            buffer_tick = []

        if vol >= volume_thresh:
            open = buffer_volume[0]
            low = np.min(buffer_volume)
            high = np.max(buffer_volume)
            close = buffer_volume[-1]
            vwap = vol_volume / cum_vol_volume
            set_volume.append((date, i, open, low, high, close, vwap))
            cum_vol_volume, vol_volume, vwap, open, low, high, close, vol = 0, 0, 0, 0, 0, 0, 0, 0
            buffer_volume = []

        if dol >= dollar_thresh:
            open = buffer_dollar[0]
            low = np.min(buffer_dollar)
            high = np.max(buffer_dollar)
            close = buffer_dollar[-1]
            vwap = vol_dollar / cum_vol_dollar
            set_dollar.append((date, i, open, low, high, close, vwap))
            cum_vol_dollar, vol_dollar, vwap, open, low, high, close, dol = 0, 0, 0, 0, 0, 0, 0, 0
            buffer_dollar = []

    cols = ['Date', 'Index', 'Open', 'Low', 'High', 'Close', 'VWAP']
    tick_bar = pd.DataFrame(set_tick, columns=cols)
    volume_bar = pd.DataFrame(set_volume, columns=cols)
    dollar_bar = pd.DataFrame(set_dollar, columns=cols)
    return tick_bar, volume_bar, dollar_bar


if __name__ == '__main__':
    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    from statsmodels.graphics.tsaplots import plot_acf
    from statsmodels.graphics.tsaplots import plot_pacf
    import seaborn as sns
    import scipy.stats as stats

    # Load data
    df = pd.read_csv(r'C:\Users\josde\OneDrive\Denny\Deep-learning\Data-sets\Trade-data\ES_Trades.csv')
    df = df.iloc[:,0:5]
    df['Dollar'] = df['Price'] * df['Volume']

    # Determine thresholds
    tick_thresh, volume_thresh, dollar_thresh = thresh(df,freq=50)

    # Form bars
    tick_bar, volume_bar, dollar_bar = bar_gen(df, tick_thresh, volume_thresh, dollar_thresh)
    plt.figure(1)
    plt.plot(df['Price'],'r')
    plt.figure(2)
    plt.plot(tick_bar['VWAP'], 'r')
    plt.plot(volume_bar['VWAP'], 'g')
    plt.plot(dollar_bar['VWAP'], 'b')
    plt.show()

    # Bar counts
    tick_temp, volume_temp, dollar_temp = bar_count(tick_bar, volume_bar, dollar_bar)
    plt.figure(3)
    plt.plot(tick_temp['Open'], 'r')
    plt.plot(volume_temp['Open'], 'g')
    plt.plot(dollar_temp['Open'], 'b')
    plt.show()

    # Bar returns
    tick_bar, volume_bar, dollar_bar = return_series(tick_bar, volume_bar, dollar_bar)
    tick_stats = stats.jarque_bera(tick_bar['Return'])
    volume_stats = stats.jarque_bera(volume_bar['Return'])
    dollar_stats = stats.jarque_bera(dollar_bar['Return'])
    raw_stats = stats.jarque_bera(df['Price'])
    print(raw_stats, tick_stats, volume_stats, dollar_stats)

    plt.figure(4)
    plt.plot(tick_bar['Return'], 'r')
    plt.plot(volume_bar['Return'], 'g')
    plt.plot(dollar_bar['Return'], 'b')
    plt.show()
    plt.figure(5)
    sns.histplot(tick_bar['Return'],legend=True, color='r')
    sns.histplot(volume_bar['Return'],legend=True, color='g')
    sns.histplot(dollar_bar['Return'],legend=True, color='b')
    plt.show()

    #plot_acf(dollar_bar['Close'])
    plot_acf(df['Price'])

    #plot_pacf(dollar_bar['Close'])
    plot_pacf(df['Price'])
    plt.show()