Научитесь реализовывать менее известный индикатор с большим потенциалом, чтобы ваши сделки выглядели лучше

Люди, которые занимаются торговлей акциями, наверняка знают о стохастическом осцилляторе, учитывая его популярность, и сегодня мы собираемся изучить индикатор, который не только похож, но и работает так же, как стохастический осциллятор. Это не что иное, как индекс относительной бодрости, известный как RVI. В этой статье мы сначала создадим некоторые основные интуитивные представления об индикаторе и математике, лежащей в его основе. Затем мы перейдем к программированию, в котором мы будем использовать Python для создания индикатора с нуля, тестирования торговой стратегии на его основе, сравнения результатов стратегии с результатами SPY ETF (ETF, специально разработанный для отслеживания движения индекса S&P 500. индекс рынка). Без лишних слов, давайте погрузимся в статью.

Индекс относительной бодрости (RVI)

Индекс относительной силы - это индикатор импульса, который действует как инструмент для определения текущего рыночного импульса, будь то восходящий или нисходящий. В отличие от стохастического осциллятора, индекс относительной силы является неограниченным осциллятором и не колеблется между определенными пороговыми значениями, а колеблется поперек центральной линии (в большинстве случаев нулевой). Индекс относительной бодрости состоит из двух компонентов: линии RVI и линии сигнала. Теперь давайте посмотрим, как рассчитывается каждый из компонентов.

Расчет линии RVI

Чтобы рассчитать показания линии RVI, мы должны сначала определить числитель и знаменатель.

Числитель можно вычислить за пять шагов:

  • Шаг первый - найти разницу между текущей ценой закрытия и текущей ценой открытия акции, и давайте рассмотрим разницу как «а».
  • Второй шаг - найти разницу между ценой закрытия два периода назад и ценой открытия два периода назад, и эта разница умножается на 2. Этот продукт можно рассматривать как «b».
  • Теперь третий шаг - определить разницу между ценой закрытия трех периодов назад и ценой открытия трех периодов, затем умноженную на два, и давайте рассмотрим это как «с».
  • Шаг четвертый - умножить разницу, полученную путем вычитания цены открытия четыре периода назад из цены закрытия четыре периода назад на два, и этот продукт можно принять как «d».
  • Последний шаг - сложить друг с другом определенные переменные a, b, c и d и вычислить скользящую сумму для заданного количества периодов (типовая настройка - 4). Расчет числителя можно представить следующим образом:
a = CURRENT CLOSE - CURRENT OPEN
b =  2 * ( CLOSE 2 PERIODS AGO - OPEN 2 PERIODS AGO )
c =  2 * ( CLOSE 3 PERIODS AGO - OPEN 3 PERIODS AGO )
d =  2 * ( CLOSE 4 PERIODS AGO - OPEN 4 PERIODS AGO )
Numerator = ROLLING SUM 4 [ a + b + c + d ]

Процедура вычисления знаменателя почти аналогична процедуре вычисления числителя, но нам просто нужно заменить данные о цене закрытия и данные о цене открытия на данные о максимальной и минимальной цене соответственно.

Знаменатель можно вычислить за пять шагов:

  • Шаг первый - найти разницу между текущей максимальной ценой и текущей низкой ценой акции, и давайте рассмотрим эту разницу как «е».
  • Второй шаг - найти разницу между высокой ценой два периода назад и низкой ценой два периода назад, и эта разница умножается на 2. Этот продукт можно рассматривать как «f».
  • Теперь третий шаг - определить разницу между высокой ценой три периода назад и низкой ценой три периода, затем умноженную на два, и давайте рассмотрим это как «g».
  • Шаг четвертый - умножить разницу, полученную путем вычитания низкой цены четыре периода назад из высокой цены четыре периода назад на два, и этот продукт можно принять как «h».
  • Последним шагом является сложение определенных переменных e, f, g и h друг к другу и скользящей суммы с 4 по мере вычисления периода ретроспективного анализа. Расчет знаменателя можно представить следующим образом:
