Скользящие средние

  • 200-дневная скользящая средняя
  • Стратегия пересечения скользящих средних
  • Экспоненциально взвешенные скользящие средние (EWMA)
  • КАМА: Адаптивная скользящая средняя
  • Определение разворотов в условиях перепроданности
  • Двухнаправленная трендовая стратегия скользящего стоп-лосса скользящей средней
  • Максимизация прибыли с помощью торговых стратегий пересечения скользящих средних

200-дневная скользящая средняя

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

Скользящие средние бывают различных видов: простые (Simple Moving Average — SMA), экспоненциальные (Exponential Moving Average — EMA), их производные. Все они имеют одно назначение – определение текущего тренда финансовых активов путем сглаживания колебаний и шума.

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

На рынке США, родине технического анализа, наиболее популярной является простая 200-дневная скользящая средняя. Она считается одним из ключевых технических индикаторов направления тенденции на рынке. Если ценовой график удерживается выше неё, значит на рынке доминируют покупатели, если же цена спускается ниже – жди распродаж.

Эта же средняя может успешно использоваться в качестве линии поддержки/сопротивления при определении оптимальной точки входа после отката.

Однако не все закономерности, которые отслеживают инвесторы и трейдеры на западных рынках, также хорошо работают на Мосбирже. В нашем исследовании мы взяли несколько популярных российских инструментов: Индекс МосБиржи, Индекс РТС, валютную пару USD/RUB_TOM, индекс гособлигаций RGBI, а также ряд ликвидных акций, в том числе бумаги Сбербанка и Газпрома.

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

Индекс МосБиржи оказался весьма чувствителен к индикатору, особенно в последние полгода. Сразу после достижения индексом средней следовал активный выкуп просадки. Можно сказать, что при отслеживании динамики IMOEX 200-дневная средняя – вполне рабочий инструмент.

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

А вот для индекса РТС индикатор оказался не столь полезен. За весь период исследования с 2007 г. 200-дневная скользящая игнорировалась участниками торгов и не давала явных сигналов. Можно смело сказать, что для этого инструмента индикатор непригоден.

Аналогичная ситуация с акциями Сбербанка и Газпрома, что оказалось несколько неожиданно. Если Газпром с 2010 г. стоит в боковике и высокой эффективности трендовых индикаторов здесь особо не ожидалось, то на графике Сбербанка индикатор вполне мог бы показать хороший результат. Несмотря на популярность среди нерезидентов и трендовый характер движения, рисунок графика Сбербанка возле средней не дает определенного сигнала о продолжении или окончании тенденции. Последним ярким примером стало падение «черного понедельника» в апреле, когда падение и откат акции происходили без малейшей оглядки на среднюю.

В противоположность акциям, валютный инструмент, задействованный в нашем исследовании, показал весьма неплохие результаты при использовании индикатора. Валютная пара USD/RUB с расчетами «завтра» с небольшой погрешностью достаточно четко сохраняла свое положение относительно средней, меняя его лишь в моменты существенного изменения настроений на рынке.

На графике хорошо различима фаза роста с 2013 по 2016 г. и фаза снижения с весны 2016 по весну 2018 г. Положение цены относительно скользящей средней хорошо демонстрирует настроения на рынке в периоды низкой волатильности. А в периоды сильных движений индикатор хорошо отрабатывает себя в качестве линии поддержки/сопротивления. С апреля 2018 г. курс валюты стабильно держится выше 200-дневной линии, указывая на негативные ожидания иностранных инвесторов относительно перспектив вложений в российские активы.

Несколько неожиданными оказались результаты тестирования индикатора на индексе гособлигаций РФ RGBI. Хотя облигации не входят в число инструментов, где активно применяется технический анализ, с конца 2015 г. до недавнего времени скользящая средняя выступала отличной линией поддержки, на которой заканчивались все более-менее значимые коррекции по индексу.

Возможно, такая тенденция связана с тем, что российские ОФЗ были весьма востребованы среди нерезидентов. Иностранные инвесторы при выборе цен для покупок ориентировались, в том числе и на этот технический индикатор. Сейчас индекс уверенно держится ниже индикатора, что является отражением сильных опасений рынка относительно санкций на госдолг РФ.

Подводя итоги исследования, мы получили следующий результат: на Индексе Мосбиржи, валютной паре USD/RUB_TOM и индексе гособлигаций RGBI 200-периодная скользящая средняя оказывается хорошим индикатором тренда и инструментом для определения оптимальных ценовых уровней входа в сделку после отката.

При анализе графиков ликвидных акций российского рынка четких закономерностей с использованием 200-дневной средней выявлено не было.

Стратегия пересечения скользящих средних

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

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

Технический подход к этой стратегии предполагает, что когда краткосрочная скользящая средняя (STMA) пересекает долгосрочную скользящую среднюю (LTMA), это является сигналом к покупке или открытию длинной позиции. И наоборот, когда STMA пересекает LTMA, это сигнал к продаже или открытию короткой позиции. Этот подход основан на принципе импульса, который гласит, что цена, движущаяся вверх или вниз в период t, скорее всего, продолжит движение в том же направлении в период t+1.

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

Базовая стратегия

Сначала импортируем библиотеки, которые будем использовать для анализа:import numpy as np
import pandas as pd
import yfinance as yf

import matplotlib.pyplot as plt
import plotly.graph_objs as go
import seaborn as sns

Мы используемyfinance (импортированную как yf), библиотеку, обеспечивающую легкий доступ к финансовым данным Yahoo Finance. С yfinance вы можете загружать исторические рыночные данные по акциям, опционам и валютам.#Download ticker price data from yfinance
tick = ‘ARKK’
ticker = yf.Ticker(tick)
ticker_history = ticker.history(period=’10y’)

Для этого примера мы проанализируем 10-летнюю дневную историю цен ARKK, тикера ARK Innovation ETF, но не стесняемся использовать и другие ценные бумаги.#Calculate 10 and 20 days moving averages
ticker_history[‘ma10’] = ticker_history[‘Close’].rolling(window=10).mean()
ticker_history[‘ma20’] = ticker_history[‘Close’].rolling(window=20).mean()

#Create a column with buy and sell signals
ticker_history[‘signal’] = 0.0
ticker_history[‘signal’] = np.where(ticker_history[‘ma10’] > ticker_history[‘ma20’], 1.0, 0.0)

#Calculate daily returns for the ticker
ticker_history[‘returns’] = ticker_history[‘Close’].pct_change()

#Calculate strategy returns
ticker_history[‘strategy_returns’] = ticker_history[‘signal’].shift(1) * ticker_history[‘returns’]

#Calculate cumulative returns for the ticker and the strategy
ticker_history[‘cumulative_returns’] = (1 + ticker_history[‘returns’]).cumprod()
ticker_history[‘cumulative_strategy_returns’] = (1 + ticker_history[‘strategy_returns’]).cumprod()

#Print the cumulative strategy returns at the last date
ticker_history[‘cumulative_strategy_returns’][-1]

2.690754730303438

Это говорит о том, что результат стратегии в анализируемом периоде составляет 269,075%. За тот же период стратегия «Купи и держи» принесла бы доходность в размере 203,12%

