Торговая стратегия SuperTrend

Извлечение из выходных данных стратегии
  • Реализация торговой стратегии Supertrend
  • SuperTrend V.1 — Система линий супертренда
  • Совмещение индикатора SuperTrend со скользящими средними в торговой стратегии.

Реализация торговой стратегии Supertrend

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

Знакомство

Это прекрасное утро, и вы сидите, просматривая конфигурацию графика TradingView в поисках горячих сигналов, только для того, чтобы столкнуться с неопределенными трендами. Кажется, что вы хотите искривить пространство-время, но вам кажется, что вы просто пытаетесь сложить ткань реальности своим разумом. Это может расстраивать, когда вы хотите сделать шаг, но рынок не является вашим постоянным другом в продуктовом магазине — он просто не сотрудничает! Поверьте мне, мы все проходили через это — это отстой. Однако вы не можете заставить рынок вести себя в вашу пользу. Время — это практически все, вы можете только подождать, пока звезды не выстроятся в одну линию, я думаю. Нам просто нужно дождаться идеального момента. Вот тут-то и пригодится индикатор Supertrend! Это инструмент, который анализирует заданные целевые рынки и следит за трендами и разворотами. Все, что вам нужно, это принимать правильные инвестиционные решения, конечно, некоторые из них могут сбить вас с толку, но это игра на постоянство — вам нужно больше побед, чем поражений. Вы должны знать, когда входить или выходить из рынка.

Почему вы хотите использовать Python для этой реализации?

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

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

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

Как работает индикатор Supertrend?

Давайте представим себе, что река иногда течет гладко, а иногда ведет себя так бурно и может выйти из берегов, это очень похоже на рынки — цены могут двигаться плавно или сильно колебаться и достигать определенных неожиданных диапазонов. Таким образом, индикатор супертренда рассматривает эти колебания, которые известны как волатильность. Это достигается путем измерения среднего истинного диапазона (ATR); который по сути является торговым диапазоном актива в течение определенного периода. Затем супертренд использует ATR для создания верхней и нижней полос, которые больше похожи на границы, в пределах которых цены на активы колеблются в пределах этого периода. Если цена пробивает верхнюю границу, что сигнализирует о восходящем тренде, а пробой нижней полосы сигнализирует о нисходящем тренде. Это простая концепция, не так ли?

Расчет супертренда

Прежде всего, мы рассчитываем средний истинный диапазон (ATR). Это мера волатильности. Рассчитывается за определенный период времени.

Начните с получения истинного диапазона (TR) для каждой точки данных. Точка данных может собираться за день, 4-часовой таймфрейм, ежечасно, по минутам и т. д. Все зависит от настройки таймфрейма.

TR = Max(High — Минимум, Abs(High — Предыдущее закрытие), Abs(Low — Предыдущее закрытие))

Затем мы вычисляем ATR за прошедший указанный период, допустим, мы могли бы сделать период 14. Таким образом, это означает, что ATR вычисляется для каждых 14 точек данных (возьмем их как строки в фрейме данных), что в значительной степени является средним значением истинных диапазонов (TR) за этот период.

Реализация Pyhton

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

Шаг 1: Подготовьте среду программирования

Установите последнюю версию дистрибутива Python 3, если у вас его еще нет. Вы можете проверить версию Python на своем компьютере, выполнив «python -v» в своем терминале.

Установите все необходимые библиотеки Python, такие как Pandas, Numpy, Matplotlib, mplfinance и ccxt.

pip install ccxt pandas matplotlib pandas-ta mplfinance

Шаг 2: Импорт зависимостей

import ccxt
import warnings
from matplotlib.pyplot import fill_between
import pandas as pd
import numpy as np
import pandas_ta as ta
import mplfinance as mpf
import matplotlib.pyplot as plt
warnings.filterwarnings('ignore')

Импортируйте необходимые зависимости для проекта. Пакет ccxt — это поставщик данных, который подключается к нескольким криптобиржам — в данном случае мы будем использовать его для подключения к Binance. Другие библиотеки Pandas и Numpy предназначены для статистических вычислений, в то время как mplfinance и matplotlib предназначены для создания графиков для визуализации данных.

Шаг 3: Получение данных об активе от поставщика (ccxt)

def fetch_asset_data(symbol, start_date, interval, exchange):
# Convert start_date to milliseconds timestamp
start_date_ms = exchange.parse8601(start_date)
ohlcv = exchange.fetch_ohlcv(symbol, interval, since=start_date_ms)
header = ["date", "open", "high", "low", "close", "volume"]
df = pd.DataFrame(ohlcv, columns=header)
df['date'] = pd.to_datetime(df['date'], unit='ms')
df.set_index("date", inplace=True)
# Drop the last row containing live data
df.drop(df.index[-1], inplace=True)
return df

Функция «fetch_asset_data» получает исторические ценовые данные, необходимые для тестирования на истории. Он принимает 4 аргумента — тикер, дату начала, период времени (например, дневной, часовой и т.д.) и биржу, на которой находится торговая пара.

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

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

Шаг 4: Рассчитайте линии супертренда

