Индикатор Know Sure Thing на Python

Полное руководство по использованию и реализации индикатора KST в python

В то время как я тестировал торговую стратегию с одним из самых популярных импульсных осцилляторов, скользящей средней конвергенцией / дивергенцией (MACD), результаты были потрясающими. Сегодня я нашел клон индикатора MACD, производительность которого еще более эффективна. Это не что иное, как Know Sure Thing, кратко известный как индикатор KST.

В этой статье мы сначала построим некоторые основные интуиции о том, что такое индикатор KST, и методы расчета индикатора. После этого мы перейдем к программной части, где мы используем Python для построения индикатора с нуля, построения простой торговой стратегии на основе индикатора, тестирования стратегии на акциях Tesla и сравнения ее производительности с показателями SPY ETF (ETF, целью которого является отслеживание движений рыночного индекса S&P 500). С учетом сказанного, давайте углубимся в статью!

Прежде чем двигаться дальше, если вы хотите протестировать свои торговые стратегии без какого-либо кодирования, для этого есть решение. Это BacktestZone. Это платформа для тестирования любого количества торговых стратегий на различных типах торгуемых активов бесплатно без кодирования. Вы можете использовать инструмент сразу, используя ссылку здесь: https://www.backtestzone.com/

Скорость изменения (ROC)

Прежде чем перейти к изучению индикатора KST, важно знать, что может предложить индикатор Rate Of Change, поскольку индикатор Know Sure Thing основан на ROC. Индикатор Rate Of Change – это индикатор импульса, который используется трейдерами в качестве инструмента для определения процентного изменения цены от текущей цены закрытия и цены заданного количества периодов назад. В отличие от других индикаторов импульса, таких как RSI и CCI, индикатор Rate Of Change является неограниченным осциллятором, значения которого не привязаны между определенными пределами.

Чтобы рассчитать показания ROC, мы должны сначала определить значение ‘n’, которое является ничем иным, как тем, сколько периодов назад сравнивается текущая цена закрытия. Определение ‘n’ варьируется от одного трейдера к другому, но традиционная настройка — 9 (широко используется для краткосрочной торговли). При значении ‘n’ 9 показания индикатора ROC рассчитываются следующим образом:

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

ROC 9 = [ ( C.CLOSE - PREV9.CLOSE ) / PREV9.CLOSE ] * 100where,
C.CLOSE = Current Closing Price
PREV9.CLOSE = Closing Price of 9 Periods ago

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

Вот что такое РПЦ и ее расчеты. Теперь мы готовы перейти к изучению основной идеи этой статьи, индикатора Know Sure Thing.

Знай наверняка (KST)

Индикатор Know Sure Thing – это неограниченный импульсный осциллятор, который широко используется трейдерами для понимания показаний индикатора ROC. Индикатор KST основан на четырех различных таймфреймах сглаженного ROC и объединяет коллективные данные в один осциллятор. Индикатор Know Sure Thing состоит из двух компонентов:

Линия KST: Первым компонентом является сама линия KST. Чтобы рассчитать показания линии KST, мы должны сначала определить четыре ROC с 10, 15, 20, 30 в качестве значений ‘n’ соответственно. Затем каждый ROC сглаживается с использованием простой скользящей средней с 10, 10, 10, 15 в качестве периода обзора соответственно. Этот сглаженный ROC известен как ROCSMA. После достижения ROCSMA на четырех разных таймфреймах мы должны умножить первый ROCSMA на один, второй ROCSMA на два, третий ROCSMA на три и четвертый на четыре. Наконец, эти четыре продукта добавляются друг к другу. Расчет линии KST может быть математически представлен следующим образом:

KL = (ROCSMA1 * 1) + (ROCSMA2 * 2) + (ROCSMA3 * 3) + (ROCSMA4 * 4)where,
KL = KST Line
ROCSMA1 = ROC 10 smoothed with SMA 10
ROCSMA2 = ROC 15 smoothed with SMA 10
ROCSMA3 = ROC 20 smoothed with SMA 10
ROCSMA4 = ROC 30 smoothed with SMA 15

