Visual Strategy Builder¶
Users create trading strategies visually without writing code. The strategy builder is a drag-and-drop canvas powered by Vue Flow where traders define indicators, compose signal conditions using expression trees, and wire up entry/exit logic — all through a graphical interface. Behind the scenes, the builder generates PineScript v6 source code that is compiled server-side into vectorized Python and executed by the backtesting engine. The result is a zero-code path from idea to backtest in under 60 seconds.
Product outcome
The same 4-signal model (long_entry, long_exit, short_entry, short_exit) powers both backtesting and live execution. A strategy built visually, tested in the backtester, and deployed to live trading uses identical logic at every stage — eliminating the "works in backtest, fails in production" disconnect that plagues platforms where backtest and execution engines diverge.
How It Works¶
- Drag indicators onto the canvas — the builder exposes all 37 supported technical indicators grouped by category (trend, momentum, volatility, volume).
- Define variables — configure indicator parameters like period lengths, thresholds, and price sources through input panels.
- Build conditions — compose boolean expressions using AND/OR groups. Each condition compares two operands (indicator vs. value, indicator vs. indicator).
- Map to signals — assign conditions to the four signal slots:
long_entry,long_exit,short_entry,short_exit. - Generate PineScript — the frontend emits valid PineScript v6 source with
strategy()declaration,input.*()calls, indicator computations, andifblocks withstrategy.entry()/strategy.close(). - Compile and backtest — the server-side PineScript compiler transforms the source into vectorized Python, and the backtesting engine runs it against historical data.
The 4-Signal Model¶
Every strategy — whether built visually, written in PineScript, or coded in raw Python — must produce exactly 4 boolean signals. This is the universal interface between strategy logic and both the execution engine and the backtester.
| Signal | Meaning | PineScript Equivalent |
|---|---|---|
long_entry |
Open a long position | strategy.entry("Long", strategy.long) |
long_exit |
Close a long position | strategy.close("Long") |
short_entry |
Open a short position | strategy.entry("Short", strategy.short) |
short_exit |
Close a short position | strategy.close("Short") |
Why 4 signals?
The 4-signal model decouples strategy logic from execution mechanics. The backtester doesn't need to understand MACD crossovers or RSI thresholds — it only sees boolean arrays. This also enables the auto-reversal logic in live trading: a long_entry signal automatically closes any existing short position before opening the long.
Components¶
The strategy builder is composed of 11 Vue 3 components using the Composition API with <script setup>:
| Component | File | Purpose |
|---|---|---|
| StrategyBuilderPage | StrategyBuilderPage.vue |
Main editor — hosts the visual flow canvas, sidebar panels, and PineScript preview. Orchestrates state across all child components. |
| SignalConditionBuilder | SignalConditionBuilder.vue |
Builds signal conditions for each of the 4 signal slots. Each signal gets its own expression tree of AND/OR conditions. |
| ExpressionBuilder | ExpressionBuilder.vue |
Expression tree editor with a function picker. Users compose conditions like RSI(14) < 30 AND SMA(20) > SMA(50). |
| ExprNode | ExprNode.vue |
Renders a single node in the expression tree — handles recursive nesting of AND/OR groups. |
| VariablesPanel | VariablesPanel.vue |
Manages indicator and variable definitions. Users add indicators (SMA, MACD, BB) and assign them to named variables. |
| StrategyInputsPanel | StrategyInputsPanel.vue |
Manages configurable input parameters — period lengths, thresholds, multipliers. These become input.int() / input.float() in the generated PineScript. |
| PineScriptPreview | PineScriptPreview.vue |
Live, read-only preview of the generated PineScript v6 code. Updates in real-time as the user modifies the visual builder. |
| OperandSelector | OperandSelector.vue |
Dropdown selector for technical indicators and price sources. Groups indicators by category (Trend, Momentum, Volatility, Volume). |
| ValueDisplay | ValueDisplay.vue |
Renders operand values in condition rows — displays indicator names, numeric constants, or variable references with appropriate formatting. |
| ConditionRow | ConditionRow.vue |
A single condition: left_operand operator right_operand (e.g., RSI(14) > 70). Supports comparison operators: >, <, >=, <=, ==, !=, crossover, crossunder. |
| ConditionGroup | ConditionGroup.vue |
Groups multiple ConditionRow instances with AND/OR logic. Supports nesting for complex boolean expressions. |
Generated PineScript Example¶
The builder generates fully valid PineScript v6 source code. Here's an example of a MACD crossover strategy with an RSI filter — the kind of strategy a user would build by dragging a MACD indicator, an RSI indicator, and configuring crossover conditions:
//@version=6
strategy("MACD + RSI Filter", overlay=true)
// Input parameters (from StrategyInputsPanel)
fast_length = input.int(12, "Fast Length")
slow_length = input.int(26, "Slow Length")
signal_length = input.int(9, "Signal Length")
rsi_length = input.int(14, "RSI Length")
rsi_upper = input.int(70, "RSI Overbought")
rsi_lower = input.int(30, "RSI Oversold")
// Indicator calculations (from VariablesPanel)
[macdLine, signalLine, _] = ta.macd(close, fast_length, slow_length, signal_length)
rsi_val = ta.rsi(close, rsi_length)
// Signal conditions (from SignalConditionBuilder)
longCondition = ta.crossover(macdLine, signalLine) and rsi_val < rsi_upper
shortCondition = ta.crossunder(macdLine, signalLine) and rsi_val > rsi_lower
// Entry signals — 4-signal model
if longCondition
strategy.entry("Long", strategy.long)
if shortCondition
strategy.entry("Short", strategy.short)
What the compiler sees
The compiler maps this to 4 signals: long_entry = crossover(macd, signal) & (rsi < 70), short_entry = crossunder(macd, signal) & (rsi > 30). Since there are no explicit strategy.close() calls, long_exit and short_exit are empty — positions are reversed by opposite entries (handled by the auto-reversal logic).
Integration with Backend¶
Data Flow Summary¶
| Step | Action | Data |
|---|---|---|
| 1 | User builds strategy | Visual flow graph |
| 2 | Frontend generates PineScript | .pine source string |
| 3 | POST /api/strategies |
{pine_source, symbol, timeframe, period} |
| 4 | Store in PostgreSQL | Strategy record with versioned source |
| 5 | Compiler transforms | PineScript → AST → Python _compute() function |
| 6 | Backtester executes | _compute(df, params) → 4 boolean Series → vbt.Portfolio.from_signals() |
| 7 | Results returned | BacktestResult with ~30 metrics, equity curve, trade list |
| 8 | Frontend renders | Stats cards, area charts, trade table |
Live Preview
The PineScriptPreview component updates in real-time as the user modifies the visual builder. This gives immediate feedback — users can verify the generated code before submitting. The preview is read-only and uses syntax highlighting via a <pre> block with PineScript-aware formatting.