TD API Reference

Complete reference documentation for the td object available in custom strategies.

Overview

The td object is the global interface for interacting with the TradeStaq platform from within your strategy code. It provides access to:

  • Market data and prices
  • Technical indicators
  • Multi-timeframe (MTF) data
  • Support/Resistance zone analysis
  • Position information
  • Account details
  • Trade execution
  • State management
  • Configuration
  • Utilities
  • Sidebar data (strategy debug panel)
  • Chart drawings (price lines, markers, zones)

td.market

Current market data for the trading pair.

Field semantics depend on execution mode. In continuous mode (default), candle arrays and scalar OHLC reflect the currently forming candle. In on_bar_close mode, they are trimmed to closed bars only so signals match backtest behavior. td.market.price is always the live ticker regardless of mode — use price for order placement, close for bar-based signal logic. See Market Data → Execution Mode for the full field-by-field table.

td.market = {
    symbol: string;           // Trading pair (e.g., "BTC/USDT")
    exchange: string;         // Exchange identifier (e.g., "binance")
    timeframe: string;        // Current timeframe (e.g., "5m", "1h")
    price: number;            // LIVE market price (ticker) — always live, regardless of execution mode
    bid: number;              // Best bid price (live)
    ask: number;              // Best ask price (live)
    spread: number;           // Bid-ask spread (live)
    candles: OHLCV[];         // Most recent candles (up to 200). on_bar_close mode: closed bars only

    // Price arrays (for indicator calculations) — match candles: forming in continuous, closed-only in on_bar_close
    opens: number[];
    highs: number[];
    lows: number[];
    closes: number[];
    volumes: number[];
    timestamps: number[];

    // Latest candle OHLCV values — forming candle in continuous, last closed in on_bar_close
    open: number;
    high: number;
    low: number;
    close: number;
    volume: number;

    // Derived price types (useful for indicators) — derived from the same candle the scalars above describe
    hl2: number;              // (high + low) / 2
    hlc3: number;             // (high + low + close) / 3
    ohlc4: number;            // (open + high + low + close) / 4

    ticker: {
        symbol: string;
        bid: number;
        ask: number;
        last: number;
        high24h: number;
        low24h: number;
        volume24h: number;
        timestamp: number;
        openGraph: { title: 'TD API Reference', description: 'Complete API reference for the TradeStaq strategy SDK.' },
};

    // Order book data (if enabled)
    orderbook?: {
        bids: { price: number; amount: number }[];
        asks: { price: number; amount: number }[];
        timestamp: number;
        imbalance: number;    // -1 to 1 (positive = more bids/buying pressure)
        openGraph: { title: 'TD API Reference', description: 'Complete API reference for the TradeStaq strategy SDK.' },
};

    // Funding rate (futures only)
    funding?: {
        rate: number;         // Current funding rate
        nextTime: number;     // Next funding timestamp
        openGraph: { title: 'TD API Reference', description: 'Complete API reference for the TradeStaq strategy SDK.' },
};
}

OHLCV Candle Structure

interface OHLCV {
    timestamp: number;    // Unix timestamp (ms)
    open: number;
    high: number;
    low: number;
    close: number;
    volume: number;
}

Example Usage

// Access current price and context
const currentPrice = td.market.price;
const exchange = td.market.exchange;     // "binance"
const timeframe = td.market.timeframe;   // "1h"

// Calculate spread percentage
const spreadPct = (td.market.spread / td.market.price) * 100;

// Use derived prices for indicators
const typicalPrice = td.market.hlc3;     // Common in VWAP calculations
const avgPrice = td.market.ohlc4;        // Average of OHLC

// Access last candle
const lastCandle = td.market.candles[td.market.candles.length - 1];

// Get 24h price change
const priceChange = ((td.market.ticker.last - td.market.ticker.low24h) /
                      td.market.ticker.low24h) * 100;

// Check order book imbalance (if available)
if (td.market.orderbook) {
    const imbalance = td.market.orderbook.imbalance;
    if (imbalance > 0.3) {
        td.utils.log('Strong buying pressure', { imbalance });
    }
}

// Check funding rate (futures only)
if (td.market.funding) {
    const fundingRate = td.market.funding.rate;
    if (fundingRate > 0.001) {
        td.utils.log('High positive funding - shorts paying longs');
    }
}

td.indicators

Pre-calculated and on-demand technical indicators.

Pre-calculated Indicators

PropertyTypeDescription
rsinumberCurrent RSI (14 period)
rsiArraynumber[]Historical RSI values
macdobjectMACD data (line, signal, histogram, trend)
atrnumberAverage True Range
atrArraynumber[]Historical ATR values
bbandsobjectBollinger Bands (upper, middle, lower, width, percentB)
adxobjectADX data (value, diPlus, diMinus, trend)
stochasticobjectStochastic (k, d, zone)
mfinumberMoney Flow Index
pivotsobjectPivot points (pivot, r1-r3, s1-s3)

Custom Period Indicators

