Реализация графика свечей (Candlestick Chart) в мобильном приложении биржи

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.
Разработка и поддержка любых видов мобильных приложений:
Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Реализация графика свечей (Candlestick Chart) в мобильном приложении биржи
Сложная
~5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1054
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Реализация графика свечей (Candlestick Chart) в мобильном приложении биржи

Candlestick chart — технически самый сложный тип графика для мобильного приложения. Не из-за математики OHLC, а из-за набора требований, которые обязательны для биржевого UX: плавный zoom/pan по 10 000+ свечей, crosshair с координатами при касании, обновление в реальном времени без перерисовки всего графика, переключение таймфреймов без мигания, корректная работа в ландшафтной и портретной ориентации. Все эти требования вместе — и большинство готовых библиотек начинают ломаться.

Почему стандартные библиотеки не подходят

fl_chart — нет candlestick из коробки. Можно собрать из кастомных BarChartRod, но это костыль без zoom и приемлемой производительности.

syncfusion_flutter_charts — есть SfCartesianChart с CandleSeries, поддерживает zoom/pan, обновление данных. Это рабочий вариант для большинства задач. Но коммерческая лицензия ($995+/год для одного разработчика) делает его нецелесообразным для стартапов.

TradingView Lightweight Charts в WebView — наиболее распространённый подход в продакшен-приложениях крупных бирж. Библиотека написана для production trading UI, оптимизирована для больших объёмов данных, поддерживает все нужные возможности. Overhead от WebView — есть, но на современных устройствах незначителен.

Нативная реализация через CanvasCustomPainter (Flutter) или CALayer (iOS) или Canvas (Android). Максимальная производительность, полный контроль. Требует значительной разработки. Оправдано, если candlestick — центральный элемент продукта.

Нативная реализация на Flutter через CustomPainter

Для приложения, где chart — главный экран, нативная реализация даёт 60fps на любом устройстве. Ключевые компоненты:

Структура данных

class Candle {
  final int timestamp;  // Unix timestamp в ms
  final double open;
  final double high;
  final double low;
  final double close;
  final double volume;

  bool get isBullish => close >= open;
}

Render pipeline

CustomPainter с shouldRepaint — вызывается при каждом изменении данных. Чтобы не перерисовывать весь chart при получении новой свечи:

class CandlestickPainter extends CustomPainter {
  final List<Candle> candles;
  final CandleChartController controller; // хранит offset и scale

  @override
  void paint(Canvas canvas, Size size) {
    final visibleRange = controller.getVisibleRange(candles.length, size.width);
    final visibleCandles = candles.sublist(visibleRange.start, visibleRange.end);

    final priceRange = _calculatePriceRange(visibleCandles);
    final candleWidth = size.width / visibleCandles.length * controller.scale;

    for (var i = 0; i < visibleCandles.length; i++) {
      _drawCandle(canvas, visibleCandles[i], i, candleWidth, size.height, priceRange);
    }

    if (controller.crosshairVisible) {
      _drawCrosshair(canvas, controller.crosshairPosition, size);
    }
  }

  void _drawCandle(Canvas canvas, Candle c, int index, double width, double height, PriceRange range) {
    final x = index * width + width / 2;
    final paint = Paint()
      ..color = c.isBullish ? const Color(0xFF26A69A) : const Color(0xFFEF5350)
      ..strokeWidth = 1.5;

    // Фитиль (wick)
    final highY = range.toY(c.high, height);
    final lowY = range.toY(c.low, height);
    canvas.drawLine(Offset(x, highY), Offset(x, lowY), paint);

    // Тело свечи
    final openY = range.toY(c.open, height);
    final closeY = range.toY(c.close, height);
    final bodyPaint = Paint()..color = paint.color;

    final bodyRect = Rect.fromLTRB(
      x - width * 0.35, min(openY, closeY),
      x + width * 0.35, max(openY, closeY),
    );
    // Для hollow candles (контур для bullish):
    if (c.isBullish) {
      canvas.drawRect(bodyRect, bodyPaint..style = PaintingStyle.stroke);
    } else {
      canvas.drawRect(bodyRect, bodyPaint..style = PaintingStyle.fill);
    }
  }

