@metamask-previews/perps-controller 11.0.0-preview-9c0f91815 → 11.0.0-preview-e57e5c3dc

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 (82) hide show
  1. package/CHANGELOG.md +0 -49
  2. package/dist/PerpsController.cjs.map +1 -1
  3. package/dist/PerpsController.d.cts +1 -1
  4. package/dist/PerpsController.d.cts.map +1 -1
  5. package/dist/PerpsController.d.mts +1 -1
  6. package/dist/PerpsController.d.mts.map +1 -1
  7. package/dist/PerpsController.mjs.map +1 -1
  8. package/dist/constants/eventNames.cjs +0 -4
  9. package/dist/constants/eventNames.cjs.map +1 -1
  10. package/dist/constants/eventNames.d.cts +0 -3
  11. package/dist/constants/eventNames.d.cts.map +1 -1
  12. package/dist/constants/eventNames.d.mts +0 -3
  13. package/dist/constants/eventNames.d.mts.map +1 -1
  14. package/dist/constants/eventNames.mjs +0 -4
  15. package/dist/constants/eventNames.mjs.map +1 -1
  16. package/dist/constants/perpsConfig.cjs +1 -54
  17. package/dist/constants/perpsConfig.cjs.map +1 -1
  18. package/dist/constants/perpsConfig.d.cts +2 -71
  19. package/dist/constants/perpsConfig.d.cts.map +1 -1
  20. package/dist/constants/perpsConfig.d.mts +2 -71
  21. package/dist/constants/perpsConfig.d.mts.map +1 -1
  22. package/dist/constants/perpsConfig.mjs +0 -53
  23. package/dist/constants/perpsConfig.mjs.map +1 -1
  24. package/dist/index.cjs +3 -12
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +4 -4
  27. package/dist/index.d.cts.map +1 -1
  28. package/dist/index.d.mts +4 -4
  29. package/dist/index.d.mts.map +1 -1
  30. package/dist/index.mjs +2 -2
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/perpsErrorCodes.cjs +0 -20
  33. package/dist/perpsErrorCodes.cjs.map +1 -1
  34. package/dist/perpsErrorCodes.d.cts +0 -19
  35. package/dist/perpsErrorCodes.d.cts.map +1 -1
  36. package/dist/perpsErrorCodes.d.mts +0 -19
  37. package/dist/perpsErrorCodes.d.mts.map +1 -1
  38. package/dist/perpsErrorCodes.mjs +0 -20
  39. package/dist/perpsErrorCodes.mjs.map +1 -1
  40. package/dist/providers/HyperLiquidProvider.cjs +14 -1276
  41. package/dist/providers/HyperLiquidProvider.cjs.map +1 -1
  42. package/dist/providers/HyperLiquidProvider.d.cts.map +1 -1
  43. package/dist/providers/HyperLiquidProvider.d.mts.map +1 -1
  44. package/dist/providers/HyperLiquidProvider.mjs +17 -1279
  45. package/dist/providers/HyperLiquidProvider.mjs.map +1 -1
  46. package/dist/types/index.cjs.map +1 -1
  47. package/dist/types/index.d.cts +2 -40
  48. package/dist/types/index.d.cts.map +1 -1
  49. package/dist/types/index.d.mts +2 -40
  50. package/dist/types/index.d.mts.map +1 -1
  51. package/dist/types/index.mjs.map +1 -1
  52. package/dist/types/perps-types.cjs.map +1 -1
  53. package/dist/types/perps-types.d.cts +2 -33
  54. package/dist/types/perps-types.d.cts.map +1 -1
  55. package/dist/types/perps-types.d.mts +2 -33
  56. package/dist/types/perps-types.d.mts.map +1 -1
  57. package/dist/types/perps-types.mjs.map +1 -1
  58. package/dist/utils/hyperLiquidValidation.cjs +3 -192
  59. package/dist/utils/hyperLiquidValidation.cjs.map +1 -1
  60. package/dist/utils/hyperLiquidValidation.d.cts +3 -30
  61. package/dist/utils/hyperLiquidValidation.d.cts.map +1 -1
  62. package/dist/utils/hyperLiquidValidation.d.mts +3 -30
  63. package/dist/utils/hyperLiquidValidation.d.mts.map +1 -1
  64. package/dist/utils/hyperLiquidValidation.mjs +5 -194
  65. package/dist/utils/hyperLiquidValidation.mjs.map +1 -1
  66. package/dist/utils/orderCalculations.cjs +1 -131
  67. package/dist/utils/orderCalculations.cjs.map +1 -1
  68. package/dist/utils/orderCalculations.d.cts +3 -88
  69. package/dist/utils/orderCalculations.d.cts.map +1 -1
  70. package/dist/utils/orderCalculations.d.mts +3 -88
  71. package/dist/utils/orderCalculations.d.mts.map +1 -1
  72. package/dist/utils/orderCalculations.mjs +2 -128
  73. package/dist/utils/orderCalculations.mjs.map +1 -1
  74. package/dist/utils/orderTypes.cjs +4 -56
  75. package/dist/utils/orderTypes.cjs.map +1 -1
  76. package/dist/utils/orderTypes.d.cts +3 -32
  77. package/dist/utils/orderTypes.d.cts.map +1 -1
  78. package/dist/utils/orderTypes.d.mts +3 -32
  79. package/dist/utils/orderTypes.d.mts.map +1 -1
  80. package/dist/utils/orderTypes.mjs +3 -54
  81. package/dist/utils/orderTypes.mjs.map +1 -1
  82. package/package.json +2 -2
@@ -1,8 +1,8 @@
1
1
  import { BASIS_POINTS_DIVISOR } from "../constants/hyperLiquidConfig.mjs";
2
- import { DECIMAL_PRECISION_CONFIG, MAX_ORDER_MARGIN_BUFFER, ORDER_SLIPPAGE_CONFIG } from "../constants/perpsConfig.mjs";
2
+ import { MAX_ORDER_MARGIN_BUFFER, ORDER_SLIPPAGE_CONFIG } from "../constants/perpsConfig.mjs";
3
3
  import { PERPS_ERROR_CODES } from "../perpsErrorCodes.mjs";
4
4
  import { formatHyperLiquidPrice, formatHyperLiquidSize } from "./hyperLiquidAdapter.mjs";
