---
title: User-Defined Functions
description: >-
  Learn how to create custom, reusable functions in kScript v2 using the func
  keyword for modular and maintainable code.
---

<div class="flex gap-3 mb-6">
  <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-purple-50 text-purple-600 text-sm font-medium">
    Advanced
  </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">
    10 min read
  </span>
</div>

## Overview

kScript v2 introduces user-defined functions, allowing you to encapsulate logic into reusable modules. Functions can accept both `var` and `timeseries` arguments, support both positional and named parameter calling (kwargs), and must follow specific scoping rules.

| Feature                | Description                             |
| ---------------------- | --------------------------------------- |
| `func`                 | Keyword used to define functions        |
| Infinite Possibilities | Create any custom logic you need        |
| Global Scope Required  | Functions must be declared at top level |

## Function Declaration

### Basic Syntax

```javascript title="Function Declaration Syntax" lines wrap
func functionName(parameter1, parameter2) {
  // Function body
  return result
}
```

### Kwargs Support

User-defined functions support both positional and named parameter calling conventions:

```javascript title="Function Call Formats" lines wrap
// Function definition
func calculate(base, multiplier, offset) {
  return base * multiplier + offset
}

// Positional calling
var result1 = calculate(10, 2, 5)  // Returns 25

// Named parameter calling (kwargs)
var result2 = calculate(base=10, multiplier=2, offset=5)  // Returns 25
var result3 = calculate(offset=5, base=10, multiplier=2)  // Returns 25
```

### Syntax Details

**Function Name:**
Must follow standard identifier rules (letters, numbers, underscore). Cannot start with a number.

**Parameters:**
Can accept `var` and `timeseries` arguments. Parameter types are inferred from usage. Support both positional and named parameter calling (kwargs).

**Return Statement:**
Functions must explicitly return a value. The return type is inferred from the returned expression.

## Function Examples

### Simple Calculation

Basic mathematical operations with error handling:

```javascript title="Safe Division Function" lines wrap
func safeDiv(a, b) {
  return b == 0 ? 0 : a / b
}

// Usage
var result = safeDiv(a=10, b=2)  // Returns 5
var safe = safeDiv(a=10, b=0)    // Returns 0
```

### Average of Two Values

```javascript lines wrap
func calculateAverage(a, b) {
  return (a + b) / 2
}

// Usage
var avg = calculateAverage(close[0], close[1])
```

### Custom Indicator Logic

```javascript lines wrap
func isGreenCandle(openPrice, closePrice) {
  return closePrice > openPrice
}

func isBullishEngulfing(prevOpen, prevClose, currOpen, currClose) {
  var prevWasRed = prevClose < prevOpen
  var currIsGreen = currClose > currOpen
  var engulfs = currOpen < prevClose && currClose > prevOpen
  return prevWasRed && currIsGreen && engulfs
}

// Usage
timeseries ohlcv = ohlcv(symbol=currentSymbol, exchange=currentExchange)
var bullish = isBullishEngulfing(
  prevOpen=ohlcv.open[1],
  prevClose=ohlcv.close[1],
  currOpen=ohlcv.open[0],
  currClose=ohlcv.close[0]
)
```

## Constraints and Rules

### No Timeseries Declarations Inside Functions

Functions cannot declare new `timeseries` inside their body. All timeseries must be declared in global scope.

```javascript lines wrap
// Invalid
func bad() {
  timeseries ts = ohlcv(...)  // Error!
}

// Valid - pass timeseries as parameter
func good(ts) {
  return ts.close[0]
}
```

### Global Scope Only

Functions must be declared in global scope, not inside loops, conditionals, or other functions.

```javascript lines wrap
// Invalid
if (condition) {
  func bad() {...}  // Error!
}

// Valid
func good() {...}

if (condition) {
  var result = good()  // OK to call
}
```

### Parameter Types

Functions can accept `var` and `timeseries` arguments. Types are inferred from how parameters are used.

```javascript lines wrap
// Valid - accepts both var and timeseries
func processData(varParam, tsParam) {
  var current = tsParam[0]  // Treats tsParam as timeseries
  return varParam + current
}
```

### Return Requirement

All functions must have an explicit return statement. The return type is inferred from the expression.

```javascript lines wrap
// Valid
func add(a, b) {
  return a + b  // Required
}

// Invalid - no return
func noReturn(a, b) {
  var sum = a + b  // Missing return!
}
```

## Best Practices

<table data-view="cards" data-layout="stack"><tbody>
<tr><td>Keep Functions Focused</td><td><p>Each function should do one thing well:</p><pre><code class="language-javascript">// Good - single responsibility
func calculateRSIColor(rsiValue) {
  return rsiValue > 70 ? 0 : (rsiValue &lt; 30 ? 1 : 2)
}

// Avoid - doing too much
func calculateAndPlotRSI(...) {
// Don't mix calculation with plotting
}</code></pre></td><td></td></tr>

<tr><td>Use Descriptive Names</td><td><p>Function names should clearly indicate what they do:</p><pre><code class="language-javascript">// Good
func isOverbought(rsiValue) { return rsiValue > 70 }
func calculatePercentChange(oldValue, newValue) {...}

// Avoid
func calc(a, b) {...}
func check(x) {...}</code></pre></td><td></td></tr>

<tr><td>Document with kwargs</td><td><p>Using named parameters makes function calls self-documenting:</p><pre><code class="language-javascript">// Self-documenting call
var signal = isBullishEngulfing(
  prevOpen=ohlcv.open[1],
  prevClose=ohlcv.close[1],
  currOpen=ohlcv.open[0],
  currClose=ohlcv.close[0]
)</code></pre></td><td></td></tr>
</tbody></table>
