Signals
Signals
Signals are enumerated values that represent trading actions: enter long, enter short, exit position, or do nothing. Using signals provides a clean, consistent way to manage trading logic, especially when combining multiple indicators or creating reusable strategies.
Signal Types
HaasScript provides the following signal enumerations:
| Signal | Action | Description |
|---|---|---|
SignalLong / SignalBuy
|
DoLong() | Enter long position |
SignalShort / SignalSell
|
DoShort() | Enter short position |
SignalExitPosition |
DoExitPosition() | Exit any current position |
SignalExitLong |
DoCloseLong() | Exit long position only |
SignalExitShort |
DoCloseShort() | Exit short position only |
SignalNone |
No action | Do not take any trading action |
SignalError |
Error | Indicates an error condition |
Basic Signal Usage
Signals are returned by indicator functions and can be processed with DoSignal():
local rsiSignal = GetBuySellLevelSignal(RSI(ClosePrices(), 14), 30, 70)
DoSignal(rsiSignal) -- Executes appropriate action based on signal
DoSignal() automatically maps signals to trading commands:
-
SignalLong→DoLong() -
SignalShort→DoShort() -
SignalExitPosition→DoExitPosition() -
SignalNone→ No action
Creating Signals from Values
GetBuySellLevelSignal
Generate signals based on value thresholds:
local rsi = RSI(ClosePrices(), 14)
local rsiSignal = GetBuySellLevelSignal(rsi, 30, 70)
-- Returns SignalLong when RSI < 30
-- Returns SignalShort when RSI > 70
-- Returns SignalNone when 30 ≤ RSI ≤ 70
GetThresholdSignal
Generate signals with optional swing buffer to prevent frequent flipping:
local macdValue = MACD(ClosePrices(), 12, 26, 9)[1]
local macdSignal = GetThresholdSignal(macdValue, 0, 0.5)
-- Below -0.5: SignalLong
-- Above 0.5: SignalShort
-- Between -0.5 and 0.5: SignalNone (swing buffer)
Combining Multiple Signals
GetConsensusSignal
Returns the majority signal from multiple indicators:
local rsiSignal = GetBuySellLevelSignal(RSI(ClosePrices(), 14), 30, 70)
local macdSignal = GetThresholdSignal(MACD(ClosePrices(), 12, 26, 9)[1], 0, 0.1)
local bbSignal = GetThresholdSignal(BBANDS(ClosePrices(), 20, 2.0, 2.0)[1], 0, 0.1)
-- Get majority vote (at least 2 out of 3 must agree)
local consensusSignal = GetConsensusSignal(rsiSignal, macdSignal, bbSignal)
DoSignal(consensusSignal)
GetUnanimousSignal
Returns a signal only when all indicators agree:
local rsiSignal = GetBuySellLevelSignal(RSI(ClosePrices(), 14), 30, 70)
local macdSignal = GetThresholdSignal(MACD(ClosePrices(), 12, 26, 9)[1], 0, 0.1)
local bbSignal = GetThresholdSignal(BBANDS(ClosePrices(), 20, 2.0, 2.0)[1], 0, 0.1)
-- Only act when all three signals match
local unanimousSignal = GetUnanimousSignal(rsiSignal, macdSignal, bbSignal)
DoSignal(unanimousSignal)
Signal Processing
IgnoreSignalIf
Prevent specific signals from being executed:
local rsiSignal = GetBuySellLevelSignal(RSI(ClosePrices(), 14), 30, 70)
-- Ignore short signals
local longOnlySignal = IgnoreSignalIf(rsiSignal, SignalShort)
DoSignal(longOnlySignal) -- Will only execute long or no action
ReverseSignal
Flip signals from long to short and vice versa:
local originalSignal = GetBuySellLevelSignal(RSI(ClosePrices(), 14), 30, 70)
local reversedSignal = ReverseSignal(originalSignal)
-- SignalLong becomes SignalShort
-- SignalShort becomes SignalLong
-- SignalNone stays SignalNone
DelaySignal
Delay signal execution by a specified number of minutes:
local rsiSignal = GetBuySellLevelSignal(RSI(ClosePrices(), 14), 30, 70)
local delayedSignal = DelaySignal(rsiSignal, 5) -- Delay by 5 minutes
DoSignal(delayedSignal) -- Signal from 5 minutes ago (if it exists)
This is useful for confirming signals before execution.
Signal Position Checks
Signals can be combined with position state for safer trading:
local rsiSignal = GetBuySellLevelSignal(RSI(ClosePrices(), 14), 30, 70)
-- Only enter long if not already in long position
if rsiSignal == SignalLong and GetPositionDirection() != PositionLong then
DoLong()
end
-- Only enter short if not already in short position
if rsiSignal == SignalShort and GetPositionDirection() != PositionShort then
DoShort()
end
-- Exit on any exit signal
if rsiSignal == SignalExitPosition then
DoExitPosition()
end
Easy Indicators
HaasScript provides "Easy" indicator functions that automatically handle input fields, charting, and return signals:
-- Easy RSI with automatic signal generation
local easyRsiSignal = EasyRSI(0, "MyRSI", 60)
-- The signal is based on default buy/sell levels (typically 30/70)
DoSignal(easyRsiSignal)
Easy indicators are convenient for quick prototyping but offer less control than manual signal generation.
Complete Example: Multi-Indicator Strategy
Here's a comprehensive example combining signals from multiple indicators:
-- Input parameters
local rsiLength = Input("RSI Length", 14)
local rsiOverbought = Input("RSI Overbought", 70)
local rsiOversold = Input("RSI Oversold", 30)
local macdFast = Input("MACD Fast", 12)
local macdSlow = Input("MACD Slow", 26)
local macdSignal = Input("MACD Signal", 9)
local bbLength = Input("BB Length", 20)
local bbDeviation = Input("BB Deviation", 2.0)
local closes = ClosePrices()
-- Generate individual signals
local rsiValue = RSI(closes, rsiLength)
local rsiSignal = GetBuySellLevelSignal(rsiValue, rsiOversold, rsiOverbought)
local macdLine = MACD(closes, macdFast, macdSlow, macdSignal).macd
local macdValue = macdLine[1] - macdLine[3] -- MACD - Signal line
local macdTradeSignal = GetThresholdSignal(macdValue, 0, 0.2)
local bb = BBANDS(closes, bbLength, bbDeviation, bbDeviation)
local bbValue = (closes[1] - bb.lower) / (bb.upper - bb.lower) -- Position within bands
local bbSignal = GetBuySellLevelSignal(bbValue, 0.2, 0.8)
-- Use consensus: at least 2 out of 3 must agree
local consensusSignal = GetConsensusSignal(rsiSignal, macdTradeSignal, bbSignal)
local posDirection = GetPositionDirection()
-- Position-aware execution
if consensusSignal == SignalLong and posDirection != PositionLong then
DoLong()
Log('Long entry: RSI=' .. rsiValue .. ' MACD=' .. macdValue)
elseif consensusSignal == SignalShort and posDirection != PositionShort then
DoShort()
Log('Short entry: RSI=' .. rsiValue .. ' MACD=' .. macdValue)
elseif consensusSignal == SignalExitPosition then
local note = ''
if posDirection == PositionLong then
note = 'Closed long position'
else
note = 'Closed short position'
end
DoExitPosition(note)
Log(note)
end
Advanced: Custom Signal Logic
You can create custom signal logic by returning signal enumerations:
function GenerateCustomSignal()
local rsi = RSI(ClosePrices(), 14)
local ema9 = EMA(ClosePrices(), 9)
local ema21 = EMA(ClosePrices(), 21)
-- Custom logic: RSI oversold AND fast MA above slow MA
if rsi < 30 and ema9 > ema21 then
return SignalLong
end
-- Custom logic: RSI overbought AND fast MA below slow MA
if rsi > 70 and ema9 < ema21 then
return SignalShort
end
-- No signal
return SignalNone
end
local customSignal = GenerateCustomSignal()
DoSignal(customSignal)
Signal vs Direct Commands
Using signals:
local signal = GetBuySellLevelSignal(RSI(ClosePrices(), 14), 30, 70)
DoSignal(signal)
Direct commands:
local rsi = RSI(ClosePrices(), 14)
if rsi < 30 then
DoLong()
elseif rsi > 70 then
DoShort()
end
Signals are preferable when:
- Combining multiple indicators
- Building reusable components
- Need flexible signal processing (delay, reverse, consensus)
- Want cleaner, more readable code
Direct commands are simpler for single-indicator strategies.
Common Mistakes
Not checking signal values:
-- Wrong: Always executes DoSignal, even on SignalNone
DoSignal(GetBuySellLevelSignal(RSI(ClosePrices(), 14), 30, 70))
-- Better: Check if there's an actual signal
local signal = GetBuySellLevelSignal(RSI(ClosePrices(), 14), 30, 70)
if signal ~= SignalNone then
DoSignal(signal)
end
Forgetting position checks:
-- Risky: Tries to enter multiple long positions, produces spam
if signal == SignalLong then
DoLong()
end
-- Better: Check current position
if signal == SignalLong and GetPositionDirection() != PositionLong then
DoLong() -- Not going to spam "already in bought/long position"
end