5
- import { getTriggerDirection, isLimitExecutionOrderType, isTriggerOrderType, SCALE_ORDER_COUNT, toSDKTimeInForce } from "./orderTypes.mjs";
5
+ import { getTriggerDirection, isLimitExecutionOrderType, isTriggerOrderType, toSDKTimeInForce } from "./orderTypes.mjs";
6
6
  /**
7
7
  * Tolerance used when deciding whether a scaled size is already on the size
8
8
  * grid, guarding against floating-point representation error.
@@ -147,132 +147,6 @@ export function floorToSizeDecimals(size, szDecimals) {
147
147
  }
148
148
  return units / multiplier;
149
149
  }
150
- /**
151
- * The smallest price increment the venue will represent at a given price.
152
- *
153
- * HyperLiquid bounds a perp price two ways at once — a decimal-place cap that
154
- * depends on the asset's size precision, and a significant-figure cap — so the
155
- * tick widens as the price grows. Whichever bound is coarser at this price is
156
- * the tick.
157
- *
158
- * @param params - Tick parameters.
159
- * @param params.price - Price to measure the increment at.
160
- * @param params.szDecimals - The asset's size decimal precision.
161
- * @returns The tick size at that price.
162
- */
163
- export function getPriceTick(params) {
164
- const { price, szDecimals } = params;
165
- const byDecimals = Math.pow(10, -(DECIMAL_PRECISION_CONFIG.MaxPriceDecimals - szDecimals));
166
- const bySignificantFigures = Math.pow(10, Math.floor(Math.log10(Math.abs(price))) -
167
- (DECIMAL_PRECISION_CONFIG.MaxSignificantFigures - 1));
168
- return Math.max(byDecimals, bySignificantFigures);
169
- }
170
- /**
171
- * Price a chase order against the book it is chasing.
172
- *
173
- * The venue's own definition: a chase rests one tick *inside* the spread —
174
- * above the best bid for a buy, below the best ask for a sell — except when the
175
- * spread is already a single tick, where there is no room to improve and the
176
- * order joins the touch instead.
177
- *
178
- * Rests inside rather than at the touch because a chase is post-only: sitting
179
- * one tick ahead of the rest of the queue is the whole point, and joining the
180
- * touch would leave it behind every order already resting there.
181
- *
182
- * @param params - Quote parameters.
183
- * @param params.bestBid - Best bid, excluding the chase's own resting order.
184
- * @param params.bestAsk - Best ask, excluding the chase's own resting order.
185
- * @param params.isBuy - Which side the chase rests on.
186
- * @param params.szDecimals - The asset's size decimal precision.
187
- * @returns The formatted price the chase should rest at.
188
- */
189
- export function computeChaseQuotePrice(params) {
190
- const { bestBid, bestAsk, isBuy, szDecimals } = params;
191
- const reference = isBuy ? bestBid : bestAsk;
192
- const tick = getPriceTick({ price: reference, szDecimals });
193
- const improved = isBuy ? bestBid + tick : bestAsk - tick;
194
- // A single-tick spread leaves nowhere to improve to: the improved price would
195
- // cross, which a post-only order cannot do. Join the touch instead.
196
- const crosses = isBuy ? improved >= bestAsk : improved <= bestBid;
197
- return formatHyperLiquidPrice({
198
- price: crosses ? reference : improved,
199
- szDecimals,
200
- });
201
- }
202
- /**
203
- * Compute the price ladder a scale placement fans out over.
204
- *
205
- * The ladder is inclusive of both ends — the first rung sits exactly on
206
- * `minPrice`, the last exactly on `maxPrice` — so the range the caller asked
207
- * for is the range that actually reaches the exchange.
208
- *
209
- * @param params - Ladder parameters.
210
- * @param params.minPrice - Lowest price in the ladder.
211
- * @param params.maxPrice - Highest price in the ladder; must exceed `minPrice`.
212
- * @param params.count - Number of rungs.
213
- * @returns The rung prices, ascending.
214
- */
215
- export function computeScalePriceLadder(params) {
216
- const { minPrice, maxPrice, count } = params;
217
- if (!Number.isInteger(count) ||
218
- count < SCALE_ORDER_COUNT.min ||
219
- count > SCALE_ORDER_COUNT.max) {
220
- throw new Error(PERPS_ERROR_CODES.ORDER_SCALE_COUNT_INVALID);
221
- }
222
- if (!Number.isFinite(minPrice) ||
223
- !Number.isFinite(maxPrice) ||
224
- minPrice <= 0 ||
225
- maxPrice <= minPrice) {
226
- throw new Error(PERPS_ERROR_CODES.ORDER_SCALE_RANGE_INVALID);
227
- }
228
- const step = (maxPrice - minPrice) / (count - 1);
229
- // The top rung is assigned rather than accumulated: `minPrice + step * n`
230
- // drifts, and a ladder that stops short of the caller's maxPrice would quietly
231
- // narrow the range they asked for.
232
- return Array.from({ length: count }, (_unused, index) => index === count - 1 ? maxPrice : minPrice + step * index);
233
- }
234
- /**
235
- * Split a scale placement's total size across its ladder rungs.
236
- *
237
- * The split is done in whole units of the asset's size grid rather than in
238
- * decimal sizes: dividing and re-flooring in floating point loses a sub-unit of
239
- * dust on every rung, and a ladder that submits less than the size that was
240
- * validated is not the order the caller placed. Whatever does not divide evenly
241
- * goes onto the first rung, so the slices sum to exactly `totalSize`.
242
- *
243
- * The total is expected to sit on the grid already — `calculateFinalPositionSize`
244
- * floors it there — so rounding onto the grid here only absorbs representation
245
- * error. A total too small to give every rung a whole unit is rejected: placing
246
- * fewer orders than asked for would silently change the strategy.
247
- *
248
- * @param params - Split parameters.
249
- * @param params.totalSize - Total size to distribute.
250
- * @param params.count - Number of rungs.
251
- * @param params.szDecimals - The asset's size decimal precision.
252
- * @returns One size string per rung, in ladder order.
253
- */
254
- export function splitScaleSizes(params) {
255
- const { totalSize, count, szDecimals } = params;
256
- // Checked here as well as in `computeScalePriceLadder`: this is exported on
257
- // its own, and a count of zero would otherwise return an empty split while a
258
- // fractional one would return slices that do not sum to the total.
259
- if (!Number.isInteger(count) ||
260
- count < SCALE_ORDER_COUNT.min ||
261
- count > SCALE_ORDER_COUNT.max) {
262
- throw new Error(PERPS_ERROR_CODES.ORDER_SCALE_COUNT_INVALID);
263
- }
264
- const multiplier = Math.pow(10, szDecimals);
265
- const totalUnits = Math.round(totalSize * multiplier);
266
- if (!Number.isSafeInteger(totalUnits) || totalUnits < count) {
267
- throw new Error(PERPS_ERROR_CODES.ORDER_SCALE_SIZE_TOO_SMALL);
268
- }
269
- const sliceUnits = Math.floor(totalUnits / count);
270
- const remainderUnits = totalUnits - sliceUnits * count;
271
- return Array.from({ length: count }, (_unused, index) => formatHyperLiquidSize({
272
- size: (index === 0 ? sliceUnits + remainderUnits : sliceUnits) / multiplier,
273
- szDecimals,
274
- }));
275
- }
276
150
  /**
277
151
  * Calculates final position size using USD as source of truth with price validation
278
152
  *
@@ -1 +1 @@
1
- {"version":3,"file":"orderCalculations.mjs","sourceRoot":"","sources":["../../src/utils/orderCalculations.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Hex } from '@metamask/utils';\n\nimport { BASIS_POINTS_DIVISOR } from '../constants/hyperLiquidConfig.js';\nimport {\n DECIMAL_PRECISION_CONFIG,\n MAX_ORDER_MARGIN_BUFFER,\n ORDER_SLIPPAGE_CONFIG,\n} from '../constants/perpsConfig.js';\nimport { PERPS_ERROR_CODES } from '../perpsErrorCodes.js';\nimport type { SDKOrderParams } from '../types/hyperliquid-types.js';\nimport type { PerpsDebugLogger } from '../types/index.js';\nimport type { OrdinaryOrderType, OrderType } from '../types/perps-types.js';\nimport {\n formatHyperLiquidPrice,\n formatHyperLiquidSize,\n} from './hyperLiquidAdapter.js';\nimport {\n getTriggerDirection,\n isLimitExecutionOrderType,\n isTriggerOrderType,\n SCALE_ORDER_COUNT,\n toSDKTimeInForce,\n} from './orderTypes.js';\n\n/**\n * Optional debug logger for order calculation functions.\n * When provided, enables detailed logging for debugging.\n */\nexport type OrderCalculationsDebugLogger = PerpsDebugLogger | undefined;\n\n/**\n * Tolerance used when deciding whether a scaled size is already on the size\n * grid, guarding against floating-point representation error.\n */\nconst FLOAT_TOLERANCE = 1e-6;\n\ntype PositionSizeParams = {\n amount: string;\n price: number;\n szDecimals: number;\n};\n\ntype MarginRequiredParams = {\n amount: string;\n leverage: number;\n};\n\ntype MaxAllowedAmountParams = {\n spendableBalance: number;\n assetPrice: number;\n assetSzDecimals: number;\n leverage: number;\n // Placement type. Only a resting order is margin-checked against its own\n // submitted price; a marketable order is charged at the fill price. Defaults\n // to 'market'.\n orderType?: 'market' | 'limit';\n // Price a limit order will rest at. Needed to size a limit order that rests\n // above the market price.\n limitPrice?: number;\n};\n\n// Advanced order calculation interfaces\nexport type CalculateFinalPositionSizeParams = {\n usdAmount?: string;\n size?: string;\n currentPrice: number;\n priceAtCalculation?: number;\n maxSlippageBps?: number;\n szDecimals: number;\n leverage?: number;\n // Reduce-only orders (position closes) may never round up: HyperLiquid\n // rejects a reduce-only order whose size exceeds the live position with\n // \"Reduce only order would increase position\".\n reduceOnly?: boolean;\n debugLogger?: OrderCalculationsDebugLogger;\n};\n\nexport type CalculateFinalPositionSizeResult = {\n finalPositionSize: number;\n};\n\nexport type CalculateOrderPriceAndSizeParams = {\n // Strategy placements are excluded: each derives its own prices and sizes and\n // never reaches this helper. Left in, `chase` would fall through as a\n // limit-priced order it carries no price for, and `twap`/`scale` would be\n // serialized as ordinary FrontendMarket orders.\n orderType: OrdinaryOrderType;\n isBuy: boolean;\n finalPositionSize: number;\n currentPrice: number;\n limitPrice?: string;\n // Trigger price for stop_*/take_profit_* placements. Required for those types:\n // `*_limit` executes at `limitPrice`, `*_market` derives a slippage-capped\n // limit price from this trigger price.\n triggerPrice?: string;\n // Max slippage in basis points (e.g. 300 = 3%). Applied to market orders and to\n // market-executing trigger orders (where it caps the limit price derived from\n // the trigger price); limit orders use limitPrice directly. Falls back to\n // ORDER_SLIPPAGE_CONFIG.DefaultMarketSlippageBps for market orders and\n // .DefaultTpslSlippageBps for market-executing triggers.\n maxSlippageBps?: number;\n szDecimals: number;\n};\n\nexport type CalculateOrderPriceAndSizeResult = {\n orderPrice: number;\n formattedSize: string;\n formattedPrice: string;\n};\n\nexport type BuildOrdersArrayParams = {\n assetId: number;\n isBuy: boolean;\n formattedPrice: string;\n formattedSize: string;\n reduceOnly: boolean;\n // Strategy placements never reach here; see `CalculateOrderPriceAndSizeParams`.\n orderType: OrdinaryOrderType;\n timeInForce?: 'GTC' | 'IOC' | 'ALO';\n clientOrderId?: string;\n // Trigger price for stop_*/take_profit_* placements (required for those types)\n triggerPrice?: string;\n takeProfitPrice?: string;\n stopLossPrice?: string;\n // Partial TP/SL sizes; default to the full order size when omitted\n takeProfitSize?: string;\n stopLossSize?: string;\n szDecimals: number;\n grouping?: 'na' | 'normalTpsl' | 'positionTpsl';\n};\n\nexport type BuildOrdersArrayResult = {\n orders: SDKOrderParams[];\n grouping: 'na' | 'normalTpsl' | 'positionTpsl';\n};\n\n/**\n * Calculate position size based on USD amount and asset price\n *\n * @param params - Amount in USD, current asset price, and required decimal precision\n * @returns Position size formatted to the asset's decimal precision\n */\nexport function calculatePositionSize(params: PositionSizeParams): string {\n const { amount, price, szDecimals } = params;\n\n // Validate required parameters\n if (szDecimals === undefined || szDecimals === null) {\n throw new Error('szDecimals is required for position size calculation');\n }\n if (szDecimals < 0) {\n throw new Error(`szDecimals must be >= 0, got: ${szDecimals}`);\n }\n\n const amountNum = parseFloat(amount || '0');\n\n if (isNaN(amountNum) || isNaN(price) || amountNum === 0 || price === 0) {\n return (0).toFixed(szDecimals);\n }\n\n const positionSize = amountNum / price;\n const multiplier = Math.pow(10, szDecimals);\n let rounded = Math.round(positionSize * multiplier) / multiplier;\n\n // Ensure rounded size meets requested USD (fix validation gap)\n const actualUsd = rounded * price;\n if (actualUsd < amountNum) {\n rounded += 1 / multiplier;\n }\n\n return rounded.toFixed(szDecimals);\n}\n\n/**\n * Calculate margin required for a position\n *\n * @param params - Position amount and leverage\n * @returns Margin required formatted to 2 decimal places\n */\nexport function calculateMarginRequired(params: MarginRequiredParams): string {\n const { amount, leverage } = params;\n const amountNum = parseFloat(amount || '0');\n\n if (\n isNaN(amountNum) ||\n isNaN(leverage) ||\n amountNum === 0 ||\n leverage === 0\n ) {\n return '0.00';\n }\n\n return (amountNum / leverage).toFixed(2);\n}\n\nexport function getMaxAllowedAmount(params: MaxAllowedAmountParams): number {\n const {\n spendableBalance,\n assetPrice,\n assetSzDecimals,\n leverage,\n orderType = 'market',\n limitPrice,\n } = params;\n if (spendableBalance === 0 || !assetPrice || assetSzDecimals === undefined) {\n return 0;\n }\n\n // HyperLiquid reserves initial margin for a RESTING order against the price\n // the order is submitted at, not the market price its size was derived from.\n // A limit order resting above the market price - typically a sell - therefore\n // needs more margin than a market-priced notional budgets for, and the\n // exchange refuses it with \"insufficient margin to place order\". Price the max\n // off that submitted price instead. A marketable order is charged at the fill\n // price, so it needs no adjustment.\n const executionPriceRatio =\n orderType === 'limit' && limitPrice && limitPrice > assetPrice\n ? limitPrice / assetPrice\n : 1;\n\n // The theoretical maximum is spendableBalance * leverage, expressed in the\n // market-price notional the caller works with.\n const theoreticalMax = (spendableBalance * leverage) / executionPriceRatio;\n\n // But we need to account for position size rounding\n // Find the largest whole dollar amount that fits within this limit\n let maxAmount = Math.floor(theoreticalMax);\n\n // Verify this amount doesn't exceed available balance after rounding\n const testPositionSize = calculatePositionSize({\n amount: maxAmount.toString(),\n price: assetPrice,\n szDecimals: assetSzDecimals,\n });\n\n const actualNotionalValue =\n parseFloat(testPositionSize) * assetPrice * executionPriceRatio;\n const requiredMargin = actualNotionalValue / leverage;\n\n // If rounding caused us to exceed available balance, step down by one position increment\n if (requiredMargin > spendableBalance) {\n const minPositionSizeIncrement = 1 / Math.pow(10, assetSzDecimals);\n const positionSizeIncrementUsd = Math.ceil(\n minPositionSizeIncrement * assetPrice,\n );\n maxAmount -= positionSizeIncrementUsd;\n }\n\n // Apply margin buffer to reduce \"Insufficient margin\" rejections from the exchange\n // (fees, rounding, and exchange-side checks can make 100% theoretical max fail)\n const bufferedMax = maxAmount * (1 - MAX_ORDER_MARGIN_BUFFER);\n\n return Math.max(0, Math.floor(bufferedMax));\n}\n\n/**\n * Round a size down onto the asset's size grid.\n *\n * Used for reduce-only orders, where rounding up would push the size past the\n * live position size. Values already on the grid are snapped rather than\n * truncated, because floating-point math can leave them just below a grid\n * point (0.0123 * 10000 === 122.99999999999999) and truncating would drop a\n * whole increment.\n *\n * The result is never greater than `size`, for negative sizes as well as\n * positive: the snap only ever recovers a grid point the input already\n * represents, so a value genuinely below a grid point is stepped down even when\n * the tolerance would have reached the point above it.\n *\n * A size whose scaled form reaches `2^53` is returned unchanged: doubles cannot\n * represent consecutive integers there, so the grid is finer than the spacing\n * between representable values and there is nothing to round down to.\n *\n * @param size - Size to round down.\n * @param szDecimals - The asset's size decimal precision.\n * @returns The size rounded down onto the size grid, never exceeding `size`.\n */\nexport function floorToSizeDecimals(size: number, szDecimals: number): number {\n const multiplier = Math.pow(10, szDecimals);\n const scaled = size * multiplier;\n\n // Past 2^53 a double cannot represent consecutive integers, so `units -= 1`\n // below would be a no-op and the step-down loop would never terminate. The\n // size grid is finer than the spacing between representable values at that\n // magnitude, so there is no increment to shave: return the input unchanged.\n if (!Number.isFinite(scaled) || Math.abs(scaled) >= Number.MAX_SAFE_INTEGER) {\n return size;\n }\n\n const nearest = Math.round(scaled);\n // The tolerance scales with the magnitude, because double-precision error\n // does too: a fixed epsilon would stop absorbing representation error for\n // sizes that scale past ~1e10 and would then shave off a whole increment.\n const tolerance = Math.max(\n FLOAT_TOLERANCE,\n Math.abs(scaled) * Number.EPSILON * 8,\n );\n let units =\n Math.abs(scaled - nearest) < tolerance ? nearest : Math.floor(scaled);\n\n // Step down until the result no longer exceeds the input. One pass is not\n // enough: a tolerance wide enough to absorb representation error at large\n // magnitudes also reaches the next grid point, and for an input less than half\n // an ulp below a grid point `size * multiplier` evaluates to exactly that grid\n // integer, so flooring the scaled value returns the same too-large result.\n // The comparison alone is the whole termination condition: for a non-negative\n // size the loop stops at or before zero, and for a negative size it stops once\n // the value is no longer above the input. Guarding on `units` instead would\n // skip a negative size below the tolerance, which snaps to `-0` — and\n // `-0 !== 0` is false. The 2^53 bail-out above keeps this bounded.\n while (units / multiplier > size) {\n units -= 1;\n }\n\n return units / multiplier;\n}\n\n/**\n * The smallest price increment the venue will represent at a given price.\n *\n * HyperLiquid bounds a perp price two ways at once — a decimal-place cap that\n * depends on the asset's size precision, and a significant-figure cap — so the\n * tick widens as the price grows. Whichever bound is coarser at this price is\n * the tick.\n *\n * @param params - Tick parameters.\n * @param params.price - Price to measure the increment at.\n * @param params.szDecimals - The asset's size decimal precision.\n * @returns The tick size at that price.\n */\nexport function getPriceTick(params: {\n price: number;\n szDecimals: number;\n}): number {\n const { price, szDecimals } = params;\n\n const byDecimals = Math.pow(\n 10,\n -(DECIMAL_PRECISION_CONFIG.MaxPriceDecimals - szDecimals),\n );\n const bySignificantFigures = Math.pow(\n 10,\n Math.floor(Math.log10(Math.abs(price))) -\n (DECIMAL_PRECISION_CONFIG.MaxSignificantFigures - 1),\n );\n\n return Math.max(byDecimals, bySignificantFigures);\n}\n\n/**\n * Price a chase order against the book it is chasing.\n *\n * The venue's own definition: a chase rests one tick *inside* the spread —\n * above the best bid for a buy, below the best ask for a sell — except when the\n * spread is already a single tick, where there is no room to improve and the\n * order joins the touch instead.\n *\n * Rests inside rather than at the touch because a chase is post-only: sitting\n * one tick ahead of the rest of the queue is the whole point, and joining the\n * touch would leave it behind every order already resting there.\n *\n * @param params - Quote parameters.\n * @param params.bestBid - Best bid, excluding the chase's own resting order.\n * @param params.bestAsk - Best ask, excluding the chase's own resting order.\n * @param params.isBuy - Which side the chase rests on.\n * @param params.szDecimals - The asset's size decimal precision.\n * @returns The formatted price the chase should rest at.\n */\nexport function computeChaseQuotePrice(params: {\n bestBid: number;\n bestAsk: number;\n isBuy: boolean;\n szDecimals: number;\n}): string {\n const { bestBid, bestAsk, isBuy, szDecimals } = params;\n\n const reference = isBuy ? bestBid : bestAsk;\n const tick = getPriceTick({ price: reference, szDecimals });\n const improved = isBuy ? bestBid + tick : bestAsk - tick;\n\n // A single-tick spread leaves nowhere to improve to: the improved price would\n // cross, which a post-only order cannot do. Join the touch instead.\n const crosses = isBuy ? improved >= bestAsk : improved <= bestBid;\n\n return formatHyperLiquidPrice({\n price: crosses ? reference : improved,\n szDecimals,\n });\n}\n\n/**\n * Compute the price ladder a scale placement fans out over.\n *\n * The ladder is inclusive of both ends — the first rung sits exactly on\n * `minPrice`, the last exactly on `maxPrice` — so the range the caller asked\n * for is the range that actually reaches the exchange.\n *\n * @param params - Ladder parameters.\n * @param params.minPrice - Lowest price in the ladder.\n * @param params.maxPrice - Highest price in the ladder; must exceed `minPrice`.\n * @param params.count - Number of rungs.\n * @returns The rung prices, ascending.\n */\nexport function computeScalePriceLadder(params: {\n minPrice: number;\n maxPrice: number;\n count: number;\n}): number[] {\n const { minPrice, maxPrice, count } = params;\n\n if (\n !Number.isInteger(count) ||\n count < SCALE_ORDER_COUNT.min ||\n count > SCALE_ORDER_COUNT.max\n ) {\n throw new Error(PERPS_ERROR_CODES.ORDER_SCALE_COUNT_INVALID);\n }\n if (\n !Number.isFinite(minPrice) ||\n !Number.isFinite(maxPrice) ||\n minPrice <= 0 ||\n maxPrice <= minPrice\n ) {\n throw new Error(PERPS_ERROR_CODES.ORDER_SCALE_RANGE_INVALID);\n }\n\n const step = (maxPrice - minPrice) / (count - 1);\n // The top rung is assigned rather than accumulated: `minPrice + step * n`\n // drifts, and a ladder that stops short of the caller's maxPrice would quietly\n // narrow the range they asked for.\n return Array.from({ length: count }, (_unused, index) =>\n index === count - 1 ? maxPrice : minPrice + step * index,\n );\n}\n\n/**\n * Split a scale placement's total size across its ladder rungs.\n *\n * The split is done in whole units of the asset's size grid rather than in\n * decimal sizes: dividing and re-flooring in floating point loses a sub-unit of\n * dust on every rung, and a ladder that submits less than the size that was\n * validated is not the order the caller placed. Whatever does not divide evenly\n * goes onto the first rung, so the slices sum to exactly `totalSize`.\n *\n * The total is expected to sit on the grid already — `calculateFinalPositionSize`\n * floors it there — so rounding onto the grid here only absorbs representation\n * error. A total too small to give every rung a whole unit is rejected: placing\n * fewer orders than asked for would silently change the strategy.\n *\n * @param params - Split parameters.\n * @param params.totalSize - Total size to distribute.\n * @param params.count - Number of rungs.\n * @param params.szDecimals - The asset's size decimal precision.\n * @returns One size string per rung, in ladder order.\n */\nexport function splitScaleSizes(params: {\n totalSize: number;\n count: number;\n szDecimals: number;\n}): string[] {\n const { totalSize, count, szDecimals } = params;\n\n // Checked here as well as in `computeScalePriceLadder`: this is exported on\n // its own, and a count of zero would otherwise return an empty split while a\n // fractional one would return slices that do not sum to the total.\n if (\n !Number.isInteger(count) ||\n count < SCALE_ORDER_COUNT.min ||\n count > SCALE_ORDER_COUNT.max\n ) {\n throw new Error(PERPS_ERROR_CODES.ORDER_SCALE_COUNT_INVALID);\n }\n\n const multiplier = Math.pow(10, szDecimals);\n const totalUnits = Math.round(totalSize * multiplier);\n\n if (!Number.isSafeInteger(totalUnits) || totalUnits < count) {\n throw new Error(PERPS_ERROR_CODES.ORDER_SCALE_SIZE_TOO_SMALL);\n }\n\n const sliceUnits = Math.floor(totalUnits / count);\n const remainderUnits = totalUnits - sliceUnits * count;\n\n return Array.from({ length: count }, (_unused, index) =>\n formatHyperLiquidSize({\n size:\n (index === 0 ? sliceUnits + remainderUnits : sliceUnits) / multiplier,\n szDecimals,\n }),\n );\n}\n\n/**\n * Calculates final position size using USD as source of truth with price validation\n *\n * This function implements the hybrid approach where USD is the source of truth,\n * but includes price staleness validation and proper rounding to prevent precision loss.\n *\n * @param params - USD amount, size, prices, and configuration\n * @returns Final position size as a number\n */\nexport function calculateFinalPositionSize(\n params: CalculateFinalPositionSizeParams,\n): CalculateFinalPositionSizeResult {\n const {\n usdAmount,\n size,\n currentPrice,\n priceAtCalculation,\n maxSlippageBps,\n szDecimals,\n leverage,\n reduceOnly,\n debugLogger,\n } = params;\n\n let finalPositionSize: number;\n\n // Validate price staleness whenever the caller supplied a calculation-time\n // price. This runs before the sizing branches on purpose: a full close submits\n // the exact live position size rather than a USD-derived one, and it must still\n // be rejected when the price has moved past the caller's tolerance.\n if (priceAtCalculation) {\n const priceDeltaBps = Math.abs(\n ((currentPrice - priceAtCalculation) / priceAtCalculation) * 10000,\n );\n const maxSlippageBpsValue =\n maxSlippageBps ?? ORDER_SLIPPAGE_CONFIG.DefaultMarketSlippageBps;\n\n if (priceDeltaBps > maxSlippageBpsValue) {\n throw new Error(\n `Price moved too much: ${priceDeltaBps.toFixed(0)} bps (max: ${maxSlippageBpsValue} bps). ` +\n `Expected: ${priceAtCalculation.toFixed(2)}, Current: ${currentPrice.toFixed(2)}`,\n );\n }\n\n debugLogger?.log('Price validation passed:', {\n priceAtCalculation,\n currentPrice,\n deltaBps: priceDeltaBps.toFixed(2),\n maxSlippageBps: maxSlippageBpsValue,\n });\n }\n\n if (usdAmount && parseFloat(usdAmount) > 0) {\n // USD amount provided - use it as source of truth\n const usdValue = parseFloat(usdAmount);\n\n // Recalculate position size with fresh price\n finalPositionSize = usdValue / currentPrice;\n\n // A reduce-only order may never exceed the size the caller asked to close:\n // that size is already clamped to the live position, while the USD amount was\n // computed against an older price and can imply a larger size after an\n // adverse move. Capping here keeps USD accuracy in the common case and makes\n // the caller's clamp binding.\n if (reduceOnly && size) {\n const requestedSize = parseFloat(size);\n\n // A supplied size must be positive, or the cap below would submit a\n // zero/negative order. Reject it rather than silently falling back to the\n // USD-derived size, matching how closePosition treats the same input.\n if (!Number.isFinite(requestedSize) || requestedSize <= 0) {\n throw new Error(PERPS_ERROR_CODES.ORDER_SIZE_POSITIVE);\n }\n\n finalPositionSize = Math.min(finalPositionSize, requestedSize);\n }\n\n // 3. Apply size decimals rounding (reduce-only never rounds up)\n const multiplier = Math.pow(10, szDecimals);\n const sizeBeforeRounding = finalPositionSize;\n finalPositionSize = reduceOnly\n ? floorToSizeDecimals(finalPositionSize, szDecimals)\n : Math.round(finalPositionSize * multiplier) / multiplier;\n\n // Rounding down can zero out a reduce-only order whose USD value is worth\n // less than one size increment. Fail with a clear error instead of\n // submitting a size of \"0\" the exchange will reject.\n if (reduceOnly && finalPositionSize <= 0 && sizeBeforeRounding > 0) {\n throw new Error(PERPS_ERROR_CODES.ORDER_SIZE_POSITIVE);\n }\n\n // 4. Ensure rounded size meets requested USD (fix validation gap).\n // Skipped for reduce-only orders: adding an increment there would submit\n // more than the position holds and HyperLiquid rejects the order.\n let actualNotionalValue = finalPositionSize * currentPrice;\n if (!reduceOnly && actualNotionalValue < usdValue) {\n // Add 1 minimum increment to meet requested USD\n finalPositionSize += 1 / multiplier;\n actualNotionalValue = finalPositionSize * currentPrice;\n\n debugLogger?.log('Position size adjusted to meet USD minimum:', {\n requestedUsd: usdValue,\n beforeAdjustment: finalPositionSize - 1 / multiplier,\n afterAdjustment: finalPositionSize,\n actualUsd: actualNotionalValue,\n });\n }\n\n const requiredMargin = actualNotionalValue / (leverage ?? 1);\n\n // Log if rounding caused significant difference\n const usdDifference = Math.abs(actualNotionalValue - usdValue);\n if (usdDifference > 0.01) {\n debugLogger?.log(\n 'Position size rounding caused USD difference (acceptable):',\n {\n requestedUsd: usdValue,\n actualUsd: actualNotionalValue,\n difference: usdDifference,\n positionSize: finalPositionSize,\n },\n );\n }\n\n debugLogger?.log('Recalculated position size with fresh price:', {\n usdAmount: usdValue,\n priceAtCalculation,\n currentPrice,\n originalSize: size,\n recalculatedSize: finalPositionSize,\n requiredMargin,\n minIncrement: 1 / multiplier,\n });\n } else {\n // Legacy: Use provided size (backward compatibility)\n finalPositionSize = parseFloat(size ?? '0');\n\n // Reduce-only sizes are formatted with toFixed() further down, which rounds\n // up; truncate onto the size grid first so a close can never exceed the\n // position it is closing.\n if (reduceOnly) {\n // A supplied size must be positive, or formatHyperLiquidSize would render\n // a zero or negative order size. The USD branch above rejects the same\n // input.\n if (size && !(finalPositionSize > 0)) {\n throw new Error(PERPS_ERROR_CODES.ORDER_SIZE_POSITIVE);\n }\n\n const sizeBeforeFlooring = finalPositionSize;\n finalPositionSize = floorToSizeDecimals(finalPositionSize, szDecimals);\n\n // A positive size that floors to zero is worth less than one increment\n if (finalPositionSize <= 0 && sizeBeforeFlooring > 0) {\n throw new Error(PERPS_ERROR_CODES.ORDER_SIZE_POSITIVE);\n }\n }\n\n debugLogger?.log(\n 'Using legacy size calculation (no USD amount provided):',\n {\n providedSize: size,\n finalSize: finalPositionSize,\n },\n );\n }\n\n return { finalPositionSize };\n}\n\n/**\n * Calculates order price and formatted size based on order type\n *\n * @param params - Order parameters including type, direction, size, and prices\n * @returns Formatted order price, size, and price string\n */\nexport function calculateOrderPriceAndSize(\n params: CalculateOrderPriceAndSizeParams,\n): CalculateOrderPriceAndSizeResult {\n const {\n orderType,\n isBuy,\n finalPositionSize,\n currentPrice,\n limitPrice,\n triggerPrice,\n maxSlippageBps,\n szDecimals,\n } = params;\n\n let orderPrice: number;\n let formattedSize: string;\n\n if (isTriggerOrderType(orderType)) {\n // Trigger placements price off the trigger, not the live market: the order\n // rests off-book until the trigger fires.\n if (!triggerPrice) {\n throw new Error(PERPS_ERROR_CODES.ORDER_TRIGGER_PRICE_REQUIRED);\n }\n\n const triggerPriceNum = parseFloat(triggerPrice);\n if (isNaN(triggerPriceNum) || triggerPriceNum <= 0) {\n throw new Error(PERPS_ERROR_CODES.ORDER_TRIGGER_PRICE_POSITIVE);\n }\n\n if (isLimitExecutionOrderType(orderType)) {\n if (!limitPrice) {\n throw new Error(PERPS_ERROR_CODES.ORDER_LIMIT_PRICE_REQUIRED);\n }\n orderPrice = parseFloat(limitPrice);\n } else {\n // Market execution on trigger: HyperLiquid still needs a limit price, used\n // as a slippage cap. The caller's tolerance wins when supplied; otherwise\n // the 10% convention of the existing TP/SL children applies.\n const effectiveBps =\n maxSlippageBps ?? ORDER_SLIPPAGE_CONFIG.DefaultTpslSlippageBps;\n const slippageValue = effectiveBps / BASIS_POINTS_DIVISOR;\n orderPrice = isBuy\n ? triggerPriceNum * (1 + slippageValue)\n : triggerPriceNum * (1 - slippageValue);\n }\n\n formattedSize = formatHyperLiquidSize({\n size: finalPositionSize,\n szDecimals,\n });\n } else if (orderType === 'market') {\n // Market orders: apply slippage buffer to the live price so HyperLiquid\n // receives a worst-case acceptable limit price. Falls back to the\n // documented default if the caller does not provide one.\n const effectiveBps =\n maxSlippageBps ?? ORDER_SLIPPAGE_CONFIG.DefaultMarketSlippageBps;\n const slippageValue = effectiveBps / BASIS_POINTS_DIVISOR;\n orderPrice = isBuy\n ? currentPrice * (1 + slippageValue)\n : currentPrice * (1 - slippageValue);\n formattedSize = formatHyperLiquidSize({\n size: finalPositionSize,\n szDecimals,\n });\n } else {\n // Limit orders: use provided price (no slippage applied)\n if (!limitPrice) {\n throw new Error(PERPS_ERROR_CODES.ORDER_LIMIT_PRICE_REQUIRED);\n }\n orderPrice = parseFloat(limitPrice);\n formattedSize = formatHyperLiquidSize({\n size: finalPositionSize,\n szDecimals,\n });\n }\n\n const formattedPrice = formatHyperLiquidPrice({\n price: orderPrice,\n szDecimals,\n });\n\n return { orderPrice, formattedSize, formattedPrice };\n}\n\n/**\n * Build the SDK order-type field for the main order.\n *\n * Trigger placements map to the SDK's trigger shape; everything else keeps the\n * existing Gtc/FrontendMarket limit shape.\n *\n * @param params - Order type parameters\n * @param params.orderType - Placement type\n * @param params.timeInForce - Time in force; only limit orders may carry one\n * @param params.triggerPrice - Trigger price (required for trigger placements)\n * @param params.szDecimals - Asset size decimals, for price formatting\n * @returns The SDK `t` field for the main order\n */\nfunction buildMainOrderTypeField(params: {\n orderType: OrderType;\n timeInForce?: 'GTC' | 'IOC' | 'ALO';\n triggerPrice?: string;\n szDecimals: number;\n}): SDKOrderParams['t'] {\n const { orderType, timeInForce, triggerPrice, szDecimals } = params;\n\n if (!isTriggerOrderType(orderType)) {\n if (orderType === 'limit') {\n return { limit: { tif: toSDKTimeInForce(timeInForce) } };\n }\n if (timeInForce !== undefined) {\n throw new Error(PERPS_ERROR_CODES.ORDER_TIME_IN_FORCE_NOT_SUPPORTED);\n }\n return { limit: { tif: 'FrontendMarket' } };\n }\n\n if (timeInForce !== undefined) {\n throw new Error(PERPS_ERROR_CODES.ORDER_TIME_IN_FORCE_NOT_SUPPORTED);\n }\n\n if (!triggerPrice) {\n throw new Error(PERPS_ERROR_CODES.ORDER_TRIGGER_PRICE_REQUIRED);\n }\n\n return {\n trigger: {\n isMarket: !isLimitExecutionOrderType(orderType),\n triggerPx: formatTriggerPrice({\n price: triggerPrice,\n szDecimals,\n error: PERPS_ERROR_CODES.ORDER_TRIGGER_PRICE_POSITIVE,\n }),\n tpsl: getTriggerDirection(orderType) === 'stop' ? 'sl' : 'tp',\n },\n };\n}\n\n/**\n * Format a price that becomes a `triggerPx`, rejecting one that disappears at\n * the asset's precision.\n *\n * A positive price below the asset's tick (`0.0004` where the asset quotes to\n * three places) formats to `'0'`, which the exchange rejects. Callers validate\n * this up front via `validateOrderPrecision`; this is the guard on the build\n * path itself, so no caller can assemble an order that cannot be accepted.\n *\n * @param params - Price parameters\n * @param params.price - The requested price\n * @param params.szDecimals - Asset size decimals\n * @param params.error - Typed error to throw when the price rounds away\n * @returns The exchange-formatted price, guaranteed positive.\n */\nfunction formatTriggerPrice(params: {\n price: string;\n szDecimals: number;\n error: string;\n}): string {\n const { price, szDecimals, error } = params;\n const formatted = formatHyperLiquidPrice({ price, szDecimals });\n\n if (parseFloat(formatted) <= 0) {\n throw new Error(error);\n }\n\n return formatted;\n}\n\n/**\n * Resolve the size of an attached TP/SL order.\n *\n * @param params - Size parameters\n * @param params.tpslSize - Requested partial size, if any\n * @param params.formattedSize - Full order size, used when no partial size is given\n * @param params.szDecimals - Asset size decimals\n * @returns The exchange-formatted TP/SL order size\n */\nfunction formatTpslSize(params: {\n tpslSize?: string;\n formattedSize: string;\n szDecimals: number;\n}): string {\n const { tpslSize, formattedSize, szDecimals } = params;\n\n if (tpslSize === undefined) {\n return formattedSize;\n }\n\n // Validation compares the requested size against `params.size`, but a\n // usdAmount-based order is finally sized from a fresher price, so the parent\n // can end up smaller than the child that validated cleanly. Clamp so the\n // attached TP/SL never exceeds the order it protects.\n const requested = parseFloat(tpslSize);\n const parentSize = parseFloat(formattedSize);\n const size =\n Number.isFinite(parentSize) && Number.isFinite(requested)\n ? Math.min(requested, parentSize)\n : requested;\n\n return formatPartialTpslSize({ size, szDecimals });\n}\n\n/**\n * Check that an order's prices and partial sizes survive the asset's precision.\n *\n * Validation elsewhere sees the values the caller supplied; this sees what the\n * exchange will actually receive. A positive value below the asset's tick\n * formats to `'0'`, which either changes the order's meaning (a zero-sized\n * trigger covers the whole position) or is rejected outright (a zero\n * `triggerPx`).\n *\n * Callers run this before taking any side effect — cancelling the position's\n * existing triggers, changing leverage, moving HIP-3 margin — so a value that\n * would only fail once the orders are built cannot leave a position stripped of\n * its protection, or an account with leverage moved, for an order that was\n * never going to be accepted.\n *\n * @param params - Price and size parameters\n * @param params.triggerPrice - Trigger price for a trigger placement, if any\n * @param params.takeProfitPrice - Attached take profit price, if any\n * @param params.stopLossPrice - Attached stop loss price, if any\n * @param params.takeProfitSize - Requested partial take profit size, if any\n * @param params.stopLossSize - Requested partial stop loss size, if any\n * @param params.szDecimals - Asset size decimals\n * @returns Validation result with isValid flag and optional error message\n */\nexport function validateOrderPrecision(params: {\n triggerPrice?: string;\n takeProfitPrice?: string;\n stopLossPrice?: string;\n takeProfitSize?: string;\n stopLossSize?: string;\n szDecimals: number;\n}): { isValid: boolean; error?: string } {\n const {\n triggerPrice,\n takeProfitPrice,\n stopLossPrice,\n takeProfitSize,\n stopLossSize,\n szDecimals,\n } = params;\n\n for (const size of [takeProfitSize, stopLossSize]) {\n if (size === undefined) {\n continue;\n }\n\n if (parseFloat(formatHyperLiquidSize({ size, szDecimals })) <= 0) {\n return {\n isValid: false,\n error: PERPS_ERROR_CODES.ORDER_TPSL_SIZE_INVALID,\n };\n }\n }\n\n // Prices carry their own precision: an asset quotes to\n // `MaxPriceDecimals - szDecimals` places, so a positive price under that tick\n // formats to '0'. Every one of these becomes a `triggerPx` the exchange\n // rejects outright.\n const priceChecks: [string | undefined, string][] = [\n [triggerPrice, PERPS_ERROR_CODES.ORDER_TRIGGER_PRICE_POSITIVE],\n [takeProfitPrice, PERPS_ERROR_CODES.ORDER_PRICE_POSITIVE],\n [stopLossPrice, PERPS_ERROR_CODES.ORDER_PRICE_POSITIVE],\n ];\n\n for (const [price, error] of priceChecks) {\n if (price === undefined) {\n continue;\n }\n\n if (parseFloat(formatHyperLiquidPrice({ price, szDecimals })) <= 0) {\n return { isValid: false, error };\n }\n }\n\n return { isValid: true };\n}\n\n/**\n * Format a partial TP/SL size, rejecting one that disappears at the asset\n * precision.\n *\n * Validation only sees the requested size, so a positive value below the\n * asset's precision (0.0004 against `szDecimals: 3`) passes and then formats to\n * `'0'`. HyperLiquid reads a zero-sized trigger as covering the whole position,\n * which would silently turn a partial TP/SL into a full close.\n *\n * @param params - Size parameters\n * @param params.size - The requested partial size\n * @param params.szDecimals - Asset size decimals\n * @returns The exchange-formatted size, guaranteed positive.\n */\nexport function formatPartialTpslSize(params: {\n size: string | number;\n szDecimals: number;\n}): string {\n const formatted = formatHyperLiquidSize(params);\n\n if (parseFloat(formatted) <= 0) {\n throw new Error(PERPS_ERROR_CODES.ORDER_TPSL_SIZE_INVALID);\n }\n\n return formatted;\n}\n\n/**\n * Builds orders array including main order and optional TP/SL orders\n *\n * @param params - Order construction parameters\n * @returns Array of SDK order params and grouping type\n */\nexport function buildOrdersArray(\n params: BuildOrdersArrayParams,\n): BuildOrdersArrayResult {\n const {\n assetId,\n isBuy,\n formattedPrice,\n formattedSize,\n reduceOnly,\n orderType,\n timeInForce,\n clientOrderId,\n triggerPrice,\n takeProfitPrice,\n stopLossPrice,\n takeProfitSize,\n stopLossSize,\n szDecimals,\n grouping,\n } = params;\n\n const orders: SDKOrderParams[] = [];\n\n // 1. Main order\n const mainOrder: SDKOrderParams = {\n a: assetId,\n b: isBuy,\n p: formattedPrice,\n s: formattedSize,\n r: reduceOnly || false,\n t: buildMainOrderTypeField({\n orderType,\n timeInForce,\n triggerPrice,\n szDecimals,\n }),\n c: clientOrderId ? (clientOrderId as Hex) : undefined,\n };\n orders.push(mainOrder);\n\n // 2. Take Profit order\n if (takeProfitPrice) {\n const tpOrder: SDKOrderParams = {\n a: assetId,\n b: !isBuy,\n p: formatHyperLiquidPrice({\n price: parseFloat(takeProfitPrice),\n szDecimals,\n }),\n s: formatTpslSize({\n tpslSize: takeProfitSize,\n formattedSize,\n szDecimals,\n }),\n r: true,\n t: {\n trigger: {\n isMarket: false,\n triggerPx: formatTriggerPrice({\n price: takeProfitPrice,\n szDecimals,\n error: PERPS_ERROR_CODES.ORDER_PRICE_POSITIVE,\n }),\n tpsl: 'tp',\n },\n },\n };\n orders.push(tpOrder);\n }\n\n // 3. Stop Loss order\n if (stopLossPrice) {\n // Apply 10% slippage to SL limit price (executes as market order when triggered)\n // HyperLiquid recommended: 10% for TP/SL orders\n const stopLossPriceNum = parseFloat(stopLossPrice);\n const slippageValue = ORDER_SLIPPAGE_CONFIG.DefaultTpslSlippageBps / 10000;\n const limitPriceWithSlippage = isBuy\n ? stopLossPriceNum * (1 - slippageValue) // Selling to close long: willing to accept LESS (slippage protection)\n : stopLossPriceNum * (1 + slippageValue); // Buying to close short: willing to pay MORE (slippage protection)\n\n const slOrder: SDKOrderParams = {\n a: assetId,\n b: !isBuy,\n p: formatHyperLiquidPrice({\n price: limitPriceWithSlippage,\n szDecimals,\n }),\n s: formatTpslSize({ tpslSize: stopLossSize, formattedSize, szDecimals }),\n r: true,\n t: {\n trigger: {\n isMarket: true,\n triggerPx: formatTriggerPrice({\n price: stopLossPrice,\n szDecimals,\n error: PERPS_ERROR_CODES.ORDER_PRICE_POSITIVE,\n }),\n tpsl: 'sl',\n },\n },\n };\n orders.push(slOrder);\n }\n\n // Determine grouping\n const finalGrouping: 'na' | 'normalTpsl' | 'positionTpsl' =\n grouping ?? ((takeProfitPrice ?? stopLossPrice) ? 'normalTpsl' : 'na');\n\n return { orders, grouping: finalGrouping };\n}\n"]}
1
+ {"version":3,"file":"orderCalculations.mjs","sourceRoot":"","sources":["../../src/utils/orderCalculations.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Hex } from '@metamask/utils';\n\nimport { BASIS_POINTS_DIVISOR } from '../constants/hyperLiquidConfig.js';\nimport {\n MAX_ORDER_MARGIN_BUFFER,\n ORDER_SLIPPAGE_CONFIG,\n} from '../constants/perpsConfig.js';\nimport { PERPS_ERROR_CODES } from '../perpsErrorCodes.js';\nimport type { SDKOrderParams } from '../types/hyperliquid-types.js';\nimport type { PerpsDebugLogger } from '../types/index.js';\nimport type { OrderType } from '../types/perps-types.js';\nimport {\n formatHyperLiquidPrice,\n formatHyperLiquidSize,\n} from './hyperLiquidAdapter.js';\nimport {\n getTriggerDirection,\n isLimitExecutionOrderType,\n isTriggerOrderType,\n toSDKTimeInForce,\n} from './orderTypes.js';\n\n/**\n * Optional debug logger for order calculation functions.\n * When provided, enables detailed logging for debugging.\n */\nexport type OrderCalculationsDebugLogger = PerpsDebugLogger | undefined;\n\n/**\n * Tolerance used when deciding whether a scaled size is already on the size\n * grid, guarding against floating-point representation error.\n */\nconst FLOAT_TOLERANCE = 1e-6;\n\ntype PositionSizeParams = {\n amount: string;\n price: number;\n szDecimals: number;\n};\n\ntype MarginRequiredParams = {\n amount: string;\n leverage: number;\n};\n\ntype MaxAllowedAmountParams = {\n spendableBalance: number;\n assetPrice: number;\n assetSzDecimals: number;\n leverage: number;\n // Placement type. Only a resting order is margin-checked against its own\n // submitted price; a marketable order is charged at the fill price. Defaults\n // to 'market'.\n orderType?: 'market' | 'limit';\n // Price a limit order will rest at. Needed to size a limit order that rests\n // above the market price.\n limitPrice?: number;\n};\n\n// Advanced order calculation interfaces\nexport type CalculateFinalPositionSizeParams = {\n usdAmount?: string;\n size?: string;\n currentPrice: number;\n priceAtCalculation?: number;\n maxSlippageBps?: number;\n szDecimals: number;\n leverage?: number;\n // Reduce-only orders (position closes) may never round up: HyperLiquid\n // rejects a reduce-only order whose size exceeds the live position with\n // \"Reduce only order would increase position\".\n reduceOnly?: boolean;\n debugLogger?: OrderCalculationsDebugLogger;\n};\n\nexport type CalculateFinalPositionSizeResult = {\n finalPositionSize: number;\n};\n\nexport type CalculateOrderPriceAndSizeParams = {\n orderType: OrderType;\n isBuy: boolean;\n finalPositionSize: number;\n currentPrice: number;\n limitPrice?: string;\n // Trigger price for stop_*/take_profit_* placements. Required for those types:\n // `*_limit` executes at `limitPrice`, `*_market` derives a slippage-capped\n // limit price from this trigger price.\n triggerPrice?: string;\n // Max slippage in basis points (e.g. 300 = 3%). Applied to market orders and to\n // market-executing trigger orders (where it caps the limit price derived from\n // the trigger price); limit orders use limitPrice directly. Falls back to\n // ORDER_SLIPPAGE_CONFIG.DefaultMarketSlippageBps for market orders and\n // .DefaultTpslSlippageBps for market-executing triggers.\n maxSlippageBps?: number;\n szDecimals: number;\n};\n\nexport type CalculateOrderPriceAndSizeResult = {\n orderPrice: number;\n formattedSize: string;\n formattedPrice: string;\n};\n\nexport type BuildOrdersArrayParams = {\n assetId: number;\n isBuy: boolean;\n formattedPrice: string;\n formattedSize: string;\n reduceOnly: boolean;\n orderType: OrderType;\n timeInForce?: 'GTC' | 'IOC' | 'ALO';\n clientOrderId?: string;\n // Trigger price for stop_*/take_profit_* placements (required for those types)\n triggerPrice?: string;\n takeProfitPrice?: string;\n stopLossPrice?: string;\n // Partial TP/SL sizes; default to the full order size when omitted\n takeProfitSize?: string;\n stopLossSize?: string;\n szDecimals: number;\n grouping?: 'na' | 'normalTpsl' | 'positionTpsl';\n};\n\nexport type BuildOrdersArrayResult = {\n orders: SDKOrderParams[];\n grouping: 'na' | 'normalTpsl' | 'positionTpsl';\n};\n\n/**\n * Calculate position size based on USD amount and asset price\n *\n * @param params - Amount in USD, current asset price, and required decimal precision\n * @returns Position size formatted to the asset's decimal precision\n */\nexport function calculatePositionSize(params: PositionSizeParams): string {\n const { amount, price, szDecimals } = params;\n\n // Validate required parameters\n if (szDecimals === undefined || szDecimals === null) {\n throw new Error('szDecimals is required for position size calculation');\n }\n if (szDecimals < 0) {\n throw new Error(`szDecimals must be >= 0, got: ${szDecimals}`);\n }\n\n const amountNum = parseFloat(amount || '0');\n\n if (isNaN(amountNum) || isNaN(price) || amountNum === 0 || price === 0) {\n return (0).toFixed(szDecimals);\n }\n\n const positionSize = amountNum / price;\n const multiplier = Math.pow(10, szDecimals);\n let rounded = Math.round(positionSize * multiplier) / multiplier;\n\n // Ensure rounded size meets requested USD (fix validation gap)\n const actualUsd = rounded * price;\n if (actualUsd < amountNum) {\n rounded += 1 / multiplier;\n }\n\n return rounded.toFixed(szDecimals);\n}\n\n/**\n * Calculate margin required for a position\n *\n * @param params - Position amount and leverage\n * @returns Margin required formatted to 2 decimal places\n */\nexport function calculateMarginRequired(params: MarginRequiredParams): string {\n const { amount, leverage } = params;\n const amountNum = parseFloat(amount || '0');\n\n if (\n isNaN(amountNum) ||\n isNaN(leverage) ||\n amountNum === 0 ||\n leverage === 0\n ) {\n return '0.00';\n }\n\n return (amountNum / leverage).toFixed(2);\n}\n\nexport function getMaxAllowedAmount(params: MaxAllowedAmountParams): number {\n const {\n spendableBalance,\n assetPrice,\n assetSzDecimals,\n leverage,\n orderType = 'market',\n limitPrice,\n } = params;\n if (spendableBalance === 0 || !assetPrice || assetSzDecimals === undefined) {\n return 0;\n }\n\n // HyperLiquid reserves initial margin for a RESTING order against the price\n // the order is submitted at, not the market price its size was derived from.\n // A limit order resting above the market price - typically a sell - therefore\n // needs more margin than a market-priced notional budgets for, and the\n // exchange refuses it with \"insufficient margin to place order\". Price the max\n // off that submitted price instead. A marketable order is charged at the fill\n // price, so it needs no adjustment.\n const executionPriceRatio =\n orderType === 'limit' && limitPrice && limitPrice > assetPrice\n ? limitPrice / assetPrice\n : 1;\n\n // The theoretical maximum is spendableBalance * leverage, expressed in the\n // market-price notional the caller works with.\n const theoreticalMax = (spendableBalance * leverage) / executionPriceRatio;\n\n // But we need to account for position size rounding\n // Find the largest whole dollar amount that fits within this limit\n let maxAmount = Math.floor(theoreticalMax);\n\n // Verify this amount doesn't exceed available balance after rounding\n const testPositionSize = calculatePositionSize({\n amount: maxAmount.toString(),\n price: assetPrice,\n szDecimals: assetSzDecimals,\n });\n\n const actualNotionalValue =\n parseFloat(testPositionSize) * assetPrice * executionPriceRatio;\n const requiredMargin = actualNotionalValue / leverage;\n\n // If rounding caused us to exceed available balance, step down by one position increment\n if (requiredMargin > spendableBalance) {\n const minPositionSizeIncrement = 1 / Math.pow(10, assetSzDecimals);\n const positionSizeIncrementUsd = Math.ceil(\n minPositionSizeIncrement * assetPrice,\n );\n maxAmount -= positionSizeIncrementUsd;\n }\n\n // Apply margin buffer to reduce \"Insufficient margin\" rejections from the exchange\n // (fees, rounding, and exchange-side checks can make 100% theoretical max fail)\n const bufferedMax = maxAmount * (1 - MAX_ORDER_MARGIN_BUFFER);\n\n return Math.max(0, Math.floor(bufferedMax));\n}\n\n/**\n * Round a size down onto the asset's size grid.\n *\n * Used for reduce-only orders, where rounding up would push the size past the\n * live position size. Values already on the grid are snapped rather than\n * truncated, because floating-point math can leave them just below a grid\n * point (0.0123 * 10000 === 122.99999999999999) and truncating would drop a\n * whole increment.\n *\n * The result is never greater than `size`, for negative sizes as well as\n * positive: the snap only ever recovers a grid point the input already\n * represents, so a value genuinely below a grid point is stepped down even when\n * the tolerance would have reached the point above it.\n *\n * A size whose scaled form reaches `2^53` is returned unchanged: doubles cannot\n * represent consecutive integers there, so the grid is finer than the spacing\n * between representable values and there is nothing to round down to.\n *\n * @param size - Size to round down.\n * @param szDecimals - The asset's size decimal precision.\n * @returns The size rounded down onto the size grid, never exceeding `size`.\n */\nexport function floorToSizeDecimals(size: number, szDecimals: number): number {\n const multiplier = Math.pow(10, szDecimals);\n const scaled = size * multiplier;\n\n // Past 2^53 a double cannot represent consecutive integers, so `units -= 1`\n // below would be a no-op and the step-down loop would never terminate. The\n // size grid is finer than the spacing between representable values at that\n // magnitude, so there is no increment to shave: return the input unchanged.\n if (!Number.isFinite(scaled) || Math.abs(scaled) >= Number.MAX_SAFE_INTEGER) {\n return size;\n }\n\n const nearest = Math.round(scaled);\n // The tolerance scales with the magnitude, because double-precision error\n // does too: a fixed epsilon would stop absorbing representation error for\n // sizes that scale past ~1e10 and would then shave off a whole increment.\n const tolerance = Math.max(\n FLOAT_TOLERANCE,\n Math.abs(scaled) * Number.EPSILON * 8,\n );\n let units =\n Math.abs(scaled - nearest) < tolerance ? nearest : Math.floor(scaled);\n\n // Step down until the result no longer exceeds the input. One pass is not\n // enough: a tolerance wide enough to absorb representation error at large\n // magnitudes also reaches the next grid point, and for an input less than half\n // an ulp below a grid point `size * multiplier` evaluates to exactly that grid\n // integer, so flooring the scaled value returns the same too-large result.\n // The comparison alone is the whole termination condition: for a non-negative\n // size the loop stops at or before zero, and for a negative size it stops once\n // the value is no longer above the input. Guarding on `units` instead would\n // skip a negative size below the tolerance, which snaps to `-0` — and\n // `-0 !== 0` is false. The 2^53 bail-out above keeps this bounded.\n while (units / multiplier > size) {\n units -= 1;\n }\n\n return units / multiplier;\n}\n\n/**\n * Calculates final position size using USD as source of truth with price validation\n *\n * This function implements the hybrid approach where USD is the source of truth,\n * but includes price staleness validation and proper rounding to prevent precision loss.\n *\n * @param params - USD amount, size, prices, and configuration\n * @returns Final position size as a number\n */\nexport function calculateFinalPositionSize(\n params: CalculateFinalPositionSizeParams,\n): CalculateFinalPositionSizeResult {\n const {\n usdAmount,\n size,\n currentPrice,\n priceAtCalculation,\n maxSlippageBps,\n szDecimals,\n leverage,\n reduceOnly,\n debugLogger,\n } = params;\n\n let finalPositionSize: number;\n\n // Validate price staleness whenever the caller supplied a calculation-time\n // price. This runs before the sizing branches on purpose: a full close submits\n // the exact live position size rather than a USD-derived one, and it must still\n // be rejected when the price has moved past the caller's tolerance.\n if (priceAtCalculation) {\n const priceDeltaBps = Math.abs(\n ((currentPrice - priceAtCalculation) / priceAtCalculation) * 10000,\n );\n const maxSlippageBpsValue =\n maxSlippageBps ?? ORDER_SLIPPAGE_CONFIG.DefaultMarketSlippageBps;\n\n if (priceDeltaBps > maxSlippageBpsValue) {\n throw new Error(\n `Price moved too much: ${priceDeltaBps.toFixed(0)} bps (max: ${maxSlippageBpsValue} bps). ` +\n `Expected: ${priceAtCalculation.toFixed(2)}, Current: ${currentPrice.toFixed(2)}`,\n );\n }\n\n debugLogger?.log('Price validation passed:', {\n priceAtCalculation,\n currentPrice,\n deltaBps: priceDeltaBps.toFixed(2),\n maxSlippageBps: maxSlippageBpsValue,\n });\n }\n\n if (usdAmount && parseFloat(usdAmount) > 0) {\n // USD amount provided - use it as source of truth\n const usdValue = parseFloat(usdAmount);\n\n // Recalculate position size with fresh price\n finalPositionSize = usdValue / currentPrice;\n\n // A reduce-only order may never exceed the size the caller asked to close:\n // that size is already clamped to the live position, while the USD amount was\n // computed against an older price and can imply a larger size after an\n // adverse move. Capping here keeps USD accuracy in the common case and makes\n // the caller's clamp binding.\n if (reduceOnly && size) {\n const requestedSize = parseFloat(size);\n\n // A supplied size must be positive, or the cap below would submit a\n // zero/negative order. Reject it rather than silently falling back to the\n // USD-derived size, matching how closePosition treats the same input.\n if (!Number.isFinite(requestedSize) || requestedSize <= 0) {\n throw new Error(PERPS_ERROR_CODES.ORDER_SIZE_POSITIVE);\n }\n\n finalPositionSize = Math.min(finalPositionSize, requestedSize);\n }\n\n // 3. Apply size decimals rounding (reduce-only never rounds up)\n const multiplier = Math.pow(10, szDecimals);\n const sizeBeforeRounding = finalPositionSize;\n finalPositionSize = reduceOnly\n ? floorToSizeDecimals(finalPositionSize, szDecimals)\n : Math.round(finalPositionSize * multiplier) / multiplier;\n\n // Rounding down can zero out a reduce-only order whose USD value is worth\n // less than one size increment. Fail with a clear error instead of\n // submitting a size of \"0\" the exchange will reject.\n if (reduceOnly && finalPositionSize <= 0 && sizeBeforeRounding > 0) {\n throw new Error(PERPS_ERROR_CODES.ORDER_SIZE_POSITIVE);\n }\n\n // 4. Ensure rounded size meets requested USD (fix validation gap).\n // Skipped for reduce-only orders: adding an increment there would submit\n // more than the position holds and HyperLiquid rejects the order.\n let actualNotionalValue = finalPositionSize * currentPrice;\n if (!reduceOnly && actualNotionalValue < usdValue) {\n // Add 1 minimum increment to meet requested USD\n finalPositionSize += 1 / multiplier;\n actualNotionalValue = finalPositionSize * currentPrice;\n\n debugLogger?.log('Position size adjusted to meet USD minimum:', {\n requestedUsd: usdValue,\n beforeAdjustment: finalPositionSize - 1 / multiplier,\n afterAdjustment: finalPositionSize,\n actualUsd: actualNotionalValue,\n });\n }\n\n const requiredMargin = actualNotionalValue / (leverage ?? 1);\n\n // Log if rounding caused significant difference\n const usdDifference = Math.abs(actualNotionalValue - usdValue);\n if (usdDifference > 0.01) {\n debugLogger?.log(\n 'Position size rounding caused USD difference (acceptable):',\n {\n requestedUsd: usdValue,\n actualUsd: actualNotionalValue,\n difference: usdDifference,\n positionSize: finalPositionSize,\n },\n );\n }\n\n debugLogger?.log('Recalculated position size with fresh price:', {\n usdAmount: usdValue,\n priceAtCalculation,\n currentPrice,\n originalSize: size,\n recalculatedSize: finalPositionSize,\n requiredMargin,\n minIncrement: 1 / multiplier,\n });\n } else {\n // Legacy: Use provided size (backward compatibility)\n finalPositionSize = parseFloat(size ?? '0');\n\n // Reduce-only sizes are formatted with toFixed() further down, which rounds\n // up; truncate onto the size grid first so a close can never exceed the\n // position it is closing.\n if (reduceOnly) {\n // A supplied size must be positive, or formatHyperLiquidSize would render\n // a zero or negative order size. The USD branch above rejects the same\n // input.\n if (size && !(finalPositionSize > 0)) {\n throw new Error(PERPS_ERROR_CODES.ORDER_SIZE_POSITIVE);\n }\n\n const sizeBeforeFlooring = finalPositionSize;\n finalPositionSize = floorToSizeDecimals(finalPositionSize, szDecimals);\n\n // A positive size that floors to zero is worth less than one increment\n if (finalPositionSize <= 0 && sizeBeforeFlooring > 0) {\n throw new Error(PERPS_ERROR_CODES.ORDER_SIZE_POSITIVE);\n }\n }\n\n debugLogger?.log(\n 'Using legacy size calculation (no USD amount provided):',\n {\n providedSize: size,\n finalSize: finalPositionSize,\n },\n );\n }\n\n return { finalPositionSize };\n}\n\n/**\n * Calculates order price and formatted size based on order type\n *\n * @param params - Order parameters including type, direction, size, and prices\n * @returns Formatted order price, size, and price string\n */\nexport function calculateOrderPriceAndSize(\n params: CalculateOrderPriceAndSizeParams,\n): CalculateOrderPriceAndSizeResult {\n const {\n orderType,\n isBuy,\n finalPositionSize,\n currentPrice,\n limitPrice,\n triggerPrice,\n maxSlippageBps,\n szDecimals,\n } = params;\n\n let orderPrice: number;\n let formattedSize: string;\n\n if (isTriggerOrderType(orderType)) {\n // Trigger placements price off the trigger, not the live market: the order\n // rests off-book until the trigger fires.\n if (!triggerPrice) {\n throw new Error(PERPS_ERROR_CODES.ORDER_TRIGGER_PRICE_REQUIRED);\n }\n\n const triggerPriceNum = parseFloat(triggerPrice);\n if (isNaN(triggerPriceNum) || triggerPriceNum <= 0) {\n throw new Error(PERPS_ERROR_CODES.ORDER_TRIGGER_PRICE_POSITIVE);\n }\n\n if (isLimitExecutionOrderType(orderType)) {\n if (!limitPrice) {\n throw new Error(PERPS_ERROR_CODES.ORDER_LIMIT_PRICE_REQUIRED);\n }\n orderPrice = parseFloat(limitPrice);\n } else {\n // Market execution on trigger: HyperLiquid still needs a limit price, used\n // as a slippage cap. The caller's tolerance wins when supplied; otherwise\n // the 10% convention of the existing TP/SL children applies.\n const effectiveBps =\n maxSlippageBps ?? ORDER_SLIPPAGE_CONFIG.DefaultTpslSlippageBps;\n const slippageValue = effectiveBps / BASIS_POINTS_DIVISOR;\n orderPrice = isBuy\n ? triggerPriceNum * (1 + slippageValue)\n : triggerPriceNum * (1 - slippageValue);\n }\n\n formattedSize = formatHyperLiquidSize({\n size: finalPositionSize,\n szDecimals,\n });\n } else if (orderType === 'market') {\n // Market orders: apply slippage buffer to the live price so HyperLiquid\n // receives a worst-case acceptable limit price. Falls back to the\n // documented default if the caller does not provide one.\n const effectiveBps =\n maxSlippageBps ?? ORDER_SLIPPAGE_CONFIG.DefaultMarketSlippageBps;\n const slippageValue = effectiveBps / BASIS_POINTS_DIVISOR;\n orderPrice = isBuy\n ? currentPrice * (1 + slippageValue)\n : currentPrice * (1 - slippageValue);\n formattedSize = formatHyperLiquidSize({\n size: finalPositionSize,\n szDecimals,\n });\n } else {\n // Limit orders: use provided price (no slippage applied)\n if (!limitPrice) {\n throw new Error(PERPS_ERROR_CODES.ORDER_LIMIT_PRICE_REQUIRED);\n }\n orderPrice = parseFloat(limitPrice);\n formattedSize = formatHyperLiquidSize({\n size: finalPositionSize,\n szDecimals,\n });\n }\n\n const formattedPrice = formatHyperLiquidPrice({\n price: orderPrice,\n szDecimals,\n });\n\n return { orderPrice, formattedSize, formattedPrice };\n}\n\n/**\n * Build the SDK order-type field for the main order.\n *\n * Trigger placements map to the SDK's trigger shape; everything else keeps the\n * existing Gtc/FrontendMarket limit shape.\n *\n * @param params - Order type parameters\n * @param params.orderType - Placement type\n * @param params.timeInForce - Time in force; only limit orders may carry one\n * @param params.triggerPrice - Trigger price (required for trigger placements)\n * @param params.szDecimals - Asset size decimals, for price formatting\n * @returns The SDK `t` field for the main order\n */\nfunction buildMainOrderTypeField(params: {\n orderType: OrderType;\n timeInForce?: 'GTC' | 'IOC' | 'ALO';\n triggerPrice?: string;\n szDecimals: number;\n}): SDKOrderParams['t'] {\n const { orderType, timeInForce, triggerPrice, szDecimals } = params;\n\n if (!isTriggerOrderType(orderType)) {\n if (orderType === 'limit') {\n return { limit: { tif: toSDKTimeInForce(timeInForce) } };\n }\n if (timeInForce !== undefined) {\n throw new Error(PERPS_ERROR_CODES.ORDER_TIME_IN_FORCE_NOT_SUPPORTED);\n }\n return { limit: { tif: 'FrontendMarket' } };\n }\n\n if (timeInForce !== undefined) {\n throw new Error(PERPS_ERROR_CODES.ORDER_TIME_IN_FORCE_NOT_SUPPORTED);\n }\n\n if (!triggerPrice) {\n throw new Error(PERPS_ERROR_CODES.ORDER_TRIGGER_PRICE_REQUIRED);\n }\n\n return {\n trigger: {\n isMarket: !isLimitExecutionOrderType(orderType),\n triggerPx: formatTriggerPrice({\n price: triggerPrice,\n szDecimals,\n error: PERPS_ERROR_CODES.ORDER_TRIGGER_PRICE_POSITIVE,\n }),\n tpsl: getTriggerDirection(orderType) === 'stop' ? 'sl' : 'tp',\n },\n };\n}\n\n/**\n * Format a price that becomes a `triggerPx`, rejecting one that disappears at\n * the asset's precision.\n *\n * A positive price below the asset's tick (`0.0004` where the asset quotes to\n * three places) formats to `'0'`, which the exchange rejects. Callers validate\n * this up front via `validateOrderPrecision`; this is the guard on the build\n * path itself, so no caller can assemble an order that cannot be accepted.\n *\n * @param params - Price parameters\n * @param params.price - The requested price\n * @param params.szDecimals - Asset size decimals\n * @param params.error - Typed error to throw when the price rounds away\n * @returns The exchange-formatted price, guaranteed positive.\n */\nfunction formatTriggerPrice(params: {\n price: string;\n szDecimals: number;\n error: string;\n}): string {\n const { price, szDecimals, error } = params;\n const formatted = formatHyperLiquidPrice({ price, szDecimals });\n\n if (parseFloat(formatted) <= 0) {\n throw new Error(error);\n }\n\n return formatted;\n}\n\n/**\n * Resolve the size of an attached TP/SL order.\n *\n * @param params - Size parameters\n * @param params.tpslSize - Requested partial size, if any\n * @param params.formattedSize - Full order size, used when no partial size is given\n * @param params.szDecimals - Asset size decimals\n * @returns The exchange-formatted TP/SL order size\n */\nfunction formatTpslSize(params: {\n tpslSize?: string;\n formattedSize: string;\n szDecimals: number;\n}): string {\n const { tpslSize, formattedSize, szDecimals } = params;\n\n if (tpslSize === undefined) {\n return formattedSize;\n }\n\n // Validation compares the requested size against `params.size`, but a\n // usdAmount-based order is finally sized from a fresher price, so the parent\n // can end up smaller than the child that validated cleanly. Clamp so the\n // attached TP/SL never exceeds the order it protects.\n const requested = parseFloat(tpslSize);\n const parentSize = parseFloat(formattedSize);\n const size =\n Number.isFinite(parentSize) && Number.isFinite(requested)\n ? Math.min(requested, parentSize)\n : requested;\n\n return formatPartialTpslSize({ size, szDecimals });\n}\n\n/**\n * Check that an order's prices and partial sizes survive the asset's precision.\n *\n * Validation elsewhere sees the values the caller supplied; this sees what the\n * exchange will actually receive. A positive value below the asset's tick\n * formats to `'0'`, which either changes the order's meaning (a zero-sized\n * trigger covers the whole position) or is rejected outright (a zero\n * `triggerPx`).\n *\n * Callers run this before taking any side effect — cancelling the position's\n * existing triggers, changing leverage, moving HIP-3 margin — so a value that\n * would only fail once the orders are built cannot leave a position stripped of\n * its protection, or an account with leverage moved, for an order that was\n * never going to be accepted.\n *\n * @param params - Price and size parameters\n * @param params.triggerPrice - Trigger price for a trigger placement, if any\n * @param params.takeProfitPrice - Attached take profit price, if any\n * @param params.stopLossPrice - Attached stop loss price, if any\n * @param params.takeProfitSize - Requested partial take profit size, if any\n * @param params.stopLossSize - Requested partial stop loss size, if any\n * @param params.szDecimals - Asset size decimals\n * @returns Validation result with isValid flag and optional error message\n */\nexport function validateOrderPrecision(params: {\n triggerPrice?: string;\n takeProfitPrice?: string;\n stopLossPrice?: string;\n takeProfitSize?: string;\n stopLossSize?: string;\n szDecimals: number;\n}): { isValid: boolean; error?: string } {\n const {\n triggerPrice,\n takeProfitPrice,\n stopLossPrice,\n takeProfitSize,\n stopLossSize,\n szDecimals,\n } = params;\n\n for (const size of [takeProfitSize, stopLossSize]) {\n if (size === undefined) {\n continue;\n }\n\n if (parseFloat(formatHyperLiquidSize({ size, szDecimals })) <= 0) {\n return {\n isValid: false,\n error: PERPS_ERROR_CODES.ORDER_TPSL_SIZE_INVALID,\n };\n }\n }\n\n // Prices carry their own precision: an asset quotes to\n // `MaxPriceDecimals - szDecimals` places, so a positive price under that tick\n // formats to '0'. Every one of these becomes a `triggerPx` the exchange\n // rejects outright.\n const priceChecks: [string | undefined, string][] = [\n [triggerPrice, PERPS_ERROR_CODES.ORDER_TRIGGER_PRICE_POSITIVE],\n [takeProfitPrice, PERPS_ERROR_CODES.ORDER_PRICE_POSITIVE],\n [stopLossPrice, PERPS_ERROR_CODES.ORDER_PRICE_POSITIVE],\n ];\n\n for (const [price, error] of priceChecks) {\n if (price === undefined) {\n continue;\n }\n\n if (parseFloat(formatHyperLiquidPrice({ price, szDecimals })) <= 0) {\n return { isValid: false, error };\n }\n }\n\n return { isValid: true };\n}\n\n/**\n * Format a partial TP/SL size, rejecting one that disappears at the asset\n * precision.\n *\n * Validation only sees the requested size, so a positive value below the\n * asset's precision (0.0004 against `szDecimals: 3`) passes and then formats to\n * `'0'`. HyperLiquid reads a zero-sized trigger as covering the whole position,\n * which would silently turn a partial TP/SL into a full close.\n *\n * @param params - Size parameters\n * @param params.size - The requested partial size\n * @param params.szDecimals - Asset size decimals\n * @returns The exchange-formatted size, guaranteed positive.\n */\nexport function formatPartialTpslSize(params: {\n size: string | number;\n szDecimals: number;\n}): string {\n const formatted = formatHyperLiquidSize(params);\n\n if (parseFloat(formatted) <= 0) {\n throw new Error(PERPS_ERROR_CODES.ORDER_TPSL_SIZE_INVALID);\n }\n\n return formatted;\n}\n\n/**\n * Builds orders array including main order and optional TP/SL orders\n *\n * @param params - Order construction parameters\n * @returns Array of SDK order params and grouping type\n */\nexport function buildOrdersArray(\n params: BuildOrdersArrayParams,\n): BuildOrdersArrayResult {\n const {\n assetId,\n isBuy,\n formattedPrice,\n formattedSize,\n reduceOnly,\n orderType,\n timeInForce,\n clientOrderId,\n triggerPrice,\n takeProfitPrice,\n stopLossPrice,\n takeProfitSize,\n stopLossSize,\n szDecimals,\n grouping,\n } = params;\n\n const orders: SDKOrderParams[] = [];\n\n // 1. Main order\n const mainOrder: SDKOrderParams = {\n a: assetId,\n b: isBuy,\n p: formattedPrice,\n s: formattedSize,\n r: reduceOnly || false,\n t: buildMainOrderTypeField({\n orderType,\n timeInForce,\n triggerPrice,\n szDecimals,\n }),\n c: clientOrderId ? (clientOrderId as Hex) : undefined,\n };\n orders.push(mainOrder);\n\n // 2. Take Profit order\n if (takeProfitPrice) {\n const tpOrder: SDKOrderParams = {\n a: assetId,\n b: !isBuy,\n p: formatHyperLiquidPrice({\n price: parseFloat(takeProfitPrice),\n szDecimals,\n }),\n s: formatTpslSize({\n tpslSize: takeProfitSize,\n formattedSize,\n szDecimals,\n }),\n r: true,\n t: {\n trigger: {\n isMarket: false,\n triggerPx: formatTriggerPrice({\n price: takeProfitPrice,\n szDecimals,\n error: PERPS_ERROR_CODES.ORDER_PRICE_POSITIVE,\n }),\n tpsl: 'tp',\n },\n },\n };\n orders.push(tpOrder);\n }\n\n // 3. Stop Loss order\n if (stopLossPrice) {\n // Apply 10% slippage to SL limit price (executes as market order when triggered)\n // HyperLiquid recommended: 10% for TP/SL orders\n const stopLossPriceNum = parseFloat(stopLossPrice);\n const slippageValue = ORDER_SLIPPAGE_CONFIG.DefaultTpslSlippageBps / 10000;\n const limitPriceWithSlippage = isBuy\n ? stopLossPriceNum * (1 - slippageValue) // Selling to close long: willing to accept LESS (slippage protection)\n : stopLossPriceNum * (1 + slippageValue); // Buying to close short: willing to pay MORE (slippage protection)\n\n const slOrder: SDKOrderParams = {\n a: assetId,\n b: !isBuy,\n p: formatHyperLiquidPrice({\n price: limitPriceWithSlippage,\n szDecimals,\n }),\n s: formatTpslSize({ tpslSize: stopLossSize, formattedSize, szDecimals }),\n r: true,\n t: {\n trigger: {\n isMarket: true,\n triggerPx: formatTriggerPrice({\n price: stopLossPrice,\n szDecimals,\n error: PERPS_ERROR_CODES.ORDER_PRICE_POSITIVE,\n }),\n tpsl: 'sl',\n },\n },\n };\n orders.push(slOrder);\n }\n\n // Determine grouping\n const finalGrouping: 'na' | 'normalTpsl' | 'positionTpsl' =\n grouping ?? ((takeProfitPrice ?? stopLossPrice) ? 'normalTpsl' : 'na');\n\n return { orders, grouping: finalGrouping };\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hashTriggerOrders = exports.toSDKTimeInForce = exports.buildTriggerOrderType = exports.buildPositionTriggerOrderFromOrder = exports.classifyTriggerDirection = exports.getTriggerDirection = exports.getTriggerExecution = exports.isLimitExecutionOrderType = exports.isStrategyOrderType = exports.isTriggerOrderType = exports.SCALE_ORDER_COUNT = exports.STRATEGY_ORDER_TYPES = exports.TRIGGER_ORDER_TYPES = void 0;
3
+ exports.hashTriggerOrders = exports.toSDKTimeInForce = exports.buildTriggerOrderType = exports.buildPositionTriggerOrderFromOrder = exports.classifyTriggerDirection = exports.getTriggerDirection = exports.getTriggerExecution = exports.isLimitExecutionOrderType = exports.isTriggerOrderType = exports.TRIGGER_ORDER_TYPES = void 0;
4
4
  /**
5
5
  * All trigger placement types, in a stable order suitable for iteration
6
6
  * (validation tables, e2e matrices).
@@ -11,24 +11,6 @@ exports.TRIGGER_ORDER_TYPES = [
11
11
  'take_profit_market',
12
12
  'take_profit_limit',
13
13
  ];
14
- /**
15
- * All strategy placement types, in a stable order suitable for iteration
16
- * (validation tables, e2e matrices).
17
- */
18
- exports.STRATEGY_ORDER_TYPES = [
19
- 'twap',
20
- 'scale',
21
- 'chase',
22
- ];
23
- /**
24
- * Bounds on how many limit orders a scale placement may fan out into.
25
- *
26
- * A ladder needs at least two rungs to span a range at all; the upper bound
27
- * keeps a single placement from consuming a venue's per-account open-order
28
- * budget. Protocol-agnostic: a provider whose venue is stricter narrows this
29
- * further in its own validation.
30
- */
31
- exports.SCALE_ORDER_COUNT = { min: 2, max: 20 };
32
14
  /**
33
15
  * Order types whose price field (`OrderParams.price`) is a real limit price the
34
16
  * exchange must honour, as opposed to a slippage cap derived from the market.
@@ -38,23 +20,6 @@ const LIMIT_EXECUTION_ORDER_TYPES = [
38
20
  'stop_limit',
39
21
  'take_profit_limit',
40
22
  ];
41
- /**
42
- * Order types that rest limit orders on the book, whatever decides their price.
43
- *
44
- * A superset of `LIMIT_EXECUTION_ORDER_TYPES`: a scale ladder and a chase both
45
- * rest limit orders, but they derive their own prices rather than taking one
46
- * from `OrderParams.price`, so they are limit *execution* without being
47
- * limit-*priced*. A TWAP is absent because its suborders cross the book.
48
- *
49
- * The distinction matters wherever execution is what is being charged or
50
- * bounded — fee tier, max order value — as opposed to where the caller's price
51
- * field is being read.
52
- */
53
- const LIMIT_RESTING_ORDER_TYPES = [
54
- ...LIMIT_EXECUTION_ORDER_TYPES,
55
- 'scale',
56
- 'chase',
57
- ];
58
23
  /**
59
24
  * Check whether an order type is a trigger placement (stop / take profit).
60
25
  *
@@ -65,17 +30,6 @@ function isTriggerOrderType(orderType) {
65
30
  return exports.TRIGGER_ORDER_TYPES.includes(orderType);
66
31
  }
67
32
  exports.isTriggerOrderType = isTriggerOrderType;
68
- /**
69
- * Check whether an order type is a strategy placement (TWAP / scale / chase).
70
- *
71
- * @param orderType - Order type to check.
72
- * @returns True when the placement expands into an execution schedule rather
73
- * than a single order.
74
- */
75
- function isStrategyOrderType(orderType) {
76
- return exports.STRATEGY_ORDER_TYPES.includes(orderType);
77
- }
78
- exports.isStrategyOrderType = isStrategyOrderType;
79
33
  /**
80
34
  * Check whether an order type executes as a limit order.
81
35
  *
@@ -93,19 +47,13 @@ exports.isLimitExecutionOrderType = isLimitExecutionOrderType;
93
47
  * Get how an order executes, ignoring whether it is trigger-gated.
94
48
  *
95
49
  * This is also the coarse execution type that consumers predating trigger orders
96
- * understand (fee tiers, max order value, analytics). It answers "does this rest
97
- * on the book or cross it", which is not the same question as
98
- * `isLimitExecutionOrderType` — a scale ladder and a chase rest limit orders
99
- * without carrying an `OrderParams.price`.
50
+ * understand (fee tiers, max order value, analytics).
100
51
  *
101
52
  * @param orderType - Order type to inspect.
102
- * @returns `'limit'` for limit, `*_limit`, `scale` and `chase`; `'market'`
103
- * otherwise, including `twap`, whose suborders cross the book.
53
+ * @returns `'limit'` for limit and `*_limit` types, `'market'` otherwise.
104
54
  */
105
55
  function getTriggerExecution(orderType) {
106
- return LIMIT_RESTING_ORDER_TYPES.includes(orderType)
107
- ? 'limit'
108
- : 'market';
56
+ return isLimitExecutionOrderType(orderType) ? 'limit' : 'market';
109
57
  }
110
58
  exports.getTriggerExecution = getTriggerExecution;
111
59
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"orderTypes.cjs","sourceRoot":"","sources":["../../src/utils/orderTypes.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Order, PositionTriggerOrder } from '../types/index.js';\nimport type {\n OrderExecution,\n OrderType,\n StrategyOrderType,\n TriggerDirection,\n TriggerOrderType,\n} from '../types/perps-types.js';\n\n/**\n * All trigger placement types, in a stable order suitable for iteration\n * (validation tables, e2e matrices).\n */\nexport const TRIGGER_ORDER_TYPES = [\n 'stop_market',\n 'stop_limit',\n 'take_profit_market',\n 'take_profit_limit',\n] as const satisfies readonly TriggerOrderType[];\n\n/**\n * All strategy placement types, in a stable order suitable for iteration\n * (validation tables, e2e matrices).\n */\nexport const STRATEGY_ORDER_TYPES = [\n 'twap',\n 'scale',\n 'chase',\n] as const satisfies readonly StrategyOrderType[];\n\n/**\n * Bounds on how many limit orders a scale placement may fan out into.\n *\n * A ladder needs at least two rungs to span a range at all; the upper bound\n * keeps a single placement from consuming a venue's per-account open-order\n * budget. Protocol-agnostic: a provider whose venue is stricter narrows this\n * further in its own validation.\n */\nexport const SCALE_ORDER_COUNT = { min: 2, max: 20 } as const;\n\n/**\n * Order types whose price field (`OrderParams.price`) is a real limit price the\n * exchange must honour, as opposed to a slippage cap derived from the market.\n */\nconst LIMIT_EXECUTION_ORDER_TYPES = [\n 'limit',\n 'stop_limit',\n 'take_profit_limit',\n] as const satisfies readonly OrderType[];\n\n/**\n * Order types that rest limit orders on the book, whatever decides their price.\n *\n * A superset of `LIMIT_EXECUTION_ORDER_TYPES`: a scale ladder and a chase both\n * rest limit orders, but they derive their own prices rather than taking one\n * from `OrderParams.price`, so they are limit *execution* without being\n * limit-*priced*. A TWAP is absent because its suborders cross the book.\n *\n * The distinction matters wherever execution is what is being charged or\n * bounded — fee tier, max order value — as opposed to where the caller's price\n * field is being read.\n */\nconst LIMIT_RESTING_ORDER_TYPES = [\n ...LIMIT_EXECUTION_ORDER_TYPES,\n 'scale',\n 'chase',\n] as const satisfies readonly OrderType[];\n\n/**\n * Check whether an order type is a trigger placement (stop / take profit).\n *\n * @param orderType - Order type to check.\n * @returns True when the type requires `OrderParams.triggerPrice`.\n */\nexport function isTriggerOrderType(\n orderType: OrderType,\n): orderType is TriggerOrderType {\n return (TRIGGER_ORDER_TYPES as readonly OrderType[]).includes(orderType);\n}\n\n/**\n * Check whether an order type is a strategy placement (TWAP / scale / chase).\n *\n * @param orderType - Order type to check.\n * @returns True when the placement expands into an execution schedule rather\n * than a single order.\n */\nexport function isStrategyOrderType(\n orderType: OrderType,\n): orderType is StrategyOrderType {\n return (STRATEGY_ORDER_TYPES as readonly OrderType[]).includes(orderType);\n}\n\n/**\n * Check whether an order type executes as a limit order.\n *\n * Covers plain limit orders and the `*_limit` trigger types, both of which\n * require `OrderParams.price`.\n *\n * @param orderType - Order type to check.\n * @returns True when the order executes as a limit order.\n */\nexport function isLimitExecutionOrderType(orderType: OrderType): boolean {\n return (LIMIT_EXECUTION_ORDER_TYPES as readonly OrderType[]).includes(\n orderType,\n );\n}\n\n/**\n * Get how an order executes, ignoring whether it is trigger-gated.\n *\n * This is also the coarse execution type that consumers predating trigger orders\n * understand (fee tiers, max order value, analytics). It answers \"does this rest\n * on the book or cross it\", which is not the same question as\n * `isLimitExecutionOrderType` — a scale ladder and a chase rest limit orders\n * without carrying an `OrderParams.price`.\n *\n * @param orderType - Order type to inspect.\n * @returns `'limit'` for limit, `*_limit`, `scale` and `chase`; `'market'`\n * otherwise, including `twap`, whose suborders cross the book.\n */\nexport function getTriggerExecution(orderType: OrderType): OrderExecution {\n return (LIMIT_RESTING_ORDER_TYPES as readonly OrderType[]).includes(orderType)\n ? 'limit'\n : 'market';\n}\n\n/**\n * Get the direction a trigger order fires in.\n *\n * @param orderType - Trigger order type.\n * @returns `'stop'` for `stop_*`, `'take_profit'` for `take_profit_*`.\n */\nexport function getTriggerDirection(\n orderType: TriggerOrderType,\n): TriggerDirection {\n return orderType === 'stop_market' || orderType === 'stop_limit'\n ? 'stop'\n : 'take_profit';\n}\n\n/**\n * Recover which way a trigger fires from its price relative to the entry.\n *\n * Used when the exchange reports a trigger without naming its placement type:\n * a long takes profit above its entry and stops out below, a short the other\n * way round. Shared by both transports so they classify identically.\n *\n * @param params - Classification parameters\n * @param params.triggerPrice - Price at which the order activates\n * @param params.entryPrice - Entry price of the position it is attached to\n * @param params.positionSize - Signed position size; its sign gives the side\n * @returns The direction, or undefined when there is nothing to compare against\n */\nexport function classifyTriggerDirection(params: {\n triggerPrice?: string;\n entryPrice?: string;\n positionSize: string;\n}): TriggerDirection | undefined {\n const { triggerPrice, entryPrice, positionSize } = params;\n\n const trigger = parseFloat(triggerPrice ?? '');\n const entry = parseFloat(entryPrice ?? '');\n const signedSize = parseFloat(positionSize || '0');\n\n if (!Number.isFinite(trigger) || !Number.isFinite(entry) || entry <= 0) {\n return undefined;\n }\n\n // A long takes profit above its entry and stops out below; a short is the\n // mirror image. A trigger sitting exactly at entry is neither, so both sides\n // fall to 'stop' — matching the legacy price fallback the scalar\n // takeProfitPrice/stopLossPrice fields still use. Splitting that tie the\n // other way would file the order under takeProfitOrders while the scalar\n // still reported it as a stop.\n const isLong = signedSize > 0;\n\n if (isLong) {\n return trigger > entry ? 'take_profit' : 'stop';\n }\n return trigger < entry ? 'take_profit' : 'stop';\n}\n\n/**\n * Project a normalized open order onto the position-state view of a trigger order.\n *\n * Returns undefined when the order is not a trigger, or when its direction can\n * be established neither from a named placement type nor from its price.\n *\n * @param params - Mapping parameters\n * @param params.order - Normalized open order\n * @param params.positionSize - Size of the position the trigger is attached to\n * @param params.entryPrice - Entry price, used to classify an unnamed trigger\n * @returns The position trigger order, or undefined\n */\nexport function buildPositionTriggerOrderFromOrder(params: {\n order: Order;\n positionSize: string;\n entryPrice?: string;\n}): PositionTriggerOrder | undefined {\n const { order, positionSize, entryPrice } = params;\n\n if (!order.isTrigger) {\n return undefined;\n }\n\n // HyperLiquid sometimes reports a bare 'Trigger', naming neither direction\n // nor execution. The direction is still recoverable from the trigger price\n // against the entry, and it is what decides which array the order belongs\n // to — so an unnamed trigger is kept rather than dropped, with its execution\n // mode left unstated. Without a position to compare against there is nothing\n // to recover, and it is dropped.\n const direction =\n order.triggerOrderType === undefined\n ? classifyTriggerDirection({\n triggerPrice: order.triggerPrice ?? order.price,\n entryPrice,\n positionSize,\n })\n : getTriggerDirection(order.triggerOrderType);\n\n if (!direction) {\n return undefined;\n }\n\n const absolutePositionSize = Math.abs(parseFloat(positionSize || '0'));\n const rawSize = Math.abs(parseFloat(order.size || '0'));\n\n // A position-bound TP/SL covers whatever the position currently is. The\n // exchange encodes that as size 0, but `adaptOrderFromSDK` has already\n // resolved it against the position as it stood when the order was adapted,\n // so the size carried here goes stale as soon as the position is resized.\n // The flag is the durable statement of what the trigger covers; the number\n // is not. Reading the number instead would report the old size, and would\n // call the order partial whenever the position had since grown.\n const isPositionBound = order.isPositionTpsl === true;\n const size =\n isPositionBound || rawSize === 0 ? absolutePositionSize : rawSize;\n\n return {\n orderId: order.orderId,\n direction,\n orderType: order.triggerOrderType,\n triggerPrice: order.triggerPrice ?? order.price,\n size: size.toString(),\n isPartial:\n !isPositionBound &&\n rawSize > 0 &&\n absolutePositionSize > 0 &&\n rawSize < absolutePositionSize,\n reduceOnly: Boolean(order.reduceOnly),\n };\n}\n\n/**\n * Build a trigger order type from its two independent dimensions.\n *\n * @param params - Trigger dimensions.\n * @param params.direction - Whether the trigger is a stop or a take profit.\n * @param params.execution - How the order executes once triggered.\n * @returns The matching trigger order type.\n */\nexport function buildTriggerOrderType(params: {\n direction: TriggerDirection;\n execution: OrderExecution;\n}): TriggerOrderType {\n const { direction, execution } = params;\n\n if (direction === 'stop') {\n return execution === 'limit' ? 'stop_limit' : 'stop_market';\n }\n\n return execution === 'limit' ? 'take_profit_limit' : 'take_profit_market';\n}\n\n/**\n * Map the controller's time in force onto the exchange's spelling.\n *\n * Shared by the two order-building paths so they cannot drift apart.\n *\n * @param timeInForce - Requested time in force; defaults to GTC.\n * @returns The SDK time-in-force value.\n */\nexport function toSDKTimeInForce(\n timeInForce?: 'GTC' | 'IOC' | 'ALO',\n): 'Gtc' | 'Ioc' | 'Alo' {\n switch (timeInForce) {\n case 'IOC':\n return 'Ioc';\n case 'ALO':\n return 'Alo';\n default:\n return 'Gtc';\n }\n}\n\n/**\n * Hash the identity of a position's trigger orders for change detection.\n *\n * Streamed positions only re-emit when their hash changes, so this has to move\n * when a trigger is added, removed, repriced, resized, or retyped — otherwise\n * subscribers never receive the updated arrays.\n *\n * The placement type is part of the identity because a trigger can be modified\n * in place: switching a stop from market to limit execution keeps its order ID,\n * trigger price, and size, so nothing else here would move even though the\n * execution semantics subscribers rely on have changed.\n *\n * @param orders - Trigger orders attached to a position, if any.\n * @returns A stable string; `'0'` for both empty and absent.\n */\nexport function hashTriggerOrders(orders?: PositionTriggerOrder[]): string {\n if (!orders || orders.length === 0) {\n return '0';\n }\n return orders\n .map(\n (order) =>\n `${order.orderId}:${order.direction}:${order.orderType ?? '?'}@${order.triggerPrice}x${order.size}${order.isPartial ? 'p' : ''}`,\n )\n .join(',');\n}\n"]}
1
+ {"version":3,"file":"orderTypes.cjs","sourceRoot":"","sources":["../../src/utils/orderTypes.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Order, PositionTriggerOrder } from '../types/index.js';\nimport type {\n OrderExecution,\n OrderType,\n TriggerDirection,\n TriggerOrderType,\n} from '../types/perps-types.js';\n\n/**\n * All trigger placement types, in a stable order suitable for iteration\n * (validation tables, e2e matrices).\n */\nexport const TRIGGER_ORDER_TYPES = [\n 'stop_market',\n 'stop_limit',\n 'take_profit_market',\n 'take_profit_limit',\n] as const satisfies readonly TriggerOrderType[];\n\n/**\n * Order types whose price field (`OrderParams.price`) is a real limit price the\n * exchange must honour, as opposed to a slippage cap derived from the market.\n */\nconst LIMIT_EXECUTION_ORDER_TYPES = [\n 'limit',\n 'stop_limit',\n 'take_profit_limit',\n] as const satisfies readonly OrderType[];\n\n/**\n * Check whether an order type is a trigger placement (stop / take profit).\n *\n * @param orderType - Order type to check.\n * @returns True when the type requires `OrderParams.triggerPrice`.\n */\nexport function isTriggerOrderType(\n orderType: OrderType,\n): orderType is TriggerOrderType {\n return (TRIGGER_ORDER_TYPES as readonly OrderType[]).includes(orderType);\n}\n\n/**\n * Check whether an order type executes as a limit order.\n *\n * Covers plain limit orders and the `*_limit` trigger types, both of which\n * require `OrderParams.price`.\n *\n * @param orderType - Order type to check.\n * @returns True when the order executes as a limit order.\n */\nexport function isLimitExecutionOrderType(orderType: OrderType): boolean {\n return (LIMIT_EXECUTION_ORDER_TYPES as readonly OrderType[]).includes(\n orderType,\n );\n}\n\n/**\n * Get how an order executes, ignoring whether it is trigger-gated.\n *\n * This is also the coarse execution type that consumers predating trigger orders\n * understand (fee tiers, max order value, analytics).\n *\n * @param orderType - Order type to inspect.\n * @returns `'limit'` for limit and `*_limit` types, `'market'` otherwise.\n */\nexport function getTriggerExecution(orderType: OrderType): OrderExecution {\n return isLimitExecutionOrderType(orderType) ? 'limit' : 'market';\n}\n\n/**\n * Get the direction a trigger order fires in.\n *\n * @param orderType - Trigger order type.\n * @returns `'stop'` for `stop_*`, `'take_profit'` for `take_profit_*`.\n */\nexport function getTriggerDirection(\n orderType: TriggerOrderType,\n): TriggerDirection {\n return orderType === 'stop_market' || orderType === 'stop_limit'\n ? 'stop'\n : 'take_profit';\n}\n\n/**\n * Recover which way a trigger fires from its price relative to the entry.\n *\n * Used when the exchange reports a trigger without naming its placement type:\n * a long takes profit above its entry and stops out below, a short the other\n * way round. Shared by both transports so they classify identically.\n *\n * @param params - Classification parameters\n * @param params.triggerPrice - Price at which the order activates\n * @param params.entryPrice - Entry price of the position it is attached to\n * @param params.positionSize - Signed position size; its sign gives the side\n * @returns The direction, or undefined when there is nothing to compare against\n */\nexport function classifyTriggerDirection(params: {\n triggerPrice?: string;\n entryPrice?: string;\n positionSize: string;\n}): TriggerDirection | undefined {\n const { triggerPrice, entryPrice, positionSize } = params;\n\n const trigger = parseFloat(triggerPrice ?? '');\n const entry = parseFloat(entryPrice ?? '');\n const signedSize = parseFloat(positionSize || '0');\n\n if (!Number.isFinite(trigger) || !Number.isFinite(entry) || entry <= 0) {\n return undefined;\n }\n\n // A long takes profit above its entry and stops out below; a short is the\n // mirror image. A trigger sitting exactly at entry is neither, so both sides\n // fall to 'stop' — matching the legacy price fallback the scalar\n // takeProfitPrice/stopLossPrice fields still use. Splitting that tie the\n // other way would file the order under takeProfitOrders while the scalar\n // still reported it as a stop.\n const isLong = signedSize > 0;\n\n if (isLong) {\n return trigger > entry ? 'take_profit' : 'stop';\n }\n return trigger < entry ? 'take_profit' : 'stop';\n}\n\n/**\n * Project a normalized open order onto the position-state view of a trigger order.\n *\n * Returns undefined when the order is not a trigger, or when its direction can\n * be established neither from a named placement type nor from its price.\n *\n * @param params - Mapping parameters\n * @param params.order - Normalized open order\n * @param params.positionSize - Size of the position the trigger is attached to\n * @param params.entryPrice - Entry price, used to classify an unnamed trigger\n * @returns The position trigger order, or undefined\n */\nexport function buildPositionTriggerOrderFromOrder(params: {\n order: Order;\n positionSize: string;\n entryPrice?: string;\n}): PositionTriggerOrder | undefined {\n const { order, positionSize, entryPrice } = params;\n\n if (!order.isTrigger) {\n return undefined;\n }\n\n // HyperLiquid sometimes reports a bare 'Trigger', naming neither direction\n // nor execution. The direction is still recoverable from the trigger price\n // against the entry, and it is what decides which array the order belongs\n // to — so an unnamed trigger is kept rather than dropped, with its execution\n // mode left unstated. Without a position to compare against there is nothing\n // to recover, and it is dropped.\n const direction =\n order.triggerOrderType === undefined\n ? classifyTriggerDirection({\n triggerPrice: order.triggerPrice ?? order.price,\n entryPrice,\n positionSize,\n })\n : getTriggerDirection(order.triggerOrderType);\n\n if (!direction) {\n return undefined;\n }\n\n const absolutePositionSize = Math.abs(parseFloat(positionSize || '0'));\n const rawSize = Math.abs(parseFloat(order.size || '0'));\n\n // A position-bound TP/SL covers whatever the position currently is. The\n // exchange encodes that as size 0, but `adaptOrderFromSDK` has already\n // resolved it against the position as it stood when the order was adapted,\n // so the size carried here goes stale as soon as the position is resized.\n // The flag is the durable statement of what the trigger covers; the number\n // is not. Reading the number instead would report the old size, and would\n // call the order partial whenever the position had since grown.\n const isPositionBound = order.isPositionTpsl === true;\n const size =\n isPositionBound || rawSize === 0 ? absolutePositionSize : rawSize;\n\n return {\n orderId: order.orderId,\n direction,\n orderType: order.triggerOrderType,\n triggerPrice: order.triggerPrice ?? order.price,\n size: size.toString(),\n isPartial:\n !isPositionBound &&\n rawSize > 0 &&\n absolutePositionSize > 0 &&\n rawSize < absolutePositionSize,\n reduceOnly: Boolean(order.reduceOnly),\n };\n}\n\n/**\n * Build a trigger order type from its two independent dimensions.\n *\n * @param params - Trigger dimensions.\n * @param params.direction - Whether the trigger is a stop or a take profit.\n * @param params.execution - How the order executes once triggered.\n * @returns The matching trigger order type.\n */\nexport function buildTriggerOrderType(params: {\n direction: TriggerDirection;\n execution: OrderExecution;\n}): TriggerOrderType {\n const { direction, execution } = params;\n\n if (direction === 'stop') {\n return execution === 'limit' ? 'stop_limit' : 'stop_market';\n }\n\n return execution === 'limit' ? 'take_profit_limit' : 'take_profit_market';\n}\n\n/**\n * Map the controller's time in force onto the exchange's spelling.\n *\n * Shared by the two order-building paths so they cannot drift apart.\n *\n * @param timeInForce - Requested time in force; defaults to GTC.\n * @returns The SDK time-in-force value.\n */\nexport function toSDKTimeInForce(\n timeInForce?: 'GTC' | 'IOC' | 'ALO',\n): 'Gtc' | 'Ioc' | 'Alo' {\n switch (timeInForce) {\n case 'IOC':\n return 'Ioc';\n case 'ALO':\n return 'Alo';\n default:\n return 'Gtc';\n }\n}\n\n/**\n * Hash the identity of a position's trigger orders for change detection.\n *\n * Streamed positions only re-emit when their hash changes, so this has to move\n * when a trigger is added, removed, repriced, resized, or retyped — otherwise\n * subscribers never receive the updated arrays.\n *\n * The placement type is part of the identity because a trigger can be modified\n * in place: switching a stop from market to limit execution keeps its order ID,\n * trigger price, and size, so nothing else here would move even though the\n * execution semantics subscribers rely on have changed.\n *\n * @param orders - Trigger orders attached to a position, if any.\n * @returns A stable string; `'0'` for both empty and absent.\n */\nexport function hashTriggerOrders(orders?: PositionTriggerOrder[]): string {\n if (!orders || orders.length === 0) {\n return '0';\n }\n return orders\n .map(\n (order) =>\n `${order.orderId}:${order.direction}:${order.orderType ?? '?'}@${order.triggerPrice}x${order.size}${order.isPartial ? 'p' : ''}`,\n )\n .join(',');\n}\n"]}
@@ -1,27 +1,10 @@
1
1
  import type { Order, PositionTriggerOrder } from "../types/index.cjs";
2
- import type { OrderExecution, OrderType, StrategyOrderType, TriggerDirection, TriggerOrderType } from "../types/perps-types.cjs";
2
+ import type { OrderExecution, OrderType, TriggerDirection, TriggerOrderType } from "../types/perps-types.cjs";
3
3
  /**
4
4
  * All trigger placement types, in a stable order suitable for iteration
5
5
  * (validation tables, e2e matrices).
6
6
  */
7
7
  export declare const TRIGGER_ORDER_TYPES: readonly ["stop_market", "stop_limit", "take_profit_market", "take_profit_limit"];
8
- /**
9
- * All strategy placement types, in a stable order suitable for iteration
10
- * (validation tables, e2e matrices).
11
- */
12
- export declare const STRATEGY_ORDER_TYPES: readonly ["twap", "scale", "chase"];
13
- /**
14
- * Bounds on how many limit orders a scale placement may fan out into.
15
- *
16
- * A ladder needs at least two rungs to span a range at all; the upper bound
17
- * keeps a single placement from consuming a venue's per-account open-order
18
- * budget. Protocol-agnostic: a provider whose venue is stricter narrows this
19
- * further in its own validation.
20
- */
21
- export declare const SCALE_ORDER_COUNT: {
22
- readonly min: 2;
23
- readonly max: 20;
24
- };
25
8
  /**
26
9
  * Check whether an order type is a trigger placement (stop / take profit).
27
10
  *
@@ -29,14 +12,6 @@ export declare const SCALE_ORDER_COUNT: {
29
12
  * @returns True when the type requires `OrderParams.triggerPrice`.
30
13
  */
31
14
  export declare function isTriggerOrderType(orderType: OrderType): orderType is TriggerOrderType;
32
- /**
33
- * Check whether an order type is a strategy placement (TWAP / scale / chase).
34
- *
35
- * @param orderType - Order type to check.
36
- * @returns True when the placement expands into an execution schedule rather
37
- * than a single order.
38
- */
39
- export declare function isStrategyOrderType(orderType: OrderType): orderType is StrategyOrderType;
40
15
  /**
41
16
  * Check whether an order type executes as a limit order.
42
17
  *
@@ -51,14 +26,10 @@ export declare function isLimitExecutionOrderType(orderType: OrderType): boolean
51
26
  * Get how an order executes, ignoring whether it is trigger-gated.
52
27
  *
53
28
  * This is also the coarse execution type that consumers predating trigger orders
54
- * understand (fee tiers, max order value, analytics). It answers "does this rest
55
- * on the book or cross it", which is not the same question as
56
- * `isLimitExecutionOrderType` — a scale ladder and a chase rest limit orders
57
- * without carrying an `OrderParams.price`.
29
+ * understand (fee tiers, max order value, analytics).
58
30
  *
59
31
  * @param orderType - Order type to inspect.
60
- * @returns `'limit'` for limit, `*_limit`, `scale` and `chase`; `'market'`
61
- * otherwise, including `twap`, whose suborders cross the book.
32
+ * @returns `'limit'` for limit and `*_limit` types, `'market'` otherwise.
62
33
  */
63
34
  export declare function getTriggerExecution(orderType: OrderType): OrderExecution;
64
35
  /**