For custom period indicators (EMA, SMA, RSI, etc.), use td.indicators.custom. The platform auto-detects which indicators you need by parsing td.indicators.custom['indicator_period'] patterns in your code.

// Direct access (works for scalar values)
td.indicators.custom['ema_20']        // 20-period EMA
td.indicators.custom['sma_50']        // 50-period SMA
td.indicators.custom['rsi_7']         // 7-period RSI
td.indicators.custom['atr_10']        // 10-period ATR
td.indicators.custom['bbands_20_2']   // Bollinger Bands (20 period, 2 stddev)

Recommended: Safe accessor pattern (handles both arrays and scalars)

// 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
getIndicator('ema_20')        // 20-period EMA (current value)
getIndicator('sma_50')        // 50-period SMA (current value)
getIndicator('rsi_7')         // 7-period RSI (current value)

Example Usage

// 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;
}

// Access pre-calculated RSI (14 period default)
const rsi = td.indicators.rsi;

// Access custom period indicators using getIndicator
const ema9 = getIndicator('ema_9');
const ema21 = getIndicator('ema_21');

// EMA crossover detection
const prevEma9 = td.state.get('prevEma9', ema9);
const prevEma21 = td.state.get('prevEma21', ema21);

if (prevEma9 <= prevEma21 && ema9 > ema21) {
    td.trade.buy({ reason: 'EMA 9/21 golden cross' });
}

td.state.set('prevEma9', ema9);
td.state.set('prevEma21', ema21);

// MACD crossover detection
const macd = td.indicators.macd;
if (macd.histogram > 0 && macd.trend === 'bullish') {
    // Bullish signal
}

// Bollinger Band squeeze
const bb = td.indicators.bbands;
if (bb.width < 0.02) {
    // Low volatility - potential breakout
}

td.position

Information about your current position, plus methods to modify it.

Properties

td.position = {
    hasPosition: boolean;              // True if position is open
    side: 'long' | 'short' | 'none';   // Position direction
    size: number;                      // Position size in base currency
    entryPrice: number;                // Average entry price
    currentPrice: number;              // Current market price
    pnl: number;                       // Unrealized P&L (USD)
    pnlPercent: number;                // Unrealized P&L (%)
    stopLoss?: number;                 // Stop loss price (if set)
    takeProfit?: number;               // Take profit price (if set)
    leverage: number;                  // Current leverage
    liquidationPrice?: number;         // Liquidation price (futures)
    margin: number;                    // Used margin
    openTime: number;                  // Position open timestamp

    // Trailing stop state (if configured)
    trailingStop?: {
        enabled: boolean;              // Whether trailing stop is configured
        activated: boolean;            // Whether trail has activated (reached profit threshold)
        percent?: number;              // Trail distance in %
        amount?: number;               // Trail distance in absolute value
        activationPercent?: number;    // Profit % required to activate trailing
        highWaterMark?: number;        // Current high water mark price
        currentTrailPrice?: number;    // Current calculated trail stop price
        openGraph: { title: 'TD API Reference', description: 'Complete API reference for the TradeStaq strategy SDK.' },
};
}

Position Methods

Methods to modify your current position:

// Move stop loss to breakeven (entry price + optional offset)
td.position.setBreakeven(offsetPercent?: number): void;

// Set a trailing stop on existing position
td.position.setTrailingStop(options: {
    amount?: number;          // Trail by fixed amount
    percent?: number;         // Trail by percentage
    activationPrice?: number; // Price to activate trailing
}): void;

// Modify stop loss price
td.position.modifyStopLoss(price: number): void;

// Modify take profit price
td.position.modifyTakeProfit(price: number): void;

// Scale into position (add to current)
td.position.addToPosition(options: {
    amount?: number;
    amountPercent?: number;
}): void;

// Scale out of position (partial close)
td.position.reducePosition(percent: number): void;

Example Usage

// Check if in profit
if (td.position.hasPosition && td.position.pnlPercent > 5) {
    td.trade.close('Taking profit at 5%');
}

// Trail stop loss
if (td.position.hasPosition && td.position.side === 'long') {
    const newStop = td.market.price * 0.98;
    if (!td.position.stopLoss || newStop > td.position.stopLoss) {
        td.position.modifyStopLoss(newStop);
    }
}

// Move to breakeven after 2% profit
if (td.position.hasPosition && td.position.pnlPercent > 2) {
    td.position.setBreakeven(0.1); // Entry + 0.1% buffer
}

// Set trailing stop after reaching profit target
if (td.position.hasPosition && td.position.pnlPercent > 3) {
    td.position.setTrailingStop({
        percent: 1.5, // Trail 1.5% behind
    });
}

// Check if trailing stop is already configured before setting
if (td.position.hasPosition && !td.position.trailingStop?.enabled) {
    td.trade.closeTrail({
        trailPercent: 1.5,
        activationPercent: 1.0,
        reason: 'Setting up trailing stop'
    });
}

