Skip to content

Worker API Reference

Complete reference for the ctx object available in worker code.

Code Structure

python
def setup(ctx):
    """Called once when the worker starts."""
    pass

def tick(ctx):
    """Called every tick_interval seconds. Required."""
    pass

def on_error(ctx, error):
    """Optional. Called when tick() raises an exception."""
    pass

ctx.state -- Persistent Storage

Redis-backed key-value store. Data persists across restarts and redeployments. Values are JSON-serialized.

Methods

MethodSignatureDescription
getget(key: str, default=None) -> AnyGet value by key. Returns default if not found.
setset(key: str, value: Any) -> NoneSet a value. Must be JSON-serializable.
deletedelete(key: str) -> NoneDelete a key.
keyskeys() -> list[str]List all state keys for this worker.
get_allget_all() -> dictGet all key-value pairs.

Example

python
def setup(ctx):
    ctx.state.set("prices", [])
    ctx.state.set("config", {"threshold": 5.0, "symbol": "BTCUSDT"})

def tick(ctx):
    config = ctx.state.get("config", {})
    prices = ctx.state.get("prices", [])

    # Append new price
    prices.append({"time": "2024-01-01", "price": 42000})
    ctx.state.set("prices", prices[-100:])  # keep last 100

    # List all keys
    all_keys = ctx.state.keys()  # ["prices", "config"]

ctx.log -- Logging

Batched logging system. Logs are flushed automatically after each tick. Visible in the worker's Log tab.

Methods

MethodSignatureDescription
infoinfo(msg: str)Info level message
warnwarn(msg: str)Warning level message
errorerror(msg: str)Error level message
debugdebug(msg: str)Debug level message
flushflush()Force flush logs (usually automatic)

Example

python
def tick(ctx):
    ctx.log.info("Starting tick")
    ctx.log.debug(f"State keys: {ctx.state.keys()}")

    try:
        result = do_something()
        ctx.log.info(f"Success: {result}")
    except Exception as e:
        ctx.log.error(f"Failed: {e}")
        ctx.log.warn("Will retry next tick")

TIP

Logs are batched (up to 20 entries) and sent in a single HTTP call for performance. Messages are truncated at 4000 characters.


ctx.monitor -- Real-time Dashboard

Send structured widget data to connected Monitor block(s). Use this to build live dashboards.

Requires: Monitor block connected via edge.

Methods

MethodSignatureDescription
renderrender(widgets: list[dict])Replace the entire display with new widgets
patchpatch(updates: list[dict])Update specific widgets by id
loglog(line: str, level: str = "info")Append a line to the monitor log
clearclear()Clear the display

Widget Builders

Use these static methods to build widget dicts:

BuilderSignatureDescription
metricmetric(label, value, color="blue", icon=None, id=None, subtitle=None)Metric card
statusstatus(label, state, detail="", id=None)Status indicator. state: "ok", "warning", "error"
tabletable(headers: list, rows: list[list], id=None)Data table
texttext(content, style="normal", id=None)Text block. style: "normal" or "monospace"
progressprogress(label, value: float, color="gold", id=None)Progress bar (0.0 to 1.0)
list_widgetlist_widget(items: list, ordered=False, id=None)Bullet or numbered list
log_widgetlog_widget(lines: list, max_lines=50, id=None)Scrolling log widget
separatorseparator()Visual divider line

Colors

Available colors for metric and progress: "blue", "green", "red", "gold", "orange", "purple", "gray".

Example

python
def tick(ctx):
    price = 42150.50
    change = +2.35

    ctx.monitor.render([
        ctx.monitor.metric("BTC Price", f"${price:,.2f}",
                          color="green" if change >= 0 else "red",
                          id="btc_price"),
        ctx.monitor.metric("24h Change", f"{change:+.2f}%",
                          color="green" if change >= 0 else "red"),
        ctx.monitor.separator(),
        ctx.monitor.status("Exchange", "ok", "Bybit connected"),
        ctx.monitor.status("Bot", "warning", "Waiting for signal"),
        ctx.monitor.separator(),
        ctx.monitor.table(
            headers=["Symbol", "Side", "Size", "PnL"],
            rows=[
                ["BTCUSDT", "Long", "0.5", "+$120"],
                ["ETHUSDT", "Short", "2.0", "-$45"],
            ]
        ),
        ctx.monitor.progress("Daily Target", 0.65, color="gold"),
    ])

Partial Updates with patch()

Use patch() to update specific widgets without re-rendering everything:

python
# First render with IDs
ctx.monitor.render([
    ctx.monitor.metric("Price", "$42,000", id="price"),
    ctx.monitor.status("Bot", "ok", "Running", id="bot_status"),
])

# Later, update only the price
ctx.monitor.patch([
    {"id": "price", "value": "$42,150"},
])

ctx.http -- HTTP Requests

Make HTTP requests to external APIs. All requests are proxied through the backend for security.

Methods