def supertrend(df, atr_multiplier=3):
# Calculate the Upper Band(UB) and the Lower Band(LB)
# Formular: Supertrend =(High+Low)/2 + (Multiplier)∗(ATR)
current_average_high_low = (df['high']+df['low'])/2
df['atr'] = ta.atr(df['high'], df['low'], df['close'], period=15)
df.dropna(inplace=True)
df['basicUpperband'] = current_average_high_low + (atr_multiplier * df['atr'])
df['basicLowerband'] = current_average_high_low - (atr_multiplier * df['atr'])
first_upperBand_value = df['basicUpperband'].iloc[0]
first_lowerBand_value = df['basicLowerband'].iloc[0]
upperBand = [first_upperBand_value]
lowerBand = [first_lowerBand_value]

for i in range(1, len(df)):
if df['basicUpperband'].iloc[i] < upperBand[i-1] or df['close'].iloc[i-1] > upperBand[i-1]:
upperBand.append(df['basicUpperband'].iloc[i])
else:
upperBand.append(upperBand[i-1])

if df['basicLowerband'].iloc[i] > lowerBand[i-1] or df['close'].iloc[i-1] < lowerBand[i-1]:
lowerBand.append(df['basicLowerband'].iloc[i])
else:
lowerBand.append(lowerBand[i-1])

df['upperband'] = upperBand
df['lowerband'] = lowerBand
df.drop(['basicUpperband', 'basicLowerband',], axis=1, inplace=True)
return df

Таким образом, эта функция «супертренда» берет датафрейм, который мы получили из предыдущей функции. Кроме того, ему нужна еще одна вещь — номер «atr_multiplier».

Он вычисляет средний истинный диапазон или ATR. Это говорит нам о том, насколько цена двигалась вверх и вниз каждый день или любой настроенный период, поэтому мы знаем, насколько «волатильным» или скачкообразным является рынок. Он использует ATR и число множителя для вычисления базовой верхней и базовой нижней полос в каждой строке.

Теперь он начинает смотреть на каждую строку, кроме самой первой. Для верхней полосы — если базовая верхняя полоса ниже, чем конечная верхняя полоса из предыдущей строки, ИЛИ если цена закрытия находится выше конечной верхней полосы из предыдущей строки, то к итоговому списку верхней полосы добавляется значение базовой верхней полосы.

Он делает то же самое для нижней полосы, просто проверяя, находится ли базовая нижняя полоса выше, чем последняя нижняя полоса из предыдущей строки, ИЛИ находится ли цена закрытия ниже последней нижней предыдущей.

Проверив каждую строку, он отбрасывает столбцы, которые нам больше не нужны, и возвращает кадр данных. Эти конечные полосы (df[‘upperband’] и df[‘lowerband’]) и цена закрытия подскажут нам, когда покупать или продавать.

Шаг 5: Сгенерируйте сигналы

def generate_signals(df):
# Intiate a signals list
signals = [0]

# Loop through the dataframe
for i in range(1 , len(df)):
if df['close'][i] > df['upperband'][i]:
signals.append(1)
elif df['close'][i] < df['lowerband'][i]:
signals.append(-1)
else:
signals.append(signals[i-1])

# Add the signals list as a new column in the dataframe
df['signals'] = signals
df['signals'] = df["signals"].shift(1) #Remove look ahead bias
return df

Функция «generate_signals» начинается с составления списка под названием «signals» и установки первого сигнала в 0. Затем он просматривает весь датафрейм строка за строкой, чтобы найти сигналы на покупку и продажу.

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

Если текущий сигнал совпадает с предыдущим, то сохраняем позицию такой же, как и в последней строке.

Каждый раз, когда он смотрит на новую строку, он обновляет список сигналов новым сигналом для этой строки. Затем он добавляет весь список сигналов в виде нового столбца в кадре данных с именем «df[‘signals’]«.

Он устраняет предвзятость заглядывания вперед, чтобы имитировать открытие позиций аналогично тому, как вы это делаете в реальной торговой среде после того, как вы увидели сигнал. Таким образом, мы не получаем сигналов, используя информацию из будущего, поэтому результаты тестирования на истории более реалистичны и надежны. Затем функции возвращают «df«, который будет использоваться для создания позиций.

Шаг 6: Сгенерируйте позиции

def create_positions(df):
# We need to shut off (np.nan) data points in the upperband where the signal is not 1
df['upperband'][df['signals'] == 1] = np.nan
# We need to shut off (np.nan) data points in the lowerband where the signal is not -1
df['lowerband'][df['signals'] == -1] = np.nan

# Create a positions list
buy_positions = [np.nan]
sell_positions = [np.nan]

# Loop through the dataframe
for i in range(1, len(df)):
# If the current signal is a 1 (Buy) & the it's not equal to the previous signal
# Then that is a trend reversal, so we BUY at that current market price
# We take note of the upperband value
if df['signals'][i] == 1 and df['signals'][i] != df['signals'][i-1]:
buy_positions.append(df['close'][i])
sell_positions.append(np.nan)
# If the current signal is a -1 (Sell) & the it's not equal to the previous signal
# Then that is a trend reversal, so we SELL at that current market price
elif df['signals'][i] == -1 and df['signals'][i] != df['signals'][i-1]:
sell_positions.append(df['close'][i])
buy_positions.append(np.nan)
else:
buy_positions.append(np.nan)
sell_positions.append(np.nan)

