
Crypto Tactical Risk Appetite
Weighted composite risk-appetite score from price momentum, volume impulse, candle pressure, open interest, perp premium proxy and volatility, with risk-on/off crossings and crowding warnings.
Moving Average
Computes a configurable SMA from candle closes; the metadata sheet is generated from declarations in the source.
Files
om-package.json: package manifestpackage.json: local AssemblyScript build scriptsasconfig.json: AssemblyScript compiler settingswrun/metadata.json: WRUN input/output metadatasrc/indicator.ts: AssemblyScript indicator sourcesrc/sdk/sdk.ts: raw WRUN host imports (advanced; variable-index access)src/sdk/ta.ts: stateful TA classes (Sma, Ema, Stdev, Zscore, Rsi, Roc, Cross)src/sdk/declare.ts: code-first declarations (params, inputs, outputs, frames, panels, drawings, strategy)src/sdk/fmt.ts: Indicator kit module (import./sdk/fmt): text formatting into string slotssrc/sdk/clock.ts: Indicator kit module (./sdk/clock): the bar clock, time zones and sessionssrc/sdk/color.ts: Indicator kit module (./sdk/color): runtime colours and palette indicessrc/sdk/ta-plus.ts: Indicator kit module (./sdk/ta-plus): the extra indicator classes beyond ta.tssrc/sdk/stats.ts: Indicator kit module (./sdk/stats): window statistics and the History helpersrc/sdk/orderflow.ts: Indicator kit module (./sdk/orderflow): delta, CVD, volume profiles and book imbalancesrc/sdk/levels.ts: Indicator kit module (./sdk/levels): period, session and pivot levelssrc/sdk/structure.ts: Indicator kit module (./sdk/structure): swings, structure breaks, gaps, order blocks and candle patternssrc/sdk/resample.ts: Indicator kit module (./sdk/resample): a higher timeframe from the chart's own bars, closed windows and smoothingsrc/sdk/options.ts: Indicator kit module (./sdk/options): options-chain analytics (gamma exposure, the zero-gamma flip, walls, max pain, put/call ratio)src/sdk/candles.ts: Indicator kit module (./sdk/candles): a candles stream read as calendar periods (week, month, quarter, year, with VWAP sums) and as the newest closed candlessrc/gen/: GENERATED name-attached slot accessors; do not editsrc/gen/frames.ts: GENERATED FRAME_<NAME> constants, the fb_* frame builder with writeFrameBuffer, and writeFrame; present only with declared framessrc/gen/draw.ts: GENERATED handle drawing runtime (draw.line/box/label/polyline, bar.isLast); do not editsrc/gen/strategy.ts: GENERATED strategy runtime (strategy.long/short/exit builders, close/closeAll/cancel/cancelAll, position getters); do not editscripts/wrun-prebuild.mjs: GENERATED prebuild step run bybun run build, also passed to asc as its--transform; do not edit.wrun-scaffold: scaffold contract version;om wrunmigrates older workspaces once
Code-first declarations
Declare settings, feeds, and outputs as top-level statements of src/indicator.ts (imported from ./sdk/declare):
param("period", 20, { min: 1, max: 200 });
input("close", ohlcv.close);
output("sma", line, overlay);
The build then DERIVES wrun/metadata.json from these declarations (provenance recorded as generated_from plus a source_digest) before generating the accessors, so there is one file to edit and nothing to keep in sync. Extraction is static (this code never runs at build time): names are string literals, defaults and options are literals, sources are bare member references, and declarations live in src/indicator.ts only. With no declarations the hand-written sheet stays the authority (metadata-first mode, unchanged). A generated sheet is derived state: edit the declarations, never the sheet (om wrun source set and agent metadata updates refuse on generated sheets and point at the declaration instead).
Snapshots, panels and pane placement
Choose each view by its lifetime and coordinates. A good sheet names every dependency, binds each snapshot to a consumer with the matching payload, and preserves gaps. Declare only the families the requested view needs.
Declare frames[] with frame(name, { max_bytes }), then write UTF-8 JSON from finalize() through ./gen/frames: build a frame that is rebuilt per bar or live tick with fb_clear(), fb_text(s), fb_str(s), fb_int(n), fb_f64(x, decimals), fb_num(x) and send it with writeFrameBuffer(slot) (allocation-free), or send a small one-shot string with writeFrame(slot, json). Use the bound frame result or its generated FRAME_<NAME> constant as the slot. Frames require wrun-4: the last write per slot is the snapshot for the whole run, never a per-bar row. Up to 8 frames, 96 KiB each; frame bytes share a 2 MiB transport budget with strings. An unwritten frame omits its view; a malformed written payload refuses selection.
| View | Sheet / code-first declaration | Contract |
|---|---|---|
| Docked profile | levels[] / plot.levels | Bind a frame with prices, values and optional colors; 1..512 strictly monotonic prices, matching lengths, null gaps; at most 4 levels declarations; left/right dock, width_frac 0.05..0.5. |
| Separate panel | panels[] / panel.bars, panel.line, panel.scatter, panel.histogram, panel.pie, panel.heatmap, panel.table, panel.tiles | Each object declares name, title, x (time/index/category), place (below/side) and frame; up to 8 panels. Frames contain rows in the kind's tuple grammar. |
| Ladder / feed | drawings[] / draw.ladder, draw.feed | Frame-bound objects: ladder has 1..64 price/value/fraction/color rows, left/right side; feed has 1..50 millisecond-time/text/color lines, text 1..80 characters. |
| Card / meter | drawings[] / draw.card, draw.meter | Cards use named outputs or string slots; meters read a last-ready output as a fraction (emit 0..1; the host does not clamp) and a 2..5-color ramp. Neither needs a frame. |
| Mini-chart grid | drawings[] / draw.minicharts | A pane-pinned grid of 1..12 candle panels: the look is its options (anchor, x/y, columns, panel size, gap, colors, candle_style, two moving averages, change badge, volume strip), each a literal, or on the ones a setting can change an "@<param>" reference; the frame holds the title and each panel's label and 0..100 [time_ms, open, high, low, close, volume] candles, written with mc_begin, mc_panel / mc_bar or mc_candles, then writeMiniCharts(slot). |
Bars, line and table panels declare 1..8 series; other panel kinds omit series. Bars/line rows start with a key, followed by one value per series; table rows contain only cells. Time keys are epoch seconds. Histogram and heatmap use category x. Panel caps are 2000 rows generally, 32 for table, 24 for pie/tiles; table cells and tiles allow 1..64-point sparks.
draw.card(name, options) takes at most 12 rows, with at most 8 cards. A row's spark: { output, window } uses the last 2..64 ready numeric values, oldest first, independently of its text. draw.meter takes a named object with label, fraction, ramp and optional text (literal or string slot). Ladder fractions also stay in 0..1, with nonnegative values. All drawing declarations share a 64-entry cap. Expanded render selections have their own 2 MiB cap, separate from frame transport.
out.inset(name, { dock, height_px, shape }) declares an ordinary numeric output: sheet outputs[].plot is inset, with its placement in inset. It keeps every ready row. Dock is top/bottom, height 16..120 px, shape histogram/area/line. Insets work on every ABI; ordinary lines use output(name, line, overlay). The SDK has no out.line declaration.
For a HUD, use the anchored handles described below. For watch values, use a metadata-first inputSources entry with source: "series", a ref of watch/<slug>/<key> or an installed @scope/name, and optional missing carry (default) or nan. Series inputs are daemon-only, never primary, and have no field or market pins. Read through in_<name>(). The last reading in each primary-grid bucket wins; carry is as-of. There is no code-first series declaration. Keep the sheet metadata-first instead of editing a generated sheet. Chart hosts cannot supply series, and a line-only chart preview does not verify snapshot or HUD rendering.
Slot accessors (src/gen)
The wasm ABI is positional, so om wrun generates one accessor per metadata name (p_<param>(), in_<input>(), out_<output>(value), emitRow()) and regenerates them from wrun/metadata.json before every build: bun run build runs scripts/wrun-prebuild.mjs first (plain JavaScript, needs only node or bun, never om), then hands the same script to asc as --transform so frame, levels, panel, card, ladder, feed and meter declarations are erased before typechecking (asconfig.json carries no transform), and om wrun build does the same. Read params/inputs and write outputs ONLY through these accessors: editing or reordering metadata then updates the slot inside the accessor while your source keeps its names. Raw positional literals (getFloat(0), setOutput(0, ...), also through an import alias or a renamed @external declaration) fail the build in every file under src/ except src/sdk and src/gen. Builds that bypass the scaffold (--wasm, --compile-command) are exported with positionalAbi: true: their slots bind by metadata position only, so never reorder metadata entries for such a package.
Handle drawings
Lines, boxes, labels, and polylines the Indicator creates, moves, and deletes across bars, each keyed by an integer id it chooses. Declare each kind once (its defaults) beside the other declarations, then draw through the handle objects in src/gen/draw.ts from finalize():
handles.box({ color: "#38bdf8", opacity: 0.2 });
const zone = draw.box(0); // one object per id, made once
zone.set(leftTs, hi, rightTs, lo); // create, or move a live box
zone.setRightBottom(barTs, lo); // partial setters re-send the rest
if (bar.isLast()) zone.delete(); // the last-bar signal
A label draws a string slot's text: draw.label(1).set(x, y).text(str_note_sb) after building the text with sb_*. Coordinates default to x in epoch seconds and y in price. handles.line({ anchor: "top_left" }) makes both axes CSS pixels from the pane's content rectangle. The nine corner and centre spots set both axes; top/bottom keep chart x and pixel y, left/right keep pixel x and chart y. Offsets have no inset and may be negative. A live handle changes placement through line.anchor(ANCHOR_TOP_LEFT) or style.anchor(line, ANCHOR_TOP_LEFT); ANCHOR_CHART clears the default and restores chart coordinates. The same placement applies to handles.line, handles.box, handles.label and handles.polyline; import the ANCHOR_* constants from ./gen/draw. Right/bottom offsets point inward. Reuse ids across bars: at most 500 live handles per kind, 1500 total, 4096 draw calls per row, 100,000 points per polyline and 524,288 across the live polylines. Any handles.* declaration or bar.isLast() call needs at least abi_version: "wrun-3"; wrun-4 retains the channel. Calling the raw draw imports directly fails the build like a raw slot literal does.
Strategies
An Indicator that places orders. Declare it once beside the other declarations (the engine's settings, every field optional; a numeric setting may link a param through the handle param(...) returns), then place orders through the builders in src/gen/strategy.ts from finalize() and read the position through its getters:
import { strategy } from "./gen/strategy"; // the declaration and the calls, one import
const risk = param("risk_pct", 25, { min: 1, max: 100 });
strategy({ initialCapital: 10000, qtyType: "percentOfEquity", qtyValue: risk, commissionPercent: 0.05 });
strategy.long("L").send(); // a market entry, filled at the next open
strategy.exit("Protect").from("L").stop(px * 0.96).send(); // a protective stop on that entry
if (strategy.positionSize() > 0.0) strategy.closeAll(); // getters read the post-fill position
The host runs the engine's own broker beside the module (fills, sizing, fees, slippage, margin and stats are the engine's), the run result carries its trade list, equity curve and stats, and the Strategy Tester reads them. The primary input must be an ohlcv feed on the instance's own market. Declaring strategy(...) derives an abi_version of wrun-3; calling the raw strategy imports directly fails the build like a raw slot literal does.
Authoring loop
om wrun validate .
om wrun install . --replace
The install command regenerates src/gen, runs the local AssemblyScript build, exports dist/package, validates the installable package, and installs it under ~/.openmarket/packages. A plain bun run build also regenerates src/gen before compiling, so a metadata edit can never compile against stale accessors.
For upstream wrun-compile parity, use a staging or local wrapper instead of plain AssemblyScript. Set OM_WRUN_COMPILE_COMMAND to a command that writes the output wasm as its third positional argument, or use placeholders:
export OM_WRUN_COMPILE_COMMAND='wrun-compile-export --source {source} --metadata {metadata} --out {wasm}'
om wrun export . --out dist/package --compile-command "$OM_WRUN_COMPILE_COMMAND" --forceIndicator
Outputs
| Output | Plot | Panel | Style |
|---|---|---|---|
risk_score | line | lower | 1 colors, width 2 |
price_score | line | lower | 1 colors, width 1 |
volume_score | line | lower | 1 colors, width 1 |
pressure_score | line | lower | 1 colors, width 1 |
oi_score | line | lower | 1 colors, width 1 |
premium_score | line | lower | 1 colors, width 1 |
volatility_score | line | lower | 1 colors, width 1 |
weight_oi | line | lower | color #ffd54f, width 1 |
weight_premium | line | lower | color #26c6da, width 1 |
weight_sum | line | lower | color #9e9e9e, width 1 |
extreme_greed_level | line | lower | color #26a641, width 1, dotted |
risk_on_level_line | line | lower | color #26a641, width 1, dashed |
mid_level | line | lower | color #9e9e9e, width 1, dotted |
risk_off_level_line | line | lower | color #f85149, width 1, dashed |
extreme_fear_level | line | lower | color #f85149, width 1, dotted |
bg_zone | data only | lower | default |
risk_on_cross_evt | data only | lower | default |
risk_off_cross_evt | data only | lower | default |
crowded_long_evt | data only | lower | default |
crowded_short_evt | data only | lower | default |
risk_on_cross_shape | shape | lower | 1 colors, where risk_on_cross_evt |
risk_off_cross_shape | shape | lower | 1 colors, where risk_off_cross_evt |
crowded_long_shape | shape | lower | 1 colors, where crowded_long_evt |
crowded_short_shape | shape | lower | 1 colors, where crowded_short_evt |
Params
| Name | Default | Min | Max |
|---|---|---|---|
fast_ema_len | 21 | 1 | 300 |
slow_ema_len | 55 | 1 | 300 |
mom_len | 10 | 1 | 100 |
z_len | 50 | 5 | 300 |
vol_len | 20 | 5 | 200 |
pressure_len | 20 | 5 | 200 |
use_oi | 1 | 0 | 1 |
oi_z_len | 20 | 5 | 200 |
use_premium | 1 | 0 | 1 |
premium_symbol | 0 | ||
premium_z_len | 20 | 5 | 200 |
atr_len | 14 | 1 | 100 |
atr_z_len | 50 | 5 | 300 |
risk_on_level | 60 | 0 | 100 |
risk_off_level | 40 | 0 | 100 |
extreme_greed | 80 | 0 | 100 |
extreme_fear | 20 | 0 | 100 |
crowding_z_level | 2 | 0.5 | 10 |
show_bg | 1 | 0 | 1 |
show_signals | 1 | 0 | 1 |
Inputs
| Input | Source | Selection |
|---|---|---|
close | ohlcv | field close |
open | ohlcv | field open |
high | ohlcv | field high |
low | ohlcv | field low |
volume | ohlcv | field volume |
oi_close | oi | field close |
spot_close | ohlcv | field close, symbol BTCUSDT, exchange BINANCE_FUTURES |
Provenance
| ABI | wrun-2 |
|---|
| Version | Published | Note |
|---|---|---|
| 0.1.0 | Oct 6, 2026 |
| File | Size |
|---|---|
| .gitignore | 36 B |
| .wrun-scaffold | 29 B |
| README.md | 13.0 KiB |
| asconfig.json | 357 B |
| bun.lock | 886 B |
| dist/package/om-package.json | 770 B |
| dist/package/src/indicator.ts | 13.6 KiB |
| dist/package/wrun/metadata.json | 13.9 KiB |
| dist/package/wrun/module.wasm | 5.9 KiB |
| om-package.json | 685 B |
| package.json | 756 B |
| scripts/wrun-prebuild.mjs | 514.6 KiB |
| src/gen/draw.ts | 32.4 KiB |
| src/gen/inputs.ts | 3.3 KiB |
| src/gen/outputs.ts | 9.0 KiB |
| src/gen/params.ts | 5.0 KiB |
| src/gen/strategy.ts | 13.6 KiB |
| src/gen/strings.ts | 10.3 KiB |
| src/indicator.ts | 13.6 KiB |
| src/sdk/candles.ts | 22.7 KiB |
| src/sdk/clock.ts | 41.3 KiB |
| src/sdk/color.ts | 20.3 KiB |
| src/sdk/declare.ts | 105.7 KiB |
| src/sdk/fmt.ts | 37.6 KiB |
| src/sdk/levels.ts | 23.3 KiB |
| src/sdk/options.ts | 16.6 KiB |
| src/sdk/orderflow.ts | 24.6 KiB |
| src/sdk/resample.ts | 41.4 KiB |
| src/sdk/sdk.ts | 1.2 KiB |
| src/sdk/stats.ts | 44.2 KiB |
| src/sdk/structure.ts | 31.6 KiB |
| src/sdk/ta-plus.ts | 43.5 KiB |
| src/sdk/ta.ts | 100.5 KiB |
| wrun/metadata.json | 13.8 KiB |
| wrun/module.wasm | 5.9 KiB |