docs

Function Node

The Function node executes custom Python or TypeScript code within your workflow. Use it to transform data, calculate position sizes, parse LLM responses, call external APIs, or implement any logic that the built-in nodes don't cover.

Calculate Position Size
Python • 12 lines

Configuration

Settings
Code

Configuration

FieldDescription
LanguagePython (default) or TypeScript. Python includes numpy, pandas, and requests. TypeScript includes fetch.
TimeoutMaximum execution time in seconds. Default: 30. Range: 1--300.
CodeYour function code. Click Use Template for a starter snippet or Expand for a full-screen Monaco editor.
DescriptionOptional note describing what this function does (e.g., "Calculate RSI-based position size").

How It Works

Your code runs inside a Docker container. You define a main function that receives data from upstream nodes and returns any JSON-serializable value.

Python Signature

def main(inputs, workflow_variables=None, global_variables=None):
    # Your logic here
    return {"result": value}

TypeScript Signature

function main(
    inputs: any,
    workflowVariables?: Record<string, string>,
    globalVariables?: Record<string, string>
): any {
    // Your logic here
    return { result: value };
}

Parameters

ParameterDescription
inputsData from connected upstream nodes. Single edge: the raw output of the previous node. Multiple edges: a dictionary keyed by edge label (e.g., inputs['price_data'], inputs['analysis']).
workflow_variablesKey-value pairs defined in this workflow's settings. Access with workflow_variables.get('KEY') (Python) or workflowVariables?.KEY (TypeScript).
global_variablesAccount-level variables shared across all your workflows. Access with global_variables.get('KEY') (Python) or globalVariables?.KEY (TypeScript).

Return Value

Return any JSON-serializable structure -- objects, arrays, strings, numbers, booleans, or nested combinations. Whatever you return becomes the node's output and flows to downstream nodes.


Available Libraries

LanguageBuilt-in libraries
Pythonnumpy, pandas, requests, re, json, math, datetime
TypeScriptfetch (global)

Both languages have full HTTP access -- you can call any external API from your function code.


Code Editor

The properties panel includes a Monaco code editor with syntax highlighting, autocompletion, and line numbers. Two buttons sit above the editor:

  • Use Template -- inserts a starter function with the correct signature, parameter docs, and a basic return statement.
  • Expand -- opens a full-screen editor dialog with minimap and additional editing space for longer functions.

Use print() (Python) or console.log() (TypeScript) for debugging. Output appears in the execution logs under the node's run details.


Python Sandbox Restrictions

Python code runs in a RestrictedPython sandbox. Several standard Python patterns are blocked at runtime and will raise a NameError if used.

TypeScript does not have these restrictions.


Handling Inputs from Multiple Edges

When a single edge connects to your Function node, inputs is that node's raw output:

def main(inputs, workflow_variables=None, global_variables=None):
    # Single edge: inputs is the previous node's output directly
    price = inputs['data']['prices'][0]['current']
    return {"price": price}

When multiple edges connect, inputs becomes a dictionary keyed by each edge's label:

def main(inputs, workflow_variables=None, global_variables=None):
    # Multiple edges: inputs = {"price_data": {...}, "portfolio": {...}}
    price = inputs['price_data']['data']['prices'][0]['current']
    balance = inputs['portfolio']['cashBalance']
    return {"price": price, "balance": balance}

Example: Parse LLM Output

Extract a structured trading signal from an LLM's free-text response.

def main(inputs, workflow_variables=None, global_variables=None):
    import re

    # Get LLM response text
    llm_text = inputs.get('output', '')

    action = 'HOLD'
    confidence = 0.0

    if 'BUY' in llm_text.upper():
        action = 'BUY'
    elif 'SELL' in llm_text.upper():
        action = 'SELL'

    # Extract confidence percentage
    match = re.search(r'(\d+)%', llm_text)
    if match:
        confidence = int(match.group(1)) / 100

    return {'action': action, 'confidence': confidence, 'raw': llm_text}

Downstream, a Conditional node can check {function.action} equals BUY and {function.confidence} is greater than 0.7 to decide whether to place a trade.


Example: Aggregate Multi-Model Outputs

When multiple LLM nodes connect to one Function node in parallel branches, you can tally their recommendations:

def main(inputs, workflow_variables=None, global_variables=None):
    # inputs = {"claude": {...}, "gpt": {...}, "gemini": {...}}
    results = {}
    for item in inputs.items():
        model_name = item[0]
        data = item[1]
        text = data.get('output', '').upper()
        if 'BUY' in text:
            results[model_name] = 'BUY'
        elif 'SELL' in text:
            results[model_name] = 'SELL'
        else:
            results[model_name] = 'HOLD'

    buy_count = sum(1 for v in results.values() if v == 'BUY')
    consensus = 'BUY' if buy_count > len(results) / 2 else 'HOLD'

    return {'consensus': consensus, 'votes': results, 'buy_count': buy_count}

Example: Calculate Position Size

A common trading pattern -- fetch price and portfolio data, then calculate how much to buy.

BTC Price
CoinGecko
My Portfolio
Alpaca
Position Size
Python
Buy BTC
Alpaca
def main(inputs, workflow_variables=None, global_variables=None):
    price = inputs['price_data']['data']['prices'][0]['current']
    cash = inputs['portfolio']['cashBalance']

    # Risk 2% of portfolio per trade
    risk_pct = 0.02
    if workflow_variables:
        risk_pct = float(workflow_variables.get('RISK_PCT', '0.02'))

    position_usd = cash * risk_pct
    quantity = position_usd / price

    print(f"Price: ${price}, Cash: ${cash}, Qty: {quantity}")

    return {
        'symbol': 'BTC/USD',
        'side': 'buy',
        'quantity': round(quantity, 6),
        'position_usd': round(position_usd, 2),
    }

The Exchange Order node downstream can use {sizing.quantity} as the dynamic amount via the f(x) toggle.


Example: Call an External API (TypeScript)

async function main(
    inputs: any,
    workflowVariables?: Record<string, string>,
    globalVariables?: Record<string, string>
) {
    const apiKey = globalVariables?.NEWS_API_KEY || '';
    const symbol = inputs?.symbol || 'BTC';

    const response = await fetch(
        `https://api.example.com/news?q=${symbol}`,
        { headers: { 'Authorization': `Bearer ${apiKey}` } }
    );
    const data = await response.json();

    return {
        headlines: data.articles.map((a: any) => a.title),
        count: data.articles.length,
    };
}

Output

The Function node's output is entirely defined by what your main function returns. There is no fixed schema -- you control the shape of the data.

PathDescription
{function.your_key}Any key in the returned object. Replace function with your edge label.
{function.nested.field}Nested fields are accessible via dot notation.
{function}The entire return value as a single object.

Next Steps