# Add the positions list as a new column in the dataframe
df['buy_positions'] = buy_positions
df['sell_positions'] = sell_positions
return df

Функция начинает с того, что избавляется от некоторых чисел в верхней и нижней полосах, которые нам не нужно видеть. Помните, как раньше рассчитывались эти верхняя и нижняя полосы? Что ж, иногда полосы продолжали показывать значения цены закрытия, даже когда сигнал не говорил о покупке или продаже. Это может привести к путанице позже, когда мы захотим просто посмотреть на полосы, где у нас действительно есть открытая позиция (тренд). Так что первые две строки в функции просто очищают нас от вещей. Он помещает «NaN» в те точки верхней полосы, где сигнал не равен «1», и в нижнюю полосу, где сигнал не равен «-1». Особенно, когда мы хотим построить полосы — вы не хотите строить целые линии, а только те точки, где тренд активен. Итак, вы знаете — «NaN» — это специальное число, которое означает «нет значения». Это будет иметь больше смысла по мере того, как мы будем двигаться дальше.

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

Таким образом, если сигнал равен «1» (что означает покупку) и отличается от предыдущего сигнала, это разворот, поэтому мы совершаем покупку. Он добавляет цену закрытия для этой строки в список покупок. Аналогичным образом, если сигнал «-1» (продажа) и отличается от предыдущего, это продажа. Он добавляет цену закрытия в список продаж. Если ничего не изменилось, он просто оставляет эти места пустыми («NaN») в списках.

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

Шаг 7: Визуализируйте данные

def plot_data(df, symbol):
# Define lowerband line plot
lowerband_line = mpf.make_addplot(df['lowerband'], label= "lowerband", color='green')
# Define upperband line plot
upperband_line = mpf.make_addplot(df['upperband'], label= "upperband", color='red')
# Define buy and sell markers
buy_position_makers = mpf.make_addplot(df['buy_positions'], type='scatter', marker='^', label= "Buy", markersize=80, color='#2cf651')
sell_position_makers = mpf.make_addplot(df['sell_positions'], type='scatter', marker='v', label= "Sell", markersize=80, color='#f50100')
# A list of all addplots(apd)
apd = [lowerband_line, upperband_line, buy_position_makers, sell_position_makers]
# Create fill plots
lowerband_fill = dict(y1=df['close'].values, y2=df['lowerband'].values, panel=0, alpha=0.3, color="#CCFFCC")
upperband_fill = dict(y1=df['close'].values, y2=df['upperband'].values, panel=0, alpha=0.3, color="#FFCCCC")
fills = [lowerband_fill, upperband_fill]
# Plot the data
mpf.plot(df, addplot=apd, type='candle', volume=True, style='charles', xrotation=20, title=str(symbol + ' Supertrend Plot'), fill_between=fills)

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

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

Он строит свечи для ohlv (открытие, максимум, минимум, закрытие), устанавливает заголовок и легенды, а затем показывает график. Теперь мы легко увидим, как бы эта стратегия разыгралась с течением времени.

Шаг 8: Рассчитайте эффективность стратегии

def strategy_performance(strategy_df, capital=100, leverage=1):
# Initialize the performance variables
cumulative_balance = capital
investment = capital
pl = 0
max_drawdown = 0
max_drawdown_percentage = 0

# Lists to store intermediate values for calculating metrics
balance_list = [capital]
pnl_list = [0]
investment_list = [capital]
peak_balance = capital

# Loop from the second row (index 1) of the DataFrame
for index in range(1, len(strategy_df)):
row = strategy_df.iloc[index]

# Calculate P/L for each trade signal
if row['signals'] == 1:
pl = ((row['close'] - row['open']) / row['open']) * \
investment * leverage
elif row['signals'] == -1:
pl = ((row['open'] - row['close']) / row['close']) * \
investment * leverage
else:
pl = 0

# Update the investment if there is a signal reversal
if row['signals'] != strategy_df.iloc[index - 1]['signals']:
investment = cumulative_balance

# Calculate the new balance based on P/L and leverage
cumulative_balance += pl

# Update the investment list
investment_list.append(investment)

# Calculate the cumulative balance and add it to the DataFrame
balance_list.append(cumulative_balance)

# Calculate the overall P/L and add it to the DataFrame
pnl_list.append(pl)

# Calculate max drawdown
drawdown = cumulative_balance - peak_balance
if drawdown < max_drawdown:
max_drawdown = drawdown
max_drawdown_percentage = (max_drawdown / peak_balance) * 100

# Update the peak balance
if cumulative_balance > peak_balance:
peak_balance = cumulative_balance

# Add new columns to the DataFrame
strategy_df['investment'] = investment_list
strategy_df['cumulative_balance'] = balance_list
strategy_df['pl'] = pnl_list
strategy_df['cumPL'] = strategy_df['pl'].cumsum()

# Calculate other performance metrics (replace with your calculations)
overall_pl_percentage = (
strategy_df['cumulative_balance'].iloc[-1] - capital) * 100 / capital
overall_pl = strategy_df['cumulative_balance'].iloc[-1] - capital
min_balance = min(strategy_df['cumulative_balance'])
max_balance = max(strategy_df['cumulative_balance'])

