FAQ

Best Practices

Guidelines and recommendations for writing efficient, maintainable, and error-free kScript code.

Guidelines 8 min read

Code Structure

Fundamental patterns for organizing your kScript code.

Variable Declarations

Use the appropriate variable type for your data.

Static variables for constants:

Use for constants and values that never change across bars (e.g., periods, thresholds, multipliers).

Static Variables
static period = 14
static multiplier = 2.0

Regular variables for dynamic values:

Use for dynamic values that change per bar but don't need historical access (e.g., signals, calculations).

Regular Variables
var signal = 0
var trend_direction = "up"

Timeseries for historical data:

Use only when absolutely necessary as they consume more memory. Declare as timeseries when you need to:

  • Store data source outputs: timeseries price_data = ohlcv(...)
  • Access past values at previous candle indices: sma_values[5]

While indicator functions like sma(), ema(), and rsi() require a timeseries as input, their return values should be stored as var unless you specifically need to access historical values (e.g., sma_values[5]). This saves memory and improves performance.

Timeseries Usage
// ✓ Good - Data source stored as timeseries
timeseries price_data = ohlcv(currentSymbol, currentExchange)

// ✓ Good - Indicator return value stored as var (unless you need history)
var sma_value = sma(price_data.close, period)
var rsi_value = rsi(price_data.close, 14)

// ✓ Only use timeseries if you need to access historical values
timeseries sma_series = sma(price_data.close, period)
var previous_sma = sma_series[5]  // Accessing 5 bars ago

Technical Indicators

Guidelines for working with technical indicators effectively.

Explicit Parameters

Always specify parameters explicitly for clarity. This makes your code more readable and maintainable.

Explicit Parameters
// ✓ Good - Parameters are explicit and clear
timeseries sma20 = sma(price_data.close, period=20)
timeseries rsi14 = rsi(price_data.close, period=14)

// ❌ Avoid - Relying on defaults makes intent unclear
timeseries sma_default = sma(price_data.close)

Historical Data Access

Use bracket notation to access historical values safely.

Historical Data Access
// Access previous bar values
var prev_close = price_data.close[1]
var close_5_bars_ago = price_data.close[5]

// Use in calculations
var price_change = price_data.close - price_data.close[1]

Plotting & Visualization

Create clear and informative visualizations.

Descriptive Plots

Use meaningful colors and widths to make your plots easy to distinguish.

Descriptive Plots
// Use meaningful colors and widths
plotLine(value=sma20, width=2, colors=["blue"], label=["SMA 20"], desc=["20-period Simple Moving Average"])
plotLine(value=sma50, width=2, colors=["red"], label=["SMA 50"], desc=["50-period Simple Moving Average"])

// Conditional plotting with ternary operators
var signal_value = trend_up ? price_data.high : na
plotShape(value=signal_value, shape="circle", width=2, colors=["green"], label=["Signal"], desc=["Trading Signal"])

Choose Appropriate Plot Types

Select the right plot function for your data type.

Debugging

Best practices for debugging and inspecting your kScript code.

Use printTimeSeries for Timeseries Objects

Always use printTimeSeries() for timeseries objects instead of print(). The print() function only outputs the first value (e.g., only "open" for OHLCV data), while printTimeSeries() displays the complete timeseries structure.

printTimeSeries for Timeseries Objects
// ❌ Avoid - print() only shows first value (open)
timeseries price_data = ohlcv(currentSymbol, currentExchange)
print("Price data:", price_data)  // Only prints open values

// ✓ Good - printTimeSeries() shows complete OHLCV structure
printTimeSeries(price_data)  // Prints [timeseriesInMs, open, high, low, close, volume]

// You can also specify which field to print
printTimeSeries(price_data, priceIndex=4)  // Print [timeseriesInMs, close] prices

Error Prevention

Protect your code against common runtime errors.

Null Checking

Always check for invalid data before performing calculations.

Null Checking
// Check for invalid data before calculations
var calculation = !isnan(price_data.close) ? price_data.close * multiplier : na

// Alternative using isnum()
if (isnum(price_data.close) && price_data.close > 0) {
    var result = price_data.close * 2
}

Division by Zero

Protect against division by zero errors.

