@metamask-previews/perps-controller 12.0.0-preview-e81fe8853 → 12.1.0-preview-3c77cf4

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 (34) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/dist/PerpsController.cjs +5 -7
  3. package/dist/PerpsController.cjs.map +1 -1
  4. package/dist/PerpsController.d.cts.map +1 -1
  5. package/dist/PerpsController.d.mts.map +1 -1
  6. package/dist/PerpsController.mjs +5 -7
  7. package/dist/PerpsController.mjs.map +1 -1
  8. package/dist/providers/HyperLiquidProvider.cjs +22 -6
  9. package/dist/providers/HyperLiquidProvider.cjs.map +1 -1
  10. package/dist/providers/HyperLiquidProvider.d.cts.map +1 -1
  11. package/dist/providers/HyperLiquidProvider.d.mts.map +1 -1
  12. package/dist/providers/HyperLiquidProvider.mjs +23 -7
  13. package/dist/providers/HyperLiquidProvider.mjs.map +1 -1
  14. package/dist/services/HyperLiquidSubscriptionService.cjs +10 -2
  15. package/dist/services/HyperLiquidSubscriptionService.cjs.map +1 -1
  16. package/dist/services/HyperLiquidSubscriptionService.d.cts.map +1 -1
  17. package/dist/services/HyperLiquidSubscriptionService.d.mts.map +1 -1
  18. package/dist/services/HyperLiquidSubscriptionService.mjs +11 -3
  19. package/dist/services/HyperLiquidSubscriptionService.mjs.map +1 -1
  20. package/dist/types/index.cjs.map +1 -1
  21. package/dist/types/index.d.cts +18 -6
  22. package/dist/types/index.d.cts.map +1 -1
  23. package/dist/types/index.d.mts +18 -6
  24. package/dist/types/index.d.mts.map +1 -1
  25. package/dist/types/index.mjs.map +1 -1
  26. package/dist/utils/orderTypes.cjs +28 -1
  27. package/dist/utils/orderTypes.cjs.map +1 -1
  28. package/dist/utils/orderTypes.d.cts +23 -0
  29. package/dist/utils/orderTypes.d.cts.map +1 -1
  30. package/dist/utils/orderTypes.d.mts +23 -0
  31. package/dist/utils/orderTypes.d.mts.map +1 -1
  32. package/dist/utils/orderTypes.mjs +26 -0
  33. package/dist/utils/orderTypes.mjs.map +1 -1
  34. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"orderTypes.cjs","sourceRoot":"","sources":["../../src/utils/orderTypes.ts"],"names":[],"mappings":";;;AASA;;;GAGG;AACU,QAAA,mBAAmB,GAAG;IACjC,aAAa;IACb,YAAY;IACZ,oBAAoB;IACpB,mBAAmB;CAC2B,CAAC;AAEjD;;;GAGG;AACU,QAAA,oBAAoB,GAAG;IAClC,MAAM;IACN,OAAO;IACP,OAAO;CACwC,CAAC;AAElD;;;;;;;GAOG;AACU,QAAA,iBAAiB,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAW,CAAC;AAE9D;;;GAGG;AACH,MAAM,2BAA2B,GAAG;IAClC,OAAO;IACP,YAAY;IACZ,mBAAmB;CACoB,CAAC;AAE1C;;;;;;;;;;;GAWG;AACH,MAAM,yBAAyB,GAAG;IAChC,GAAG,2BAA2B;IAC9B,OAAO;IACP,OAAO;CACgC,CAAC;AAE1C;;;;;GAKG;AACH,SAAgB,kBAAkB,CAChC,SAAoB;IAEpB,OAAQ,2BAA4C,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAC3E,CAAC;AAJD,gDAIC;AAED;;;;;;GAMG;AACH,SAAgB,mBAAmB,CACjC,SAAoB;IAEpB,OAAQ,4BAA6C,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAC5E,CAAC;AAJD,kDAIC;AAED;;;;;;;;GAQG;AACH,SAAgB,yBAAyB,CAAC,SAAoB;IAC5D,OAAQ,2BAAoD,CAAC,QAAQ,CACnE,SAAS,CACV,CAAC;AACJ,CAAC;AAJD,8DAIC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,mBAAmB,CAAC,SAAoB;IACtD,OAAQ,yBAAkD,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC5E,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,QAAQ,CAAC;AACf,CAAC;AAJD,kDAIC;AAED;;;;;GAKG;AACH,SAAgB,mBAAmB,CACjC,SAA2B;IAE3B,OAAO,SAAS,KAAK,aAAa,IAAI,SAAS,KAAK,YAAY;QAC9D,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,aAAa,CAAC;AACpB,CAAC;AAND,kDAMC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,wBAAwB,CAAC,MAIxC;IACC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;IAE1D,MAAM,OAAO,GAAG,UAAU,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,UAAU,CAAC,YAAY,IAAI,GAAG,CAAC,CAAC;IAEnD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACvE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,0EAA0E;IAC1E,6EAA6E;IAC7E,iEAAiE;IACjE,yEAAyE;IACzE,yEAAyE;IACzE,+BAA+B;IAC/B,MAAM,MAAM,GAAG,UAAU,GAAG,CAAC,CAAC;IAE9B,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;IAClD,CAAC;IACD,OAAO,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;AAClD,CAAC;AA3BD,4DA2BC;AAED;;;;;;;;;;;GAWG;AACH,SAAgB,kCAAkC,CAAC,MAIlD;IACC,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;IAEnD,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QACrB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,2EAA2E;IAC3E,2EAA2E;IAC3E,0EAA0E;IAC1E,6EAA6E;IAC7E,6EAA6E;IAC7E,iCAAiC;IACjC,MAAM,SAAS,GACb,KAAK,CAAC,gBAAgB,KAAK,SAAS;QAClC,CAAC,CAAC,wBAAwB,CAAC;YACvB,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,KAAK;YAC/C,UAAU;YACV,YAAY;SACb,CAAC;QACJ,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAElD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,oBAAoB,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,YAAY,IAAI,GAAG,CAAC,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC;IAExD,wEAAwE;IACxE,uEAAuE;IACvE,2EAA2E;IAC3E,0EAA0E;IAC1E,2EAA2E;IAC3E,0EAA0E;IAC1E,gEAAgE;IAChE,MAAM,eAAe,GAAG,KAAK,CAAC,cAAc,KAAK,IAAI,CAAC;IACtD,MAAM,IAAI,GACR,eAAe,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,OAAO,CAAC;IAEpE,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS;QACT,SAAS,EAAE,KAAK,CAAC,gBAAgB;QACjC,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,KAAK;QAC/C,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;QACrB,SAAS,EACP,CAAC,eAAe;YAChB,OAAO,GAAG,CAAC;YACX,oBAAoB,GAAG,CAAC;YACxB,OAAO,GAAG,oBAAoB;QAChC,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC;KACtC,CAAC;AACJ,CAAC;AAzDD,gFAyDC;AAED;;;;;;;GAOG;AACH,SAAgB,qBAAqB,CAAC,MAGrC;IACC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAExC,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACzB,OAAO,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC;IAC9D,CAAC;IAED,OAAO,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,oBAAoB,CAAC;AAC5E,CAAC;AAXD,sDAWC;AAED;;;;;;;GAOG;AACH,SAAgB,gBAAgB,CAC9B,WAAmC;IAEnC,QAAQ,WAAW,EAAE,CAAC;QACpB,KAAK,KAAK;YACR,OAAO,KAAK,CAAC;QACf,KAAK,KAAK;YACR,OAAO,KAAK,CAAC;QACf;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC;AAXD,4CAWC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,iBAAiB,CAAC,MAA+B;IAC/D,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,MAAM;SACV,GAAG,CACF,CAAC,KAAK,EAAE,EAAE,CACR,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACnI;SACA,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAVD,8CAUC","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 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 * Resolve the scalar TP/SL summary price a position reports for one direction.\n *\n * The scalar fields are only ever scanned from position-bound triggers, so a\n * position whose only take profit (or stop loss) is quantity-scoped reported a\n * count of 1 with no price — and a client that renders the scalar showed\n * nothing. When the direction has exactly one trigger order, that order is the\n * price, whether or not it is position-bound.\n *\n * Two or more triggers keep the scanned value: no single price describes them,\n * and clients render the count instead. Zero triggers keep it too, because it\n * still carries the TP/SL of a *pending* order on the market, which the arrays\n * deliberately exclude.\n *\n * @param params - Resolution parameters\n * @param params.triggerOrders - Trigger orders attached to the position for one direction\n * @param params.scannedPrice - Price scanned from position-bound triggers, if any\n * @returns The price to report, or undefined when there is none\n */\nexport function resolvePositionTriggerSummaryPrice(params: {\n triggerOrders: PositionTriggerOrder[];\n scannedPrice?: string;\n}): string | undefined {\n const { triggerOrders, scannedPrice } = params;\n\n if (triggerOrders.length === 1) {\n return triggerOrders[0].triggerPrice;\n }\n\n return scannedPrice;\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"]}