# Print the performance metrics
print("Overall P/L: {:.2f}%".format(overall_pl_percentage))
print("Overall P/L: {:.2f}".format(overall_pl))
print("Min balance: {:.2f}".format(min_balance))
print("Max balance: {:.2f}".format(max_balance))
print("Maximum Drawdown: {:.2f}".format(max_drawdown))
print("Maximum Drawdown %: {:.2f}%".format(max_drawdown_percentage))

# Return the Strategy DataFrame
return strategy_df

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

Теперь он устанавливает несколько пустых списков и переменных для отслеживания таких вещей, как баланс, прибыль и убытки (P&L), а также то, сколько денег мы инвестируем во время каждой позиции. Затем он просматривает каждую строку в нашем фрейме данных стратегии, одну за другой. Для каждого сигнала он рассчитывает P&L, видя, насколько изменилась цена актива между покупкой и продажей, а затем обновляет наш баланс, сумму инвестиций и добавляет новые цифры в списки. Он также отслеживает другие показатели, такие как самый высокий и самый низкий баланс, который у нас когда-либо был, общую прибыль, максимальную просадку и т. д. Этих, он распечатывает. Обратите внимание, что эта функция также увеличивает доходность, т.е. инвестицией является доступный баланс даже по всем закрытым позициям.

Шаг 9: Визуализируйте эффективность стратегии

def plot_performance_curve(strategy_df):
# Plot the strategy performace curve
plt.plot(strategy_df['cumulative_balance'], label='Strategy')
plt.title('Performance Curve')
plt.xlabel('Date')
plt.ylabel('Balance')
plt.xticks(rotation=70)
plt.legend()
plt.show()

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

Шаг 10: Запустите скрипт

if __name__ == '__main__':
# Initialize data fetch parameters
symbol = "BTC/USDT"
start_date = "2024-1-1"
interval = '4h'
exchange = ccxt.binance()

# Fetch historical OHLC data for ETH/USDT
data = fetch_asset_data(symbol=symbol, start_date=start_date, interval=interval, exchange=exchange)


volatility = 3

# Apply supertrend formula
supertrend_data = supertrend(df=data, atr_multiplier=volatility)

# Generate the Signals
supertrend_positions = generate_signals(supertrend_data)

# Generate the Positions
supertrend_positions = create_positions(supertrend_positions)

# Calculate performance
supertrend_df = strategy_performance(supertrend_positions, capital=100, leverage=1)
print(supertrend_df)

# Plot data
plot_data(supertrend_positions, symbol=symbol)

# Plot the performance curve
plot_performance_curve(supertrend_df)

Настройте переменные в конструкторе «if __name__ == ‘__main__’:», а затем доберитесь до конечной точки, перейдите в каталог, содержащий файл, и затем запустите скрипт.

Мой файл был supertrend.py, поэтому вы запускаете его, запустив «python supertrend.py» в своем терминале.

Вот результирующий датафрейм:

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

Как вы можете видеть, за период в 3 месяца, с инвестициями в размере 100 долларов и без кредитного плеча, BTC/USDT принес бы 40% прибыли от инвестиций. Не стесняйтесь рассчитывать другие метрики, такие как коэффициент Сортино, альфа и т.д.

А это был стратегический график (он немного уменьшен, поэтому некоторые точки данных могут быть обрезаны)

И, наконец, вот результирующий график для кривой производительности:

Ограничения стратегии

Параметр Senstivity: Индикатор супертренда может быть чувствителен к своим параметрам, таким как период и множитель волатильности atr. Различные торговые пары могут сильно меняться в течение определенного периода, но быть довольно спокойными в отношении других. Таким образом, то, что хорошо сработало для одной пары, может не работать так же хорошо для другой. Вы можете настроить параметры волатильности в соответствии с торговой парой.

Различия в волатильности: Индикатор SuperTrend в значительной степени полагается на ATR для захвата трендов, но если торговая пара настолько волатильна или испытывает частые резкие колебания цены, она может давать ложные сигналы или не улавливать значимые тренды. Поэтому индикатор супертренда может не подойти для высоковолатильных активов.

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

Вот полный код на моем Github

SuperTrend V.1 — Система линий супертренда

I. Происхождение истории

Г-н Ран, мой хороший друг, давно наблюдал за этим показателем и рекомендовал его мне перед Новым годом, чтобы обсудить, можно ли его перевести в количественную оценку.
Жаль, что прокрастинатор до сих пор медлит, чтобы помочь ему исполнить такое желание. На самом деле, мое понимание алгоритмов в последнее время быстро улучшилось.
Предполагается, что когда-нибудь я напишу переводчик для соснового языка. Все может быть python.
Ну и без лишних глупостей представим легендарную супертрендовую линию.

II. Введение в систему

В новом поколении интеллектуальной торговой системы в CMC Markets мы можем выбрать «Super Trend Line» из технических индикаторов для использования,
Мы можем настроить «цвет и толщину» возрастающих и уменьшающихся сигналов в соответствии с нашими собственными предпочтениями.
Так что же такое супертрендовый индикатор? Прежде чем понимать формулу индикатора супертренда, необходимо понять ATR, потому что супертренд принимает значение ATR для расчета значения индикатора.
Основные алгоритмы также описаны на следующем рисунке:

