Создание мощной стратегии на основе SuperTrend и RSI.

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

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



RSI

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

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

Для расчета индекса относительной силы нам понадобится массив OHLC (а не фрейм данных). Это означает, что мы будем смотреть на массив из 4 столбцов. Таким образом, функция индекса относительной силы:

def adder(Data, times):
    
    for i in range(1, times + 1):
    
        new = np.zeros((len(Data), 1), dtype = float)
        Data = np.append(Data, new, axis = 1)
    return Data
def deleter(Data, index, times):
    
    for i in range(1, times + 1):
    
        Data = np.delete(Data, index, axis = 1)
    return Data
   
def jump(Data, jump):
    
    Data = Data[jump:, ]
    
    return Data
def ma(Data, lookback, close, where): 
    
    Data = adder(Data, 1)
    
    for i in range(len(Data)):
           
            try:
                Data[i, where] = (Data[i - lookback + 1:i + 1, close].mean())
            
            except IndexError:
                pass
            
    # Cleaning
    Data = jump(Data, lookback)
    
    return Data
def ema(Data, alpha, lookback, what, where):
    
    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 rsi(Data, lookback, close, where, width = 1, genre = 'Smoothed'):
    
    # Adding a few columns
    Data = adder(Data, 7)
    
    # Calculating Differences
    for i in range(len(Data)):
        
        Data[i, where] = Data[i, close] - Data[i - width, close]
     
    # Calculating the Up and Down absolute values
    for i in range(len(Data)):
        
        if Data[i, where] > 0:
            
            Data[i, where + 1] = Data[i, where]
            
        elif Data[i, where] < 0:
            
            Data[i, where + 2] = abs(Data[i, where])
            
    # Calculating the Smoothed Moving Average on Up and Down
    absolute values    
    if genre == 'Smoothed':                                
      lookback = (lookback * 2) - 1 # From exponential to smoothed
      Data = ema(Data, 2, lookback, where + 1, where + 3)
      Data = ema(Data, 2, lookback, where + 2, where + 4)
    
    if genre == 'Simple':                                      
      Data = ma(Data, lookback, where + 1, where + 3)
      Data = ma(Data, lookback, where + 2, where + 4)
    
    # Calculating the Relative Strength
    Data[:, where + 5] = Data[:, where + 3] / Data[:, where + 4]
    
    # Calculate the Relative Strength Index
    Data[:, where + 6] = (100 - (100 / (1 + Data[:, where + 5])))  
    
    # Cleaning
    Data = deleter(Data, where, 6)
    Data = jump(Data, lookback)
    return Data

Если вы хотите поддержать меня и статьи, которые я регулярно публикую, рассмотрите возможность подписки на мой информационный бюллетень (доступен бесплатный план) по приведенной ниже ссылке. Это поможет мне продолжать делиться своими исследованиями. Спасибо!



Индикатор SuperTrend

Первая концепция, которую мы должны понять перед созданием индикатора SuperTrend, - это волатильность. Иногда мы измеряем волатильность, используя средний истинный диапазон. Хотя ATR считается запаздывающим индикатором, он дает некоторое представление о том, где сейчас волатильность и где она была в последний период (день, неделя, месяц и т. Д.). Но перед этим мы должны понять, как рассчитывается Истинный диапазон (ATR - это просто среднее значение этого расчета).

Истинный диапазон - это просто наибольшее из трех ценовых различий:

  • Высокая - Низкая
  • | High - Предыдущее закрытие |
  • | Предыдущее закрытие - минимум |

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

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

def adder(Data, times):
    
    for i in range(1, times + 1):
    
        new = np.zeros((len(Data), 1), dtype = float)
        Data = np.append(Data, new, axis = 1)
    return Data
def deleter(Data, index, times):
    
    for i in range(1, times + 1):
    
        Data = np.delete(Data, index, axis = 1)
    return Data
   
def jump(Data, jump):
    
    Data = Data[jump:, ]
    
    return Data
def ma(Data, lookback, close, where): 
    
    Data = adder(Data, 1)
    
    for i in range(len(Data)):
           
            try:
                Data[i, where] = (Data[i - lookback + 1:i + 1, close].mean())
            
            except IndexError:
                pass
            
    # Cleaning
    Data = jump(Data, lookback)
    
    return Data
def ema(Data, alpha, lookback, what, where):
    
    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 atr(Data, lookback, high, low, close, where, genre = 'Smoothed'):
    
    # Adding the required columns
    Data = adder(Data, 1)
    
    # True Range Calculation
    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   
    
    if genre == 'Smoothed':
        
        # Average True Range Calculation
        Data = ema(Data, 2, lookback, where, where + 1)
    
    if genre == 'Simple':
    
        # Average True Range Calculation
        Data = ma(Data, lookback, where, where + 1)
    
    # Cleaning
    Data = deleter(Data, where, 1)
    Data = jump(Data, lookback)
    return Data

