Repurpose Indicators
The chart’s built-in indicators can be reused to build your own custom composite indicators.
Overview
TradeX Chart provides a set of built-in technical indicators that can be combined and extended to create custom composite indicators. This allows you to:
- Combine multiple indicators into one
- Create custom trading signals
- Build indicator-based strategies
- Extend existing indicators with custom logic
Accessing Built-in Indicators
Get Indicator Class
// Get the RSI indicator class
const RSI = chart.getIndicatorClass('RSI')
// Get the MACD indicator class
const MACD = chart.getIndicatorClass('MACD')
// Get the EMA indicator class
const EMA = chart.getIndicatorClass('EMA')
Available Indicators
- Moving Averages: SMA, EMA, WMA, VWMA
- Oscillators: RSI, Stochastic, CCI, Williams %R
- Trend: MACD, ADX, Aroon, Parabolic SAR
- Volatility: Bollinger Bands, ATR, Keltner Channels
- Volume: OBV, MFI, Volume Profile
Creating Composite Indicators
Example 1: RSI with EMA Smoothing
class SmoothedRSI {
constructor(rsiPeriod = 14, emaPeriod = 9) {
this.rsiPeriod = rsiPeriod
this.emaPeriod = emaPeriod
// Get indicator classes
this.RSI = chart.getIndicatorClass('RSI')
this.EMA = chart.getIndicatorClass('EMA')
}
calculate(data) {
// Calculate RSI
const rsiValues = this.RSI.calculate(data, { period: this.rsiPeriod })
// Smooth RSI with EMA
const smoothedRSI = this.EMA.calculate(rsiValues, { period: this.emaPeriod })
return smoothedRSI
}
}
// Usage
const smoothedRSI = new SmoothedRSI(14, 9)
const result = smoothedRSI.calculate(chartData)
Example 2: MACD with RSI Filter
class MACDRSICombo {
constructor() {
this.MACD = chart.getIndicatorClass('MACD')
this.RSI = chart.getIndicatorClass('RSI')
}
calculate(data) {
// Calculate MACD
const macd = this.MACD.calculate(data, {
fastPeriod: 12,
slowPeriod: 26,
signalPeriod: 9
})
// Calculate RSI
const rsi = this.RSI.calculate(data, { period: 14 })
// Generate signals
const signals = []
for (let i = 0; i < data.length; i++) {
const macdLine = macd.macd[i]
const signalLine = macd.signal[i]
const rsiValue = rsi[i]
let signal = 'neutral'
// Buy signal: MACD crosses above signal AND RSI < 30
if (macdLine > signalLine && rsiValue < 30) {
signal = 'buy'
}
// Sell signal: MACD crosses below signal AND RSI > 70
else if (macdLine < signalLine && rsiValue > 70) {
signal = 'sell'
}
signals.push(signal)
}
return { macd, rsi, signals }
}
}
// Usage
const combo = new MACDRSICombo()
const result = combo.calculate(chartData)
Example 3: Multi-Timeframe RSI
class MultiTimeframeRSI {
constructor(periods = [14, 28, 56]) {
this.periods = periods
this.RSI = chart.getIndicatorClass('RSI')
}
calculate(data) {
const results = {}
this.periods.forEach(period => {
results[`rsi${period}`] = this.RSI.calculate(data, { period })
})
// Calculate average RSI
const avgRSI = []
for (let i = 0; i < data.length; i++) {
let sum = 0
let count = 0
this.periods.forEach(period => {
const value = results[`rsi${period}`][i]
if (value !== null && value !== undefined) {
sum += value
count++
}
})
avgRSI.push(count > 0 ? sum / count : null)
}
results.average = avgRSI
return results
}
}
// Usage
const mtfRSI = new MultiTimeframeRSI([14, 28, 56])
const result = mtfRSI.calculate(chartData)
Extending Indicators
Add Custom Logic
class CustomBollingerBands {
constructor() {
this.BB = chart.getIndicatorClass('BollingerBands')
}
calculate(data, params = {}) {
const period = params.period || 20
const stdDev = params.stdDev || 2
// Calculate standard Bollinger Bands
const bb = this.BB.calculate(data, { period, stdDev })
// Add custom squeeze detection
const squeeze = []
for (let i = 0; i < data.length; i++) {
const bandwidth = (bb.upper[i] - bb.lower[i]) / bb.middle[i]
// Detect squeeze (narrow bands)
squeeze.push(bandwidth < 0.05)
}
return {
...bb,
squeeze
}
}
}
// Usage
const customBB = new CustomBollingerBands()
const result = customBB.calculate(chartData, { period: 20, stdDev: 2 })
Override Calculation
class WeightedRSI {
constructor() {
this.RSI = chart.getIndicatorClass('RSI')
}
calculate(data, params = {}) {
const period = params.period || 14
const weights = params.weights || [1, 1, 1, 2, 2, 3] // Recent candles weighted more
// Get price changes
const changes = []
for (let i = 1; i < data.length; i++) {
changes.push(data[i][4] - data[i - 1][4]) // close - prev close
}
// Calculate weighted gains and losses
const rsi = []
for (let i = period; i < changes.length; i++) {
let weightedGains = 0
let weightedLosses = 0
let totalWeight = 0
for (let j = 0; j < period; j++) {
const change = changes[i - period + j]
const weight = weights[j % weights.length]
if (change > 0) {
weightedGains += change * weight
} else {
weightedLosses += Math.abs(change) * weight
}
totalWeight += weight
}
const avgGain = weightedGains / totalWeight
const avgLoss = weightedLosses / totalWeight
const rs = avgLoss === 0 ? 100 : avgGain / avgLoss
const rsiValue = 100 - (100 / (1 + rs))
rsi.push(rsiValue)
}
return rsi
}
}
// Usage
const weightedRSI = new WeightedRSI()
const result = weightedRSI.calculate(chartData, {
period: 14,
weights: [1, 1, 1, 2, 2, 3]
})
Registering Custom Indicators
Register with Chart
// Define custom indicator
class MyCustomIndicator {
static id = 'MY_CUSTOM'
static name = 'My Custom Indicator'
constructor(params) {
this.params = params
}
calculate(data) {
// Your calculation logic
return []
}
render(ctx, data) {
// Your rendering logic
}
}
// Register indicator
chart.registerIndicator(MyCustomIndicator)
// Use indicator
const indicatorId = chart.addIndicator('MY_CUSTOM', {
// params
})
Full Example: Composite Indicator
class TrendStrength {
static id = 'TREND_STRENGTH'
static name = 'Trend Strength'
constructor(params = {}) {
this.adxPeriod = params.adxPeriod || 14
this.macdFast = params.macdFast || 12
this.macdSlow = params.macdSlow || 26
// Get indicator classes
this.ADX = chart.getIndicatorClass('ADX')
this.MACD = chart.getIndicatorClass('MACD')
}
calculate(data) {
// Calculate ADX for trend strength
const adx = this.ADX.calculate(data, { period: this.adxPeriod })
// Calculate MACD for trend direction
const macd = this.MACD.calculate(data, {
fastPeriod: this.macdFast,
slowPeriod: this.macdSlow,
signalPeriod: 9
})
// Combine into trend strength score
const trendStrength = []
for (let i = 0; i < data.length; i++) {
const adxValue = adx[i]
const macdValue = macd.macd[i]
const signalValue = macd.signal[i]
if (adxValue === null || macdValue === null) {
trendStrength.push(null)
continue
}
// Score: 0-100
// ADX > 25 = strong trend
// MACD above signal = uptrend, below = downtrend
let score = (adxValue / 50) * 100 // Normalize ADX to 0-100
if (macdValue < signalValue) {
score = -score // Negative for downtrend
}
trendStrength.push(Math.max(-100, Math.min(100, score)))
}
return trendStrength
}
render(ctx, data, values) {
// Render as histogram
const width = ctx.canvas.width
const height = ctx.canvas.height
const barWidth = width / data.length
values.forEach((value, i) => {
if (value === null) return
const x = i * barWidth
const barHeight = (Math.abs(value) / 100) * (height / 2)
const y = value > 0 ? height / 2 - barHeight : height / 2
ctx.fillStyle = value > 0 ? '#26a69a' : '#ef5350'
ctx.fillRect(x, y, barWidth, barHeight)
})
}
}
// Register and use
chart.registerIndicator(TrendStrength)
const id = chart.addIndicator('TREND_STRENGTH', {
adxPeriod: 14,
macdFast: 12,
macdSlow: 26
})
Best Practices
1. Cache Calculations
class CachedIndicator {
constructor() {
this.cache = new Map()
}
calculate(data, params) {
const key = this.getCacheKey(data, params)
if (this.cache.has(key)) {
return this.cache.get(key)
}
const result = this.doCalculation(data, params)
this.cache.set(key, result)
return result
}
getCacheKey(data, params) {
return `${data.length}_${JSON.stringify(params)}`
}
}
2. Handle Null Values
function safeCalculate(indicator, data, params) {
try {
const result = indicator.calculate(data, params)
// Filter out null/undefined values
return result.map(v => {
if (v === null || v === undefined || isNaN(v)) {
return null
}
return v
})
} catch (error) {
console.error('Indicator calculation failed:', error)
return new Array(data.length).fill(null)
}
}
3. Validate Parameters
class ValidatedIndicator {
constructor(params) {
this.validateParams(params)
this.params = params
}
validateParams(params) {
if (params.period && params.period < 1) {
throw new Error('Period must be >= 1')
}
if (params.stdDev && params.stdDev < 0) {
throw new Error('Standard deviation must be >= 0')
}
}
}
Related Documentation
- Custom Indicators - Creating indicators from scratch
- API Reference - Indicator API
- Examples - Indicator examples