e = CURRENT HIGH - CURRENT LOW
f =  2 * ( HIGH 2 PERIODS AGO - LOW 2 PERIODS AGO )
g =  2 * ( HIGH 3 PERIODS AGO - LOW 3 PERIODS AGO )
h =  2 * ( HIGH 4 PERIODS AGO - LOW 4 PERIODS AGO )
Denominator = ROLLING SUM 4 [ e + f + g + h ]

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

RVI LINE = SMA 10 [ NUMERATOR / DENOMINATOR ]

Расчет сигнальной линии

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

Расчет сигнальной линии состоит из четырех основных этапов: Первый шаг - умножить показания линии RVI за один период на два, и давайте рассмотрим это как «rvi1». Следующим шагом является умножение показаний линии RVI два периода назад на два, и это можно принять как «rvi2». Третий шаг - определить показания линии RVI три периода назад, и эти значения можно рассматривать как «rvi3». Последним шагом является сложение определенных «rvi1», «rvi2», «rvi3» вместе с показаниями линии RVI друг к другу, и общая сумма или результат делятся на 6. Расчет можно представить следующим образом. :

rvi1 = 2 * RVI LINE 1 PERIOD AGO
rvi2 = 2 * RVI LINE 2 PERIODS AGO
rvi3 = RVI LINE 3 PERIODS AGO
SIGNAL LINE = ( RVI LINE + rvi1 + rvi2 + rvi3 ) / 6

Вот и весь процесс расчета компонентов индекса относительной силы. Теперь давайте проанализируем график, на котором данные Apple о ценах на закрытие нанесены вместе с его индексом относительной силы, рассчитанным с 10 в качестве периода ретроспективного анализа, чтобы получить твердое представление об индикаторе и его использовании.

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

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

IF PREV.RLINE < PREV.SLINE AND CUR.RLINE > CUR.SLINE ==> BUY SIGNAL
IF PREV.RLINE > PREV.SLINE AND CUR.RLINE < CUR.SLINE ==> SELL SIGNAL

В большинстве случаев трейдеры используют индекс относительной силы вместе с другим техническим индикатором для построения торговой стратегии, но это не входит в объем данной статьи (рекомендуется попробовать). Вот и все! На этом мы завершаем нашу теоретическую часть по индексу относительной силы, и давайте перейдем к части программирования, где мы будем использовать Python, чтобы сначала построить индикатор с нуля, построить стратегию перекрестной торговли, протестировать стратегию на данных акций Apple и, наконец, сравнить результаты. с этим SPY ETF. Давай займемся кодированием! Прежде чем двигаться дальше, примечание об отказе от ответственности: единственная цель этой статьи - обучить людей, и ее следует рассматривать как информационный материал, а не как инвестиционный совет или что-то в этом роде.

Реализация на Python

Часть кодирования подразделяется на следующие этапы:

1. Importing Packages
2. Extracting Stock Data from Twelve Data
3. Relative Vigor Index Calculation
4. Creating the Crossover Trading Strategy
5. Plotting the Trading Lists
6. Creating our Position
7. Backtesting
8. SPY ETF Comparison

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

Шаг 1. Импорт пакетов

Импорт необходимых пакетов в среду Python - шаг, который нельзя пропустить. Основными пакетами будут Pandas для работы с данными, NumPy для работы с массивами и для сложных функций, Matplotlib для построения графиков и запросы на выполнение вызовов API. Вторичными пакетами будут Math для математических функций и Termcolor для настройки шрифта (необязательно).

Реализация Python:

# IMPORTING PACKAGES

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import requests
from termcolor import colored as cl
from math import floor

plt.style.use('fivethirtyeight')
plt.rcParams['figure.figsize'] = (20,10)

Теперь, когда мы импортировали все необходимые пакеты в наш python. Давайте возьмем исторические данные Apple с конечной точки API Twelve Data.

Шаг 2: извлечение данных из двенадцати данных