Взгляните вкратце. В основном он описывает канал, где HL2 (средняя цена k-линии) плюс n раз ATR. Совершите прорыв в тренде.
Но статья простая. Нет детально проработанного алгоритма. Затем я подумал о самом удивительном сообществе — TradingView.
Действительно, она действительно есть.

Глядя на график, он соответствует тренду. К сожалению, это всего лишь сигнал тревоги Alert.

III. Изучение исходного кода

Код не слишком длинный, поэтому попробуем его перевести. !(っ•̀ω•́)っ✎⁾⁾!

Полный код сосны приведен выше.

VI. Преобразование кода

Здесь мы создаем новую стратегию на FMZ, назовем ее SuperTrend

Далее мы установим два параметра, Factor и Pd

Чтобы лучше упростить работу с кодом и облегчить понимание, нам нужно использовать расширенный пакет расширения данных Python Pandas (https://pandas.pydata.org/)
FMZ теперь поддерживает эту библиотеку.

Нам нужно импортировать библиотеку панд и библиотеку времени.

В основной функции установите использование квартальных контрактов (в основном для okx).

Установите цикл doTicker() для обнаружения раз в 15 минут.
Запустите код в течение 15 минут.
Затем пишем основную стратегию в doTicker ().

import pandas as pd
import time

def main():
exchange.SetContractType("quarter")
preTime = 0
Log(exchange.GetAccount())
while True:
records = exchange.GetRecords(PERIOD_M15)
if records and records[-2].Time > preTime:
preTime = records[-2].Time
doTicker(records[:-1])
Sleep(1000 *60)

Нам нужно получить OHCLV K-line, поэтому мы используем GetRecords().

Мы импортируем полученные данные в pandas M15 = pd. DataFrame(records).

Нам нужно изменить заголовок таблицы. M15.columns = [‘время’,’открыть’,’высокий’,’низкий’,’закрыть’,’объем’,’OpenInterest’]
Фактически, он заключается в том, чтобы изменить начальные буквы «open», «high», «low» и «close» на строчные буквы, чтобы нашему последующему написанию кода не нужно было переходить с верхнего регистра на строчный.

def doTicker(records):
M15 = pd.DataFrame(records)
M15.columns = ['time','open','high','low','close','volume','OpenInterest']

Добавьте столбец в набор данных hl2 hl2=(высокий+низкий)/2.

#HL2
M15['hl2']=(M15['high']+M15['low'])/2

Тогда рассчитаем ATR.
Потому что для вычисления ATR необходимо импортировать переменную длину, значение которой равно Pd

Затем мы обратимся к руководству MyLanguage, и шаги алгоритма среднего значения реальной амплитуды колебаний ATR выглядят следующим образом:
TR : MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW));
ATR : RMA (TR, N)

Значение TR является максимальным из следующих трех отличий

  1. Колебание между самой высокой ценой и самой низкой ценой в текущий торговый день HIGH-LOW
  2. Колебание между ценой закрытия предыдущего торгового дня и самой высокой ценой текущего торгового дня REF (CLOSE, 1) — HIGH)
  3. Колебание между ценой закрытия предыдущего торгового дня и самой низкой ценой текущего торгового дня REF (CLOSE, 1) — LOW)
    Итак, TR: MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW));

В расчете Python

M15['prev_close']=M15['close'].shift(1)

Нам нужно настроить prev_close для получения данных close в предыдущей строке, то есть переместить close вправо на одну сетку для формирования нового параметра

ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]

Далее мы определяем промежуточную переменную, которая записывает массив из 3 контрастных значений для TR. (ВЫСОКИЙ-НИЗКИЙ) (высокий-prev_close) (низкий-prev_close)

M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)

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

alpha = (1.0 / length) if length > 0 else 0.5
M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()

Наконец, нам нужно вычислить значение ATR, ATR: RMA (TR, N). Установлено, что алгоритм RMA фактически является вариантом алгоритма EMA с фиксированным значением.
N — это переменная, которую мы импортируем. Параметр ATR по умолчанию равен 14. Здесь мы импортируем обратное значение alpha=length.

===

Затем алгоритм ewm используется для расчета ema
Полный процесс расчета ATR выглядит следующим образом:

#ATR(PD)
length=Pd
M15['prev_close']=M15['close'].shift(1)
ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]
M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)
alpha = (1.0 / length) if length > 0 else 0.5
M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()

9 Начните вычислять Up и Dn

M15['Up']=M15['hl2']-(Factor*M15['atr'])
M15['Dn']=M15['hl2']+(Factor*M15['atr'])

Up=hl2 -(Множитель * atr)
Dn=hl2 +(Множитель * atr)
Разве это не просто?

Вот раздел основного кода строк 15–21 из TV

TrendUp=close[1]>TrendUp[1]? max(Up,TrendUp[1]) : Up
TrendDown=close[1]<TrendDown[1]? min(Dn,TrendDown[1]) : Dn

Trend = close > TrendDown[1] ? 1: close< TrendUp[1]? -1: nz(Trend[1],1)
Tsl = Trend==1? TrendUp: TrendDown

linecolor = Trend == 1 ? green : red

