Реализация уведомлений о ценах криптовалют (Price Alerts) в мобильном приложении

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

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Реализация уведомлений о ценах криптовалют (Price Alerts) в мобильном приложении
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • 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

Реализация уведомлений о ценах криптовалют (Price Alerts) в мобильном приложении

Price Alerts — классически простая на вид задача: пользователь ставит цену, при достижении которой приходит уведомление. На практике это real-time система с ценовым стримом, логикой триггеров и гарантированной доставкой push. Главный вопрос архитектуры — где проверять условия: на сервере или на клиенте.

Серверная vs клиентская проверка

Клиентская — приложение в фоне опрашивает цену и сравнивает с порогом. Не работает: iOS убивает фоновые процессы через несколько минут, Android без foreground service — тоже. Непригодно для production.

Серверная — единственно правильный вариант. Сервер получает ценовой стрим, проверяет алерты всех пользователей, при срабатывании отправляет push. Клиент только создаёт/удаляет алерты и получает уведомления.

Ценовой стрим: источники данных

Варианты для получения real-time цен:

Источник Протокол Задержка Покрытие
Binance WebSocket WSS < 100ms Все торговые пары Binance
CoinGecko API REST polling 30–60 сек 10,000+ монет
CryptoCompare WebSocket WSS < 500ms Агрегация бирж
Coinbase Advanced Trade WSS < 200ms Только Coinbase пары

Для real-time алертов — WebSocket. Для менее срочных — polling.

Бэкенд подписывается на Binance WebSocket:

// Node.js — подписка на цены через Binance WebSocket
const WebSocket = require('ws');

const PAIRS = ['btcusdt', 'ethusdt', 'solusdt'];
const ws = new WebSocket(`wss://stream.binance.com:9443/stream?streams=${PAIRS.map(p => p + '@ticker').join('/')}`);

ws.on('message', (data) => {
    const { stream, data: ticker } = JSON.parse(data);
    const symbol = stream.replace('@ticker', '').toUpperCase();
    const price = parseFloat(ticker.c); // текущая цена

    priceCache.set(symbol, price);
    alertEngine.checkAlerts(symbol, price);
});

Движок алертов

При каждом обновлении цены — проверяем все активные алерты для этой пары:

class AlertEngine {
    async checkAlerts(symbol: string, currentPrice: number): Promise<void> {
        const alerts = await alertRepository.getActiveAlerts(symbol);

        const triggered = alerts.filter(alert => {
            if (alert.type === 'ABOVE') return currentPrice >= alert.targetPrice;
            if (alert.type === 'BELOW') return currentPrice <= alert.targetPrice;
            if (alert.type === 'PERCENT_CHANGE') {
                const change = Math.abs((currentPrice - alert.basePrice) / alert.basePrice * 100);
                return change >= alert.percentThreshold;
            }
            return false;
        });

        for (const alert of triggered) {
            await this.fireAlert(alert, currentPrice);
        }
    }

    private async fireAlert(alert: PriceAlert, price: number): Promise<void> {
        // Деактивируем алерт чтобы не дублировать уведомление
        await alertRepository.deactivate(alert.id);

        // Отправляем push
        await pushService.sendToUser(alert.userId, {
            title: `${alert.symbol} достиг ${formatPrice(price)}`,
            body: this.buildAlertMessage(alert, price),
            data: { screen: 'price_detail', symbol: alert.symbol }
        });

        // Сохраняем в историю
        await alertRepository.saveTriggeredAlert(alert, price);
    }
}

Деактивация до отправки push — важно. Если push-отправка зафейлится и будет retry, алерт уже деактивирован — нет дублей.

Мобильный клиент: создание алерта

// iOS — форма создания алерта
struct CreateAlertView: View {
    @State private var targetPrice: String = ""
    @State private var alertType: AlertType = .above
    let symbol: String
    let currentPrice: Double

    var body: some View {
        Form {
            Section("Условие") {
                Picker("Тип алерта", selection: $alertType) {
                    Text("Цена выше").tag(AlertType.above)
                    Text("Цена ниже").tag(AlertType.below)
                    Text("Изменение %").tag(AlertType.percentChange)
                }
                .pickerStyle(.segmented)

                HStack {
                    Text("$")
                    TextField("0.00", text: $targetPrice)
                        .keyboardType(.decimalPad)
                }
            }

            Section {
                Text("Текущая цена: \(formatPrice(currentPrice))")
                    .foregroundColor(.secondary)
            }

            Button("Создать алерт") {
                createAlert()
            }
            .disabled(targetPrice.isEmpty)
        }
    }
}

Список активных алертов

Пользователь должен видеть все свои алерты и управлять ими. Каждый алерт — отображение условия, текущей цены относительно порога, кнопка удаления.

Для визуализации близости к порогу — progress indicator с текущей ценой между базовой и целевой:

// Android — визуализация расстояния до порога
@Composable
fun AlertProgressBar(currentPrice: Double, targetPrice: Double, basePrice: Double) {
    val progress = ((currentPrice - basePrice) / (targetPrice - basePrice)).coerceIn(0.0, 1.0)
    LinearProgressIndicator(
        progress = progress.toFloat(),
        modifier = Modifier.fillMaxWidth(),
        color = if (progress > 0.8) Color.Orange else MaterialTheme.colorScheme.primary
    )
    Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
        Text(formatPrice(basePrice), style = MaterialTheme.typography.labelSmall)
        Text("Цель: ${formatPrice(targetPrice)}", style = MaterialTheme.typography.labelSmall)
    }
}

Повторяемые алерты

По умолчанию алерт срабатывает один раз и деактивируется. Пользователь может выбрать «повторять» — тогда алерт реактивируется через N минут после срабатывания, чтобы не спамить при volatile рынке:

if (alert.isRepeating) {
    const cooldownMs = alert.cooldownMinutes * 60 * 1000;
    await alertRepository.scheduleReactivation(alert.id, Date.now() + cooldownMs);
}

Сроки

Реализация серверного движка алертов с WebSocket ценовым стримом (Binance/CryptoCompare), мобильный UI создания/управления алертами, push при срабатывании с историей — 8–12 рабочих дней.