Создание и кодирование противоположной торговой стратегии с нуля.

Противоположные стратегии связаны с выбором времени на рынке и стремятся найти экстремальные условия, чтобы позиция в обратном направлении стала прибыльной по мере корректировки рынка. В этой статье обсуждается простая стратегия, основанная на разностном методе одного известного индикатора импульса, называемого индексом относительной силы, который мы много раз обсуждали в предыдущих статьях. Затем мы протестируем стратегию на исторических данных и посмотрим, как ее оптимизировать.

Я только что опубликовал новую книгу после успеха Новые технические индикаторы в Python. Он содержит более полное описание и добавление сложных торговых стратегий со страницей Github, посвященной постоянно обновляемому коду. Если вы считаете, что это вас заинтересует, не стесняйтесь перейти по приведенной ниже ссылке или если вы предпочитаете купить версию в формате PDF, вы можете связаться со мной в Linkedin.

Я только что опубликовал новую книгу после успеха Новые технические индикаторы в Python. Он содержит более полное описание и добавление сложных торговых стратегий со страницей Github, посвященной постоянно обновляемому коду. Если вы считаете, что это вас заинтересует, не стесняйтесь перейти по приведенной ниже ссылке или если вы предпочитаете купить версию в формате PDF, вы можете связаться со мной в Linkedin.



Индекс относительной силы

RSI, без сомнения, является самым известным индикатором импульса, и этого следовало ожидать, поскольку у него много сильных сторон, особенно на ранжированных рынках. Он также ограничен от 0 до 100, что упрощает интерпретацию. Кроме того, тот факт, что он известен, увеличивает его потенциал.

Это связано с тем, что чем больше трейдеров и управляющих портфелем смотрят на RSI, тем больше людей будет реагировать на его сигналы, а это, в свою очередь, может подтолкнуть рыночные цены. Конечно, мы не можем доказать эту идею, но она интуитивно понятна, поскольку одна из основ технического анализа заключается в том, что она самореализируется.

RSI рассчитывается довольно простым способом. Сначала мы берем разницу в ценах за один период. Это означает, что мы должны вычесть каждую цену закрытия из предыдущей. Затем мы вычислим сглаженное среднее положительных разностей и разделим его на сглаженное среднее отрицательных разностей. Последний расчет дает нам относительную силу, которая затем используется в формуле RSI для преобразования в меру от 0 до 100.

def rsi(Data, rsi_lookback, what1, what2):
    
    rsi_lookback = (rsi_lookback * 2) - 1 # From exponential to smoothed
          
    # Get the difference in price from previous step
    delta = []
   
    for i in range(len(Data)):
        try:
            diff = Data[i, what1] - Data[i - 1, what1] 
            delta = np.append(delta, diff)                  
        except IndexError:
            pass
        
    delta = np.insert(delta, 0, 0, axis = 0)               
    delta = delta[1:] 
    
    # Make the positive gains (up) and negative gains (down) Series
    up, down = delta.copy(), delta.copy()
    up[up < 0] = 0
    down[down > 0] = 0
    
    up = np.array(up)
    down = np.array(down)
    
    roll_up = up
    roll_down = down
    
    roll_up = np.reshape(roll_up, (-1, 1))
    roll_down = np.reshape(roll_down, (-1, 1))
    
    roll_up = adder(roll_up, 3)
    roll_down = adder(roll_down, 3)
    
    roll_up = ema(roll_up, 2, rsi_lookback, what2, 1)
    roll_down = ema(abs(roll_down), 2, rsi_lookback, what2, 1)
    roll_up = roll_up[rsi_lookback:, 1:2]
    roll_down = roll_down[rsi_lookback:, 1:2]
    Data = Data[rsi_lookback + 1:,]
    RS = roll_up / roll_down
    RSI = (100.0 - (100.0 / (1.0 + RS)))
    RSI = np.array(RSI)
    RSI = np.reshape(RSI, (-1, 1))
    RSI = RSI[1:,]
    
    Data = np.concatenate((Data, RSI), axis = 1)            
    return Data
# Using the RSI function on OHLC numpy array
my_data = rsi(my_data, lookback, 3, 0)

Ускоритель импульса