На этом этапе мы собираемся получить исторические данные об акциях Apple, используя конечную точку API, предоставленную twelvedata.com. Перед этим заметка на twelvedata.com: Twelve Data - один из ведущих поставщиков рыночных данных, имеющий огромное количество конечных точек API для всех типов рыночных данных. Очень легко взаимодействовать с API, предоставляемыми Twelve Data, и у него одна из лучших документации. Кроме того, убедитесь, что у вас есть учетная запись на twelvedata.com, только тогда вы сможете получить доступ к своему ключу API (жизненно важный элемент для извлечения данных с помощью API).

Реализация Python:

# EXTRACTING STOCK DATA

def get_historical_data(symbol, start_date):
    api_key = 'YOUR API KEY'
    api_url = f'https://api.twelvedata.com/time_series?symbol={symbol}&interval=1day&outputsize=5000&apikey={api_key}'
    raw_df = requests.get(api_url).json()
    df = pd.DataFrame(raw_df['values']).iloc[::-1].set_index('datetime').astype(float)
    df = df[df.index >= start_date]
    df.index = pd.to_datetime(df.index)
    return df

aapl = get_historical_data('AAPL', '2019-01-01')
aapl.tail()

Вывод:

Пояснение к коду: Первое, что мы сделали, - это определили функцию с именем «get_historical_data», которая принимает в качестве параметров символ акции («symbol») и дату начала исторических данных («start_date»). Внутри функции мы определяем ключ API и URL-адрес и сохраняем их в соответствующей переменной. Затем мы извлекаем исторические данные в формате JSON с помощью функции «get» и сохраняем их в переменной «raw_df». После выполнения некоторых процессов очистки и форматирования необработанных данных JSON мы возвращаем их в виде чистого фрейма данных Pandas. Наконец, мы вызываем созданную функцию, чтобы получить исторические данные Apple с начала 2019 года и сохранить их в переменной «aapl».

Шаг 3: Расчет индекса относительной бодрости

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

Реализация Python:

# RELATIVE VIGOR INDEX CALCULATION

def get_rvi(open, high, low, close, lookback):
    a = close - open
    b = 2 * (close.shift(2) - open.shift(2))
    c = 2 * (close.shift(3) - open.shift(3))
    d = close.shift(4) - open.shift(4)
    numerator = a + b + c + d
    
    e = high - low
    f = 2 * (high.shift(2) - low.shift(2))
    g = 2 * (high.shift(3) - low.shift(3))
    h = high.shift(4) - low.shift(4)
    denominator = e + f + g + h
    
    numerator_sum = numerator.rolling(4).sum()
    denominator_sum = denominator.rolling(4).sum()
    rvi = (numerator_sum / denominator_sum).rolling(lookback).mean()
    
    rvi1 = 2 * rvi.shift(1)
    rvi2 = 2 * rvi.shift(2)
    rvi3 = rvi.shift(3)
    rvi_signal = (rvi + rvi1 + rvi2 + rvi3) / 6
    
    return rvi, rvi_signal

aapl['rvi'], aapl['signal_line'] = get_rvi(aapl['open'], aapl['high'], aapl['low'], aapl['close'], 10)
aapl = aapl.dropna()
aapl = aapl[aapl.index >= '2020-01-01']
aapl.tail()

Вывод:

Пояснение к коду: мы сначала определяем функцию с именем 'get_rvi', которая принимает значение открытия ('open'), максимума ('high'), минимума ('low'), закрытия ('close') акции. ) ценовые данные и период ретроспективного анализа (ретроспективный анализ) в качестве параметров.

Внутри функции мы сначала вычисляем переменные, участвующие в вычислении числителя, то есть a, b, c и d. Эти переменные вычисляются по формулам, которые мы обсуждали ранее, и складываются друг с другом для определения числителя. Затем идет вычисление знаменателя, которое почти аналогично вычислению числителя, но мы просто заменяем определенные значения. Перед вычислением показаний линии RVI мы определяем скользящую сумму числителя и знаменателя с 4 в качестве периода ретроспективного анализа, а затем результаты подставляются в формулу линии RVI для получения показаний.

После этого мы вычисляем три обязательные переменные, которые являются rvi1, rvi2 и rvi3, которые мы обсуждали ранее, и подставляем в формулу сигнальной линии вместе с ранее рассчитанными значениями линии RVI, чтобы получить показания. Наконец, мы возвращаем и вызываем функцию показания индекса относительной энергии Apple в магазине с 10 в качестве периода ретроспективного анализа.

