← все проекты
Mar 2018 — May 2019

Smart Trade (Germany)

Backend-разработчик

SaaS-автоматизация торговли. Backend на Django + 30 REST endpoints, конструктор стратегий на React.

Метрики

−87%
Время настройки стратегии
−50%
Регрессионных багов
30+
REST-эндпоинтов

Контекст

Smart Trade — немецкий SaaS для retail-трейдеров, которые хотят автоматизировать стратегии на Interactive Brokers, Binance, Bitfinex без написания кода. Аудитория — те же люди, что покупают TradingView Premium за $2K: технически любопытные, но не программисты.

Когда я пришёл, настройка стратегии занимала 2 часа: вложенные YAML, три разных страницы, ручная настройка webhook-ов. Drop-off после регистрации был жёстким.

Что я сделал

  • Спроектировал и выпустил Django + DRF backend с 30+ REST-эндпоинтами: стратегии, бэктесты, история сигналов, брокер-коннекторы, биллинг.
  • Сделал React + Redux drag-and-drop конструктор стратегий. Юзеры собирают стратегии визуально из блоков (условие входа → сайзинг → правило выхода). Время настройки упало с 2 часов до 15 минут — измерено в воронке онбординга.
  • Интегрировал брокеров: Interactive Brokers (FIX gateway), Binance, Bitfinex. Нормализовал схемы ордеров/позиций/балансов между венчами, чтобы конструктор не зависел от выбора брокера.
  • Вёл E2E-фреймворк на Selenium: page-object pattern, параллельный запуск через Selenium Grid в Docker, результаты как JUnit XML в Jenkins. Регрессионные баги в проде упали примерно на 50% за 6 месяцев.
  • Был единственным Python-разработчиком в Agile-команде из 8 человек. Тесно работал с двумя фронтендерами и product owner-ом; участвовал в planning, demo, retro.

Технические решения

Валидация стратегий — на backend, не в React-конструкторе. Фронт отправляет непрозрачный граф стратегии; backend прогоняет через детерминированный валидатор перед сохранением. Source of truth — на сервере, и валидатор можно эволюционировать без релиза фронта.

Бэктесты — отдельным worker-пулом, никогда в web-процессе. Даже маленький бэктест может занять 30 секунд — в синхронном HTTP это выглядело бы как сломанный API.

Selenium page-objects с explicit waits, не implicit. Implicit прячут flakiness; explicit делают failure mode понятным («кнопка не стала кликабельной» — реальный сигнал).

Из кода

python
Валидатор стратегий (backend как source of truth)
class StrategyValidator:
    def validate(self, graph: StrategyGraph) -> ValidationResult:
        issues = []
        if not graph.entry_blocks:
            issues.append("strategy needs at least one entry condition")
        if not graph.exit_blocks:
            issues.append("strategy needs at least one exit rule")

        for block in graph.position_sizing_blocks:
            if block.risk_per_trade_pct > 10:
                issues.append(
                    f"{block.id}: risk-per-trade {block.risk_per_trade_pct}% "
                    f"exceeds safety limit of 10%"
                )

        # Reject orphan blocks: every block must be reachable from entry.
        unreachable = self._find_unreachable(graph)
        for block_id in unreachable:
            issues.append(f"block {block_id} is unreachable from entry")

        return ValidationResult(ok=not issues, issues=issues)
python
Selenium page-object pattern
class StrategyBuilderPage(BasePage):
    URL = "/builder"
    ADD_BLOCK_BUTTON = (By.CSS_SELECTOR, "[data-test='add-block']")
    SAVE_BUTTON = (By.CSS_SELECTOR, "[data-test='save-strategy']")

    def add_entry_block(self, indicator: str, threshold: float) -> "StrategyBuilderPage":
        self.wait_clickable(self.ADD_BLOCK_BUTTON).click()
        self.select_dropdown("[data-test='block-type']", "entry")
        self.select_dropdown("[data-test='indicator']", indicator)
        self.fill("[data-test='threshold']", str(threshold))
        return self

    def save(self) -> "StrategyListPage":
        self.wait_clickable(self.SAVE_BUTTON).click()
        self.wait_for_toast("Strategy saved")
        return StrategyListPage(self.driver)

Результат

Время настройки стратегии: 2ч → 15 мин. Регрессионные баги: −50%. Конверсия онбординга заметно выросла (команда не публиковала точные цифры).

Первая коммерческая backend-роль после QA — проект, который научил меня думать про API-контракты и транзакции БД, а не только тест-кейсы.

Стек

PythonDjangoDjango REST FrameworkReactReduxPostgreSQLRedisSeleniumDockerJenkins