@@ -103,6 +103,29 @@ export declare function buildPositionTriggerOrderFromOrder(params: {
103
103
  positionSize: string;
104
104
  entryPrice?: string;
105
105
  }): PositionTriggerOrder | undefined;
106
+ /**
107
+ * Resolve the scalar TP/SL summary price a position reports for one direction.
108
+ *
109
+ * The scalar fields are only ever scanned from position-bound triggers, so a
110
+ * position whose only take profit (or stop loss) is quantity-scoped reported a
111
+ * count of 1 with no price — and a client that renders the scalar showed
112
+ * nothing. When the direction has exactly one trigger order, that order is the
113
+ * price, whether or not it is position-bound.
114
+ *
115
+ * Two or more triggers keep the scanned value: no single price describes them,
116
+ * and clients render the count instead. Zero triggers keep it too, because it
117
+ * still carries the TP/SL of a *pending* order on the market, which the arrays
118
+ * deliberately exclude.
119
+ *
120
+ * @param params - Resolution parameters
121
+ * @param params.triggerOrders - Trigger orders attached to the position for one direction
122
+ * @param params.scannedPrice - Price scanned from position-bound triggers, if any
123
+ * @returns The price to report, or undefined when there is none
124
+ */
125
+ export declare function resolvePositionTriggerSummaryPrice(params: {
126
+ triggerOrders: PositionTriggerOrder[];
127
+ scannedPrice?: string;
128
+ }): string | undefined;
106
129
  /**
107
130
  * Build a trigger order type from its two independent dimensions.
108
131
  *
@@ -1 +1 @@
1
- {"version":3,"file":"orderTypes.d.cts","sourceRoot":"","sources":["../../src/utils/orderTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,oBAAoB,EAAE,2BAA0B;AACrE,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EACjB,iCAAgC;AAEjC;;;GAGG;AACH,eAAO,MAAM,mBAAmB,mFAKgB,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,qCAIgB,CAAC;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB;;;CAA+B,CAAC;AA8B9D;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,GACnB,SAAS,IAAI,gBAAgB,CAE/B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,GACnB,SAAS,IAAI,iBAAiB,CAEhC;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAIvE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,cAAc,CAIxE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,gBAAgB,GAC1B,gBAAgB,CAIlB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,gBAAgB,GAAG,SAAS,CAuB/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kCAAkC,CAAC,MAAM,EAAE;IACzD,KAAK,EAAE,KAAK,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,oBAAoB,GAAG,SAAS,CAqDnC;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,SAAS,EAAE,gBAAgB,CAAC;IAC5B,SAAS,EAAE,cAAc,CAAC;CAC3B,GAAG,gBAAgB,CAQnB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAClC,KAAK,GAAG,KAAK,GAAG,KAAK,CASvB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,oBAAoB,EAAE,GAAG,MAAM,CAUzE"}
1
+ {"version":3,"file":"orderTypes.d.cts","sourceRoot":"","sources":["../../src/utils/orderTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,oBAAoB,EAAE,2BAA0B;AACrE,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EACjB,iCAAgC;AAEjC;;;GAGG;AACH,eAAO,MAAM,mBAAmB,mFAKgB,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,qCAIgB,CAAC;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB;;;CAA+B,CAAC;AA8B9D;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,GACnB,SAAS,IAAI,gBAAgB,CAE/B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,GACnB,SAAS,IAAI,iBAAiB,CAEhC;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAIvE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,cAAc,CAIxE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,gBAAgB,GAC1B,gBAAgB,CAIlB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,gBAAgB,GAAG,SAAS,CAuB/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kCAAkC,CAAC,MAAM,EAAE;IACzD,KAAK,EAAE,KAAK,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,oBAAoB,GAAG,SAAS,CAqDnC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kCAAkC,CAAC,MAAM,EAAE;IACzD,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,MAAM,GAAG,SAAS,CAQrB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,SAAS,EAAE,gBAAgB,CAAC;IAC5B,SAAS,EAAE,cAAc,CAAC;CAC3B,GAAG,gBAAgB,CAQnB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAClC,KAAK,GAAG,KAAK,GAAG,KAAK,CASvB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,oBAAoB,EAAE,GAAG,MAAM,CAUzE"}
@@ -103,6 +103,29 @@ export declare function buildPositionTriggerOrderFromOrder(params: {
103
103
  positionSize: string;
104
104
  entryPrice?: string;
105
105
  }): PositionTriggerOrder | undefined;
106
+ /**
107
+ * Resolve the scalar TP/SL summary price a position reports for one direction.
108
+ *
109
+ * The scalar fields are only ever scanned from position-bound triggers, so a
110
+ * position whose only take profit (or stop loss) is quantity-scoped reported a
111
+ * count of 1 with no price — and a client that renders the scalar showed
112
+ * nothing. When the direction has exactly one trigger order, that order is the
113
+ * price, whether or not it is position-bound.
114
+ *
115
+ * Two or more triggers keep the scanned value: no single price describes them,
116
+ * and clients render the count instead. Zero triggers keep it too, because it
117
+ * still carries the TP/SL of a *pending* order on the market, which the arrays
118
+ * deliberately exclude.
119
+ *
120
+ * @param params - Resolution parameters
121
+ * @param params.triggerOrders - Trigger orders attached to the position for one direction
122
+ * @param params.scannedPrice - Price scanned from position-bound triggers, if any
123
+ * @returns The price to report, or undefined when there is none
124
+ */
125
+ export declare function resolvePositionTriggerSummaryPrice(params: {
126
+ triggerOrders: PositionTriggerOrder[];
127
+ scannedPrice?: string;
128
+ }): string | undefined;
106
129
  /**
107
130
  * Build a trigger order type from its two independent dimensions.
108
131
  *
@@ -1 +1 @@
1
- {"version":3,"file":"orderTypes.d.mts","sourceRoot":"","sources":["../../src/utils/orderTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,oBAAoB,EAAE,2BAA0B;AACrE,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EACjB,iCAAgC;AAEjC;;;GAGG;AACH,eAAO,MAAM,mBAAmB,mFAKgB,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,qCAIgB,CAAC;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB;;;CAA+B,CAAC;AA8B9D;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,GACnB,SAAS,IAAI,gBAAgB,CAE/B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,GACnB,SAAS,IAAI,iBAAiB,CAEhC;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAIvE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,cAAc,CAIxE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,gBAAgB,GAC1B,gBAAgB,CAIlB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,gBAAgB,GAAG,SAAS,CAuB/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kCAAkC,CAAC,MAAM,EAAE;IACzD,KAAK,EAAE,KAAK,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,oBAAoB,GAAG,SAAS,CAqDnC;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,SAAS,EAAE,gBAAgB,CAAC;IAC5B,SAAS,EAAE,cAAc,CAAC;CAC3B,GAAG,gBAAgB,CAQnB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAClC,KAAK,GAAG,KAAK,GAAG,KAAK,CASvB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,oBAAoB,EAAE,GAAG,MAAM,CAUzE"}
1
+ {"version":3,"file":"orderTypes.d.mts","sourceRoot":"","sources":["../../src/utils/orderTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,oBAAoB,EAAE,2BAA0B;AACrE,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EACjB,iCAAgC;AAEjC;;;GAGG;AACH,eAAO,MAAM,mBAAmB,mFAKgB,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,qCAIgB,CAAC;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB;;;CAA+B,CAAC;AA8B9D;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,GACnB,SAAS,IAAI,gBAAgB,CAE/B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,GACnB,SAAS,IAAI,iBAAiB,CAEhC;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAIvE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,cAAc,CAIxE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,gBAAgB,GAC1B,gBAAgB,CAIlB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,gBAAgB,GAAG,SAAS,CAuB/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kCAAkC,CAAC,MAAM,EAAE;IACzD,KAAK,EAAE,KAAK,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,oBAAoB,GAAG,SAAS,CAqDnC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kCAAkC,CAAC,MAAM,EAAE;IACzD,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,MAAM,GAAG,SAAS,CAQrB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,SAAS,EAAE,gBAAgB,CAAC;IAC5B,SAAS,EAAE,cAAc,CAAC;CAC3B,GAAG,gBAAgB,CAQnB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAClC,KAAK,GAAG,KAAK,GAAG,KAAK,CASvB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,oBAAoB,EAAE,GAAG,MAAM,CAUzE"}
@@ -202,6 +202,32 @@ export function buildPositionTriggerOrderFromOrder(params) {
202
202
  reduceOnly: Boolean(order.reduceOnly),
203
203
  };
204
204
  }
205
+ /**
206
+ * Resolve the scalar TP/SL summary price a position reports for one direction.
207
+ *
208
+ * The scalar fields are only ever scanned from position-bound triggers, so a
209
+ * position whose only take profit (or stop loss) is quantity-scoped reported a
210
+ * count of 1 with no price — and a client that renders the scalar showed
211
+ * nothing. When the direction has exactly one trigger order, that order is the
212
+ * price, whether or not it is position-bound.
213
+ *
214
+ * Two or more triggers keep the scanned value: no single price describes them,
215
+ * and clients render the count instead. Zero triggers keep it too, because it
216
+ * still carries the TP/SL of a *pending* order on the market, which the arrays
217
+ * deliberately exclude.
218
+ *
219
+ * @param params - Resolution parameters
220
+ * @param params.triggerOrders - Trigger orders attached to the position for one direction
221
+ * @param params.scannedPrice - Price scanned from position-bound triggers, if any
222
+ * @returns The price to report, or undefined when there is none
223
+ */
224
+ export function resolvePositionTriggerSummaryPrice(params) {
225
+ const { triggerOrders, scannedPrice } = params;
226
+ if (triggerOrders.length === 1) {
227
+ return triggerOrders[0].triggerPrice;
228
+ }
229
+ return scannedPrice;
230
+ }
205
231
  /**
206
232
  * Build a trigger order type from its two independent dimensions.
207
233
  *
@@ -1 +1 @@
1
- {"version":3,"file":"orderTypes.mjs","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.mjs","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 * Resolve the scalar TP/SL summary price a position reports for one direction.\n *\n * The scalar fields are only ever scanned from position-bound triggers, so a\n * position whose only take profit (or stop loss) is quantity-scoped reported a\n * count of 1 with no price — and a client that renders the scalar showed\n * nothing. When the direction has exactly one trigger order, that order is the\n * price, whether or not it is position-bound.\n *\n * Two or more triggers keep the scanned value: no single price describes them,\n * and clients render the count instead. Zero triggers keep it too, because it\n * still carries the TP/SL of a *pending* order on the market, which the arrays\n * deliberately exclude.\n *\n * @param params - Resolution parameters\n * @param params.triggerOrders - Trigger orders attached to the position for one direction\n * @param params.scannedPrice - Price scanned from position-bound triggers, if any\n * @returns The price to report, or undefined when there is none\n */\nexport function resolvePositionTriggerSummaryPrice(params: {\n triggerOrders: PositionTriggerOrder[];\n scannedPrice?: string;\n}): string | undefined {\n const { triggerOrders, scannedPrice } = params;\n\n if (triggerOrders.length === 1) {\n return triggerOrders[0].triggerPrice;\n }\n\n return scannedPrice;\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/perps-controller",
3
- "version": "12.0.0-preview-e81fe8853",
3
+ "version": "12.1.0-preview-3c77cf4",
4
4
  "description": "Controller for perpetual trading functionality in MetaMask",
5
5
  "keywords": [
6
6
  "Ethereum",