Agent Skillsmarketcalls/openalgo › flow-builder

flow-builder

GitHub

用于构建、编辑和调试 OpenAlgo Flow 无代码工作流。通过读取节点文档、选择触发器、连接图结构并严格验证 JSON,确保工作流正确导入和运行。

.claude/skills/flow-builder/SKILL.md marketcalls/openalgo

Trigger Scenarios

创建或编辑工作流 调试导入后无响应的工作流 将策略移植到 Flow

Install

npx skills add marketcalls/openalgo --skill flow-builder -g -y
More Options

Non-standard path

npx skills add https://github.com/marketcalls/openalgo/tree/main/.claude/skills/flow-builder -g -y

Use without installing

npx skills use marketcalls/openalgo@flow-builder

指定 Agent (Claude Code)

npx skills add marketcalls/openalgo --skill flow-builder -a claude-code -g -y

安装 repo 全部 skill

npx skills add marketcalls/openalgo --all -g -y

预览 repo 内 skill

npx skills add marketcalls/openalgo --list

SKILL.md

Frontmatter
{
    "name": "flow-builder",
    "description": "Build, edit or debug an OpenAlgo Flow workflow - the no-code node graph at \/flow. Use when asked to create a workflow, wire a webhook or TradingView alert to an order, add a node, port a strategy into Flow, or work out why a workflow imported but did nothing. Produces a workflow JSON that is validated against the real importer before it is handed over."
}

Building an OpenAlgo Flow workflow

A workflow is a JSON graph: nodes (what happens) plus edges (in what order). It is imported at /flow, or written straight into flow_workflows.

The one rule

Validate before you hand anything over.

uv run python .claude/skills/flow-builder/validate.py <workflow.json>

It calls the importer's own validate_workflow, so it cannot disagree with what the server accepts, and then adds the checks the importer deliberately skips. Those matter more than they sound: Flow ignores a data key nothing reads. Write strikeOffset when the field is offset and the workflow imports cleanly, runs successfully, and silently uses ATM. Nothing in the run says so.

Exit 0 means it imports. Warnings mean it imports and then misbehaves - treat them as errors unless you know why.

Workflow

  1. Read reference/nodes.md. All 61 node types, their required fields, and every data key each one actually reads. Generated from the validator and the executor, so it cannot drift.
  2. Pick the trigger. Exactly one, and it is the execution root: start (schedule), webhookTrigger (external POST), priceAlert, orderUpdateTrigger. A second trigger is rejected at import.
  3. Wire the graph. Every edge names source and target node ids. An edge leaving a condition node must also set sourceHandle to pick the TRUE or FALSE branch.
  4. Validate. Fix. Re-validate.
  5. Import at /flow, or POST /flow/api/workflows/import.

For the full field-by-field contract, docs/prompt/flow-import-format.md is the long form. This skill is the short path plus the check.

The shape

{
  "name": "Webhook long entry",
  "nodes": [
    { "id": "node_1", "type": "webhookTrigger",
      "position": { "x": 0, "y": 0 }, "data": { "label": "TV alert" } },
    { "id": "node_2", "type": "placeOrder",
      "position": { "x": 0, "y": 120 },
      "data": { "symbol": "{{webhook.symbol}}", "exchange": "NSE",
                "action": "{{webhook.action}}", "quantity": "{{webhook.quantity}}",
                "product": "MIS", "priceType": "MARKET",
                "outputVariable": "orderResult" } }
  ],
  "edges": [
    { "id": "e1", "source": "node_1", "target": "node_2", "type": "default" }
  ]
}

Values from outside: {{webhook.*}}

The webhook body is exposed as {{webhook.<key>}}. Nested paths and array indexing both work: {{webhook.legs[0].qty}}.

Every field on an order node takes a reference - not only symbol. The editor shows a dropdown for exchange and a BUY/SELL pair for action, but the stored value is a string either way, so "exchange": "{{webhook.exchange}}" is valid. The same is true of quantity, product, priceType, price, triggerPrice, offset, optionType and expiryType.

A field holding exactly one whole token keeps its type, so "quantity": "{{webhook.quantity}}" against {"quantity": 10} arrives as the number 10.

Enumerated fields are case-insensitive, which matters because TradingView sends whatever the chart carries: "action": "buy" is accepted. symbol is upper-cased on order nodes for the same reason.

An unresolved reference on an order field fails the node. It does not fall back to a default. That is deliberate: a webhook omitting quantity used to place a real order for 1 unit. If a value is genuinely optional, use a literal or branch on a condition node first.

Traps

A key nothing reads is ignored, not rejected. The single most common way a workflow "works" and does the wrong thing. validate.py catches it.