Сигнальная линия: Теперь вторым компонентом индикатора Know Sure Thing является компонент Сигнальная линия. Этот компонент является не чем иным, как сглаженной версией линейки KST. Для сглаживания значений линии KST широко используется простая скользящая средняя с 9 в качестве периода оглядки. Расчет сигнальной линии выглядит примерно так:

SIGNAL LINE = SMA9 ( KST LINE )

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

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

IF P.KST LINE < P.SIGNAL LINE AND C.KST LINE > C.SIGNAL LINE => BUY
IF P.KST LINE > P.SIGNAL LINE AND C.KST LINE < C.SIGNAL LINE => SELL

На этом мы завершаем нашу теоретическую часть по индикатору Know Sure Thing. Теперь перейдем к программной части, где мы собираемся использовать python для построения индикатора с нуля, построения стратегии кроссоверной торговли, тестирования стратегии на акциях Tesla, сравнения доходности стратегии кроссовера с доходностью SPY ETF. Без лишних слов, давайте перейдем к программной части и сделаем некоторое кодирование! Прежде чем двигаться дальше, примечание об отказе от ответственности: единственная цель этой статьи — обучить людей и должна рассматриваться как информационная часть, но не как инвестиционный совет или около того.

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

Кодирующая часть классифицируется на различные этапы следующим образом:

1. Importing Packages
2. Extracting Stock Data from Twelve Data
3. ROC Calculation
4. Know Sure Thing Calculation
5. Know Sure Thing indicator Plot
6. Creating the Trading Strategy
7. Plotting the Trading Lists
8. Creating our Position
9. Backtesting
10. SPY ETF Comparison

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

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

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

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

# IMPORTING PACKAGES

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

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

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

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

На этом шаге мы собираемся извлечь исторические данные о запасах Tesla, используя конечную точку 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

tsla = get_historical_data('TSLA', '2019-01-01')
tsla

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

Шаг-3: Расчет ROC

На этом шаге мы определим функцию для вычисления значений индикатора Rate Of Change для заданного ряда.

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

# ROC CALCULATIONdef get_roc(close, n):
difference = close.diff(n)
nprev_values = close.shift(n)
roc = (difference / nprev_values) * 100
return roc

Пояснение к коду: Сначала мы определяем функцию с именем ‘get_roc’, которая принимает в качестве параметров цену закрытия акции (‘close’) и значение ‘n’ (‘n’). Внутри функции мы сначала берем разницу между текущей ценой закрытия и ценой закрытия за указанное количество периодов назад, используя функцию ‘diff’, предоставляемую пакетом Pandas. С помощью функции ‘shift’ мы учитываем цену закрытия за указанное количество периодов назад и сохраняем ее в переменной ‘nprev_values’. Затем мы подставляем определенные значения в формулу индикатора ROC, которую мы обсуждали ранее, чтобы рассчитать значения и, наконец, вернуть данные.

Шаг-4: Знай уверенный расчет

На этом шаге мы рассчитаем компоненты индикатора Know Sure Thing, следуя методам и формулам, которые мы обсуждали ранее.

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

# KST CALCULATION

def get_kst(close, sma1, sma2, sma3, sma4, roc1, roc2, roc3, roc4, signal):
rcma1 = get_roc(close, roc1).rolling(sma1).mean()
rcma2 = get_roc(close, roc2).rolling(sma2).mean()
rcma3 = get_roc(close, roc3).rolling(sma3).mean()
rcma4 = get_roc(close, roc4).rolling(sma4).mean()
kst = (rcma1 * 1) + (rcma2 * 2) + (rcma3 * 3) + (rcma4 * 4)
signal = kst.rolling(signal).mean()
return kst, signal

tsla['kst'], tsla['signal_line'] = get_kst(tsla['close'], 10, 10, 10, 15, 10, 15, 20, 30, 9)
tsla = tsla[tsla.index >= '2020-01-01']
tsla.tail()

Пояснение к коду: Во-первых, мы определяем функцию с именем «get_kst», которая принимает цену закрытия акции («закрытие»), четыре периода обзора для сглаживания значений ROC («sma1», «sma2», «sma3», «sma4»), четыре значения «n» ROC («roc1», «roc2», «roc3», «roc4») и период обратного просмотра для сигнальной линии («сигнал») в качестве параметров.