Шаг 4: Создание торговой стратегии

На этом этапе мы собираемся реализовать обсуждаемую торговую стратегию кроссовера Relative Vigor Index на Python.

Реализация Python:

# RELATIVE VIGOR INDEX STRATEGY

def implement_rvi_strategy(prices, rvi, signal_line):
    buy_price = []
    sell_price = []
    rvi_signal = []
    signal = 0
    
    for i in range(len(prices)):
        if rvi[i-1] < signal_line[i-1] and rvi[i] > signal_line[i]:
            if signal != 1:
                buy_price.append(prices[i])
                sell_price.append(np.nan)
                signal = 1
                rvi_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                rvi_signal.append(0)
        elif rvi[i-1] > signal_line[i-1] and rvi[i] < signal_line[i]:
            if signal != -1:
                buy_price.append(np.nan)
                sell_price.append(prices[i])
                signal = -1
                rvi_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                rvi_signal.append(0)
        else:
            buy_price.append(np.nan)
            sell_price.append(np.nan)
            rvi_signal.append(0)
            
    return buy_price, sell_price, rvi_signal

buy_price, sell_price, rvi_signal = implement_rvi_strategy(aapl['close'], aapl['rvi'], aapl['signal_line'])

Пояснение к коду. Во-первых, мы определяем функцию с именем «implementation_rvi_strategy», которая принимает цены акций («цены») и компоненты индекса относительной энергии («rvi», «signal_line») как параметры.

Внутри функции мы создаем три пустых списка (buy_price, sell_price и rvi_signal), в которые значения будут добавляться при создании торговой стратегии.

После этого реализуем торговую стратегию через цикл for. Внутри цикла for мы передаем определенные условия, и если они выполнены, соответствующие значения будут добавлены в пустые списки. Если условие покупки акции будет выполнено, цена покупки будет добавлена ​​в список «buy_price», а значение сигнала будет добавлено как 1, представляющее покупку акции. Точно так же, если условие продажи акций будет выполнено, цена продажи будет добавлена ​​в список «sell_price», а значение сигнала будет добавлено как -1, что означает продажу акций.

Наконец, мы возвращаем списки, к которым добавлены значения. Затем мы вызываем созданную функцию и сохраняем значения в соответствующих переменных. Список не имеет смысла, пока мы не нанесем значения на график. Итак, нанесем на график значения созданных торговых списков.

Шаг 5: Построение торговых сигналов

На этом этапе мы собираемся построить созданные торговые списки, чтобы понять их смысл.

Реализация Python:

# RELATIVE VIGOR INDEX TRADING SIGNALS PLOT