Первый шаг к созданию этого индикатора - подумать с точки зрения различий в расчетах импульса. Вот где вступает RSI. Идея состоит в том, чтобы рассчитать медленный RSI и быстрый, а затем взять разницу между ними. Первый шаг - импортировать данные OHLC. Рассмотрим файл Excel с именем my_data, который содержит четыре столбца OHLC. Мы можем продолжить, создав ускоритель Momentum Accelerator.

  • Импортируйте файл excel OHLC в интерпретатор Python:
# Importing the required libraries
import pandas as pd
import numpy as np
# Importing the excel file (not csv)
my_data = pd.read_excel('my_data.xlsx')
# Transforming to array
my_data = np.array(my_data)
  • Выберите периоды ретроспективного анализа и рассчитайте два индекса RSI:
# Selecting the lookback periods
lookback_fast = 5
lookback_slow = 14
# Calling the RSI function as seen in the first part of the article
my_data= rsi(my_data, lookback_fast, 3, 0)
my_data= rsi(my_data, lookback_slow, 3, 0)
  • Рассчитайте ускоритель моментума, сравнив медленный и быстрый RSI:
# Adding a few columns using the adder function
def adder(Data, times):
    
    for i in range(1, times + 1):
    
        z = np.zeros((len(Data), 1), dtype = float)
        Data = np.append(Data, z, axis = 1)
return Data
my_data = adder(my_data, 20)
# Calculating the Momentum Accelerator
my_data[:, 6] = my_data[:, 4] - my_data[:, 5]
  • Постройте ускоритель моментума рядом с рыночной ценой и выберите барьеры:
# Defining the plotting function
def indicator_plot_double(Data, first, second, name = '', name_ind = '', window = 250):
fig, ax = plt.subplots(2, figsize = (10, 5))
Chosen = Data[-window:, ]
    
    for i in range(len(Chosen)):
        
        ax[0].vlines(x = i, ymin = Chosen[i, 2], ymax = Chosen[i, 1], color = 'black', linewidth = 1)  
        ax[0].vlines(x = i, ymin = Chosen[i, 2], ymax = Chosen[i, 1], color = 'black', linewidth = 1)
   
    ax[0].grid() 
     
    ax[1].plot(Data[-window:, second], color = 'blue')
    ax[1].grid()
    ax[1].legend()
# Choosing the barriers (Oversold/Overbought levels)
upper_barrier = 20
lower_barrier = -20
# Calling the function
indicator_plot_double(my_data, 3, 6, name = assets[0], name_ind = 'Momentum Accelerator', window = 500)    
plt.axhline(y = upper_barrier, color = 'black', linewidth = 1, linestyle = '--')
plt.axhline(y = lower_barrier, color = 'black', linewidth = 1, linestyle = '--')

Теперь следующий шаг - посмотреть, может ли разница импульсов дать нам хорошие сигналы или нет. Мы можем сформировать следующие условия:

  • Открывайте длинную позицию (Покупайте), когда Momentum Accelerator достигает -20 с двумя предыдущими значениями выше -20. Удерживайте эту позицию до получения нового сигнала или до тех пор, пока алгоритм управления рисками не остановит вас.
  • Открывайте короткую позицию (продавайте), когда Momentum Accelerator достигает 20 при двух предыдущих значениях ниже 20. Удерживайте эту позицию до тех пор, пока не получите новый сигнал или не остановитесь алгоритмом управления рисками.
def signal(Data, what, buy, sell):
    
    for i in range(len(Data)):
            
        if Data[i, what] < lower_barrier and Data[i - 1, what] > lower_barrier and Data[i - 2, what] > lower_barrier :
            Data[i, buy] = 1
            
        if Data[i, what] > upper_barrier and Data[i - 1, what] < upper_barrier and Data[i - 2, what] < upper_barrier :
            Data[i, sell] = -1
# Calling the signal function
signal(my_data, 6, 7, 8)

Вышеупомянутая сигнальная функция вводит 1 в столбец с индексом 7 всякий раз, когда у нас есть сигнал на покупку, и вводит -1 в столбец с индексом 8 всякий раз, когда у нас есть сигнал на продажу. Это можно использовать позже в функции прибыли / убытка, которая перебирает данные и вычисляет разницу между ценами закрытия.

Пример сигнальной функции на почасовых данных GBPUSD должен дать следующее, учитывая стратегию барьеров -20/20.

Запуск стратегии на 10 основных валютных парах с гипотетическим спредом 0,5 пункта и теоретическим соотношением риска и прибыли 0,25 дает следующие результаты. Данные ежечасно с января 2010 года.

