Войти
Главная / Data science в трейдинге / Как собрать свой дашборд на Python с несколькими инструментами
Хотите зарабатывать на трейдинге?

Подписывайтесь на наш телеграм-канал для трейдеров — с глубокой аналитикой и рабочими стратегиями

Подписаться

Как собрать свой журнал для тестирования стратегии? Заберите бесплатный гайд

Гайд от нашей команды поможет вам систематизировать торговлю и принимать взвешенные решения

Подтвердите согласие

Как собрать свой дашборд на Python с несколькими инструментами

Привет! Меня зовут Григорий Соколов, я CTO Trade2Good. В этой статье я расскажу, как с помощью Python, а также библиотек Dash и Plotly, создать интерактивный дашборд для анализа финансовых данных. Мы будем использовать OHLC-данные (открытие, максимум, минимум, закрытие) — они часто применяются для анализа акций и других финансовых инструментов.

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

Визуализация сложных данных

  • Графики вместо чисел. Большие таблицы сложно читать. А дашборд помогает быстро увидеть тренды и аномалии.
  • Пример: cвечной график OHLC сразу показывает, как колебалась цена акции — это не так очевидно в обычной таблице Excel.

Интерактивное исследование

  • Глубокая аналитика без написания кода каждым сотрудником.
  • «Живые» фильтры вместо статичных отчетовCopyDownload. Пользователи могут:
dcc.DatePickerRange()  # Выбор периода
dcc.Dropdown()        # Сравнение метрик
dcc.Checklist()       # Включение индикаторов

3. Оперативное принятие решений

  • Мониторинг в реальном времени. Дашборды могут обновляться через API (например, для трейдинга):
# Псевдокод для подключения биржевых данных
import yfinance as yf
data =yf.download('AAPL', period='1d', interval='1m')

4. Автоматизация отчетности

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

5. Обнаружение аномалий

  • Визуальные триггеры для быстрого реагирования. Пример кода для выделения аномалий:

fig.add_shape(  # Красная линия при падении цены >5%
    type="line",
    x0=df['Date'].min(), y0=alert_level,
    x1=df['Date'].max(), y1=alert_level,
    line=dict(color="red", width=2, dash="dot")
)

6. Универсальный инструмент

