Technical Indicators

Comprehensive guide to all technical indicators available in TradeStaq custom strategies. TradeStaq uses the tulind library, providing access to 100+ professional-grade technical indicators.

Overview

Technical indicators are accessed through td.indicators. The system provides:

  • Pre-calculated Indicators - Automatically computed each spin with optimal defaults
  • Custom Period Indicators - Use td.indicators.custom['ema_20'] pattern for custom periods
  • Full Tulind Library - Access to 100+ tulind indicators
  • Auto-detection - Platform detects which custom indicators you need from your code

Pre-calculated Indicators

These indicators are automatically calculated each spin with default parameters for best performance.

RSI (Relative Strength Index)

Measures momentum on a scale of 0-100. Default period: 14.

const rsi = td.indicators.rsi;          // Current RSI value
const rsiArray = td.indicators.rsiArray; // Historical RSI values

// Interpretation
// < 30: Oversold (potential buy)
// > 70: Overbought (potential sell)
// 30-70: Neutral zone

Example: RSI Divergence Detection

const rsi = td.indicators.rsiArray;
const candles = td.market.candles;

// Price making higher high but RSI making lower high = bearish divergence
const priceHigh1 = candles[candles.length - 10].high;
const priceHigh2 = candles[candles.length - 1].high;
const rsiHigh1 = rsi[rsi.length - 10];
const rsiHigh2 = rsi[rsi.length - 1];

if (priceHigh2 > priceHigh1 && rsiHigh2 < rsiHigh1) {
    td.utils.log('Bearish RSI divergence detected');
}

MACD (Moving Average Convergence Divergence)

Trend-following momentum indicator. Default: 12/26/9.

const macd = td.indicators.macd;

macd.line       // MACD line (12 EMA - 26 EMA)
macd.signal     // Signal line (9 EMA of MACD)
macd.histogram  // MACD - Signal
macd.trend      // 'bullish' | 'bearish' | 'neutral'

Example: MACD Crossover Strategy

const macd = td.indicators.macd;
const prevHistogram = td.state.get('prevHistogram', 0);

// Detect crossover
const bullishCross = prevHistogram <= 0 && macd.histogram > 0;
const bearishCross = prevHistogram >= 0 && macd.histogram < 0;

td.state.set('prevHistogram', macd.histogram);

if (bullishCross && !td.position.hasPosition) {
    td.trade.buy({ reason: 'MACD bullish crossover' });
}

Bollinger Bands

Volatility-based bands around a moving average. Default: 20 period, 2 std dev.

const bb = td.indicators.bbands;

bb.upper     // Upper band (middle + 2 std dev)
bb.middle    // Middle band (20 SMA)
bb.lower     // Lower band (middle - 2 std dev)
bb.width     // Band width (volatility measure)
bb.percentB  // Price position within bands (0-1)

Example: Bollinger Squeeze Breakout

const bb = td.indicators.bbands;
const price = td.market.price;

// Low volatility squeeze
if (bb.width < 0.02) {
    td.state.set('inSqueeze', true);
}

// Breakout from squeeze
if (td.state.get('inSqueeze') && bb.width > 0.03) {
    if (price > bb.upper) {
        td.trade.buy({ reason: 'BB squeeze breakout (long)' });
    } else if (price < bb.lower) {
        td.trade.sell({ reason: 'BB squeeze breakout (short)' });
    }
    td.state.set('inSqueeze', false);
}

ADX (Average Directional Index)

Measures trend strength (not direction). Default period: 14.

const adx = td.indicators.adx;

adx.value    // ADX value (0-100)
adx.diPlus   // +DI (bullish direction)
adx.diMinus  // -DI (bearish direction)
adx.trend    // 'strong' (>=25) | 'weak' (20-25) | 'none' (<20)

Example: ADX Trend Filter

const adx = td.indicators.adx;

// Only trade when trend is strong
if (adx.value < 25) {
    td.utils.log('No strong trend, skipping');
    return;
}

// Determine trend direction
if (adx.diPlus > adx.diMinus) {
    // Bullish trend - look for long entries
} else {
    // Bearish trend - look for short entries
}

ATR (Average True Range)

Measures market volatility. Default period: 14.

const atr = td.indicators.atr;          // Current ATR
const atrArray = td.indicators.atrArray; // Historical ATR

Example: ATR-based Stop Loss

const atr = td.indicators.atr;
const multiplier = td.config.getNumber('ATR_MULTIPLIER', 2);

// Stop loss at 2x ATR from entry
const stopLoss = td.market.price - (atr * multiplier);

