@rune-kit/rune 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +357 -0
- package/agents/.gitkeep +0 -0
- package/agents/architect.md +29 -0
- package/agents/asset-creator.md +11 -0
- package/agents/audit.md +11 -0
- package/agents/autopsy.md +11 -0
- package/agents/brainstorm.md +11 -0
- package/agents/browser-pilot.md +11 -0
- package/agents/coder.md +29 -0
- package/agents/completion-gate.md +11 -0
- package/agents/constraint-check.md +11 -0
- package/agents/context-engine.md +11 -0
- package/agents/cook.md +11 -0
- package/agents/db.md +11 -0
- package/agents/debug.md +11 -0
- package/agents/dependency-doctor.md +11 -0
- package/agents/deploy.md +11 -0
- package/agents/design.md +11 -0
- package/agents/docs-seeker.md +11 -0
- package/agents/fix.md +11 -0
- package/agents/hallucination-guard.md +11 -0
- package/agents/incident.md +11 -0
- package/agents/integrity-check.md +11 -0
- package/agents/journal.md +11 -0
- package/agents/launch.md +11 -0
- package/agents/logic-guardian.md +11 -0
- package/agents/marketing.md +11 -0
- package/agents/onboard.md +11 -0
- package/agents/perf.md +11 -0
- package/agents/plan.md +11 -0
- package/agents/preflight.md +11 -0
- package/agents/problem-solver.md +11 -0
- package/agents/rescue.md +11 -0
- package/agents/research.md +11 -0
- package/agents/researcher.md +29 -0
- package/agents/review-intake.md +11 -0
- package/agents/review.md +11 -0
- package/agents/reviewer.md +28 -0
- package/agents/safeguard.md +11 -0
- package/agents/sast.md +11 -0
- package/agents/scanner.md +28 -0
- package/agents/scope-guard.md +11 -0
- package/agents/scout.md +11 -0
- package/agents/sentinel.md +11 -0
- package/agents/sequential-thinking.md +11 -0
- package/agents/session-bridge.md +11 -0
- package/agents/skill-forge.md +11 -0
- package/agents/skill-router.md +11 -0
- package/agents/surgeon.md +11 -0
- package/agents/team.md +11 -0
- package/agents/test.md +11 -0
- package/agents/trend-scout.md +11 -0
- package/agents/verification.md +11 -0
- package/agents/video-creator.md +11 -0
- package/agents/watchdog.md +11 -0
- package/agents/worktree.md +11 -0
- package/commands/.gitkeep +0 -0
- package/commands/rune.md +168 -0
- package/compiler/__tests__/openclaw-adapter.test.js +140 -0
- package/compiler/__tests__/parser.test.js +55 -0
- package/compiler/adapters/antigravity.js +59 -0
- package/compiler/adapters/claude.js +37 -0
- package/compiler/adapters/cursor.js +67 -0
- package/compiler/adapters/generic.js +60 -0
- package/compiler/adapters/index.js +45 -0
- package/compiler/adapters/openclaw.js +150 -0
- package/compiler/adapters/windsurf.js +60 -0
- package/compiler/bin/rune.js +288 -0
- package/compiler/doctor.js +153 -0
- package/compiler/emitter.js +240 -0
- package/compiler/parser.js +208 -0
- package/compiler/transformer.js +69 -0
- package/compiler/transforms/branding.js +27 -0
- package/compiler/transforms/cross-references.js +29 -0
- package/compiler/transforms/frontmatter.js +38 -0
- package/compiler/transforms/hooks.js +68 -0
- package/compiler/transforms/subagents.js +36 -0
- package/compiler/transforms/tool-names.js +60 -0
- package/contexts/dev.md +34 -0
- package/contexts/research.md +43 -0
- package/contexts/review.md +55 -0
- package/extensions/ai-ml/PACK.md +517 -0
- package/extensions/analytics/PACK.md +557 -0
- package/extensions/backend/PACK.md +678 -0
- package/extensions/chrome-ext/PACK.md +995 -0
- package/extensions/content/PACK.md +381 -0
- package/extensions/devops/PACK.md +520 -0
- package/extensions/ecommerce/PACK.md +280 -0
- package/extensions/gamedev/PACK.md +393 -0
- package/extensions/mobile/PACK.md +273 -0
- package/extensions/saas/PACK.md +805 -0
- package/extensions/security/PACK.md +536 -0
- package/extensions/trading/PACK.md +597 -0
- package/extensions/ui/PACK.md +947 -0
- package/package.json +47 -0
- package/skills/.gitkeep +0 -0
- package/skills/adversary/SKILL.md +271 -0
- package/skills/asset-creator/SKILL.md +157 -0
- package/skills/audit/SKILL.md +466 -0
- package/skills/autopsy/SKILL.md +200 -0
- package/skills/ba/SKILL.md +279 -0
- package/skills/brainstorm/SKILL.md +266 -0
- package/skills/browser-pilot/SKILL.md +168 -0
- package/skills/completion-gate/SKILL.md +151 -0
- package/skills/constraint-check/SKILL.md +165 -0
- package/skills/context-engine/SKILL.md +176 -0
- package/skills/cook/SKILL.md +636 -0
- package/skills/db/SKILL.md +256 -0
- package/skills/debug/SKILL.md +240 -0
- package/skills/dependency-doctor/SKILL.md +235 -0
- package/skills/deploy/SKILL.md +174 -0
- package/skills/design/DESIGN-REFERENCE.md +365 -0
- package/skills/design/SKILL.md +462 -0
- package/skills/doc-processor/SKILL.md +254 -0
- package/skills/docs/SKILL.md +336 -0
- package/skills/docs-seeker/SKILL.md +166 -0
- package/skills/fix/SKILL.md +192 -0
- package/skills/git/SKILL.md +285 -0
- package/skills/hallucination-guard/SKILL.md +204 -0
- package/skills/incident/SKILL.md +241 -0
- package/skills/integrity-check/SKILL.md +169 -0
- package/skills/journal/SKILL.md +190 -0
- package/skills/launch/SKILL.md +330 -0
- package/skills/logic-guardian/SKILL.md +240 -0
- package/skills/marketing/SKILL.md +229 -0
- package/skills/mcp-builder/SKILL.md +311 -0
- package/skills/onboard/SKILL.md +298 -0
- package/skills/perf/SKILL.md +297 -0
- package/skills/plan/SKILL.md +520 -0
- package/skills/preflight/SKILL.md +231 -0
- package/skills/problem-solver/SKILL.md +284 -0
- package/skills/rescue/SKILL.md +434 -0
- package/skills/research/SKILL.md +122 -0
- package/skills/review/SKILL.md +354 -0
- package/skills/review-intake/SKILL.md +222 -0
- package/skills/safeguard/SKILL.md +188 -0
- package/skills/sast/SKILL.md +190 -0
- package/skills/scaffold/SKILL.md +276 -0
- package/skills/scope-guard/SKILL.md +150 -0
- package/skills/scout/SKILL.md +232 -0
- package/skills/sentinel/SKILL.md +320 -0
- package/skills/sentinel-env/SKILL.md +226 -0
- package/skills/sequential-thinking/SKILL.md +234 -0
- package/skills/session-bridge/SKILL.md +287 -0
- package/skills/skill-forge/SKILL.md +317 -0
- package/skills/skill-router/SKILL.md +267 -0
- package/skills/surgeon/SKILL.md +203 -0
- package/skills/team/SKILL.md +397 -0
- package/skills/test/SKILL.md +271 -0
- package/skills/trend-scout/SKILL.md +145 -0
- package/skills/verification/SKILL.md +201 -0
- package/skills/video-creator/SKILL.md +201 -0
- package/skills/watchdog/SKILL.md +166 -0
- package/skills/worktree/SKILL.md +140 -0
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "@rune/trading"
|
|
3
|
+
description: Fintech and trading patterns — real-time data, financial dashboards, technical indicators, WebSocket architecture, and experiment-driven strategy development.
|
|
4
|
+
metadata:
|
|
5
|
+
author: runedev
|
|
6
|
+
version: "0.3.0"
|
|
7
|
+
layer: L4
|
|
8
|
+
price: "$15"
|
|
9
|
+
target: Fintech developers
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# @rune/trading
|
|
13
|
+
|
|
14
|
+
## Purpose
|
|
15
|
+
|
|
16
|
+
Fintech applications demand precision that general-purpose patterns cannot guarantee. This pack groups five tightly-coupled concerns — safe money arithmetic, WebSocket reliability, financial chart rendering, streaming indicator computation, and experiment-driven strategy development — because a gap in any one layer breaks the entire trading surface. It solves the recurring problem of developers accidentally using JavaScript floats for currency, missing auto-reconnect logic, or computing indicators on stale snapshots. Activates automatically when trading or financial project signals are detected.
|
|
17
|
+
|
|
18
|
+
## Triggers
|
|
19
|
+
|
|
20
|
+
- Auto-trigger: when `TradingView`, `Lightweight Charts`, `decimal.js`, `ccxt`, or `ws` detected in `package.json`
|
|
21
|
+
- Auto-trigger: when files matching `**/price*.ts`, `**/ticker*.ts`, `**/orderbook*.ts` exist in project
|
|
22
|
+
- `/rune trading` — manual invocation
|
|
23
|
+
- Called by `cook` (L1) when fintech or trading project context detected
|
|
24
|
+
|
|
25
|
+
## Skills Included
|
|
26
|
+
|
|
27
|
+
### fintech-patterns
|
|
28
|
+
|
|
29
|
+
Financial application patterns — safe money handling with Decimal/BigInt, transaction processing, audit trails, regulatory compliance, and PnL calculations. Prevents the #1 fintech bug: float arithmetic on money.
|
|
30
|
+
|
|
31
|
+
#### Workflow
|
|
32
|
+
|
|
33
|
+
**Step 1 — Detect money handling code**
|
|
34
|
+
Use `Grep` to scan for raw float arithmetic on price/amount/balance fields: `Grep pattern="(price|amount|balance|pnl)\s*[\+\-\*\/]" glob="**/*.ts"`. Flag any result not wrapped in Decimal or BigInt.
|
|
35
|
+
|
|
36
|
+
**Step 2 — Enforce Decimal/BigInt boundaries**
|
|
37
|
+
Use `Read` on each flagged file to identify entry points (API response parsing, user input). Replace raw number literals with `new Decimal(value)` at parse time. All arithmetic must flow through Decimal operations until final display.
|
|
38
|
+
|
|
39
|
+
**Step 3 — Implement audit trail and verify rounding**
|
|
40
|
+
Use `Bash` to run `tsc --noEmit` confirming no implicit `any` on financial fields. Add an immutable audit log entry on every mutation (create, fill, cancel). Verify rounding mode is `ROUND_HALF_EVEN` (banker's rounding) for all display formatting.
|
|
41
|
+
|
|
42
|
+
#### Example
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import Decimal from 'decimal.js';
|
|
46
|
+
|
|
47
|
+
Decimal.set({ rounding: Decimal.ROUND_HALF_EVEN });
|
|
48
|
+
|
|
49
|
+
// NEVER: const fee = price * 0.001
|
|
50
|
+
// ALWAYS: Decimal arithmetic — exact, auditable
|
|
51
|
+
function calculateFee(price: string, quantity: string, feeRate: string): Decimal {
|
|
52
|
+
return new Decimal(price)
|
|
53
|
+
.times(new Decimal(quantity))
|
|
54
|
+
.times(new Decimal(feeRate))
|
|
55
|
+
.toDecimalPlaces(8);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function formatUSD(value: Decimal): string {
|
|
59
|
+
return new Intl.NumberFormat('en-US', {
|
|
60
|
+
style: 'currency',
|
|
61
|
+
currency: 'USD',
|
|
62
|
+
minimumFractionDigits: 2,
|
|
63
|
+
}).format(value.toNumber());
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### realtime-data
|
|
70
|
+
|
|
71
|
+
Real-time data architecture — WebSocket lifecycle management, auto-reconnect with exponential backoff, event normalization, rate limiting, and TanStack Query cache invalidation.
|
|
72
|
+
|
|
73
|
+
#### Workflow
|
|
74
|
+
|
|
75
|
+
**Step 1 — WebSocket setup and event normalization**
|
|
76
|
+
Use `Read` on existing data-fetching files to understand current polling or REST patterns. Replace with a WebSocket client class that emits typed, normalized events regardless of upstream message format. Define a `NormalizedTick` interface at the boundary.
|
|
77
|
+
|
|
78
|
+
**Step 2 — Implement exponential backoff reconnect**
|
|
79
|
+
In the WebSocket class, add a reconnect handler: attempt 1 after 1 s, attempt 2 after 2 s, attempt 3 after 4 s, cap at 30 s. Use `Bash` to run unit tests covering disconnect and reconnect sequences. Track `reconnectAttempts` in state; reset to 0 on successful open.
|
|
80
|
+
|
|
81
|
+
**Step 3 — Wire to TanStack Query cache invalidation**
|
|
82
|
+
On each normalized event received, call `queryClient.setQueryData(['ticker', symbol], tick)` for optimistic updates or `queryClient.invalidateQueries(['orderbook', symbol])` for full refresh. Use `Grep` to confirm no stale `setInterval` polling remains alongside the new WebSocket feed.
|
|
83
|
+
|
|
84
|
+
#### Example
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
class TradingWebSocket {
|
|
88
|
+
private ws: WebSocket | null = null;
|
|
89
|
+
private reconnectAttempts = 0;
|
|
90
|
+
private readonly MAX_DELAY_MS = 30_000;
|
|
91
|
+
|
|
92
|
+
connect(url: string, onTick: (tick: NormalizedTick) => void): void {
|
|
93
|
+
this.ws = new WebSocket(url);
|
|
94
|
+
|
|
95
|
+
this.ws.onmessage = (event) => {
|
|
96
|
+
const raw = JSON.parse(event.data as string);
|
|
97
|
+
onTick(this.normalize(raw));
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
this.ws.onclose = () => {
|
|
101
|
+
const delay = Math.min(
|
|
102
|
+
1000 * 2 ** this.reconnectAttempts,
|
|
103
|
+
this.MAX_DELAY_MS,
|
|
104
|
+
);
|
|
105
|
+
this.reconnectAttempts += 1;
|
|
106
|
+
setTimeout(() => this.connect(url, onTick), delay);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
this.ws.onopen = () => { this.reconnectAttempts = 0; };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private normalize(raw: unknown): NormalizedTick {
|
|
113
|
+
// map exchange-specific shape to shared interface
|
|
114
|
+
const r = raw as Record<string, unknown>;
|
|
115
|
+
return { symbol: String(r['s']), price: String(r['p']), ts: Date.now() };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### chart-components
|
|
123
|
+
|
|
124
|
+
Financial chart patterns — candlestick, line, and area charts using TradingView Lightweight Charts. Real-time update handlers, zoom, crosshair sync, indicator overlays, and responsive layout with reduced-motion support.
|
|
125
|
+
|
|
126
|
+
#### Workflow
|
|
127
|
+
|
|
128
|
+
**Step 1 — Detect chart library and configure chart instance**
|
|
129
|
+
Use `Grep` to check for `lightweight-charts` or `@tradingview/charting_library` in `package.json`. Initialize with `createChart(container, { autoSize: true, layout: { background: { color: '#0c1419' } } })`. Create a `CandlestickSeries` with green/red up/down colors matching the project palette.
|
|
130
|
+
|
|
131
|
+
**Step 2 — Real-time update handler**
|
|
132
|
+
Subscribe to the normalized WebSocket feed from `realtime-data`. On each tick, call `series.update({ time, open, high, low, close, volume })`. Batch rapid updates with `requestAnimationFrame` to avoid layout thrashing. Use `Read` to verify the container element is stable (not re-mounting on every render).
|
|
133
|
+
|
|
134
|
+
**Step 3 — Responsive layout and reduced-motion**
|
|
135
|
+
Use `Bash` to run `window.matchMedia('(prefers-reduced-motion: reduce)')` check at init time. When true, disable chart animations (`animation: { duration: 0 }`). Add `ResizeObserver` on the container and call `chart.applyOptions({ width, height })` on size change.
|
|
136
|
+
|
|
137
|
+
#### Example
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { createChart, CandlestickSeries } from 'lightweight-charts';
|
|
141
|
+
|
|
142
|
+
function initCandlestickChart(container: HTMLElement): CandlestickSeries {
|
|
143
|
+
const reducedMotion = window.matchMedia(
|
|
144
|
+
'(prefers-reduced-motion: reduce)',
|
|
145
|
+
).matches;
|
|
146
|
+
|
|
147
|
+
const chart = createChart(container, {
|
|
148
|
+
autoSize: true,
|
|
149
|
+
layout: { background: { color: '#0c1419' }, textColor: '#a0aeb8' },
|
|
150
|
+
grid: { vertLines: { color: '#2a3f52' }, horzLines: { color: '#2a3f52' } },
|
|
151
|
+
crosshair: { mode: 1 },
|
|
152
|
+
animation: { duration: reducedMotion ? 0 : 300 },
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const series = chart.addSeries(CandlestickSeries, {
|
|
156
|
+
upColor: '#00d084',
|
|
157
|
+
downColor: '#ff6b6b',
|
|
158
|
+
borderVisible: false,
|
|
159
|
+
wickUpColor: '#00d084',
|
|
160
|
+
wickDownColor: '#ff6b6b',
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
new ResizeObserver(() => chart.applyOptions({ width: container.clientWidth }))
|
|
164
|
+
.observe(container);
|
|
165
|
+
|
|
166
|
+
return series;
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### indicator-library
|
|
173
|
+
|
|
174
|
+
Technical indicator implementations — SMA, EMA, RSI, MACD, Bollinger Bands, VWAP. Streaming calculation patterns that update incrementally on each new tick rather than recomputing the full history.
|
|
175
|
+
|
|
176
|
+
#### Workflow
|
|
177
|
+
|
|
178
|
+
**Step 1 — Select indicators and initialize state**
|
|
179
|
+
Use `Read` on the product spec or existing chart config to identify required indicators. For each, allocate a rolling window buffer sized to the longest period (e.g., 200 for SMA-200). Initialize with historical OHLCV data fetched via REST before the WebSocket feed opens.
|
|
180
|
+
|
|
181
|
+
**Step 2 — Streaming incremental calculation**
|
|
182
|
+
On each new tick from `realtime-data`, push the close price into the rolling buffer and evict the oldest value. Recompute only the current indicator value — not the full series. For RSI, maintain running average gains/losses using Wilder smoothing. Use `Bash` to run unit tests comparing streaming output against a reference batch computation.
|
|
183
|
+
|
|
184
|
+
**Step 3 — Overlay on chart component**
|
|
185
|
+
Create a `LineSeries` on the chart instance from `chart-components` for each indicator. On each streaming update, call `indicatorSeries.update({ time, value })`. Use `Grep` to confirm indicator series are cleaned up (`chart.removeSeries(s)`) when the symbol or timeframe changes to prevent memory leaks.
|
|
186
|
+
|
|
187
|
+
#### Example
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
class StreamingSMA {
|
|
191
|
+
private readonly window: number[] = [];
|
|
192
|
+
|
|
193
|
+
constructor(private readonly period: number) {}
|
|
194
|
+
|
|
195
|
+
update(price: number): number | null {
|
|
196
|
+
this.window.push(price);
|
|
197
|
+
if (this.window.length > this.period) {
|
|
198
|
+
this.window.shift();
|
|
199
|
+
}
|
|
200
|
+
if (this.window.length < this.period) return null;
|
|
201
|
+
const sum = this.window.reduce((acc, v) => acc + v, 0);
|
|
202
|
+
return sum / this.period;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
class StreamingEMA {
|
|
207
|
+
private ema: number | null = null;
|
|
208
|
+
private readonly k: number;
|
|
209
|
+
|
|
210
|
+
constructor(private readonly period: number) {
|
|
211
|
+
this.k = 2 / (period + 1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
update(price: number): number | null {
|
|
215
|
+
this.ema = this.ema === null
|
|
216
|
+
? price
|
|
217
|
+
: price * this.k + this.ema * (1 - this.k);
|
|
218
|
+
return this.ema;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
### trade-logic
|
|
226
|
+
|
|
227
|
+
Trading logic preservation and reasoning — entry/exit spec management, indicator parameter registry, strategy state tracking, and backtest result linkage. Prevents the #1 trading bot failure: AI sessions overwriting working logic without understanding it.
|
|
228
|
+
|
|
229
|
+
#### Workflow
|
|
230
|
+
|
|
231
|
+
**Step 1 — Load trading logic context**
|
|
232
|
+
Check if `logic-guardian` (L2) has a manifest loaded. If `.rune/logic-manifest.json` exists, read it and extract trading-specific components (ENTRY_LOGIC, EXIT_LOGIC, FILTER, INDICATOR). If no manifest exists, trigger `logic-guardian` Phase 3 to generate one with trading-aware scanning.
|
|
233
|
+
|
|
234
|
+
Trading-specific file patterns to scan:
|
|
235
|
+
- `**/scenarios/**`, `**/signals/**`, `**/strategies/**` — entry/exit logic
|
|
236
|
+
- `**/trailing/**`, `**/exit/**`, `**/stoploss/**` — exit engine components
|
|
237
|
+
- `**/indicators/**`, `**/core/indicators*` — technical indicator implementations
|
|
238
|
+
- `**/backtest/**`, `**/engine*` — backtesting mirrors of production logic
|
|
239
|
+
- `**/config/settings*`, `**/config/token*` — parameter source of truth
|
|
240
|
+
|
|
241
|
+
**Step 2 — Build trading logic spec**
|
|
242
|
+
For each trading component, extract a structured spec:
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
COMPONENT: RSI Entry Detector
|
|
246
|
+
TYPE: ENTRY_LOGIC
|
|
247
|
+
STATUS: ACTIVE (production)
|
|
248
|
+
LAYERS: [which layer in the trading pipeline this belongs to]
|
|
249
|
+
|
|
250
|
+
ENTRY CONDITIONS:
|
|
251
|
+
1. TrendPass ticket exists with available fires
|
|
252
|
+
2. RSI_MA crosses threshold (65 LONG / 35 SHORT)
|
|
253
|
+
3. Previous RSI in entry zone (30-55 LONG / 45-70 SHORT)
|
|
254
|
+
4. RSI crosses RSI_MA + 40% TF filter + EMA filter
|
|
255
|
+
|
|
256
|
+
PARAMETERS:
|
|
257
|
+
- rsi_period: 7 (source: settings.py)
|
|
258
|
+
- challenge_threshold_long: 65 (source: settings.py)
|
|
259
|
+
- entry_zone_long: [30, 55] (source: settings.py)
|
|
260
|
+
|
|
261
|
+
DEPENDENCIES: trend_pass.tracker, core.indicators
|
|
262
|
+
MIRROR: backtest/engine.py (must stay in sync with production)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Step 3 — Enforce production-backtest sync**
|
|
266
|
+
For trading bots, production logic and backtest logic MUST be mirrors. Scan for:
|
|
267
|
+
- Production file: `src/worker/production_worker.py` or equivalent
|
|
268
|
+
- Backtest file: `backtest/engine.py` or equivalent
|
|
269
|
+
- Compare entry/exit function signatures and conditional branches
|
|
270
|
+
- Flag any divergence: "Production uses condition X but backtest doesn't"
|
|
271
|
+
|
|
272
|
+
**Step 4 — Parameter registry**
|
|
273
|
+
Build a parameter registry linking every configurable threshold to its source:
|
|
274
|
+
- Single source of truth file (e.g., `settings.py`)
|
|
275
|
+
- Per-token overrides (e.g., `token_config.py`, `final_config.json`)
|
|
276
|
+
- Scan for hardcoded magic numbers in logic files that should be in config
|
|
277
|
+
- Flag: "Hardcoded value 65 in detect.py:L42 — should reference settings.CHALLENGE_THRESHOLD_LONG"
|
|
278
|
+
|
|
279
|
+
**Step 5 — Strategy state machine documentation**
|
|
280
|
+
If the trading logic uses a multi-step state machine (e.g., 3-step RSI entry):
|
|
281
|
+
- Document each state and its transition conditions
|
|
282
|
+
- Generate a state diagram in text format
|
|
283
|
+
- Save to manifest as `state_machine` field on the component
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
State Machine: RSI Entry
|
|
287
|
+
[IDLE] --ticket_exists--> [STEP1_CHALLENGE]
|
|
288
|
+
[STEP1_CHALLENGE] --rsi_ma_crosses_threshold--> [STEP2_ZONE_CHECK]
|
|
289
|
+
[STEP2_ZONE_CHECK] --prev_rsi_in_zone--> [STEP3_ENTRY_POINT]
|
|
290
|
+
[STEP3_ENTRY_POINT] --rsi_crosses_rsi_ma + filters--> [SIGNAL_EMITTED]
|
|
291
|
+
[any_step] --ticket_expired--> [IDLE]
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Step 6 — Backtest result linkage**
|
|
295
|
+
Link logic components to their backtest performance:
|
|
296
|
+
- Scan `backtest/scan_results/` or equivalent for result files
|
|
297
|
+
- Associate each strategy variant with its performance metrics
|
|
298
|
+
- Record in manifest: "RSI Entry v5 with EMA Follow: $20,445 over 6mo backtest"
|
|
299
|
+
- Flag if logic was modified AFTER the latest backtest: "Logic changed since last backtest — results may be invalid"
|
|
300
|
+
|
|
301
|
+
#### Example
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
# trade-logic generates this spec from code analysis:
|
|
305
|
+
# COMPONENT: EMA Follow Exit
|
|
306
|
+
# TYPE: EXIT_LOGIC
|
|
307
|
+
# STATUS: ACTIVE
|
|
308
|
+
# BUG_HISTORY: 2026-02-22 fixed wick detection (was using close, now uses candle_low/high)
|
|
309
|
+
#
|
|
310
|
+
# EXIT CONDITION:
|
|
311
|
+
# if candle_wick crosses EMA120 -> exit position
|
|
312
|
+
# (NOT candle_close — this was the V4 bug)
|
|
313
|
+
#
|
|
314
|
+
# PARAMETERS:
|
|
315
|
+
# ema_period: 120 (source: settings.py)
|
|
316
|
+
# use_wick: True (source: settings.py, changed from False in V4)
|
|
317
|
+
#
|
|
318
|
+
# MIRROR: backtest/exit_checker.py:check_ema_follow()
|
|
319
|
+
# BACKTEST: $22,481 (x2.0 adaptive variant, validated 2026-02-22)
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
### experiment-loop
|
|
325
|
+
|
|
326
|
+
Scientific method for trading strategy development — hypothesize → implement → backtest → analyze → refine. Prevents the #1 strategy development failure: changing parameters randomly without tracking what was tested, what worked, and why.
|
|
327
|
+
|
|
328
|
+
#### Workflow
|
|
329
|
+
|
|
330
|
+
**Step 1 — Define hypothesis**
|
|
331
|
+
Every strategy change starts as a falsifiable hypothesis:
|
|
332
|
+
```
|
|
333
|
+
HYPOTHESIS: [What you believe]
|
|
334
|
+
EVIDENCE: [Why you believe it — chart observation, backtest anomaly, market regime]
|
|
335
|
+
TEST: [How to verify — specific backtest config, date range, token set]
|
|
336
|
+
SUCCESS CRITERIA: [Measurable threshold — "win rate > 55% AND max drawdown < 15%"]
|
|
337
|
+
FAILURE CRITERIA: [When to reject — "win rate < 45% OR drawdown > 25%"]
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Check `.rune/experiments/` for prior experiments on the same component. If a similar hypothesis was already tested and rejected, flag it: "This was tested in experiment #12 and rejected because [reason]. Proceed anyway?"
|
|
341
|
+
|
|
342
|
+
**Step 2 — Implement variant**
|
|
343
|
+
Create the strategy variant in an isolated branch or config:
|
|
344
|
+
- Use `Grep` to find the parameter or logic being changed
|
|
345
|
+
- Create a named variant (e.g., `rsi_entry_v6_longer_period`) — NEVER modify the production logic directly
|
|
346
|
+
- Document the exact change: "Changed RSI period from 7 to 14, challenge threshold from 65 to 60"
|
|
347
|
+
- If logic change (not just parameter): ensure backtest engine mirrors the change (production-backtest sync from `trade-logic`)
|
|
348
|
+
|
|
349
|
+
**Step 3 — Run backtest**
|
|
350
|
+
Execute backtest against the defined test conditions:
|
|
351
|
+
- Use `Bash` to run the backtest command with the variant config
|
|
352
|
+
- Capture results: total PnL, win rate, max drawdown, Sharpe ratio, number of trades
|
|
353
|
+
- Compare against the control (current production parameters)
|
|
354
|
+
- Record execution time and date range
|
|
355
|
+
|
|
356
|
+
**Step 4 — Analyze results**
|
|
357
|
+
Structured analysis against success/failure criteria:
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
EXPERIMENT #14: RSI Period 14 vs 7
|
|
361
|
+
STATUS: REJECTED ❌
|
|
362
|
+
|
|
363
|
+
RESULTS:
|
|
364
|
+
| Metric | Control (v5) | Variant (v6) | Δ |
|
|
365
|
+
|---------------|-------------|-------------|---------|
|
|
366
|
+
| Total PnL | $20,445 | $18,200 | -$2,245 |
|
|
367
|
+
| Win Rate | 58.3% | 52.1% | -6.2% |
|
|
368
|
+
| Max Drawdown | 12.4% | 14.8% | +2.4% |
|
|
369
|
+
| Sharpe Ratio | 1.42 | 1.18 | -0.24 |
|
|
370
|
+
| Trade Count | 156 | 89 | -67 |
|
|
371
|
+
|
|
372
|
+
CONCLUSION: Longer RSI period reduces signal frequency by 43% without
|
|
373
|
+
improving quality. Win rate dropped below 55% threshold. REJECTED.
|
|
374
|
+
|
|
375
|
+
INSIGHT: RSI 7 captures mean-reversion signals faster on 15m timeframe.
|
|
376
|
+
Longer periods may suit 4H+ timeframes (not tested — add to backlog).
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**Step 5 — Record and route**
|
|
380
|
+
Save experiment to `.rune/experiments/<number>-<name>.md`:
|
|
381
|
+
- If **ACCEPTED**: update production parameters → run `trade-logic` to sync manifest → commit
|
|
382
|
+
- If **REJECTED**: record conclusion and insight → add derived hypotheses to backlog
|
|
383
|
+
- If **INCONCLUSIVE**: define additional test conditions or longer date range → re-run
|
|
384
|
+
- Link to the experiment from `trade-logic` manifest: "RSI Entry v5: validated by experiment #14"
|
|
385
|
+
|
|
386
|
+
Update experiment index `.rune/experiments/index.md`:
|
|
387
|
+
```
|
|
388
|
+
| # | Hypothesis | Component | Status | Key Metric | Date |
|
|
389
|
+
|---|-----------|-----------|--------|------------|------|
|
|
390
|
+
| 14 | RSI 14 > RSI 7 | rsi_entry | ❌ Rejected | WR 52% < 55% | 2025-03-15 |
|
|
391
|
+
| 13 | EMA 120 wick exit | ema_follow | ✅ Accepted | PnL +$2,036 | 2025-03-10 |
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
#### Example
|
|
395
|
+
|
|
396
|
+
```python
|
|
397
|
+
# Experiment runner pattern
|
|
398
|
+
from dataclasses import dataclass
|
|
399
|
+
from decimal import Decimal
|
|
400
|
+
|
|
401
|
+
@dataclass(frozen=True)
|
|
402
|
+
class ExperimentConfig:
|
|
403
|
+
name: str
|
|
404
|
+
hypothesis: str
|
|
405
|
+
variant_params: dict[str, str | int | float]
|
|
406
|
+
control_params: dict[str, str | int | float]
|
|
407
|
+
date_range: tuple[str, str]
|
|
408
|
+
tokens: list[str]
|
|
409
|
+
success_criteria: dict[str, tuple[str, float]] # metric: (operator, threshold)
|
|
410
|
+
|
|
411
|
+
@dataclass(frozen=True)
|
|
412
|
+
class ExperimentResult:
|
|
413
|
+
config: ExperimentConfig
|
|
414
|
+
control_metrics: dict[str, Decimal]
|
|
415
|
+
variant_metrics: dict[str, Decimal]
|
|
416
|
+
status: str # 'accepted' | 'rejected' | 'inconclusive'
|
|
417
|
+
conclusion: str
|
|
418
|
+
insights: list[str]
|
|
419
|
+
|
|
420
|
+
def evaluate_experiment(result: ExperimentResult) -> str:
|
|
421
|
+
"""Evaluate variant against success criteria."""
|
|
422
|
+
for metric, (op, threshold) in result.config.success_criteria.items():
|
|
423
|
+
variant_val = result.variant_metrics.get(metric, Decimal('0'))
|
|
424
|
+
if op == '>' and variant_val <= Decimal(str(threshold)):
|
|
425
|
+
return 'rejected'
|
|
426
|
+
if op == '<' and variant_val >= Decimal(str(threshold)):
|
|
427
|
+
return 'rejected'
|
|
428
|
+
return 'accepted'
|
|
429
|
+
|
|
430
|
+
# Usage:
|
|
431
|
+
# config = ExperimentConfig(
|
|
432
|
+
# name="rsi_period_14",
|
|
433
|
+
# hypothesis="RSI 14 captures better signals than RSI 7 on 15m",
|
|
434
|
+
# variant_params={"rsi_period": 14, "challenge_threshold": 60},
|
|
435
|
+
# control_params={"rsi_period": 7, "challenge_threshold": 65},
|
|
436
|
+
# date_range=("2024-09-01", "2025-03-01"),
|
|
437
|
+
# tokens=["BTCUSDT", "ETHUSDT", "SOLUSDT"],
|
|
438
|
+
# success_criteria={"win_rate": (">", 0.55), "max_drawdown": ("<", 0.15)},
|
|
439
|
+
# )
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### quant-analysis
|
|
443
|
+
|
|
444
|
+
Quantitative analysis patterns — portfolio metrics, risk calculations, statistical edge detection, Monte Carlo simulation, and position sizing models.
|
|
445
|
+
|
|
446
|
+
#### Workflow
|
|
447
|
+
|
|
448
|
+
**Step 1 — Define analysis scope**
|
|
449
|
+
Determine what the user needs: portfolio-level metrics (Sharpe, Sortino, max drawdown, VaR), strategy-level analysis (win rate, profit factor, expectancy, risk-of-ruin), or position sizing (Kelly criterion, fixed fractional, volatility-adjusted). Load trade history from data source (CSV, database query, API response).
|
|
450
|
+
|
|
451
|
+
**Step 2 — Calculate core metrics**
|
|
452
|
+
For portfolio analysis:
|
|
453
|
+
- **Sharpe Ratio**: (mean return - risk-free rate) / std(returns). Annualize with √252.
|
|
454
|
+
- **Sortino Ratio**: (mean return - risk-free rate) / downside_std. Only penalizes downside volatility.
|
|
455
|
+
- **Max Drawdown**: Largest peak-to-trough decline. Include recovery time.
|
|
456
|
+
- **Value at Risk (VaR)**: 95th/99th percentile loss using historical simulation or parametric method.
|
|
457
|
+
- **Calmar Ratio**: Annualized return / max drawdown. > 1.0 = good risk-adjusted return.
|
|
458
|
+
|
|
459
|
+
For strategy analysis:
|
|
460
|
+
- **Expectancy**: (win_rate × avg_win) - (loss_rate × avg_loss). Must be positive.
|
|
461
|
+
- **Profit Factor**: gross_profit / gross_loss. > 1.5 = viable, > 2.0 = strong.
|
|
462
|
+
- **Risk of Ruin**: probability of losing X% of capital given win rate and risk per trade.
|
|
463
|
+
|
|
464
|
+
**Step 3 — Monte Carlo simulation**
|
|
465
|
+
Run 10,000 random resamples of the trade sequence to estimate:
|
|
466
|
+
- Probability of reaching profit target within N trades
|
|
467
|
+
- Confidence interval for max drawdown (95th percentile)
|
|
468
|
+
- Optimal position size that maximizes geometric growth (Kelly fraction)
|
|
469
|
+
|
|
470
|
+
Emit results as structured data + visualization-ready format for `chart-components`.
|
|
471
|
+
|
|
472
|
+
**Step 4 — Position sizing recommendation**
|
|
473
|
+
Based on Monte Carlo results, recommend:
|
|
474
|
+
- **Conservative**: Half-Kelly (50% of optimal Kelly fraction)
|
|
475
|
+
- **Moderate**: Full Kelly
|
|
476
|
+
- **Aggressive**: 1.5x Kelly (with warning about increased ruin probability)
|
|
477
|
+
|
|
478
|
+
Save analysis to `.rune/trading/quant-analysis-<date>.md`.
|
|
479
|
+
|
|
480
|
+
#### Example
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
import Decimal from 'decimal.js';
|
|
484
|
+
|
|
485
|
+
interface TradeResult {
|
|
486
|
+
pnl: Decimal;
|
|
487
|
+
entryPrice: Decimal;
|
|
488
|
+
exitPrice: Decimal;
|
|
489
|
+
size: Decimal;
|
|
490
|
+
duration: number; // minutes
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
interface QuantMetrics {
|
|
494
|
+
totalTrades: number;
|
|
495
|
+
winRate: Decimal;
|
|
496
|
+
profitFactor: Decimal;
|
|
497
|
+
expectancy: Decimal;
|
|
498
|
+
sharpeRatio: Decimal;
|
|
499
|
+
sortinoRatio: Decimal;
|
|
500
|
+
maxDrawdown: Decimal;
|
|
501
|
+
maxDrawdownDuration: number;
|
|
502
|
+
calmarRatio: Decimal;
|
|
503
|
+
valueAtRisk95: Decimal;
|
|
504
|
+
kellyFraction: Decimal;
|
|
505
|
+
riskOfRuin: Decimal;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function calculateExpectancy(trades: TradeResult[]): Decimal {
|
|
509
|
+
const wins = trades.filter(t => t.pnl.gt(0));
|
|
510
|
+
const losses = trades.filter(t => t.pnl.lte(0));
|
|
511
|
+
const winRate = new Decimal(wins.length).div(trades.length);
|
|
512
|
+
const avgWin = wins.length > 0
|
|
513
|
+
? wins.reduce((sum, t) => sum.plus(t.pnl), new Decimal(0)).div(wins.length)
|
|
514
|
+
: new Decimal(0);
|
|
515
|
+
const avgLoss = losses.length > 0
|
|
516
|
+
? losses.reduce((sum, t) => sum.plus(t.pnl.abs()), new Decimal(0)).div(losses.length)
|
|
517
|
+
: new Decimal(0);
|
|
518
|
+
return winRate.mul(avgWin).minus(new Decimal(1).minus(winRate).mul(avgLoss));
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function kellyFraction(winRate: Decimal, avgWinLossRatio: Decimal): Decimal {
|
|
522
|
+
// Kelly: f* = (p * b - q) / b where p=winRate, q=1-p, b=avgWin/avgLoss
|
|
523
|
+
const q = new Decimal(1).minus(winRate);
|
|
524
|
+
return winRate.mul(avgWinLossRatio).minus(q).div(avgWinLossRatio);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Monte Carlo: resample trades 10,000 times
|
|
528
|
+
function monteCarloDrawdown(trades: TradeResult[], simulations = 10000): Decimal {
|
|
529
|
+
const drawdowns: Decimal[] = [];
|
|
530
|
+
for (let i = 0; i < simulations; i++) {
|
|
531
|
+
const shuffled = [...trades].sort(() => Math.random() - 0.5);
|
|
532
|
+
let peak = new Decimal(0), maxDd = new Decimal(0), equity = new Decimal(0);
|
|
533
|
+
for (const t of shuffled) {
|
|
534
|
+
equity = equity.plus(t.pnl);
|
|
535
|
+
if (equity.gt(peak)) peak = equity;
|
|
536
|
+
const dd = peak.minus(equity).div(peak.gt(0) ? peak : new Decimal(1));
|
|
537
|
+
if (dd.gt(maxDd)) maxDd = dd;
|
|
538
|
+
}
|
|
539
|
+
drawdowns.push(maxDd);
|
|
540
|
+
}
|
|
541
|
+
drawdowns.sort((a, b) => a.cmp(b));
|
|
542
|
+
return drawdowns[Math.floor(simulations * 0.95)]; // 95th percentile
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## Connections
|
|
549
|
+
|
|
550
|
+
```
|
|
551
|
+
Calls → @rune/ui (L4): chart component styling, color tokens, responsive layout
|
|
552
|
+
Called By ← cook (L1): when trading project detected
|
|
553
|
+
Called By ← launch (L1): pre-flight check for financial dashboards
|
|
554
|
+
Called By ← logic-guardian (L2): when project is classified as trading domain
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
## Tech Stack Support
|
|
558
|
+
|
|
559
|
+
| Framework | Library | Notes |
|
|
560
|
+
|-----------|---------|-------|
|
|
561
|
+
| React 19 / Vite | Lightweight Charts 5.x | Preferred for custom dashboards |
|
|
562
|
+
| React 19 / Next.js | TradingView Charting Library | For advanced trading terminals |
|
|
563
|
+
| Any | Decimal.js 10.x | Required for all money arithmetic |
|
|
564
|
+
| Any | ws / native WebSocket | Auto-reconnect via `realtime-data` skill |
|
|
565
|
+
| React 19 | TanStack Query v5 | WebSocket → cache invalidation bridge |
|
|
566
|
+
| Any | date-fns-tz | Timezone-safe candle timestamp handling |
|
|
567
|
+
|
|
568
|
+
## Constraints
|
|
569
|
+
|
|
570
|
+
1. MUST use `Decimal` (decimal.js) or `BigInt` for ALL money calculations — NEVER JavaScript `number` floats on price, quantity, fee, or PnL fields.
|
|
571
|
+
2. MUST implement WebSocket auto-reconnect with exponential backoff — silent reconnect failures will silently stale the UI with no error signal.
|
|
572
|
+
3. MUST handle `prefers-reduced-motion` for all chart animations — financial dashboards are used in low-stimulation accessibility contexts.
|
|
573
|
+
4. MUST NOT store financial data (positions, balances, order history) in `localStorage` — use secure in-memory state management with explicit session boundaries.
|
|
574
|
+
|
|
575
|
+
## Sharp Edges
|
|
576
|
+
|
|
577
|
+
| Failure Mode | Severity | Mitigation |
|
|
578
|
+
|---|---|---|
|
|
579
|
+
| Float arithmetic on price (`0.1 + 0.2 !== 0.3`) silently corrupts PnL | HIGH | Enforce Decimal.js at parse boundary; lint rule banning `*`, `+`, `-` on raw number price fields |
|
|
580
|
+
| WebSocket silently stops receiving after network blip with no reconnect | HIGH | Always attach `onclose` handler; test disconnect/reconnect in CI with a mock server |
|
|
581
|
+
| Chart series not removed on symbol change causes memory leak and ghost lines | HIGH | Track series refs; call `chart.removeSeries(s)` in cleanup / `useEffect` return |
|
|
582
|
+
| Indicator computed on float prices accumulates rounding drift over 1000+ ticks | MEDIUM | Feed Decimal-converted `toNumber()` only at the indicator boundary; document precision loss |
|
|
583
|
+
| `localStorage` used for auth token or balance cache exposes data to XSS | HIGH | Use `httpOnly` cookies or in-memory store; audit with `Grep pattern="localStorage" glob="**/*.ts"` |
|
|
584
|
+
| Candlestick timestamps in local timezone cause gaps on DST transitions | MEDIUM | Normalize all timestamps to UTC unix seconds at the WebSocket boundary |
|
|
585
|
+
|
|
586
|
+
## Done When
|
|
587
|
+
|
|
588
|
+
- All price/quantity/fee fields are wrapped in `Decimal` with no raw float arithmetic reachable by Grep
|
|
589
|
+
- WebSocket reconnects automatically after 5-second disconnect in manual or automated test
|
|
590
|
+
- Chart renders candlesticks and at least one indicator overlay without layout shift on resize
|
|
591
|
+
- Streaming indicator values match reference batch output within floating-point display tolerance
|
|
592
|
+
- `prefers-reduced-motion` disables chart animations (verified via browser devtools emulation)
|
|
593
|
+
- No `localStorage` usage for financial data (confirmed by Grep audit)
|
|
594
|
+
|
|
595
|
+
## Cost Profile
|
|
596
|
+
|
|
597
|
+
~2 000–4 000 tokens per skill activation. `sonnet` default for code generation; `haiku` for Grep/file-scan steps; `opus` if regulatory compliance or security audit context is detected. Full pack activation (all four skills) runs ~10 000–14 000 tokens end-to-end.
|