Когда я начал торговать криптой, меня раздражало одно: большинство аналитических инструментов выдают «BUY» или «SELL» без объяснений. Три индикатора сказали покупать — вот тебе сигнал. Никаких весов, никакого контекста, никакой логики за цифрой.
Ссылка на статью о библиотеке Ta-Lib: TA-Lib Python: руководство для алготрейдера
Я решил сделать иначе. Программа, о которой пойдёт речь — это десктопное приложение для Windows, которое запускается двойным кликом, подключается к открытому API Binance и выдаёт взвешенный Score по шести категориям индикаторов, бэктест на последних 100 барах, уровни поддержки/сопротивления по фракталам и ATR-метрики риска. В статье расскажу, как всё это устроено изнутри — с формулами и кодом.

Архитектура: как данные превращаются в сигнал
Поток данных выглядит так:
Binance REST API ↓ get_binance_data() — 500 свечей OHLCV ↓ calculate_all_indicators() — 17+ индикаторов через TA-Lib ↓ detect_patterns() — 58 свечных паттернов ↓ generate_professional_signals() — взвешенный Score по 6 категориям ↓ Flask API → браузер → интерактивные графики + аналитическая панель
Данные приходят с публичного эндпоинта api.binance.com/api/v3/klines — он не требует авторизации. Программа запрашивает 300–500 свечей, конвертирует их в pandas DataFrame с типами float64 и передаёт дальше по цепочке.
Индикаторы: что считается и как
Все индикаторы рассчитываются через TA-Lib — C-библиотеку с Python-обвязкой. Скорость расчёта на 500 свечах — единицы миллисекунд.
Скользящие средние
indicators['SMA_7'] = talib.SMA(close, timeperiod=7) indicators['SMA_25'] = talib.SMA(close, timeperiod=25) indicators['SMA_50'] = talib.SMA(close, timeperiod=50) indicators['SMA_100'] = talib.SMA(close, timeperiod=100) indicators['SMA_200'] = talib.SMA(close, timeperiod=200) indicators['EMA_12'] = talib.EMA(close, timeperiod=12) indicators['EMA_26'] = talib.EMA(close, timeperiod=26)

SMA — простая скользящая средняя, среднее арифметическое цен закрытия за N баров:
SMA(n) = (C₁ + C₂ + ... + Cₙ) / n
EMA — экспоненциальная, придаёт больший вес свежим ценам:
EMA(t) = C(t) × k + EMA(t-1) × (1 - k), где k = 2 / (n + 1)
Для EMA(12): k = 2/13 ≈ 0.154. Для EMA(26): k = 2/27 ≈ 0.074. Именно поэтому EMA(12) реагирует на движение цены быстрее.
MACD

macd, signal, hist = talib.MACD(close, fastperiod=12, slowperiod=26, signalperiod=9)
MACD — разность двух EMA:
MACD = EMA(12) - EMA(26) Signal = EMA(9) от MACD Histogram = MACD - Signal
Гистограмма — ключевой элемент. Когда она растёт в положительной зоне — импульс усиливается. Когда начинает падать, оставаясь положительной — импульс слабеет, хотя тренд ещё вверх. Это ранний сигнал разворота.
RSI

indicators['RSI'] = talib.RSI(close, timeperiod=14)
RSI Уайлдера измеряет скорость и размер ценовых изменений:
RS = Avg(Up closes, 14) / Avg(Down closes, 14) RSI = 100 - 100 / (1 + RS)
Диапазон 0–100. Традиционные уровни 30/70. Программа использует расширенные: < 25 — «глубоко перепродан» (вес сигнала 1.0), < 30 — «перепродан» (0.7), < 40 — «близко к перепроданности» (0.2). Аналогично для верхней зоны.
Stochastic

stoch_k, stoch_d = talib.STOCH( high, low, close, fastk_period=14, slowk_period=3, slowk_matype=0, slowd_period=3, slowd_matype=0 )
Стохастик сравнивает цену закрытия с диапазоном цен за период:
%K = (Close - Lowest Low₁₄) / (Highest High₁₄ - Lowest Low₁₄) × 100 %D = SMA(3) от %K
Программа реагирует не только на уровни, но и на кроссовер %K/%D в зонах перекупленности/перепроданности — это более точный сигнал, чем просто факт вхождения в зону.
Bollinger Bands