Внутри функции мы сначала вычисляем четыре значения ROCSMA, используя функцию ‘rolling’, предоставляемую пакетом Pandas, и функцию ‘get_roc’, которую мы создали ранее. Затем мы подставляем вычисляемые ROCSMA в формулу, которую мы обсуждали ранее, чтобы определить показания линии KST. Затем мы сглаживаем значения линии KST с помощью функции ‘rolling’, чтобы получить значения Сигнальной линии и сохранить их в переменной ‘signal’.

Наконец, мы возвращаем как рассчитанные компоненты индикатора KST, так и вызовем созданную функцию для хранения показаний линии KST Теслы и сигнальной линии.

Шаг-5: График индикатора Know Sure Thing

На этом шаге мы собираемся построить рассчитанные компоненты значений индикатора KST Tesla, чтобы сделать из них больше смысла. Основная цель этой части заключается не в разделе кодирования, а в том, чтобы наблюдать за графиком, чтобы получить четкое представление о техническом индикаторе Know Sure Thing.

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

# KST INDICATOR 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(tsla['close'], linewidth = 2.5)
ax1.set_title('TSLA CLOSING PRICES')
ax2.plot(tsla['kst'], linewidth = 2, label = 'KST', color = 'orange')
ax2.plot(tsla['signal_line'], linewidth = 2, label = 'SIGNAL', color = 'mediumorchid')
ax2.legend()
ax2.set_title('TSLA KST')
plt.show()

Приведенный выше график разделен на две панели: верхнюю панель с ценой закрытия Tesla, и нижнюю панель с компонентами индикатора Know Sure Thing. Можно заметить, что показания обоих компонентов являются неопределенными и не ограничены между определенными пределами, поскольку индикатор Know Sure Thing является неограниченным осциллятором. Это является причиной отсутствия уровней перекупленности и перепроданности на графике, что широко построено в других импульсных осцилляторах. Но некоторые предполагают, что этот индикатор может быть использован для определения уровней перекупленности и перепроданности, но отличается от одной акции к другой. Это означает, что в отличие от других импульсных осцилляторов, которые имеют уровни перекупленности и перепроданности по умолчанию, необходимо проанализировать движение и показания компонентов KST для определения уровней перекупленности и перепроданности. В нашем случае оптимально было бы определить уровень перекупленности на уровне 100 и уровень перепроданности на -100.

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

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

На этом этапе мы собираемся реализовать обсуждаемую стратегию кроссовера Know Sure Thing на python.

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

# KST CROSSOVER TRADING STRATEGY

def implement_kst_strategy(prices, kst_line, signal_line):
buy_price = []
sell_price = []
kst_signal = []
signal = 0

for i in range(len(kst_line)):

if kst_line[i-1] < signal_line[i-1] and kst_line[i] > signal_line[i]:
if signal != 1:
buy_price.append(prices[i])
sell_price.append(np.nan)
signal = 1
kst_signal.append(signal)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
kst_signal.append(0)

elif kst_line[i-1] > signal_line[i-1] and kst_line[i] < signal_line[i]:
if signal != -1:
buy_price.append(np.nan)
sell_price.append(prices[i])
signal = -1
kst_signal.append(signal)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
kst_signal.append(0)

else:
buy_price.append(np.nan)
sell_price.append(np.nan)
kst_signal.append(0)

return buy_price, sell_price, kst_signal

buy_price, sell_price, kst_signal = implement_kst_strategy(tsla['close'], tsla['kst'], tsla['signal_line'])

Пояснение к коду: Во-первых, мы определяем функцию с именем «implement_kst_strategy», которая принимает цены акций («цены»), показания линии KST («kst_line») и показания сигнальной линии («signal_line») в качестве параметров.

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

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

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

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

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

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

# 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(tsla['close'], linewidth = 2, label = 'TSLA')
ax1.plot(tsla.index, buy_price, marker = '^', markersize = 12, linewidth = 0, color = 'green', label = 'BUY SIGNAL')
ax1.plot(tsla.index, sell_price, marker = 'v', markersize = 12, linewidth = 0, color = 'r', label = 'SELL SIGNAL')
ax1.legend()
ax1.set_title('TSLA KST TRADING SIGNALS')
ax2.plot(tsla['kst'], linewidth = 2, label = 'KST', color = 'orange')
ax2.plot(tsla['signal_line'], linewidth = 2, label = 'SIGNAL', color = 'mediumorchid')
ax2.legend()
ax2.set_title('TSLA KST')
plt.show()

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

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