td.trade.buy({
    amountPercent: 100,
    stopLoss: stopLoss,
    reason: 'Entry with ATR stop'
});

Supertrend

Trend-following indicator that provides dynamic support/resistance. Default: period 10, multiplier 3.

const supertrend = td.indicators.supertrend;

supertrend.value      // Supertrend line value
supertrend.direction  // 'up' | 'down'
supertrend.trend      // 'bullish' | 'bearish'
supertrend.stopLoss   // Same as value (can use as trailing stop)

Example: Supertrend Trend Following

const st = td.indicators.supertrend;
const price = td.market.price;

if (!td.position.hasPosition) {
    // Enter long when price crosses above Supertrend
    if (st.trend === 'bullish') {
        td.trade.buy({
            stopLoss: st.stopLoss,
            reason: 'Supertrend bullish'
        });
    }
    // Enter short when price crosses below Supertrend
    if (st.trend === 'bearish') {
        td.trade.sell({
            stopLoss: st.stopLoss,
            reason: 'Supertrend bearish'
        });
    }
}

Example: Multi-Timeframe Supertrend Filter

// Use higher timeframe Supertrend as trend filter
const htfSupertrend = td.mtf.indicators(1)?.supertrend;
const ltfRsi = td.indicators.rsi;

if (htfSupertrend?.trend === 'bullish' && ltfRsi < 30) {
    td.trade.buy({ reason: 'HTF bullish + RSI oversold' });
}

if (htfSupertrend?.trend === 'bearish' && ltfRsi > 70) {
    td.trade.sell({ reason: 'HTF bearish + RSI overbought' });
}

Stochastic Oscillator

Momentum indicator comparing closing price to price range. Default: 14/3.

const stoch = td.indicators.stochastic;

stoch.k      // %K line (fast, 0-100)
stoch.d      // %D line (slow, 3-period SMA of %K)
stoch.zone   // 'overbought' (>=80) | 'oversold' (<=20) | 'neutral'

Example: Stochastic Crossover

const stoch = td.indicators.stochastic;
const prevK = td.state.get('prevK', stoch.k);
const prevD = td.state.get('prevD', stoch.d);

// Bullish crossover in oversold zone
if (stoch.zone === 'oversold' && prevK < prevD && stoch.k > stoch.d) {
    td.trade.buy({ reason: 'Stochastic bullish crossover' });
}

td.state.set('prevK', stoch.k);
td.state.set('prevD', stoch.d);

MFI (Money Flow Index)

Volume-weighted RSI. Default period: 14.

const mfi = td.indicators.mfi;  // 0-100 scale

// < 20: Oversold
// > 80: Overbought

Pivot Points

Support and resistance levels calculated from previous candle.

const pivots = td.indicators.pivots;

pivots.pivot  // Pivot point
pivots.r1     // Resistance 1
pivots.r2     // Resistance 2
pivots.r3     // Resistance 3
pivots.s1     // Support 1
pivots.s2     // Support 2
pivots.s3     // Support 3

Example: Pivot Point Bounce

const pivots = td.indicators.pivots;
const price = td.market.price;
const tolerance = 0.001; // 0.1%

// Buy at S1 support
if (Math.abs(price - pivots.s1) / pivots.s1 < tolerance) {
    td.trade.buy({
        stopLoss: pivots.s2,
        takeProfit: pivots.pivot,
        reason: 'S1 support bounce'
    });
}

VWAP (Volume Weighted Average Price)

Volume-weighted average price with bands.

const vwap = td.indicators.vwap;

vwap.value      // VWAP value
vwap.upperBand  // VWAP + 1 standard deviation
vwap.lowerBand  // VWAP - 1 standard deviation
vwap.deviation  // Current price deviation from VWAP

Example: VWAP Mean Reversion

const vwap = td.indicators.vwap;
const price = td.market.price;

// Buy when price touches lower VWAP band
if (price <= vwap.lowerBand && !td.position.hasPosition) {
    td.trade.buy({
        takeProfit: vwap.value,
        reason: 'VWAP lower band touch'
    });
}

Ichimoku Cloud

Comprehensive trend indicator with multiple components.

const ichimoku = td.indicators.ichimoku;

ichimoku.tenkanSen    // Conversion Line (9-period)
ichimoku.kijunSen     // Base Line (26-period)
ichimoku.senkouSpanA  // Leading Span A
ichimoku.senkouSpanB  // Leading Span B
ichimoku.chikouSpan   // Lagging Span
ichimoku.cloudTop     // Higher of Senkou A/B
ichimoku.cloudBottom  // Lower of Senkou A/B
ichimoku.trend        // 'bullish' | 'bearish' | 'neutral'
ichimoku.priceVsCloud // 'above' | 'below' | 'inside'