Если вас также интересуют другие технические индикаторы и использование Python для создания стратегий, то моя книга-бестселлер по техническим индикаторам может вас заинтересовать:



Попытка оптимизации

Как правило, когда мы тестируем стратегию на исторических данных, мы не можем найти из нее оптимальный ожидаемый результат, и именно здесь на помощь приходит оптимизация. Ключ состоит в том, чтобы найти баланс между переоснащением и недостаточным соответствием, и хотя эти два термина в значительной степени рассматриваются в машинном обучении в нашем случае мы просто постараемся не настраивать слишком много параметров, из-за которых одна пара активов заметно превосходит другие. Возьмем для примера пару EURUSD. Параметры по умолчанию, протестированные выше, были:

  • Быстрый RSI: 5 периодов
  • Медленный RSI: 14 периодов
  • Барьеры: -20/20

Это дало нам следующую не очень кривую капитала с 2010 года:

Первое, что здесь видно, это то, что кривая кажется случайной (недетерминированной), а сигналы довольно редки. Мы хотим усилить сигналы. Это может быть сделано за счет уменьшения периодов ретроспективного анализа двух RSI, а также барьеров, так что частота сигнала увеличивается. Следовательно, у нас могут быть следующие условия:

  • Быстрый RSI: 4 периода
  • Медленный RSI: 9 периодов
  • Барьеры: -10/10

Это дает нам следующую кривую капитала:

Огромное улучшение по сравнению с первым и несколькими сгенерированными сигналами. Давайте теперь посмотрим, что произойдет, когда мы применим новые условия к остальным валютным парам.

Результаты значительно улучшились за счет оптимизации.

Слово об управлении рисками

Когда я говорю, что использую систему управления рисками на основе ATR (средний истинный диапазон), это означает, что алгоритм будет выполнять следующие шаги в отношении занимаемой позиции.

Длинная позиция (покупка):

  • Алгоритм инициирует ордер на покупку после того, как сгенерирован сигнал по определенной стратегии.
  • Затем алгоритм будет отслеживать тики, и всякий раз, когда максимум равен определенной константе, умноженной на значение ATR во время открытия сделки, инициируется ордер выхода (с прибылью). Одновременно, если виден минимум, равный определенной константе, умноженной на значение ATR на момент открытия сделки, инициируется выход (с убытком). Первым встреченный выход, естественно, является принятым событием.

Короткая (продажа) позиция:

  • Алгоритм инициирует короткий ордер на продажу после того, как был сгенерирован сигнал в соответствии с определенной стратегией.
  • Затем алгоритм будет отслеживать тики, и всякий раз, когда минимум равен определенной константе, умноженной на значение ATR во время открытия сделки, инициируется ордер выхода (с прибылью). Одновременно, если виден максимум, равный определенной константе, умноженной на значение ATR на момент открытия сделки, инициируется выход (с убытком). Первым встреченный выход, естественно, является принятым событием.

График выше показывает средний истинный диапазон, который я обычно использую. Он основан на экспоненциальной скользящей средней, а не на исходной сглаженной скользящей средней.

Взгляните на последнее значение ATR. Это около 0,0014 (14 пунктов). Если мы инициируем ордер на покупку, следуя простому соотношению риска и прибыли 2,00 (рискуя половиной того, что мы ожидаем получить), мы можем разместить ордер следующим образом:

  • Купить по текущей рыночной цене.
  • Тейк-профит по текущей рыночной цене + (2 x 14 пунктов).
  • Остановите позицию по текущей рыночной цене - (1 x 14 пунктов).

Код, который я использую для индикатора среднего истинного диапазона, выглядит следующим образом:

def ma(Data, lookback, what, where):
    
    for i in range(len(Data)):
      try:
        Data[i, where] = (Data[i - lookback + 1:i + 1, what].mean())
        
            except IndexError:
                pass
    return Data
def ema(Data, alpha, lookback, what, where):
    
    # alpha is the smoothing factor
    # window is the lookback period
    # what is the column that needs to have its average calculated
    # where is where to put the exponential moving average
    
    alpha = alpha / (lookback + 1.0)
    beta  = 1 - alpha
    
    # First value is a simple SMA
    Data = ma(Data, lookback, what, where)
    
    # Calculating first EMA
    Data[lookback + 1, where] = (Data[lookback + 1, what] * alpha) + (Data[lookback, where] * beta)
    # Calculating the rest of EMA
    for i in range(lookback + 2, len(Data)):
            try:
                Data[i, where] = (Data[i, what] * alpha) + (Data[i - 1, where] * beta)
        
            except IndexError:
                pass
    return Data