Мы можем создать график с кумулятивной доходностью для тикера и стратегии, примененной с Plotly.fig = go.Figure()
fig.add_trace(go.Scatter(x=ticker_history.index, y=ticker_history[‘cumulative_returns’], name=tick,
line=dict(color=’white’, width=2)))
fig.add_trace(go.Scatter(x=ticker_history.index, y=ticker_history[‘cumulative_strategy_returns’], name=’Estrategy’,
line=dict(color=’#22a7f0′, width=2)))
fig.update_layout(title=(‘Moving average crossover strategy backtest in ‘ + tick),
xaxis_title=’Date’,
yaxis_title=’Cumulative performance’,
font=dict(color=’white’),
paper_bgcolor=’black’,
plot_bgcolor=’black’,
legend=dict(x=0, y=1.2, bgcolor=’rgba(0,0,0,0)’),
yaxis=dict(gridcolor=’rgba(255,255,255,0.2)’),
xaxis=dict(gridcolor=’rgba(255,255,255,0.2)’))
fig.update_xaxes(showgrid=True, ticklabelmode=»period»)
fig.update_yaxes(showgrid=True)
fig.show()

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

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

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

Изучить просадку стратегии можно с помощью следующего кода:#Calculate the maximum cumulative returns of the strategy so far
ticker_history[‘cumulative_strategy_max’] = ticker_history[‘cumulative_strategy_returns’].cummax()

#Calculate the current drawdown of the strategy returns in relation to the maximum cumulative returns
ticker_history[‘cumulative_strategy_drawdown’] = (ticker_history[‘cumulative_strategy_returns’] / ticker_history[‘cumulative_strategy_max’]) — 1

#Print the current drawdown
print(‘The current drawdown of the strategy is:’, ticker_history[‘cumulative_strategy_drawdown’][-1])

Текущая просадка стратегии составляет: -0.5364383150776705

Но что, если мы изменим STMA на 20, а STMA на 40? Или STMA до 9 и STMA до 21? Учитывая, что наиболее распространенными скользящими средними являются 5, 7, 9, 10, 20, 21, 30, 40, 50, 100 и 200, можно проанализировать доходность и просадку различных комбинаций скользящих средних.

Возвращение матрицы с разными скользящими средними

Для того, чтобы создать матрицу, сначала разработаем аналогичную модель пересечения МА#Define base slow and fast moving averages
fast_ma = 10
slow_ma = 20

#Calculate the moving averages for the strategy
ticker_history[‘fast_ma’] = ticker_history[‘Close’].rolling(window=fast_ma).mean()
ticker_history[‘slow_ma’] = ticker_history[‘Close’].rolling(window=slow_ma).mean()

#Create a column with buy and sell signals
ticker_history[‘signal’] = np.where(ticker_history[‘fast_ma’] > ticker_history[‘slow_ma’], 1.0, 0.0)

# Calculate daily ticker and strategy returns
ticker_history[‘returns’] = ticker_history[‘Close’].pct_change()
ticker_history[‘strategy_returns’] = ticker_history[‘signal’].shift(1) * ticker_history[‘returns’]

Затем мы создадим нулевую матрицу с различными комбинациями скользящих средних, используя Pandas DataFrame:# Define two lists of fast and slow moving average values to be used as index and columns for a returns matrix
fast_ma_range = [5, 7, 9, 10, 20, 21, 30, 40, 50, 100]
slow_ma_range = [7, 9, 10, 20, 21, 30, 40, 50, 100, 200]

# Create a DataFrame with the index and columns as the fast and slow moving average values, respectively
# The DataFrame will be used to store returns data for different combinations of the moving averages
returns_matrix = pd.DataFrame(index=fast_ma_range, columns=slow_ma_range)

В результате получаем нулевую матрицу 10х10. Для наполнения его гипотетическими доходностями стратегий с разными комбинациями скользящих средних используем 2 цикла for:# Iterate through all combinations of fast and slow moving average values
for fast_ma in fast_ma_range:
for slow_ma in slow_ma_range:

# Calculate the fast and slow moving averages of the stock’s closing price
ticker_history[‘fast_ma’] = ticker_history[‘Close’].rolling(window=fast_ma).mean()
ticker_history[‘slow_ma’] = ticker_history[‘Close’].rolling(window=slow_ma).mean()

# Generate a signal to buy (1.0) or sell (0.0) based on the relative positions of the two moving averages
ticker_history[‘signal’] = np.where(ticker_history[‘fast_ma’] > ticker_history[‘slow_ma’], 1.0, 0.0)

# Calculate the daily returns of the stock and the strategy
ticker_history[‘strategy_returns’] = ticker_history[‘signal’].shift(1) * ticker_history[‘returns’]

# Calculate the cumulative returns of the strategy
cumulative_strategy_returns = (1 + ticker_history[‘strategy_returns’]).cumprod()

# Add the cumulative returns of the strategy to the returns matrix
returns_matrix.loc[fast_ma, slow_ma] = cumulative_strategy_returns[-1]

Теперь у нас есть полная матрица с доходностью стратегий с разными комбинациями скользящей средней. Для визуализации мы можем создать тепловую карту с помощью библиотеки Seaborn:# Make a copy of the returns_matrix and convert index and columns to string type
values = returns_matrix.copy()
values.index = values.index.astype(str)
values.columns = values.columns.astype(str)

# Convert values to float type
values = values.astype(float)

# Change the style of the plot and set the default color scheme
plt.style.use(‘dark_background’)
sns.set_palette(«bright»)

# Create a heatmap with the provided values, including annotations, using the RdYlGn color map
sns.heatmap(values, annot=True, cmap=’RdYlGn’)

# Add labels to the axes
plt.xlabel(‘Slow Moving Average’, fontsize=12, color=’white’)
plt.ylabel(‘Fast Moving Average’, fontsize=12, color=’white’)

# Add a title to the plot
plt.title(‘Heatmap of Returns’, fontsize=16, color=’white’)

# Increase the font size to 0.8 and change the color to white for all text elements
sns.set(font_scale=0.8, rc={‘text.color’:’white’, ‘axes.labelcolor’:’white’, ‘xtick.color’:’white’, ‘ytick.color’:’white’})

# Change the background color of the plot to black
fig = plt.gcf()
fig.set_facecolor(‘#000000’)

# Display the plot
plt.show()

Матрица возвращаемых значений

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

  1. STMA:50, LTMA:200 с совокупной доходностью 4,5 (451,48%)
  2. STMA:7, LTMA:50 с совокупной доходностью 4.2 (421.29%)

200, 50 и 40 кажутся хорошими вариантами для выбора LTMA для этой ценной бумаги, но не слишком ясно, чтобы выбрать хорошего кандидата для STMA.

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

Матрица просадок с разными скользящими средними

Создайте еще одну нулевую матрицу с различными комбинациями скользящих средних, используя Pandas DataFrame:# Define two lists of fast and slow moving average values to be used as index and columns for a returns matrix
fast_ma_range = [5, 7, 9, 10, 20, 21, 30, 40, 50, 100]
slow_ma_range = [7, 9, 10, 20, 21, 30, 40, 50, 100, 200]

# Create a DataFrame with the index and columns as the fast and slow moving average values, respectively
# The DataFrame will be used to store returns data for different combinations of the moving averages
dd_matrix = pd.DataFrame(index=fast_ma_range, columns=slow_ma_range)

В результате получаем нулевую матрицу 10х10. Для заполнения его гипотетическими просадками стратегий с разными комбинациями скользящих средних используем 2 цикла for:# Loop through different values for fast and slow moving averages
for fast_ma in fast_ma_range:
for slow_ma in slow_ma_range:

# Calculate the moving averages for the strategy
ticker_history[‘fast_ma’] = ticker_history[‘Close’].rolling(window=fast_ma).mean()
ticker_history[‘slow_ma’] = ticker_history[‘Close’].rolling(window=slow_ma).mean()

# Create a binary signal column for buy/sell signals
ticker_history[‘signal’] = np.where(ticker_history[‘fast_ma’] > ticker_history[‘slow_ma’], 1.0, 0.0)

# Calculate daily returns for the ticker and the strategy
ticker_history[‘strategy_returns’] = ticker_history[‘signal’].shift(1) * ticker_history[‘returns’]

# Calculate cumulative returns for the strategy
ticker_history[‘cumulative_strategy_returns’] = (1 + ticker_history[‘strategy_returns’]).cumprod()

# Calculate maximum cumulative returns for the strategy
ticker_history[‘cumulative_strategy_max’] = ticker_history[‘cumulative_strategy_returns’].cummax()

# Calculate the drawdown for the strategy
ticker_history[‘cumulative_strategy_drawdown’] = (ticker_history[‘cumulative_strategy_returns’] / ticker_history[‘cumulative_strategy_max’]) — 1

# Add drawdown to the matrix of drawdowns
dd_matrix.loc[fast_ma, slow_ma] = ticker_history[‘cumulative_strategy_drawdown’][-1]

Теперь у нас есть полная матрица с просадками стратегий с разными комбинациями скользящих средних. Для визуализации мы можем создать тепловую карту с помощью библиотеки Seaborn:# Make a copy of the drawdown matrix and convert the index and columns to string type
values_dd = dd_matrix.copy()
values_dd.index = values_dd.index.astype(str)
values_dd.columns = values_dd.columns.astype(str)

# Convert values to float type
values_dd = values_dd.astype(float)

# Change the style of the plot and set the default color scheme
plt.style.use(‘dark_background’)
sns.set_palette(«bright»)

# Create a heatmap with the provided values, including annotations, using the RdYlGn color map
sns.heatmap(values_dd, annot=True, cmap=’RdYlGn’)

# Add labels to the axes
plt.xlabel(‘Slow Moving Average’, fontsize=12, color=’white’)
plt.ylabel(‘Fast Moving Average’, fontsize=12, color=’white’)

# Add a title to the plot
plt.title(‘Heatmap of Drawdowns’, fontsize=16, color=’white’)

# Increase the font size to 0.8 and change the color to white for all text elements
sns.set(font_scale=0.8, rc={‘text.color’:’white’, ‘axes.labelcolor’:’white’, ‘xtick.color’:’white’, ‘ytick.color’:’white’})

# Change the background color of the plot to black
fig = plt.gcf()
fig.set_facecolor(‘#000000’)

# Display the plot
plt.show()

Матрица просадок

Опять же, мы видим положительные метрики в стратегиях, которые работают с 40 и 200 LTMA для этого актива. С другой стороны, матрица представляет высокие просадки для быстрых чисел, таких как STMA:5//LTMA:10 или STMA:7//LTMA:9.

Заключение

Благодаря проведенному исследованию можно наблюдать, как выбор различных скользящих средних может повлиять на результаты стратегии. Тем не менее, необходимо проявлять большую осторожность при параметризации возможных стратегий, поскольку исследование показывает результаты на прошлых котировках (см. In-sample vs out-of-sample для получения дополнительной информации).

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

Смотрите полный код на GitHub (испанская версия)

Экспоненциально взвешенные скользящие средние (EWMA)

Знакомство

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

Понимание экспоненциально взвешенных скользящих средних (EWMA) похоже на тонкую настройку радиоприемника в мире статики; Он вносит ясность в шум данных и раскрывает мелодии значимых идей.

Понимание экспоненциально взвешенных скользящих средних

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

Математическая формулировка

Формула EWMA относительно проста, но очень эффективна. Для X_t точек данных временных рядов EWMA вычисляется следующим образом:

EWMAt = α ∗ Xt + (1 − α) ∗ EWMAt − 1

Где:

  • EWMAt — экспоненциально взвешенная скользящая средняя в момент времени t.
  • Xt — точка данных в момент времени t.
  • α — параметр сглаживания, часто изменяющийся в диапазоне от 0 до 1.

Параметр α определяет вес, присваиваемый самой последней точке данных относительно существующей EWMA. Чем выше значение α, тем больше внимания уделяется последним данным, что делает EWMA более чутким к недавним изменениям. И наоборот, более низкое значение α делает EWMA более плавным и менее чувствительным к недавним колебаниям.

Практическое применение

  1. Финансовое прогнозирование: EWMA широко используется на финансовых рынках для прогнозирования цен на активы, волатильности и управления рисками. Это особенно полезно для моделирования изменяющегося во времени характера финансовых данных, которые часто демонстрируют тенденции и внезапные изменения.
  2. Контроль качества: В обрабатывающей промышленности и перерабатывающей промышленности EWMA может применяться для обнаружения аномалий в производственных данных. Отслеживая экспоненциально взвешенную скользящую среднюю ключевых параметров, можно быстро выявлять отклонения от ожидаемых значений, что приводит к ранним корректирующим действиям.
  3. Прогнозирование спроса: Компании используют EWMA для прогнозирования будущего спроса на свою продукцию. Придавая больший вес последним данным о продажах и принимая во внимание исторические тенденции, компании могут оптимизировать свои запасы и производственные графики.
  4. Анализ временных рядов: При общем анализе временных рядов EWMA помогает выявлять тенденции и сезонные закономерности даже при работе с зашумленными данными. Это важнейший инструмент для понимания поведения данных с течением времени.

Преимущества EWMA

  1. Адаптивность: EWMA обладает высокой адаптивностью и чувствительностью к изменениям данных, что делает ее пригодной для анализа временных рядов с непостоянными дисперсиями или трендами.
  2. Простота: Формула EWMA проста, что упрощает ее внедрение и интерпретацию даже для тех, кто не обладает достаточными знаниями в области статистики.
  3. Уменьшение задержек: EWMA уменьшает запаздывание, присущее традиционным скользящим средним, так как придает больший вес недавним наблюдениям. Это полезно в ситуациях, когда требуется немедленная реакция на изменение данных.
  4. Сглаживание: эффективно сглаживает зашумленные данные, выявляя основные тенденции и закономерности, не перегружая себя краткосрочными колебаниями.

Код

Ниже приведен пример кода Python, демонстрирующий, как рассчитать и визуализировать экспоненциально взвешенные скользящие средние (EWMA) с помощью Python с образцом набора данных и графиков. Для этого мы будем использовать популярные библиотеки Pandas, NumPy и Matplotlib.import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Create a sample dataset
data = {
‘Date’: pd.date_range(start=’2023-01-01′, periods=100, freq=’D’),
‘Value’: np.random.rand(100) * 100 # Random data for illustration
}

df = pd.DataFrame(data)
df.set_index(‘Date’, inplace=True)

# Set the smoothing parameter (alpha)
alpha = 0.2

# Calculate EWMA
df[‘EWMA’] = df[‘Value’].ewm(alpha=alpha, adjust=False).mean()

# Plot the original data and EWMA
plt.figure(figsize=(12, 6))
plt.plot(df.index, df[‘Value’], label=’Original Data’, color=’blue’)
plt.plot(df.index, df[‘EWMA’], label=f’EWMA (alpha={alpha})’, color=’red’)
plt.xlabel(‘Date’)
plt.ylabel(‘Value’)
plt.legend()
plt.title(‘Exponentially Weighted Moving Averages (EWMA)’)
plt.grid(True)

# Show the plot
plt.show()

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

Убедитесь, что вы установили необходимые библиотеки (Pandas, NumPy, Matplotlib), если вы еще не использовали pip install pandas numpy matplotlib. Этот код создаст график, показывающий исходные данные и линию EWMA для примера набора данных.

Заключение

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

КАМА: Адаптивная скользящая средняя

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

В стандартном курсе временных рядов, когда дело доходит до скользящих средних, простая скользящая средняя (SMA) и экспоненциально-взвешенная скользящая средняя (EWMA) всегда находятся в центре внимания. Расчеты для этих методов просты. В момент времени t одномерного временного ряда оглянемся назад на n периодов и возьмем среднее значение этих значений. В случае EWMA взвесьте более поздние значения времени t больше, чем старые. Эти методы помогают сглаживать действительно зашумленные данные временных рядов. Однако с КАМА он не только сглаживает данные, но и учитывает тренд и волатильность. Что вы имеете в виду под учетом тренда и волатильности?

Изображение предоставлено автором: NVIDA Stock Price

Ответ на этот вопрос можно увидеть на приведенном выше графике цены акций NVIDIA с марта 2023 года по середину ноября. Оранжевая линия — это наша рассчитанная цена КАМА с использованием 10-дневного периода ретроспективного анализа, 2-дневного быстрого периода и 30-дневного медленного периода. Что означают эти термины, будет объяснено в разделе, посвященном формулам этого поста. Синяя линия — это наша дневная скорректированная цена закрытия, а зеленая линия — это наша простая скользящая средняя с 10-дневным периодом ретроспективного анализа. Взгляните на период с июля по август. Что Вы видите? Сначала наблюдается восходящий тренд, за которым следует резкое снижение, снова короткий подъем вверх, а затем еще одно снижение. Если сравнивать тренд SMA с трендом KAMA, то SMA немного отслеживает движение исходной цены, в то время как KAMA остается неизменным на протяжении всего тренда. Это связано с тем, что КАМА отслеживает цену только тогда, когда волатильность или шум низкие. Однако, когда волатильность и шум усиливаются, он немного перестает отслеживать цену до тех пор, пока шум снова не уменьшится. Чтобы понять его внутреннюю работу, давайте углубимся в его формулу ниже.

Формула

Расчет КАМА начинается с коэффициента полезного действия, ER.

  • T — одномерная последовательность временных рядов, t ∈ {0, 1, …, T}
  • M – импульс цены, рассчитанный в момент времени t последовательности. Он вычисляется, беря текущую цену и вычитая ее на цену n периодов до этого, t — n. Где n — количество периодов для ретроспективного анализа.
  • V – волатильность заданных n периодов до времени t. Он вычисляется путем получения сначала абсолютного значения каждого изменения цены в последовательности предыдущих n периодов. И, наконец, подведение итогов этих изменений цен.

Учитывая формулу коэффициента полезного действия, значение будет находиться в диапазоне от 0 до 1. Значение коэффициента эффективности, близкое к 1, указывает на существенное изменение цены при низкой волатильности в предыдущие n ценовых периодов. С другой стороны, значение, близкое к 0, указывает на небольшое изменение цены по отношению к высокой волатильности в предыдущие n периодов. Теперь, когда коэффициент полезного действия рассчитан, нужно перейти к коэффициенту сглаживания.

Коэффициент сглаживания вычисляется на каждом временном периоде в ряду значений времени, T. Именно здесь вступают в игру наши быстрые и краткосрочные ценности. Это значения 2 и 30, упомянутые ранее. Эти краткосрочные и долгосрочные значения представляют собой простые скользящие средние за n периодов. Краткосрочные и долгосрочные константы рассчитываются ниже. Где — краткосрочный период, а l — долгосрочный.

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

После вычисления константы можно выполнить заключительный шаг вычисления КАМА. Формула для расчета приведена ниже.

Если вам больше нравится читать код, вот немного кода на python для вычисления КАМА.def kama_calc(price_series: pd.Series | np.array,
n_period: int=10,
period_fast: int=2,
period_slow: int=30) -> pd.Series | np.array:
«»»
Function calculates the Kaufman’s adaptive moving average on a univariate time series
Parameters
______________

price_series: pd.Series | np.array
The array of time series values.

n_period: int
The number of periods to look back from the current time. Sliding window

period_fast: int
The fast period paramter to calculate the fast period smoothing constant

period_slow: int
The slow period paramter to calculate the fast period smoothing constant,
should be greater than the fast parameter

Return
_____________

function returns the KAMA value arrays and the effiency ratio value
arrays

pd.Series | np.array, pd.Series | np.array
«»»

#Efficiency Ratio calculation
momentum = abs(price_series — price_series.shift(n_period))
volatility = (abs(price_series — price_series.shift())).rolling(n_period).sum()
er = momentum/volatility

#Smoothing Constant
sc_fatest = 2/(period_fast + 1)
sc_slowest = 2/(period_slow + 1)
smoothing_constant= (er * (sc_fatest — sc_slowest) + sc_slowest)**2

#KAMA
kama=np.zeros_like(price_series)
kama[n_period-1] = price_series[n_period-1]
for i in range(n_period, len(price_series)):
kama[i] = kama[i-1] + smoothing_constant[i] * (price_series[i] — kama[i-1])
kama[kama==0]=np.nan

return kama, er

Заключение

Моя общая критика этой скользящей средней заключается в том, что она интересна. Это все равно, что посыпать приправой адобо ваши типичные техники скользящего среднего. По мере того, как некоторые будущие работы по более глубокому пониманию этого метода, я хотел бы поиграть с взаимосвязью между быстро и медленно движущимися параметрами константы сглаживания, чтобы увидеть, как это влияет на результаты. Также в большинстве работ в интернете КАМА используется для сглаживания данных об уровне цен. Что касается того, почему он не преподается в общих курсах временных рядов, так это из-за дополнительной приправы, о которой я упоминал, полностью предполагая, кстати. SMA и EWMA являются основой для этих типов методов сглаживания временных рядов. Понимание их в первую очередь является переходом к пониманию этого. Что касается использования в торговых исследованиях, из того, что мне удалось найти в Интернете, многие трейдеры используют комбинацию более короткого временного окна для n с более длинным для поиска точек входа и выхода по акции. Действительно ли это практично, я по себе не уверен. Тем не менее, я вижу некоторые варианты использования, в которых коэффициент эффективности может быть использован в качестве характеристик при моделировании временных рядов, учитывая, что значения находятся в диапазоне от 0 до 1. Можно ли использовать ценности КАМА в качестве практических характеристик? Исходя из того, что я узнал на курсах по временным рядам, я бы предположил, что не учитывая возможность нестационарности. Если вы думаете иначе или видите какие-то другие варианты использования, которые не были упомянуты, не стесняйтесь оставлять комментарии к посту. Обратная связь здесь более чем приветствуется.

Смотреть:

[1]. Поморский и Д. Горс. Совершенствование модели регрессии с переключением Маркова за счет использования адаптивной скользящей средней. Препринт arXiv arXiv:2208.11574, 2022.

Определение разворотов в условиях перепроданности

rsi moving average strategy
Фото Алекса Раделича с сайта Unsplash

Выделите

  • Сложность статьи:★★☆☆☆
  • Данная статья посвящена разработке торговой стратегии (RSI Moving Average Strategy) с использованием дивергенции индекса относительной силы (RSI) и простых скользящих средних для определения точек входа и выхода.
  • Использование платформы тестирования на истории TQuant Lab для формулирования торговых стратегий и оценки связанных с ними рисков и производительности.

Предисловие

Индекс относительной силы (RSI) — это широко популярный осциллирующий технический индикатор, который рассчитывает относительную силу между покупателями и продавцами. Когда RSI выше 50, это указывает на более существенную покупательную способность; когда RSI ниже 50, это сигнализирует о более существенной силе продаж. В техническом анализе RSI, превышающий 70, обычно считается перекупленностью, в то время как RSI ниже 30 считается перепроданностью. Кроме того, RSI является опережающим индикатором, часто предугадывающим ценовые максимумы/минимумы, позволяя на ранних стадиях обнаруживать развороты рынка и облегчая противоположные торговые стратегии.

Кроме того, поскольку сам RSI не имеет информации о направлении, мы можем дополнить его скользящими средними для определения рынка, как показано ниже:

rsi moving average
Дивергенция RSI

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

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

Среда программирования

Эта статья написана в MacOS с использованием Jupyter Notebook в качестве основного редактора.

Импорт данных

import os
os.environ[‘TEJAPI_KEY’] = «your key»
os.environ[‘TEJAPI_BASE’] = «https://api.tej.com.tw»
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from zipline.data import bundles
from zipline.utils.calendar_utils import get_calendar
from zipline.sources.TEJ_Api_Data import get_Benchmark_Return

Период данных: с 1 января 2021 г. по 31 декабря 2023 г.

Используя функцию get_universe, получим пул акций секторов «Химическая биотехнология» и «Медицинская помощь». Кроме того, в качестве ориентира для сравнения рынка был включен индекс доходности химической биотехнологии и медицинской помощи (IX0019).from zipline.sources.TEJ_Api_Data import get_universe
start = ‘2021-01-01’
end = ‘2023-12-31’
pool = get_universe(start, end, mkt = ‘TWSE’, stktp_e = ‘Common Stock’, main_ind_e = ‘M1700 Chemical Biotechnology & Medical Care’)
print(f’total stock: {len(pool)}:\n’, pool)

start_dt, end_dt = pd.Timestamp(start, tz=’utc’), pd.Timestamp(end, tz=’utc’)
tickers = ‘ ‘.join(pool)
os.environ[‘ticker’] = tickers+’ IX0019′
os.environ[‘mdate’] = start+’ ‘+end
!zipline ingest -b tquant

bundle = bundles.load(‘tquant’)
benchmark_asset = bundle.asset_finder.lookup_symbol(‘IX0019’,as_of_date = None)

Выбранный пул акций

Правила торговли по стратегии RSI Moving Average

  • Сигнал на покупку:

Когда RSI ниже 30, наклон RSI положительный, а скользящая средняя цены акции снижается, что указывает на усиление покупателей и нижнюю точку цены акции. В этом сценарии инициируйте покупку 1000 акций.

  • Дополнительный сигнал на покупку:

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

  • Сигнал на продажу:

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

Реализуйте торговую стратегию

Реализуем стратегию RSI Moving Average с функцией run_algorithm() установив торговый период от start_time (2021-01-01) до end_time (2023-12-31). Используйте набор данных tquant и инициализируйте starting_cash до 5 000 000 NTD. Выходные данные, или results, будут представлять ежедневную производительность и подробные записи транзакций.from zipline import run_algorithm
results = run_algorithm(
start = start_dt,
end = end_dt,
initialize=initialize,
bundle=’tquant’,
analyze=analyze,
capital_base=5e6,
handle_data = handle_data
)
results

rsi moving average
Детали транзакции

Использование Pyfolio для оценки производительности

import pyfolio as pf
from pyfolio.utils import extract_rets_pos_txn_from_zipline

returns, positions, transactions = pf.utils.extract_rets_pos_txn_from_zipline(results)
benchmark_rets = results.benchmark_return

# Creating a Full Tear Sheet
pf.create_full_tear_sheet(returns, positions = positions, transactions = transactions,
benchmark_rets = benchmark_rets,
round_trips=False)

Сравнительный график производительности тестирования на истории с рынком

При использовании стратегии RSI Moving Average мы наблюдаем годовую доходность примерно в 12,32% за эти 34 месяца, накапливая общую доходность, близкую к 40%. Общая производительность превосходит рыночный бенчмарк. Однако опора стратегии на обратные операции, основанные на колебаниях цен на акции, приводит к более высокой волатильности, достигающей около 17%. Что касается максимальной просадки, то заметно, что в целом рынок демонстрировал плохие показатели в 3 и 4 кварталах 2022 года из-за таких факторов, как повышение процентных ставок ФРС и влияние пандемии COVID-19. Как следствие, показатель доходности стратегии RSI Moving Average в этот период испытал небольшое снижение.

rsi moving average
Ежемесячная доходность

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

Напоминаем, что эта стратегия приведена только для справки и не относится к какому-либо продукту или инвестиционному совету.

Исходный код

https://medium.com/media/eaf2c9f7d0160f8c16074936a838a025

Двухнаправленная трендовая стратегия скользящего стоп-лосса скользящей средней

Обзор

Стратегия сочетает в себе индикаторы Super Trend, SSL Hybrid Baseline Channel и QQE для реализации двунаправленного отслеживания позиций, стоп-лосса и захвата средне- и долгосрочных трендов.

Логика стратегии

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

  1. Используйте индикатор Super Trend, чтобы определить общее направление тренда и помочь в определении времени входа.
  2. Используйте базовый канал SSL Hybrid для определения конкретных точек входа. Пробой канала служит основным сигналом на вход.
  3. Используйте пересечения индикаторов QQE для подтверждения сигналов на вход.
  4. Используйте индикатор ATR для расчета стоп-лосса и тейк-профита.
  5. Используйте управление рисками на основе процента и динамическую корректировку стопов для контроля риска по сделке.

Логика входа требует разворота Super Trend вместе с пробоем ценой базового канала и пересечением индикатора QQE перед входом в позицию.

Эта комбинированная система индикаторов может эффективно контролировать время входа и избегать ненужных входов во время оттока рынка.

Логика выхода проста — сигналом к закрытию позиций служит разворот Super Trend, а также сработавшие стоп-лоссы и тейк-профиты.

Анализ преимуществ

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

Еще одним важным моментом является использование процентных стопов для контроля риска по сделке.

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

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

Использование трейлинг-стопа для фиксации прибыли также является ключевой частью повышения доходности.

Анализ риска

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

Когда Super Trend показывает ложный пробой или QQE дает неверные кроссы, стратегии становится легко ошибочно входить в позиции и сталкиваться с повышенной вероятностью стоп-аутов.

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

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

Возможности оптимизации

Остается место для дальнейших улучшений:

  1. Тестируйте комбинации с большим количеством индикаторов, например, добавляя осциллятор KD.
  2. Исследуйте стабильность параметров при различных конфигурациях.
  3. Попробуйте методы автоматической оптимизации, такие как машинное обучение.
  4. Внедрите адаптивную логику стоп-стопов для регулировки стоп-дистанции в зависимости от волатильности рынка.
  5. Постройте логику повторного входа, чтобы уменьшить упущенные возможности после срабатывания остановок.

Исходный код стратегии

/*backtest
start: 2024-02-22 00:00:00
end: 2024-02-27 00:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © fpemehd
// Thanks to W3MCT - @simonFUTURE2 w3mct.com -
// @version=5
strategy(title = '[D] SLH W3MCT combo Indicator',
shorttitle = '[D] SLH[swing-low-high] W3MCT',
overlay = true,
pyramiding = 0,
currency = currency.USD,
default_qty_type = strategy.percent_of_equity,
default_qty_value = 100,
commission_value = 0.1,
initial_capital = 10000,
max_bars_back = 500,
max_lines_count = 150,
max_labels_count = 300)

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// Time, Direction, Etc - Basic Settings Inputs
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// 1. Time: Based on UTC +09:00
i_start = input (defval = timestamp("20 Jan 2009 00:00 +0900"), title = "Start Date", tooltip = "Choose Backtest Start Date", inline = "Start Date", group = "Time" )
i_end = input (defval = timestamp("20 Dec 2030 00:00 +0900"), title = "End Date", tooltip = "Choose Backtest End Date", inline = "End Date", group = "Time" )
inTime = true

// 2. Inputs for direction: Long? Short? Both?
i_longEnabled = input.bool (defval = true , title = "Long?", tooltip = "Enable Long Position Trade?", inline = "Long / Short", group = "Long / Short" )
i_shortEnabled = input.bool (defval = true , title = "Short?", tooltip = "Enable Short Position Trade?", inline = "Long / Short", group = "Long / Short" )

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// Filter - Inputs, Indicaotrs
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// 3. Use Filters? What Filters?
//// 3-1. ATR Filter
i_ATRFilterOn = input.bool (defval = false , title = "ATR Filter On?", tooltip = "ATR Filter On? Order will not be made unless filter condition is fulfilled", inline = "1", group = "Filters")
i_ATRFilterLen = input.int (defval = 9, title = "Length for ATR Filter", minval = 1 , maxval = 100 , step = 1 , tooltip = "", inline = "2", group = "Filters")
i_ATRSMALen = input.int (defval = 27, title = "SMA Length for ATR SMA", minval = 1 , maxval = 100000 , step = 1 , tooltip = "ATR should be bigger than this", inline = "2", group = "Filters")
bool ATRFilter = ta.atr(i_ATRFilterLen) >= ta.sma(ta.atr(length = i_ATRFilterLen), i_ATRSMALen) ? true : false

//// 3-2. EMA Filter
i_EMAFilterOn = input.bool (defval = false , title = "EMA Filter On?", tooltip = "EMA Filter On? Order will not be made unless filter condition is fulfilled", inline = "3", group = "Filters")
i_EMALen = input.int (defval = 122, title = "EMA Length", minval = 1 , maxval = 100000 , step = 1 , tooltip = "EMA Length", inline = "4", group = "Filters")
bool longEMAFilter = close >= ta.ema(source = close, length = i_EMALen) ? true : false
bool shortEMAFilter = close <= ta.ema(source = close, length = i_EMALen) ? true : false
plot(i_EMAFilterOn ? ta.ema(source = close, length = i_EMALen) : na, title = "EMA Filter", color = color.new(color = color.orange , transp = 0), linewidth = 1)

//// 3-3. ADX Filter
////3-4. DMI Filter (Uses same ADX Length)
i_ADXFilterOn = input.bool (defval = false , title = "ADX Filter On?", tooltip = "ADX Filter On? Order will not be made unless filter condition is fulfilled", inline = "5", group = "Filters")
i_DMIFilterOn = input.bool (defval = false , title = "DMI Filter On?", tooltip = "DMI (Directional Moving Index) Filter On? Order will not be made unless filter condition is fulfilled", inline = "6", group = "Filters")
i_ADXLength = input.int (defval = 18, title = "ADX Length", minval = 1 , maxval = 100000 , step = 1 , tooltip = "ADX Length", inline = "7", group = "Filters")
i_ADXThreshold = input.int (defval = 36, title = "ADX Threshold", minval = 1 , maxval = 100000 , step = 1 , tooltip = "ADX should be bigger than threshold", inline = "8", group = "Filters")

//// 3-4. SuperTrend Filter
// i_superTrendFilterOn = input.bool (defval = false , title = "Super Trend Filter On?", tooltip = "Super Trend Filter On? Order will not be made unless filter condition is fulfilled", inline = "9", group = "Filters")
// i_superTrendATRLen = input.int (defval = 10, title = "ATR Length", minval = 1 , maxval = 100000 , step = 1 , tooltip = "Super Trend ATR Length", inline = "10", group = "Filters")
// i_superTrendATRFactor = input.float (defval = 3, title = "Factor", minval = 1 , maxval = 100000 , step = 0.1 , tooltip = "Super Trend ATR Factor", inline = "11", group = "Filters")

// ADX and DI Thanks to @BeikabuOyaji
int len = i_ADXLength
float th = i_ADXThreshold

TR = math.max(math.max(high - low, math.abs(high - nz(close[1]))), math.abs(low - nz(close[1])))
DMPlus = high - nz(high[1]) > nz(low[1]) - low ? math.max(high - nz(high[1]), 0) : 0
DMMinus = nz(low[1]) - low > high - nz(high[1]) ? math.max(nz(low[1]) - low, 0) : 0

SmoothedTR = 0.0
SmoothedTR := nz(SmoothedTR[1]) - nz(SmoothedTR[1]) / len + TR

SmoothedDMPlus = 0.0
SmoothedDMPlus := nz(SmoothedDMPlus[1]) - nz(SmoothedDMPlus[1]) / len + DMPlus

SmoothedDMMinus = 0.0
SmoothedDMMinus := nz(SmoothedDMMinus[1]) - nz(SmoothedDMMinus[1]) / len + DMMinus

DIPlus = SmoothedDMPlus / SmoothedTR * 100
DIMinus = SmoothedDMMinus / SmoothedTR * 100
DX = math.abs(DIPlus - DIMinus) / (DIPlus + DIMinus) * 100
ADX = ta.sma(source = DX, length = len)

// plot(DIPlus, color=color.new(color.green, 0), title='DI+')
// plot(DIMinus, color=color.new(color.red, 0), title='DI-')
// plot(ADX, color=color.new(color.navy, 0), title='ADX')
// hline(th, color=color.white)

bool ADXFilter = ADX > th ? true : false
bool longDMIFilter = DIPlus >= DIMinus ? true : false
bool shortDMIFilter = DIPlus <= DIMinus ? true : false

// Calculate Super Trend for Filter
// i_superTrendFilterOn = input.bool (defval = false , title = "Super Trend Filter On?", tooltip = "Super Trend Filter On? Order will not be made unless filter condition is fulfilled", inline = "9", group = "Filters")
// i_superTrendATRLen = input.int (defval = 10, title = "ATR Length", minval = 1 , maxval = 100000 , step = 1 , tooltip = "Super Trend ATR Length", inline = "10", group = "Filters")
// i_superTrendATRFactor = input.float (defval = 3, title = "Factor", minval = 1 , maxval = 100000 , step = 0.1 , tooltip = "Super Trend ATR Factor", inline = "11", group = "Filters")
// [supertrend, direction] = ta.supertrend(factor = i_superTrendATRFactor, atrPeriod = i_superTrendATRLen)
// bodyMiddle = plot((open + close) / 2, display=display.none)
// upTrend = plot(i_superTrendFilterOn ? direction < 0 ? supertrend : na : na, "Up Trend", color = color.green, style=plot.style_linebr)
// downTrend = plot(i_superTrendFilterOn ? direction < 0 ? na : supertrend : na, "Down Trend", color = color.red, style=plot.style_linebr)
// fill(bodyMiddle, upTrend, color.new(color.green, 90), fillgaps=false)
// fill(bodyMiddle, downTrend, color.new(color.red, 90), fillgaps=false)
// bool longSTFilter = direction <= 0
// bool shortSTFilter = direction >= 0

// Filter
bool longFilterFilled = (not i_ATRFilterOn or ATRFilter) and (not i_EMAFilterOn or longEMAFilter) and (not i_ADXFilterOn or ADXFilter) and (not i_DMIFilterOn or longDMIFilter) // and (not i_superTrendFilterOn or longSTFilter)
bool shortFilterFilled = (not i_ATRFilterOn or ATRFilter) and (not i_EMAFilterOn or shortEMAFilter) and (not i_ADXFilterOn or ADXFilter) and (not i_DMIFilterOn or shortDMIFilter) // and (not i_superTrendFilterOn or shortSTFilter)

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// Strategy Logic (Entry & Exit Condition) - Inputs, Indicators for Strategy
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
//// Indicators
// Inputs for Strategy Indicators
//// 1. Super Trend
i_superTrendATRLen = input.int (defval = 21, title = "ATR Length", minval = 1 , maxval = 100000 , step = 1 , tooltip = "Super Trend ATR Length", inline = "1", group = "1: SuperTrend")
i_superTrendATRFactor = input.float (defval = 8, title = "Factor", minval = 1 , maxval = 100000 , step = 0.1 , tooltip = "Super Trend ATR Factor", inline = "2", group = "1: SuperTrend")
[supertrend, direction] = ta.supertrend(factor = i_superTrendATRFactor, atrPeriod = i_superTrendATRLen)

//// 2. SSL Hybrid Baseline
i_useTrueRange = input.bool (defval = true, title = "use true range for Keltner Channel?", tooltip = "", inline = "1", group = "2: SSL Hybrid")
i_maType = input.string (defval ='EMA', title='Baseline Type', options=['SMA', 'EMA', 'DEMA', 'TEMA', 'LSMA', 'WMA', 'VAMA', 'TMA', 'HMA', 'McGinley'], inline="2", group = "2: SSL Hybrid")
i_len = input.int (defval =30, title='Baseline Length', inline="2", group = "2: SSL Hybrid")
i_multy = input.float (defval = 0.2, title='Base Channel Multiplier', minval = 0, maxval = 100, step=0.05, inline="3", group = "2: SSL Hybrid")
i_volatility_lookback = input.int (defval =42, title='Volatility lookback length(for VAMA)', inline='4',group="2: SSL Hybrid")

tema(src, len) =>
ema1 = ta.ema(src, len)
ema2 = ta.ema(ema1, len)
ema3 = ta.ema(ema2, len)
3 * ema1 - 3 * ema2 + ema3

f_ma(type, src, len) =>
float result = 0
if type == 'TMA'
result := ta.sma(ta.sma(src, math.ceil(len / 2)), math.floor(len / 2) + 1)
result
if type == 'LSMA'
result := ta.linreg(src, len, 0)
result
if type == 'SMA' // Simple
result := ta.sma(src, len)
result
if type == 'EMA' // Exponential
result := ta.ema(src, len)
result
if type == 'DEMA' // Double Exponential
e = ta.ema(src, len)
result := 2 * e - ta.ema(e, len)
result
if type == 'TEMA' // Triple Exponential
e = ta.ema(src, len)
result := 3 * (e - ta.ema(e, len)) + ta.ema(ta.ema(e, len), len)
result
if type == 'WMA' // Weighted
result := ta.wma(src, len)
result
if type == 'VAMA' // Volatility Adjusted
/// Copyright © 2019 to present, Joris Duyck (JD)
mid = ta.ema(src, len)
dev = src - mid
vol_up = ta.highest(dev, i_volatility_lookback)
vol_down = ta.lowest(dev, i_volatility_lookback)
result := mid + math.avg(vol_up, vol_down)
result
if type == 'HMA' // Hull
result := ta.wma(2 * ta.wma(src, len / 2) - ta.wma(src, len), math.round(math.sqrt(len)))
result
if type == 'McGinley'
mg = 0.0
mg := na(mg[1]) ? ta.ema(src, len) : mg[1] + (src - mg[1]) / (len * math.pow(src / mg[1], 4))
result := mg
result
result

//// 2-1. SSL Hybrid Keltner Baseline Channel
BBMC = f_ma (i_maType, close, i_len) // BaseLone
Keltma = f_ma (i_maType, close, i_len)
range_1 = i_useTrueRange ? ta.tr : high - low
rangema = ta.ema(range_1, i_len)
upperk = Keltma + rangema * i_multy
lowerk = Keltma - rangema * i_multy

//// 3. QQE MOD, thanks to Mihkel100
RSI_Period = input.int (defval = 11, title = 'RSI Length', inline = "1", group = "3: QQE MOD")
SF = input.int (defval = 9, title = 'RSI Smoothing', inline = "2", group = "3: QQE MOD")
QQE = input.float (defval = 4, title = 'Fast QQE Factor', inline = "3", group = "3: QQE MOD")
ThreshHold = input.int (defval = 4, title = 'Thresh-hold', inline = "4", group = "3: QQE MOD")
src = input (defval = low, title='RSI Source')

Wilders_Period = RSI_Period * 2 - 1


Rsi = ta.rsi(src, RSI_Period)
RsiMa = ta.ema(Rsi, SF)
AtrRsi = math.abs(RsiMa[1] - RsiMa)
MaAtrRsi = ta.ema(AtrRsi, Wilders_Period)
dar = ta.ema(MaAtrRsi, Wilders_Period) * QQE

longband = 0.0
shortband = 0.0
trend = 0

DeltaFastAtrRsi = dar
RSIndex = RsiMa
newshortband = RSIndex + DeltaFastAtrRsi
newlongband = RSIndex - DeltaFastAtrRsi
longband := RSIndex[1] > longband[1] and RSIndex > longband[1] ? math.max(longband[1], newlongband) : newlongband
shortband := RSIndex[1] < shortband[1] and RSIndex < shortband[1] ? math.min(shortband[1], newshortband) : newshortband
cross_1 = ta.cross(longband[1], RSIndex)
trend := ta.cross(RSIndex, shortband[1]) ? 1 : cross_1 ? -1 : nz(trend[1], 1)
FastAtrRsiTL = trend == 1 ? longband : shortband
////////////////////

length = input.int (defval = 42, minval = 1, title = 'Bollinger Length', group = "3: QQE MOD")
mult = input.float (defval = 0.27, minval = 0.01, maxval = 5, step = 0.1, title = 'BB Multiplier', group = "3: QQE MOD")

basis = ta.sma(FastAtrRsiTL - 50, length)
dev = mult * ta.stdev(FastAtrRsiTL - 50, length)
upper = basis + dev
lower = basis - dev
color_bar = RsiMa - 50 > upper ? #00c3ff : RsiMa - 50 < lower ? #00ff33 : color.rgb(19, 67, 239, 13)


//
// Zero cross
QQEzlong = 0
QQEzlong := nz(QQEzlong[1])
QQEzshort = 0
QQEzshort := nz(QQEzshort[1])
QQEzlong := RSIndex >= 50 ? QQEzlong + 1 : 0
QQEzshort := RSIndex < 50 ? QQEzshort + 1 : 0
//

// Zero = hline(0, color=color.white, linestyle=hline.style_dotted, linewidth=1)

////////////////////////////////////////////////////////////////
RSI_Period2 = input.int (defval = 6, title = 'RSI 2 Length', group = "3: QQE MOD")
SF2 = input.int (defval = 5, title = 'RSI Smoothing', group = "3: QQE MOD")
QQE2 = input.float (defval = 1.61, title = 'Fast QQE2 Factor', group = "3: QQE MOD")
ThreshHold2 = input.int (defval = 3, title = 'Thresh-hold', group = "3: QQE MOD")
src2 = input (defval = close, title = 'RSI Source', group = "3: QQE MOD")
//

//
Wilders_Period2 = RSI_Period2 * 2 - 1


Rsi2 = ta.rsi(src2, RSI_Period2)
RsiMa2 = ta.ema(Rsi2, SF2)
AtrRsi2 = math.abs(RsiMa2[1] - RsiMa2)
MaAtrRsi2 = ta.ema(AtrRsi2, Wilders_Period2)
dar2 = ta.ema(MaAtrRsi2, Wilders_Period2) * QQE2
longband2 = 0.0
shortband2 = 0.0
trend2 = 0

DeltaFastAtrRsi2 = dar2
RSIndex2 = RsiMa2
newshortband2 = RSIndex2 + DeltaFastAtrRsi2
newlongband2 = RSIndex2 - DeltaFastAtrRsi2
longband2 := RSIndex2[1] > longband2[1] and RSIndex2 > longband2[1] ? math.max(longband2[1], newlongband2) : newlongband2
shortband2 := RSIndex2[1] < shortband2[1] and RSIndex2 < shortband2[1] ? math.min(shortband2[1], newshortband2) : newshortband2
cross_2 = ta.cross(longband2[1], RSIndex2)
trend2 := ta.cross(RSIndex2, shortband2[1]) ? 1 : cross_2 ? -1 : nz(trend2[1], 1)
FastAtrRsi2TL = trend2 == 1 ? longband2 : shortband2


//
// Zero cross
QQE2zlong = 0
QQE2zlong := nz(QQE2zlong[1])
QQE2zshort = 0
QQE2zshort := nz(QQE2zshort[1])
QQE2zlong := RSIndex2 >= 50 ? QQE2zlong + 1 : 0
QQE2zshort := RSIndex2 < 50 ? QQE2zshort + 1 : 0
//

hcolor2 = RsiMa2 - 50 > ThreshHold2 ? color.silver : RsiMa2 - 50 < 0 - ThreshHold2 ? color.silver : na

Greenbar1 = RsiMa2 - 50 > ThreshHold2
Greenbar2 = RsiMa - 50 > upper

Redbar1 = RsiMa2 - 50 < 0 - ThreshHold2
Redbar2 = RsiMa - 50 < lower


// Plot: Indicators
//// 1. Super Trend
bodyMiddle = plot((open + close) / 2, display=display.none)
upTrend = plot(direction < 0 ? supertrend : na, "Up Trend", color = color.green, style=plot.style_linebr)
downTrend = plot(direction < 0 ? na : supertrend, "Down Trend", color = color.red, style=plot.style_linebr)
fill(bodyMiddle, upTrend, color.new(color.green, 90), fillgaps=false)
fill(bodyMiddle, downTrend, color.new(color.red, 90), fillgaps=false)

//// 2. SSL Hybrid
var bullSSLColor = #00c3ff
var bearSSLColor = #ff0062
// color_bar = color.new(color = close > upperk ? bullSSLColor : close < lowerk ? bearSSLColor : color.gray, transp = 0)
// i_show_color_bar = input.bool(defval = true , title = "Color Bars")
// barcolor(i_show_color_bar ? color_bar : na)
plot(series = BBMC, title = 'MA Baseline', color = color_bar, linewidth = 1, style = plot.style_line)
up_channel = plot(upperk, color=color_bar, title='Baseline Upper Channel')
low_channel = plot(lowerk, color=color_bar, title='Basiline Lower Channel')
fill(up_channel, low_channel, color.new(color=color_bar, transp=90))

//// 3. QQE MOD: No Plotting because of overlay option
// plot(FastAtrRsi2TL - 50, title='QQE Line', color=color.new(color.white, 0), linewidth=2)
// plot(RsiMa2 - 50, color=hcolor2, title='Histo2', style=plot.style_columns, transp=50)
// plot(Greenbar1 and Greenbar2 == 1 ? RsiMa2 - 50 : na, title='QQE Up', style=plot.style_columns, color=color.new(#00c3ff, 0))
// plot(Redbar1 and Redbar2 == 1 ? RsiMa2 - 50 : na, title='QQE Down', style=plot.style_columns, color=color.new(#ff0062, 0))


////// Entry, Exit
// Long, Short Logic with Indicator
bool longSTCond = direction[1] >= 0 and direction <= 0
bool shortSTCond = direction[1] <= 0 and direction >= 0

bool longSSLCond = close > upperk
bool shortSSLCond = close < lowerk

bool longQQECond = Greenbar1 and Greenbar2 == 1
bool shortQQECond = Redbar1 and Redbar2 == 1

// Basic Cond + Long, Short Entry Condition
bool longCond = (i_longEnabled and inTime) and (longSTCond and longSSLCond and longQQECond)
bool shortCond = (i_shortEnabled and inTime) and (shortSTCond and shortSSLCond and shortQQECond)

// Basic Cond + Long, Short Exit Condition
bool closeLong = (i_longEnabled) and (shortSTCond)
bool closeShort = (i_shortEnabled) and (longSTCond)

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// Position Control
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// Long, Short Entry Condition + Not entered Position Yet
bool openLong = longCond and not (strategy.opentrades.size(strategy.opentrades - 1) > 0) and longFilterFilled
bool openShort = shortCond and not (strategy.opentrades.size(strategy.opentrades - 1) < 0) and shortFilterFilled
bool enteringTrade = openLong or openShort
float entryBarIndex = bar_index

// Long, Short Entry Fulfilled or Already Entered
bool inLong = openLong or strategy.opentrades.size(strategy.opentrades - 1) > 0 and not closeLong
bool inShort = openShort or strategy.opentrades.size(strategy.opentrades - 1) < 0 and not closeShort

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// Stop Loss - Inputs, Indicaotrs
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
//// Use SL? TSL?
i_useSLTP = input.bool (defval = true, title = "Enable SL & TP?", tooltip = "", inline = "1", group = "Stop Loss")
i_tslEnabled = input.bool (defval = false , title = "Enable Trailing SL?", tooltip = "Enable Stop Loss & Take Profit? \n\Enable Trailing SL?", inline = "1", group = "Stop Loss")
// i_breakEvenAfterTP = input.bool (defval = false, title = 'Enable Break Even After TP?', tooltip = 'When Take Profit price target is hit, move the Stop Loss to the entry price (or to a more strict price defined by the Stop Loss %/ATR Multiplier).', inline = '2', group = 'Stop Loss / Take Profit')
//// Sl Options
i_slType = input.string (defval = "ATR", title = "Stop Loss Type", options = ["Percent", "ATR", "Previous LL / HH"], tooltip = "Stop Loss based on %? ATR?", inline = "3", group = "Stop Loss")
i_slATRLen = input.int (defval = 14, title = "ATR Length", minval = 1 , maxval = 200 , step = 1, inline = "4", group = "Stop Loss")
i_slATRMult = input.float (defval = 3, title = "ATR Multiplier", minval = 1 , maxval = 200 , step = 0.1, tooltip = "", inline = "4", group = "Stop Loss")
i_slPercent = input.float (defval = 3, title = "Percent", tooltip = "", inline = "5", group = "Stop Loss")
i_slLookBack = input.int (defval = 30, title = "Lowest Price Before Entry", group = "Stop Loss", inline = "6", minval = 30, step = 1, tooltip = "Lookback to find the Lowest Price. \nStopLoss is determined by the Lowest price of the look back period. Take Profit is derived from this also by multiplying the StopLoss value by the Risk:Reward multiplier.")

// Functions for Stop Loss
float openAtr = ta.valuewhen(condition = enteringTrade, source = ta.atr(i_slATRLen), occurrence = 0)
float openLowest = ta.valuewhen(condition = openLong, source = ta.lowest(low, i_slLookBack), occurrence = 0)
float openHighest = ta.valuewhen(condition = openShort, source = ta.highest(high, i_slLookBack), occurrence = 0)

f_getLongSLPrice(source) =>
switch i_slType
"Percent" => source * (1 - (i_slPercent/100))
"ATR" => source - (i_slATRMult * openAtr)
"Previous LL / HH" => openLowest
=> na

f_getShortSLPrice(source) =>
switch i_slType
"Percent" => source * (1 + (i_slPercent/100))
"ATR" => source + (i_slATRMult * openAtr)
"Previous LL / HH" => openHighest
=> na

// Calculate Stop Loss
var float longSLPrice = na
var float shortSLPrice = na
bool longTPExecuted = false
bool shortTPExecuted = false

longSLPrice := if (inLong and i_useSLTP)
if (openLong)
f_getLongSLPrice (close)
else
// 1. Trailing Stop Loss
if i_tslEnabled
stopLossPrice = f_getLongSLPrice (high)
math.max(stopLossPrice, nz(longSLPrice[1]))
// 2. Normal StopLoss
else
nz(source = longSLPrice[1], replacement = 0)
else
na

shortSLPrice := if (inShort and i_useSLTP)
if (openShort)
f_getShortSLPrice (close)
else
// 1. Trailing Stop Loss
if i_tslEnabled
stopLossPrice = f_getShortSLPrice (low)
math.min(stopLossPrice, nz(shortSLPrice[1]))
// 2. Normal StopLoss
else
nz(source = shortSLPrice[1], replacement = 999999.9)
else
na

// Plot: Stop Loss of Long, Short Entry
var longSLPriceColor = color.new(color.maroon, 0)
plot(series = longSLPrice, title = 'Long Stop Loss', color = longSLPriceColor, linewidth = 1, style = plot.style_linebr, offset = 1)
var shortSLPriceColor = color.new(color.maroon, 0)
plot(series = shortSLPrice, title = 'Short Stop Loss', color = shortSLPriceColor, linewidth = 1, style = plot.style_linebr, offset = 1)

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// Take Profit - Inputs, Indicaotrs
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
i_useTPExit = input.bool (defval = true, title = "Use Take Profit?", tooltip = "", inline = "1", group = "Take Profit")
i_RRratio = input.float (defval = 1.8, title = "R:R Ratio", minval = 0.1 , maxval = 200 , step = 0.1, tooltip = "R:R Ratio > Risk Reward Ratio? It will automatically set Take Profit % based on Stop Loss", inline = "2", group = "Take Profit")
i_tpQuantityPerc = input.float (defval = 50, title = 'Take Profit Quantity %', minval = 0.0, maxval = 100, step = 1.0, tooltip = '% of position closed when tp target is met.', inline="34", group = 'Take Profit')

var float longTPPrice = na
var float shortTPPrice = na

f_getLongTPPrice() =>
close + i_RRratio * math.abs (close - f_getLongSLPrice (close))

f_getShortTPPrice() =>
close - i_RRratio * math.abs(close - f_getShortSLPrice (close))

longTPPrice := if (inLong and i_useSLTP)
if (openLong)
f_getLongTPPrice ()
else
nz(source = longTPPrice[1], replacement = f_getLongTPPrice ())
else
na

shortTPPrice := if (inShort and i_useSLTP)
if (openShort)
f_getShortTPPrice ()
else
nz(source = shortTPPrice[1], replacement = f_getShortTPPrice ())
else
na

// Plot: Take Profit of Long, Short Entry
var longTPPriceColor = color.new(color.teal, 0)
plot(series = longTPPrice, title = 'Long Take Profit', color = longTPPriceColor, linewidth = 1, style = plot.style_linebr, offset = 1)
var shortTPPriceColor = color.new(color.teal, 0)
plot(series = shortTPPrice, title = 'Short Take Profit', color = shortTPPriceColor, linewidth = 1, style = plot.style_linebr, offset = 1)

// Plot: Entry Price
var posColor = color.new(color.white, 0)
plot(series = strategy.opentrades.entry_price(strategy.opentrades - 1), title = 'Position Entry Price', color = posColor, linewidth = 1, style = plot.style_linebr)

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// Quantity - Inputs
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
i_useRiskManangement = input.bool (defval = true, title = "Use Risk Manangement?", tooltip = "", inline = "1", group = "Quantity")
i_riskPerTrade = input.float (defval = 3, title = "Risk Per Trade (%)", minval = 0, maxval = 100, step = 0.1, tooltip = "Use Risk Manangement by Quantity Control?", inline = "2", group = "Quantity")
// i_leverage = input.float (defval = 2, title = "Leverage", minval = 0, maxval = 100, step = 0.1, tooltip = "Leverage", inline = "3", group = "Quantity")

float qtyPercent = na
float entryQuantity = na

f_calQtyPerc() =>
if (i_useRiskManangement)
riskPerTrade = (i_riskPerTrade) / 100 // 1번 거래시 3% 손실
stopLossPrice = openLong ? f_getLongSLPrice (close) : openShort ? f_getShortSLPrice (close) : na
riskExpected = math.abs((close-stopLossPrice)/close) // 손절가랑 6% 차이
riskPerTrade / riskExpected // 0 ~ 1
else
1

f_calQty(qtyPerc) =>
math.min (math.max (0.000001, strategy.equity / close * qtyPerc), 1000000000)

// TP Execution
longTPExecuted := strategy.opentrades.size(strategy.opentrades - 1) > 0 and (longTPExecuted[1] or strategy.opentrades.size(strategy.opentrades - 1) < strategy.opentrades.size(strategy.opentrades - 1)[1] or strategy.opentrades.size(strategy.opentrades - 1)[1] == 0 and high >= longTPPrice)
shortTPExecuted := strategy.opentrades.size(strategy.opentrades - 1) < 0 and (shortTPExecuted[1] or strategy.opentrades.size(strategy.opentrades - 1) > strategy.opentrades.size(strategy.opentrades - 1)[1] or strategy.opentrades.size(strategy.opentrades - 1)[1] == 0 and low <= shortTPPrice)

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// Plot Label, Boxes, Results, Etc
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
i_showSimpleLabel = input.bool(false, "Show Simple Label for Entry?", group = "Strategy: Drawings", inline = "1", tooltip ="")
i_showLabels = input.bool(false, "Show Trade Exit Labels", group = "Strategy: Drawings", inline = "1", tooltip = "Useful labels to identify Profit/Loss and cumulative portfolio capital after each trade closes.\n\nAlso note that TradingView limits the max number of 'boxes' that can be displayed on a chart (max 500). This means when you lookback far enough on the chart you will not see the TP/SL boxes. However you can check this option to identify where trades exited.")
i_showDashboard = input.bool(false, "Show Dashboard", group = "Strategy: Drawings", inline = "2", tooltip = "Show Backtest Results. Backtest Dates, Win/Lose Rates, Etc.")

// Plot: Label for Long, Short Entry
var openLongColor = color.new(#2962FF, 0)
var openShortColor = color.new(#FF1744, 0)
var entryTextColor = color.new(color.white, 0)

if (openLong and i_showSimpleLabel)
label.new (x = bar_index, y = na, text = 'Open', yloc = yloc.belowbar, color = openLongColor, style = label.style_label_up, textcolor = entryTextColor)
entryBarIndex := bar_index
if (openShort and i_showSimpleLabel)
label.new (x = bar_index, y = na, text = 'Close', yloc = yloc.abovebar, color = openShortColor, style = label.style_label_down, textcolor = entryTextColor)
entryBarIndex := bar_index

float prevEntryPrice = strategy.closedtrades.entry_price (strategy.closedtrades - 1)
float pnl = strategy.closedtrades.profit (strategy.closedtrades - 1)
float prevExitPrice = strategy.closedtrades.exit_price (strategy.closedtrades - 1)

f_enteringTradeLabel(x, y, qty, entryPrice, slPrice, tpPrice, rrRatio, direction) =>
if i_showLabels
labelStr = ("Trade Start"
+ "\nDirection: " + direction
+ "\nRisk Per Trade: " + str.tostring (i_useRiskManangement ? i_riskPerTrade : 100, "#.##") + "%"
+ "\nExpected Risk: " + str.tostring (math.abs((close-slPrice)/close) * 100, "#.##") + "%"
+ "\nEntry Position Qty: " + str.tostring(math.abs(qty * 100), "#.##") + "%"
+ "\nEntry Price: " + str.tostring(entryPrice, "#.##"))
+ "\nStop Loss Price: " + str.tostring(slPrice, "#.##")
+ "\nTake Profit Price: " + str.tostring(tpPrice, "#.##")
+ "\nRisk - Reward Ratio: " + str.tostring(rrRatio, "#.##")
label.new(x = x, y = y, text = labelStr, color = color.new(color.blue, 60) , textcolor = color.white, style = label.style_label_up)


f_exitingTradeLabel(x, y, entryPrice, exitPrice, direction) =>
if i_showLabels
labelStr = ("Trade Result"
+ "\nDirection: " + direction
+ "\nEntry Price: " + str.tostring(entryPrice, "#.##")
+ "\nExit Price: " + str.tostring(exitPrice,"#.##")
+ "\nGain %: " + str.tostring(direction == 'Long' ? -(entryPrice-exitPrice) / entryPrice * 100 : (entryPrice-exitPrice) / entryPrice * 100 ,"#.##") + "%")
label.new(x = x, y = y, text = labelStr, color = pnl > 0 ? color.new(color.green, 60) : color.new(color.red, 60), textcolor = color.white, style = label.style_label_down)

f_fillCell(_table, _column, _row, _title, _value, _bgcolor, _txtcolor) =>
_cellText = _title + " " + _value
table.cell(_table, _column, _row, _cellText, bgcolor=_bgcolor, text_color=_txtcolor, text_size=size.auto)

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// Orders
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

if (inTime)
if (openLong)
qtyPercent := f_calQtyPerc()
entryQuantity := f_calQty(qtyPercent)
strategy.entry(id = "Long", direction = strategy.long, qty = entryQuantity, comment = 'Long(' + syminfo.ticker + '): Started', alert_message = 'Long(' + syminfo.ticker + '): Started')
f_enteringTradeLabel(x = bar_index + 1, y = close-3*ta.tr, entryPrice = close, qty = qtyPercent, slPrice = longSLPrice, tpPrice = longTPPrice, rrRatio = i_RRratio, direction = "Long")

if (openShort)
qtyPercent := f_calQtyPerc()
entryQuantity := f_calQty(qtyPercent)
strategy.entry(id = "Short", direction = strategy.short, qty = entryQuantity, comment = 'Short(' + syminfo.ticker + '): Started', alert_message = 'Short(' + syminfo.ticker + '): Started')
f_enteringTradeLabel(x = bar_index + 1, y = close-3*ta.tr, entryPrice = close, qty = qtyPercent, slPrice = shortSLPrice, tpPrice = shortTPPrice, rrRatio = i_RRratio, direction = "Short")

if (closeLong)
strategy.close(id = 'Long', comment = 'Close Long', alert_message = 'Long: Closed at market price')
strategy.position_size > 0 ? f_exitingTradeLabel(x = bar_index, y = close+3*ta.tr, entryPrice = prevEntryPrice, exitPrice = prevExitPrice, direction = 'Long') : na

if (closeShort)
strategy.close(id = 'Short', comment = 'Close Short', alert_message = 'Short: Closed at market price')
strategy.position_size < 0 ? f_exitingTradeLabel(x = bar_index, y = close+3*ta.tr, entryPrice = prevEntryPrice, exitPrice = prevExitPrice, direction = 'Short') : na

if (inLong)
strategy.exit(id = 'Long TP / SL', from_entry = 'Long', qty_percent = i_tpQuantityPerc, limit = longTPPrice, stop = longSLPrice, alert_message = 'Long(' + syminfo.ticker + '): Take Profit or Stop Loss executed')
strategy.exit(id = 'Long SL', from_entry = 'Long', stop = longSLPrice, alert_message = 'Long(' + syminfo.ticker + '): Stop Loss executed')

if (inShort)
strategy.exit(id = 'Short TP / SL', from_entry = 'Short', qty_percent = i_tpQuantityPerc, limit = shortTPPrice, stop = shortSLPrice, alert_message = 'Short(' + syminfo.ticker + '): Take Profit or Stop Loss executed')
strategy.exit(id = 'Short SL', from_entry = 'Short', stop = shortSLPrice, alert_message = 'Short(' + syminfo.ticker + '): Stop Loss executed')

if strategy.position_size[1] > 0 and strategy.position_size == 0
f_exitingTradeLabel(x = bar_index, y = close+3*ta.tr, entryPrice = prevEntryPrice, exitPrice = prevExitPrice, direction = 'Long')

if strategy.position_size[1] < 0 and strategy.position_size == 0
f_exitingTradeLabel(x = bar_index, y = close+3*ta.tr, entryPrice = prevEntryPrice, exitPrice = prevExitPrice, direction = 'Short')

// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
// Backtest Result Dashboard
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

if i_showDashboard
var bgcolor = color.new(color = color.black, transp = 100)
var greenColor = color.new(color = #02732A, transp = 0)
var redColor = color.new(color = #D92332, transp = 0)
var yellowColor = color.new(color = #F2E313, transp = 0)
// Keep track of Wins/Losses streaks
newWin = (strategy.wintrades > strategy.wintrades[1]) and (strategy.losstrades == strategy.losstrades[1]) and (strategy.eventrades == strategy.eventrades[1])
newLoss = (strategy.wintrades == strategy.wintrades[1]) and (strategy.losstrades > strategy.losstrades[1]) and (strategy.eventrades == strategy.eventrades[1])

varip int winRow = 0
varip int lossRow = 0
varip int maxWinRow = 0
varip int maxLossRow = 0

if newWin
lossRow := 0
winRow := winRow + 1
if winRow > maxWinRow
maxWinRow := winRow

if newLoss
winRow := 0
lossRow := lossRow + 1
if lossRow > maxLossRow
maxLossRow := lossRow


// Prepare stats table
var table dashTable = table.new(position.top_right, 1, 15, border_width=1)


if barstate.islastconfirmedhistory
dollarReturn = strategy.netprofit
f_fillCell(dashTable, 0, 0, "Start:", str.format("{0,date,long}", strategy.closedtrades.entry_time(0)) , bgcolor, color.white) // + str.format(" {0,time,HH:mm}", strategy.closedtrades.entry_time(0))
f_fillCell(dashTable, 0, 1, "End:", str.format("{0,date,long}", strategy.opentrades.entry_time(0)) , bgcolor, color.white) // + str.format(" {0,time,HH:mm}", strategy.opentrades.entry_time(0))
_profit = (strategy.netprofit / strategy.initial_capital) * 100
f_fillCell(dashTable, 0, 2, "Net Profit:", str.tostring(_profit, '##.##') + "%", _profit > 0 ? greenColor : redColor, color.white)
_numOfDaysInStrategy = (strategy.opentrades.entry_time(0) - strategy.closedtrades.entry_time(0)) / (1000 * 3600 * 24)
f_fillCell(dashTable, 0, 3, "Percent Per Day", str.tostring(_profit / _numOfDaysInStrategy, '#########################.#####')+"%", _profit > 0 ? greenColor : redColor, color.white)
_winRate = ( strategy.wintrades / strategy.closedtrades ) * 100
f_fillCell(dashTable, 0, 4, "Percent Profitable:", str.tostring(_winRate, '##.##') + "%", _winRate < 50 ? redColor : _winRate < 75 ? greenColor : yellowColor, color.white)
f_fillCell(dashTable, 0, 5, "Profit Factor:", str.tostring(strategy.grossprofit / strategy.grossloss, '##.###'), strategy.grossprofit > strategy.grossloss ? greenColor : redColor, color.white)
f_fillCell(dashTable, 0, 6, "Total Trades:", str.tostring(strategy.closedtrades), bgcolor, color.white)
f_fillCell(dashTable, 0, 8, "Max Wins In A Row:", str.tostring(maxWinRow, '######') , bgcolor, color.white)
f_fillCell(dashTable, 0, 9, "Max Losses In A Row:", str.tostring(maxLossRow, '######') , bgcolor, color.white)

// You made it to the end of my script. At W3MCT, we take our expertise and combine it with the TradingView community. We must give acknowledgement to the TradingView community for helping me get version 1.0 completed. Enjoy!!!

Параметры стратегии

Исходный адрес: FMZ — Торговая платформа FMZ Quant

Максимизация прибыли с помощью торговых стратегий пересечения скользящих средних

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

Основы скользящих средних

Прежде чем погружаться глубже, важно понять концепцию простой скользящей средней (SMA) в торговле. Торговые данные, часто предоставляемые биржами и API данных, такими как API EODHD, поступают в различных формах, таких как дневные свечи. Каждая свеча представляет собой точку данных, которая включает информацию OHLCV (открытие, максимум, минимум, закрытие и объем). Если вы построите график цены закрытия акции, вы заметите, что линия довольно «прерывистая», что не очень полезно для анализа. Цель состоит в том, чтобы сгладить эти колебания, рассчитав скользящую скользящую среднюю цен закрытия. Например, взятие 50-дневной скользящей средней, широко известной как SMA50, помогает создать более интерпретируемую и более плавную линию.

from eodhd import APIClient
import config as cfg

api = APIClient(cfg.API_KEY)

def get_ohlc_data():
df = api.get_historical_data("AAPL", "d")
return df

def calculate_technical_indicators(df):
df_ta = df.copy()
df_ta["sma50"] = df_ta["close"].rolling(50, min_periods=0).mean()
return df_ta

if __name__ == "__main__":
df_apple = get_ohlc_data()
df_apple_ta = calculate_technical_indicators(df_apple)
print(df_apple_ta)
Apple APPL Daily OHLCV
import matplotlib.pyplot as plt
from eodhd import APIClient
import config as cfg

api = APIClient(cfg.API_KEY)

def get_ohlc_data():
df = api.get_historical_data("AAPL", "d")
return df

def calculate_technical_indicators(df):
df_ta = df.copy()
df_ta["sma50"] = df_ta["close"].rolling(50, min_periods=0).mean()
return df_ta

if __name__ == "__main__":
df_apple = get_ohlc_data()
df_apple_ta = calculate_technical_indicators(df_apple)

plt.figure(figsize=(30, 10))
plt.plot(df_apple_ta["close"], color="black", label="Close")
plt.plot(df_apple_ta["sma50"], color="blue", label="SMA50")
plt.ylabel("Price")
plt.xticks(rotation=90)
plt.title(f"Close vs. SMA50")
plt.legend()
plt.show()
Apple AAPL «close» против «SMA50»

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

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

Вы можете задаться вопросом, полезна ли простая скользящая средняя (SMA) сама по себе — это не так. Реальная ценность заключается в сравнении двух SMA, таких как SMA50 и SMA200. Это сравнение может выявить важные тенденции на рынке.

import matplotlib.pyplot as plt
from eodhd import APIClient
import config as cfg

api = APIClient(cfg.API_KEY)

def get_ohlc_data():
df = api.get_historical_data("AAPL", "d")
return df

def calculate_technical_indicators(df):
df_ta = df.copy()
df_ta["sma50"] = df_ta["close"].rolling(50, min_periods=0).mean()
df_ta["sma200"] = df_ta["close"].rolling(200, min_periods=0).mean()
return df_ta

if __name__ == "__main__":
df_apple = get_ohlc_data()
df_apple_ta = calculate_technical_indicators(df_apple)

plt.figure(figsize=(30, 10))
plt.plot(df_apple_ta["close"], color="black", label="Close")
plt.plot(df_apple_ta["sma50"], color="blue", label="SMA50")
plt.plot(df_apple_ta["sma200"], color="green", label="SMA200")
plt.ylabel("Price")
plt.xticks(rotation=90)
plt.title(f"Close vs. SMA50 vs. SMA200")
plt.legend()
plt.show()
Apple AAPL «close» против «SMA50» против «SMA200»

На графике выше SMA50 представлена синей линией, а SMA200 — зеленой. Синяя линия – это «краткосрочная скользящая средняя», а зеленая – «долгосрочная скользящая средняя». Сравнение этих двух показателей дает четкое представление о направлении рынка. Например, если SMA50 (краткосрочная) выше SMA200 (долгосрочная), это указывает на растущий рынок, или «бычий рынок». И наоборот, если SMA50 ниже SMA200, это указывает на снижающийся рынок, или «медвежий рынок». Многие трейдеры используют эти точки пересечения в качестве сигналов на покупку или продажу, и такой подход может быть достаточно эффективным. Наблюдение за точками пересечения относительно цены закрытия ясно показывает эту динамику в действии.

В то время как можно сравнивать любые краткосрочные и долгосрочные скользящие средние, SMA50 и SMA200 заслуживают особого внимания. Их пересечения называются «Золотым крестом», когда рынок имеет восходящий тренд, и «Крестом смерти», когда тренд нисходящий. Термины говорят сами за себя. Также часто используются другие распространенные скользящие средние, такие как SMA5, SMA10, SMA20, SMA50 и SMA200.

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

Изучение пересечений многоуровневых скользящих средних

Торговая стратегия работает следующим образом:

Этап 1: Недельный анализ с использованием SMA50 и SMA200

Во-первых, мы получаем исторические данные о торговле на еженедельной основе. Каждая точка данных соответствует одной свече в неделю. Построив графики и сравнив SMA50 и SMA200, вы можете оценить долгосрочное состояние рынка с помощью так называемых сигналов Золотого Креста и Креста Смерти. Золотой крест, где SMA50 находится выше SMA200, указывает на долгосрочный восходящий тренд. И наоборот, «крест смерти», когда SMA50 опускается ниже SMA200, указывает на нисходящий тренд. Желательно не торговать на рынках, где выставлен Крест Смерти.

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

import pandas as pd
import matplotlib.pyplot as plt
from eodhd import APIClient
import config as cfg

api = APIClient(cfg.API_KEY)


def get_ohlc_data():
df = api.get_historical_data("AAPL", "d", results=1000)
return df


def calculate_technical_indicators(df):
df_ta = df.copy()
df_ta["sma50"] = df_ta["close"].rolling(50, min_periods=0).mean()
df_ta["sma200"] = df_ta["close"].rolling(200, min_periods=0).mean()
return df_ta


if __name__ == "__main__":
df_daily = get_ohlc_data()

df_weekly = df_daily.resample("W").agg(
{
"open": "first",
"high": "max",
"low": "min",
"close": "last",
"adjusted_close": "last",
"volume": "sum",
}
)

df_weekly["symbol"] = "AAPL"
df_weekly["interval"] = "w"
df_weekly = df_weekly[["symbol", "interval", "open", "high", "low", "close", "adjusted_close", "volume"]]

df_weekly_ta = calculate_technical_indicators(df_weekly)

print(df_weekly_ta)

Первоначально мы использовали ежедневные данные, помеченные как «df_daily», в наших примерах кода, а затем пересчитывали их в еженедельные данные, повторно добавляя «символ» и «интервал» в агрегат для ясности.

На практике, чтобы получить данные о свечах за 200 недель, вам понадобится около 1400 дней, учитывая 7 дней в неделю. Для этого с помощью библиотеки Python EODHD API вы можете указать ‘results=1400‘ в функции ‘get_historical_data‘.

Использование акций Apple (AAPL) в качестве примера может быть не идеальным, поскольку SMA50 (175.63620000) в настоящее время находится ниже SMA200 (176.28325000) на недельных графиках, что противоречит нашему первому торговому правилу. Однако, глядя на такой индекс, как S&P 500 на Лондонской фондовой бирже («HSPX. LSE») может показать SMA50 выше SMA200 на недельном графике, что соответствует критериям.

Этап 2: Ежедневное сравнение данных

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

Этап 3: Сравнение почасовых данных

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

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

Как мы узнаем, когда продавать?

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

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

Заключение

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

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

Источник

Источник

Источник

Источник

Источник

Источник

Источник