MethodSignatureReturns
getget(url: str, headers: dict = None) -> dict{"status": int, "body": str, "url": str, "content_type": str}
postpost(url: str, data: dict = None, headers: dict = None) -> dict{"status": int, "body": str, "url": str}

Example

python
def tick(ctx):
    # Fetch JSON API
    resp = ctx.http.get("https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd")
    body = resp.get("body", {})

    # body might be a string — parse it
    if isinstance(body, str):
        import json
        body = json.loads(body)

    price = body.get("bitcoin", {}).get("usd", 0)
    ctx.log.info(f"BTC price: ${price}")

    # POST request with custom headers
    resp = ctx.http.post(
        "https://hooks.slack.com/services/xxx",
        data={"text": f"BTC is ${price}"},
        headers={"Content-Type": "application/json"}
    )

ctx.bybit -- Bybit Exchange

Trade on Bybit. Supports spot and derivatives.

Requires: Bybit block connected via edge, with API keys configured.

Methods

MethodSignatureDescription
get_tickersget_tickers(symbol: str = "BTCUSDT") -> dictMarket ticker data (last price, bid, ask, volume)
get_balanceget_balance(coin: str = None) -> dictAccount balance
get_positionsget_positions(symbol: str = None) -> dictOpen positions
get_ordersget_orders(symbol: str = None) -> dictActive orders
place_orderplace_order(symbol, side, qty, order_type="Market", price=None) -> dictPlace an order
cancel_ordercancel_order(symbol: str, order_id: str) -> dictCancel an order

Parameters

place_order:

  • symbol: Trading pair, e.g. "BTCUSDT"
  • side: "Buy" or "Sell"
  • qty: Order quantity as string, e.g. "0.001"
  • order_type: "Market" or "Limit"
  • price: Required for Limit orders, string e.g. "42000"

Example

python
def tick(ctx):
    # Check positions
    positions = ctx.bybit.get_positions("BTCUSDT")
    ctx.log.info(f"Positions: {positions}")

    # Get current price
    ticker = ctx.bybit.get_tickers("BTCUSDT")
    last_price = ticker.get("last_price", "0")

    # Place a market buy
    result = ctx.bybit.place_order(
        symbol="BTCUSDT",
        side="Buy",
        qty="0.001",
        order_type="Market"
    )
    ctx.log.info(f"Order result: {result}")

Error Handling

If no Bybit block is connected, calling any method raises RuntimeError:

RuntimeError: No trading block connected. Connect a Bybit block to this worker.

ctx.ig -- IG Markets

Trade CFDs, forex, indices, and commodities on IG Markets.

Requires: IG Markets block connected via edge, with API keys configured.

Methods

MethodSignatureDescription
get_accountsget_accounts() -> dictAccount info (balance, P&L, margin)
get_positionsget_positions() -> dictAll open positions
get_ordersget_orders() -> dictAll working orders
search_marketssearch_markets(search_term: str) -> dictSearch instruments by name
get_market_infoget_market_info(epic: str) -> dictDetailed info for an instrument
open_positionopen_position(epic, direction, size, ...) -> dictOpen a new position
close_positionclose_position(deal_id, direction, size, ...) -> dictClose a position
update_positionupdate_position(deal_id, stop_level=None, limit_level=None) -> dictUpdate stop/limit
confirm_dealconfirm_deal(deal_reference: str) -> dictGet deal confirmation

open_position parameters

ParameterTypeDefaultDescription
epicstrrequiredInstrument identifier, e.g. "CS.D.EURUSD.TODAY.IP"
directionstrrequired"BUY" or "SELL"
sizefloatrequiredPosition size
order_typestr"MARKET""MARKET" or "LIMIT"
currency_codestr"USD"Account currency
expirystr"DFB"Expiry type
force_openboolTrueForce open even if opposing position exists
guaranteed_stopboolFalseUse guaranteed stop loss
stop_distancefloatNoneStop loss distance in points
limit_distancefloatNoneTake profit distance in points
stop_levelfloatNoneAbsolute stop loss level
limit_levelfloatNoneAbsolute take profit level

Example

python
def tick(ctx):
    # Check account
    accounts = ctx.ig.get_accounts()
    ctx.log.info(f"Account balance: {accounts}")

    # Search for an instrument
    results = ctx.ig.search_markets("Gold")

    # Get positions
    positions = ctx.ig.get_positions()
    for pos in positions.get("positions", []):
        deal_id = pos["position"]["dealId"]
        pnl = pos["position"].get("profit", 0)
        ctx.log.info(f"Position {deal_id}: PnL={pnl}")

    # Open a position with stop loss
    result = ctx.ig.open_position(
        epic="CS.D.EURUSD.TODAY.IP",
        direction="BUY",
        size=1.0,
        stop_distance=50.0,
        limit_distance=100.0
    )
    ctx.log.info(f"Opened: {result}")

ctx.telegram -- Telegram Messaging

Send and receive messages via Telegram bot.

Requires: Telegram block connected via edge, with bot token and chat ID configured.

Methods

