Визуализация торговых сигналов в Python

Построение торговых сигналов на покупку и продажу на графике Python

Будем считать, что вы знакомы с основами технического анализа в трейдинге. Если нет, мы предлагаем использовать отличный ресурс, такой как Investopedia, чтобы узнать больше.

Если вы хотите узнать, как установить официальную библиотеку Python Financial API EODHD и активировать ключ API, начните с изучения нашей подробной документации.

Рассмотрим простой пример:

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

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

Когда SMA50 пересекает SMA200, это сигнализирует о восходящем рыночном тренде. И наоборот, когда SMA50 пересекает SMA200, это признак нисходящего тренда. Хотя можно использовать любые скользящие средние, эта конкретная комбинация уникальна. Когда SMA50 пересекает SMA200, его называют «Золотым крестом», а когда он опускается ниже, его называют «Крестом смерти». Эти события часто предшествуют значительным движениям цены.

Что нам нужно, чтобы начать визуализировать торговые сигналы в Python?

Мы используем записную книжку Jupyter в Google Colab, которая является бесплатным и удобным инструментом, если вы хотите следовать инструкциям. В демонстрационных целях мы также используем демонстрационный ключ API EODHD в конце дня.

Шаг 1: Импорт необходимых библиотек

Для начала импортируйте следующие библиотеки:

import json
import requests
import pandas as pd
import matplotlib.pyplot as plt

Шаг 2: Получите ежедневные данные Apple

Затем извлеките ежедневные данные Apple (AAPL) с помощью API и сохраните их в Pandas DataFrame:

resp = requests.get("https://eodhistoricaldata.com/api/eod/AAPL?api_token=demo&fmt=json")
json_data = json.loads(resp.content)
df = pd.DataFrame(json_data)
df

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

Имея в наличии исторические данные Apple за 10 534 дня, стоит отметить, что, хотя значения «close» и «adjusted_close» очень похожи, мы будем использовать «close» в этом руководстве.

Теперь давайте вычислим и добавим столбцы SMA50 и SMA200 в наш DataFrame:

df["sma50"] = df.close.rolling(50, min_periods=50).mean()
df["sma200"] = df.close.rolling(200, min_periods=200).mean()
df

В обновленном DataFrame вы заметите два новых столбца с метками «sma50» и «sma200». Любые значения «NaN» отображаются, потому что для каждого SMA требуется минимальное количество точек данных для вычисления первого значения. Например, SMA50 требуется 50 цен закрытия, прежде чем он сможет выдать начальное среднее значение.

У нас есть два способа обработки этих значений «NaN»: заменить их другими значениями, такими как цена закрытия, или просто удалить строки с отсутствующими данными. Поскольку у нас есть значительный объем данных, удаление первых 200 строк не повлияет на наш анализ.

Отбросим строки, содержащие «NaN»:

df.dropna(inplace=True)
df

После удаления строк с отсутствующими значениями наш DataFrame теперь будет содержать только строки с полностью заполненными значениями SMA50 и SMA200.

Как это выглядит на графике?

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

Вот как строить графики торговых сигналов:

plt.figure(figsize=(30,10))
plt.plot(df["close"], color="black", label="Price")
plt.plot(df["sma50"], color="blue", label="SMA50")
plt.plot(df["sma200"], color="green", label="SMA200")
plt.ylabel("Price")
plt.xticks(rotation=90)
plt.title("APPL Daily SMA50/SMA200")
plt.legend()
plt.show()

Несмотря на то, что график отображается успешно, его нелегко интерпретировать, поскольку он визуализирует данные за 28 лет, что больше, чем нам нужно. Чтобы было понятнее, мы можем сократить набор данных только до прошлого года, ограничив его последними 365 днями:

df = df.tail(365)

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

Вы можете заметить, что на оси X в настоящее время отображаются номера индексов, а не фактические даты. Хотя это не обязательно проблематично, это не особенно визуально привлекательно. Чтобы исправить это, мы заменим метки по оси X фактическими датами.

df.set_index(['date'], inplace=True)
df

И вот, обновленный график будет выглядеть так…

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

Вот как работает код:

ax = plt.gca()
for index, label in enumerate(ax.xaxis.get_ticklabels()):
if index % 7 != 0:
label.set_visible(False)

В этом коде мы проверяем, делится ли порядковый номер на 7. В противном случае соответствующая метка оси X будет скрыта, оставляя видимой только каждый седьмой день. Это делает график намного четче и проще для понимания.

Вот полный код…

plt.figure(figsize=(30,10))
plt.plot(df["close"], color="black", label="Price")
plt.plot(df["sma50"], color="blue", label="SMA50")
plt.plot(df["sma200"], color="green", label="SMA200")
plt.ylabel("Price")
plt.xticks(rotation=90)
plt.title("APPL Daily SMA50/SMA200")
ax = plt.gca()
for index, label in enumerate(ax.xaxis.get_ticklabels()):
if index % 7 != 0:
label.set_visible(False)
plt.legend()
plt.show()

Отлично!

На графике вы заметите черную линию, представляющую дневную цену закрытия, синюю линию для SMA50 и зеленую линию для SMA200.

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

Как мы можем программно определить, когда SMA пересекаются?

В Pandas мы можем создать новый признак или столбец, содержащий логические значения (true или false) на основе определенного условия.

pd.options.mode.chained_assignment = None
df.loc[df["sma50"] > df["sma200"], "sma50gtsma200"] = True
df["sma50gtsma200"].fillna(False, inplace=True)
df.loc[df["sma50"] < df["sma200"], "sma50ltsma200"] = True
df["sma50ltsma200"].fillna(False, inplace=True)
df