def eATR(Data, lookback, high, low, close, where):
    
    # TR
    for i in range(len(Data)):
        try:
            
            Data[i, where] = max(Data[i, high] - Data[i, low],
                               abs(Data[i, high] - Data[i - 1, close]),
                               abs(Data[i, low] - Data[i - 1, close]))
            
        except ValueError:
            pass
        
    Data[0, where] = 0    
    Data = ema(Data, 2, lookback, where, where + 1)
return Data

Заключение

Если вы регулярно следите за моими статьями, вы обнаружите, что многие индикаторы, которые я разрабатываю или оптимизирую, имеют высокий коэффициент попадания и в среднем являются прибыльными. В основном это связано с тем, что я использую метод управления рисками. Но как насчет рыночной случайности и того факта, что многие отстающие винят технический анализ в своей неудаче?

Прежде всего, я постоянно публикую свои торговые журналы в Твиттере до и после запуска, чтобы показать результаты. Это обеспечивает прозрачность. Я также публикую в Twitter каждые 1–3 месяца. Однако я никогда не гарантирую ни возврата ни превосходного мастерства. Что касается индикаторов, которые я разрабатываю, я постоянно использую их в своей личной торговле. Следовательно, у меня нет мотива публиковать предвзятые исследования. Моя цель - поделиться тем, что я узнал от онлайн-сообщества.

Не забывайте всегда проводить тесты на исторических данных. Несмотря на то, что я раскрываю функцию индикатора (а не просто хвастаюсь им и говорю, что это Святой Грааль, а его функция - секрет), вы всегда должны верить, что другие люди ошибаются. Мои индикаторы и стиль торговли работают для меня, но, возможно, не для всех. Я полагаюсь на это правило:

Рыночную цену невозможно предсказать или ее очень трудно предсказать более чем в 50% случаев. Но реакцию рынка можно предсказать.

Приведенная выше цитата означает, что мы можем сформировать небольшую зону вокруг области и сказать с некоторой степенью уверенности, что рыночная цена покажет реакцию вокруг этой области. Но мы не можем сказать, что он упадет оттуда на 4%, затем снова протестирует его и пробьет с третьей попытки до 103,85 доллара. Параметр ошибки становится экспоненциально выше, потому что мы делаем прогнозы, а не прогнозы.

Пока мы обсуждаем эту тему, я должен отметить несколько моментов в моих тестах на истории и статьях:

  • Я использую спред, основанный на институциональных котировках небольшой доли пунктов. Как правило, розничным трейдерам предоставляется колоссальный спред в 1-2 пункта за сделку. Это огромно и несправедливо по отношению к ним. Я использую спред 0,2–0,5. Однако большинство стратегий, использующих часовой таймфрейм, по-прежнему работают со спредом в 1 пункт. Для тех, кто использует таймфреймы M15 или M5, они не могут быть прибыльными со спредом в 1 пункт.
  • Я использую расчет периода удержания, близкий к закрытому на случай отсутствия процесса управления рисками.
  • Хотя я не рекомендую торговать только по одному индикатору, цифры не лгут. Я представляю то, что могло произойти, если принять во внимание низкий спред.
  • Некоторые из представленных мною тестов на истории неудачны, и они публикуются либо для того, чтобы развенчать миф о торговле, либо для того, чтобы представить интересные функции, которые читатели могут запрограммировать.
  • Наконец, я твердо убежден, что нельзя кормить учащихся с ложечки. Я научился на практике, а не копируя. Вы должны понять идею, функцию, интуицию, условия стратегии, а затем разработать (даже лучше) одну из них самостоятельно, чтобы вы протестировали ее на исторических данных и улучшили, прежде чем принимать решение о том, чтобы применить ее вживую или отменить.

Подводя итог, можно ли сказать, что стратегии, которые я предлагаю, реалистичны? Да, но с оптимизацией среды (надежный алгоритм, низкие затраты, честный брокер, управление рисками). Предусмотрены ли стратегии исключительно для торговли? Нет, это нужно для стимулирования мозгового штурма и получения новых торговых идей, поскольку мы все устали слышать о перепроданности RSI как о причине для открытия короткой позиции или о преодолении сопротивления как о причине долго.