MethodSignatureDescription
sendsend(text: str, parse_mode: str = "HTML") -> dictSend a message
get_messagesget_messages(limit: int = 10) -> dictGet recent messages
get_chat_infoget_chat_info() -> dictGet chat details (title, type)

parse_mode

  • "HTML" -- supports <b>bold</b>, <i>italic</i>, <code>code</code>, <pre>block</pre>, <a href="url">link</a>
  • "Markdown" -- supports *bold*, _italic_, `code`

Example

python
def tick(ctx):
    price = 42000
    change = +2.5

    # Send formatted alert
    ctx.telegram.send(
        f"<b>BTC Update</b>\n"
        f"Price: <code>${price:,}</code>\n"
        f"Change: {change:+.1f}%",
        parse_mode="HTML"
    )

    # Read recent messages
    messages = ctx.telegram.get_messages(limit=5)
    for msg in messages.get("messages", []):
        text = msg.get("text", "")
        ctx.log.info(f"Received: {text}")

ctx.files -- File Storage

Read and write files in a sandboxed file system (100 MB limit per workspace).

Requires: File Explorer block connected via edge.

Methods

MethodSignatureDescription
listlist(path: str = "/") -> dictList files and dirs. Returns {"files": [...], "dirs": [...]}
readread(path: str) -> dictRead file (max 50 KB). Returns {"content": "..."}
writewrite(path: str, content: str) -> dictWrite/create file. Returns {"ok": true, "path": "..."}
deletedelete(path: str) -> dictDelete file or dir. Returns {"ok": true}

Example

python
def tick(ctx):
    import json
    import datetime

    # Save data to file
    data = {"price": 42000, "time": str(datetime.datetime.now())}
    ctx.files.write("/logs/latest.json", json.dumps(data, indent=2))

    # Read it back
    result = ctx.files.read("/logs/latest.json")
    content = result.get("content", "")
    ctx.log.info(f"Saved: {content}")

    # List directory
    listing = ctx.files.list("/logs/")
    ctx.log.info(f"Files: {listing.get('files', [])}")

ctx.llm -- LLM Queries

Send prompts to connected AI models (Claude, Grok, OpenAI) and get text responses.

Requires: LLM Agent block (Claude Agent, Grok Agent, or LLM Agent) connected via edge.

Methods

MethodSignatureDescription
askask(message, system="", model="", max_tokens=2048, temperature=0.7) -> strSend a prompt, get text response
ask_with_toolsask_with_tools(message, system="", model="", max_tokens=4096) -> strAsk with tool access (trading, telegram, etc.)

Parameters

ParameterTypeDefaultDescription
messagestrrequiredYour prompt / question
systemstr""System prompt (instructions for the AI)
modelstr""Model override (leave empty for block default)
max_tokensint2048 / 4096Maximum response length
temperaturefloat0.7Creativity (0.0 = deterministic, 1.0 = creative)

Example

python
def tick(ctx):
    # Simple question
    answer = ctx.llm.ask(
        "Summarize the current BTC market sentiment in 2 sentences.",
        system="You are a cryptocurrency analyst.",
        temperature=0.3
    )
    ctx.log.info(f"AI says: {answer}")

    # Use with trading data
    positions = ctx.bybit.get_positions("BTCUSDT")
    analysis = ctx.llm.ask(
        f"Analyze these positions and suggest actions: {positions}",
        system="You are a quantitative trading advisor. Be concise."
    )

    # Send analysis to Telegram
    ctx.telegram.send(f"<b>AI Analysis:</b>\n{analysis}", parse_mode="HTML")

ctx.connected_blocks -- Connection Info

Read-only dictionary showing which blocks are connected to this worker.

Example

python
def setup(ctx):
    blocks = ctx.connected_blocks
    ctx.log.info(f"Connected: {blocks}")
    # Example output:
    # {
    #   "trading_node_id": "bybit_1",
    #   "trading_block_type": "trading.bybit",
    #   "llm_node_id": "claude_1",
    #   "llm_block_type": "ai.claude_agent",
    #   "monitor_node_ids": ["monitor_1"],
    #   "telegram_node_id": "telegram_1",
    #   "file_explorer_node_id": "files_1",
    #   "workspace_id": 42
    # }

    if not blocks.get("trading_node_id"):
        ctx.log.warn("No trading block connected!")

Error Handling Pattern

python
def tick(ctx):
    try:
        ticker = ctx.bybit.get_tickers("BTCUSDT")

        # Check for API errors
        if "error" in ticker:
            ctx.log.error(f"API error: {ticker['error']}")
            return

        price = ticker.get("last_price", "0")
        ctx.log.info(f"BTC: {price}")

    except RuntimeError as e:
        # Block not connected
        ctx.log.error(f"Missing connection: {e}")
    except Exception as e:
        # Unexpected error -- will trigger on_error() if defined
        raise

def on_error(ctx, error):
    ctx.log.error(f"Tick failed: {error}")
    ctx.telegram.send(f"Worker error: {error}")

WARNING

All API methods return dict results. Always check for an "error" key in the response before using the data.

AiSpinner Documentation