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
continuousmode (default), candle arrays and scalar OHLC reflect the currently forming candle. Inon_bar_closemode, they are trimmed to closed bars only so signals match backtest behavior.td.market.priceis always the live ticker regardless of mode — usepricefor order placement,closefor 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
| Property | Type | Description |
|---|---|---|
rsi | number | Current RSI (14 period) |
rsiArray | number[] | Historical RSI values |
macd | object | MACD data (line, signal, histogram, trend) |
atr | number | Average True Range |
atrArray | number[] | Historical ATR values |
bbands | object | Bollinger Bands (upper, middle, lower, width, percentB) |
adx | object | ADX data (value, diPlus, diMinus, trend) |
stochastic | object | Stochastic (k, d, zone) |
mfi | number | Money Flow Index |
pivots | object | Pivot 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:
- Strategy emits a
close_on_candlesignal - The system calculates when the current candle closes (based on bot timeframe)
advancedExitis set on all open trades for this bot/symbol- 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 Type | Description | Example |
|---|---|---|
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 value | 30 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:
| Setting | Description | Default |
|---|---|---|
| Proximity Type | How to detect if price is "near" a level: atr, percentage, or both | both |
| ATR Multiplier | Price within ATR × multiplier counts as near (uses primary TF ATR) | 1.0 |
| % Threshold | Price within this % of a level counts as near | 0.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
| Level | Description | Use Case |
|---|---|---|
info | Informational | Strategy status updates |
warning | Warning | Unusual conditions, potential issues |
error | Error | Strategy errors, failed conditions |
trade_entry | Trade entry | Position opened notifications |
trade_exit | Trade exit | Position 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
| Constraint | Value |
|---|---|
| Max items per spin | 30 |
| Max label length | 30 characters |
| Max value length | 50 characters |
| Max group name length | 20 characters |
| Deduplication | Last 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
coloroption 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
| Constraint | Value |
|---|---|
| Max drawings per spin | 20 |
| Max title/text length | 30 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 anifblock — it will disappear on the next spin when the condition is false.