  @override
  bool shouldRepaint(CandlestickPainter old) =>
      old.candles != candles || old.controller != controller;
}

Gesture handling: pan и pinch-zoom

GestureDetector с onScaleStart/Update для pinch-zoom, onPanUpdate для скролла по временной оси:

GestureDetector(
  onScaleUpdate: (details) {
    setState(() {
      controller.scale = (controller.scale * details.scale).clamp(0.5, 10.0);
      controller.offset += details.focalPointDelta.dx;
    });
  },
  child: CustomPaint(painter: CandlestickPainter(candles, controller)),
)

Clamp scale — важно: без ограничения пользователь уйдёт в режим, где одна свеча занимает весь экран.

Crosshair при long press

GestureDetector(
  onLongPressStart: (details) {
    controller.crosshairVisible = true;
    controller.crosshairPosition = details.localPosition;
    // Вычисляем ближайшую свечу к позиции касания
    final candleIndex = controller.positionToIndex(details.localPosition.dx, candles.length);
    if (candleIndex < candles.length) {
      _showCandleInfo(candles[candleIndex]);
    }
  },
  onLongPressMoveUpdate: (details) {
    controller.crosshairPosition = details.localPosition;
    // обновляем информационную панель
  },
  onLongPressEnd: (_) => controller.crosshairVisible = false,
)

Real-time обновление последней свечи

WebSocket подключение к бирже даёт tick-данные. При получении нового тика — обновляем только последнюю свечу, не перестраиваем весь список:

void onTickReceived(Tick tick) {
  if (_candles.isEmpty) return;
  final last = _candles.last;

  // Обновляем OHLC последней свечи
  _candles[_candles.length - 1] = last.copyWith(
    high: max(last.high, tick.price),
    low: min(last.low, tick.price),
    close: tick.price,
    volume: last.volume + tick.volume,
  );

  // Новый таймфрейм — новая свеча
  if (tick.timestamp >= last.timestamp + timeframe.milliseconds) {
    _candles.add(Candle.fromTick(tick));
    if (_candles.length > maxCandlesInMemory) _candles.removeAt(0);
  }

  // Notify только painter, не весь экран
  _chartController.notifyListeners();
}

ValueNotifier + ValueListenableBuilder только вокруг CustomPaint — перерисовывается только canvas, не AppBar и не боковые панели.

Переключение таймфреймов

Кнопки 1m / 5m / 15m / 1h / 4h / 1D / 1W. При переключении — запрашиваем новый набор свечей, применяем crossfade-анимацию, чтобы убрать «моргание»:

AnimatedSwitcher(
  duration: const Duration(milliseconds: 200),
  child: CandlestickWidget(
    key: ValueKey(selectedTimeframe), // при смене ключа — анимация
    candles: _candles,
  ),
)

Технические индикаторы (опционально)

MA (Moving Average), EMA, Bollinger Bands, RSI — каждый рисуется дополнительным слоем в CustomPainter. RSI и MACD — на отдельном нижнем CustomPaint с фиксированной высотой и общей горизонтальной осью времени с основным графиком.

Что входит в работу

  • Реализация candlestick CustomPainter или интеграция TradingView в WebView
  • Pan/zoom жесты с правильными ограничениями
  • Crosshair с информационной панелью
  • Real-time обновление через WebSocket
  • Переключение таймфреймов
  • Технические индикаторы (MA, EMA, Bollinger Bands — по согласованию)
  • Volume bars на нижней панели

Сроки

WebView + TradingView Lightweight Charts: 1–2 недели (включая WebSocket-интеграцию). Нативный CustomPainter с полным функционалом биржевого графика: 3–5 недель. Стоимость рассчитывается индивидуально.