bb_upper, bb_middle, bb_lower = talib.BBANDS( close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0 )
Полосы Боллинджера:
Middle = SMA(20) Upper = SMA(20) + 2 × σ(20) Lower = SMA(20) - 2 × σ(20)
где σ(20) — стандартное отклонение цен закрытия за 20 баров. При нормальном распределении ~95% цен находится внутри полос. Статистически цена за пределами полосы — аномалия, которая имеет тенденцию к возврату к средней.
Ключевой производный показатель — %B (Percent Bandwidth):
%B = (Close - Lower) / (Upper - Lower)
%B = 0 — цена на нижней полосе, %B = 1 — на верхней, < 0 или > 1 — за полосой.
Второй производный — BB Width, ширина полос:
BBW = (Upper - Lower) / Middle × 100
Когда BBW в нижних 10% своего исторического диапазона — BB Squeeze: волатильность сжата до минимума, рынок накапливает энергию для сильного движения.
ADX и направленное движение
indicators['ADX'] = talib.ADX(high, low, close, timeperiod=14) indicators['PLUS_DI'] = talib.PLUS_DI(high, low, close, timeperiod=14) indicators['MINUS_DI'] = talib.MINUS_DI(high, low, close, timeperiod=14)
Система Уайлдера для оценки силы и направления тренда:
+DM = max(High - PrevHigh, 0), если > |Low - PrevLow|, иначе 0 -DM = max(PrevLow - Low, 0), если > |High - PrevHigh|, иначе 0 TR = max(High - Low, |High - PrevClose|, |Low - PrevClose|) +DI = 100 × EMA(14, +DM) / EMA(14, TR) -DI = 100 × EMA(14, -DM) / EMA(14, TR) DX = 100 × |+DI - -DI| / (+DI + -DI) ADX = EMA(14, DX)

ADX измеряет силу тренда, не направление. ADX > 25 — сильный тренд, > 40 — очень сильный. Направление даёт сравнение +DI и -DI: +DI > -DI означает восходящий тренд.
В программе ADX используется двояко: определяет направление сигнала через +DI/-DI, и влияет на Confidence (уверенность итогового сигнала).
ATR
indicators['ATR'] = talib.ATR(high, low, close, timeperiod=14)
True Range — наибольшее из трёх значений:
TR = max(High - Low, |High - PrevClose|, |Low - PrevClose|) ATR = EMA(14, TR)

ATR — мера волатильности в абсолютных единицах цены. Используется в программе в трёх местах: как составляющая риск-метрик (стоп/тейк), для оценки текущего режима волатильности (сравнение с 20-баровым средним ATR) и как исходный параметр для определения размера позиции.
OBV
indicators['OBV'] = talib.OBV(close, volume)

On-Balance Volume — накопительный индикатор давления покупок/продаж:
OBV(t) = OBV(t-1) + Volume(t), если Close > PrevClose OBV(t) = OBV(t-1) - Volume(t), если Close < PrevClose OBV(t) = OBV(t-1), если Close = PrevClose
Смысл в дивергенции: если OBV растёт, а цена падает — умные деньги покупают на снижении. Программа считает наклон OBV за последние 10 баров и сравнивает его с направлением цены.
CCI, Williams %R, MFI
indicators['CCI'] = talib.CCI(high, low, close, timeperiod=20) indicators['WILLR'] = talib.WILLR(high, low, close, timeperiod=14) indicators['MFI'] = talib.MFI(high, low, close, volume, timeperiod=14)
CCI измеряет отклонение типичной цены от её скользящей средней:
TP = (High + Low + Close) / 3 CCI = (TP - SMA(TP, 20)) / (0.015 × MeanDeviation)
Зоны ±100 — традиционные уровни, ±150 — экстремальные.
Williams %R — перевёрнутый стохастик:
%R = (Highest High₁₄ - Close) / (Highest High₁₄ - Lowest Low₁₄) × (-100)
Диапазон от 0 до -100. < -80 — перепроданность, > -20 — перекупленность.
MFI (Money Flow Index) — RSI, взвешенный по объёму:
TP = (High + Low + Close) / 3 Money Flow = TP × Volume Positive MF = сумма Money Flow за дни, когда TP > PrevTP Negative MF = сумма Money Flow за дни, когда TP < PrevTP MFI = 100 - 100 / (1 + Positive MF / Negative MF)

