openbroker 1.0.57 → 1.0.59

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/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  All notable changes to Open Broker will be documented in this file.
4
4
 
5
+ ## [1.0.59] - 2026-03-11
6
+
7
+ ### Fixed
8
+ - **Plugin Tools Parity with CLI**: Reviewed all plugin tools against CLI commands and aligned behavior
9
+ - **`ob_account`**: Now uses `crossMarginSummary` (matches CLI), includes `signingWallet`, `walletType`, `marginRatio`, translates order sides (`B`→`buy`, `A`→`sell`), adds `side` field to positions
10
+ - **`ob_positions`**: Now fetches mark prices (`getAllMids`) and cumulative funding (`getUserFunding`) in parallel — includes `markPrice`, `notional`, `cumulativeFunding`, `liquidationDistance`, `maxLeverage`, `leverageType` fields matching CLI output
11
+ - **`ob_markets`**: Now includes HIP-3 markets (was main dex only), adds `change24h` field, sorts by volume, includes `type` field (`perp`/`hip3`)
12
+ - **`ob_buy`/`ob_sell`**: Fixed default slippage — was using `builderInfo.f` (1 bps) instead of config default (50 bps), causing market orders to fail with price-too-far errors
13
+
14
+ ### Added
15
+ - **SKILL.md CLI Fallback Guide**: Added troubleshooting section with plugin tool → CLI command mapping table, so agents can fall back to CLI when plugin tools return errors
16
+
17
+ ## [1.0.58] - 2026-03-11
18
+
19
+ ### Fixed
20
+ - **`ob_funding` HIP-3 Support**: Rewrote plugin tool to use `getMetaAndAssetCtxs()` + `getAllPerpMetas()` instead of `getPredictedFundings()` which only returned main dex data. Now correctly returns funding rates for HIP-3 assets like `xyz:GOLD`. Fixes "Cannot read properties of null" crash.
21
+ - **`ob_search` HIP-3 Filter**: Fixed inverted type filter condition — HIP-3 search was gated behind `typeFilter === 'perp'` instead of `typeFilter === 'hip3'`, causing empty results in some filter combinations. Added `funding` and `openInterest` fields to search results.
22
+
5
23
  ## [1.0.57] - 2026-03-10
6
24
 
7
25
  ### Fixed
package/SKILL.md CHANGED
@@ -4,7 +4,7 @@ description: Hyperliquid trading plugin with background position monitoring. Exe
4
4
  license: MIT
5
5
  compatibility: Requires Node.js 22+, network access to api.hyperliquid.xyz
6
6
  homepage: https://www.npmjs.com/package/openbroker
7
- metadata: {"author": "monemetrics", "version": "1.0.57", "openclaw": {"requires": {"bins": ["openbroker"], "env": ["HYPERLIQUID_PRIVATE_KEY"]}, "primaryEnv": "HYPERLIQUID_PRIVATE_KEY", "install": [{"id": "node", "kind": "node", "package": "openbroker", "bins": ["openbroker"], "label": "Install openbroker (npm)"}]}}
7
+ metadata: {"author": "monemetrics", "version": "1.0.59", "openclaw": {"requires": {"bins": ["openbroker"], "env": ["HYPERLIQUID_PRIVATE_KEY"]}, "primaryEnv": "HYPERLIQUID_PRIVATE_KEY", "install": [{"id": "node", "kind": "node", "package": "openbroker", "bins": ["openbroker"], "label": "Install openbroker (npm)"}]}}
8
8
  allowed-tools: ob_account ob_positions ob_funding ob_markets ob_search ob_spot ob_fills ob_orders ob_order_status ob_fees ob_candles ob_funding_history ob_trades ob_rate_limit ob_funding_scan ob_buy ob_sell ob_limit ob_trigger ob_tpsl ob_cancel ob_twap ob_bracket ob_chase ob_watcher_status Bash(openbroker:*)
9
9
  ---
10
10
 
