Best Practices & Common Mistakes
Tips for writing reliable, performant CodeLab indicators.
Handle the First Bar
Many calculations need a warmup period. On bar 0, there's no previous data.
calculate() {
// Guard: need at least `period` bars for a meaningful value
if (this.bar < this.length) return;
// Or use isFirstBar for initialization
if (this.isFirstBar) {
this.setState('prevSignal', 0);
}
}TIP
Built-in functions (sma, ema, rsi, etc.) handle warmup internally. You only need guards for your own custom calculations.
Don't Use setState for Built-in Functions
Built-in functions manage their own state. Wrapping them in setState is redundant and can cause bugs.
// BAD - don't do this
this.setState('myEma', this.ema('close', 14));
const prev = this.getState('myEma');
// GOOD - the function tracks its own state
const emaVal = this.ema('close', 14);
plot('EMA', emaVal);
const prevEma = this.getPlot('EMA', 1); // previous bar's valueCross Detection Requires 4 Arguments
crossOver and crossUnder need both current AND previous values.
// BAD - only 2 args, won't work
if (this.crossOver(fast, slow)) { ... }
// GOOD - 4 args with previous bar values via getPlot
const fast = this.ema('close', 9);
const slow = this.ema('close', 21);
plot('Fast', fast);
plot('Slow', slow);
const prevFast = this.getPlot('Fast', 1);
const prevSlow = this.getPlot('Slow', 1);
if (this.crossOver(fast, slow, prevFast, prevSlow)) { ... }Keep Built-in Functions Outside Conditionals
Built-in functions that manage state (ema, rsi, macd, etc.) must be called every bar so their internal state stays consistent. Don't put them inside if/else blocks.
// BAD - ema is only called some bars, state gets out of sync
if (someCondition) {
const ema = this.ema('close', 14);
plot('EMA', ema);
}
// GOOD - always call, conditionally use
const ema = this.ema('close', 14);
if (someCondition) {
plot('EMA', ema);
}Plot Names Must Be Unique
Each plot(), plotHistogram(), and plotColor() call needs a unique name.
// BAD - duplicate name
plot('Value', smaValue);
plot('Value', emaValue); // Overwrites the first!
// GOOD - unique names
plot('SMA', smaValue);
plot('EMA', emaValue);Class Names Must Be PascalCase Alphanumeric
// BAD
export default class my-indicator extends Script { ... }
export default class My Indicator extends Script { ... }
// GOOD
export default class MyIndicator extends Script { ... }
export default class BollingerBands extends Script { ... }
export default class RSIMomentum extends Script { ... }Use Overlay Correctly
Indicators that should be at price scale (moving averages, bands, channels) need overlay: true. Oscillators (RSI, MACD, Stochastic) should NOT use overlay.
// Moving average - OVERLAY (same scale as price)
plot('SMA', smaVal, { color: color.blue, overlay: true });
// RSI - SUB-PANE (0-100 scale, not price scale)
plot('RSI', rsiVal, { color: color.purple });NaN / Undefined Safety
If you access data before it exists (e.g., this.get('close', 100) when only 50 bars are loaded), the value may be NaN. The SDK sanitizes plot values, but your intermediate calculations should handle this:
const prevClose = this.bar > 0 ? this.get('close', 1) : this.close;
const change = this.close - prevClose; // Safe on bar 0Performance Tips
- Avoid expensive loops in calculate() — it runs once per bar, so keep it lean
- Use built-in functions (
sma,highest, etc.) instead of manually looping over historical data - Don't log on every bar —
this.log()only outputs on the last bar by design - Limit state keys — each
setState()creates a data row. Use meaningful, consistent key names
Import Only What You Use
// BAD - importing everything
import { Script, Strategy, input, plot, plotHistogram, plotColor, hline, plotShape, plotArrow, plotArea, bgcolor, barcolor, color } from '@chartlabs/script-sdk';
// GOOD - import only what you need
import { Script, input, plot, plotColor, hline, color } from '@chartlabs/script-sdk';Debugging Tips
- Use
this.log()to print values on the last bar - Start simple — get basic logic working before adding complexity
- Test on multiple instruments — some instruments may have different data characteristics
- Check bar count — short charts may not have enough bars for long periods

