bandkit 1.0.4 → 1.0.6

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 (44) hide show
  1. package/README.md +17 -14
  2. package/dist/AnimatedNumber.d.ts +10 -0
  3. package/dist/AnimatedNumber.d.ts.map +1 -0
  4. package/dist/AnimatedNumber.js +35 -0
  5. package/dist/AnimatedNumber.js.map +1 -0
  6. package/dist/BotDecisionPanel.d.ts.map +1 -1
  7. package/dist/BotDecisionPanel.js +15 -3
  8. package/dist/BotDecisionPanel.js.map +1 -1
  9. package/dist/ContractEventFeed.d.ts.map +1 -1
  10. package/dist/ContractEventFeed.js +14 -1
  11. package/dist/ContractEventFeed.js.map +1 -1
  12. package/dist/GasBadge.d.ts.map +1 -1
  13. package/dist/GasBadge.js +10 -2
  14. package/dist/GasBadge.js.map +1 -1
  15. package/dist/OrderBookWidget.d.ts.map +1 -1
  16. package/dist/OrderBookWidget.js +27 -3
  17. package/dist/OrderBookWidget.js.map +1 -1
  18. package/dist/RegimeBadge.d.ts.map +1 -1
  19. package/dist/RegimeBadge.js +3 -1
  20. package/dist/RegimeBadge.js.map +1 -1
  21. package/dist/Skeleton.d.ts +15 -0
  22. package/dist/Skeleton.d.ts.map +1 -0
  23. package/dist/Skeleton.js +26 -0
  24. package/dist/Skeleton.js.map +1 -0
  25. package/dist/StrategyValueSparkline.d.ts +9 -0
  26. package/dist/StrategyValueSparkline.d.ts.map +1 -0
  27. package/dist/StrategyValueSparkline.js +99 -0
  28. package/dist/StrategyValueSparkline.js.map +1 -0
  29. package/dist/TradeTape.d.ts.map +1 -1
  30. package/dist/TradeTape.js +20 -7
  31. package/dist/TradeTape.js.map +1 -1
  32. package/dist/decisionEngine.d.ts.map +1 -1
  33. package/dist/decisionEngine.js +51 -25
  34. package/dist/decisionEngine.js.map +1 -1
  35. package/dist/index.d.ts +4 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +6 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/useStrategyValueHistory.d.ts +24 -0
  40. package/dist/useStrategyValueHistory.d.ts.map +1 -0
  41. package/dist/useStrategyValueHistory.js +112 -0
  42. package/dist/useStrategyValueHistory.js.map +1 -0
  43. package/package.json +1 -1
  44. package/scripts/test-decision-stream.cjs +38 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bandkit",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
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",
@@ -97,6 +97,11 @@ function classifyRegime(prices) {
97
97
  return { regime, stddevPct, driftPct };
98
98
  }
99
99
 