Основная цель этого пункта состоит в том, чтобы указать, что:
Если он находится в бычьей стадии, (нижняя линия) TrendUp=max (Вверх, TrendUp [1])
Если он находится в стадии падения, (верхняя линия) TrendDown=min (Dn, TrendDown [1])
Иными словами, в тренде значение ATR использовало технологию, аналогичную стратегии Бандита Боллинджера.
Продолжайте сужать другую сторону канала

Здесь каждый расчет TrendUp и TrendDown требует самостоятельной итерации.
То есть каждый шаг должен быть рассчитан в соответствии с предыдущим шагом.
Поэтому набор данных должен быть итерирован в цикле.

Сначала мы создаем новые поля TrendUp, TrendDown, Trend, linecolor для набора данных. И дать им начальное значение
Затем мы используем грамматическую fillna (0), чтобы заполнить данные нулевым значением в ранее вычисленном результате с 0

M15['TrendUp']=0.0
M15['TrendDown']=0.0
M15['Trend']=1
M15['Tsl']=0.0
M15['linecolor']='Homily'
M15 = M15.fillna(0)

Включить цикл for
Использование троичной операции python в цикле

for x in range(len(M15)):

Рассчитать TrendUp
TrendUp = MAX(Up,TrendUp[-1]), если close[-1]>TrendUp[-1] else Up
Это примерно означает, что если предыдущее закрытие>предыдущее TrendUp истинно, будет взято максимальное значение между Up и предыдущим TrendUp; если нет, то будет взято значение Up и передано текущему TrendUp

M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]

Аналогичным образом рассчитайте TrendDown
TrendDown=min(Dn,TrendDown[-1]), если закрыть[-1]<TrendDown[-1] иначе Dn
Это примерно означает, что если предыдущее закрытие<предыдущий TrendDown истинен, будет взято минимальное значение между Dn и предыдущим TrendDown; если нет, то значение Dn будет взято и передано текущему TrendDown

M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]

Ниже приведен флаг для вычисления направления управления. Я упростил псевдокод
Trend= 1 if (close > TrendDown[-1]) else (x)
x = -1, если (close< TrendUp[-1]) else Trend[-1]

Смысл в том, что если цена закрытия> предыдущий TrendDown, возьмите значение 1 (бычье). Если нет, возьмите значение x
Если цена закрытия меньше предыдущего TrendUp, возьмите значение -1 (медвежье). Если нет, возьмите предыдущий тренд (то есть без изменений)
В переводе на язык изображения это то, что прорыв верхнего флага перехода трека для бычьего; и пробой нижнего флага перехода на медвежьи рельсы. В остальном время не изменится.

M15['Tsl'].values[x] = M15['TrendUp'].values[x] if  (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]

Вычислить tsl и linecolor
Tsl= rendUp, если (Trend==1), иначе TrendDown
Tsl — это значение, используемое для представления SuperTrend на изображении. Это означает отмечать нисходящую дорожку на изображении, когда мы находимся в бычьем настроении, и отмечать верхнюю дорожку на изображении, когда мы находимся в медвежьем настроении.
linecolor= ‘зеленый’, если (Trend==1) иначе ‘красный’
Значение цвета линии состоит в том, чтобы отметить зеленую линию, если мы находимся в бычьем настроении, и отметить пустой цвет, если мы находимся в медвежьем тренде (в основном для отображения Tradeview)

M15['Tsl'].values[x] = M15['TrendUp'].values[x] if  (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
M15['linecolor'].values[x]= 'green' if ( M15['Trend'].values[x]==1) else 'red'

Следующие 23–30 строк кода — это в основном чертежи графика, которые здесь не объясняются.

Наконец, есть 2 строки кода для управления сигналом покупки и продажи
В Tradeview это означает, что сигнал подается после разворота флага
Преобразуйте условный оператор в python.
Если последний флаг тренда меняется с -1 на 1, это означает, что верхнее сопротивление преодолено и открывается длинная позиция.
Если последний флаг тренда меняется с 1 на -1, это означает, что поддержка вниз превышена и открыта короткая позиция.

if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1):
Log('SuperTrend V.1 Alert Long',"Create Order Buy)
if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1):
Log('SuperTrend V.1 Alert Long',"Create Order Sell)

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

M15['TrendUp']=0.0
M15['TrendDown']=0.0
M15['Trend']=1
M15['Tsl']=0.0
M15['linecolor']='Homily'
M15 = M15.fillna(0)

for x in range(len(M15)):
M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]
M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]
M15['Trend'].values[x] = 1 if (M15['close'].values[x] > M15['TrendDown'].values[x-1]) else ( -1 if (M15['close'].values[x]< M15['TrendUp'].values[x-1])else M15['Trend'].values[x-1] )
M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
M15['linecolor'].values[x]= 'green' if ( M15['Trend'].values[x]==1) else 'red'