Example: Ichimoku Cloud Breakout

const ichimoku = td.indicators.ichimoku;

if (ichimoku.priceVsCloud === 'above' && ichimoku.trend === 'bullish') {
    td.trade.buy({ reason: 'Price above bullish cloud' });
}

Donchian Channels

Price channel based on highest high and lowest low.

const donchian = td.indicators.donchian;

donchian.upper   // Highest high over period
donchian.lower   // Lowest low over period
donchian.middle  // (upper + lower) / 2
donchian.width   // Channel width as percentage

Keltner Channels

Volatility-based envelope around EMA.

const keltner = td.indicators.keltner;

keltner.upper   // EMA + ATR * multiplier
keltner.middle  // EMA
keltner.lower   // EMA - ATR * multiplier
keltner.width   // Channel width as percentage

Chandelier Exit

Trailing stop indicator based on ATR.

const chandelier = td.indicators.chandelier;

chandelier.longStop   // Exit for long positions
chandelier.shortStop  // Exit for short positions
chandelier.direction  // 'long' | 'short'

Example: Chandelier Trailing Stop

const chandelier = td.indicators.chandelier;

if (td.position.side === 'long') {
    td.trade.setStopLoss(chandelier.longStop);
}

Heikin Ashi

Smoothed candlestick representation.

const ha = td.indicators.heikinAshi;

ha.open   // Heikin Ashi open
ha.high   // Heikin Ashi high
ha.low    // Heikin Ashi low
ha.close  // Heikin Ashi close
ha.trend  // 'bullish' | 'bearish' | 'doji'

Squeeze Momentum

Detects low volatility squeezes (BB inside KC).

const squeeze = td.indicators.squeezeMomentum;

squeeze.value     // Momentum value
squeeze.squeeze   // true if in squeeze (low volatility)
squeeze.momentum  // 'increasing' | 'decreasing'
squeeze.histogram // For visualization

Example: Squeeze Breakout

const squeeze = td.indicators.squeezeMomentum;
const prevSqueeze = td.state.get('prevSqueeze', false);

// Breakout from squeeze
if (prevSqueeze && !squeeze.squeeze && squeeze.momentum === 'increasing') {
    td.trade.buy({ reason: 'Squeeze breakout' });
}
td.state.set('prevSqueeze', squeeze.squeeze);

Elder Ray

Measures buying and selling pressure.

const elderRay = td.indicators.elderRay;

elderRay.bullPower  // High - EMA (buying pressure)
elderRay.bearPower  // Low - EMA (selling pressure)
elderRay.trend      // 'bullish' | 'bearish' | 'neutral'

Parabolic SAR

Trend-following stop and reverse indicator.

const psar = td.indicators.psar;

psar.value     // Current PSAR value
psar.trend     // 'bullish' | 'bearish'
psar.reversal  // true if trend just reversed

Example: PSAR Trend Following

const psar = td.indicators.psar;

if (psar.reversal && psar.trend === 'bullish') {
    td.trade.buy({ stopLoss: psar.value, reason: 'PSAR bullish reversal' });
}

Williams %R

Momentum indicator similar to Stochastic.

const williamsR = td.indicators.williamsR;

williamsR.value  // -100 to 0
williamsR.zone   // 'overbought' | 'oversold' | 'neutral'

CCI (Commodity Channel Index)

Measures price deviation from average.

const cci = td.indicators.cci;

cci.value  // CCI value (typically -200 to +200)
cci.zone   // 'overbought' | 'oversold' | 'neutral'

OBV (On-Balance Volume)

Volume-based trend confirmation.

const obv = td.indicators.obv;

obv.value       // OBV value
obv.trend       // 'accumulation' | 'distribution' | 'neutral'
obv.divergence  // 'bullish' | 'bearish' | 'none'

Example: OBV Divergence

const obv = td.indicators.obv;

if (obv.divergence === 'bullish' && td.indicators.rsi < 40) {
    td.trade.buy({ reason: 'Bullish OBV divergence' });
}

Market Sentiment Indicators

TradeStaq provides access to market sentiment data through td.market.sentiment.

Fear & Greed Index

The Crypto Fear & Greed Index measures market sentiment on a scale of 0-100.

const sentiment = td.market.sentiment;
const fearGreed = sentiment?.fearGreed;

if (fearGreed) {
    fearGreed.value           // Current value (0-100)
    fearGreed.classification  // 'extreme_fear' | 'fear' | 'neutral' | 'greed' | 'extreme_greed'
    fearGreed.timestamp       // Data timestamp
    fearGreed.previousValue   // Yesterday's value
    fearGreed.change          // Change from previous day
    fearGreed.weekAverage     // 7-day average
    fearGreed.monthAverage    // 30-day average
}