На этом шаге мы создадим список, который указывает 1, если мы держим акцию, или 0, если мы не владеем или не держим акции.

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

# STOCK POSITION

position = []
for i in range(len(kst_signal)):
if kst_signal[i] > 1:
position.append(0)
else:
position.append(1)

for i in range(len(tsla['close'])):
if kst_signal[i] == 1:
position[i] = 1
elif kst_signal[i] == -1:
position[i] = 0
else:
position[i] = position[i-1]

close_price = tsla['close']
kst = tsla['kst']
signal_line = tsla['signal_line']
kst_signal = pd.DataFrame(kst_signal).rename(columns = {0:'kst_signal'}).set_index(tsla.index)
position = pd.DataFrame(position).rename(columns = {0:'kst_position'}).set_index(tsla.index)

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

strategy

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

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

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

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

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

# BACKTESTING

tsla_ret = pd.DataFrame(np.diff(tsla['close'])).rename(columns = {0:'returns'})
kst_strategy_ret = []for i in range(len(tsla_ret)):
returns = tsla_ret['returns'][i]*strategy['kst_position'][i]
kst_strategy_ret.append(returns)

kst_strategy_ret_df = pd.DataFrame(kst_strategy_ret).rename(columns = {0:'kst_returns'})
investment_value = 100000
number_of_stocks = floor(investment_value/tsla['close'][0])
kst_investment_ret = []for i in range(len(kst_strategy_ret_df['kst_returns'])):
returns = number_of_stocks*kst_strategy_ret_df['kst_returns'][i]
kst_investment_ret.append(returns)kst_investment_ret_df = pd.DataFrame(kst_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(kst_investment_ret_df['investment_returns']), 2)
profit_percentage = floor((total_investment_ret/investment_value)*100)
print(cl('Profit gained from the KST strategy by investing $100k in TSLA : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the KST strategy : {}%'.format(profit_percentage), attrs = ['bold']))

Выпуск:

Profit gained from the KST strategy by investing $100k in TSLA : 295766.23
Profit percentage of the KST strategy : 295%

Пояснение к коду: Во-первых, мы рассчитываем доходность акций Tesla, используя функцию ‘diff’, предоставляемую пакетом NumPy, и мы сохранили ее в виде фрейма данных в переменной ‘tsla_ret’. Затем мы передаем цикл for-loop для итерации значений переменной ‘tsla_ret’, чтобы рассчитать доходность, которую мы получили от нашей торговой стратегии индикатора Know Sure Thing, и эти значения доходности добавляются в список ‘kst_strategy_ret’. Затем мы преобразуем список ‘kst_strategy_ret’ в кадр данных и сохраняем его в переменную ‘kst_strategy_ret_df’.

Далее следует процесс тестирования. Мы собираемся протестировать нашу стратегию, вложив сто тысяч долларов США в нашу торговую стратегию. Итак, во-первых, мы храним сумму инвестиций в переменной «investment_value». После этого мы рассчитываем количество акций Tesla, которые мы можем купить, используя сумму инвестиций. Вы можете заметить, что я использовал функцию «floor», предоставляемую пакетом Math, потому что, деля сумму инвестиций на цену закрытия акций Tesla, она выдает выход с десятичными числами. Число акций должно быть целым, но не десятичным числом. Используя функцию ‘floor’, мы можем вырезать десятичные дроби. Помните, что функция «этаж» намного сложнее, чем функция «круглый». Затем мы передаем цикл for-loop, чтобы найти доходность инвестиций, за которой следуют некоторые задачи манипулирования данными.

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

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

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