Division by Zero
// Protect against division by zero
var ratio = denominator != 0 ? numerator / denominator : na

// With additional safety checks
var safe_ratio = isnum(denominator) && denominator != 0 ? numerator / denominator : na

Array Bounds

Be careful when accessing historical data to avoid index errors.

Array Bounds
// Check bar index before accessing historical data
var safe_previous = barIndex > 0 ? price_data.close[1] : price_data.close

// For multiple bars back
var safe_value = barIndex >= 5 ? price_data.close[5] : price_data.close

Performance Optimization

Write efficient code that executes quickly.

Avoid Redundant Calculations

Store frequently used calculations in variables instead of recalculating.

Avoid Redundant Calculations
// ✓ Good - Calculate once and reuse
static lookback_period = 20
timeseries typical_price = (price_data.high + price_data.low + price_data.close) / 3
timeseries sma_typical = sma(typical_price, lookback_period)

// ❌ Avoid - Recalculating the same value multiple times
var value1 = sma((price_data.high + price_data.low + price_data.close) / 3, 20)
var value2 = sma((price_data.high + price_data.low + price_data.close) / 3, 20)

Use Static Variables

Declare constants as static to avoid unnecessary recalculation on every bar.

Use Static Variables
// ✓ Good - Declare constants as static
static fibonacci_ratio = 0.618
static golden_ratio = 1.618
static pi = 3.14159

// These values don't change, so no need to recalculate each bar

Code Organization

Structure your code for maximum readability and maintainability.

Organize your code into logical sections: inputs, data sources, calculations, signals, and plotting.

Group Related Code
// @version=2
define("My Strategy", "overlay", overlay=true)

// Input parameters
var period = input("Period", "number", defaultValue=14)
var threshold = input("Threshold", "number", defaultValue=0.5)

// Data sources
timeseries price_data = ohlcv(currentSymbol, currentExchange)

// Calculations
timeseries ma_fast = sma(price_data.close, period)
timeseries ma_slow = sma(price_data.close, period * 2)

// Signals
var buy_signal = crossover(ma_fast, ma_slow)
var sell_signal = crossunder(ma_fast, ma_slow)

// Plotting
plotLine(value=ma_fast, width=2, colors=["blue"], label=["Fast MA"], desc=["Fast Moving Average"])
plotLine(value=ma_slow, width=2, colors=["red"], label=["Slow MA"], desc=["Slow Moving Average"])
plotShape(value=buy_signal ? price_data.low : na, shape="circle", width=2, colors=["green"], label=["Buy Signal"], desc=["Buy Signal Marker"])

Use Comments Effectively

Write clear comments that explain the "why" behind your code logic.

Use Comments Effectively
// Calculate RSI divergence
var rsi_current = rsi(price_data.close, 14)
var rsi_previous = rsi_current[1]

// Look for bullish divergence:
// Price makes lower low but RSI makes higher low
var price_lower_low = price_data.low < price_data.low[1]
var rsi_higher_low = rsi_current > rsi_previous
var bullish_divergence = price_lower_low && rsi_higher_low

Use Descriptive Labels and Descriptions

Always include label and desc parameters in your plotting functions. These parameters improve autocomplete functionality, make scripts more readable, and help with script organization in the editor.

Use Descriptive Labels and Descriptions
// ✓ Always use label and desc parameters for better organization
timeseries price_data = ohlcv(currentSymbol, currentExchange)
var sma20 = sma(price_data.close, 20)
var sma50 = sma(price_data.close, 50)
var rsi = rsi(price_data.close, 14)

// Good: Descriptive labels help with autocomplete and script organization
plotLine(value=sma20, width=2, colors=["blue"], label=["SMA 20"], desc=["20-period Simple Moving Average"])
plotLine(value=sma50, width=2, colors=["red"], label=["SMA 50"], desc=["50-period Simple Moving Average"])
plotLine(value=rsi, width=2, colors=["purple"], label=["RSI"], desc=["14-period Relative Strength Index"])

// ❌ Avoid: Missing labels make scripts harder to understand
plotLine(value=sma20, width=2, colors=["blue"])
plotLine(value=sma50, width=2, colors=["red"])
plotLine(value=rsi, width=2, colors=["purple"])