100
+ function softCap(raw, ceiling = 88, scale = 55) {
101
+ const bounded = Math.max(0, raw);
102
+ return Math.round(ceiling * (1 - Math.exp(-bounded / scale)));
103
+ }
104
+
100
105
  function computeDecision(inputs) {
101
106
  const reasons = [];
102
107
  if (inputs.regime === "UNKNOWN") {
@@ -105,72 +110,87 @@ function computeDecision(inputs) {
105
110
  if (inputs.regime === "TRENDING") {
106
111
  return {
107
112
  decision: "STAND DOWN",
108
- confidence: 90,
113
+ confidence: 72,
109
114
  reasons: ["TRENDING regime — bot sits out"]
110
115
  };
111
116
  }
112
117
 
113
118
  let scoreBuy = 0;
114
119
  let scoreSell = 0;
115
- let confidence = inputs.regime === "RANGING" ? 45 : 30;
120
+ let baseScore = inputs.regime === "RANGING" ? 25 : 12;
116
121
  reasons.push(`Regime: ${inputs.regime}`);
117
122
 
123
+ const absBook = Math.abs(inputs.bookImbalance);
118
124
  if (inputs.bookImbalance >= 0.15) {
119
- scoreBuy += 25;
125
+ scoreBuy += 8 + Math.min(10, (absBook - 0.15) * 18);
120
126
  reasons.push(`Book +${(inputs.bookImbalance * 100).toFixed(0)}% bid`);
121
127
  } else if (inputs.bookImbalance <= -0.15) {
122
- scoreSell += 25;
128
+ scoreSell += 8 + Math.min(10, (absBook - 0.15) * 18);
123
129
  reasons.push(`Book ${(inputs.bookImbalance * 100).toFixed(0)}% ask`);
124
130
  } else {
125
131
  reasons.push(`Book ${(inputs.bookImbalance * 100).toFixed(0)}%`);
126
132
  }
127
133
 
134
+ const absFlow = Math.abs(inputs.tradeImbalance);
128
135
  if (inputs.tradeImbalance >= 0.10) {
129
- scoreBuy += 20;
136
+ scoreBuy += 6 + Math.min(8, (absFlow - 0.10) * 20);
130
137
  reasons.push(`Flow ${((0.5 + inputs.tradeImbalance / 2) * 100).toFixed(0)}% buys`);
131
138
  } else if (inputs.tradeImbalance <= -0.10) {
132
- scoreSell += 20;
139
+ scoreSell += 6 + Math.min(8, (absFlow - 0.10) * 20);
133
140
  reasons.push(`Flow ${((0.5 - inputs.tradeImbalance / 2) * 100).toFixed(0)}% sells`);
134
141
  } else {
135
142
  reasons.push("Flow balanced");
136
143
  }
137
144
 
138
145
  if (inputs.spreadPct > 0.05) {
139
- confidence -= 10;
146
+ baseScore -= 8;
140
147
  reasons.push(`Spread wide ${inputs.spreadPct.toFixed(3)}%`);
141
148
  } else {
142
- confidence += 5;
149
+ baseScore += 3;
143
150
  }
144
151
 
145
152
  if (inputs.venueDivergenceBps !== null && Number.isFinite(inputs.venueDivergenceBps)) {
153
+ const absDiv = Math.abs(inputs.venueDivergenceBps);
146
154
  if (inputs.venueDivergenceBps > 5) {
147
- scoreBuy += 12;
155
+ scoreBuy += 4 + Math.min(6, (absDiv - 5) * 0.6);
148
156
  reasons.push(`BIN +${inputs.venueDivergenceBps.toFixed(1)}bps`);
149
157
  } else if (inputs.venueDivergenceBps < -5) {
150
- scoreSell += 12;
158
+ scoreSell += 4 + Math.min(6, (absDiv - 5) * 0.6);
151
159
  reasons.push(`BIN ${inputs.venueDivergenceBps.toFixed(1)}bps`);
152
160
  }
153
161
  }
154
162
 
155
163
  if (inputs.bookDepth < 50) {
156
- confidence -= 15;
164
+ baseScore -= 10;
157
165
  reasons.push(`Thin book (${inputs.bookDepth.toFixed(0)} ETH)`);
158
166
  } else {
159
- confidence += 5;
167
+ baseScore += 3;
168
+ }
169
+
170
+ // Conflicting-signal penalty
171
+ if (scoreBuy > 0 && scoreSell > 0) {
172
+ if (scoreBuy >= scoreSell) {
173
+ scoreBuy -= scoreSell * 0.7;
174
+ scoreSell = 0;
175
+ } else {
176
+ scoreSell -= scoreBuy * 0.7;
177
+ scoreBuy = 0;
178
+ }
160
179
  }
161
180
 
162
181
  let decision = "WAIT";
163
- if (scoreBuy > scoreSell + 12) {
182
+ let confidence;
183
+ if (scoreBuy > scoreSell + 6) {
164
184
  decision = "BUY";
165
- confidence = Math.min(100, Math.max(0, confidence + scoreBuy));
166
- } else if (scoreSell > scoreBuy + 12) {
185
+ confidence = softCap(baseScore + scoreBuy);
186
+ } else if (scoreSell > scoreBuy + 6) {
167
187
  decision = "SELL";
168
- confidence = Math.min(100, Math.max(0, confidence + scoreSell));
188
+ confidence = softCap(baseScore + scoreSell);
169
189
  } else {
170
- confidence = Math.min(100, Math.max(0, confidence + Math.max(scoreBuy, scoreSell) / 2));
190
+ confidence = softCap(Math.max(baseScore / 2, 5), 55, 35);
171
191
  }
172
192
 
173
- return { decision, confidence: Math.round(confidence), reasons };
193
+ return { decision, confidence, reasons };
174
194
  }
175
195
 
176
196
  // ---- WebSocket setup -----------------------------------------------------