Целью этого проекта был анализ возможностей Python для исследования цен на акции. Легенда этого исследования такова: некий инвестор хочет инвестировать в акции Tesla, потому что он видит хорошую тенденцию к росту на многочисленных биржевых сайтах. Я, как аналитик, должен ответить ему хорошее ли сейчас время для покупки или нет.
Поскольку Python сам строит графики, а не берет их с веб-сайтов, моей целью было построить эти графики в этой среде. Я также хотел понять, действительно ли эти акции имеют тенденцию к росту и каковы перспективы будущего роста.
Итак, давайте посмотрим как можно проанализировать акции с помощью Python. Давайте импортируем котировки акций Tesla в Jupyter Notebook. Мы это сделаем через библиотеку yfinance от Yahoo. А для анализа данных и построения графиков будем использовать библиотеки pandas, datetime и plotly.
import pandas as pd
import yfinance as yf
import datetime
from datetime import date, timedelta
import plotly.graph_objects as go
import plotly.express as px
today = date.today()
d1 = today.strftime("%Y-%m-%d")
end_date = d1
d2 = date.today() - timedelta(days=365)
d2 = d2.strftime("%Y-%m-%d")
start_date = d2
data = yf.download('TSLA',
start=start_date,
end=end_date,
progress=False)
data["Date"] = data.index
data = data[["Date", "Open", "High", "Low", "Close"]]
data.reset_index(drop=True, inplace=True)
print(data.tail(15))
Рис. 1: Котировки акций в Jupyter Notebook
Беглого взгляда на эти данные достаточно, чтобы понять что акции Тесла уверенно растут последние 15 дней и этот рост составил 11%!
Но разумеется это лишь просто хороший период для этих акций. И волатильность их очень даже высокая. Давайте построим свечной график цен закрытия чтобы убедиться в этом.
figure = go.Figure(data=[go.Candlestick(x=data["Date"],
open=data["Open"], high=data["High"],
low=data["Low"], close=data["Close"])])
figure.update_layout(title = "Свечной график акций Тесла", xaxis_rangeslider_visible=False)
figure.show()
Рис. 2: Свечной график акций Tesla
Как мы видим по этому графику разброс цен на акции Тесла за последние 365 дней был коллоссальным (от 140 с небольшим долларов до 270). И сейчас мы находимся выше середины этого диапазона.
Означает ли это что цены на эти акции еще не перегреты? Чтобы это понять, нам нужно будет построить еще несколько графиков.
figure = px.bar(data, title = "Бар-чарт акций Тесла", x = "Date", y= "Close")
figure.show()
Рис. 3: Бар-чарт акций Tesla
Как ни странно, но бар чарт иногда лучше читается, чем свечной график. Хотя все биржевики просто обожают свечные графики, но иногда они слишком шумные.
По этому бар-чарту более наглядно видно где у акций дно, а где пики. И очевидно, что краткосрочно (на несколько недель) у нас есть шансы на рост. Но не более того, так как мы близки к годовому максимуму. Чтобы убедиться в этом, давайте построим еще один график – Range Slider.
figure = px.line(data, x='Date', y='Close',
title='Анализ акций Тесла с селекторами времени')
figure.update_xaxes(rangeslider_visible=True)
figure = px.line(data, x='Date', y='Close',
title='Анализ акций Тесла с селекторами времени')
figure.update_xaxes(
rangeselector=dict(
buttons=list([
dict(count=1, label="1m", step="month", stepmode="backward"),
dict(count=6, label="6m", step="month", stepmode="backward"),
dict(count=3, label="3m", step="month", stepmode="backward"),
dict(count=1, label="1y", step="year", stepmode="backward"),
dict(step="all")
])
)
)
figure.show()
Рис. 4: График цен акций Tesla с селекторами времени
Переключая кнопками периоды графика мы видим, что акции Тесла имеют растущий тренд на всех интервалах: 1, 3, и 6 месяцев. Однако на более крупных таймфреймах они выглядят уже не столь привлекательно.
Таким образом мы можем сделать вывод что у Tesla сейчас хороший тренд наверх, однако на долгосроке они уже выглядят так, что имеют справедливую цену.
Анализ с использованием скользящих средних
Одним из самых популярных инструментов для анализа трендов на финансовых рынках является скользящая средняя (SMA – Simple Moving Average). Давайте добавим 50-дневную и 100-дневную скользящие средние, чтобы лучше понять долгосрочные и краткосрочные тренды в котировках акций Tesla.
data['SMA50'] = data['Close'].rolling(window=50).mean()
data['SMA100'] = data['Close'].rolling(window=100).mean()
# График с SMA
figure = go.Figure()
figure.add_trace(go.Scatter(x=data['Date'], y=data['Close'], name='Цена закрытия', mode='lines'))
figure.add_trace(go.Scatter(x=data['Date'], y=data['SMA50'], name='50-дневная SMA', mode='lines'))
figure.add_trace(go.Scatter(x=data['Date'], y=data['SMA100'], name='100-дневная SMA', mode='lines'))
figure.update_layout(title='Акции Tesla с 50 и 100-дневными скользящими средними', xaxis_rangeslider_visible=False)
figure.show()
Рис. 5: Акции Tesla с 50-дневной и 100-дневной скользящими средними (SMA)
На этом графике хорошо видны долгосрочные и краткосрочные тренды. Когда краткосрочная (50-дневная) скользящая средняя пересекает долгосрочную (100-дневную) сверху вниз, это сигнал на продажу (так называемый “мёртвый крест”). И наоборот, пересечение снизу вверх может быть сигналом на покупку (“золотой крест”).
Судя по этому графику акции Тесла находятся в затухающем растущем тренде, а значит потенциал роста невысок.
Индикатор относительной силы (RSI)
Следующим шагом является использование индикатора RSI (Relative Strength Index). RSI показывает, перекуплены или перепроданы акции на текущем рынке. Значение выше 70 говорит о перекупленности акций, а ниже 30 — о перепроданности.
import numpy as np
# Функция для расчета RSI
def calculate_RSI(data, window=14):
# Вычисляем изменения цены закрытия
delta = data['Close'].diff(1)
# Отделяем положительные и отрицательные изменения
gain = np.where(delta > 0, delta, 0)
loss = np.where(delta < 0, -delta, 0)
# Скользящее среднее для прибыли и убытков
avg_gain = pd.Series(gain).rolling(window=window, min_periods=1).mean()
avg_loss = pd.Series(loss).rolling(window=window, min_periods=1).mean()
# Рассчитываем RS (отношение средних прибылей к убыткам)
rs = avg_gain / avg_loss
# Рассчитываем RSI
rsi = 100 - (100 / (1 + rs))
return rsi
# Добавляем RSI в DataFrame
data['RSI'] = calculate_RSI(data)
# Строим график с RSI
figure = go.Figure()
figure.add_trace(go.Scatter(x=data['Date'], y=data['RSI'], name='RSI', mode='lines'))
figure.add_hline(y=70, line_dash="dash", line_color="red", annotation_text="Перекупленность", annotation_position="top right")
figure.add_hline(y=30, line_dash="dash", line_color="green", annotation_text="Перепроданность", annotation_position="bottom right")
figure.update_layout(title='Индикатор RSI для акций Tesla', xaxis_rangeslider_visible=False)
figure.show()
Рис. 6: Анализ акций на перекупленность / перепроданность методом RSI
На этом графике мы явно видим, что акции Tesla уже поздно покупать – их котировки близки к границе перекупленности. Покупка здесь может привести к тому, что мы будем долго находится в убытках.
Как работает этот код? Все очень просто. Мы рассчитываем следующие метрики:
- Изменение цен (delta): Рассчитываем разницу между сегодняшней и вчерашней ценами закрытия.
- Прибыль и убыток (gain/loss): Из этой разницы извлекаем положительные значения (прибыль) и отрицательные (убыток).
- Скользящее среднее: Рассчитываем средние прибыли и убытки за указанное окно (обычно 14 дней).
- RSI: Далее вычисляется относительный индекс на основе формулы индикатора RSI.
Прогнозирование котировок с помощью машинного обучения
В заключение, давайте попробуем предсказать цены акций Tesla с помощью модели машинного обучения. Я буду использовать здесь метод LSTM (Long Short-Term Memory), который отлично подходит для временных рядов. Однако для начала проведу предварительную подготовку данных и разделю их на обучающую и тестовую выборки.
from sklearn.preprocessing import MinMaxScaler
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense
# Нормализация данных
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(data['Close'].values.reshape(-1,1))
# Подготовка данных для LSTM
prediction_days = 60
X_train, y_train = [], []
for x in range(prediction_days, len(scaled_data)):
X_train.append(scaled_data[x-prediction_days:x, 0])
y_train.append(scaled_data[x, 0])
X_train, y_train = np.array(X_train), np.array(y_train)
X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))
Что делает этот код? Рассмотрим по шагам:
- Импортирует необходимые библиотеки: MinMaxScaler, numpy, Sequential, LSTM, Dense;
- Нормализует данные о ценах закрытия акций в диапазоне от 0 до 1 с помощью MinMaxScaler;
- Устанавливает количество дней для предсказания (60);
- Создает списки для входных (X_train) и выходных (y_train) данных;
- Заполняет X_train последовательностями из 60 дней и y_train соответствующими значениями на следующий день;
- Преобразует списки в массивы NumPy;
- Изменяет форму массива X_train для соответствия требованиям LSTM (3 измерения: образцы, временные шаги, признаки).
Окей. Давайте приступим к обучению модели.
# Создание модели LSTM
model = Sequential()
model.add(LSTM(units=50, return_sequences=True, input_shape=(X_train.shape[1], 1)))
model.add(LSTM(units=50, return_sequences=False))
model.add(Dense(units=1))
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(X_train, y_train, epochs=25, batch_size=32)
Рис. 7: Процесс обучения модели LSTM (эпохи, затраченное время и лоссы)
Этот код создаст базовую модель LSTM, которая обучается на исторических данных по акциям Tesla и попытается предсказать будущие цены. Мы можем усложнить модель и добавить больше данных для повышения точности, однако базовая версия может уже показать интересные результаты.
Далее, чтобы оценить качество построенной модели, нам нужно будет сделать следующее:
- Провести тестирование модели на новых данных: Мы разделим данные на обучающую и тестовую выборки, чтобы проверить качество предсказаний;
- Создать визуализацию прогнозов по сравнению с реальными данными: Построим график сравнения фактических данных и прогнозов;
- Провести оценку точности модели: Рассчитаем метрики качества, такие как RMSE (корень среднеквадратичной ошибки) или MAE (средняя абсолютная ошибка);
- Сохранить веса модели для дальнейшего использования (продакшен).
from sklearn.metrics import mean_squared_error, mean_absolute_error
import math
import matplotlib.pyplot as plt
# Разделение данных на обучающую и тестовую выборки
train_data_len = int(len(scaled_data) * 0.8)
test_data = scaled_data[train_data_len - prediction_days:]
X_test, y_test = [], []
for x in range(prediction_days, len(test_data)):
X_test.append(test_data[x-prediction_days:x, 0])
y_test.append(test_data[x, 0])
X_test, y_test = np.array(X_test), np.array(y_test)
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 1))
# Прогнозирование на тестовых данных
predicted_prices = model.predict(X_test)
predicted_prices = scaler.inverse_transform(predicted_prices) # Денормализация
# Денормализуем тестовые данные для сравнения
real_prices = scaler.inverse_transform(y_test.reshape(-1, 1))
# Оценка модели с помощью метрик
rmse = math.sqrt(mean_squared_error(real_prices, predicted_prices))
mae = mean_absolute_error(real_prices, predicted_prices)
print(f"RMSE: {rmse}")
print(f"MAE: {mae}")
Рис. 8: Значения метрик RMSE и MAE для тестовой выборки
Мы видим, что ошибки получились небольшими и это весьма неожиданно для базовой модели, обученной всего на 25 эпохах. Давайте взглянем что покажут графики.
# Визуализация прогноза и реальных данных
plt.figure(figsize=(10,6))
plt.plot(real_prices, color='blue', label='Реальные цены')
plt.plot(predicted_prices, color='red', label='Прогнозируемые цены')
plt.title('Прогноз цен акций Tesla с помощью LSTM')
plt.xlabel('Время')
plt.ylabel('Цена акции')
plt.legend()
plt.show()
Рис. 9: Прогноз цен акций Tesla с помощью модели LSTM
Увы, но график не впечатлил. Да, в целом модель угадывает направление верно, но запаздывание просто кошмарное!
Очевидно, что наша модель – сырая. Такую нельзя пускать в прод. Но для полноты исследования давайте все-же сохраним веса.
# Сохранение модели
model.save('lstm_tesla_model.h5')
Итак, давайте подытожим каков был алгоритм нашей модели и что тут можно улучшить:
- Тестовые данные: Мы взяли 20% последних данных для тестирования, сохраняя 80% для обучения. Никакой кросс-валидации здесь сделано не было;
- Прогнозы: После обучения модели, она предсказывает цены на основе тестовой выборки. Можно добавить еще валидационную выборку;
- Оценка точности: Использовали метрики RMSE, измеряющую среднее отклонение предсказанных цен от фактических и MAE, которая показывает среднюю разницу между фактическими и прогнозируемыми значениями. Можно добавить еще R2, MAPE, попробовать обучать модель по ним;
- Визуализация: Построили график для сравнения фактических и предсказанных цен акций. Можно добавить график остатков;
- Сохранение модели: Сохранили модель в файл, чтобы ее можно было использовать для предсказаний в будущем.
Выводы
Анализ акций Tesla с использованием Python может включать не только простые техники сравнения цен и визуализации, но и более сложные методы анализа и прогнозирования. В этой статье мы рассмотрели скользящие средние, RSI и даже построили простую модель машинного обучения для предсказания будущих цен.