Classification Ranges

RangeClassificationMarket Sentiment
0-24extreme_fearVery bearish, potential buying opportunity
25-44fearBearish sentiment
45-55neutralBalanced market
56-74greedBullish sentiment
75-100extreme_greedVery bullish, potential top

Example: Fear & Greed Strategy

const fearGreed = td.market.sentiment?.fearGreed;
const rsi = td.indicators.rsi;

if (!fearGreed) {
    td.utils.log('Fear & Greed data not available');
    return;
}

// Buy on extreme fear with oversold RSI (contrarian)
if (!td.position.hasPosition) {
    if (fearGreed.classification === 'extreme_fear' && rsi < 30) {
        td.trade.buy({
            amountPercent: 50,
            reason: `Extreme fear (${fearGreed.value}) + RSI oversold`
        });
    }
}

// Take profits on extreme greed
if (td.position.hasPosition && td.position.side === 'long') {
    if (fearGreed.classification === 'extreme_greed' && rsi > 70) {
        td.trade.close(`Extreme greed (${fearGreed.value}) + RSI overbought`);
    }
}

// Log sentiment
td.utils.log('Market Sentiment', {
    value: fearGreed.value,
    classification: fearGreed.classification,
    change: fearGreed.change,
    weekAvg: fearGreed.weekAverage
});

Example: Sentiment Trend Analysis

const fearGreed = td.market.sentiment?.fearGreed;

if (fearGreed && fearGreed.weekAverage && fearGreed.monthAverage) {
    // Sentiment improving (short-term above long-term)
    const sentimentImproving = fearGreed.weekAverage > fearGreed.monthAverage;

    // Sentiment deteriorating
    const sentimentDeteriorating = fearGreed.weekAverage < fearGreed.monthAverage;

    // Current vs average (momentum)
    const sentimentMomentum = fearGreed.value - fearGreed.weekAverage;

    td.utils.log('Sentiment Analysis', {
        current: fearGreed.value,
        weekAvg: fearGreed.weekAverage,
        monthAvg: fearGreed.monthAverage,
        improving: sentimentImproving,
        momentum: sentimentMomentum
    });

    // Use sentiment as a filter
    if (!td.position.hasPosition && sentimentImproving && td.indicators.rsi < 40) {
        td.trade.buy({
            amountPercent: 25,
            reason: 'Sentiment improving + RSI favorable'
        });
    }
}

Sentiment Score Helper

Convert Fear & Greed to a -1 to 1 scale:

const fearGreed = td.market.sentiment?.fearGreed;

// Convert 0-100 to -1 to 1 scale
// -1 = extreme fear, 0 = neutral, 1 = extreme greed
function getSentimentScore(fg) {
    if (!fg) return 0;
    return (fg.value - 50) / 50;
}

const score = getSentimentScore(fearGreed);

// Use in position sizing
// Reduce size in extreme sentiment
const baseSize = 50; // 50% of balance
const sentimentMultiplier = 1 - Math.abs(score) * 0.5; // 0.5 to 1.0
const adjustedSize = baseSize * sentimentMultiplier;

td.utils.log('Position sizing', { score, baseSize, adjustedSize });

Data Availability

  • Fear & Greed Index updates once per day
  • Data is cached for 1 hour to reduce API calls
  • Always check if data exists before using: td.market.sentiment?.fearGreed
  • Historical averages require sufficient data (7/30 days)

News Sentiment (td.news)

TradeStaq provides real-time AI-powered news sentiment analysis through td.news. News data is aggregated from multiple sources, classified by impact level, and scored for sentiment — giving your strategies an information edge.

Tier Requirement: News sentiment requires a subscription tier with allowNewsTrading enabled (Pro and above).

Available Properties

const news = td.news;

news.sentiment                // Aggregate sentiment score (-1 to 1)
news.isBullish                // true if sentiment > 0.2
news.isBearish                // true if sentiment < -0.2
news.hasHighImpact            // true if high-impact articles exist
news.articleCount             // Total articles in lookback window
news.bullishCount             // Number of bullish articles
news.bearishCount             // Number of bearish articles
news.neutralCount             // Number of neutral articles
news.highImpactCount          // Number of high/critical impact articles
news.impactWeightedSentiment  // Sentiment weighted by article impact
news.latestHeadline           // Most recent headline string
news.latestSentiment          // Sentiment of the latest article
news.updatedAt                // Timestamp of last news update

