bandkit 1.0.2 → 1.0.3
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/README.md +27 -3
- package/dist/BotDecisionPanel.d.ts +7 -0
- package/dist/BotDecisionPanel.d.ts.map +1 -0
- package/dist/BotDecisionPanel.js +124 -0
- package/dist/BotDecisionPanel.js.map +1 -0
- package/dist/decisionEngine.d.ts +41 -0
- package/dist/decisionEngine.d.ts.map +1 -0
- package/dist/decisionEngine.js +166 -0
- package/dist/decisionEngine.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/useBotDecision.d.ts +32 -0
- package/dist/useBotDecision.d.ts.map +1 -0
- package/dist/useBotDecision.js +104 -0
- package/dist/useBotDecision.js.map +1 -0
- package/dist/useCoinbaseTicker.d.ts +28 -0
- package/dist/useCoinbaseTicker.d.ts.map +1 -0
- package/dist/useCoinbaseTicker.js +87 -0
- package/dist/useCoinbaseTicker.js.map +1 -0
- package/package.json +4 -2
- package/scripts/test-decision-stream.cjs +417 -0
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ The package ships:
|
|
|
10
10
|
- Live market widgets: `<OrderBookWidget />`, `<TradeTape />`
|
|
11
11
|
- On-chain feed: `<ContractEventFeed />` (watches your deployed strategy contract's events)
|
|
12
12
|
- Context badges: `<RegimeBadge />` (volatility regime), `<GasBadge />` (mainnet gas price)
|
|
13
|
+
- Decision proxy: `<BotDecisionPanel />` — live BUY/SELL/WAIT/STAND-DOWN preview from Binance + Coinbase data (preview only, not the on-chain bot)
|
|
13
14
|
- A dark UI throughout
|
|
14
15
|
|
|
15
16
|
It does **not** ship trading strategy logic, signal generation, risk management, or the off-chain trade-execution daemon — those are out of scope.
|
|
@@ -47,19 +48,23 @@ max-width 1100px, gap 20px, system font stack:
|
|
|
47
48
|
│ │
|
|
48
49
|
├──────────────────────┬──────────────────────────────┤
|
|
49
50
|
│ │ │
|
|
50
|
-
│ <BandPanel /> │ <
|
|
51
|
+
│ <BandPanel /> │ <BotDecisionPanel /> │ ← two-column split
|
|
51
52
|
│ (the bot UI: it │ │ on >=900px wide,
|
|
52
53
|
│ has its own deploy,├──────────────────────────────┤ stacked below
|
|
53
|
-
│ fund, start, stop, │
|
|
54
|
+
│ fund, start, stop, │ <OrderBookWidget /> │
|
|
54
55
|
│ withdraw buttons. │ │
|
|
55
56
|
│ Default title is ├──────────────────────────────┤
|
|
56
|
-
│ "Get started") │
|
|
57
|
+
│ "Get started") │ <TradeTape /> │
|
|
58
|
+
│ │ │
|
|
59
|
+
│ ├──────────────────────────────┤
|
|
60
|
+
│ │ <ContractEventFeed │
|
|
57
61
|
│ │ strategyContractAddress= │
|
|
58
62
|
│ │ {deployedAddress} /> │
|
|
59
63
|
└──────────────────────┴──────────────────────────────┘
|
|
60
64
|
|
|
61
65
|
Components to import from bandkit:
|
|
62
66
|
- BandPanel (the main staged UI)
|
|
67
|
+
- BotDecisionPanel (live BUY/SELL/WAIT decision preview — top of right column)
|
|
63
68
|
- OrderBookWidget (live Binance ETH/USDT depth)
|
|
64
69
|
- TradeTape (live recent trades)
|
|
65
70
|
- ContractEventFeed (on-chain events from the deployed strategy)
|
|
@@ -224,6 +229,25 @@ type RegimeBadgeProps = {
|
|
|
224
229
|
};
|
|
225
230
|
```
|
|
226
231
|
|
|
232
|
+
### `<BotDecisionPanel />`
|
|
233
|
+
|
|
234
|
+
Live decision proxy. Combines Binance order book imbalance + recent trade flow + volatility regime + Coinbase cross-venue divergence into a `BUY` / `SELL` / `WAIT` / `STAND DOWN` decision with a 0–100 confidence score and human-readable reasons. **Preview only — does not execute trades.**
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
type BotDecisionPanelProps = {
|
|
238
|
+
className?: string;
|
|
239
|
+
title?: string; // default: "Bot Decision (live)"
|
|
240
|
+
evaluationIntervalMs?: number; // default: 2000
|
|
241
|
+
flowWindowMs?: number; // default: 60000
|
|
242
|
+
historyLimit?: number; // default: 30
|
|
243
|
+
thresholds?: Partial<DecisionThresholds>; // override imbalance/flow/spread/divergence/depth cutoffs
|
|
244
|
+
enabled?: boolean;
|
|
245
|
+
// plus sub-options: orderBook, trades, coinbase, regime
|
|
246
|
+
};
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The pure decision function `computeBotDecision()` is also exported, so you can re-use the same logic in a Node script or backtest without React.
|
|
250
|
+
|
|
227
251
|
### `<GasBadge />`
|
|
228
252
|
|
|
229
253
|
Current mainnet gas price, color-coded by tier (low/normal/high/spike).
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type UseBotDecisionOptions } from "./useBotDecision.js";
|
|
2
|
+
export type BotDecisionPanelProps = UseBotDecisionOptions & {
|
|
3
|
+
className?: string;
|
|
4
|
+
title?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function BotDecisionPanel({ className, title, ...hookOptions }: BotDecisionPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=BotDecisionPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BotDecisionPanel.d.ts","sourceRoot":"","sources":["../src/BotDecisionPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAGjF,MAAM,MAAM,qBAAqB,GAAG,qBAAqB,GAAG;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAsCF,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,KAA6B,EAAE,GAAG,WAAW,EAAE,EAAE,qBAAqB,2CA2HnH"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useBotDecision } from "./useBotDecision.js";
|
|
3
|
+
const palette = {
|
|
4
|
+
bg: "#0a0a0a",
|
|
5
|
+
surface: "#111113",
|
|
6
|
+
surfaceMuted: "#0f0f11",
|
|
7
|
+
border: "#1f1f23",
|
|
8
|
+
text: "#f5f5f7",
|
|
9
|
+
textMuted: "#9ca3af",
|
|
10
|
+
textDim: "#52525b",
|
|
11
|
+
buy: "#10b981",
|
|
12
|
+
sell: "#ef4444",
|
|
13
|
+
wait: "#fbbf24",
|
|
14
|
+
standdown: "#6b7280",
|
|
15
|
+
good: "#10b981",
|
|
16
|
+
bad: "#ef4444",
|
|
17
|
+
neutral: "#9ca3af"
|
|
18
|
+
};
|
|
19
|
+
const decisionMeta = {
|
|
20
|
+
BUY: { label: "WOULD BUY", color: palette.buy, arrow: "▲" },
|
|
21
|
+
SELL: { label: "WOULD SELL", color: palette.sell, arrow: "▼" },
|
|
22
|
+
WAIT: { label: "WAIT", color: palette.wait, arrow: "•" },
|
|
23
|
+
"STAND DOWN": { label: "STAND DOWN", color: palette.standdown, arrow: "■" }
|
|
24
|
+
};
|
|
25
|
+
const reasonColor = {
|
|
26
|
+
good: palette.good,
|
|
27
|
+
bad: palette.bad,
|
|
28
|
+
neutral: palette.neutral
|
|
29
|
+
};
|
|
30
|
+
const reasonSymbol = {
|
|
31
|
+
good: "✓",
|
|
32
|
+
bad: "✗",
|
|
33
|
+
neutral: "•"
|
|
34
|
+
};
|
|
35
|
+
export function BotDecisionPanel({ className, title = "Bot Decision (live)", ...hookOptions }) {
|
|
36
|
+
const result = useBotDecision(hookOptions);
|
|
37
|
+
const meta = decisionMeta[result.decision];
|
|
38
|
+
const recent = result.history.slice(0, 20);
|
|
39
|
+
const tally = recent.reduce((acc, h) => {
|
|
40
|
+
acc[h.decision] = (acc[h.decision] ?? 0) + 1;
|
|
41
|
+
return acc;
|
|
42
|
+
}, { BUY: 0, SELL: 0, WAIT: 0, "STAND DOWN": 0 });
|
|
43
|
+
return (_jsxs("section", { className: className, style: {
|
|
44
|
+
background: palette.bg,
|
|
45
|
+
border: `1px solid ${palette.border}`,
|
|
46
|
+
borderRadius: 14,
|
|
47
|
+
boxShadow: "0 20px 60px rgba(0, 0, 0, 0.55)",
|
|
48
|
+
color: palette.text,
|
|
49
|
+
display: "grid",
|
|
50
|
+
fontFamily: "system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif",
|
|
51
|
+
gap: 14,
|
|
52
|
+
maxWidth: 420,
|
|
53
|
+
padding: 18
|
|
54
|
+
}, children: [_jsxs("header", { style: { alignItems: "center", display: "flex", justifyContent: "space-between" }, children: [_jsx("h3", { style: { fontSize: 14, fontWeight: 700, letterSpacing: "0.02em", margin: 0 }, children: title }), _jsx("span", { style: { color: palette.textDim, fontSize: 11 }, children: result.evaluatedAt ? `updated ${timeAgoShort(result.evaluatedAt.getTime())}` : "warming up" })] }), _jsxs("div", { style: {
|
|
55
|
+
alignItems: "center",
|
|
56
|
+
background: palette.surface,
|
|
57
|
+
border: `1px solid ${meta.color}55`,
|
|
58
|
+
borderRadius: 10,
|
|
59
|
+
display: "flex",
|
|
60
|
+
gap: 12,
|
|
61
|
+
padding: "12px 14px"
|
|
62
|
+
}, children: [_jsx("span", { "aria-hidden": true, style: {
|
|
63
|
+
color: meta.color,
|
|
64
|
+
fontSize: 22,
|
|
65
|
+
fontWeight: 800,
|
|
66
|
+
lineHeight: 1,
|
|
67
|
+
width: 22
|
|
68
|
+
}, children: meta.arrow }), _jsxs("div", { style: { display: "grid", flex: 1, gap: 2 }, children: [_jsx("strong", { style: { color: meta.color, fontSize: 15, fontWeight: 700, letterSpacing: "0.04em" }, children: meta.label }), _jsxs("span", { style: { color: palette.textMuted, fontSize: 11 }, children: ["confidence ", result.confidence, "%"] })] }), _jsx(ConfidenceBar, { value: result.confidence, color: meta.color })] }), _jsx("ul", { style: { display: "grid", gap: 4, listStyle: "none", margin: 0, padding: 0 }, children: result.reasons.map((reason, idx) => (_jsxs("li", { style: {
|
|
69
|
+
alignItems: "baseline",
|
|
70
|
+
color: palette.text,
|
|
71
|
+
display: "grid",
|
|
72
|
+
fontSize: 12,
|
|
73
|
+
gap: 8,
|
|
74
|
+
gridTemplateColumns: "12px 1fr"
|
|
75
|
+
}, children: [_jsx("span", { style: { color: reasonColor[reason.tone] }, children: reasonSymbol[reason.tone] }), _jsx("span", { style: { color: reason.tone === "bad" ? palette.textMuted : palette.text }, children: reason.text })] }, `${reason.text}-${idx}`))) }), _jsxs("div", { style: {
|
|
76
|
+
alignItems: "center",
|
|
77
|
+
background: palette.surfaceMuted,
|
|
78
|
+
border: `1px solid ${palette.border}`,
|
|
79
|
+
borderRadius: 8,
|
|
80
|
+
color: palette.textMuted,
|
|
81
|
+
display: "flex",
|
|
82
|
+
fontSize: 11,
|
|
83
|
+
fontVariantNumeric: "tabular-nums",
|
|
84
|
+
gap: 10,
|
|
85
|
+
justifyContent: "space-between",
|
|
86
|
+
padding: "8px 12px"
|
|
87
|
+
}, children: [_jsxs("span", { children: ["Last ", recent.length, ":"] }), _jsx(Pill, { label: "BUY", count: tally.BUY, color: palette.buy }), _jsx(Pill, { label: "WAIT", count: tally.WAIT, color: palette.wait }), _jsx(Pill, { label: "SELL", count: tally.SELL, color: palette.sell }), _jsx(Pill, { label: "OFF", count: tally["STAND DOWN"], color: palette.standdown })] }), _jsx("div", { style: {
|
|
88
|
+
color: palette.textDim,
|
|
89
|
+
fontSize: 10,
|
|
90
|
+
lineHeight: 1.4
|
|
91
|
+
}, children: "Preview only. Decisions here describe what the strategy would do given live Binance + Coinbase data \u2014 they do not execute on-chain trades. The actual bot runs as a separate Node process against Uniswap." })] }));
|
|
92
|
+
}
|
|
93
|
+
function Pill({ label, count, color }) {
|
|
94
|
+
return (_jsxs("span", { style: {
|
|
95
|
+
alignItems: "center",
|
|
96
|
+
background: `${color}1f`,
|
|
97
|
+
border: `1px solid ${color}55`,
|
|
98
|
+
borderRadius: 6,
|
|
99
|
+
color,
|
|
100
|
+
display: "inline-flex",
|
|
101
|
+
fontSize: 10,
|
|
102
|
+
fontWeight: 700,
|
|
103
|
+
gap: 4,
|
|
104
|
+
padding: "2px 8px"
|
|
105
|
+
}, children: [label, " ", _jsx("strong", { children: count })] }));
|
|
106
|
+
}
|
|
107
|
+
function ConfidenceBar({ value, color }) {
|
|
108
|
+
const safe = Math.max(0, Math.min(100, value));
|
|
109
|
+
return (_jsx("div", { "aria-hidden": true, style: {
|
|
110
|
+
background: palette.surfaceMuted,
|
|
111
|
+
border: `1px solid ${palette.border}`,
|
|
112
|
+
borderRadius: 6,
|
|
113
|
+
height: 6,
|
|
114
|
+
overflow: "hidden",
|
|
115
|
+
width: 72
|
|
116
|
+
}, children: _jsx("div", { style: { background: color, height: "100%", width: `${safe}%` } }) }));
|
|
117
|
+
}
|
|
118
|
+
function timeAgoShort(ts) {
|
|
119
|
+
const seconds = Math.max(0, (Date.now() - ts) / 1000);
|
|
120
|
+
if (seconds < 60)
|
|
121
|
+
return `${seconds.toFixed(0)}s ago`;
|
|
122
|
+
return `${Math.floor(seconds / 60)}m ago`;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=BotDecisionPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BotDecisionPanel.js","sourceRoot":"","sources":["../src/BotDecisionPanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Regime } from "./useVolatilityRegime.js";
|
|
2
|
+
export type BotDecision = "BUY" | "SELL" | "WAIT" | "STAND DOWN";
|
|
3
|
+
export type DecisionReason = {
|
|
4
|
+
text: string;
|
|
5
|
+
tone: "good" | "bad" | "neutral";
|
|
6
|
+
};
|
|
7
|
+
export type DecisionInputs = {
|
|
8
|
+
regime: Regime;
|
|
9
|
+
bookImbalance: number;
|
|
10
|
+
tradeImbalance: number;
|
|
11
|
+
spreadPct: number;
|
|
12
|
+
venueDivergenceBps: number | null;
|
|
13
|
+
bookDepthWithinHalfPctEth: number;
|
|
14
|
+
midPrice: number | null;
|
|
15
|
+
};
|
|
16
|
+
export type DecisionResult = {
|
|
17
|
+
decision: BotDecision;
|
|
18
|
+
confidence: number;
|
|
19
|
+
reasons: DecisionReason[];
|
|
20
|
+
};
|
|
21
|
+
export type DecisionThresholds = {
|
|
22
|
+
imbalanceThreshold: number;
|
|
23
|
+
flowThreshold: number;
|
|
24
|
+
spreadWidePct: number;
|
|
25
|
+
divergenceBps: number;
|
|
26
|
+
minDepthEth: number;
|
|
27
|
+
};
|
|
28
|
+
export declare const DEFAULT_DECISION_THRESHOLDS: DecisionThresholds;
|
|
29
|
+
export declare function computeBotDecision(inputs: DecisionInputs, thresholds?: DecisionThresholds): DecisionResult;
|
|
30
|
+
export type BookLevel = {
|
|
31
|
+
price: number;
|
|
32
|
+
size: number;
|
|
33
|
+
};
|
|
34
|
+
export declare function computeBookImbalance(bids: BookLevel[], asks: BookLevel[], midPrice: number, withinPct?: number): number;
|
|
35
|
+
export declare function computeBookDepth(bids: BookLevel[], asks: BookLevel[], midPrice: number, withinPct?: number): number;
|
|
36
|
+
export declare function computeTradeImbalance(trades: {
|
|
37
|
+
side: "buy" | "sell";
|
|
38
|
+
size: number;
|
|
39
|
+
timestamp: number;
|
|
40
|
+
}[], windowMs?: number, nowMs?: number): number;
|
|
41
|
+
//# sourceMappingURL=decisionEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decisionEngine.d.ts","sourceRoot":"","sources":["../src/decisionEngine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAEvD,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,CAAC;AAEjE,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,yBAAyB,EAAE,MAAM,CAAC;IAClC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,kBAMzC,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,cAAc,EACtB,UAAU,GAAE,kBAAgD,GAC3D,cAAc,CA2HhB;AAKD,MAAM,MAAM,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,SAAS,EAAE,EACjB,IAAI,EAAE,SAAS,EAAE,EACjB,QAAQ,EAAE,MAAM,EAChB,SAAS,SAAQ,GAChB,MAAM,CASR;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EAAE,EACjB,IAAI,EAAE,SAAS,EAAE,EACjB,QAAQ,EAAE,MAAM,EAChB,SAAS,SAAQ,GAChB,MAAM,CAMR;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE;IAAE,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAAE,EACnE,QAAQ,SAAS,EACjB,KAAK,SAAa,GACjB,MAAM,CAYR"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
export const DEFAULT_DECISION_THRESHOLDS = {
|
|
2
|
+
imbalanceThreshold: 0.15,
|
|
3
|
+
flowThreshold: 0.1,
|
|
4
|
+
spreadWidePct: 0.05,
|
|
5
|
+
divergenceBps: 5,
|
|
6
|
+
minDepthEth: 50
|
|
7
|
+
};
|
|
8
|
+
export function computeBotDecision(inputs, thresholds = DEFAULT_DECISION_THRESHOLDS) {
|
|
9
|
+
const reasons = [];
|
|
10
|
+
// Regime gate
|
|
11
|
+
if (inputs.regime === "UNKNOWN") {
|
|
12
|
+
return {
|
|
13
|
+
decision: "WAIT",
|
|
14
|
+
confidence: 0,
|
|
15
|
+
reasons: [{ text: "Volatility classifier still warming up", tone: "neutral" }]
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (inputs.regime === "TRENDING") {
|
|
19
|
+
return {
|
|
20
|
+
decision: "STAND DOWN",
|
|
21
|
+
confidence: 90,
|
|
22
|
+
reasons: [
|
|
23
|
+
{ text: "Regime: TRENDING — range bot sits out", tone: "bad" },
|
|
24
|
+
{ text: "Re-engages when price stabilises in a range", tone: "neutral" }
|
|
25
|
+
]
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
let scoreBuy = 0;
|
|
29
|
+
let scoreSell = 0;
|
|
30
|
+
let confidence = inputs.regime === "RANGING" ? 45 : 30;
|
|
31
|
+
if (inputs.regime === "RANGING") {
|
|
32
|
+
reasons.push({ text: "Regime: RANGING (favourable)", tone: "good" });
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
reasons.push({ text: "Regime: MIXED", tone: "neutral" });
|
|
36
|
+
}
|
|
37
|
+
// Book imbalance
|
|
38
|
+
if (inputs.bookImbalance >= thresholds.imbalanceThreshold) {
|
|
39
|
+
const pct = inputs.bookImbalance * 100;
|
|
40
|
+
scoreBuy += 25;
|
|
41
|
+
reasons.push({ text: `Book imbalance +${pct.toFixed(0)}% bid-heavy`, tone: "good" });
|
|
42
|
+
}
|
|
43
|
+
else if (inputs.bookImbalance <= -thresholds.imbalanceThreshold) {
|
|
44
|
+
const pct = inputs.bookImbalance * 100;
|
|
45
|
+
scoreSell += 25;
|
|
46
|
+
reasons.push({ text: `Book imbalance ${pct.toFixed(0)}% ask-heavy`, tone: "good" });
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
reasons.push({
|
|
50
|
+
text: `Book imbalance balanced (${(inputs.bookImbalance * 100).toFixed(0)}%)`,
|
|
51
|
+
tone: "neutral"
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// Trade flow (last 60s)
|
|
55
|
+
if (inputs.tradeImbalance >= thresholds.flowThreshold) {
|
|
56
|
+
const buyPct = (0.5 + inputs.tradeImbalance / 2) * 100;
|
|
57
|
+
scoreBuy += 20;
|
|
58
|
+
reasons.push({ text: `Recent flow ${buyPct.toFixed(0)}% buys`, tone: "good" });
|
|
59
|
+
}
|
|
60
|
+
else if (inputs.tradeImbalance <= -thresholds.flowThreshold) {
|
|
61
|
+
const sellPct = (0.5 - inputs.tradeImbalance / 2) * 100;
|
|
62
|
+
scoreSell += 20;
|
|
63
|
+
reasons.push({ text: `Recent flow ${sellPct.toFixed(0)}% sells`, tone: "good" });
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
reasons.push({ text: "Recent flow balanced", tone: "neutral" });
|
|
67
|
+
}
|
|
68
|
+
// Spread
|
|
69
|
+
if (inputs.spreadPct > thresholds.spreadWidePct) {
|
|
70
|
+
confidence -= 10;
|
|
71
|
+
reasons.push({ text: `Spread wide (${inputs.spreadPct.toFixed(3)}%)`, tone: "bad" });
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
confidence += 5;
|
|
75
|
+
reasons.push({ text: `Spread tight (${inputs.spreadPct.toFixed(3)}%)`, tone: "good" });
|
|
76
|
+
}
|
|
77
|
+
// Cross-venue divergence
|
|
78
|
+
if (inputs.venueDivergenceBps !== null && Number.isFinite(inputs.venueDivergenceBps)) {
|
|
79
|
+
if (inputs.venueDivergenceBps > thresholds.divergenceBps) {
|
|
80
|
+
scoreBuy += 12;
|
|
81
|
+
reasons.push({
|
|
82
|
+
text: `Binance leading +${inputs.venueDivergenceBps.toFixed(1)} bps vs Coinbase`,
|
|
83
|
+
tone: "good"
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
else if (inputs.venueDivergenceBps < -thresholds.divergenceBps) {
|
|
87
|
+
scoreSell += 12;
|
|
88
|
+
reasons.push({
|
|
89
|
+
text: `Binance lagging ${inputs.venueDivergenceBps.toFixed(1)} bps vs Coinbase`,
|
|
90
|
+
tone: "good"
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
reasons.push({
|
|
95
|
+
text: `Venue divergence ${inputs.venueDivergenceBps.toFixed(1)} bps (aligned)`,
|
|
96
|
+
tone: "neutral"
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Depth check
|
|
101
|
+
if (inputs.bookDepthWithinHalfPctEth < thresholds.minDepthEth) {
|
|
102
|
+
confidence -= 15;
|
|
103
|
+
reasons.push({
|
|
104
|
+
text: `Thin book — ${inputs.bookDepthWithinHalfPctEth.toFixed(0)} ETH within ±0.5%`,
|
|
105
|
+
tone: "bad"
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
confidence += 5;
|
|
110
|
+
reasons.push({
|
|
111
|
+
text: `Book depth healthy (${inputs.bookDepthWithinHalfPctEth.toFixed(0)} ETH within ±0.5%)`,
|
|
112
|
+
tone: "good"
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
let decision;
|
|
116
|
+
let finalConfidence = confidence;
|
|
117
|
+
if (scoreBuy > scoreSell + 12) {
|
|
118
|
+
decision = "BUY";
|
|
119
|
+
finalConfidence = Math.min(100, Math.max(0, confidence + scoreBuy));
|
|
120
|
+
}
|
|
121
|
+
else if (scoreSell > scoreBuy + 12) {
|
|
122
|
+
decision = "SELL";
|
|
123
|
+
finalConfidence = Math.min(100, Math.max(0, confidence + scoreSell));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
decision = "WAIT";
|
|
127
|
+
finalConfidence = Math.min(100, Math.max(0, confidence + Math.max(scoreBuy, scoreSell) / 2));
|
|
128
|
+
reasons.push({ text: "Signals mixed — no clear direction", tone: "neutral" });
|
|
129
|
+
}
|
|
130
|
+
return { decision, confidence: finalConfidence, reasons };
|
|
131
|
+
}
|
|
132
|
+
export function computeBookImbalance(bids, asks, midPrice, withinPct = 0.005) {
|
|
133
|
+
const lowerBound = midPrice * (1 - withinPct);
|
|
134
|
+
const upperBound = midPrice * (1 + withinPct);
|
|
135
|
+
const bidSize = bids.filter((b) => b.price >= lowerBound).reduce((sum, b) => sum + b.size, 0);
|
|
136
|
+
const askSize = asks.filter((a) => a.price <= upperBound).reduce((sum, a) => sum + a.size, 0);
|
|
137
|
+
const total = bidSize + askSize;
|
|
138
|
+
if (total <= 0)
|
|
139
|
+
return 0;
|
|
140
|
+
return (bidSize - askSize) / total;
|
|
141
|
+
}
|
|
142
|
+
export function computeBookDepth(bids, asks, midPrice, withinPct = 0.005) {
|
|
143
|
+
const lowerBound = midPrice * (1 - withinPct);
|
|
144
|
+
const upperBound = midPrice * (1 + withinPct);
|
|
145
|
+
const bidSize = bids.filter((b) => b.price >= lowerBound).reduce((sum, b) => sum + b.size, 0);
|
|
146
|
+
const askSize = asks.filter((a) => a.price <= upperBound).reduce((sum, a) => sum + a.size, 0);
|
|
147
|
+
return bidSize + askSize;
|
|
148
|
+
}
|
|
149
|
+
export function computeTradeImbalance(trades, windowMs = 60_000, nowMs = Date.now()) {
|
|
150
|
+
const cutoff = nowMs - windowMs;
|
|
151
|
+
let buyVolume = 0;
|
|
152
|
+
let sellVolume = 0;
|
|
153
|
+
for (const t of trades) {
|
|
154
|
+
if (t.timestamp < cutoff)
|
|
155
|
+
continue;
|
|
156
|
+
if (t.side === "buy")
|
|
157
|
+
buyVolume += t.size;
|
|
158
|
+
else
|
|
159
|
+
sellVolume += t.size;
|
|
160
|
+
}
|
|
161
|
+
const total = buyVolume + sellVolume;
|
|
162
|
+
if (total <= 0)
|
|
163
|
+
return 0;
|
|
164
|
+
return (buyVolume - sellVolume) / total;
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=decisionEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decisionEngine.js","sourceRoot":"","sources":["../src/decisionEngine.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,10 @@ export { useVolatilityRegime, type Regime, type UseVolatilityRegimeOptions, type
|
|
|
21
21
|
export { RegimeBadge, type RegimeBadgeProps } from "./RegimeBadge.js";
|
|
22
22
|
export { useGasPrice, type GasTier, type UseGasPriceOptions, type UseGasPriceResult } from "./useGasPrice.js";
|
|
23
23
|
export { GasBadge, type GasBadgeProps } from "./GasBadge.js";
|
|
24
|
+
export { useCoinbaseTicker, type CoinbaseTickerStatus, type CoinbaseTickerMessage, type UseCoinbaseTickerOptions, type UseCoinbaseTickerResult } from "./useCoinbaseTicker.js";
|
|
25
|
+
export { useBotDecision, type BotDecision, type DecisionResult, type DecisionThresholds, type DecisionHistoryEntry, type UseBotDecisionOptions, type UseBotDecisionResult } from "./useBotDecision.js";
|
|
26
|
+
export { computeBotDecision, computeBookImbalance, computeBookDepth, computeTradeImbalance, DEFAULT_DECISION_THRESHOLDS, type DecisionInputs, type DecisionReason, type BookLevel } from "./decisionEngine.js";
|
|
27
|
+
export { BotDecisionPanel, type BotDecisionPanelProps } from "./BotDecisionPanel.js";
|
|
24
28
|
/** @deprecated Use BAND_STRATEGY_ABI. Will be removed in v1.0. */
|
|
25
29
|
export { BAND_STRATEGY_ABI as ETH_BOT_VAULT_ABI } from "./bandStrategyAbi.js";
|
|
26
30
|
/** @deprecated Use BAND_STRATEGY_BYTECODE. Will be removed in v1.0. */
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,mBAAmB,EAAE,KAAK,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AACvF,OAAO,EACL,iCAAiC,EACjC,6BAA6B,EAC7B,KAAK,oCAAoC,EACzC,KAAK,mCAAmC,EACzC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC/B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC7B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,mBAAmB,EACnB,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC/B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,KAAK,0BAA0B,EAAE,KAAK,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAGhI,OAAO,EACL,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC/B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAElF,OAAO,EACL,gBAAgB,EAChB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC5B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhE,OAAO,EACL,yBAAyB,EACzB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,gCAAgC,EACrC,KAAK,+BAA+B,EACrC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAGxF,OAAO,EACL,mBAAmB,EACnB,KAAK,MAAM,EACX,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC/B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEtE,OAAO,EACL,WAAW,EACX,KAAK,OAAO,EACZ,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,11 @@ export { useVolatilityRegime } from "./useVolatilityRegime.js";
|
|
|
24
24
|
export { RegimeBadge } from "./RegimeBadge.js";
|
|
25
25
|
export { useGasPrice } from "./useGasPrice.js";
|
|
26
26
|
export { GasBadge } from "./GasBadge.js";
|
|
27
|
+
// --- Cross-venue + decision proxy ---------------------------------------
|
|
28
|
+
export { useCoinbaseTicker } from "./useCoinbaseTicker.js";
|
|
29
|
+
export { useBotDecision } from "./useBotDecision.js";
|
|
30
|
+
export { computeBotDecision, computeBookImbalance, computeBookDepth, computeTradeImbalance, DEFAULT_DECISION_THRESHOLDS } from "./decisionEngine.js";
|
|
31
|
+
export { BotDecisionPanel } from "./BotDecisionPanel.js";
|
|
27
32
|
/** @deprecated Use BAND_STRATEGY_ABI. Will be removed in v1.0. */
|
|
28
33
|
export { BAND_STRATEGY_ABI as ETH_BOT_VAULT_ABI } from "./bandStrategyAbi.js";
|
|
29
34
|
/** @deprecated Use BAND_STRATEGY_BYTECODE. Will be removed in v1.0. */
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,SAAS,EAAuB,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAA+B,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,mBAAmB,EAAmC,MAAM,iBAAiB,CAAC;AACvF,OAAO,EACL,iCAAiC,EACjC,6BAA6B,EAG9B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAgC,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EAI1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,EAKlB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,mBAAmB,EAIpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAmE,MAAM,0BAA0B,CAAC;AAEhI,4EAA4E;AAC5E,OAAO,EACL,mBAAmB,EAMpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,eAAe,EAA6B,MAAM,sBAAsB,CAAC;AAElF,OAAO,EACL,gBAAgB,EAMjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAuB,MAAM,gBAAgB,CAAC;AAEhE,4EAA4E;AAC5E,OAAO,EACL,yBAAyB,EAK1B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAA+B,MAAM,wBAAwB,CAAC;AAExF,4EAA4E;AAC5E,OAAO,EACL,mBAAmB,EAIpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,WAAW,EAAyB,MAAM,kBAAkB,CAAC;AAEtE,OAAO,EACL,WAAW,EAIZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAsB,MAAM,eAAe,CAAC;AAE7D,kEAAkE;AAClE,OAAO,EAAE,iBAAiB,IAAI,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9E,uEAAuE;AACvE,OAAO,EAAE,sBAAsB,IAAI,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAC7F,kEAAkE;AAClE,OAAO,EAAE,iBAAiB,IAAI,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnF,uEAAuE;AACvE,OAAO,EAAE,sBAAsB,IAAI,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAClG,0DAA0D;AAC1D,OAAO,EAAE,SAAS,IAAI,WAAW,EAA2C,MAAM,gBAAgB,CAAC;AACnG,kEAAkE;AAClE,OAAO,EAAE,iBAAiB,IAAI,aAAa,EAAqD,MAAM,wBAAwB,CAAC;AAC/H,oEAAoE;AACpE,OAAO,EAAE,mBAAmB,IAAI,4BAA4B,EAA0E,MAAM,iBAAiB,CAAC;AAC9J,kFAAkF;AAClF,OAAO,EAAE,iCAAiC,IAAI,iCAAiC,EAAE,MAAM,oCAAoC,CAAC;AAC5H,8EAA8E;AAC9E,OAAO,EACL,6BAA6B,IAAI,wBAAwB,EAG1D,MAAM,oCAAoC,CAAC;AAC5C,iEAAiE;AACjE,OAAO,EAAE,gBAAgB,IAAI,kBAAkB,EAA6D,MAAM,uBAAuB,CAAC;AAC1I,oEAAoE;AACpE,OAAO,EACL,mBAAmB,IAAI,WAAW,EAInC,MAAM,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type UseBinanceOrderBookOptions } from "./useBinanceOrderBook.js";
|
|
2
|
+
import { type UseBinanceTradesOptions } from "./useBinanceTrades.js";
|
|
3
|
+
import { type UseCoinbaseTickerOptions } from "./useCoinbaseTicker.js";
|
|
4
|
+
import { type UseVolatilityRegimeOptions } from "./useVolatilityRegime.js";
|
|
5
|
+
import { type BotDecision, type DecisionResult, type DecisionThresholds } from "./decisionEngine.js";
|
|
6
|
+
export type { BotDecision, DecisionResult, DecisionThresholds } from "./decisionEngine.js";
|
|
7
|
+
export type DecisionHistoryEntry = {
|
|
8
|
+
at: number;
|
|
9
|
+
decision: BotDecision;
|
|
10
|
+
confidence: number;
|
|
11
|
+
};
|
|
12
|
+
export type UseBotDecisionOptions = {
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
evaluationIntervalMs?: number;
|
|
15
|
+
flowWindowMs?: number;
|
|
16
|
+
historyLimit?: number;
|
|
17
|
+
thresholds?: Partial<DecisionThresholds>;
|
|
18
|
+
orderBook?: UseBinanceOrderBookOptions;
|
|
19
|
+
trades?: UseBinanceTradesOptions;
|
|
20
|
+
coinbase?: UseCoinbaseTickerOptions;
|
|
21
|
+
regime?: UseVolatilityRegimeOptions;
|
|
22
|
+
};
|
|
23
|
+
export type UseBotDecisionResult = DecisionResult & {
|
|
24
|
+
midPriceBinance: number | null;
|
|
25
|
+
midPriceCoinbase: number | null;
|
|
26
|
+
venueDivergenceBps: number | null;
|
|
27
|
+
bookDepthWithinHalfPctEth: number;
|
|
28
|
+
history: DecisionHistoryEntry[];
|
|
29
|
+
evaluatedAt?: Date;
|
|
30
|
+
};
|
|
31
|
+
export declare function useBotDecision(options?: UseBotDecisionOptions): UseBotDecisionResult;
|
|
32
|
+
//# sourceMappingURL=useBotDecision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBotDecision.d.ts","sourceRoot":"","sources":["../src/useBotDecision.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,KAAK,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAChG,OAAO,EAAoB,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,EAAqB,KAAK,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EAAuB,KAAK,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAChG,OAAO,EAML,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE3F,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACzC,SAAS,CAAC,EAAE,0BAA0B,CAAC;IACvC,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,QAAQ,CAAC,EAAE,wBAAwB,CAAC;IACpC,MAAM,CAAC,EAAE,0BAA0B,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,cAAc,GAAG;IAClD,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,yBAAyB,EAAE,MAAM,CAAC;IAClC,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE,IAAI,CAAC;CACpB,CAAC;AAQF,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,oBAAoB,CAqHxF"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { useBinanceOrderBook } from "./useBinanceOrderBook.js";
|
|
3
|
+
import { useBinanceTrades } from "./useBinanceTrades.js";
|
|
4
|
+
import { useCoinbaseTicker } from "./useCoinbaseTicker.js";
|
|
5
|
+
import { useVolatilityRegime } from "./useVolatilityRegime.js";
|
|
6
|
+
import { computeBookDepth, computeBookImbalance, computeBotDecision, computeTradeImbalance, DEFAULT_DECISION_THRESHOLDS } from "./decisionEngine.js";
|
|
7
|
+
const EMPTY_RESULT = {
|
|
8
|
+
decision: "WAIT",
|
|
9
|
+
confidence: 0,
|
|
10
|
+
reasons: [{ text: "Waiting for live data", tone: "neutral" }]
|
|
11
|
+
};
|
|
12
|
+
export function useBotDecision(options = {}) {
|
|
13
|
+
const enabled = options.enabled ?? true;
|
|
14
|
+
const evaluationIntervalMs = options.evaluationIntervalMs ?? 2000;
|
|
15
|
+
const flowWindowMs = options.flowWindowMs ?? 60_000;
|
|
16
|
+
const historyLimit = options.historyLimit ?? 30;
|
|
17
|
+
const thresholds = { ...DEFAULT_DECISION_THRESHOLDS, ...(options.thresholds ?? {}) };
|
|
18
|
+
const book = useBinanceOrderBook({ ...options.orderBook, enabled });
|
|
19
|
+
const trades = useBinanceTrades({ ...options.trades, enabled });
|
|
20
|
+
const coinbase = useCoinbaseTicker({ ...options.coinbase, enabled });
|
|
21
|
+
const regime = useVolatilityRegime({ ...options.regime, enabled });
|
|
22
|
+
const bookRef = useRef(book);
|
|
23
|
+
const tradesRef = useRef(trades);
|
|
24
|
+
const coinbaseRef = useRef(coinbase);
|
|
25
|
+
const regimeRef = useRef(regime);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
bookRef.current = book;
|
|
28
|
+
}, [book]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
tradesRef.current = trades;
|
|
31
|
+
}, [trades]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
coinbaseRef.current = coinbase;
|
|
34
|
+
}, [coinbase]);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
regimeRef.current = regime;
|
|
37
|
+
}, [regime]);
|
|
38
|
+
const [snapshot, setSnapshot] = useState({
|
|
39
|
+
result: EMPTY_RESULT,
|
|
40
|
+
midBinance: null,
|
|
41
|
+
midCoinbase: null,
|
|
42
|
+
divergenceBps: null,
|
|
43
|
+
depthEth: 0
|
|
44
|
+
});
|
|
45
|
+
const [history, setHistory] = useState([]);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!enabled)
|
|
48
|
+
return;
|
|
49
|
+
function evaluate() {
|
|
50
|
+
const b = bookRef.current;
|
|
51
|
+
const t = tradesRef.current;
|
|
52
|
+
const c = coinbaseRef.current;
|
|
53
|
+
const r = regimeRef.current;
|
|
54
|
+
const midBinance = b.midPrice;
|
|
55
|
+
if (midBinance === null) {
|
|
56
|
+
setSnapshot((prev) => ({ ...prev, evaluatedAt: new Date() }));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const bookImbalance = computeBookImbalance(b.bids, b.asks, midBinance);
|
|
60
|
+
const depthEth = computeBookDepth(b.bids, b.asks, midBinance);
|
|
61
|
+
const tradeImbalance = computeTradeImbalance(t.trades, flowWindowMs);
|
|
62
|
+
const spreadPct = b.spreadPercent ?? 0;
|
|
63
|
+
const midCoinbase = c.midPrice;
|
|
64
|
+
const divergenceBps = midCoinbase !== null && midCoinbase > 0
|
|
65
|
+
? ((midBinance - midCoinbase) / midCoinbase) * 10000
|
|
66
|
+
: null;
|
|
67
|
+
const result = computeBotDecision({
|
|
68
|
+
regime: r.regime,
|
|
69
|
+
bookImbalance,
|
|
70
|
+
tradeImbalance,
|
|
71
|
+
spreadPct,
|
|
72
|
+
venueDivergenceBps: divergenceBps,
|
|
73
|
+
bookDepthWithinHalfPctEth: depthEth,
|
|
74
|
+
midPrice: midBinance
|
|
75
|
+
}, thresholds);
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
setSnapshot({
|
|
78
|
+
result,
|
|
79
|
+
midBinance,
|
|
80
|
+
midCoinbase,
|
|
81
|
+
divergenceBps,
|
|
82
|
+
depthEth,
|
|
83
|
+
evaluatedAt: new Date(now)
|
|
84
|
+
});
|
|
85
|
+
setHistory((prev) => [
|
|
86
|
+
{ at: now, decision: result.decision, confidence: result.confidence },
|
|
87
|
+
...prev
|
|
88
|
+
].slice(0, historyLimit));
|
|
89
|
+
}
|
|
90
|
+
evaluate();
|
|
91
|
+
const id = window.setInterval(evaluate, evaluationIntervalMs);
|
|
92
|
+
return () => window.clearInterval(id);
|
|
93
|
+
}, [enabled, evaluationIntervalMs, flowWindowMs, historyLimit, thresholds]);
|
|
94
|
+
return useMemo(() => ({
|
|
95
|
+
...snapshot.result,
|
|
96
|
+
midPriceBinance: snapshot.midBinance,
|
|
97
|
+
midPriceCoinbase: snapshot.midCoinbase,
|
|
98
|
+
venueDivergenceBps: snapshot.divergenceBps,
|
|
99
|
+
bookDepthWithinHalfPctEth: snapshot.depthEth,
|
|
100
|
+
history,
|
|
101
|
+
evaluatedAt: snapshot.evaluatedAt
|
|
102
|
+
}), [history, snapshot]);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=useBotDecision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBotDecision.js","sourceRoot":"","sources":["../src/useBotDecision.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAmC,MAAM,0BAA0B,CAAC;AAChG,OAAO,EAAE,gBAAgB,EAAgC,MAAM,uBAAuB,CAAC;AACvF,OAAO,EAAE,iBAAiB,EAAiC,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EAAE,mBAAmB,EAAmC,MAAM,0BAA0B,CAAC;AAChG,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,2BAA2B,EAI5B,MAAM,qBAAqB,CAAC;AA+B7B,MAAM,YAAY,GAAmB;IACnC,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,CAAC;IACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;CAC9D,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,UAAiC,EAAE;IAChE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC;IACxC,MAAM,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,IAAI,IAAI,CAAC;IAClE,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,MAAM,CAAC;IACpD,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;IAChD,MAAM,UAAU,GAAG,EAAE,GAAG,2BAA2B,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,CAAC;IAErF,MAAM,IAAI,GAAG,mBAAmB,CAAC,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAChE,MAAM,QAAQ,GAAG,iBAAiB,CAAC,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,mBAAmB,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAEnE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAEjC,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IACzB,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACX,SAAS,CAAC,GAAG,EAAE;QACb,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;IAC7B,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IACb,SAAS,CAAC,GAAG,EAAE;QACb,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IACjC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IACf,SAAS,CAAC,GAAG,EAAE;QACb,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;IAC7B,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAOrC;QACD,MAAM,EAAE,YAAY;QACpB,UAAU,EAAE,IAAI;QAChB,WAAW,EAAE,IAAI;QACjB,aAAa,EAAE,IAAI;QACnB,QAAQ,EAAE,CAAC;KACZ,CAAC,CAAC;IACH,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAyB,EAAE,CAAC,CAAC;IAEnE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,SAAS,QAAQ;YACf,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;YAC1B,MAAM,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC;YAC5B,MAAM,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC;YAC9B,MAAM,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC;YAE5B,MAAM,UAAU,GAAG,CAAC,CAAC,QAAQ,CAAC;YAC9B,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;gBACxB,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;gBAC9D,OAAO;YACT,CAAC;YAED,MAAM,aAAa,GAAG,oBAAoB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YACvE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC9D,MAAM,cAAc,GAAG,qBAAqB,CAAC,CAAC,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;YACrE,MAAM,SAAS,GAAG,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC;YAEvC,MAAM,WAAW,GAAG,CAAC,CAAC,QAAQ,CAAC;YAC/B,MAAM,aAAa,GACjB,WAAW,KAAK,IAAI,IAAI,WAAW,GAAG,CAAC;gBACrC,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,WAAW,CAAC,GAAG,KAAK;gBACpD,CAAC,CAAC,IAAI,CAAC;YAEX,MAAM,MAAM,GAAG,kBAAkB,CAC/B;gBACE,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,aAAa;gBACb,cAAc;gBACd,SAAS;gBACT,kBAAkB,EAAE,aAAa;gBACjC,yBAAyB,EAAE,QAAQ;gBACnC,QAAQ,EAAE,UAAU;aACrB,EACD,UAAU,CACX,CAAC;YAEF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,WAAW,CAAC;gBACV,MAAM;gBACN,UAAU;gBACV,WAAW;gBACX,aAAa;gBACb,QAAQ;gBACR,WAAW,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC;aAC3B,CAAC,CAAC;YACH,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;gBACnB,EAAE,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE;gBACrE,GAAG,IAAI;aACR,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;QAC5B,CAAC;QAED,QAAQ,EAAE,CAAC;QACX,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;QAC9D,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC;IAE5E,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,CAAC;QACL,GAAG,QAAQ,CAAC,MAAM;QAClB,eAAe,EAAE,QAAQ,CAAC,UAAU;QACpC,gBAAgB,EAAE,QAAQ,CAAC,WAAW;QACtC,kBAAkB,EAAE,QAAQ,CAAC,aAAa;QAC1C,yBAAyB,EAAE,QAAQ,CAAC,QAAQ;QAC5C,OAAO;QACP,WAAW,EAAE,QAAQ,CAAC,WAAW;KAClC,CAAC,EACF,CAAC,OAAO,EAAE,QAAQ,CAAC,CACpB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type CoinbaseTickerStatus = "connecting" | "live" | "stale" | "error";
|
|
2
|
+
export type CoinbaseTickerMessage = {
|
|
3
|
+
type: string;
|
|
4
|
+
product_id: string;
|
|
5
|
+
price?: string;
|
|
6
|
+
best_bid?: string;
|
|
7
|
+
best_ask?: string;
|
|
8
|
+
side?: "buy" | "sell";
|
|
9
|
+
time?: string;
|
|
10
|
+
};
|
|
11
|
+
export type UseCoinbaseTickerOptions = {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
productId?: string;
|
|
14
|
+
reconnectDelayMs?: number;
|
|
15
|
+
};
|
|
16
|
+
export type UseCoinbaseTickerResult = {
|
|
17
|
+
price: number | null;
|
|
18
|
+
bestBid: number | null;
|
|
19
|
+
bestAsk: number | null;
|
|
20
|
+
midPrice: number | null;
|
|
21
|
+
lastSide?: "buy" | "sell";
|
|
22
|
+
status: CoinbaseTickerStatus;
|
|
23
|
+
error?: string;
|
|
24
|
+
productId: string;
|
|
25
|
+
updatedAt?: Date;
|
|
26
|
+
};
|
|
27
|
+
export declare function useCoinbaseTicker(options?: UseCoinbaseTickerOptions): UseCoinbaseTickerResult;
|
|
28
|
+
//# sourceMappingURL=useCoinbaseTicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCoinbaseTicker.d.ts","sourceRoot":"","sources":["../src/useCoinbaseTicker.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAE7E,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC1B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB,CAAC;AAIF,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,GAAG,uBAAuB,CA+FjG"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from "react";
|
|
2
|
+
const DEFAULT_URL = "wss://ws-feed.exchange.coinbase.com";
|
|
3
|
+
export function useCoinbaseTicker(options = {}) {
|
|
4
|
+
const enabled = options.enabled ?? true;
|
|
5
|
+
const productId = options.productId ?? "ETH-USD";
|
|
6
|
+
const reconnectDelayMs = options.reconnectDelayMs ?? 3000;
|
|
7
|
+
const [message, setMessage] = useState();
|
|
8
|
+
const [status, setStatus] = useState("connecting");
|
|
9
|
+
const [error, setError] = useState();
|
|
10
|
+
const [updatedAt, setUpdatedAt] = useState();
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!enabled) {
|
|
13
|
+
setStatus("stale");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (typeof WebSocket === "undefined") {
|
|
17
|
+
setStatus("error");
|
|
18
|
+
setError("WebSocket is not available in this runtime.");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
let reconnectTimer;
|
|
22
|
+
let closedByHook = false;
|
|
23
|
+
let socket;
|
|
24
|
+
function connect() {
|
|
25
|
+
setStatus("connecting");
|
|
26
|
+
socket = new WebSocket(DEFAULT_URL);
|
|
27
|
+
socket.onopen = () => {
|
|
28
|
+
socket?.send(JSON.stringify({
|
|
29
|
+
type: "subscribe",
|
|
30
|
+
product_ids: [productId],
|
|
31
|
+
channels: ["ticker"]
|
|
32
|
+
}));
|
|
33
|
+
setStatus("live");
|
|
34
|
+
setError(undefined);
|
|
35
|
+
};
|
|
36
|
+
socket.onmessage = (event) => {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(String(event.data));
|
|
39
|
+
if (parsed.type === "ticker" && parsed.product_id === productId) {
|
|
40
|
+
setMessage(parsed);
|
|
41
|
+
setStatus("live");
|
|
42
|
+
setUpdatedAt(new Date());
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
setStatus("error");
|
|
47
|
+
setError("Could not parse Coinbase ticker message.");
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
socket.onerror = () => {
|
|
51
|
+
setStatus("error");
|
|
52
|
+
setError("Coinbase ticker connection failed.");
|
|
53
|
+
};
|
|
54
|
+
socket.onclose = () => {
|
|
55
|
+
if (closedByHook)
|
|
56
|
+
return;
|
|
57
|
+
setStatus("stale");
|
|
58
|
+
reconnectTimer = setTimeout(connect, reconnectDelayMs);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
connect();
|
|
62
|
+
return () => {
|
|
63
|
+
closedByHook = true;
|
|
64
|
+
if (reconnectTimer)
|
|
65
|
+
clearTimeout(reconnectTimer);
|
|
66
|
+
socket?.close();
|
|
67
|
+
};
|
|
68
|
+
}, [enabled, productId, reconnectDelayMs]);
|
|
69
|
+
return useMemo(() => {
|
|
70
|
+
const price = message?.price ? Number(message.price) : null;
|
|
71
|
+
const bestBid = message?.best_bid ? Number(message.best_bid) : null;
|
|
72
|
+
const bestAsk = message?.best_ask ? Number(message.best_ask) : null;
|
|
73
|
+
const midPrice = bestBid !== null && bestAsk !== null ? (bestBid + bestAsk) / 2 : null;
|
|
74
|
+
return {
|
|
75
|
+
price: price !== null && Number.isFinite(price) ? price : null,
|
|
76
|
+
bestBid: bestBid !== null && Number.isFinite(bestBid) ? bestBid : null,
|
|
77
|
+
bestAsk: bestAsk !== null && Number.isFinite(bestAsk) ? bestAsk : null,
|
|
78
|
+
midPrice: midPrice !== null && Number.isFinite(midPrice) ? midPrice : null,
|
|
79
|
+
lastSide: message?.side,
|
|
80
|
+
status,
|
|
81
|
+
error,
|
|
82
|
+
productId,
|
|
83
|
+
updatedAt
|
|
84
|
+
};
|
|
85
|
+
}, [error, message, productId, status, updatedAt]);
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=useCoinbaseTicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCoinbaseTicker.js","sourceRoot":"","sources":["../src/useCoinbaseTicker.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bandkit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Open-source scaffolding for Ethereum range-bound trading strategies: strategy contract deployment, restricted executor, and React hooks. Strategy logic is not included.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"@tanstack/react-query": "^5.0.0",
|
|
64
64
|
"@types/node": "^22.0.0",
|
|
65
65
|
"@types/react": "^19.0.0",
|
|
66
|
+
"@types/ws": "^8.18.1",
|
|
66
67
|
"chai": "^6.2.2",
|
|
67
68
|
"dotenv": "^16.4.7",
|
|
68
69
|
"fp-ts": "^2.16.11",
|
|
@@ -73,7 +74,8 @@
|
|
|
73
74
|
"ts-node": "^10.9.2",
|
|
74
75
|
"typescript": "^5.7.0",
|
|
75
76
|
"viem": "^2.0.0",
|
|
76
|
-
"wagmi": "^2.0.0"
|
|
77
|
+
"wagmi": "^2.0.0",
|
|
78
|
+
"ws": "^8.21.0"
|
|
77
79
|
},
|
|
78
80
|
"keywords": [
|
|
79
81
|
"ethereum",
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
//
|
|
3
|
+
// test-decision-stream.cjs
|
|
4
|
+
//
|
|
5
|
+
// Standalone Node script that streams live Binance ETH/USDT order book + trades
|
|
6
|
+
// and Coinbase ETH-USD ticker, and runs the same decision logic as
|
|
7
|
+
// src/decisionEngine.ts. Prints live decisions; after the run window,
|
|
8
|
+
// prints a summary tally and a diagnostic signal-vs-forward-price comparison.
|
|
9
|
+
//
|
|
10
|
+
// IMPORTANT — what this proves and does NOT prove:
|
|
11
|
+
// - It DOES verify the decision logic responds to live data without crashing.
|
|
12
|
+
// - It DOES show the cadence and balance of signals.
|
|
13
|
+
// - It DOES report a crude correlation between BUY/SELL signals and the
|
|
14
|
+
// next minute's price change.
|
|
15
|
+
// - It DOES NOT prove the strategy is profitable.
|
|
16
|
+
// Real validation needs months of historical data, realistic slippage and
|
|
17
|
+
// gas modelling, out-of-sample windows, and statistical significance tests.
|
|
18
|
+
//
|
|
19
|
+
// Usage:
|
|
20
|
+
// node scripts/test-decision-stream.cjs [durationMinutes]
|
|
21
|
+
// default duration: 7 minutes
|
|
22
|
+
//
|
|
23
|
+
|
|
24
|
+
const WebSocket = require("ws");
|
|
25
|
+
|
|
26
|
+
const DURATION_MIN = Number(process.argv[2] ?? 7);
|
|
27
|
+
const BINANCE_DEPTH_URL = "wss://stream.binance.com:9443/ws/ethusdt@depth20@100ms";
|
|
28
|
+
const BINANCE_TRADE_URL = "wss://stream.binance.com:9443/ws/ethusdt@trade";
|
|
29
|
+
const COINBASE_URL = "wss://ws-feed.exchange.coinbase.com";
|
|
30
|
+
|
|
31
|
+
// ---- State ----------------------------------------------------------------
|
|
32
|
+
const state = {
|
|
33
|
+
binanceBook: { bids: [], asks: [], midPrice: null, spreadPct: null, ts: 0 },
|
|
34
|
+
coinbaseMid: null,
|
|
35
|
+
recentTrades: [], // { price, size, side, ts }
|
|
36
|
+
priceTicks: [], // for volatility regime
|
|
37
|
+
decisions: [], // { at, decision, confidence, midPrice, reasons }
|
|
38
|
+
startedAt: Date.now()
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const PRICE_TICK_WINDOW = 60;
|
|
42
|
+
const FLOW_WINDOW_MS = 60_000;
|
|
43
|
+
const EVAL_INTERVAL_MS = 2000;
|
|
44
|
+
const FORWARD_PRICE_DELAY_MS = 60_000;
|
|
45
|
+
|
|
46
|
+
// ---- Decision logic (mirrors src/decisionEngine.ts) ----------------------
|
|
47
|
+
function computeBookImbalance(bids, asks, mid, withinPct = 0.005) {
|
|
48
|
+
const lo = mid * (1 - withinPct);
|
|
49
|
+
const hi = mid * (1 + withinPct);
|
|
50
|
+
let bidSum = 0;
|
|
51
|
+
let askSum = 0;
|
|
52
|
+
for (const b of bids) if (b.price >= lo) bidSum += b.size;
|
|
53
|
+
for (const a of asks) if (a.price <= hi) askSum += a.size;
|
|
54
|
+
const total = bidSum + askSum;
|
|
55
|
+
return total > 0 ? (bidSum - askSum) / total : 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function computeBookDepth(bids, asks, mid, withinPct = 0.005) {
|
|
59
|
+
const lo = mid * (1 - withinPct);
|
|
60
|
+
const hi = mid * (1 + withinPct);
|
|
61
|
+
let total = 0;
|
|
62
|
+
for (const b of bids) if (b.price >= lo) total += b.size;
|
|
63
|
+
for (const a of asks) if (a.price <= hi) total += a.size;
|
|
64
|
+
return total;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function computeTradeImbalance(trades, windowMs, nowMs) {
|
|
68
|
+
const cutoff = nowMs - windowMs;
|
|
69
|
+
let buyVol = 0;
|
|
70
|
+
let sellVol = 0;
|
|
71
|
+
for (const t of trades) {
|
|
72
|
+
if (t.ts < cutoff) continue;
|
|
73
|
+
if (t.side === "buy") buyVol += t.size;
|
|
74
|
+
else sellVol += t.size;
|
|
75
|
+
}
|
|
76
|
+
const total = buyVol + sellVol;
|
|
77
|
+
return total > 0 ? (buyVol - sellVol) / total : 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function classifyRegime(prices) {
|
|
81
|
+
if (prices.length < Math.max(8, Math.floor(PRICE_TICK_WINDOW / 4))) {
|
|
82
|
+
return { regime: "UNKNOWN", stddevPct: null, driftPct: null };
|
|
83
|
+
}
|
|
84
|
+
const mean = prices.reduce((s, v) => s + v, 0) / prices.length;
|
|
85
|
+
const variance = prices.reduce((s, v) => s + (v - mean) ** 2, 0) / prices.length;
|
|
86
|
+
const stddev = Math.sqrt(variance);
|
|
87
|
+
const stddevPct = mean > 0 ? (stddev / mean) * 100 : null;
|
|
88
|
+
const drift = prices[prices.length - 1] - prices[0];
|
|
89
|
+
const driftPct = mean > 0 ? (drift / mean) * 100 : null;
|
|
90
|
+
const absDriftPct = driftPct === null ? null : Math.abs(driftPct);
|
|
91
|
+
|
|
92
|
+
let regime = "MIXED";
|
|
93
|
+
if (stddevPct !== null && absDriftPct !== null) {
|
|
94
|
+
if (absDriftPct >= 0.45) regime = "TRENDING";
|
|
95
|
+
else if (stddevPct <= 0.15 && absDriftPct < 0.45) regime = "RANGING";
|
|
96
|
+
}
|
|
97
|
+
return { regime, stddevPct, driftPct };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function computeDecision(inputs) {
|
|
101
|
+
const reasons = [];
|
|
102
|
+
if (inputs.regime === "UNKNOWN") {
|
|
103
|
+
return { decision: "WAIT", confidence: 0, reasons: ["Warming up"] };
|
|
104
|
+
}
|
|
105
|
+
if (inputs.regime === "TRENDING") {
|
|
106
|
+
return {
|
|
107
|
+
decision: "STAND DOWN",
|
|
108
|
+
confidence: 90,
|
|
109
|
+
reasons: ["TRENDING regime — bot sits out"]
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let scoreBuy = 0;
|
|
114
|
+
let scoreSell = 0;
|
|
115
|
+
let confidence = inputs.regime === "RANGING" ? 45 : 30;
|
|
116
|
+
reasons.push(`Regime: ${inputs.regime}`);
|
|
117
|
+
|
|
118
|
+
if (inputs.bookImbalance >= 0.15) {
|
|
119
|
+
scoreBuy += 25;
|
|
120
|
+
reasons.push(`Book +${(inputs.bookImbalance * 100).toFixed(0)}% bid`);
|
|
121
|
+
} else if (inputs.bookImbalance <= -0.15) {
|
|
122
|
+
scoreSell += 25;
|
|
123
|
+
reasons.push(`Book ${(inputs.bookImbalance * 100).toFixed(0)}% ask`);
|
|
124
|
+
} else {
|
|
125
|
+
reasons.push(`Book ${(inputs.bookImbalance * 100).toFixed(0)}%`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (inputs.tradeImbalance >= 0.10) {
|
|
129
|
+
scoreBuy += 20;
|
|
130
|
+
reasons.push(`Flow ${((0.5 + inputs.tradeImbalance / 2) * 100).toFixed(0)}% buys`);
|
|
131
|
+
} else if (inputs.tradeImbalance <= -0.10) {
|
|
132
|
+
scoreSell += 20;
|
|
133
|
+
reasons.push(`Flow ${((0.5 - inputs.tradeImbalance / 2) * 100).toFixed(0)}% sells`);
|
|
134
|
+
} else {
|
|
135
|
+
reasons.push("Flow balanced");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (inputs.spreadPct > 0.05) {
|
|
139
|
+
confidence -= 10;
|
|
140
|
+
reasons.push(`Spread wide ${inputs.spreadPct.toFixed(3)}%`);
|
|
141
|
+
} else {
|
|
142
|
+
confidence += 5;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (inputs.venueDivergenceBps !== null && Number.isFinite(inputs.venueDivergenceBps)) {
|
|
146
|
+
if (inputs.venueDivergenceBps > 5) {
|
|
147
|
+
scoreBuy += 12;
|
|
148
|
+
reasons.push(`BIN +${inputs.venueDivergenceBps.toFixed(1)}bps`);
|
|
149
|
+
} else if (inputs.venueDivergenceBps < -5) {
|
|
150
|
+
scoreSell += 12;
|
|
151
|
+
reasons.push(`BIN ${inputs.venueDivergenceBps.toFixed(1)}bps`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (inputs.bookDepth < 50) {
|
|
156
|
+
confidence -= 15;
|
|
157
|
+
reasons.push(`Thin book (${inputs.bookDepth.toFixed(0)} ETH)`);
|
|
158
|
+
} else {
|
|
159
|
+
confidence += 5;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let decision = "WAIT";
|
|
163
|
+
if (scoreBuy > scoreSell + 12) {
|
|
164
|
+
decision = "BUY";
|
|
165
|
+
confidence = Math.min(100, Math.max(0, confidence + scoreBuy));
|
|
166
|
+
} else if (scoreSell > scoreBuy + 12) {
|
|
167
|
+
decision = "SELL";
|
|
168
|
+
confidence = Math.min(100, Math.max(0, confidence + scoreSell));
|
|
169
|
+
} else {
|
|
170
|
+
confidence = Math.min(100, Math.max(0, confidence + Math.max(scoreBuy, scoreSell) / 2));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return { decision, confidence: Math.round(confidence), reasons };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ---- WebSocket setup -----------------------------------------------------
|
|
177
|
+
function connectBinanceDepth() {
|
|
178
|
+
const ws = new WebSocket(BINANCE_DEPTH_URL);
|
|
179
|
+
ws.on("message", (raw) => {
|
|
180
|
+
try {
|
|
181
|
+
const msg = JSON.parse(raw.toString());
|
|
182
|
+
const bids = (msg.bids || [])
|
|
183
|
+
.map(([p, q]) => ({ price: Number(p), size: Number(q) }))
|
|
184
|
+
.filter((l) => l.size > 0);
|
|
185
|
+
const asks = (msg.asks || [])
|
|
186
|
+
.map(([p, q]) => ({ price: Number(p), size: Number(q) }))
|
|
187
|
+
.filter((l) => l.size > 0);
|
|
188
|
+
const bestBid = bids[0]?.price ?? null;
|
|
189
|
+
const bestAsk = asks[0]?.price ?? null;
|
|
190
|
+
if (bestBid && bestAsk) {
|
|
191
|
+
const mid = (bestBid + bestAsk) / 2;
|
|
192
|
+
state.binanceBook = {
|
|
193
|
+
bids,
|
|
194
|
+
asks,
|
|
195
|
+
midPrice: mid,
|
|
196
|
+
spreadPct: ((bestAsk - bestBid) / mid) * 100,
|
|
197
|
+
ts: Date.now()
|
|
198
|
+
};
|
|
199
|
+
// Sample price tick (rate-limited)
|
|
200
|
+
if (
|
|
201
|
+
state.priceTicks.length === 0 ||
|
|
202
|
+
Date.now() - (state.priceTicks._lastSampleAt || 0) > 1000
|
|
203
|
+
) {
|
|
204
|
+
state.priceTicks.push(mid);
|
|
205
|
+
if (state.priceTicks.length > PRICE_TICK_WINDOW) state.priceTicks.shift();
|
|
206
|
+
state.priceTicks._lastSampleAt = Date.now();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} catch (err) {
|
|
210
|
+
/* swallow parse */
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
ws.on("error", () => {});
|
|
214
|
+
ws.on("close", () => setTimeout(connectBinanceDepth, 3000));
|
|
215
|
+
return ws;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function connectBinanceTrades() {
|
|
219
|
+
const ws = new WebSocket(BINANCE_TRADE_URL);
|
|
220
|
+
ws.on("message", (raw) => {
|
|
221
|
+
try {
|
|
222
|
+
const msg = JSON.parse(raw.toString());
|
|
223
|
+
const trade = {
|
|
224
|
+
price: Number(msg.p),
|
|
225
|
+
size: Number(msg.q),
|
|
226
|
+
side: msg.m ? "sell" : "buy",
|
|
227
|
+
ts: Number(msg.T)
|
|
228
|
+
};
|
|
229
|
+
if (Number.isFinite(trade.price) && Number.isFinite(trade.size)) {
|
|
230
|
+
state.recentTrades.push(trade);
|
|
231
|
+
// Prune
|
|
232
|
+
const cutoff = Date.now() - FLOW_WINDOW_MS * 2;
|
|
233
|
+
while (state.recentTrades.length > 0 && state.recentTrades[0].ts < cutoff) {
|
|
234
|
+
state.recentTrades.shift();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} catch (err) {
|
|
238
|
+
/* swallow */
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
ws.on("error", () => {});
|
|
242
|
+
ws.on("close", () => setTimeout(connectBinanceTrades, 3000));
|
|
243
|
+
return ws;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function connectCoinbase() {
|
|
247
|
+
const ws = new WebSocket(COINBASE_URL);
|
|
248
|
+
ws.on("open", () => {
|
|
249
|
+
ws.send(
|
|
250
|
+
JSON.stringify({
|
|
251
|
+
type: "subscribe",
|
|
252
|
+
product_ids: ["ETH-USD"],
|
|
253
|
+
channels: ["ticker"]
|
|
254
|
+
})
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
ws.on("message", (raw) => {
|
|
258
|
+
try {
|
|
259
|
+
const msg = JSON.parse(raw.toString());
|
|
260
|
+
if (msg.type === "ticker" && msg.product_id === "ETH-USD") {
|
|
261
|
+
const bid = Number(msg.best_bid);
|
|
262
|
+
const ask = Number(msg.best_ask);
|
|
263
|
+
if (Number.isFinite(bid) && Number.isFinite(ask)) {
|
|
264
|
+
state.coinbaseMid = (bid + ask) / 2;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
} catch (err) {
|
|
268
|
+
/* swallow */
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
ws.on("error", () => {});
|
|
272
|
+
ws.on("close", () => setTimeout(connectCoinbase, 3000));
|
|
273
|
+
return ws;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ---- Main loop -----------------------------------------------------------
|
|
277
|
+
function pad(s, n) {
|
|
278
|
+
s = String(s);
|
|
279
|
+
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function evaluate() {
|
|
283
|
+
const mid = state.binanceBook.midPrice;
|
|
284
|
+
if (!mid) return;
|
|
285
|
+
|
|
286
|
+
const bookImbalance = computeBookImbalance(state.binanceBook.bids, state.binanceBook.asks, mid);
|
|
287
|
+
const bookDepth = computeBookDepth(state.binanceBook.bids, state.binanceBook.asks, mid);
|
|
288
|
+
const tradeImbalance = computeTradeImbalance(state.recentTrades, FLOW_WINDOW_MS, Date.now());
|
|
289
|
+
const { regime, stddevPct, driftPct } = classifyRegime(state.priceTicks);
|
|
290
|
+
const venueDivergenceBps =
|
|
291
|
+
state.coinbaseMid && state.coinbaseMid > 0
|
|
292
|
+
? ((mid - state.coinbaseMid) / state.coinbaseMid) * 10000
|
|
293
|
+
: null;
|
|
294
|
+
|
|
295
|
+
const result = computeDecision({
|
|
296
|
+
regime,
|
|
297
|
+
bookImbalance,
|
|
298
|
+
tradeImbalance,
|
|
299
|
+
spreadPct: state.binanceBook.spreadPct ?? 0,
|
|
300
|
+
venueDivergenceBps,
|
|
301
|
+
bookDepth
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
const entry = {
|
|
305
|
+
at: Date.now(),
|
|
306
|
+
decision: result.decision,
|
|
307
|
+
confidence: result.confidence,
|
|
308
|
+
midPrice: mid,
|
|
309
|
+
reasons: result.reasons,
|
|
310
|
+
regime,
|
|
311
|
+
bookImbalance,
|
|
312
|
+
tradeImbalance,
|
|
313
|
+
venueDivergenceBps,
|
|
314
|
+
stddevPct,
|
|
315
|
+
driftPct
|
|
316
|
+
};
|
|
317
|
+
state.decisions.push(entry);
|
|
318
|
+
|
|
319
|
+
const elapsedSec = ((Date.now() - state.startedAt) / 1000).toFixed(0);
|
|
320
|
+
console.log(
|
|
321
|
+
`[t+${pad(elapsedSec, 4)}s] ${pad(result.decision, 11)} conf=${pad(result.confidence, 3)}` +
|
|
322
|
+
` mid=${mid.toFixed(2)} regime=${pad(regime, 8)}` +
|
|
323
|
+
` book=${pad((bookImbalance * 100).toFixed(0) + "%", 5)}` +
|
|
324
|
+
` flow=${pad((tradeImbalance * 100).toFixed(0) + "%", 5)}` +
|
|
325
|
+
` div=${venueDivergenceBps === null ? " — " : (venueDivergenceBps.toFixed(1) + "bps")}` +
|
|
326
|
+
` ${result.reasons.slice(0, 3).join(", ")}`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function printSummary() {
|
|
331
|
+
console.log("\n" + "=".repeat(80));
|
|
332
|
+
console.log(`SUMMARY (duration ${DURATION_MIN} min, ${state.decisions.length} evaluations)`);
|
|
333
|
+
console.log("=".repeat(80));
|
|
334
|
+
|
|
335
|
+
const tally = { BUY: 0, SELL: 0, WAIT: 0, "STAND DOWN": 0 };
|
|
336
|
+
const regimeTally = { RANGING: 0, MIXED: 0, TRENDING: 0, UNKNOWN: 0 };
|
|
337
|
+
for (const d of state.decisions) {
|
|
338
|
+
tally[d.decision]++;
|
|
339
|
+
regimeTally[d.regime]++;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
console.log("\nDecision tally:");
|
|
343
|
+
for (const [k, v] of Object.entries(tally)) {
|
|
344
|
+
const pct = state.decisions.length > 0 ? ((v / state.decisions.length) * 100).toFixed(1) : "0.0";
|
|
345
|
+
console.log(` ${pad(k, 12)} ${pad(v, 5)} (${pct}%)`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
console.log("\nRegime time-in-state:");
|
|
349
|
+
for (const [k, v] of Object.entries(regimeTally)) {
|
|
350
|
+
const pct = state.decisions.length > 0 ? ((v / state.decisions.length) * 100).toFixed(1) : "0.0";
|
|
351
|
+
console.log(` ${pad(k, 12)} ${pad(v, 5)} (${pct}%)`);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Forward-price diagnostic: for each BUY/SELL signal, look at the price
|
|
355
|
+
// ~60s later and tally whether it moved in the signal's favor.
|
|
356
|
+
const directional = state.decisions.filter((d) => d.decision === "BUY" || d.decision === "SELL");
|
|
357
|
+
let evaluated = 0;
|
|
358
|
+
let correct = 0;
|
|
359
|
+
let totalForwardBps = 0;
|
|
360
|
+
for (const d of directional) {
|
|
361
|
+
const targetTs = d.at + FORWARD_PRICE_DELAY_MS;
|
|
362
|
+
// find first decision at or after target
|
|
363
|
+
const futureIdx = state.decisions.findIndex((x) => x.at >= targetTs);
|
|
364
|
+
if (futureIdx === -1) continue;
|
|
365
|
+
const futurePrice = state.decisions[futureIdx].midPrice;
|
|
366
|
+
const moveBps = ((futurePrice - d.midPrice) / d.midPrice) * 10000;
|
|
367
|
+
evaluated++;
|
|
368
|
+
const signedMove = d.decision === "BUY" ? moveBps : -moveBps;
|
|
369
|
+
totalForwardBps += signedMove;
|
|
370
|
+
if (signedMove > 0) correct++;
|
|
371
|
+
}
|
|
372
|
+
console.log("\nDirectional diagnostic (60s-forward, NOT a profitability test):");
|
|
373
|
+
if (evaluated === 0) {
|
|
374
|
+
console.log(" no eligible directional signals with forward window");
|
|
375
|
+
} else {
|
|
376
|
+
const hitRate = ((correct / evaluated) * 100).toFixed(1);
|
|
377
|
+
const avgSignedBps = (totalForwardBps / evaluated).toFixed(2);
|
|
378
|
+
console.log(` signals scored: ${evaluated}`);
|
|
379
|
+
console.log(` moved in our favour: ${correct} (${hitRate}%)`);
|
|
380
|
+
console.log(` avg signed move: ${avgSignedBps} bps`);
|
|
381
|
+
console.log(
|
|
382
|
+
` ↳ baseline ~50% means no edge; small samples (<50) are noise; gas + slippage`
|
|
383
|
+
);
|
|
384
|
+
console.log(` would eat anything below ~10 bps on Uniswap mainnet.`);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
console.log("\nReminders:");
|
|
388
|
+
console.log(" - This verifies the logic responds. It does NOT prove profitability.");
|
|
389
|
+
console.log(" - Binance signals ≠ Uniswap fills. Cross-venue + AMM curves differ.");
|
|
390
|
+
console.log(" - Real validation needs months of historical data + walk-forward.\n");
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ---- Run ------------------------------------------------------------------
|
|
394
|
+
console.log(`Starting decision stream test for ${DURATION_MIN} minutes…`);
|
|
395
|
+
console.log(`Streams: Binance depth + trade (ETHUSDT), Coinbase ticker (ETH-USD)`);
|
|
396
|
+
console.log(`Evaluating every ${EVAL_INTERVAL_MS}ms. Press Ctrl-C to abort.\n`);
|
|
397
|
+
|
|
398
|
+
connectBinanceDepth();
|
|
399
|
+
connectBinanceTrades();
|
|
400
|
+
connectCoinbase();
|
|
401
|
+
|
|
402
|
+
const evalTimer = setInterval(evaluate, EVAL_INTERVAL_MS);
|
|
403
|
+
const stopAt = Date.now() + DURATION_MIN * 60_000;
|
|
404
|
+
const stopTimer = setInterval(() => {
|
|
405
|
+
if (Date.now() >= stopAt) {
|
|
406
|
+
clearInterval(evalTimer);
|
|
407
|
+
clearInterval(stopTimer);
|
|
408
|
+
printSummary();
|
|
409
|
+
process.exit(0);
|
|
410
|
+
}
|
|
411
|
+
}, 1000);
|
|
412
|
+
|
|
413
|
+
process.on("SIGINT", () => {
|
|
414
|
+
console.log("\n\nAborted by user. Printing partial summary…");
|
|
415
|
+
printSummary();
|
|
416
|
+
process.exit(0);
|
|
417
|
+
});
|