// Check if trailing stop has activated
if (td.position.trailingStop?.activated) {
    td.utils.log('Trail is active', {
        highWaterMark: td.position.trailingStop.highWaterMark,
        currentTrailPrice: td.position.trailingStop.currentTrailPrice
    });
}

// Scale into position
if (td.position.hasPosition && td.indicators.rsi < 25) {
    td.position.addToPosition({ amountPercent: 25 }); // Add 25% more
}

// Take partial profits
if (td.position.hasPosition && td.position.pnlPercent > 10) {
    td.position.reducePosition(50); // Close 50% of position
}

td.account

Account balance, permissions, and bot configuration.

td.account = {
    balance: number;                   // Total account balance
    availableBalance: number;          // Available for trading
    isPaper: boolean;                  // True if paper trading
    tier: string;                      // User's subscription tier
    maxPositionSize: number;           // Max allowed position
    allowedTimeframes: string[];       // Permitted timeframes
    minSpinInterval: number;           // Min seconds between spins
    orderSize: number;                 // Bot's configured order size (from position sizing)
    leverage: number;                  // Bot's configured leverage
}

Example Usage

// Use bot's configured order size
td.trade.buy({
    amount: td.account.orderSize,
    reason: 'Using bot order size'
});

// Position sizing based on balance
const riskPercent = 2;
const positionSize = td.account.availableBalance * (riskPercent / 100);

// Check if paper trading
if (td.account.isPaper) {
    td.utils.log('Running on paper account');
}

// Log account info
td.utils.log('Account', {
    balance: td.account.balance,
    leverage: td.account.leverage,
    isPaper: td.account.isPaper
});

td.orders

Recent order history.

td.orders = {
    orders: Order[];                   // Recent orders array
    lastBuy?: Order;                   // Last buy order
    lastSell?: Order;                  // Last sell order
}

interface Order {
    id: string;
    symbol: string;
    side: 'buy' | 'sell';
    type: 'market' | 'limit' | 'stop' | 'stop_limit';
    size: number;                      // Order size
    price?: number;                    // Limit price (optional for market orders)
    stopPrice?: number;                // Stop trigger price
    status: 'open' | 'filled' | 'cancelled' | 'rejected';
    filledSize: number;                // Amount filled so far
    avgFillPrice?: number;             // Average fill price
    createdAt: number;                 // Order creation timestamp
    updatedAt: number;                 // Last update timestamp
}

Example Usage

// Check last order status
if (td.orders.lastBuy && td.orders.lastBuy.status === 'filled') {
    td.utils.log('Last buy order filled', {
        price: td.orders.lastBuy.avgFillPrice,
        size: td.orders.lastBuy.filledSize
    });
}

// Count open orders
const openOrders = td.orders.orders.filter(o => o.status === 'open');
td.utils.log('Open orders', { count: openOrders.length });

td.trade

Trade execution functions.

td.trade = {
    // === Core signals ===
    buy(options?: TradeOptions): void;
    sell(options?: TradeOptions): void;
    buyLimit(price: number, options?: TradeOptions): void;
    sellLimit(price: number, options?: TradeOptions): void;
    close(reason?: string): void;
    setStopLoss(price: number): void;
    setTakeProfit(price: number): void;

    // === Entry with trailing stop ===
    buyTrail(options: TrailOptions): void;
    sellTrail(options: TrailOptions): void;

    // === Exit with trailing stop ===
    closeTrail(options: TrailOptions): void;

    // === Time-based exits ===
    closeOnCandleClose(options?: CandleCloseOptions): void;
    closeAfter(options: { seconds?: number; candles?: number }): void;

    // === Advanced exit management ===
    lockProfits(options: { stepPercent?: number; trailPercent?: number }): void;
    breakevenAfter(options: { profitPercent?: number; offsetPercent?: number }): void;

    // === Position management ===
    flip(reason?: string): void;
}

interface TradeOptions {
    amount?: number;          // Fixed amount in base currency
    amountPercent?: number;   // Percentage of balance (1-100)
    price?: number;           // Limit price (market if omitted)
    stopLoss?: number;        // Stop loss price
    takeProfit?: number;      // Take profit price
    leverage?: number;        // Position leverage
    reason?: string;          // Log reason for trade
}

interface TrailOptions {
    trailAmount?: number;     // Trail by fixed amount
    trailPercent?: number;    // Trail by percentage
    activationPrice?: number; // Price at which to activate trailing
    activationPercent?: number; // Profit % to activate trailing
    reason?: string;
}

interface CandleCloseOptions {
    offsetSeconds?: number;   // Exit this many seconds before candle close (default: 0)
    reason?: string;          // Log reason
}

Trade Limits

  • Maximum 5 signals per spin
  • Duplicate signals are ignored
  • Signals queue for execution after strategy completes

Example Usage

// Market buy with full balance
td.trade.buy({
    amountPercent: 100,
    stopLoss: td.market.price * 0.98,
    takeProfit: td.market.price * 1.04,
    reason: 'RSI oversold entry'
});