Отличие от RSI: учитывает объём. MFI < 20 означает, что деньги уходят с рынка при снижении объёма — давление продавцов ослабевает.
Parabolic SAR
indicators['SAR'] = talib.SAR(high, low, acceleration=0.02, maximum=0.2)
Параболический SAR — следящий стоп, который автоматически поднимается за ценой при восходящем тренде:
SAR(t) = SAR(t-1) + AF × (EP - SAR(t-1))
где AF (Acceleration Factor) начинается с 0.02 и увеличивается на 0.02 при каждом новом экстремуме, но не более 0.20. EP — крайняя точка (Extreme Point): максимум при восходящем тренде, минимум при нисходящем.
Когда цена пересекает SAR — тренд считается сменившимся.
58 свечных паттернов
Каждый паттерн проверяется функцией TA-Lib вида talib.CDL*(open, high, low, close). Возвращаемое значение: +100 (бычий), -100 (медвежий), 0 (паттерн не обнаружен). Некоторые паттерны возвращают промежуточные значения от -200 до +200 для обозначения силы.

Программа проверяет последние 5 свечей — это нужно для многосвечных паттернов (утренняя звезда, три белых солдата), которые формируются на нескольких барах.
Примеры логики обнаружения (внутри TA-Lib):
Молот (Hammer): маленькое тело в верхней части диапазона, длинная нижняя тень не менее 2× тела, маленькая или отсутствующая верхняя тень. Появляется после нисходящего тренда.
Поглощение (Engulfing): вторая свеча полностью поглощает тело первой. При бычьем поглощении: первая красная, вторая зелёная и больше по телу.
Три белых солдата: три последовательных длинных зелёных свечи, каждая открывается выше предыдущей и закрывается в верхней части своего диапазона.
В системе сигналов каждый паттерн имеет свой вес от 0.3 до 1.0. Вес определяет вклад паттерна в категорию «Паттерны» (8% итогового Score).
Мультифакторная система сигналов

Это центральная часть программы. Логика реализована в signal_engine.py (775 строк).
Структура весов
CATEGORY_WEIGHTS = { 'trend': 0.28, 'momentum': 0.22, 'macd': 0.18, 'volatility': 0.14, 'volume': 0.10, 'patterns': 0.08, }
Сумма весов = 1.0. Каждая категория возвращает cat_score в диапазоне [-1, +1].
Итоговый Score
total_score = sum(cat_scores.get(cat, 0) * w for cat, w in CATEGORY_WEIGHTS.items())
Это взвешенная сумма: Score = 0.28×trend + 0.22×momentum + 0.18×macd + 0.14×volatility + 0.10×volume + 0.08×patterns
Результат в диапазоне [-1, +1]:
Score ≥ +0.45 → STRONG BUY Score ≥ +0.20 → BUY −0.20 < Score < +0.20 → NEUTRAL Score ≤ −0.20 → SELL Score ≤ −0.45 → STRONG SELL
Как считается cat_score для каждой категории
Каждый сигнал внутри категории имеет свой вес. Например, категория «Тренд»:
Событие |
Вес сигнала |
|---|---|
Golden Cross SMA 50/200 |
+1.0 |
Death Cross SMA 50/200 |
−1.0 |
Golden Cross EMA 12/26 |
+0.8 |
Death Cross EMA 12/26 |
−0.8 |
SMA 50 > SMA 200 (устойчиво) |
+0.4 |
SAR разворот вверх |
+0.6 |
SAR разворот вниз |
−0.6 |
Цена выше SAR |
+0.2 |
ADX направление (+DI > -DI) |
+0.4 |
Цена >5% выше SMA 50 |
−0.3 (перекуп) |
cat_score нормируется делением на 3 (максимально возможная сумма весов в категории) и обрезается до [-1, +1]:
cat_scores['trend'] = max(-1.0, min(1.0, trend_score / 3))
Дивергенции