Sentiment Scale

RangeClassificationMeaning
0.5 to 1.0Very BullishStrong positive news flow
0.2 to 0.5BullishPositive sentiment
-0.2 to 0.2NeutralMixed or no significant news
-0.5 to -0.2BearishNegative sentiment
-1.0 to -0.5Very BearishStrong negative news flow

Example: News Sentiment Filter

Use news as a confirmation filter alongside technical indicators:

const rsi = td.indicators.rsi;
const news = td.news;

if (!td.position.hasPosition) {
    // Only buy when both technicals AND news are favorable
    if (rsi < 30 && news.isBullish) {
        td.trade.buy({
            reason: `RSI oversold (${rsi.toFixed(1)}) + bullish news (${news.sentiment.toFixed(2)})`
        });
    }
}

// Exit on strongly bearish news even if technicals look ok
if (td.position.hasPosition && td.position.side === 'long') {
    if (news.sentiment < -0.5 && news.hasHighImpact) {
        td.trade.close(`Strongly bearish news: ${news.latestHeadline}`);
    }
}

Example: News-Weighted Position Sizing

Adjust position size based on news confidence:

const baseSize = 50; // 50% of balance
const news = td.news;

// Scale position size based on sentiment alignment
let sizeMultiplier = 1.0;

if (news.isBullish && news.hasHighImpact) {
    sizeMultiplier = 1.5; // Increase size on strong bullish news
} else if (news.isBearish) {
    sizeMultiplier = 0.5; // Reduce size on bearish news
}

const adjustedSize = Math.min(baseSize * sizeMultiplier, 100);
td.trade.buy({ amountPercent: adjustedSize, reason: 'News-adjusted entry' });

Example: News Momentum Strategy

Trade breakouts confirmed by news flow:

const news = td.news;
const macd = td.indicators.macd;
const prevSentiment = td.state.get('prevSentiment', 0);

// Detect sentiment shift
const sentimentImproving = news.sentiment > prevSentiment + 0.1;
const sentimentDeclining = news.sentiment < prevSentiment - 0.1;

td.state.set('prevSentiment', news.sentiment);

if (!td.position.hasPosition) {
    // Enter on improving sentiment + MACD confirmation
    if (sentimentImproving && macd.trend === 'bullish' && news.highImpactCount >= 2) {
        td.trade.buy({ reason: 'News momentum + MACD bullish' });
    }
}

td.utils.log('News State', {
    sentiment: news.sentiment,
    articles: news.articleCount,
    highImpact: news.highImpactCount,
    headline: news.latestHeadline
});

Visual Builder Support

News Sentiment is available in the No-Code Strategy Builder under the Sentiment category. You can add:

  • News Sentiment — The aggregate sentiment score (-1 to 1). Use comparisons like > 0.2 (bullish) or < -0.2 (bearish).
  • News Impact Count — Number of high-impact articles. Use > 0 to check for significant news.
  • News Article Count — Total articles in the lookback window.

These can be combined with any technical indicator in your entry/exit conditions using AND/OR logic.

Data Availability

  • News sentiment updates every 5-15 minutes depending on source refresh rates
  • Data is asset-specific — sentiment is computed for the trading pair's base asset
  • Always check if data exists: if (td.news) { ... }
  • If no news is available, td.news.sentiment defaults to 0 (neutral)

Custom Period Indicators

For indicators with custom periods, use td.indicators.custom. The platform auto-detects which indicators you need from your code by parsing td.indicators.custom['indicator_period'] patterns.

Direct Access Pattern

// Direct access (works for scalar values)
const ema20 = td.indicators.custom['ema_20'];     // 20-period EMA
const sma50 = td.indicators.custom['sma_50'];     // 50-period SMA
const rsi7 = td.indicators.custom['rsi_7'];       // 7-period RSI
const atr10 = td.indicators.custom['atr_10'];     // 10-period ATR

Recommended: Safe Accessor Pattern

Custom indicators may return arrays or scalar values. Use this helper function to safely access the current value:

// Helper function (include at top of your strategy)
function getIndicator(key) {
    const val = td.indicators.custom[key];
    if (val === undefined || val === null) return 0;
    if (Array.isArray(val)) return val[val.length - 1];
    return val;
}

// Usage with helper (recommended - handles both arrays and scalars)
const ema20 = getIndicator('ema_20');     // 20-period EMA
const sma50 = getIndicator('sma_50');     // 50-period SMA
const rsi7 = getIndicator('rsi_7');       // 7-period RSI
const atr10 = getIndicator('atr_10');     // 10-period ATR

Available Custom Indicators

