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.
Configuration
Settings
Code
Configuration
| Field | Description |
|---|---|
| Language | Python (default) or TypeScript. Python includes numpy, pandas, and requests. TypeScript includes fetch. |
| Timeout | Maximum execution time in seconds. Default: 30. Range: 1--300. |
| Code | Your function code. Click Use Template for a starter snippet or Expand for a full-screen Monaco editor. |
| Description | Optional 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
| Parameter | Description |
|---|---|
| inputs | Data 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_variables | Key-value pairs defined in this workflow's settings. Access with workflow_variables.get('KEY') (Python) or workflowVariables?.KEY (TypeScript). |
| global_variables | Account-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
| Language | Built-in libraries |
|---|---|
| Python | numpy, pandas, requests, re, json, math, datetime |
| TypeScript | fetch (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.
RestrictedPython blocks these patterns -- your code will fail if you use them:
- No sequence unpacking:
a, b = pairandfor k, v in dict.items()are blocked. Usea = pair[0]; b = pair[1]andfor item in dict.items(): k = item[0]; v = item[1]instead. - No spread operators:
*argsand**kwargsare not allowed. - No double quotes in f-strings:
f"{d["key"]}"fails. Use single quotes:f"{d['key']}". - Forbidden functions:
eval,exec,compile,subprocess, andos.systemare all blocked. - File writes are limited to
/tmp/only.
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}
for item in inputs.items() with index access instead of for k, v in inputs.items() -- this is required by the RestrictedPython sandbox.Example: Calculate Position Size
A common trading pattern -- fetch price and portfolio data, then calculate how much to buy.
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.
| Path | Description |
|---|---|
| {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. |
function with the actual edge label connecting this node to the next. If the edge is labeled sizing, downstream nodes reference {sizing.quantity}, {sizing.symbol}, etc.Next Steps
- Conditional Node -- Route your workflow based on Function output values.
- LLM Node -- Feed transformed data into an AI model for analysis.
- Exchange Order Node -- Execute trades using calculated values from your function.
- Price Data Node -- Fetch market data to process in your function.