// Limit sell order (manual control)
td.trade.sell({
    amountPercent: 50,
    price: td.market.price * 1.02,
    reason: 'Scaling out'
});

// Limit buy order (explicit syntax)
td.trade.buyLimit(42500, {
    amountPercent: 10,
    stopLoss: 41000,
    reason: 'Buying dip at support'
});

// Close position
td.trade.close('Strategy exit signal');

// Modify stop loss
td.trade.setStopLoss(td.market.price * 0.95);

Close on Candle Close

Schedule all open trades to close at the current candle boundary. This is a strategy-level signal — it tells the order monitor to close the position when the candle ends, based on the bot's timeframe.

// Close at candle end (default)
td.trade.closeOnCandleClose();

// Close 2 minutes before candle end
td.trade.closeOnCandleClose({
    offsetSeconds: 120,
    reason: 'Exit before candle close'
});

How it works:

  1. Strategy emits a close_on_candle signal
  2. The system calculates when the current candle closes (based on bot timeframe)
  3. advancedExit is set on all open trades for this bot/symbol
  4. The order monitor closes the trade when the timestamp is reached

Note: There are two ways to use candle close:

  • Bot-level setting — Enable "Close on Candle Close" in bot configuration. Every entry trade is automatically scheduled to close at candle end. No strategy code needed.
  • Strategy-level signal — Call td.trade.closeOnCandleClose() in your strategy code. This gives you programmatic control over when to apply it (e.g., only on certain conditions).

Priority: If a trailing stop is active on a trade, candle close will not fire — the trailing stop takes priority.

Trailing Stop Examples

// Entry with trailing stop (long)
td.trade.buyTrail({
    trailPercent: 1.5,
    activationPercent: 1.0,    // Activate after 1% profit
    reason: 'Trailing entry'
});

// Exit with trailing stop on existing position
td.trade.closeTrail({
    trailPercent: 2.0,
    activationPercent: 1.5,
    reason: 'Trailing take profit'
});

// Lock profits progressively (move SL up in steps)
td.trade.lockProfits({
    stepPercent: 5,     // Move SL every 5% profit
    trailPercent: 50    // Keep SL at 50% of max profit
});

// Auto-breakeven after reaching profit threshold
td.trade.breakevenAfter({
    profitPercent: 2,   // Trigger at 2% profit
    offsetPercent: 0.1  // Set SL at entry + 0.1%
});

// Flip position (close current + open opposite)
td.trade.flip('Signal reversed');

td.state

Persistent state storage across spins.

td.state = {
    get<T>(key: string, defaultValue?: T): T;
    set(key: string, value: unknown): void;
    getAll(): Record<string, unknown>;
    delete(key: string): void;
    clear(): void;
}

Example Usage

// Track trade count
const tradeCount = td.state.get('tradeCount', 0);
td.state.set('tradeCount', tradeCount + 1);

// Store previous indicator values
td.state.set('prevRsi', td.indicators.rsi);

// Clear stale state periodically
const lastClear = td.state.get('lastStateClear', 0);
if (Date.now() - lastClear > 86400000) { // 24 hours
    td.state.clear();
    td.state.set('lastStateClear', Date.now());
}

td.config

Access strategy configuration parameters.

td.config = {
    get(key: string, defaultValue?: string): string;
    getNumber(key: string, defaultValue?: number): number;
    getBoolean(key: string, defaultValue?: boolean): boolean;
    getSelect(key: string, defaultValue: string, options: string[]): string;
}

Example Usage

// Get configurable parameters
const rsiPeriod = td.config.getNumber('RSI_PERIOD', 14);
const oversold = td.config.getNumber('RSI_OVERSOLD', 30);
const overbought = td.config.getNumber('RSI_OVERBOUGHT', 70);
const useStopLoss = td.config.getBoolean('USE_STOP_LOSS', true);
const tradingMode = td.config.get('TRADING_MODE', 'conservative');

// Get select option (validates against allowed options)
const exitMode = td.config.getSelect('EXIT_MODE', 'trailing', ['trailing', 'fixed', 'scaled']);
const entryType = td.config.getSelect('ENTRY_TYPE', 'market', ['market', 'limit', 'dca']);

// If configured value is not in options, returns defaultValue
// e.g., if EXIT_MODE='invalid', returns 'trailing' (the default)

td.utils

Utility functions for common operations.

td.utils = {
    log(message: string, data?: unknown): void;

    // Cross detection functions - flexible input types
    crossOver(
        series1: number[] | { current: number; previous: number },
        series2: number[] | { current: number; previous: number } | number
    ): boolean;

    crossUnder(
        series1: number[] | { current: number; previous: number },
        series2: number[] | { current: number; previous: number } | number
    ): boolean;

    cross(
        series1: number[] | { current: number; previous: number },
        series2: number[] | { current: number; previous: number } | number
    ): boolean;  // Returns true for either crossOver or crossUnder

    round(value: number, decimals?: number): number;
    percentChange(from: number, to: number): number;
    inRange(value: number, min: number, max: number): boolean;
}