PatternDescription
ema_NExponential Moving Average (N period)
sma_NSimple Moving Average (N period)
wma_NWeighted Moving Average (N period)
rsi_NRSI (N period)
atr_NATR (N period)
bbands_N_MBollinger Bands (N period, M stddev)

Example: Multiple EMA Crossover

// Helper function for safe indicator access
function getIndicator(key) {
    const val = td.indicators.custom[key];
    if (val === undefined || val === null) return 0;
    if (Array.isArray(val)) return val[val.length - 1];
    return val;
}

const ema9 = getIndicator('ema_9');
const ema21 = getIndicator('ema_21');
const ema50 = getIndicator('ema_50');

// Track previous values for crossover detection
const prevEma9 = td.state.get('prevEma9', ema9);
const prevEma21 = td.state.get('prevEma21', ema21);

// Golden cross: fast EMA crosses above slow EMA
const goldenCross = prevEma9 <= prevEma21 && ema9 > ema21;

if (goldenCross && td.market.price > ema50) {
    td.trade.buy({ reason: 'EMA golden cross above 50 EMA' });
}

// Save for next spin
td.state.set('prevEma9', ema9);
td.state.set('prevEma21', ema21);

Full Tulind Indicator Reference

TradeStaq provides access to all 100+ tulind indicators through the custom indicator system. Configure these in your bot settings or use td.indicators.custom.

Moving Averages

IndicatorNameOptionsDescription
emaExponential MA[period]Exponential moving average
smaSimple MA[period]Simple moving average
wmaWeighted MA[period]Weighted moving average
demaDouble EMA[period]Double exponential moving average
temaTriple EMA[period]Triple exponential moving average
trimaTriangular MA[period]Triangular moving average
kamaKaufman AMA[period]Kaufman adaptive moving average
mamaMESA Adaptive[fast, slow]MESA adaptive moving average
t3T3[period, factor]T3 moving average
zlemaZero-Lag EMA[period]Zero-lag exponential moving average
hmaHull MA[period]Hull moving average (smooth, responsive)
wildersWilder's Smooth[period]Wilder's smoothing
vwmaVolume Weighted[period]Volume-weighted moving average
vidyaVariable Index[short, long, alpha]Variable index dynamic average

Momentum Indicators

IndicatorNameOptionsDescription
rsiRSI[period]Relative strength index (0-100)
stochrsiStochastic RSI[period]Stochastic RSI oscillator
rocRate of Change[period]Rate of change
rocrROC Ratio[period]Rate of change ratio
momMomentum[period]Price momentum
cmoChande Momentum[period]Chande momentum oscillator
stochStochastic[k, slowing, d]Stochastic oscillator
willrWilliams %R[period]Williams %R (0 to -100)
ultoscUltimate Osc[short, med, long]Ultimate oscillator
aoAwesome Osc-Awesome oscillator
trixTRIX[period]Triple EXP smoothed price ROC
apoAbsolute Price[short, long]Absolute price oscillator
ppoPercentage Price[short, long]Percentage price oscillator
foscForecast Osc[period]Forecast oscillator
bopBalance of Power-Balance of power

Trend Indicators

IndicatorNameOptionsDescription
macdMACD[fast, slow, signal]Moving average convergence divergence
adxADX[period]Average directional index (trend strength)
adxrADXR[period]ADX rating
dxDX[period]Directional movement index
diDI[period]Directional indicator (+DI)
dmDM[period]Directional movement
aroonAroon[period]Aroon indicator (up/down)
aroonoscAroon Osc[period]Aroon oscillator
psarParabolic SAR[accel, max]Parabolic SAR
supertrendSupertrend[period, mult]Dynamic support/resistance trend indicator
vhfVHF[period]Vertical horizontal filter
dpoDPO[period]Detrended price oscillator

Volatility Indicators

IndicatorNameOptionsDescription
atrATR[period]Average true range
natrNATR[period]Normalized ATR
trTrue Range-True range (single value)
bbandsBollinger[period, stddev]Bollinger bands (lower, middle, upper)
volatilityVolatility[period]Annualized volatility
cciCCI[period]Commodity channel index
massMass Index[period]Mass index

Volume Indicators

IndicatorNameOptionsDescription
mfiMFI[period]Money flow index (volume-weighted RSI)
adAccum/Dist-Accumulation/distribution line
adoscA/D Oscillator[short, long]A/D oscillator
obvOBV-On-balance volume
nviNVI-Negative volume index
pviPVI-Positive volume index
kvoKlinger[short, long]Klinger volume oscillator
emvEMV-Ease of movement
marketfiMarket Fac-Market facilitation index
voscVolume Osc[short, long]Volume oscillator
wadW. Accum/Dist-Williams accumulation/distribution