Применение в разных сферах с минимальными изменениями: финансы (анализ акций — как в нашем примере, маркетинг: конверсии и ROI кампаний, продажи (динамика выручки), логистика (мониторинг поставок).

Совместная работа

  • Общее пространство для команды — все видят одни данные без искажений.

Масштабируемость

Базовый вариант:

# Только цена и объем
dcc.Graph(id='price-chart'),
dcc.Graph(id='volume-chart')

Продвинутая версия:

# Добавляем:
dcc.Graph(id='correlation-matrix'),  # Корреляции активов
dcc.Graph(id='news-sentiment')      # Анализ новостей

9. Интеграция с современным стеком

Дашборды легко встраиваются в веб-приложения (Flask/Django), BI-системы (Tableau/Power BI через iframe) и мобильные решения (с адаптивным дизайном).

Стек технологий

Dash — фреймворк для создания веб-дашбордов, Plotly — библиотека для интерактивной визуализации, Pandas — для обработки и анализа данных.

Создаем дашбоард

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

# Импорт библиотек для работы с данными
import pandas as pd  # Для работы с табличными данными
import numpy as np   # Для математических операций

# Импорт компонентов для работы с датами
from datetime import datetime, timedelta  # Для работы с датами

# Импорт компонентов Dash для создания веб-интерфейса
from dash import Dash, dcc, html, Input, Output
# Dash - основной класс приложения
# dcc - компоненты графиков и элементов управления
# html - HTML-компоненты
# Input, Output - для создания интерактивности

# Импорт библиотек для визуализации
import plotly.graph_objects as go  # Для создания сложных графиков
import plotly.express as px        # Для создания простых графиков

Шаг 2: Генерация синтетических OHLC-данных

def generate_ohlc_data(days=30):
    """
    Генерирует синтетические OHLC данные для демонстрации работы дашборда
    
    Параметры:
        days (int): количество дней данных для генерации
        
    Возвращает:
        pd.DataFrame: DataFrame с колонками Date, Open, High, Low, Close, Volume
    """
    # Устанавливаем seed для воспроизводимости результатов
    np.random.seed(42)
    
    # Создаем список дат, начиная с текущей даты минус указанное количество дней
    start_date = datetime.now() - timedelta(days=days)
    dates = [start_date + timedelta(days=i) for i in range(days)]
    
    # Генерируем базовую цену с небольшим случайным отклонением
    base_price = 100 + np.random.normal(0, 10)
    
    # Инициализируем списки для хранения данных
    opens = []    # Цены открытия
    highs = []    # Максимальные цены
    lows = []     # Минимальные цены
    closes = []   # Цены закрытия
    volumes = []  # Объемы торгов
    
    # Генерируем данные для каждого дня
    for i in range(days):
        # Цена открытия равна цене закрытия предыдущего дня с небольшим изменением
        # Для первого дня используем базовую цену
        open_price = base_price if i == 0 else closes[i-1] * (1 + np.random.normal(0, 0.02))
        
        # Цена закрытия отличается от цены открытия
        close_price = open_price * (1 + np.random.normal(0, 0.03))
        
        # Максимальная цена - максимальная из open/close плюс небольшой шум
        high_price = max(open_price, close_price) * (1 + abs(np.random.normal(0, 0.01)))
        
        # Минимальная цена - минимальная из open/close минус небольшой шум
        low_price = min(open_price, close_price) * (1 - abs(np.random.normal(0, 0.01)))
        
        # Объем торгов - случайное значение вокруг 100000
        volume = int(np.random.normal(100000, 20000))
        
        # Добавляем сгенерированные значения в списки
        opens.append(round(open_price, 2))    # Округляем до 2 знаков после запятой
        closes.append(round(close_price, 2))
        highs.append(round(high_price, 2))
        lows.append(round(low_price, 2))
        volumes.append(volume)
    
    # Создаем DataFrame из сгенерированных данных
    return pd.DataFrame({
        'Date': dates,     # Колонка с датами
        'Open': opens,     # Цены открытия
        'High': highs,    # Максимальные цены
        'Low': lows,       # Минимальные цены
        'Close': closes,   # Цены закрытия
        'Volume': volumes  # Объемы торгов
    })

# Генерируем 60 дней данных и сохраняем в переменную ohlc_data
ohlc_data = generate_ohlc_data(60)

Шаг 3: Инициализация приложения Dash

# Создаем экземпляр приложения Dash
# __name__ помогает Dash находить статические файлы
app = Dash(__name__)

Шаг 4: Создание макета дашборда

# Определяем структуру дашборда с помощью компонентов Dash
app.layout = html.Div([
    # Заголовок дашборда
    html.H1("Финансовый аналитический дашборд",
            style={'textAlign': 'center'}),  # Центрируем заголовок

    # Панель управления - левая часть дашборда
    html.Div([
        # Блок выбора типа графика
        html.Div([
            html.Label("Тип графика:"),  # Метка для dropdown
            dcc.Dropdown(
                id='chart-type',  # Идентификатор для callback
                options=[  # Варианты выбора
                    {'label': 'Линейный', 'value': 'line'},
                    {'label': 'Свечной', 'value': 'candle'},
                    {'label': 'OHLC', 'value': 'ohlc'}
                ],
                value='candle',  # Значение по умолчанию
                clearable=False  # Запрещаем очистку выбора
            ),
        ], style={'padding': '10px'}),  # Отступы для блока

        # Блок выбора диапазона дат
        html.Div([
            html.Label("Диапазон дат:"),
            dcc.DatePickerRange(
                id='date-range',
                min_date_allowed=ohlc_data['Date'].min(),  # Минимальная доступная дата
                max_date_allowed=ohlc_data['Date'].max(),  # Максимальная доступная дата
                start_date=ohlc_data['Date'].min(),       # Начальная дата по умолчанию
                end_date=ohlc_data['Date'].max()          # Конечная дата по умолчанию
            ),
        ], style={'padding': '10px'}),

        # Блок выбора скользящих средних
        html.Div([
            html.Label("Скользящие средние:"),
            dcc.Checklist(
                id='ma-toggle',
                options=[  # Варианты скользящих средних
                    {'label': 'MA 7', 'value': 7},
                    {'label': 'MA 14', 'value': 14},
                    {'label': 'MA 30', 'value': 30}
                ],
                value=[]  # По умолчанию ничего не выбрано
            ),
        ], style={'padding': '10px'}),

        # Блок выбора технических индикаторов
        html.Div([
            html.Label("Индикаторы:"),
            dcc.Checklist(
                id='rsi-toggle',
                options=[
                    {'label': 'RSI (14)', 'value': 14}  # Индекс относительной силы
                ],
                value=[]
            ),
        ], style={'padding': '10px'}),
    ], style={
        'width': '20%',  # Ширина панели управления
        'display': 'inline-block',  # Расположение в строке
        'vertical-align': 'top',    # Выравнивание по верху
        'border-right': '1px solid #ddd',  # Граница справа
        'padding': '10px'  # Внутренние отступы
    }),

    # Основная область с графиками - правая часть дашборда
    html.Div([
        # График цен
        dcc.Graph(id='price-chart'),
        # График объемов
        dcc.Graph(id='volume-chart'),
        # График RSI (изначально скрыт)
        dcc.Graph(id='rsi-chart', style={'display': 'none'})
    ], style={
        'width': '78%',  # Ширина области с графиками
        'display': 'inline-block',  # Расположение в строке
        'padding': '10px'  # Внутренние отступы
    })
])

Шаг 5: Настройка интерактивности (callback)

# Декоратор callback определяет, какие компоненты будут обновляться
# при изменении входных параметров
@app.callback(
    # Выходные параметры (что обновляем):
    [Output('price-chart', 'figure'),   # График цен
     Output('volume-chart', 'figure'),  # График объемов
     Output('rsi-chart', 'figure'),     # График RSI
     Output('rsi-chart', 'style')],     # Стиль графика RSI (видимость)
    # Входные параметры (что отслеживаем):
    [Input('chart-type', 'value'),      # Тип графика
     Input('date-range', 'start_date'), # Начальная дата
     Input('date-range', 'end_date'),   # Конечная дата
     Input('ma-toggle', 'value'),       # Выбранные скользящие средние
     Input('rsi-toggle', 'value')]      # Показать RSI
)
def update_charts(chart_type, start_date, end_date, ma_values, rsi_values):
    """
    Обновляет все графики на основе выбранных пользователем параметров

    Параметры:
        chart_type (str): тип графика (line/candle/ohlc)
        start_date (str): начальная дата в формате YYYY-MM-DD
        end_date (str): конечная дата в формате YYYY-MM-DD
        ma_values (list): список периодов для скользящих средних
        rsi_values (list): список периодов для RSI

    Возвращает:
        tuple: (fig_price, fig_volume, fig_rsi, rsi_style)
    """

    # 1. Фильтрация данных по выбранному диапазону дат
    filtered_data = ohlc_data[
        (ohlc_data['Date'] >= pd.to_datetime(start_date)) &
        (ohlc_data['Date'] <= pd.to_datetime(end_date))
    ].copy()  # Создаем копию, чтобы не изменять исходные данные

    # 2. Создание ценового графика в зависимости от выбранного типа
    if chart_type == 'line':
        # Линейный график цен закрытия
        price_fig = px.line(filtered_data,
                           x='Date',
                           y='Close',
                           title='Цена закрытия')
    elif chart_type == 'candle':
        # Свечной график
        price_fig = go.Figure(data=[go.Candlestick(
            x=filtered_data['Date'],
            open=filtered_data['Open'],
            high=filtered_data['High'],
            low=filtered_data['Low'],
            close=filtered_data['Close'],
            name='Цена'  # Название для легенды
        )])
        price_fig.update_layout(title='Свечной график')
    else:
        # OHLC график
        price_fig = go.Figure(data=[go.Ohlc(
            x=filtered_data['Date'],
            open=filtered_data['Open'],
            high=filtered_data['High'],
            low=filtered_data['Low'],
            close=filtered_data['Close'],
            name='Цена'
        )])
        price_fig.update_layout(title='OHLC график')

    # 3. Добавление скользящих средних, если они выбраны
    for window in ma_values:
        # Создаем колонку с MA для текущего окна
        ma_col = f'MA_{window}'
        filtered_data[ma_col] = filtered_data['Close'].rolling(window=window).mean()

        # Добавляем линию MA на график
        price_fig.add_trace(go.Scatter(
            x=filtered_data['Date'],
            y=filtered_data[ma_col],
            name=f'MA {window}',  # Название для легенды
            line=dict(width=2)    # Толщина линии
        ))

    # 4. Создание графика объемов
    volume_fig = px.bar(filtered_data,
                       x='Date',
                       y='Volume',
                       title='Объем торгов')

    # 5. Подготовка графика RSI
    rsi_fig = go.Figure()  # Пустой график по умолчанию
    rsi_style = {'display': 'none'}  # Стиль - скрыт

    # Если RSI выбран
    if rsi_values:
        window = rsi_values[0]  # Получаем период RSI

        # Рассчитываем RSI:
        # 1. Разница между текущей и предыдущей ценой закрытия
        delta = filtered_data['Close'].diff()

        # 2. Отделяем рост и падение
        gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()

        # 3. Рассчитываем относительную силу
        rs = gain / loss

        # 4. Рассчитываем RSI
        rsi = 100 - (100 / (1 + rs))

        # Создаем график RSI
        rsi_fig.add_trace(go.Scatter(
            x=filtered_data['Date'],
            y=rsi,
            name='RSI',
            line=dict(color='purple', width=2)
        ))

        # Добавляем горизонтальные линии уровней 30 и 70
        rsi_fig.add_hline(y=30, line_dash="dash", line_color="red")
        rsi_fig.add_hline(y=70, line_dash="dash", line_color="red")

        # Настраиваем заголовок и размер
        rsi_fig.update_layout(title=f'RSI ({window} дней)', height=300)

        # Делаем график видимым
        rsi_style = {'display': 'block'}

    # 6. Общие настройки для всех графиков
    for fig in [price_fig, volume_fig, rsi_fig]:
        fig.update_layout(
            hovermode='x unified',  # Общий режим hover для всех графиков
            margin=dict(l=20, r=20, t=40, b=20),  # Отступы
            xaxis=dict(rangeslider=dict(visible=False))  # Скрываем rangeslider

    # Дополнительные настройки для графиков
    price_fig.update_layout(height=500)  # Высота ценового графика
    volume_fig.update_layout(height=250) # Высота графика объемов

    # Возвращаем все графики и стиль RSI
    return price_fig, volume_fig, rsi_fig, rsi_style

Шаг 6: Запуск приложения

# Запускаем приложение, если скрипт выполняется напрямую
if __name__ == '__main__':
    # app.run - основной метод для запуска приложения Dash
    # debug=True - включение режима отладки
    # host='0.0.0.0' - делает приложение доступным в локальной сети
    # port=8050 - порт для доступа к приложению
    app.run(debug=True, host='0.0.0.0', port=8050)

Как работает дашборд

  1. Генерация данных: Создаются синтетические OHLC-данные за 60 дней с случайными колебаниями цен.
  2. Инициализация Dash: Создается веб-приложение с помощью Dash.
  3. Макет интерфейса:
    • Левая панель с элементами управления (выбор типа графика, диапазона дат, индикаторов)
    • Правая область с тремя графиками (цен, объемов и RSI)
  4. Интерактивность:
    • При изменении любого параметра срабатывает callback-функция
    • Данные фильтруются по выбранному диапазону дат
    • Строятся соответствующие графики с учетом всех выбранных параметров
  5. Технические индикаторы:
    • Скользящие средние добавляются как дополнительные линии на ценовой график
    • RSI отображается на отдельном графике, когда выбран
  6. Запуск: Приложение становится доступно по адресу http://localhost:8050

Этот дашборд можно легко расширить, добавив:

  • Загрузку реальных данных через API (например, yfinance)
  • Дополнительные технические индикаторы
  • Возможность сравнения нескольких активов
  • Экспорт данных и графиков

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

Хотите разбираться в рынках и зарабатывать на них при любых условиях?

Подписывайтесь на наш Telegram-канал о финансах и Data Science в трейдинге:
– Делаем глубокую аналитику
– Тестируем и разрабатываем торговые стратегии
– Кодим индикаторы и алгоритмы
– Собираем базу знаний для трейдеров

Подписаться
Хотите разбираться в рынках и зарабатывать на них при любых условиях?

Подписывайтесь на наш Telegram-канал о финансах и Data Science в трейдинге:
– Делаем глубокую аналитику
– Тестируем и разрабатываем торговые стратегии
– Кодим индикаторы и алгоритмы
– Собираем базу знаний для трейдеров

Подписаться

Научитесь читать графики всего за 15 минут

Заберите простой мини-гайд для быстрого старта в трейдинге. Идеально для новичков

Подтвердите согласие
Как изменившийся мир влияет на инвестиции: главные тренды 2025 года
Сейчас февраль 2025 года — мир продолжает меняться, и это неизбежно влияет на экономику, технологии, финансовые рынки и торговлю. Инфляция,...
Читать далее
📈 Как создать индикатор боллинджера в трейдинге на Python
Индикатор полосы боллинджера (волны боллинджера, боллинджер бандс) — один из самых популярных индикаторов для технического анализа. Он помогает трейдерам определять...
Читать далее
Как начать торговать без риска: зачем нужен демо счет на бирже и как его открыть
Многие, кто только начинают интересоваться трейдингом, задаются вопросом: с чего начать, чтобы не потерять деньги уже на первом шаге? Ответ...
Читать далее