Дивергенции — один из самых сильных сигналов разворота. Реализованы в упрощённой форме через сравнение изменений за фиксированный период:
# RSI дивергенция price_chg = (close[-1] - close[-5]) / close[-5] * 100 rsi_chg = rsi_val - _safe(rsi, -5) if price_chg < -2 and rsi_chg > 2: # Бычья: цена падает, RSI растёт → вес 0.8 # MACD гистограмма дивергенция price_chg = (close[-1] - close[-10]) / close[-10] * 100 mh_chg = float(macd_hist[-1]) - float(macd_hist[-10]) if price_chg < -3 and mh_chg > 0: # Бычья: цена падает, гистограмма растёт → вес 0.9
Это упрощение: классическое определение дивергенции требует поиска локальных экстремумов. Но сравнение за фиксированный период работает как быстрый и достаточно надёжный фильтр.
Уверенность (Confidence)
adx_conf = min(1.0, adx_val / 50) signal_count = количество сработавших сигналов confidence = min(1.0, adx_conf * 0.5 + min(signal_count, 10) / 10 * 0.5)
Confidence = среднее двух составляющих:
ADX-компонента: чем сильнее тренд, тем выше уверенность (ADX 50+ → 100%)
Насыщенность: чем больше сигналов сработало, тем выше уверенность (10+ сигналов → 100%)
Уровни поддержки и сопротивления: алгоритм фракталов
def _find_support_resistance(high, low, close, n_levels=4, window=5): for i in range(window, len(highs) - window): if highs[i] == max(highs[i-window:i+window+1]): fractal_highs.append(float(highs[i])) if lows[i] == min(lows[i-window:i+window+1]): fractal_lows.append(float(lows[i]))
Шаг 1: Фракталы. Точка является фрактальным максимумом, если она выше всех соседних точек в окне ±5 баров. Аналогично для минимумов. Это классическое определение Билла Вильямса.
Шаг 2: Кластеризация. Близкие уровни сливаются в один:
def cluster(levels, radius_pct=0.5): levels = sorted(levels) clusters = [] group = [levels[0]] for v in levels[1:]: if abs(v - group[-1]) / group[-1] * 100 <= radius_pct: group.append(v) # уровни в радиусе 0.5% — один кластер else: clusters.append(np.mean(group)) # среднее кластера group = [v]
Порог 0.5%: если два уровня отстоят менее чем на 0.5% — они сливаются в один (среднее значение). Это убирает «кашу» из близких уровней и даёт чистые значимые зоны.
Шаг 3: Фильтрация. Из кластеров выбираются 4 ближайших поддержки (ниже текущей цены, с отступом 0.2%) и 4 ближайших сопротивления (выше цены).
Бэктест: как проверяется историческая точность
def _backtest_rule(signal_series, close, forward=3, lookback=100): for i in range(start, end): s = sig[i] if s == 0: continue entry = close[i] exit_ = close[i + forward] # закрытие через 3 бара ret = (exit_ - entry) / entry * 100 * s returns.append(ret) if ret > 0: wins += 1
Для каждого момента в прошлом, где сработал сигнал, программа смотрит, что было через 3 бара. Если сигнал был на покупку (s = +1) и цена выросла — сделка прибыльная.
Результат: количество сделок, win rate (%), средняя доходность сделки (%).
Важное ограничение: бэктест на тех же данных что используются для анализа — это in-sample тест. Он показывает, как правило работало на недавней истории. Не является доказательством будущей эффективности.
Риск-метрики
atr_stop_long = round(price - 2 * atrv, 6) # стоп-лосс atr_tp_long = round(price + 3 * atrv, 6) # тейк-профит rr = 3.0 # R/R = 1:3