Statistical & Math

IndicatorNameOptionsDescription
linregLinear Reg[period]Linear regression
linregslopeLinReg Slope[period]Linear regression slope
linreginterceptLinReg Int[period]Linear regression intercept
tsfTime Series[period]Time series forecast
stddevStd Deviation[period]Standard deviation
stderrStd Error[period]Standard error
varVariance[period]Variance
mdMean Deviation[period]Mean deviation

Price Transforms

IndicatorNameOptionsDescription
typpriceTypical Price-(H+L+C)/3
wcpriceWeighted Close-(H+L+2C)/4
medpriceMedian Price-(H+L)/2
avgpriceAverage Price-(O+H+L+C)/4
fisherFisher Transform[period]Fisher transform
qstickQStick[period]QStick indicator

Custom Indicator Configuration

Bot Settings Configuration

Pre-compute custom indicators by configuring them in your bot settings:

{
    "customIndicators": [
        { "name": "hma", "options": [20] },
        { "name": "kama", "options": [10] },
        { "name": "tema", "options": [14] },
        { "name": "aroon", "options": [25] },
        { "name": "psar", "options": [0.02, 0.2] }
    ]
}

Accessing Custom Indicators

Use this helper function to safely access custom indicators:

// Helper to get custom indicator value safely
function getIndicator(key) {
    const val = td.indicators.custom[key];
    if (val === undefined || val === null) return 0;
    if (Array.isArray(val)) return val[val.length - 1];
    return val;
}

// Access pre-computed custom indicators
const hma20 = getIndicator('hma_20');
const kama10 = getIndicator('kama_10');
const tema14 = getIndicator('tema_14');

// Multi-output indicators (like aroon returns up/down)
const aroonUp = getIndicator('aroon_25');        // First output
const aroonDown = getIndicator('aroon_25_1');    // Second output

// Parabolic SAR with multiple options
const psar = getIndicator('psar_0.02_0.2');

Naming Convention

Custom indicator keys follow this pattern:

{indicator_name}_{option1}_{option2}...        // Array or value
{indicator_name}_{option1}_{option2}..._{n}    // Additional outputs (0-indexed)

The getIndicator helper automatically handles both array and scalar values.


Using Any Tulind Indicator

Configure custom indicators in your bot or strategy settings, then access them using the helper:

// Helper to get custom indicator value safely
function getIndicator(key) {
    const val = td.indicators.custom[key];
    if (val === undefined || val === null) return 0;
    if (Array.isArray(val)) return val[val.length - 1];
    return val;
}

// Access any tulind indicator
const hma20 = getIndicator('hma_20');
const kama10 = getIndicator('kama_10');
const tema14 = getIndicator('tema_14');
const zlema30 = getIndicator('zlema_30');

For indicators with multiple options (e.g., PSAR with step and max), the key uses underscores: psar_0.02_0.2.


Combining Multiple Indicators

Confluence Strategy

// Multi-indicator confirmation system
let bullScore = 0;
let bearScore = 0;

// RSI
if (td.indicators.rsi < 35) bullScore++;
if (td.indicators.rsi > 65) bearScore++;

// MACD
if (td.indicators.macd.histogram > 0) bullScore++;
if (td.indicators.macd.histogram < 0) bearScore++;

// ADX trend direction
if (td.indicators.adx.value > 25) {
    if (td.indicators.adx.diPlus > td.indicators.adx.diMinus) bullScore++;
    else bearScore++;
}

// Bollinger position
if (td.indicators.bbands.percentB < 0.2) bullScore++;
if (td.indicators.bbands.percentB > 0.8) bearScore++;

// Require minimum confluence
const minScore = td.config.getNumber('MIN_CONFLUENCE', 3);

if (bullScore >= minScore && !td.position.hasPosition) {
    td.trade.buy({ reason: `Bull confluence: ${bullScore} signals` });
} else if (bearScore >= minScore && td.position.hasPosition) {
    td.trade.close(`Bear confluence: ${bearScore} signals`);
}

Advanced Custom Indicator Example

// Using Hull Moving Average for trend + RSI for timing

// Helper to get custom indicator value safely
function getIndicator(key) {
    const val = td.indicators.custom[key];
    if (val === undefined || val === null) return 0;
    if (Array.isArray(val)) return val[val.length - 1];
    return val;
}

// Helper to get full array for trend analysis
function getIndicatorArray(key) {
    const val = td.indicators.custom[key];
    if (Array.isArray(val)) return val;
    return [];
}

