@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.
Files changed (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +357 -0
  3. package/agents/.gitkeep +0 -0
  4. package/agents/architect.md +29 -0
  5. package/agents/asset-creator.md +11 -0
  6. package/agents/audit.md +11 -0
  7. package/agents/autopsy.md +11 -0
  8. package/agents/brainstorm.md +11 -0
  9. package/agents/browser-pilot.md +11 -0
  10. package/agents/coder.md +29 -0
  11. package/agents/completion-gate.md +11 -0
  12. package/agents/constraint-check.md +11 -0
  13. package/agents/context-engine.md +11 -0
  14. package/agents/cook.md +11 -0
  15. package/agents/db.md +11 -0
  16. package/agents/debug.md +11 -0
  17. package/agents/dependency-doctor.md +11 -0
  18. package/agents/deploy.md +11 -0
  19. package/agents/design.md +11 -0
  20. package/agents/docs-seeker.md +11 -0
  21. package/agents/fix.md +11 -0
  22. package/agents/hallucination-guard.md +11 -0
  23. package/agents/incident.md +11 -0
  24. package/agents/integrity-check.md +11 -0
  25. package/agents/journal.md +11 -0
  26. package/agents/launch.md +11 -0
  27. package/agents/logic-guardian.md +11 -0
  28. package/agents/marketing.md +11 -0
  29. package/agents/onboard.md +11 -0
  30. package/agents/perf.md +11 -0
  31. package/agents/plan.md +11 -0
  32. package/agents/preflight.md +11 -0
  33. package/agents/problem-solver.md +11 -0
  34. package/agents/rescue.md +11 -0
  35. package/agents/research.md +11 -0
  36. package/agents/researcher.md +29 -0
  37. package/agents/review-intake.md +11 -0
  38. package/agents/review.md +11 -0
  39. package/agents/reviewer.md +28 -0
  40. package/agents/safeguard.md +11 -0
  41. package/agents/sast.md +11 -0
  42. package/agents/scanner.md +28 -0
  43. package/agents/scope-guard.md +11 -0
  44. package/agents/scout.md +11 -0
  45. package/agents/sentinel.md +11 -0
  46. package/agents/sequential-thinking.md +11 -0
  47. package/agents/session-bridge.md +11 -0
  48. package/agents/skill-forge.md +11 -0
  49. package/agents/skill-router.md +11 -0
  50. package/agents/surgeon.md +11 -0
  51. package/agents/team.md +11 -0
  52. package/agents/test.md +11 -0
  53. package/agents/trend-scout.md +11 -0
  54. package/agents/verification.md +11 -0
  55. package/agents/video-creator.md +11 -0
  56. package/agents/watchdog.md +11 -0
  57. package/agents/worktree.md +11 -0
  58. package/commands/.gitkeep +0 -0
  59. package/commands/rune.md +168 -0
  60. package/compiler/__tests__/openclaw-adapter.test.js +140 -0
  61. package/compiler/__tests__/parser.test.js +55 -0
  62. package/compiler/adapters/antigravity.js +59 -0
  63. package/compiler/adapters/claude.js +37 -0
  64. package/compiler/adapters/cursor.js +67 -0
  65. package/compiler/adapters/generic.js +60 -0
  66. package/compiler/adapters/index.js +45 -0
  67. package/compiler/adapters/openclaw.js +150 -0
  68. package/compiler/adapters/windsurf.js +60 -0
  69. package/compiler/bin/rune.js +288 -0
  70. package/compiler/doctor.js +153 -0
  71. package/compiler/emitter.js +240 -0
  72. package/compiler/parser.js +208 -0
  73. package/compiler/transformer.js +69 -0
  74. package/compiler/transforms/branding.js +27 -0
  75. package/compiler/transforms/cross-references.js +29 -0
  76. package/compiler/transforms/frontmatter.js +38 -0
  77. package/compiler/transforms/hooks.js +68 -0
  78. package/compiler/transforms/subagents.js +36 -0
  79. package/compiler/transforms/tool-names.js +60 -0
  80. package/contexts/dev.md +34 -0
  81. package/contexts/research.md +43 -0
  82. package/contexts/review.md +55 -0
  83. package/extensions/ai-ml/PACK.md +517 -0
  84. package/extensions/analytics/PACK.md +557 -0
  85. package/extensions/backend/PACK.md +678 -0
  86. package/extensions/chrome-ext/PACK.md +995 -0
  87. package/extensions/content/PACK.md +381 -0
  88. package/extensions/devops/PACK.md +520 -0
  89. package/extensions/ecommerce/PACK.md +280 -0
  90. package/extensions/gamedev/PACK.md +393 -0
  91. package/extensions/mobile/PACK.md +273 -0
  92. package/extensions/saas/PACK.md +805 -0
  93. package/extensions/security/PACK.md +536 -0
  94. package/extensions/trading/PACK.md +597 -0
  95. package/extensions/ui/PACK.md +947 -0
  96. package/package.json +47 -0
  97. package/skills/.gitkeep +0 -0
  98. package/skills/adversary/SKILL.md +271 -0
  99. package/skills/asset-creator/SKILL.md +157 -0
  100. package/skills/audit/SKILL.md +466 -0
  101. package/skills/autopsy/SKILL.md +200 -0
  102. package/skills/ba/SKILL.md +279 -0
  103. package/skills/brainstorm/SKILL.md +266 -0
  104. package/skills/browser-pilot/SKILL.md +168 -0
  105. package/skills/completion-gate/SKILL.md +151 -0
  106. package/skills/constraint-check/SKILL.md +165 -0
  107. package/skills/context-engine/SKILL.md +176 -0
  108. package/skills/cook/SKILL.md +636 -0
  109. package/skills/db/SKILL.md +256 -0
  110. package/skills/debug/SKILL.md +240 -0
  111. package/skills/dependency-doctor/SKILL.md +235 -0
  112. package/skills/deploy/SKILL.md +174 -0
  113. package/skills/design/DESIGN-REFERENCE.md +365 -0
  114. package/skills/design/SKILL.md +462 -0
  115. package/skills/doc-processor/SKILL.md +254 -0
  116. package/skills/docs/SKILL.md +336 -0
  117. package/skills/docs-seeker/SKILL.md +166 -0
  118. package/skills/fix/SKILL.md +192 -0
  119. package/skills/git/SKILL.md +285 -0
  120. package/skills/hallucination-guard/SKILL.md +204 -0
  121. package/skills/incident/SKILL.md +241 -0
  122. package/skills/integrity-check/SKILL.md +169 -0
  123. package/skills/journal/SKILL.md +190 -0
  124. package/skills/launch/SKILL.md +330 -0
  125. package/skills/logic-guardian/SKILL.md +240 -0
  126. package/skills/marketing/SKILL.md +229 -0
  127. package/skills/mcp-builder/SKILL.md +311 -0
  128. package/skills/onboard/SKILL.md +298 -0
  129. package/skills/perf/SKILL.md +297 -0
  130. package/skills/plan/SKILL.md +520 -0
  131. package/skills/preflight/SKILL.md +231 -0
  132. package/skills/problem-solver/SKILL.md +284 -0
  133. package/skills/rescue/SKILL.md +434 -0
  134. package/skills/research/SKILL.md +122 -0
  135. package/skills/review/SKILL.md +354 -0
  136. package/skills/review-intake/SKILL.md +222 -0
  137. package/skills/safeguard/SKILL.md +188 -0
  138. package/skills/sast/SKILL.md +190 -0
  139. package/skills/scaffold/SKILL.md +276 -0
  140. package/skills/scope-guard/SKILL.md +150 -0
  141. package/skills/scout/SKILL.md +232 -0
  142. package/skills/sentinel/SKILL.md +320 -0
  143. package/skills/sentinel-env/SKILL.md +226 -0
  144. package/skills/sequential-thinking/SKILL.md +234 -0
  145. package/skills/session-bridge/SKILL.md +287 -0
  146. package/skills/skill-forge/SKILL.md +317 -0
  147. package/skills/skill-router/SKILL.md +267 -0
  148. package/skills/surgeon/SKILL.md +203 -0
  149. package/skills/team/SKILL.md +397 -0
  150. package/skills/test/SKILL.md +271 -0
  151. package/skills/trend-scout/SKILL.md +145 -0
  152. package/skills/verification/SKILL.md +201 -0
  153. package/skills/video-creator/SKILL.md +201 -0
  154. package/skills/watchdog/SKILL.md +166 -0
  155. 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.