Cross Detection

The cross functions support multiple input formats:

Input TypeDescriptionExample
number[]Array of values (uses last 2 elements)emaArray(9)
{current, previous}Object with current and previous values{current: rsi, previous: prevRsi}
number (series2 only)Static threshold value30 for RSI oversold

Example Usage

// Logging
td.utils.log('Strategy executed', { price: td.market.price });

// 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;
}

// EMA Crossover using state (recommended pattern)
const ema9 = getIndicator('ema_9');
const ema21 = getIndicator('ema_21');
const prevEma9 = td.state.get('prevEma9', ema9);
const prevEma21 = td.state.get('prevEma21', ema21);

if (td.utils.crossOver(
    { current: ema9, previous: prevEma9 },
    { current: ema21, previous: prevEma21 }
)) {
    td.trade.buy({ reason: 'EMA 9/21 golden cross' });
}
td.state.set('prevEma9', ema9);
td.state.set('prevEma21', ema21);

// RSI crossing above 30 (oversold recovery)
const prevRsi = td.state.get('prevRsi', td.indicators.rsi);
if (td.utils.crossOver({ current: td.indicators.rsi, previous: prevRsi }, 30)) {
    td.trade.buy({ reason: 'RSI crossing above oversold' });
}
td.state.set('prevRsi', td.indicators.rsi);

// Stochastic K/D cross (any direction)
const stoch = td.indicators.stochastic;
const prevK = td.state.get('prevStochK', stoch.k);
const prevD = td.state.get('prevStochD', stoch.d);
if (td.utils.cross(
    { current: stoch.k, previous: prevK },
    { current: stoch.d, previous: prevD }
)) {
    td.utils.log('Stochastic K/D crossed');
}
td.state.set('prevStochK', stoch.k);
td.state.set('prevStochD', stoch.d);

// MACD crossover
const macd = td.indicators.macd;
const prevMacd = td.state.get('prevMacd', macd.line);
const prevSignal = td.state.get('prevSignal', macd.signal);
if (td.utils.crossOver(
    { current: macd.line, previous: prevMacd },
    { current: macd.signal, previous: prevSignal }
)) {
    td.trade.buy({ reason: 'MACD bullish crossover' });
}
td.state.set('prevMacd', macd.line);
td.state.set('prevSignal', macd.signal);

// Price calculations
const change = td.utils.percentChange(entryPrice, td.market.price);
const roundedPrice = td.utils.round(td.market.price, 2);

// Range check
if (td.utils.inRange(td.indicators.rsi, 30, 70)) {
    // RSI in neutral zone
}

td.mtf

Multi-timeframe data access for analyzing higher/lower timeframes. Supports both string-based (e.g., '4h') and index-based (e.g., 1) access.