Теперь, когда мы поняли, что такое ATR и как его рассчитать, мы можем продолжить работу с индикатором SuperTrend. Индикатор стремится предоставить уровни входа и выхода для последователей тренда. Вы можете думать об этом как о скользящей средней или MACD. Его уникальность - это его главное преимущество, и хотя метод расчета намного сложнее, чем два других индикатора, он интуитивно понятен и не так сложен для понимания. По сути, у нас есть две переменные на выбор.

Обратный прогноз ATR и значение множителя. Первый - это просто период, используемый для расчета ATR, в то время как последний обычно является целым числом (обычно 2 или 3).

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

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

def supertrend(Data, multiplier, atr_col, close, high, low, where):
    
    Data = adder(Data, 6)
    
    for i in range(len(Data)):
        
            # Average Price
            Data[i, where] = (Data[i, high] + Data[i, low]) / 2
            # Basic Upper Band
            Data[i, where + 1] = Data[i, where] + (multiplier * Data[i, atr_col])
            # Lower Upper Band
            Data[i, where + 2] = Data[i, where] - (multiplier * Data[i, atr_col])
    
    # Final Upper Band
    for i in range(len(Data)):
        
        if i == 0:
            Data[i, where + 3] = 0
            
        else:  
            if (Data[i, where + 1] < Data[i - 1, where + 3]) or (Data[i - 1, close] > Data[i - 1, where + 3]):
                Data[i, where + 3] = Data[i, where + 1]
            
            else:
                Data[i, where + 3] = Data[i - 1, where + 3]
    
    # Final Lower Band
    for i in range(len(Data)):
        
        if i == 0:
            Data[i, where + 4] = 0
            
        else:  
            if (Data[i, where + 2] > Data[i - 1, where + 4]) or (Data[i - 1, close] < Data[i - 1, where + 4]):
                Data[i, where + 4] = Data[i, where + 2]
            
            else:
                Data[i, where + 4] = Data[i - 1, where + 4]
      
    # SuperTrend
    for i in range(len(Data)):
        
        if i == 0:
            Data[i, where + 5] = 0
        
        elif (Data[i - 1, where + 5] == Data[i - 1, where + 3]) and (Data[i, close] <= Data[i, where + 3]):
            Data[i, where + 5] = Data[i, where + 3]
        
        elif (Data[i - 1, where + 5] == Data[i - 1, where + 3]) and (Data[i, close] >  Data[i, where + 3]):
            Data[i, where + 5] = Data[i, where + 4]
        
        elif (Data[i - 1, where + 5] == Data[i - 1, where + 4]) and (Data[i, close] >= Data[i, where + 4]):
            Data[i, where + 5] = Data[i, where + 4]
        
        elif (Data[i - 1, where + 5] == Data[i - 1, where + 4]) and (Data[i, close] <  Data[i, where + 4]):
            Data[i, where + 5] = Data[i, where + 3]   
            
    # Cleaning columns
    Data = deleter(Data, where, 5)        
    
    return Data

На приведенном выше графике показаны почасовые значения EURUSD с 10-периодным супертрендом (представлен периодом ATR) и множителем 1,25.

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

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



Создание и оценка стратегии

Теперь наша задача - создать массив, содержащий данные OHLC, а также столбцы для RSI и SuperTrend. Мы можем сделать это, следуя этому коду, если мы импортировали необходимые исторические данные.

# Indicator Parameters
lookback_rsi        = 13
lookback_supertrend = 10
multiplier          = 3
# Adding a few columns
my_data = adder(my_data, 10)
# Calling the RSI function
my_data = rsi(my_data, lookback_rsi, 3, 4)
# Calling the ATR function to be used in the SuperTrend
my_data = atr(my_data, lookback_supertrend, 1, 2, 3, 5, genre = 'Smoothed')
# Calling the SuperTrend Indicator
my_data = supertrend(my_data, multiplier, 5, 3, 1, 2, 6)

Стратегия основана на переходе от SuperTrend с условием, что RSI выше его уровня нейтральности 50%. Это усиливает бычий уклон и наоборот медвежий уклон.

  • Сигнал на покупку (длинную позицию) генерируется всякий раз, когда индикатор SuperTrend опускается ниже рыночной цены, в то время как RSI превышает 50%.
  • Сигнал на продажу (короткую продажу) генерируется всякий раз, когда индикатор SuperTrend поднимается выше рыночной цены, а RSI ниже 50%.

def signal(Data, close, rsi_column, supertrend_column, buy, sell):
    
    Data = adder(Data, 10)
        
    for i in range(len(Data)):
            
        if Data[i, rsi_column] > 50 and   Data[i, close] > Data[i, supertrend_column] and Data[i - 1, close] < Data[i - 1, supertrend_column]:
               
           Data[i, buy] = 1
            
        elif Data[i, rsi_column] < 50 and Data[i, close] < Data[i, supertrend_column] and Data[i - 1, close] > Data[i - 1, supertrend_column]:
               
               
           Data[i, sell] = -1
            
    return Data

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

Заключение

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

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

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