if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1):
Log('SuperTrend V.1 Alert Long',"Create Order Buy)
Log('Tsl=',Tsl)
if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1):
Log('SuperTrend V.1 Alert Long',"Create Order Sell)
Log('Tsl=',Tsl)

V. Полный код

Я скорректировал общую структуру кода.
И я объединил инструкции по ордерам, связанные с длинными и короткими позициями, в стратегию.
Вот полный код:

'''backtest
start: 2019-05-01 00:00:00
end: 2020-04-21 00:00:00
period: 15m
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"}]
'''

import pandas as pd
import time

def main():
exchange.SetContractType("quarter")
preTime = 0
Log(exchange.GetAccount())
while True:
records = exchange.GetRecords(PERIOD_M15)
if records and records[-2].Time > preTime:
preTime = records[-2].Time
doTicker(records[:-1])
Sleep(1000 *60)


def doTicker(records):
#Log('onTick',exchange.GetTicker())
M15 = pd.DataFrame(records)

#Factor=3
#Pd=7

M15.columns = ['time','open','high','low','close','volume','OpenInterest']

#HL2
M15['hl2']=(M15['high']+M15['low'])/2

#ATR(PD)
length=Pd
M15['prev_close']=M15['close'].shift(1)
ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]
M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)
alpha = (1.0 / length) if length > 0 else 0.5
M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()


M15['Up']=M15['hl2']-(Factor*M15['atr'])
M15['Dn']=M15['hl2']+(Factor*M15['atr'])

M15['TrendUp']=0.0
M15['TrendDown']=0.0
M15['Trend']=1
M15['Tsl']=0.0
M15['linecolor']='Homily'
M15 = M15.fillna(0)

for x in range(len(M15)):
M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]
M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]
M15['Trend'].values[x] = 1 if (M15['close'].values[x] > M15['TrendDown'].values[x-1]) else ( -1 if (M15['close'].values[x]< M15['TrendUp'].values[x-1])else M15['Trend'].values[x-1] )
M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
M15['linecolor'].values[x]= 'Long' if ( M15['Trend'].values[x]==1) else 'Short'

linecolor=M15['linecolor'].values[-2]
close=M15['close'].values[-2]
Tsl=M15['Tsl'].values[-2]


if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1):

Log('SuperTrend V.1 Alert Long','Create Order Buy')
Log('Tsl=',Tsl)
position = exchange.GetPosition()
if len(position) > 0:
Amount=position[0]["Amount"]
exchange.SetDirection("closesell")
exchange.Buy(_C(exchange.GetTicker).Sell*1.01, Amount);

exchange.SetDirection("buy")
exchange.Buy(_C(exchange.GetTicker).Sell*1.01, vol);

if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1):
Log('SuperTrend V.1 Alert Long','Create Order Sell')
Log('Tsl=',Tsl)
position = exchange.GetPosition()
if len(position) > 0:
Amount=position[0]["Amount"]
exchange.SetDirection("closebuy")
exchange.Sell(_C(exchange.GetTicker).Buy*0.99,Amount);
exchange.SetDirection("sell")
exchange.Sell(_C(exchange.GetTicker).Buy*0.99, vol*2);

Адрес публичной стратегии: https://www.fmz.com/strategy/200625

VI. Тестирование на истории и резюме

Мы отобрали данные за прошлый год для тестирования на истории.
Мы используем квартальный контракт OKEX в течение 15 минут.
Задаются следующие параметры:
Множитель=3
Pd=45
Vol=100 (100 контрактов на каждый заказ)
Доходность в годовом исчислении составляет около 33%.
Вообще говоря, вывод не очень большой,
Резкое снижение на 312 оказало относительно большое влияние на систему,
Если нет 312, отдача должна быть лучше.

VII. Напишите в конце

SuperTrend — очень хорошая торговая система

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

Я строю график вверх, dn, TrendUp и TrendDn отдельно на TradeView,
что облегчает понимание стратегии.
Видите это с первого взгляда:

Кроме того, на github есть версия js. Я не силен в js, но кажется, что что-то не так с оператором if.
Адрес: https://github.com/Dodo33/gekko-supertrend-strategy/blob/master/Supertrend.js

Совмещение индикатора SuperTrend со скользящими средними в торговой стратегии.

Индикатор обратного отсчета — это противоположный индикатор, который я разработал в основном для дискреционной торговли. Параллельно скользящие средние также могут намекать на возможный перегиб рынка через свои динамические уровни поддержки/сопротивления. Что, если мы попытаемся объединить их вместе, чтобы дать нам своевременный сигнал о том, когда должна произойти реакция. Это означает, что мы будем использовать сигналы от индикатора обратного отсчета и скользящей средней Фибоначчи. В первых двух частях будут представлены индикаторы, а в третьей части будет представлена стратегия. Исследование, как всегда, проводится на Python.

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

Понятие скользящих средних

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

Часовые значения EURUSD с 200-периодной простой скользящей средней.

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

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

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

Чтобы использовать его, нам нужен массив данных OHLC с дополнительным пустым столбцом. Это можно сделать с помощью следующего кода:

# Defining the function that adds a certain number of columns
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# Adding 1 extra column
my_data = adder(my_data, 1)# Calling the moving average function
my_data = ma(my_data, 200, 3, 4)

Выше указано, что функция скользящей средней будет вызываться на массиве с именем my_data для периода ретроспективного анализа, равного 200, на столбце, индексированном на 3 (цены закрытия в массиве OHLC). Затем значения скользящей средней будут помещены в столбец, проиндексированный на 4, который мы добавили с помощью функции сумматора.

Часовые значения USDCHF с 200-периодной простой скользящей средней.

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

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