Index Access (Recommended): Use index-based access for dynamic MTF strategies where users configure their own timeframes in the MTF step.

  • Index 0 = Primary timeframe (bot's main timeframe)
  • Index 1 = First additional MTF timeframe
  • Index 2 = Second additional MTF timeframe, etc.
td.mtf = {
    // Get indicators (by timeframe string or index)
    indicators(tfOrIndex: string | number): IndicatorValues | null;

    // Get OHLCV candles (by timeframe string or index)
    candles(tfOrIndex: string | number): OHLCV[];

    // Get ticker-like data (last candle OHLC)
    ticker(tfOrIndex: string | number): { high: number; low: number; open: number; close: number } | null;

    // List available timeframes
    available(): string[];

    // Check if a timeframe is loaded (by string or index)
    has(tfOrIndex: string | number): boolean;

    // Get timeframe string by index
    timeframe(index: number): string | null;

    // Number of available timeframes
    count(): number;

    // The primary (bot configured) timeframe
    primary: string;

    // Raw MTF data for advanced access
    data: Record<string, MTFData>;
}

Index-Based Access (Recommended)

Use index-based access when users configure MTF timeframes in the wizard:

// Access by index - works with any user-configured timeframes
const mtf1Rsi = td.mtf.indicators(1)?.rsi;          // First MTF RSI
const mtf1Trend = td.mtf.indicators(1)?.macd?.trend; // First MTF MACD trend
const mtf2Rsi = td.mtf.indicators(2)?.rsi;          // Second MTF RSI

// Get timeframe names for logging
const mtf1Name = td.mtf.timeframe(1);  // e.g., '4h' or whatever user configured
const mtf2Name = td.mtf.timeframe(2);  // e.g., '1d'

// Check MTF availability
const mtfCount = td.mtf.count();       // Number of timeframes (including primary)
const hasMtf1 = td.mtf.has(1);         // true if first MTF exists

// Multi-timeframe confluence strategy
const primaryRsi = td.indicators.rsi;
const htfTrend = td.mtf.indicators(1)?.macd?.trend;

if (primaryRsi < 30 && htfTrend === 'bullish') {
    td.trade.buy({ reason: `RSI oversold + ${mtf1Name} bullish` });
}

String-Based Access

Use string-based access when you know the exact timeframe:

// Access by timeframe string
const htfRsi = td.mtf.indicators('4h')?.rsi;
const dailyTrend = td.mtf.indicators('1d')?.macd?.trend;

// Get higher timeframe candles
const htfCandles = td.mtf.candles('4h');
const htfLastCandle = htfCandles[htfCandles.length - 1];

// Check what timeframes are available
const availableTFs = td.mtf.available(); // e.g., ['1h', '4h', '1d']

// Conditional logic based on MTF availability
if (td.mtf.has('4h')) {
    const htfRsi = td.mtf.indicators('4h')?.rsi;
    if (htfRsi > 70) {
        td.utils.log('4H RSI overbought - avoiding longs');
    }
}

// Access ticker data for quick price reference
const htfClose = td.mtf.ticker('4h')?.close;
const htfHigh = td.mtf.ticker('1d')?.high;

td.zones

Multi-timeframe support and resistance zone analysis. Available when S/R Zone Analysis is enabled in the strategy's Multi-Timeframe Setup step.

The platform dynamically selects 3 candle timeframes (short, medium, long) from your exchange's supported timeframes and generates up to 20 logarithmically-spaced time intervals — from intraday up to yearly windows. Each interval computes classic pivot points (P, S1-S3, R1-R3) and checks if the current price is near any level using ATR-based and/or percentage-based proximity detection.

td.zones = {
    // Confluence counts — how many intervals have price near S/R
    withinSupportCount: number;       // ATR-based support proximity (0 to totalIntervals)
    withinResistanceCount: number;    // ATR-based resistance proximity
    withinSupportCountPct: number;    // Percentage-based support proximity
    withinResistanceCountPct: number; // Percentage-based resistance proximity

    // Strongest (closest) levels across all intervals
    strongestSupport: number;         // Nearest support price
    strongestResistance: number;      // Nearest resistance price

    // Metadata
    totalIntervals: number;           // Actual interval count (up to 20)
    timeframesUsed: string[];         // The 3 candle TFs selected (e.g., ['15m', '4h', '1w'])
    computedAt: number;               // Computation timestamp

    // Per-interval breakdown
    levels: ZoneLevelResult[];        // Array of interval-level details
}

Each entry in levels contains:

{
    label: string;              // Human-readable label (e.g., '15m', '1d', '3M')
    durationMs: number;         // Interval duration in milliseconds
    pivot: number;              // Pivot point: (H + L + C) / 3
    r1: number;                 // Resistance 1: 2P - L
    r2: number;                 // Resistance 2: P + (H - L)
    r3: number;                 // Resistance 3: H + 2(P - L)
    s1: number;                 // Support 1: 2P - H
    s2: number;                 // Support 2: P - (H - L)
    s3: number;                 // Support 3: L - 2(H - P)
    nearSupport: boolean;       // Price near any support (ATR-based)
    nearResistance: boolean;    // Price near any resistance (ATR-based)
    nearSupportPct: boolean;    // Price near any support (%-based)
    nearResistancePct: boolean; // Price near any resistance (%-based)
    closestSupportDist: number; // Distance to nearest support level
    closestResistanceDist: number; // Distance to nearest resistance level
}

Zone Confluence Strategy

Use withinSupportCount / withinResistanceCount to gauge how many timeframes agree that price is near S/R — higher counts mean stronger confluence:

const zones = td.zones;
if (!zones) return; // Zones not enabled

const { price } = td.market;
const { rsi } = td.indicators;

// Strong support confluence — price near support on 5+ intervals
const supportDepth = zones.withinSupportCount;
const resistanceDepth = zones.withinResistanceCount;

// Buy near strong multi-TF support
if (!td.position.hasPosition && supportDepth >= 5 && rsi < 40) {
    td.trade.buy({
        amountPercent: 5,
        stopLoss: zones.strongestSupport * 0.99, // Below strongest support
        takeProfit: zones.strongestResistance,     // Target nearest resistance
        reason: `S/R confluence: ${supportDepth}/${zones.totalIntervals} support zones`
    });
}

// Sell near strong multi-TF resistance
if (td.position.hasPosition && td.position.side === 'long' && resistanceDepth >= 5) {
    td.trade.close(`Near resistance: ${resistanceDepth}/${zones.totalIntervals} zones`);
}

Accessing Individual Levels

Each level in td.zones.levels has a label corresponding to its time window. Use .find() to access a specific interval:

Available labels (depending on exchange support): 15m, 30m, 1h, 2h, 4h, 8h, 12h, 1d, 3d, 1w, 2w, 1M, 3M, 6M, 1Y, 2Y, 3Y+

Not all labels will be present — the platform generates up to 20 intervals logarithmically spaced from the shortest to longest timeframe. Duplicate labels are deduplicated. The actual labels depend on which candle timeframes your exchange supports.

const zones = td.zones;
if (!zones) return;

// Access a specific interval — e.g. 3-month S/R levels
const threeMonth = zones.levels.find(l => l.label === '3M');
if (threeMonth) {
    td.utils.log('3M Support/Resistance', {
        pivot: threeMonth.pivot,
        s1: threeMonth.s1, s2: threeMonth.s2, s3: threeMonth.s3,
        r1: threeMonth.r1, r2: threeMonth.r2, r3: threeMonth.r3,
        nearSupport: threeMonth.nearSupport
    });

    // Use 3-month support as stop loss reference
    if (!td.position.hasPosition && td.indicators.rsi < 35) {
        td.trade.buy({
            amountPercent: 5,
            stopLoss: threeMonth.s2,          // Below 3M S2
            takeProfit: threeMonth.r1,         // Target 3M R1
            reason: 'RSI oversold near 3M support'
        });
    }
}

// Use the daily pivot as a bias filter
const dailyLevel = zones.levels.find(l => l.label === '1d');
if (dailyLevel) {
    const abovePivot = td.market.price > dailyLevel.pivot;
    td.state.set('dailyBias', abovePivot ? 'bullish' : 'bearish');
}

// Find all intervals where price is near support
const supportZones = zones.levels.filter(l => l.nearSupport);
td.utils.log(`Near support on: ${supportZones.map(l => l.label).join(', ')}`);
// e.g. "Near support on: 4h, 1d, 3M"

Configuration

Zone analysis is configured in the Multi-Timeframe Setup step of the strategy wizard:

SettingDescriptionDefault
Proximity TypeHow to detect if price is "near" a level: atr, percentage, or bothboth
ATR MultiplierPrice within ATR × multiplier counts as near (uses primary TF ATR)1.0
% ThresholdPrice within this % of a level counts as near0.5%

Bot-level settings can override strategy defaults.


td.meta

Metadata about the current execution context.

td.meta = {
    version: string;              // API version (e.g., "1.0.0")
    spinCount: number;            // Total spins executed
    spinInterval: number;         // Seconds between spins
    lastSpinTime: number | null;  // Last spin timestamp
    timeframe: string;            // Chart timeframe (e.g., "1h")
    botId: string;                // Bot identifier
    strategyId: string;           // Strategy identifier
}

Example Usage

// Skip first few spins for warmup
if (td.meta.spinCount < 5) {
    td.utils.log('Warming up...');
    return;
}

// Log execution info
td.utils.log('Spin info', {
    spinCount: td.meta.spinCount,
    timeframe: td.meta.timeframe,
    botId: td.meta.botId
});

td.alert()

Send alerts via email/Telegram. Alerts are throttled to prevent spam.

td.alert(
    level: 'info' | 'warning' | 'error' | 'trade_entry' | 'trade_exit',
    title: string,
    message: string,
    data?: object
): void;

Alert Levels

LevelDescriptionUse Case
infoInformationalStrategy status updates
warningWarningUnusual conditions, potential issues
errorErrorStrategy errors, failed conditions
trade_entryTrade entryPosition opened notifications
trade_exitTrade exitPosition closed notifications

Example Usage

// Send info alert
td.alert('info', 'Strategy Started', 'RSI strategy is now active', {
    symbol: td.market.symbol,
    price: td.market.price
});

// Warn about unusual conditions
if (td.market.orderbook?.imbalance < -0.5) {
    td.alert('warning', 'Heavy Selling Pressure',
        'Order book shows significant selling pressure', {
        imbalance: td.market.orderbook.imbalance
    });
}

// Alert on trade entry
if (entryCondition && !td.position.hasPosition) {
    td.trade.buy({ reason: 'RSI oversold entry' });
    td.alert('trade_entry', 'Long Entry',
        `Opened long position on ${td.market.symbol}`, {
        price: td.market.price,
        rsi: td.indicators.rsi
    });
}

// Alert on significant profit
if (td.position.hasPosition && td.position.pnlPercent > 10) {
    td.alert('info', 'Profit Target Near',
        `Position up ${td.position.pnlPercent.toFixed(1)}%`);
}

Throttling

Alerts are throttled to prevent spam:

  • Same alert type is limited to once per configured interval
  • Excessive alerts may be queued or dropped
  • Check your notification settings for throttle configuration

td.sidebar

Display key-value stats in a Strategy tab on the bot's chart sidebar. Use this to surface runtime debug data — zone counts, trend state, margin ratio, profit targets, DCA status, and more — directly alongside the chart.

Sidebar data is persisted on the bot document after each spin and overwrites the previous values.

td.sidebar = {
    set(label: string, value: string | number | boolean, options?: {
        color?: string;     // Value text color (e.g., '#22c55e')
        group?: string;     // Group header (items with same group are grouped together)
    }): void;

    clear(): void;          // Remove all sidebar items
}

Limits

ConstraintValue
Max items per spin30
Max label length30 characters
Max value length50 characters
Max group name length20 characters
DeduplicationLast write wins (by label)

Example Usage

// Basic stats (ungrouped — appear at top)
td.sidebar.set('Mode', 'LONG');
td.sidebar.set('Trend', 'Bullish', { color: '#22c55e' });

// Grouped stats
td.sidebar.set('Margin', marginRatio.toFixed(1) + '%', {
    color: marginRatio > 50 ? '#ef4444' : '#22c55e',
    group: 'Risk'
});
td.sidebar.set('Stop Loss', stopLossPercent.toFixed(1) + '%', {
    group: 'Risk',
    color: '#ef4444'
});
td.sidebar.set('PnL', pnlPercent.toFixed(2) + '%', {
    color: pnlPercent >= 0 ? '#22c55e' : '#ef4444',
    group: 'Risk'
});

td.sidebar.set('Target', minProfit.toFixed(2) + '%', {
    group: 'Targets',
    color: '#22c55e'
});
td.sidebar.set('DCA', dcaBuys + '/' + maxDcaBuys, { group: 'Entry' });
td.sidebar.set('Entry Signal', entrySignal || 'None', {
    color: entrySignal ? '#22c55e' : '#6b7280',
    group: 'Entry'
});

// Overwrite a previous value (same label = last write wins)
td.sidebar.set('Trend', 'Bearish', { color: '#ef4444' });

// Clear all sidebar items (rarely needed)
td.sidebar.clear();

Display

The sidebar renders as a Strategy tab in the chart sidebar panel on bot detail pages. Items are displayed as label/value rows:

  • Ungrouped items appear first
  • Grouped items are displayed under section headers
  • Values with a color option are rendered in that color

Note: The Strategy tab only appears when the bot has sidebar data. If td.sidebar.set() is never called, the tab is hidden.


td.chart

Draw visual elements on the bot's chart — price lines, markers, and zones. Chart drawings are persisted on the bot document after each spin and overwrite the previous set.

td.chart = {
    priceLine(price: number, options?: {
        color?: string;                              // Line color (default: '#2196F3')
        lineStyle?: 'solid' | 'dashed' | 'dotted';  // Line style (default: 'solid')
        lineWidth?: number;                          // Line width in pixels (default: 1)
        title?: string;                              // Label shown on the line
    }): void;

    marker(time: number, options?: {
        position?: 'above' | 'below';                // Marker position (default: 'above')
        shape?: 'arrowUp' | 'arrowDown' | 'circle';  // Marker shape (default: 'circle')
        color?: string;                               // Marker color
        text?: string;                                // Marker label
    }): void;

    zone(priceHigh: number, priceLow: number, options?: {
        color?: string;                              // Fill color (default: rgba blue)
        borderColor?: string;                        // Border color
        title?: string;                              // Zone label
    }): void;

    clear(): void;          // Remove all drawings
}

Limits

ConstraintValue
Max drawings per spin20
Max title/text length30 characters

Example Usage

// Draw support and resistance lines
td.chart.priceLine(pivotPoints.support, {
    color: '#22c55e',
    lineStyle: 'dotted',
    title: 'Support'
});
td.chart.priceLine(pivotPoints.resistance, {
    color: '#ef4444',
    lineStyle: 'dotted',
    title: 'Resistance'
});
td.chart.priceLine(pivotPoints.pivot, {
    color: '#3b82f6',
    lineStyle: 'dashed',
    title: 'Pivot'
});

// Draw entry, TP, and SL lines when in a position
if (td.position.hasPosition) {
    td.chart.priceLine(td.position.entryPrice, {
        color: '#3b82f6',
        lineStyle: 'solid',
        title: 'Entry'
    });
    td.chart.priceLine(profitTarget, {
        color: '#22c55e',
        lineStyle: 'dashed',
        title: 'TP'
    });
    td.chart.priceLine(stopLossPrice, {
        color: '#ef4444',
        lineStyle: 'solid',
        title: 'SL'
    });
}

// Mark a signal event on the chart
td.chart.marker(Date.now(), {
    position: 'below',
    shape: 'arrowUp',
    color: '#22c55e',
    text: 'Buy Signal'
});

// Highlight a price zone (e.g., consolidation range)
td.chart.zone(resistancePrice, supportPrice, {
    color: 'rgba(139, 92, 246, 0.1)',
    borderColor: '#3b82f6',
    title: 'Range'
});

// Clear all drawings (useful before redrawing)
td.chart.clear();

Drawing Types

Price Lines

Horizontal lines drawn across the chart at a specific price. Use them for support/resistance levels, entry prices, stop losses, and take profit targets.

Markers

Point markers placed at a specific time on the chart. Use them to mark trade entries, exits, or signal events.

Zones

Shaded rectangular regions between two price levels. Use them to highlight consolidation ranges, supply/demand zones, or price bands.

Note: Drawings are overwritten on every spin. If you want persistent lines, call td.chart.priceLine() on every spin. If a line should only appear conditionally (e.g., only when in a position), wrap it in an if block — it will disappear on the next spin when the condition is false.