← all work
Mar 2018 — May 2019

Smart Trade (Germany)

Backend Developer

SaaS for retail trading automation. Backend on Django + 30 REST endpoints, React drag-and-drop strategy builder.

Outcomes

−87%
Strategy setup time (2h → 15min)
−50%
Regression bugs (via E2E framework)
30+
REST endpoints designed

Context

Smart Trade is a German SaaS for retail traders who want to automate strategies on Interactive Brokers, Binance, Bitfinex and similar venues — without writing code. The audience is the same person who'd buy a $2K TradingView Premium subscription: technically curious but not a developer.

When I joined, strategy setup was a 2-hour ordeal: nested YAML configs, three different web pages, manual webhook wiring. Drop-off after registration was brutal.

What I built

  • Designed and shipped a Django + DRF backend with 30+ REST endpoints covering strategies, backtests, signal history, broker connections, and billing.
  • Built a React + Redux drag-and-drop strategy builder. Users compose strategies visually from blocks (entry condition → position sizing → exit rule). Strategy setup time dropped from 2 hours to 15 minutes — measured in onboarding funnel.
  • Integrated with broker APIs: Interactive Brokers (FIX gateway), Binance, Bitfinex. Normalized order/position/balance schemas across venues so the strategy builder didn't care which broker the user picked.
  • Owned the Selenium-based E2E framework: page-object pattern, parallelized via Selenium Grid in Docker, results published as JUnit XML to Jenkins. Regression bugs in production dropped ~50% over 6 months.
  • Was the only Python dev in an 8-person Agile team. Worked closely with two frontend devs and a product owner; participated in sprint planning, demos, retros.

Technical decisions

Strategy validation happens on the backend, not in the React builder. The frontend ships an opaque strategy graph; the backend runs it through a deterministic validator before persisting. This keeps the source of truth on the server and lets us evolve the validator without shipping a frontend release.

Backtests run in a separate worker pool, never in the web process. Even a small backtest can take 30 seconds — putting that behind a synchronous HTTP call would have made the API feel broken.

Selenium page-objects with explicit waits over implicit. Implicit waits hide flakiness; explicit waits make the failure mode obvious ('button never became clickable' is a real signal).

From the code

python
Strategy validator (backend authority)
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)

Result

Strategy setup time: 2h → 15 min. Regression bugs: −50%. Onboarding completion rate noticeably improved (the team didn't share the exact number publicly).

Was my first commercial backend role after QA — the project that taught me to think about API contracts and database transactions, not just test cases.

Stack

PythonDjangoDjango REST FrameworkReactReduxPostgreSQLRedisSeleniumDockerJenkins