Как построить рыночный профиль (Market Profile) с помощью Python

Рыночный профиль (Market Profile) — это мощный инструмент анализа, который помогает трейдерам понять распределение цен и объемов в течение определенного периода. В этой статье мы разберем, как использовать Python и Plotly для построения рыночного профиля, включая его реализацию через shapes для наглядного отображения.
Из материала вы узнаете:
- Как получить данные OHLC (Open, High, Low, Close) с помощью yfinance
- Как сгруппировать данные по дню, неделе, месяцу для рыночного профиля
- Как визуализировать рыночный профиль через shapes в Plotly
- Полезные паттерны использования Market Profile
Что такое рыночный профиль и зачем он нужен?
Рыночный профиль отображает распределение объема по уровням цен. В отличие от классических свечных графиков, Market Profile показывает, где накапливается ликвидность, а где рынок движется без особого интереса участников.
Что можно увидеть в Market Profile:
- Уровни Value Area (зоны справедливой цены)
- Point of Control (PoC) — уровень с наибольшей ликвидностью
- Области с маленьким объемом (возможные уровни поддержки и сопротивления)
Шаг 1. Получаем данные OHLC с помощью Python
Для начала загрузим данные с yfinance.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import pandas as pd import numpy as np import plotly.graph_objects as go import yfinance as yf # <img draggable="false" role="img" class="emoji" alt=" TICKER = "AAPL" # Можно заменить на другой актив PERIOD = "1mo" # Длительность загрузки данных INTERVAL = "1h" # Интервал свечей # Функция для загрузки данных OHLC def get_ohlc_data(ticker = TICKER, period = PERIOD, interval = INTERVAL): data = yf.download(ticker, period = period, interval = interval) data.reset_index(inplace = True ) # <img draggable="false" role="img" class="emoji" alt=" data.columns = [col[ 0 ].lower() if isinstance (col, tuple ) else col.lower() for col in data.columns] return data # Загружаем данные data = get_ohlc_data() print (data.head()) # Проверяем структуру |
Разбор кода:
- Используем
yfinance
для загрузки данных - Переводим названия колонок в нижний регистр (
.lower()
) - Убираем возможный
MultiIndex
, чтобы избежать ошибок
Шаг 2. Рассчитываем Market Profile для разных временных интервалов
Теперь мы хотим строить Market Profile не только для 1 дня, но и для недели (1W) и месяца (1M).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | # <img draggable="false" role="img" class="emoji" alt=" MARKET_PROFILE_INTERVAL = "1D" # Варианты: "1D", "1W", "1M" PRICE_BINS_METHOD = "auto" # Количество бинов (можно поставить число) VOLUME_SCALE = 0.5 # Масштабирование ширины профиля # Функция для расчета Market Profile def calculate_market_profile(df, interval = MARKET_PROFILE_INTERVAL): if interval = = "1D" : df[ 'date' ] = df[ 'datetime' ].dt.floor( 'D' ) # Группируем по дням elif interval = = "1W" : df[ 'date' ] = df[ 'datetime' ].dt.to_period( 'W' ). apply ( lambda r: r.start_time) elif interval = = "1M" : df[ 'date' ] = df[ 'datetime' ].dt.to_period( 'M' ). apply ( lambda r: r.start_time) else : raise ValueError( "Неверный интервал! Используйте '1D', '1W' или '1M'." ) grouped = df.groupby( 'date' ) market_profiles = [] for date, group in grouped: price_bins = np.histogram_bin_edges(group[ 'close' ], bins = PRICE_BINS_METHOD) hist, edges = np.histogram(group[ 'close' ], bins = price_bins) market_profiles.append({ 'date' : pd.to_datetime(date), 'prices' : edges[: - 1 ], 'volumes' : hist / hist. max () * VOLUME_SCALE }) return market_profiles # Рассчитываем Market Profile market_profiles = calculate_market_profile(data, interval = MARKET_PROFILE_INTERVAL) print (market_profiles[: 2 ]) # Проверяем структуру |
Разбор кода:
- Группируем свечи по 1D, 1W или 1M
- Строим гистограмму по цене (np.histogram)
- Масштабируем объемы, чтобы Market Profile был читабельным
Шаг 3. Визуализируем Market Profile в Plotly (shapes)
Теперь построим Market Profile через shapes в Plotly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | # Функция для построения Market Profile через SHAPES def plot_market_profile(df, market_profiles): fig = go.Figure() # <img draggable="false" role="img" class="emoji" alt=" fig.add_trace(go.Candlestick( x = df[ 'datetime' ], open = df[ 'open' ], high = df[ 'high' ], low = df[ 'low' ], close = df[ 'close' ], name = 'OHLC' )) # <img draggable="false" role="img" class="emoji" alt=" shapes = [] for profile in market_profiles: date = profile[ 'date' ] for price, volume in zip (profile[ 'prices' ], profile[ 'volumes' ]): shapes.append( dict ( type = "line" , x0 = date, x1 = date + pd.Timedelta(hours = volume * 24 ), y0 = price, y1 = price, line = dict (color = "blue" , width = 2 ) ) ) # <img draggable="false" role="img" class="emoji" alt=" fig.update_layout( title = f 'Market Profile ({MARKET_PROFILE_INTERVAL}) for {TICKER}' , xaxis_title = 'Datetime' , yaxis_title = 'Price' , xaxis = dict ( type = 'date' , tickformat = "%Y-%m-%d" , tickmode = 'linear' , rangeslider = dict (visible = False )), yaxis = dict (autorange = 'reversed' ), showlegend = True , shapes = shapes ) fig.show() # Визуализация plot_market_profile(data, market_profiles) |
Разбор кода:
- Используем shapes для Market Profile (рисуем линии горизонтально)
- Привязываем их к ценовому диапазону и дате
- Market Profile теперь масштабируется динамически
Этот подход можно доработать и использовать в алготрейдинге, бэктестинге и реальном трейдинге.

Где пригодится Market Profile?
- Для поиска ключевых уровней поддержки и сопротивления
- Для определения балансных зон (где цена «застревает»)
- Для анализа объемов внутри свечных данных
- Для поиска Point of Control (PoC) — где цена была самой активной
Совет: Market Profile особенно полезен в фьючерсах и акциях, где объемы важны для принятия решений.
Код доступен в нашем репозитории на github. Не забудьте подписаться на профиль и поставить звездочку репозиторию.