source is not the price field. On indicator, barOffset and priorPeriodOhlc, source selects the data source (api or db). The price field is sourceField. Passing close to source fails with a history error naming neither.

Options expiry has two forms. expiryType takes a relative type (current_week, next_week, current_month, next_month) or a DDMMMYY date such as 28OCT25, used as given. expiryDate is the same explicit date under its own key and wins when both are set.

The webhookTrigger carries no instrument. It has only a label. Symbol and exchange come from the request.

A failed read is not a False. priceCondition, positionCheck and fundCheck error rather than answering when the broker call fails, so they take neither branch. Do not write a graph that relies on the FALSE branch firing when data is unavailable - it will not.

A gate waits for inputCount, not for the wires. Configure three inputs and wire two and the gate errors rather than evaluating on part of the condition.

timeWindow may cross midnight. 22:00 to 02:00 spans midnight rather than being empty, so an overnight MCX or crypto guard works as written.

unsubscribe needs a symbol unless streamType is all, because the underlying call clears every subscription on the instance - including the ones the Sandbox engine uses to trigger pending SL and LIMIT orders.

Subscriptions end with the workflow. Deactivating or deleting it releases everything its subscribe nodes opened, so no cleanup node is needed for that.

A condition evaluates once per run, like a gate. A diamond that reaches one condition by two paths fires its branch once, not twice.

waitUntil is capped at 30 minutes. It sleeps inside the triggering request, so anything longer belongs on a start schedule trigger.

A schedule is clock-aligned and market-hours gated. interval fires on the clock (HH:MM:02, not activation time + 60s), and the start node carries marketHoursOnly (the editor writes true, but an imported JSON that omits the key defaults to false and runs around the clock, so set it explicitly), marketHoursStart (09:15), marketHoursEnd (15:15) and marketHoursExchange (NSE). The window narrows the exchange's session; it never reopens a holiday.

A 1-minute strategy needs a shorter history cache. The indicator node reuses a fetch for FLOW_HISTORY_CACHE_TTL seconds, 30 by default, which is half a one-minute candle. Set it to 2-3 for 1m work.

Read a closed bar, not a forming one. {{ind.latest.*}} is the bar still being built and repaints within the period. {{ind.previous.*}} is the last closed bar. With offsetBars: 2, {{ind.at_offset.*}} is the bar before that, so one node gives both bars a flip test needs.

A tuple indicator returns out0, out1, ... not value. Supertrend gives out0 (the line) and out1 (direction), and its direction is inverted from the usual convention: -1 is the uptrend, +1 the downtrend. Verify an indicator's encoding before trading it.

Conditions need sourceHandle. An edge leaving priceCondition, timeCondition, timeWindow, positionCheck, fundCheck or varCondition must say which branch it is.

Gates wait for every input. andGate and orGate fire once, after all wired inputs have been evaluated.

Testing a workflow without the editor

Trigger it over HTTP, exactly as an alert would, and read the run back:

curl -X POST http://127.0.0.1:5000/flow/webhook/<token> \
  -H "Content-Type: application/json" \
  -d '{"secret":"<secret>","symbol":"RELIANCE","action":"BUY","quantity":1}'
SELECT id, status, logs FROM flow_workflow_executions ORDER BY id DESC LIMIT 1;

The log lines are the evidence: they name the values each node actually used, so a field that silently kept its default shows up as a mismatch against the payload you sent. Check settings.analyze_mode first - 1 routes orders to the Sandbox engine, 0 sends them to a live broker.

Files

Path
validate.py the gate; wraps the importer's own validator
reference/nodes.md all 61 node types, generated
generate_reference.py regenerate it after a node's fields change
coverage.py fails if a node type is undocumented
docs/prompt/flow-import-format.md the long-form contract

Version History

  • 4a613c7 Current 2026-09-03 03:54

Same Skill Collection

.claude/skills/broker-integration/SKILL.md
.claude/skills/chart-indicator/SKILL.md
.claude/skills/custom-indicator/SKILL.md
.claude/skills/fd-audit/SKILL.md
.claude/skills/indicator-chart/SKILL.md
.claude/skills/indicator-dashboard/SKILL.md
.claude/skills/indicator-expert/SKILL.md
.claude/skills/indicator-scanner/SKILL.md
.claude/skills/indicator-setup/SKILL.md
.claude/skills/live-feed/SKILL.md
.claude/skills/security-audit/SKILL.md
.claude/skills/verify/SKILL.md
.claude/skills/version-bump/SKILL.md

Metadata

Files
0
Version
4a613c7
Hash
a54ea157
Indexed
2026-09-03 03:54

Home - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-07 07:03
浙ICP备14020137号-1 $Map of visitor$