Smart Trade (Germany)
SaaS for retail trading automation. Backend on Django + 30 REST endpoints, React drag-and-drop strategy builder.
Outcomes
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
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)
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.