Реализация 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[-1])
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('KST Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage), attrs = ['bold']))

Выпуск:

Benchmark profit by investing $100k : 22650.46
Benchmark Profit percentage : 22%
KST Strategy profit is 273% higher than the Benchmark Profit

Пояснение к коду: Код, используемый на этом этапе, почти аналогичен тому, который использовался на предыдущем этапе тестирования, но вместо того, чтобы инвестировать в Tesla, мы инвестируем в SPY ETF, не реализуя никаких торговых стратегий. Из выходных данных мы видим, что наша кроссоверная торговая стратегия Know Sure Thing превзошла SPY ETF на 273%. Это здорово!

Заключение

После ошеломляющего процесса сокрушения как теории, так и части кодирования, мы успешно узнали, что такое индикатор Know Sure Thing, математика, стоящая за ним, и как построить простую торговую стратегию кроссовера KST на python.

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

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

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

Полный код:

# IMPORTING PACKAGES

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

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

tsla = get_historical_data('TSLA', '2019-01-01')
print(tsla)


# ROC CALCULATION

def get_roc(close, n):
    difference = close.diff(n)
    nprev_values = close.shift(n)
    roc = (difference / nprev_values) * 100
    return roc


# KST CALCULATION

def get_kst(close, sma1, sma2, sma3, sma4, roc1, roc2, roc3, roc4, signal):
    rcma1 = get_roc(close, roc1).rolling(sma1).mean()
    rcma2 = get_roc(close, roc2).rolling(sma2).mean()
    rcma3 = get_roc(close, roc3).rolling(sma3).mean()
    rcma4 = get_roc(close, roc4).rolling(sma4).mean()
    kst = (rcma1 * 1) + (rcma2 * 2) + (rcma3 * 3) + (rcma4 * 4)
    signal = kst.rolling(signal).mean()
    return kst, signal

tsla['kst'], tsla['signal_line'] = get_kst(tsla['close'], 10, 10, 10, 15, 10, 15, 20, 30, 9)
tsla = tsla[tsla.index >= '2020-01-01']
print(tsla.tail())


# KST INDICATOR 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(tsla['close'], linewidth = 2.5)
ax1.set_title('TSLA CLOSING PRICES')
ax2.plot(tsla['kst'], linewidth = 2, label = 'KST', color = 'orange')
ax2.plot(tsla['signal_line'], linewidth = 2, label = 'SIGNAL', color = 'mediumorchid')
ax2.legend()
ax2.set_title('TSLA KST')
plt.show()


# KST CROSSOVER TRADING STRATEGY

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

buy_price, sell_price, kst_signal = implement_kst_strategy(tsla['close'], tsla['kst'], tsla['signal_line'])


# 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(tsla['close'], linewidth = 2, label = 'TSLA')
ax1.plot(tsla.index, buy_price, marker = '^', markersize = 12, linewidth = 0, color = 'green', label = 'BUY SIGNAL')
ax1.plot(tsla.index, sell_price, marker = 'v', markersize = 12, linewidth = 0, color = 'r', label = 'SELL SIGNAL')
ax1.legend()
ax1.set_title('TSLA KST TRADING SIGNALS')
ax2.plot(tsla['kst'], linewidth = 2, label = 'KST', color = 'orange')
ax2.plot(tsla['signal_line'], linewidth = 2, label = 'SIGNAL', color = 'mediumorchid')
ax2.legend()
ax2.set_title('TSLA KST')
plt.show()


# STOCK POSITION

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

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

print(strategy)
print(strategy[35:40])


# BACKTESTING

tsla_ret = pd.DataFrame(np.diff(tsla['close'])).rename(columns = {0:'returns'})
kst_strategy_ret = []

for i in range(len(tsla_ret)):
    returns = tsla_ret['returns'][i]*strategy['kst_position'][i]
    kst_strategy_ret.append(returns)
    
kst_strategy_ret_df = pd.DataFrame(kst_strategy_ret).rename(columns = {0:'kst_returns'})
investment_value = 100000
number_of_stocks = floor(investment_value/tsla['close'][0])
kst_investment_ret = []

for i in range(len(kst_strategy_ret_df['kst_returns'])):
    returns = number_of_stocks*kst_strategy_ret_df['kst_returns'][i]
    kst_investment_ret.append(returns)

kst_investment_ret_df = pd.DataFrame(kst_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(kst_investment_ret_df['investment_returns']), 2)
profit_percentage = floor((total_investment_ret/investment_value)*100)
print(cl('Profit gained from the KST strategy by investing $100k in TSLA : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the KST 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[-1])
    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('KST Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage), attrs = ['bold']))

Источник