В нашем DataFrame вы заметите два новых столбца: «sma50gtsma200″ и «sma50ltsma200″. Первый столбец возвращает значение «Истина», когда SMA50 больше, чем SMA200, а второй возвращает значение «Истина», когда SMA50 меньше SMA200. Если ни одно из условий не выполняется, значения будут иметь значение ‘False`.

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

df["sma50gtsma200co"] = df.sma50gtsma200.ne(df.sma50gtsma200.shift())
df.loc[df["sma50gtsma200"] == False, "sma50gtsma200co"] = False

df["sma50ltsma200co"] = df.sma50ltsma200.ne(df.sma50ltsma200.shift())
df.loc[df["sma50ltsma200"] == False, "sma50ltsma200co"] = False

df

Столбцы новой функции «sma50gtsma200co» и «sma50ltsma200co» будут указывать на то, когда происходят кроссоверы.

Подтвердим сигнал(ы) на покупку:

buysignals = df[df["sma50gtsma200co"] == True]
buysignals

И сигнал(ы) на продажу:

sellsignals = df[df["sma50ltsma200co"] == True]
sellsignals

Построение торговых сигналов на покупку и продажу в Python

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

for idx in buysignals.index.tolist():
plt.plot(
idx,
df.loc[idx]["close"],
"g*",
markersize=25
)

for idx in sellsignals.index.tolist():
plt.plot(
idx,
df.loc[idx]["close"],
"r*",
markersize=25
)

Этот код добавит зеленые звезды для записей в кадре данных «buysignals» и красные звезды для записей в кадре данных «sellsignals». Он визуализирует все сигналы на покупку и продажу в кадрах данных, чтобы объединение сигналов давало более продвинутые результаты.

Полный код выглядит так…

plt.figure(figsize=(30,10))
plt.plot(df["close"], color="black", label="Price")
plt.plot(df["sma50"], color="blue", label="SMA50")
plt.plot(df["sma200"], color="green", label="SMA200")
plt.ylabel("Price")
plt.xticks(rotation=90)
plt.title("APPL Daily SMA50/SMA200")
for idx in buysignals.index.tolist():
plt.plot(
idx,
df.loc[idx]["close"],
"g*",
markersize=25
)

for idx in sellsignals.index.tolist():
plt.plot(
idx,
df.loc[idx]["close"],
"r*",
markersize=25
)

ax = plt.gca()
for index, label in enumerate(ax.xaxis.get_ticklabels()):
if index % 7 != 0:
label.set_visible(False)
plt.legend()
plt.show()

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

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

Давайте включим индикатор Moving Average Convergence Divergence (MACD) и посмотрим, сможет ли он улучшить наши сигналы.

df["ema12"] = df["close"].ewm(span=12, adjust=False).mean()
df["ema26"] = df["close"].ewm(span=26, adjust=False).mean()

df["macd"] = df["ema12"] - df["ema26"]
df["signal"] = df["macd"].ewm(span=9, adjust=False).mean()

df.loc[df["macd"] > df["signal"], "macdgtsignal"] = True
df["macdgtsignal"].fillna(False, inplace=True)

df.loc[df["macd"] < df["signal"], "macdltsignal"] = True
df["macdltsignal"].fillna(False, inplace=True)

df

Чтобы понять технический индикатор MACD, вы можете обратиться к Investopedia для получения дополнительной информации. Вкратце, MACD рассчитывается путем вычитания 26-периодной экспоненциальной скользящей средней (EMA) из 12-периодной EMA. Сигнал — это еще одна экспоненциальная скользящая средняя, использующая последние 9 значений MACD.

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

Чтобы объединить индикаторы SMA50/200 и MACD/Signal в составные торговые сигналы на покупку и продажу:

buysignals = df[(df["sma50gtsma200co"] == 1) & (df["macdgtsignal"] == 1)]

sellsignals = df[(df["sma50ltsma200co"] == 1) & (df["macdltsignal"] == 1)]

Эта логика диктует, что сигнал на покупку срабатывает, когда SMA50 пересекает SMA200, а MACD больше сигнала. И наоборот, сигнал на продажу срабатывает, когда SMA50 пересекает SMA200, а MACD меньше сигнала.

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

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

df["macdgtsignalco"] = df.macdgtsignal.ne(df.macdgtsignal.shift())
df.loc[df["macdgtsignal"] == False, "macdgtsignalco"] = False

df["macdltsignalco"] = df.macdltsignal.ne(df.macdltsignal.shift())
df.loc[df["macdltsignal"] == False, "macdltsignalco"] = False

buysignals = df[(df["sma50gtsma200co"] == 1) & (df["macdgtsignal"] == 1)]

sellsignals = df[(df["sma50gtsma200"] == 1) & (df["macdltsignalco"] == 1)]

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

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

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

Почему бы не попробовать добавить индекс относительной силы (RSI)? Сигнал на покупку обычно указывается, если RSI14 ниже 30, а сигнал на продажу определяется, если RSI14 выше 70.

Чтобы помочь вам начать, помните, что расчет RSI немного сложнее, чем для скользящих средних или MACD. Использование библиотеки ‘pandas_ta’ упрощает этот процесс.

В Google Colab установите библиотеку ‘pandas_ta’, используя отдельную ячейку кода:

pip install pandas_ta

Затем импортируйте библиотеку:

import pandas_ta as ta

Наконец, рассчитаем RSI14:

df["rsi14"] = ta.rsi(df["close"], length=14, fillna=50)

Имейте в виду, что первые 14 позиций RSI14 будут содержать значения «NaN». Используя ‘fillna’, мы устанавливаем значение по умолчанию равным 50.

Источник