const hma = getIndicator('hma_20');
const hmaArray = getIndicatorArray('hma_20');
const price = td.market.price;
const rsi = td.indicators.rsi;

// HMA trend direction
let hmaTrend = 'neutral';
if (hmaArray.length >= 2) {
    const prevHMA = hmaArray[hmaArray.length - 2];
    if (hma > prevHMA) hmaTrend = 'bullish';
    else if (hma < prevHMA) hmaTrend = 'bearish';
}

// Price above/below HMA
const priceAboveHMA = price > hma;
const priceBelowHMA = price < hma;

// Entry conditions
if (!td.position.hasPosition) {
    // Long: Price above rising HMA + RSI oversold
    if (priceAboveHMA && hmaTrend === 'bullish' && rsi < 35) {
        td.trade.buy({ reason: 'HMA bullish + RSI oversold' });
    }

    // Short: Price below falling HMA + RSI overbought
    if (priceBelowHMA && hmaTrend === 'bearish' && rsi > 65) {
        td.trade.sell({ reason: 'HMA bearish + RSI overbought' });
    }
}

Parabolic SAR Trailing Stop

// Using Parabolic SAR for dynamic trailing stop
// Configure: { "customIndicators": [{ "name": "psar", "options": [0.02, 0.2] }] }

const psar = getIndicator('psar_0.02_0.2_last');
const price = td.market.price;

if (td.position.hasPosition) {
    const side = td.position.side;

    // Long position: PSAR below price = stay, above = exit
    if (side === 'long' && price < psar) {
        td.trade.close('PSAR stop hit');
    }

    // Short position: PSAR above price = stay, below = exit
    if (side === 'short' && price > psar) {
        td.trade.close('PSAR stop hit');
    }
}

Klinger Volume Oscillator

// Volume-based trend confirmation

// Helpers
function getIndicator(key) {
    const val = td.indicators.custom[key];
    if (val === undefined || val === null) return 0;
    if (Array.isArray(val)) return val[val.length - 1];
    return val;
}

function getIndicatorArray(key) {
    const val = td.indicators.custom[key];
    if (Array.isArray(val)) return val;
    return [];
}

const kvo = getIndicator('kvo_34_55');
const kvoArray = getIndicatorArray('kvo_34_55');
const macd = td.indicators.macd;

// KVO crossover detection
let kvoCrossUp = false;
let kvoCrossDown = false;

if (kvoArray.length >= 2) {
    const prevKVO = kvoArray[kvoArray.length - 2];
    kvoCrossUp = prevKVO <= 0 && kvo > 0;
    kvoCrossDown = prevKVO >= 0 && kvo < 0;
}

// Combine with MACD for stronger signal
if (!td.position.hasPosition) {
    if (kvoCrossUp && macd.trend === 'bullish') {
        td.trade.buy({ reason: 'KVO bullish crossover + MACD bullish' });
    }

    if (kvoCrossDown && macd.trend === 'bearish') {
        td.trade.sell({ reason: 'KVO bearish crossover + MACD bearish' });
    }
}

Best Practices

1. Don't Rely on Single Indicators

Use multiple indicators for confirmation. A single indicator can generate false signals.

2. Consider Market Conditions

  • Trend indicators (MACD, ADX) work best in trending markets
  • Oscillators (RSI, Stochastic) work best in ranging markets
  • Use ADX to determine market type first

3. Use Appropriate Timeframes

  • Higher timeframes reduce noise but lag more
  • Lower timeframes are more responsive but noisier
  • Consider multi-timeframe analysis for best results

4. Backtest Thoroughly

  • Test indicator combinations on historical data
  • Verify signals work across different market conditions
  • Check for sufficient trade count for statistical significance

5. Avoid Over-Optimization

  • Too many indicators can lead to curve fitting
  • Stick to 2-4 complementary indicators
  • Simpler strategies often outperform complex ones

6. Use Pre-computed Indicators

  • Configure frequently-used custom indicators in bot settings
  • This improves performance vs calculating on-the-fly
  • Cache is shared across strategy spins

Performance Considerations

Indicator Performance

TypePerformanceUse Case
Pre-calculated (rsi, macd, etc.)FastestUse for common indicators with default periods
Auto-detected custom (ema_20, sma_50)FastPlatform detects from your code and pre-computes
Bot-configured custom (hma, kama)FastConfigure in bot settings for advanced indicators

Optimization Tips

  1. Configure custom indicators in bot settings rather than calculating in strategy code
  2. Access arrays once and store in variables rather than repeated calls
  3. Use pre-calculated indicators when defaults are acceptable
  4. Limit custom indicator count to what you actually need

Next Steps