---
title: Best Practices
description: >-
  Guidelines and recommendations for writing efficient, maintainable, and
  error-free kScript code.
---

<div class="flex gap-3 mb-6">
  <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-yellow-50 text-yellow-600 text-sm font-medium">
    Guidelines
  </span>
  <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-gray-100 text-gray-600 text-sm font-medium">
    8 min read
  </span>
</div>

## 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).

```javascript title="Static Variables" lines wrap
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).

```javascript title="Regular Variables" lines wrap
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.

```javascript title="Timeseries Usage" lines wrap
// ✓ 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.

```javascript title="Explicit Parameters" lines wrap
// ✓ 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.

```javascript title="Historical Data Access" lines wrap
// 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.

```javascript title="Descriptive Plots" lines wrap
// 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.

{% hint style="info" %}
**Plot Types**

- `plotLine()` for continuous lines
- `plotBar()` for histogram data
- `plotCandle()` for OHLC data
- `plotShape()` for discrete signals
{% endhint %}

## 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.

```javascript title="printTimeSeries for Timeseries Objects" lines wrap
// ❌ 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.

```javascript title="Null Checking" lines wrap
// 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.

```javascript title="Division by Zero" lines wrap
// 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.

```javascript title="Array Bounds" lines wrap
// 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.

```javascript title="Avoid Redundant Calculations" lines wrap
// ✓ 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.

```javascript title="Use Static Variables" lines wrap
// ✓ 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.

### Group Related Code

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

```javascript title="Group Related Code" lines wrap
// @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.

```javascript title="Use Comments Effectively" lines wrap
// 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.

```javascript title="Use Descriptive Labels and Descriptions" lines wrap
// ✓ 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"])
```