ax1 = plt.subplot2grid((11,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((11,1), (6,0), rowspan = 5, colspan = 1)
ax1.plot(aapl['close'], linewidth = 2)
ax1.plot(aapl.index, buy_price, marker = '^', markersize = 12, color = 'green', linewidth = 0, label = 'BUY SIGNAL')
ax1.plot(aapl.index, sell_price, marker = 'v', markersize = 12, color = 'r', linewidth = 0, label = 'SELL SIGNAL')
ax1.legend()
ax1.set_title('AAPL RVI TRADING SIGNALS')
ax2.plot(aapl['rvi'], linewidth = 2, color = 'orange', label = 'RVI LINE')
ax2.plot(aapl['signal_line'], linewidth = 2, color = '#BA5FE3', label = 'SIGNAL LINE')
ax2.set_title('AAPL RVI 10')
ax2.legend()
plt.show()

Вывод:

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

Шаг 6: Создание нашей позиции

На этом этапе мы собираемся создать список, в котором будет указано 1, если мы владеем акциями, или 0, если мы не владеем акциями или не держим их.

Реализация Python:

# STOCK POSITION

position = []
for i in range(len(rvi_signal)):
    if rvi_signal[i] > 1:
        position.append(0)
    else:
        position.append(1)
        
for i in range(len(aapl['close'])):
    if rvi_signal[i] == 1:
        position[i] = 1
    elif rvi_signal[i] == -1:
        position[i] = 0
    else:
        position[i] = position[i-1]
        
close_price = aapl['close']
rvi = aapl['rvi']
signal_line = aapl['signal_line']
rvi_signal = pd.DataFrame(rvi_signal).rename(columns = {0:'rvi_signal'}).set_index(aapl.index)
position = pd.DataFrame(position).rename(columns = {0:'rvi_position'}).set_index(aapl.index)

frames = [close_price, rvi, signal_line, rvi_signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)

strategy

Вывод:

Пояснение к коду: Сначала мы создаем пустой список с именем «позиция». Мы передаем два цикла for, один из которых предназначен для генерации значений для списка «position», которые просто соответствуют длине списка «signal». Другой цикл for - это тот, который мы используем для генерации фактических значений положения. Внутри второго цикла for мы перебираем значения списка «signal», и значения списка «position» добавляются в зависимости от того, какое условие выполняется. Стоимость позиции остается равной 1, если мы держим акцию, или 0, если мы продали акцию или не владеем ею. Наконец, мы делаем некоторые манипуляции с данными, чтобы объединить все созданные списки в один фрейм данных.

Из показанных выходных данных мы видим, что в первой строке наша позиция в акции осталась 1 (поскольку нет никаких изменений в сигнале индекса относительной силы), но наша позиция внезапно превратилась в -1, когда мы продали акцию. когда торговый сигнал индекса относительной силы представляет собой сигнал продажи (-1). Наша позиция будет оставаться 0 до тех пор, пока не произойдут некоторые изменения в торговом сигнале. Пришло время провести тестирование на истории!

Шаг 7: Тестирование на истории

Прежде чем двигаться дальше, важно знать, что такое бэктестинг. Бэктестинг - это процесс проверки того, насколько хорошо наша торговая стратегия работает с данными по акциям. В нашем случае мы собираемся реализовать процесс тестирования нашей торговой стратегии Relative Vigor Index по данным акций Apple.

Реализация Python:

# BACKTESTING

aapl_ret = pd.DataFrame(np.diff(aapl['close'])).rename(columns = {0:'returns'})
rvi_strategy_ret = []

for i in range(len(aapl_ret)):
    returns = aapl_ret['returns'][i]*strategy['rvi_position'][i]
    rvi_strategy_ret.append(returns)
    
rvi_strategy_ret_df = pd.DataFrame(rvi_strategy_ret).rename(columns = {0:'rvi_returns'})
investment_value = 100000
number_of_stocks = floor(investment_value/aapl['close'][0])
rvi_investment_ret = []

for i in range(len(rvi_strategy_ret_df['rvi_returns'])):
    returns = number_of_stocks*rvi_strategy_ret_df['rvi_returns'][i]
    rvi_investment_ret.append(returns)

rvi_investment_ret_df = pd.DataFrame(rvi_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(rvi_investment_ret_df['investment_returns']), 2)
profit_percentage = floor((total_investment_ret/investment_value)*100)
print(cl('Profit gained from the RVI strategy by investing $100k in AAPL : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the RVI strategy : {}%'.format(profit_percentage), attrs = ['bold']))

Вывод:

Profit gained from the RVI strategy by investing $100k in AAPL : 71643.4
Profit percentage of the RVI strategy : 71%

Пояснение к коду. Сначала мы рассчитываем доходность акций Apple с помощью функции «diff», предоставляемой пакетом NumPy, и сохранили ее как фрейм данных в переменной «aapl_ret». Затем мы передаем цикл for для перебора значений переменной «aapl_ret» для расчета прибыли, которую мы получили от нашей торговой стратегии RVI, и эти значения возвращаемых значений добавляются в список «rvi_strategy_ret». Затем мы конвертируем список «rvi_strategy_ret» в фрейм данных и сохраняем его в переменной «rvi_strategy_ret_df».

Затем следует процесс тестирования на истории. Мы собираемся протестировать нашу стратегию на исторических данных, вложив в нашу торговую стратегию сто тысяч долларов. Итак, сначала мы сохраняем сумму инвестиций в переменной «investment_value». После этого мы рассчитываем количество акций Apple, которые мы можем купить, используя сумму инвестиций. Вы можете заметить, что я использовал функцию «floor», предоставляемую пакетом Math, потому что при делении суммы инвестиций на цену закрытия акций Apple выводится результат с десятичными числами. Количество акций должно быть целым, а не десятичным числом. Используя функцию «floor», мы можем вырезать десятичные дроби. Помните, что функция «пол» намного сложнее, чем «круглая» функция. Затем мы передаем цикл for для определения окупаемости инвестиций, за которым следуют некоторые задачи по манипулированию данными.

Наконец, мы печатаем общую прибыль, которую мы получили, вложив сто тысяч в нашу торговую стратегию, и выясняется, что мы получили приблизительную прибыль в семьдесят одну тысячу долларов США за один год. Это неплохо! Теперь давайте сравним нашу доходность с доходностью SPY ETF (ETF, предназначенного для отслеживания индекса фондового рынка S&P 500).

Шаг 8: Сравнение SPY ETF

Этот шаг не является обязательным, но настоятельно рекомендуется, поскольку мы можем получить представление о том, насколько хорошо наша торговая стратегия работает по сравнению с эталонным тестом (SPY ETF). На этом этапе мы извлечем данные SPY ETF, используя созданную нами функцию «get_historical_data», и сравним доходность, которую мы получаем от SPY ​​ETF, с доходностью нашей кроссоверной торговой стратегии RVI на Apple.

Вы могли заметить, что во всех моих статьях по алгоритмической торговле я сравнивал результаты стратегии не с самим рыночным индексом S&P 500, а с SPY ETF, и это связано с тем, что большинство поставщиков данных по акциям (например, Twelve Data) этого не делают. t предоставить данные индекса S&P 500. Итак, у меня нет другого выбора, кроме как использовать SPY ETF. Если вам посчастливилось получить данные рыночного индекса S&P 500, рекомендуется использовать их для сравнения, а не какой-либо ETF.

Реализация Python:

# SPY ETF COMPARISON

def get_benchmark(start_date, investment_value):
    spy = get_historical_data('SPY', start_date)['close']
    benchmark = pd.DataFrame(np.diff(spy)).rename(columns = {0:'benchmark_returns'})
    
    investment_value = investment_value
    number_of_stocks = floor(investment_value/spy[0])
    benchmark_investment_ret = []
    
    for i in range(len(benchmark['benchmark_returns'])):
        returns = number_of_stocks*benchmark['benchmark_returns'][i]
        benchmark_investment_ret.append(returns)

    benchmark_investment_ret_df = pd.DataFrame(benchmark_investment_ret).rename(columns = {0:'investment_returns'})
    return benchmark_investment_ret_df

benchmark = get_benchmark('2020-01-01', 100000)
investment_value = 100000
total_benchmark_investment_ret = round(sum(benchmark['investment_returns']), 2)
benchmark_profit_percentage = floor((total_benchmark_investment_ret/investment_value)*100)
print(cl('Benchmark profit by investing $100k : {}'.format(total_benchmark_investment_ret), attrs = ['bold']))
print(cl('Benchmark Profit percentage : {}%'.format(benchmark_profit_percentage), attrs = ['bold']))
print(cl('RVI Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage), attrs = ['bold']))

Вывод:

Benchmark profit by investing $100k : 28491.13
Benchmark Profit percentage : 28%
RVI Strategy profit is 43% higher than the Benchmark Profit

Пояснение к коду. Код, использованный на этом этапе, почти аналогичен коду, который использовался на предыдущем этапе тестирования на истории, но вместо того, чтобы инвестировать в Apple, мы инвестируем в SPY ETF, не реализуя никаких торговых стратегий. Из выходных данных мы видим, что наша торговая стратегия кроссовера Relative Vigor Index превзошла SPY ETF на 43%. Замечательно!

Последние мысли!

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

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

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

Сказав это, вы подошли к концу статьи. Если вы забыли выполнить какую-либо часть кода, не волнуйтесь. Я предоставил полный исходный код в конце статьи. Надеюсь, вы узнали что-то новое и полезное из этой статьи.

Полный код:

# IMPORTING PACKAGES

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import requests
from termcolor import colored as cl
from math import floor

plt.style.use('fivethirtyeight')
plt.rcParams['figure.figsize'] = (20,10)

# EXTRACTING STOCK DATA

def get_historical_data(symbol, start_date):
    api_key = 'YOUR API KEY'
    api_url = f'https://api.twelvedata.com/time_series?symbol={symbol}&interval=1day&outputsize=5000&apikey={api_key}'
    raw_df = requests.get(api_url).json()
    df = pd.DataFrame(raw_df['values']).iloc[::-1].set_index('datetime').astype(float)
    df = df[df.index >= start_date]
    df.index = pd.to_datetime(df.index)
    return df

aapl = get_historical_data('AAPL', '2019-01-01')
aapl.tail()

# RELATIVE VIGOR INDEX CALCULATION

def get_rvi(open, high, low, close, lookback):
    a = close - open
    b = 2 * (close.shift(2) - open.shift(2))
    c = 2 * (close.shift(3) - open.shift(3))
    d = close.shift(4) - open.shift(4)
    numerator = a + b + c + d
    
    e = high - low
    f = 2 * (high.shift(2) - low.shift(2))
    g = 2 * (high.shift(3) - low.shift(3))
    h = high.shift(4) - low.shift(4)
    denominator = e + f + g + h
    
    numerator_sum = numerator.rolling(4).sum()
    denominator_sum = denominator.rolling(4).sum()
    rvi = (numerator_sum / denominator_sum).rolling(lookback).mean()
    
    rvi1 = 2 * rvi.shift(1)
    rvi2 = 2 * rvi.shift(2)
    rvi3 = rvi.shift(3)
    rvi_signal = (rvi + rvi1 + rvi2 + rvi3) / 6
    
    return rvi, rvi_signal

aapl['rvi'], aapl['signal_line'] = get_rvi(aapl['open'], aapl['high'], aapl['low'], aapl['close'], 10)
aapl = aapl.dropna()
aapl = aapl[aapl.index >= '2020-01-01']
aapl.tail()

# RELATIVE VIGOR INDEX PLOT

ax1 = plt.subplot2grid((11,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((11,1), (6,0), rowspan = 5, colspan = 1)
ax1.plot(aapl['close'], linewidth = 2.5)
ax1.set_title('AAPL CLOSING PRICES')
ax2.plot(aapl['rvi'], linewidth = 2, color = 'orange', label = 'RVI LINE')
ax2.plot(aapl['signal_line'], linewidth = 2, color = '#BA5FE3', label = 'SIGNAL LINE')
ax2.legend()
ax2.set_title('AAPL RVI 10')
plt.show()

# RELATIVE VIGOR INDEX STRATEGY

def implement_rvi_strategy(prices, rvi, signal_line):
    buy_price = []
    sell_price = []
    rvi_signal = []
    signal = 0
    
    for i in range(len(prices)):
        if rvi[i-1] < signal_line[i-1] and rvi[i] > signal_line[i]:
            if signal != 1:
                buy_price.append(prices[i])
                sell_price.append(np.nan)
                signal = 1
                rvi_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                rvi_signal.append(0)
        elif rvi[i-1] > signal_line[i-1] and rvi[i] < signal_line[i]:
            if signal != -1:
                buy_price.append(np.nan)
                sell_price.append(prices[i])
                signal = -1
                rvi_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                rvi_signal.append(0)
        else:
            buy_price.append(np.nan)
            sell_price.append(np.nan)
            rvi_signal.append(0)
            
    return buy_price, sell_price, rvi_signal

buy_price, sell_price, rvi_signal = implement_rvi_strategy(aapl['close'], aapl['rvi'], aapl['signal_line'])

# RELATIVE VIGOR INDEX TRADING SIGNALS PLOT

ax1 = plt.subplot2grid((11,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((11,1), (6,0), rowspan = 5, colspan = 1)
ax1.plot(aapl['close'], linewidth = 2)
ax1.plot(aapl.index, buy_price, marker = '^', markersize = 12, color = 'green', linewidth = 0, label = 'BUY SIGNAL')
ax1.plot(aapl.index, sell_price, marker = 'v', markersize = 12, color = 'r', linewidth = 0, label = 'SELL SIGNAL')
ax1.legend()
ax1.set_title('AAPL RVI TRADING SIGNALS')
ax2.plot(aapl['rvi'], linewidth = 2, color = 'orange', label = 'RVI LINE')
ax2.plot(aapl['signal_line'], linewidth = 2, color = '#BA5FE3', label = 'SIGNAL LINE')
ax2.set_title('AAPL RVI 10')
ax2.legend()
plt.show()

# STOCK POSITION

position = []
for i in range(len(rvi_signal)):
    if rvi_signal[i] > 1:
        position.append(0)
    else:
        position.append(1)
        
for i in range(len(aapl['close'])):
    if rvi_signal[i] == 1:
        position[i] = 1
    elif rvi_signal[i] == -1:
        position[i] = 0
    else:
        position[i] = position[i-1]
        
close_price = aapl['close']
rvi = aapl['rvi']
signal_line = aapl['signal_line']
rvi_signal = pd.DataFrame(rvi_signal).rename(columns = {0:'rvi_signal'}).set_index(aapl.index)
position = pd.DataFrame(position).rename(columns = {0:'rvi_position'}).set_index(aapl.index)

frames = [close_price, rvi, signal_line, rvi_signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)

strategy
strategy[19:24]

# BACKTESTING

aapl_ret = pd.DataFrame(np.diff(aapl['close'])).rename(columns = {0:'returns'})
rvi_strategy_ret = []

for i in range(len(aapl_ret)):
    returns = aapl_ret['returns'][i]*strategy['rvi_position'][i]
    rvi_strategy_ret.append(returns)
    
rvi_strategy_ret_df = pd.DataFrame(rvi_strategy_ret).rename(columns = {0:'rvi_returns'})
investment_value = 100000
number_of_stocks = floor(investment_value/aapl['close'][0])
rvi_investment_ret = []

for i in range(len(rvi_strategy_ret_df['rvi_returns'])):
    returns = number_of_stocks*rvi_strategy_ret_df['rvi_returns'][i]
    rvi_investment_ret.append(returns)

rvi_investment_ret_df = pd.DataFrame(rvi_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(rvi_investment_ret_df['investment_returns']), 2)
profit_percentage = floor((total_investment_ret/investment_value)*100)

print(cl('Profit gained from the RVI strategy by investing $100k in AAPL : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the RVI strategy : {}%'.format(profit_percentage), attrs = ['bold']))

# SPY ETF COMPARISON

def get_benchmark(start_date, investment_value):
    spy = get_historical_data('SPY', start_date)['close']
    benchmark = pd.DataFrame(np.diff(spy)).rename(columns = {0:'benchmark_returns'})
    
    investment_value = investment_value
    number_of_stocks = floor(investment_value/spy[0])
    benchmark_investment_ret = []
    
    for i in range(len(benchmark['benchmark_returns'])):
        returns = number_of_stocks*benchmark['benchmark_returns'][i]
        benchmark_investment_ret.append(returns)

    benchmark_investment_ret_df = pd.DataFrame(benchmark_investment_ret).rename(columns = {0:'investment_returns'})
    return benchmark_investment_ret_df

benchmark = get_benchmark('2020-01-01', 100000)

investment_value = 100000
total_benchmark_investment_ret = round(sum(benchmark['investment_returns']), 2)
benchmark_profit_percentage = floor((total_benchmark_investment_ret/investment_value)*100)

print(cl('Benchmark profit by investing $100k : {}'.format(total_benchmark_investment_ret), attrs = ['bold']))
print(cl('Benchmark Profit percentage : {}%'.format(benchmark_profit_percentage), attrs = ['bold']))
print(cl('RVI Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage), attrs = ['bold']))