ATR-стоп — один из наиболее распространённых методов постановки стопа. Логика: стоп ставится на расстоянии 2×ATR от цены входа. ATR отражает среднюю волатильность — нормальные колебания цены не должны выбивать стоп.
Волатильность за 20 баров:
returns20 = np.diff(np.log(close[-21:])) vol20 = float(np.std(returns20)) * 100
Стандартное отклонение лог-доходностей × 100. Это «однобаровая волатильность» в процентах. Для дневных данных умножение на √252 даёт годовую волатильность, но в программе используется именно однобаровая — она нагляднее для оценки риска текущей позиции.
Max Drawdown за 50 баров:
peak = np.maximum.accumulate(close[-50:]) dd = (close[-50:] - peak) / peak * 100 max_drawdown = float(np.min(dd))
Максимальное отклонение от исторического пика на последних 50 барах. Характеризует риск удержания позиции.
Технические решения, которые стоят упоминания
Почему Flask, а не обычное окно приложения?
Интерактивные финансовые графики с зумом, панорамой и переключением индикаторов — это задача для браузерных библиотек (Chart.js). Написать то же самое на tkinter или PyQt потребовало бы в несколько раз больше кода и дало худший результат визуально. Flask запускает локальный сервер на порту 5000, браузер открывается автоматически.
Зум и панорама на canvas-графиках
Полноэкранные графики (свечной, BB, RSI, MACD, Stochastic, объём) рисуются вручную на <canvas> без Chart.js. Для них реализован собственный зум и панорама через modalViewport:
function handleModalWheel(e) { const zoomFactor = e.deltaY > 0 ? 1.15 : (1 / 1.15); let newRange = Math.round(range * zoomFactor); const ratio = (e.clientX - rect.left) / rect.width; const centerIdx = view.start + range * ratio; // масштаб с центром в позиции курсора let newStart = Math.round(centerIdx - newRange * ratio); }
Колесо мыши масштабирует диапазон относительно позиции курсора. Перетаскивание сдвигает видимую область. Кнопка «Сброс» — modalViewport = null.
Сборка в .exe
Программа поставляется как standalone-бинарник, собранный через PyInstaller (--onefile --collect-all talib). При запуске Python-интерпретатор и все зависимости распаковываются в системную временную папку. Для корректной работы Flask с templates внутри .exe используется sys._MEIPASS:
if getattr(sys, 'frozen', False): BASE_DIR = sys._MEIPASS else: BASE_DIR = os.path.dirname(os.path.abspath(__file__)) app = Flask(__name__, template_folder=os.path.join(BASE_DIR, 'templates'))
Что не реализовано и почему
Нет реального времени. WebSocket-подписка на тикер Binance технически несложна, но усложняет архитектуру: нужен фоновый поток, синхронизация с основным, инкрементальное обновление графиков. Текущая версия — запрос по кнопке.
Нет скринера. Анализировать список из 50 монет последовательно займёт ~2 минуты (4 секунды на тикер: запрос к API + расчёт). Параллельные запросы упираются в rate limit Binance. Реализуемо, но требует очереди с задержками.
Нет кэша. Каждый запрос «Анализировать» — новый HTTP-запрос к Binance. Для нормального использования это не проблема. При частом нажатии Binance начинает отвечать 429 (Too Many Requests).
Дивергенции упрощены. Классический алгоритм поиска дивергенций требует нахождения локальных экстремумов через пиковый детектор (например, scipy.signal.argrelmax). В текущей версии сравниваются значения с фиксированным лагом (5 и 10 баров). Это даёт ложные срабатывания в боковом рынке.
AROON не влияет на Score. Рассчитывается, отображается в таблице индикаторов, но в signal_engine.py не используется. Причина прагматичная: AROON хорошо работает для идентификации начала тренда, но его интерпретация сильно зависит от таймфрейма.
Итого
Программа состоит из двух смысловых частей: стандартный конвейер расчёта индикаторов через TA-Lib и нестандартный движок сигналов с категорийными весами. Второе — основная идея. Вместо «три индикатора сказали покупать» — взвешенный Score от -1 до +1, который учитывает относительную важность каждой группы сигналов.
Всё это работает локально, данные никуда не уходят, интернет нужен только для запроса свечей с Binance.
Код написан на Python, собран в .exe для Windows.
Комментарии (12)

Dreams_and_magic
14.06.2026 19:40А почему не Метатрейдер? Там уже встроенные индикаторы, и ничего "рисовать" на экране не нужно. И оптимизатор есть, и тестер стратегий...

ura-ch Автор
14.06.2026 19:40Метатрейдер - это тяжелый терминал, в котором рядовому пользователю нужно долго разбираться. Там придется искать подходящего брокера, настраивать потоки котировок крипты и вручную собирать шаблоны с графиками.
Моя программа предлагает другой подход - всё работает сразу из коробки. Вы просто запускаете файл и моментально получаете готовый аналитический дашборд с конкретными сигналами, риск-метриками и паттернами в одном окне.
К тому же, за копейки вы получаете этот инструмент навсегда. В базовом Метатрейдере нет такой готовой сводной панели с баллами и оценкой уверенности. Чтобы собрать нечто подобное там, придется идти в их магазин и покупать кастомные индикаторы или советников. Хорошие сборки там стоят в десятки раз дороже, и продаются по ежемесячной подписке. Здесь же платится один раз и получаете понятную картину рынка без танцев с бубном и сложных настроек.

tarle18
14.06.2026 19:40Где можно более подробно ознакомиться с проэктом и скачать саму программу?

ura-ch Автор
14.06.2026 19:40https://hummingbot.ru/ - ссылка на сайт с программой
https://www.youtube.com/watch?v=HEIiTj0qYtY - ссылка на видео

saitporyadke
14.06.2026 19:40Понравилась прозрачность: веса по категориям + ограничение in-sample в бэктесте — редко кто так пишет. Вопрос практический: пробовали ли walk-forward (обучить веса на одном окне, проверить на следующем)? Без этого STRONG BUY на боковике легко ловит ложняк, особенно на упрощённых дивергенциях с лагом 5/10.

ura-ch Автор
14.06.2026 19:40walk-forward я пока не делал. Веса категорий (28, 22, 18, 14, 10, 8%) - это вообще не результат машинного обучения, а моя ручная настройка по опыту. Я просто примерно прикинул, какие группы индикаторов важнее, и зафиксировал. То же самое с порогами Score и весами внутри категорий.И вы абсолютно правы про боковик. Это слабое место. Мой текущий бэктест на 100 барах in-sample не даёт гарантии, что веса не подогнаны под конкретный трендовый кусок истории. На флэте дивергенции с фиксированным лагом 5/10 начинают хаотично дёргаться, и сигналы становятся ложными, как вы и описали.
Walk-forward - это правильный путь, но там сразу куча вопросов: что именно оптимизировать, как не переобучить веса на шум, да и пространство параметров большое, если учесть, что внутри каждой категории ещё свои правила. На 100 барах это легко превращается в подгонку. Поэтому в следующей версии я хочу сделать проще — добавить фильтр по ADX и ширине полос Боллинджера. Если рынок явно во флэте, просто снижать вес дивергенций или вообще выключать их из итогового Score. Это не решит все проблемы, но конкретно этот случай должно закрыть, и без лишнего риска. И по поводу доработок: если добавлять полноценный walk-forward и обучать веса статистически, то цена программы, естественно, вырастёт. Но реализация, скорее всего, будет нулевая так как у нас, к сожалению, российская аудитория, вечно хотящая халявы.

Mayurifag
Кажется, ваш AI не добавил ссылку на приложение в статью. Во всяком случае ссылку на гитхаб поиском по странице не нашёл. Ещё не нашёл ссылку на телеграм канал и это радует!
Не совсем остался отвеченным вопрос, как именно вы используете написанное приложение. Есть какие-нибудь дальнейшие планы на развитие? Если вы торгуете руками, то вам в любом случае рано или поздно вебсокет понадобится. А там дальше алерты, затем алготрейдинг.
Какая-то есть причина помимо личных преференций в сборке бинарника или вполне ваш код мог остаться браузерной страницей для использования на MacOS/Linux?
Непонял как вы выдали веса той или иной категории. Это вы уже бектест прогнали на многих монетах на многих таймфреймах и вывели универсальную формулу где шарп будет самым лучшим? Делали бектест на умерших монетах? Или просто на свой вкус сделали формулу? Отдельно интересно что ваша комбинация показывала в черные криптушные дни по типу 10 октября 2025.
Спасибо, что делитесь опытом!
sunnybear
Спасибо за нетоксичный комментарий!
ura-ch Автор
Я собрал всё в exe-файл по двум простым причинам. Во-первых, я изначально не планировал выкладывать исходный код в открытый доступ, и бинарник решает эту задачу. Во-вторых, это банально удобнее для пользователей в формате "скачал и запустил". Человеку не нужно ставить интерпретатор, настраивать окружение и мучиться с установкой сишной библиотеки TA-Lib под Windows.
По поводу весов индикаторов, они взяты не с потолка. Если заглянуть в архитектуру, движок не просто суммирует сигналы. В нем реализован бэктест без заглядывания вперед: система на лету проверяет историческую отработку каждого правила и уже на основе этого формирует итоговый скор и уверенность.
Про вебсокеты замечание верное, но только если речь идет о высокочастотном алготрейдинге. Моя программа решает задачу десктопного аналитика-советника для человека. Для того чтобы оценивать рынок и обновлять графики, обычного REST API сейчас хватает за глаза. Переход на сокеты — это уже следующий этап для полноценного торгового бота.