Индикатор SuperTrend

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

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

  • Высокий — Низкий
  • | Высокий — Предыдущее закрытие |
  • | Предыдущее закрытие — Минимум |

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

def atr(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)
Data = deleter(Data, where, 1)
Data = jump(Data, lookback) return Data
Значения EURUSD M30 на первой панели с 14-периодным средним истинным диапазоном на второй панели.

Теперь, когда мы поняли, что такое ATR и как его рассчитать, мы можем перейти к индикатору SuperTrend.

Теперь, когда у нас есть план атаки, мы можем продолжить, поняв SuperTrend и закодировав его.

  1. Рассчитайте ATR, используя функцию, представленную выше.
  2. Используйте приведенные ниже формулы для расчета супертренда.
  3. Стройте график индикатора, создавайте торговые правила и анализируйте результаты.

Индикатор стремится предоставить уровни входа и выхода для последователей тренда. Вы можете думать об этом как о скользящей средней или MACD. Его уникальность является его главным преимуществом, и хотя его метод расчета намного сложнее, чем у двух других индикаторов, он интуитивно понятен по своей природе и не так сложен для понимания. По сути, у нас есть две переменные на выбор. Ретроспективный анализ ATR и значение мультипликатора. Первый — это просто период, используемый для вычисления ATR, а второй, как правило, является целым числом (обычно 2 или 3).

eATR выше в функции — это просто нормальный ATR с ретроспективным анализом, умноженным на 2, затем мы вычитаем 1. Но это только в иллюстративных целях, и единственное различие заключается в периоде ретроспективного анализа. Вы можете использовать 5 или 500, если хотите.

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

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

def deleter(Data, index, times):

for i in range(1, times + 1):

Data = np.delete(Data, index, axis = 1) return Datadef supertrend(Data, multiplier, lookback):

for i in range(len(Data)):

# Average Price
Data[i, 5] = (Data[i, 1] + Data[i, 2]) / 2
# Basic Upper Band
Data[i, 6] = Data[i, 5] + (multiplier * Data[i, 4])
# Lower Upper Band
Data[i, 7] = Data[i, 5] - (multiplier * Data[i, 4])

# Final Upper Band
for i in range(len(Data)):

if i == 0:
Data[i, 8] = 0

else:
if (Data[i, 6] < Data[i - 1, 8]) or (Data[i - 1, 3] > Data[i - 1, 8]):
Data[i, 8] = Data[i, 6]

else:
Data[i, 8] = Data[i - 1, 8]

# Final Lower Band
for i in range(len(Data)):

if i == 0:
Data[i, 9] = 0

else:
if (Data[i, 7] > Data[i - 1, 9]) or (Data[i - 1, 3] < Data[i - 1, 9]):
Data[i, 9] = Data[i, 7]

else:
Data[i, 9] = Data[i - 1, 9]

# SuperTrend
for i in range(len(Data)):

if i == 0:
Data[i, 10] = 0

elif (Data[i - 1, 10] == Data[i - 1, 8]) and (Data[i, 3] <= Data[i, 8]):
Data[i, 10] = Data[i, 8]

elif (Data[i - 1, 10] == Data[i - 1, 8]) and (Data[i, 3] > Data[i, 8]):
Data[i, 10] = Data[i, 9]

elif (Data[i - 1, 10] == Data[i - 1, 9]) and (Data[i, 3] >= Data[i, 9]):
Data[i, 10] = Data[i, 9]

elif (Data[i - 1, 10] == Data[i - 1, 9]) and (Data[i, 3] < Data[i, 9]):
Data[i, 10] = Data[i, 8]

# Cleaning columns
Data = deleter(Data, 5, 5)

return Data

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

Пример индикатора SuperTrend на паре EURUSD

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

Создание комбинированной стратегии

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

Условия комбинированной стратегии следующие:

  • Открывайте длинную позицию (покупка) всякий раз, когда рыночная цена поднимается выше индикатора SuperTrend, находясь выше скользящей средней. Удерживайте это положение до получения нового сигнала.
  • Открывайте короткую позицию (продажу) всякий раз, когда рыночная цена опускается ниже индикатора SuperTrend, находясь ниже скользящей средней. Удерживайте это положение до получения нового сигнала.
Сигнальный график по EURUSD по стратегии.
def signal(Data, close, super_trend_col, ma_col, buy, sell):

for i in range(len(Data)):

if Data[i, close] > Data[i, super_trend_col] and Data[i - 1, close] < Data[i - 1, super_trend_col] and Data[i, close] > Data[i, ma_col]:
Data[i, buy] = 1

if Data[i, close] < Data[i, super_trend_col] and Data[i - 1, close] > Data[i - 1, super_trend_col] and Data[i, close] < Data[i, ma_col]:
Data[i, sell] = -1
Сигнальный график на USDCHF по стратегии.

Несмотря на то, что приведенные выше сигналы показывают сигналы превосходного качества, результаты тестирования на истории были разочаровывающими (ретроспективный взгляд на 10 на SuperTrend с множителем 2 и взгляд на 21 на скользящую среднюю), однако для его улучшения можно провести большую оптимизацию. До тех пор стратегия хорошо работает в дискреционной торговле:

Кривые эквити в соответствии со стратегией.

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

Заключение

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

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

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

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

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

Источник

Источник

Источник