Signal Generator - Build Trading Signals Without Code
The Signal Generator transforms how traders create entry and exit signals. Instead of learning Pine Script, MQL4, or Python, you build signals using a visual condition builder that's both powerful and intuitive. This guide covers everything from basic conditions to advanced nested logic and percentage-based signals.
Why Signal Generator?
Traditional vs ApudFlow Approach
Traditional Approach (Pine Script):
// Requires learning Pine Script syntax
longCondition = ta.crossover(ta.rsi(close, 14), 30) and close > ta.sma(close, 200)
if (longCondition)
strategy.entry("Long", strategy.long)
ApudFlow Approach (Visual):
{
"long_conditions": [
{"left": "rsi", "operator": "crosses_above", "right": "30"},
{"left": "close", "operator": ">", "right": "sma_200"}
],
"long_logic": "AND"
}
Both achieve the same result, but the ApudFlow version:
- ✅ Requires no coding knowledge
- ✅ Uses visual dropdown selectors
- ✅ Auto-completes field names from your data
- ✅ Validates conditions before execution
Basic Condition Structure
Every condition has three parts:
left: field or expression
operator: comparison operator
right: value, field, or expression
Left Side (What to Check)
- Field name:
rsi,close,sma_20 - Expression:
high - low,close * 0.99 - Previous bar:
close[-1] - Percentage function:
pct_change(close)
Operator (How to Compare)
| Category | Operators |
|---|---|
| Numeric | >, >=, <, <=, ==, != |
| Crossing | crosses_above, crosses_below |
| String | contains, not_contains, starts_with, ends_with, matches |
| Existence | is_empty, is_not_empty |
Right Side (Compare Against)
- Static number:
30,1.5,-10 - Field:
sma_20,bb_upper - Expression:
close[-1] + atr - Text:
"USDT"(for string operators)
Numeric Operators in Detail
Basic Comparisons
Greater Than
{"left": "rsi", "operator": ">", "right": "70"}
Signal when RSI is above 70.
Less Than or Equal
{"left": "close", "operator": "<=", "right": "bb_lower"}
Signal when price touches or breaks below lower Bollinger Band.
Crossing Operators
Crosses Above - Signal only on the bar where crossing occurs
{"left": "sma_20", "operator": "crosses_above", "right": "sma_50"}
Golden cross signal - fast MA crosses above slow MA.
Crosses Below - Opposite direction
{"left": "rsi", "operator": "crosses_below", "right": "70"}
RSI exits overbought zone.
Key Difference:
>triggers on EVERY bar where condition is truecrosses_abovetriggers ONLY on the bar where it becomes true
Field Math and Expressions
You can perform calculations directly in conditions:
Simple Math
{"left": "volume", "operator": ">", "right": "avg_volume * 2"}
Volume is more than 2x average.
Two-Field Comparison
{"left": "high - low", "operator": ">", "right": "atr"}
Candle range exceeds ATR (high volatility bar).
Percentage of Price
{"left": "high - low", "operator": ">", "right": "close * 0.02"}
Candle range exceeds 2% of price.
Previous Bar Reference
{"left": "close", "operator": ">", "right": "close[-1] * 1.03"}
Price jumped 3% from previous bar.
Percentage Functions
Three powerful functions for percentage-based conditions:
pct_change(field)
Returns % change from previous bar.
Signal on 3%+ price spike:
{"left": "pct_change(close)", "operator": ">=", "right": "3"}
Signal on volume surge (50%+ increase):
{"left": "pct_change(volume)", "operator": ">=", "right": "50"}
abs_pct_change(field)
Returns absolute % change (always positive).
Signal on any big move (2%+ up or down):
{"left": "abs_pct_change(close)", "operator": ">=", "right": "2"}
pct(a, b)
Returns percentage difference: (a - b) / b × 100
Close is 2% above open (bullish candle):
{"left": "pct(close, open)", "operator": ">=", "right": "2"}
High broke above previous high by 1%:
{"left": "pct(high, high[-1])", "operator": ">=", "right": "1"}
AND/OR Logic
Simple AND (All Must Be True)
{
"long_conditions": [
{"left": "rsi", "operator": "<", "right": "30"},
{"left": "close", "operator": ">", "right": "sma_200"}
],
"long_logic": "AND"
}
RSI oversold AND price above 200 SMA (trend filter).
Simple OR (Any Can Be True)
{
"long_conditions": [
{"left": "rsi", "operator": "<", "right": "30"},
{"left": "close", "operator": "<=", "right": "bb_lower"}
],
"long_logic": "OR"
}
RSI oversold OR price at lower Bollinger Band.
Nested Groups (Complex Logic)
For (A AND B) OR (C AND D):
{
"long_conditions": [
{
"logic": "AND",
"conditions": [
{"left": "rsi", "operator": "<", "right": "30"},
{"left": "macd", "operator": ">", "right": "0"}
]
},
{
"logic": "AND",
"conditions": [
{"left": "close", "operator": ">", "right": "sma_200"},
{"left": "adx", "operator": ">", "right": "25"}
]
}
],
"long_logic": "OR"
}
Signal when: (RSI oversold AND MACD positive) OR (Above 200 SMA AND strong trend)
Signal Modes (Avoiding Duplicates)
One of the most important settings to understand:
The Problem
Condition: close > 4000
- every mode: Generates signal on EVERY bar where close > 4000 (duplicates!)
- first mode: Only generates signal on the FIRST bar where close > 4000
first Mode (Recommended)
{
"signal_mode": "first",
"long_conditions": [{"left": "close", "operator": ">", "right": "sma_20"}]
}
Signal only when price CROSSES above SMA, not while it stays above.
cooldown Mode
{
"signal_mode": "cooldown",
"cooldown_bars": 10,
"long_conditions": [{"left": "rsi", "operator": "<", "right": "30"}]
}
After a signal, wait at least 10 bars before allowing another.
{
"signal_mode": "cooldown",
"cooldown_seconds": 3600
}
After a signal, wait at least 1 hour.
Close Modes
Control when positions are closed:
| Mode | Description | Use Case |
|---|---|---|
reverse | Close on opposite signal | Most strategies |
conditions | Close only when explicit conditions met | Separate exit logic |
both | Close on reverse OR conditions | Maximum flexibility |
none | Never generate close signals | Use backtest SL/TP |
reverse Mode (Default)
{
"close_mode": "reverse"
}
Long signal → enter long Short signal → close long + enter short
conditions Mode
{
"close_mode": "conditions",
"long_conditions": [{"left": "rsi", "operator": "<", "right": "30"}],
"close_long_conditions": [{"left": "rsi", "operator": ">", "right": "50"}]
}
Enter when RSI < 30, exit when RSI > 50 (specific exit).
none Mode (Recommended for Backtest)
{
"close_mode": "none"
}
Let Backtest Strategy handle exits via SL/TP/trailing.
String/Text Operators
Filter by symbol, news, or text patterns:
Symbol Filtering
{"left": "symbol", "operator": "contains", "right": "BTC"}
Only BTC pairs.
{"left": "symbol", "operator": "ends_with", "right": "USDT"}
Only USDT pairs.
News/Headline Filtering
{"left": "headline", "operator": "matches", "right": "bull|bullish|surge|rally"}
Regex match for bullish keywords.
Non-Empty Check
{"left": "signal", "operator": "is_not_empty", "right": ""}
Only when signal field has a value.
Candle Pattern Conditions
Bullish Candle (Close > Open)
{"left": "close - open", "operator": ">", "right": "0"}
Large Candle (20+ Pips for Forex)
{
"long_conditions": [
{"left": "high - low", "operator": ">", "right": "0.0020"},
{"left": "close - open", "operator": ">", "right": "0"}
],
"long_logic": "AND"
}
Bullish Reversal (Up After Down)
{
"long_conditions": [
{"left": "close - open", "operator": ">", "right": "0"},
{"left": "close[-1] - open[-1]", "operator": "<", "right": "0"}
],
"long_logic": "AND"
}
Bullish Engulfing
{
"long_conditions": [
{"left": "close - open", "operator": ">", "right": "0"},
{"left": "close[-1] - open[-1]", "operator": "<", "right": "0"},
{"left": "close - open", "operator": ">", "right": "open[-1] - close[-1]"}
],
"long_logic": "AND"
}
Gap Up
{"left": "open", "operator": ">", "right": "high[-1]"}
Higher High + Higher Low (Trend)
{
"long_conditions": [
{"left": "high", "operator": ">", "right": "high[-1]"},
{"left": "low", "operator": ">", "right": "low[-1]"}
],
"long_logic": "AND"
}
Complete Strategy Examples
1. RSI Mean Reversion
{
"long_conditions": [
{"left": "rsi", "operator": "crosses_above", "right": "30"}
],
"short_conditions": [
{"left": "rsi", "operator": "crosses_below", "right": "70"}
],
"close_mode": "reverse"
}
2. Moving Average Crossover
{
"long_conditions": [
{"left": "sma_20", "operator": "crosses_above", "right": "sma_50"}
],
"short_conditions": [
{"left": "sma_20", "operator": "crosses_below", "right": "sma_50"}
]
}
3. Breakout with Volume
{
"long_conditions": [
{"left": "close", "operator": ">", "right": "high[-1]"},
{"left": "volume", "operator": ">", "right": "volume_sma * 1.5"}
],
"long_logic": "AND",
"close_mode": "none"
}
4. Bollinger Band Squeeze
{
"long_conditions": [
{"left": "close", "operator": "<=", "right": "bb_lower"}
],
"close_long_conditions": [
{"left": "close", "operator": ">=", "right": "bb_middle"}
],
"close_mode": "conditions"
}
5. Momentum with Trend Filter
{
"long_conditions": [
{
"logic": "AND",
"conditions": [
{"left": "pct_change(close)", "operator": ">=", "right": "2"},
{"left": "close", "operator": ">", "right": "sma_200"}
]
}
],
"signal_mode": "cooldown",
"cooldown_bars": 5
}
6. Multi-Symbol with Text Filter
{
"long_conditions": [
{"left": "symbol", "operator": "contains", "right": "BTC"},
{"left": "rsi", "operator": "<", "right": "35"}
],
"long_logic": "AND"
}
Output Structure
Signal Generator produces:
signals Array
{
"signals": [
{"time": 1700000000, "action": "long"},
{"time": 1700003600, "action": "close"},
{"time": 1700007200, "action": "short"}
]
}
summary Statistics
{
"summary": {
"total_signals": 150,
"long_signals": 50,
"short_signals": 50,
"close_signals": 50,
"data_rows": 1000
}
}
Connecting to Backtest Strategy
Signal Generator output connects directly to Backtest Strategy:
[Data Source] → [Indicators] → [Signal Generator] → [Backtest Strategy]
↓
{signals: [...]}
In Backtest Strategy, set:
signals: {{workers[X].signals}}
Tips and Best Practices
1. Use signal_mode: first by Default
Prevents duplicate signals on consecutive bars.
2. Use crosses_above/below for Entry
More precise than >/>= which trigger every bar.
3. Set close_mode: none with Backtest
Let SL/TP handle exits for cleaner results.
4. Pre-calculate Complex Indicators
If expression is complex, calculate in upstream worker.
5. Test Signal Count
Check summary.total_signals - if too high, add more conditions or use cooldown.
6. Use Percentage Functions
pct_change(close) is more universal than absolute price differences.
Troubleshooting
No Signals Generated?
- Check
time_fieldmatches your data (datetime, time, date) - Verify field names exist in your data
- Try
signal_mode: everytemporarily - Check for data gaps
Too Many Signals?
- Use
signal_mode: first(default) - Add more restrictive conditions
- Use
crosses_above/belowinstead of>/>= - Add cooldown period
Signals Not Closing?
- Check
close_modesetting - If using
conditions, verify close conditions exist - Try
close_mode: both
The Signal Generator empowers you to build sophisticated trading signals without code. Start simple, test thoroughly, and gradually add complexity as you understand each feature. Combined with Backtest Strategy's AI optimization, you have a complete toolkit for strategy development.