@@ -46,6 +46,34 @@ Or with the `ob_search` plugin tool: `{ "query": "gold" }` or `{ "query": "oil",
46
46
 
47
47
  **HIP-3 assets use `dex:COIN` format** — e.g., `xyz:CL` not just `CL`. If you get an error like "No market data found", search for the asset to find the correct prefixed ticker. Common HIP-3 dexes: `xyz`, `flx`, `km`, `hyna`, `vntl`, `cash`.
48
48
 
49
+ ## Troubleshooting: CLI Fallback
50
+
51
+ If an `ob_*` plugin tool returns unexpected errors, empty results, or crashes, **fall back to the equivalent CLI command** via Bash. The CLI and plugin tools share the same core code, but the CLI has more mature error handling and output.
52
+
53
+ | Plugin Tool | CLI Equivalent |
54
+ |-------------|---------------|
55
+ | `ob_account` | `openbroker account --json` |
56
+ | `ob_positions` | `openbroker positions --json` |
57
+ | `ob_funding` | `openbroker funding --json --include-hip3` |
58
+ | `ob_markets` | `openbroker markets --json --include-hip3` |
59
+ | `ob_search` | `openbroker search --query <QUERY>` |
60
+ | `ob_buy` | `openbroker buy --coin <COIN> --size <SIZE>` |
61
+ | `ob_sell` | `openbroker sell --coin <COIN> --size <SIZE>` |
62
+ | `ob_limit` | `openbroker limit --coin <COIN> --side <SIDE> --size <SIZE> --price <PRICE>` |
63
+ | `ob_tpsl` | `openbroker tpsl --coin <COIN> --tp <PRICE> --sl <PRICE>` |
64
+ | `ob_cancel` | `openbroker cancel --all` or `--coin <COIN>` |
65
+ | `ob_fills` | `openbroker fills --json` |
66
+ | `ob_orders` | `openbroker orders --json` |
67
+ | `ob_funding_scan` | `openbroker funding-scan --json` |
68
+ | `ob_candles` | `openbroker candles --coin <COIN> --json` |
69
+
70
+ **When to use CLI fallback:**
71
+ - Plugin tool returns `null`, empty data, or throws an error
72
+ - You need data the plugin tool doesn't expose (e.g., `--verbose` debug output)
73
+ - Long-running operations (strategies, TWAP) — the CLI handles timeouts and progress better
74
+
75
+ Add `--dry` to any trading CLI command to preview without executing. Add `--json` to info commands for structured output.
76
+
49
77
  ## Command Reference
50
78
 
51
79
  ### Setup
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "openbroker",
3
3
  "name": "OpenBroker — Hyperliquid Trading",
4
- "version": "1.0.57",
4
+ "version": "1.0.59",
5
5
  "description": "Trade on Hyperliquid DEX with background position monitoring",
6
6
  "configSchema": {
7
7
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openbroker",
3
- "version": "1.0.57",
3
+ "version": "1.0.59",
4
4
  "description": "Hyperliquid trading CLI - execute orders, manage positions, and run trading strategies",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,18 +37,25 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
37
37
  const state = await client.getUserStateAll();
38
38
  const accountMode = await client.getAccountMode();
39
39
 
40
+ const margin = state.crossMarginSummary;
41
+ const accountValue = parseFloat(margin.accountValue);
42
+ const totalMarginUsed = parseFloat(margin.totalMarginUsed);
43
+
40
44
  const result: Record<string, unknown> = {
41
45
  address: client.address,
42
- isApiWallet: client.isApiWallet,
46
+ signingWallet: client.walletAddress,
47
+ walletType: client.isApiWallet ? 'api' : 'main',
43
48
  accountMode,
44
- equity: state.marginSummary.accountValue,
45
- totalNtlPos: state.marginSummary.totalNtlPos,
46
- totalMarginUsed: state.marginSummary.totalMarginUsed,
47
- withdrawable: state.marginSummary.withdrawable,
49
+ equity: margin.accountValue,
50
+ totalNtlPos: margin.totalNtlPos,
51
+ totalMarginUsed: margin.totalMarginUsed,
52
+ withdrawable: margin.withdrawable,
53
+ marginRatio: totalMarginUsed > 0 && accountValue > 0 ? totalMarginUsed / accountValue : 0,
48
54
  positions: state.assetPositions
49
55
  .filter(ap => parseFloat(ap.position.szi) !== 0)
50
56
  .map(ap => ({
51
57
  coin: ap.position.coin,
58
+ side: parseFloat(ap.position.szi) > 0 ? 'long' : 'short',
52
59
  size: ap.position.szi,
53
60
  entryPrice: ap.position.entryPx,
54
61
  positionValue: ap.position.positionValue,
@@ -63,7 +70,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
63
70
  result.openOrders = orders.map(o => ({
64
71
  coin: o.coin,
65
72
  oid: o.oid,
66
- side: o.side,
73
+ side: o.side === 'B' ? 'buy' : 'sell',
67
74
  size: o.sz,
68
75
  price: o.limitPx,
69
76
  orderType: o.orderType,
@@ -87,22 +94,44 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
87
94
  async execute(_id, params) {
88
95
  const { getClient } = await import('../core/client.js');
89
96
  const client = getClient();
90
- const state = await client.getUserStateAll();
97
+ const [state, mids, fundingHistory] = await Promise.all([
98
+ client.getUserStateAll(),
99
+ client.getAllMids(),
100
+ client.getUserFunding(),
101
+ ]);
102
+
103
+ // Sum cumulative funding per coin
104
+ const fundingByCoin = new Map<string, number>();
105
+ for (const entry of fundingHistory) {
106
+ const coin = entry.delta.coin;
107
+ const usdc = parseFloat(entry.delta.usdc);
108
+ fundingByCoin.set(coin, (fundingByCoin.get(coin) ?? 0) + usdc);
109
+ }
91
110
 
92
111
  let positions = state.assetPositions
93
112
  .filter(ap => parseFloat(ap.position.szi) !== 0)
94
- .map(ap => ({
95
- coin: ap.position.coin,
96
- side: parseFloat(ap.position.szi) > 0 ? 'long' : 'short',
97
- size: ap.position.szi,
98
- entryPrice: ap.position.entryPx,
99
- positionValue: ap.position.positionValue,
100
- unrealizedPnl: ap.position.unrealizedPnl,
101
- returnOnEquity: ap.position.returnOnEquity,
102
- liquidationPx: ap.position.liquidationPx,
103
- leverage: ap.position.leverage,
104
- marginUsed: ap.position.marginUsed,
105
- }));
113
+ .map(ap => {
114
+ const pos = ap.position;
115
+ const markPx = parseFloat(mids[pos.coin] || '0');
116
+ const liqPx = pos.liquidationPx ? parseFloat(pos.liquidationPx) : null;
117
+ return {
118
+ coin: pos.coin,
119
+ side: parseFloat(pos.szi) > 0 ? 'long' : 'short',
120
+ size: pos.szi,
121
+ entryPrice: pos.entryPx,
122
+ markPrice: markPx,
123
+ notional: Math.abs(parseFloat(pos.positionValue)),
124
+ unrealizedPnl: parseFloat(pos.unrealizedPnl),
125
+ returnOnEquity: parseFloat(pos.returnOnEquity),
126
+ cumulativeFunding: fundingByCoin.get(pos.coin) ?? 0,
127
+ marginUsed: parseFloat(pos.marginUsed),
128
+ leverage: `${pos.leverage.value}x`,
129
+ leverageType: pos.leverage.type,
130
+ liquidationPrice: liqPx,
131
+ liquidationDistance: liqPx && markPx ? Math.abs((markPx - liqPx) / markPx) : null,
132
+ maxLeverage: pos.maxLeverage,
133
+ };
134
+ });
106
135
 
107
136
  if (params.coin) {
108
137
  const coin = normalizeCoin(params.coin as string);
@@ -127,23 +156,59 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
127
156
  const { getClient } = await import('../core/client.js');
128
157
  const { annualizeFundingRate } = await import('../core/utils.js');
129
158
  const client = getClient();
130
- const raw = await client.getPredictedFundings();
131
159
 
132
- // raw is Array<[coin, Array<[venue, { fundingRate, nextFundingTime }]>]>
133
- let results = raw.map(([coin, venues]) => {
134
- // Use the first venue's funding rate
135
- const rate = venues.length > 0 ? parseFloat(venues[0][1].fundingRate) : 0;
136
- return {
137
- coin,
138
- fundingRate: rate,
139
- annualizedRate: annualizeFundingRate(rate),
140
- venues: venues.map(([venue, data]) => ({ venue, fundingRate: data.fundingRate })),
141
- };
142
- });
160
+ const filterCoin = params.coin ? normalizeCoin(params.coin as string) : undefined;
161
+ const includeHip3 = !filterCoin || filterCoin.includes(':');
143
162
 
144
- if (params.coin) {
145
- const coin = normalizeCoin(params.coin as string);
146
- results = results.filter(r => r.coin === coin);
163
+ let results: Array<{ coin: string; fundingRate: number; annualizedRate: number; openInterest: string; markPx: string; type: string }> = [];
164
+
165
+ // Main dex funding from meta
166
+ try {
167
+ const { meta, assetCtxs } = await client.getMetaAndAssetCtxs();
168
+ for (let i = 0; i < meta.universe.length; i++) {
169
+ const asset = meta.universe[i];
170
+ const ctx = assetCtxs[i];
171
+ if (!ctx) continue;
172
+ if (filterCoin && asset.name !== filterCoin) continue;
173
+
174
+ const rate = parseFloat(ctx.funding);
175
+ results.push({
176
+ coin: asset.name,
177
+ fundingRate: rate,
178
+ annualizedRate: annualizeFundingRate(rate),
179
+ openInterest: ctx.openInterest,
180
+ markPx: ctx.markPx,
181
+ type: 'perp',
182
+ });
183
+ }
184
+ } catch { /* skip */ }
185
+
186
+ // HIP-3 dex funding
187
+ if (includeHip3) {
188
+ try {
189
+ const allPerps = await client.getAllPerpMetas();
190
+ for (let dexIdx = 1; dexIdx < allPerps.length; dexIdx++) {
191
+ const dexData = allPerps[dexIdx];
192
+ if (!dexData?.meta?.universe) continue;
193
+
194
+ for (let i = 0; i < dexData.meta.universe.length; i++) {
195
+ const asset = dexData.meta.universe[i];
196
+ const ctx = dexData.assetCtxs[i];
197
+ if (!asset || !ctx) continue;
198
+ if (filterCoin && asset.name !== filterCoin) continue;
199
+
200
+ const rate = parseFloat(ctx.funding);
201
+ results.push({
202
+ coin: asset.name,
203
+ fundingRate: rate,
204
+ annualizedRate: annualizeFundingRate(rate),
205
+ openInterest: ctx.openInterest,
206
+ markPx: ctx.markPx,
207
+ type: 'hip3',
208
+ });
209
+ }
210
+ }
211
+ } catch { /* skip */ }
147
212
  }
148
213
 
149
214
  results.sort((a, b) => Math.abs(b.annualizedRate) - Math.abs(a.annualizedRate));
@@ -157,7 +222,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
157
222
 
158
223
  {
159
224
  name: 'ob_markets',
160
- description: 'View market data for Hyperliquid perpetuals (price, volume, open interest)',
225
+ description: 'View market data for Hyperliquid perpetuals (price, volume, open interest). Includes HIP-3 markets.',
161
226
  parameters: {
162
227
  type: 'object',
163
228
  properties: {
@@ -170,28 +235,75 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
170
235
  const client = getClient();
171
236
  const { meta, assetCtxs } = await client.getMetaAndAssetCtxs();
172
237
 
173
- let markets = meta.universe.map((asset, i) => ({
174
- coin: asset.name,
175
- szDecimals: asset.szDecimals,
176
- maxLeverage: asset.maxLeverage,
177
- markPx: assetCtxs[i]?.markPx,
178
- midPx: assetCtxs[i]?.midPx,
179
- oraclePx: assetCtxs[i]?.oraclePx,
180
- funding: assetCtxs[i]?.funding,
181
- openInterest: assetCtxs[i]?.openInterest,
182
- dayVolume: assetCtxs[i]?.dayNtlVlm,
183
- prevDayPx: assetCtxs[i]?.prevDayPx,
184
- }));
238
+ const filterCoin = params.coin ? normalizeCoin(params.coin as string) : undefined;
239
+ const includeHip3 = !filterCoin || filterCoin.includes(':');
240
+
241
+ interface MarketEntry { coin: string; type: string; markPx: number; oraclePx: number; change24h: number; dayVolume: number; openInterest: number; maxLeverage: number; szDecimals: number; funding: string }
242
+
243
+ const markets: MarketEntry[] = [];
244
+
245
+ // Main dex
246
+ for (let i = 0; i < meta.universe.length; i++) {
247
+ const asset = meta.universe[i];
248
+ const ctx = assetCtxs[i];
249
+ if (!ctx) continue;
250
+ if (filterCoin && asset.name !== filterCoin) continue;
251
+
252
+ const markPx = parseFloat(ctx.markPx);
253
+ const prevDayPx = parseFloat(ctx.prevDayPx);
254
+ markets.push({
255
+ coin: asset.name,
256
+ type: 'perp',
257
+ markPx,
258
+ oraclePx: parseFloat(ctx.oraclePx),
259
+ change24h: prevDayPx > 0 ? (markPx - prevDayPx) / prevDayPx : 0,
260
+ dayVolume: parseFloat(ctx.dayNtlVlm),
261
+ openInterest: parseFloat(ctx.openInterest),
262
+ maxLeverage: asset.maxLeverage,
263
+ szDecimals: asset.szDecimals,
264
+ funding: ctx.funding,
265
+ });
266
+ }
185
267
 
186
- if (params.coin) {
187
- const coin = normalizeCoin(params.coin as string);
188
- markets = markets.filter(m => m.coin === coin);
268
+ // HIP-3 dexes
269
+ if (includeHip3) {
270
+ try {
271
+ const allPerps = await client.getAllPerpMetas();
272
+ for (let dexIdx = 1; dexIdx < allPerps.length; dexIdx++) {
273
+ const dexData = allPerps[dexIdx];
274
+ if (!dexData?.meta?.universe) continue;
275
+ for (let i = 0; i < dexData.meta.universe.length; i++) {
276
+ const asset = dexData.meta.universe[i];
277
+ const ctx = dexData.assetCtxs[i];
278
+ if (!asset || !ctx) continue;
279
+ if (filterCoin && asset.name !== filterCoin) continue;
280
+
281
+ const markPx = parseFloat(ctx.markPx);
282
+ const prevDayPx = parseFloat(ctx.prevDayPx);
283
+ markets.push({
284
+ coin: asset.name,
285
+ type: 'hip3',
286
+ markPx,
287
+ oraclePx: parseFloat(ctx.oraclePx),
288
+ change24h: prevDayPx > 0 ? (markPx - prevDayPx) / prevDayPx : 0,
289
+ dayVolume: parseFloat(ctx.dayNtlVlm),
290
+ openInterest: parseFloat(ctx.openInterest),
291
+ maxLeverage: asset.maxLeverage,
292
+ szDecimals: asset.szDecimals,
293
+ funding: ctx.funding,
294
+ });
295
+ }
296
+ }
297
+ } catch { /* skip */ }
189
298
  }
190
299
 
300
+ // Sort by volume (matches CLI behavior)
301
+ markets.sort((a, b) => b.dayVolume - a.dayVolume);
302
+
191
303
  const top = (params.top as number) || 30;
192
- markets = markets.slice(0, top);
304
+ const result = filterCoin ? markets : markets.slice(0, top);
193
305
 
194
- return json({ markets });
306
+ return json({ markets: result });
195
307
  },
196
308
  },
197
309
 
@@ -228,7 +340,9 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
228
340
  coin: asset.name,
229
341
  type: 'perp',
230
342
  markPx: assetCtxs[i]?.markPx,
343
+ funding: assetCtxs[i]?.funding,
231
344
  dayVolume: assetCtxs[i]?.dayNtlVlm,
345
+ openInterest: assetCtxs[i]?.openInterest,
232
346
  maxLeverage: asset.maxLeverage,
233
347
  });
234
348
  }
@@ -236,24 +350,26 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
236
350
  } catch (e) { errors.push(`perp: ${e instanceof Error ? e.message : String(e)}`); }
237
351
  }
238
352
 
239
- // Search HIP-3 perps
240
- if (!typeFilter || typeFilter === 'hip3' || typeFilter === 'perp') {
353
+ // Search HIP-3 perps (always included unless filtering to perp-only or spot-only)
354
+ if (!typeFilter || typeFilter === 'hip3') {
241
355
  try {
242
356
  const allPerps = await client.getAllPerpMetas();
243
357
  for (let dexIdx = 1; dexIdx < allPerps.length; dexIdx++) {
244
358
  const dexData = allPerps[dexIdx];
245
- if (!dexData || !dexData.meta?.universe) continue;
359
+ if (!dexData?.meta?.universe) continue;
246
360
  for (let i = 0; i < dexData.meta.universe.length; i++) {
247
361
  const asset = dexData.meta.universe[i];
362
+ const ctx = dexData.assetCtxs[i];
248
363
  if (!asset) continue;
249
364
  if (asset.name.toUpperCase().includes(query)) {
250
365
  results.push({
251
- // API returns names already prefixed (e.g., "xyz:CL")
252
366
  coin: asset.name,
253
367
  type: 'hip3',
254
368
  dex: dexData.dexName,
255
- markPx: dexData.assetCtxs[i]?.markPx,
256
- dayVolume: dexData.assetCtxs[i]?.dayNtlVlm,
369
+ markPx: ctx?.markPx,
370
+ funding: ctx?.funding,
371
+ dayVolume: ctx?.dayNtlVlm,
372
+ openInterest: ctx?.openInterest,
257
373
  maxLeverage: asset.maxLeverage,
258
374
  });
259
375
  }
@@ -759,7 +875,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
759
875
 
760
876
  const coin = normalizeCoin(params.coin as string);
761
877
  const size = params.size as number;
762
- const slippageBps = (params.slippage as number) ?? client.builderInfo.f;
878
+ const slippageBps = params.slippage as number | undefined;
763
879
  const leverage = params.leverage as number | undefined;
764
880
 
765
881
  const mids = await client.getAllMids();
@@ -768,7 +884,8 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
768
884
 
769
885
  const szDecimals = await client.getSzDecimals(coin);
770
886
  const roundedSize = roundSize(size, szDecimals);
771
- const slippagePrice = getSlippagePrice(midPrice, true, slippageBps);
887
+ const effectiveSlippage = slippageBps ?? 50;
888
+ const slippagePrice = getSlippagePrice(midPrice, true, effectiveSlippage);
772
889
 
773
890
  if (params.dry) {
774
891
  return json({
@@ -778,7 +895,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
778
895
  size: roundedSize,
779
896
  midPrice,
780
897
  slippagePrice,
781
- slippageBps,
898
+ slippageBps: effectiveSlippage,
782
899
  leverage,
783
900
  });
784
901
  }
@@ -811,7 +928,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
811
928
 
812
929
  const coin = normalizeCoin(params.coin as string);
813
930
  const size = params.size as number;
814
- const slippageBps = (params.slippage as number) ?? client.builderInfo.f;
931
+ const slippageBps = params.slippage as number | undefined;
815
932
  const leverage = params.leverage as number | undefined;
816
933
 
817
934
  const mids = await client.getAllMids();
@@ -820,7 +937,8 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
820
937
 
821
938
  const szDecimals = await client.getSzDecimals(coin);
822
939
  const roundedSize = roundSize(size, szDecimals);
823
- const slippagePrice = getSlippagePrice(midPrice, false, slippageBps);
940
+ const effectiveSlippage = slippageBps ?? 50;
941
+ const slippagePrice = getSlippagePrice(midPrice, false, effectiveSlippage);
824
942
 
825
943
  if (params.dry) {
826
944
  return json({
@@ -830,7 +948,7 @@ export function createTools(watcher: PositionWatcher | null): PluginTool[] {
830
948
  size: roundedSize,
831
949
  midPrice,
832
950
  slippagePrice,
833
- slippageBps,
951
+ slippageBps: effectiveSlippage,
834
952
  leverage,
835
953
  });
836
954
  }