@lifi/perps-sdk 23.0.0 → 24.0.0

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 (289) hide show
  1. package/README.md +85 -45
  2. package/dist/cjs/client/PerpsClient.d.ts +3 -2
  3. package/dist/cjs/client/PerpsClient.d.ts.map +1 -1
  4. package/dist/cjs/client/PerpsClient.js +16 -9
  5. package/dist/cjs/client/PerpsClient.js.map +1 -1
  6. package/dist/cjs/decimal/arithmetic.d.ts +13 -0
  7. package/dist/cjs/decimal/arithmetic.d.ts.map +1 -0
  8. package/dist/cjs/decimal/arithmetic.js +48 -0
  9. package/dist/cjs/decimal/arithmetic.js.map +1 -0
  10. package/dist/cjs/decimal/big.d.ts +0 -1
  11. package/dist/cjs/decimal/big.d.ts.map +1 -1
  12. package/dist/cjs/decimal/big.js +0 -4
  13. package/dist/cjs/decimal/big.js.map +1 -1
  14. package/dist/cjs/decimal/compare.d.ts +7 -0
  15. package/dist/cjs/decimal/compare.d.ts.map +1 -0
  16. package/dist/cjs/decimal/compare.js +21 -0
  17. package/dist/cjs/decimal/compare.js.map +1 -0
  18. package/dist/cjs/decimal/convert.d.ts +12 -6
  19. package/dist/cjs/decimal/convert.d.ts.map +1 -1
  20. package/dist/cjs/decimal/convert.js +36 -14
  21. package/dist/cjs/decimal/convert.js.map +1 -1
  22. package/dist/cjs/decimal/decimalStringToBig.d.ts +5 -0
  23. package/dist/cjs/decimal/decimalStringToBig.d.ts.map +1 -0
  24. package/dist/cjs/decimal/decimalStringToBig.js +27 -0
  25. package/dist/cjs/decimal/decimalStringToBig.js.map +1 -0
  26. package/dist/cjs/decimal/format.d.ts.map +1 -1
  27. package/dist/cjs/decimal/format.js +70 -49
  28. package/dist/cjs/decimal/format.js.map +1 -1
  29. package/dist/cjs/decimal/parse.d.ts +4 -3
  30. package/dist/cjs/decimal/parse.d.ts.map +1 -1
  31. package/dist/cjs/decimal/parse.js +44 -19
  32. package/dist/cjs/decimal/parse.js.map +1 -1
  33. package/dist/cjs/index.d.ts +13 -10
  34. package/dist/cjs/index.d.ts.map +1 -1
  35. package/dist/cjs/index.js +66 -7
  36. package/dist/cjs/index.js.map +1 -1
  37. package/dist/cjs/math/fills.d.ts +1 -1
  38. package/dist/cjs/math/fills.d.ts.map +1 -1
  39. package/dist/cjs/math/fills.js +28 -13
  40. package/dist/cjs/math/fills.js.map +1 -1
  41. package/dist/cjs/math/order.d.ts +27 -16
  42. package/dist/cjs/math/order.d.ts.map +1 -1
  43. package/dist/cjs/math/order.js +106 -122
  44. package/dist/cjs/math/order.js.map +1 -1
  45. package/dist/cjs/math/position.d.ts +85 -35
  46. package/dist/cjs/math/position.d.ts.map +1 -1
  47. package/dist/cjs/math/position.js +88 -91
  48. package/dist/cjs/math/position.js.map +1 -1
  49. package/dist/cjs/services/getGasRecommendation.js +1 -1
  50. package/dist/cjs/services/getGasRecommendation.js.map +1 -1
  51. package/dist/cjs/services/getQuote.d.ts +1 -1
  52. package/dist/cjs/services/resolveSubscribeQuote.d.ts.map +1 -1
  53. package/dist/cjs/services/resolveSubscribeQuote.js +3 -2
  54. package/dist/cjs/services/resolveSubscribeQuote.js.map +1 -1
  55. package/dist/cjs/transport/request.d.ts +1 -0
  56. package/dist/cjs/transport/request.d.ts.map +1 -1
  57. package/dist/cjs/transport/request.js +2 -2
  58. package/dist/cjs/transport/request.js.map +1 -1
  59. package/dist/cjs/types/api.d.ts +4 -0
  60. package/dist/cjs/types/api.d.ts.map +1 -1
  61. package/dist/cjs/types/provider.d.ts +9 -5
  62. package/dist/cjs/types/provider.d.ts.map +1 -1
  63. package/dist/cjs/types/withdrawal.d.ts +12 -4
  64. package/dist/cjs/types/withdrawal.d.ts.map +1 -1
  65. package/dist/cjs/utils/createSafeFunction.d.ts +2 -0
  66. package/dist/cjs/utils/createSafeFunction.d.ts.map +1 -0
  67. package/dist/cjs/utils/createSafeFunction.js +15 -0
  68. package/dist/cjs/utils/createSafeFunction.js.map +1 -0
  69. package/dist/cjs/utils/timestampToIsoString.d.ts +3 -0
  70. package/dist/cjs/utils/timestampToIsoString.d.ts.map +1 -0
  71. package/dist/cjs/utils/timestampToIsoString.js +16 -0
  72. package/dist/cjs/utils/timestampToIsoString.js.map +1 -0
  73. package/dist/cjs/utils/warnOnce.d.ts +2 -0
  74. package/dist/cjs/utils/warnOnce.d.ts.map +1 -1
  75. package/dist/cjs/utils/warnOnce.js +13 -1
  76. package/dist/cjs/utils/warnOnce.js.map +1 -1
  77. package/dist/cjs/version.d.ts +1 -1
  78. package/dist/cjs/version.js +1 -1
  79. package/dist/cjs/websocket/PerpsWsClient.d.ts +2 -0
  80. package/dist/cjs/websocket/PerpsWsClient.d.ts.map +1 -1
  81. package/dist/cjs/websocket/PerpsWsClient.js +14 -1
  82. package/dist/cjs/websocket/PerpsWsClient.js.map +1 -1
  83. package/dist/cjs/websocket/wsLog.d.ts +2 -0
  84. package/dist/cjs/websocket/wsLog.d.ts.map +1 -1
  85. package/dist/cjs/websocket/wsLog.js +8 -0
  86. package/dist/cjs/websocket/wsLog.js.map +1 -1
  87. package/dist/cjs/wire/orderAmounts.d.ts.map +1 -1
  88. package/dist/cjs/wire/orderAmounts.js +6 -9
  89. package/dist/cjs/wire/orderAmounts.js.map +1 -1
  90. package/dist/cjs/wire/refuel.d.ts +4 -1
  91. package/dist/cjs/wire/refuel.d.ts.map +1 -1
  92. package/dist/cjs/wire/refuel.js +23 -6
  93. package/dist/cjs/wire/refuel.js.map +1 -1
  94. package/dist/cjs/wire/transferable.js +3 -3
  95. package/dist/cjs/wire/transferable.js.map +1 -1
  96. package/dist/cjs/wire/withdraw.d.ts.map +1 -1
  97. package/dist/cjs/wire/withdraw.js +3 -3
  98. package/dist/cjs/wire/withdraw.js.map +1 -1
  99. package/dist/esm/client/PerpsClient.d.ts +17 -4
  100. package/dist/esm/client/PerpsClient.d.ts.map +1 -1
  101. package/dist/esm/client/PerpsClient.js +30 -8
  102. package/dist/esm/client/PerpsClient.js.map +1 -1
  103. package/dist/esm/decimal/arithmetic.d.ts +84 -0
  104. package/dist/esm/decimal/arithmetic.d.ts.map +1 -0
  105. package/dist/esm/decimal/arithmetic.js +110 -0
  106. package/dist/esm/decimal/arithmetic.js.map +1 -0
  107. package/dist/esm/decimal/big.d.ts +0 -2
  108. package/dist/esm/decimal/big.d.ts.map +1 -1
  109. package/dist/esm/decimal/big.js +0 -4
  110. package/dist/esm/decimal/big.js.map +1 -1
  111. package/dist/esm/decimal/compare.d.ts +45 -0
  112. package/dist/esm/decimal/compare.d.ts.map +1 -0
  113. package/dist/esm/decimal/compare.js +53 -0
  114. package/dist/esm/decimal/compare.js.map +1 -0
  115. package/dist/esm/decimal/convert.d.ts +62 -26
  116. package/dist/esm/decimal/convert.d.ts.map +1 -1
  117. package/dist/esm/decimal/convert.js +76 -25
  118. package/dist/esm/decimal/convert.js.map +1 -1
  119. package/dist/esm/decimal/decimalStringToBig.d.ts +26 -0
  120. package/dist/esm/decimal/decimalStringToBig.d.ts.map +1 -0
  121. package/dist/esm/decimal/decimalStringToBig.js +40 -0
  122. package/dist/esm/decimal/decimalStringToBig.js.map +1 -0
  123. package/dist/esm/decimal/format.d.ts +4 -1
  124. package/dist/esm/decimal/format.d.ts.map +1 -1
  125. package/dist/esm/decimal/format.js +71 -66
  126. package/dist/esm/decimal/format.js.map +1 -1
  127. package/dist/esm/decimal/parse.d.ts +26 -7
  128. package/dist/esm/decimal/parse.d.ts.map +1 -1
  129. package/dist/esm/decimal/parse.js +59 -33
  130. package/dist/esm/decimal/parse.js.map +1 -1
  131. package/dist/esm/index.d.ts +13 -10
  132. package/dist/esm/index.d.ts.map +1 -1
  133. package/dist/esm/index.js +9 -6
  134. package/dist/esm/index.js.map +1 -1
  135. package/dist/esm/math/fills.d.ts +6 -3
  136. package/dist/esm/math/fills.d.ts.map +1 -1
  137. package/dist/esm/math/fills.js +31 -16
  138. package/dist/esm/math/fills.js.map +1 -1
  139. package/dist/esm/math/order.d.ts +85 -65
  140. package/dist/esm/math/order.d.ts.map +1 -1
  141. package/dist/esm/math/order.js +158 -166
  142. package/dist/esm/math/order.js.map +1 -1
  143. package/dist/esm/math/position.d.ts +155 -98
  144. package/dist/esm/math/position.d.ts.map +1 -1
  145. package/dist/esm/math/position.js +157 -155
  146. package/dist/esm/math/position.js.map +1 -1
  147. package/dist/esm/services/getGasRecommendation.js +1 -1
  148. package/dist/esm/services/getGasRecommendation.js.map +1 -1
  149. package/dist/esm/services/getQuote.d.ts +2 -2
  150. package/dist/esm/services/getQuote.js +1 -1
  151. package/dist/esm/services/resolveSubscribeQuote.d.ts +1 -1
  152. package/dist/esm/services/resolveSubscribeQuote.d.ts.map +1 -1
  153. package/dist/esm/services/resolveSubscribeQuote.js +4 -3
  154. package/dist/esm/services/resolveSubscribeQuote.js.map +1 -1
  155. package/dist/esm/transport/request.d.ts +5 -0
  156. package/dist/esm/transport/request.d.ts.map +1 -1
  157. package/dist/esm/transport/request.js +2 -2
  158. package/dist/esm/transport/request.js.map +1 -1
  159. package/dist/esm/types/api.d.ts +9 -0
  160. package/dist/esm/types/api.d.ts.map +1 -1
  161. package/dist/esm/types/provider.d.ts +26 -13
  162. package/dist/esm/types/provider.d.ts.map +1 -1
  163. package/dist/esm/types/withdrawal.d.ts +32 -7
  164. package/dist/esm/types/withdrawal.d.ts.map +1 -1
  165. package/dist/esm/utils/createSafeFunction.d.ts +9 -0
  166. package/dist/esm/utils/createSafeFunction.d.ts.map +1 -0
  167. package/dist/esm/utils/createSafeFunction.js +19 -0
  168. package/dist/esm/utils/createSafeFunction.js.map +1 -0
  169. package/dist/esm/utils/timestampToIsoString.d.ts +15 -0
  170. package/dist/esm/utils/timestampToIsoString.d.ts.map +1 -0
  171. package/dist/esm/utils/timestampToIsoString.js +24 -0
  172. package/dist/esm/utils/timestampToIsoString.js.map +1 -0
  173. package/dist/esm/utils/warnOnce.d.ts +16 -0
  174. package/dist/esm/utils/warnOnce.d.ts.map +1 -1
  175. package/dist/esm/utils/warnOnce.js +24 -0
  176. package/dist/esm/utils/warnOnce.js.map +1 -1
  177. package/dist/esm/version.d.ts +1 -1
  178. package/dist/esm/version.js +1 -1
  179. package/dist/esm/websocket/PerpsWsClient.d.ts +7 -0
  180. package/dist/esm/websocket/PerpsWsClient.d.ts.map +1 -1
  181. package/dist/esm/websocket/PerpsWsClient.js +20 -2
  182. package/dist/esm/websocket/PerpsWsClient.js.map +1 -1
  183. package/dist/esm/websocket/wsLog.d.ts +12 -0
  184. package/dist/esm/websocket/wsLog.d.ts.map +1 -1
  185. package/dist/esm/websocket/wsLog.js +18 -0
  186. package/dist/esm/websocket/wsLog.js.map +1 -1
  187. package/dist/esm/wire/orderAmounts.d.ts.map +1 -1
  188. package/dist/esm/wire/orderAmounts.js +2 -2
  189. package/dist/esm/wire/orderAmounts.js.map +1 -1
  190. package/dist/esm/wire/refuel.d.ts +24 -14
  191. package/dist/esm/wire/refuel.d.ts.map +1 -1
  192. package/dist/esm/wire/refuel.js +38 -17
  193. package/dist/esm/wire/refuel.js.map +1 -1
  194. package/dist/esm/wire/transferable.js +3 -3
  195. package/dist/esm/wire/transferable.js.map +1 -1
  196. package/dist/esm/wire/withdraw.d.ts.map +1 -1
  197. package/dist/esm/wire/withdraw.js +3 -3
  198. package/dist/esm/wire/withdraw.js.map +1 -1
  199. package/dist/types/client/PerpsClient.d.ts +17 -4
  200. package/dist/types/client/PerpsClient.d.ts.map +1 -1
  201. package/dist/types/decimal/arithmetic.d.ts +84 -0
  202. package/dist/types/decimal/arithmetic.d.ts.map +1 -0
  203. package/dist/types/decimal/big.d.ts +0 -2
  204. package/dist/types/decimal/big.d.ts.map +1 -1
  205. package/dist/types/decimal/compare.d.ts +45 -0
  206. package/dist/types/decimal/compare.d.ts.map +1 -0
  207. package/dist/types/decimal/convert.d.ts +62 -26
  208. package/dist/types/decimal/convert.d.ts.map +1 -1
  209. package/dist/types/decimal/decimalStringToBig.d.ts +26 -0
  210. package/dist/types/decimal/decimalStringToBig.d.ts.map +1 -0
  211. package/dist/types/decimal/format.d.ts +4 -1
  212. package/dist/types/decimal/format.d.ts.map +1 -1
  213. package/dist/types/decimal/parse.d.ts +26 -7
  214. package/dist/types/decimal/parse.d.ts.map +1 -1
  215. package/dist/types/index.d.ts +13 -10
  216. package/dist/types/index.d.ts.map +1 -1
  217. package/dist/types/math/fills.d.ts +6 -3
  218. package/dist/types/math/fills.d.ts.map +1 -1
  219. package/dist/types/math/order.d.ts +85 -65
  220. package/dist/types/math/order.d.ts.map +1 -1
  221. package/dist/types/math/position.d.ts +155 -98
  222. package/dist/types/math/position.d.ts.map +1 -1
  223. package/dist/types/services/getQuote.d.ts +2 -2
  224. package/dist/types/services/resolveSubscribeQuote.d.ts +1 -1
  225. package/dist/types/services/resolveSubscribeQuote.d.ts.map +1 -1
  226. package/dist/types/transport/request.d.ts +5 -0
  227. package/dist/types/transport/request.d.ts.map +1 -1
  228. package/dist/types/types/api.d.ts +9 -0
  229. package/dist/types/types/api.d.ts.map +1 -1
  230. package/dist/types/types/provider.d.ts +26 -13
  231. package/dist/types/types/provider.d.ts.map +1 -1
  232. package/dist/types/types/withdrawal.d.ts +32 -7
  233. package/dist/types/types/withdrawal.d.ts.map +1 -1
  234. package/dist/types/utils/createSafeFunction.d.ts +9 -0
  235. package/dist/types/utils/createSafeFunction.d.ts.map +1 -0
  236. package/dist/types/utils/timestampToIsoString.d.ts +15 -0
  237. package/dist/types/utils/timestampToIsoString.d.ts.map +1 -0
  238. package/dist/types/utils/warnOnce.d.ts +16 -0
  239. package/dist/types/utils/warnOnce.d.ts.map +1 -1
  240. package/dist/types/version.d.ts +1 -1
  241. package/dist/types/websocket/PerpsWsClient.d.ts +7 -0
  242. package/dist/types/websocket/PerpsWsClient.d.ts.map +1 -1
  243. package/dist/types/websocket/wsLog.d.ts +12 -0
  244. package/dist/types/websocket/wsLog.d.ts.map +1 -1
  245. package/dist/types/wire/orderAmounts.d.ts.map +1 -1
  246. package/dist/types/wire/refuel.d.ts +24 -14
  247. package/dist/types/wire/refuel.d.ts.map +1 -1
  248. package/dist/types/wire/withdraw.d.ts.map +1 -1
  249. package/package.json +2 -2
  250. package/src/client/PerpsClient.ts +48 -10
  251. package/src/decimal/arithmetic.ts +144 -0
  252. package/src/decimal/big.ts +0 -5
  253. package/src/decimal/compare.ts +67 -0
  254. package/src/decimal/convert.ts +123 -44
  255. package/src/decimal/decimalStringToBig.ts +47 -0
  256. package/src/decimal/format.ts +81 -68
  257. package/src/decimal/parse.ts +73 -36
  258. package/src/index.ts +76 -8
  259. package/src/math/fills.ts +32 -17
  260. package/src/math/order.ts +261 -216
  261. package/src/math/position.ts +277 -194
  262. package/src/services/getGasRecommendation.ts +1 -1
  263. package/src/services/getQuote.ts +2 -2
  264. package/src/services/resolveSubscribeQuote.ts +4 -3
  265. package/src/transport/request.ts +7 -2
  266. package/src/types/api.ts +10 -0
  267. package/src/types/provider.ts +35 -13
  268. package/src/types/withdrawal.ts +35 -8
  269. package/src/utils/createSafeFunction.ts +20 -0
  270. package/src/utils/timestampToIsoString.ts +31 -0
  271. package/src/utils/warnOnce.ts +44 -0
  272. package/src/version.ts +1 -1
  273. package/src/websocket/PerpsWsClient.ts +25 -2
  274. package/src/websocket/wsLog.ts +31 -0
  275. package/src/wire/orderAmounts.ts +3 -2
  276. package/src/wire/refuel.ts +56 -20
  277. package/src/wire/transferable.ts +3 -3
  278. package/src/wire/withdraw.ts +3 -5
  279. package/dist/cjs/decimal/requireDecimal.d.ts +0 -4
  280. package/dist/cjs/decimal/requireDecimal.d.ts.map +0 -1
  281. package/dist/cjs/decimal/requireDecimal.js +0 -17
  282. package/dist/cjs/decimal/requireDecimal.js.map +0 -1
  283. package/dist/esm/decimal/requireDecimal.d.ts +0 -11
  284. package/dist/esm/decimal/requireDecimal.d.ts.map +0 -1
  285. package/dist/esm/decimal/requireDecimal.js +0 -18
  286. package/dist/esm/decimal/requireDecimal.js.map +0 -1
  287. package/dist/types/decimal/requireDecimal.d.ts +0 -11
  288. package/dist/types/decimal/requireDecimal.d.ts.map +0 -1
  289. package/src/decimal/requireDecimal.ts +0 -21
package/src/math/order.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  /**
2
- * Display-tier order formulas. Every function takes and gives `number`;
3
- * exact decimal arithmetic happens internally with `DivBig`.
2
+ * Display-tier order formulas. Every function takes and gives decimal
3
+ * strings; arithmetic is exact Big.js, with division to 40 decimal places.
4
+ * Each formula throws `ValidationError` on an input that is not a decimal
5
+ * string and has a `safe*` pair that gives `undefined` in place of the throw.
4
6
  */
5
7
 
6
8
  import {
@@ -20,10 +22,14 @@ import {
20
22
  type TradeType,
21
23
  type TriggerOrder,
22
24
  } from '@lifi/perps-types'
23
- import type Big from 'big.js'
24
- import { areFinite, DivBig } from '../decimal/big.js'
25
- import { numberToDecimalString } from '../decimal/convert.js'
25
+ import { DivBig } from '../decimal/big.js'
26
+ import {
27
+ bigToDecimalString,
28
+ decimalStringToBig,
29
+ decimalStringToDivBig,
30
+ } from '../decimal/decimalStringToBig.js'
26
31
  import { PerpsError } from '../errors/PerpsError.js'
32
+ import { createSafeFunction } from '../utils/createSafeFunction.js'
27
33
  import {
28
34
  isActiveOrderStatus,
29
35
  isRegularOrder,
@@ -32,74 +38,91 @@ import {
32
38
  import { calculateRealizedPnl } from './position.js'
33
39
 
34
40
  /**
35
- * Calculate position size in asset units from margin.
41
+ * Position size in asset units from margin: `marginUsd × leverage ÷ price`.
36
42
  *
37
- * @param marginUsd - Margin amount in USD
38
- * @param leverage - Position leverage
39
- * @param price - Current asset price
40
- * @returns Position size in asset units
41
43
  * @example
42
44
  * ```ts
43
- * calculateSize(100, 10, 2000) // 0.5 (ETH at $2000)
45
+ * calculateSize('100', '10', '2000') // '0.5' (ETH at $2000)
44
46
  * ```
47
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string
48
+ * or `price` is zero.
45
49
  * @public
46
50
  */
47
51
  export function calculateSize(
48
- marginUsd: number,
49
- leverage: number,
50
- price: number
51
- ): number {
52
- if (price === 0) {
53
- return (marginUsd * leverage) / price
54
- }
55
- if (!areFinite(marginUsd, leverage, price)) {
56
- return Number.NaN
52
+ marginUsd: string,
53
+ leverage: string,
54
+ price: string
55
+ ): string {
56
+ const notional = decimalStringToDivBig(marginUsd).times(
57
+ decimalStringToBig(leverage)
58
+ )
59
+ const priceBig = decimalStringToBig(price)
60
+ if (priceBig.eq(0)) {
61
+ throw new PerpsError(
62
+ PerpsErrorCode.ValidationError,
63
+ 'Price must not be zero.'
64
+ )
57
65
  }
58
- return new DivBig(marginUsd).times(leverage).div(price).toNumber()
66
+ return bigToDecimalString(notional.div(priceBig))
59
67
  }
60
68
 
69
+ /** @public */
70
+ export const safeCalculateSize = createSafeFunction(
71
+ 'calculateSize',
72
+ calculateSize
73
+ )
74
+
61
75
  /**
62
- * Estimate trading fees.
76
+ * Estimated trading fee in USD: `sizeUsd × feeRate`.
63
77
  *
64
- * @param sizeUsd - Position size in USD (notional value)
65
- * @param feeRate - Fee rate as decimal (e.g., 0.00035 for 0.035%)
66
- * @returns Estimated fee in USD
78
+ * @param feeRate - Fee rate as a fraction (`'0.00035'` for 0.035%).
79
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
67
80
  * @public
68
81
  */
69
- export function estimateFees(sizeUsd: number, feeRate: number): number {
70
- if (!areFinite(sizeUsd, feeRate)) {
71
- return Number.NaN
72
- }
73
- return new DivBig(sizeUsd).times(feeRate).toNumber()
82
+ export function estimateFees(sizeUsd: string, feeRate: string): string {
83
+ return bigToDecimalString(
84
+ decimalStringToBig(sizeUsd).times(decimalStringToBig(feeRate))
85
+ )
74
86
  }
75
87
 
88
+ /** @public */
89
+ export const safeEstimateFees = createSafeFunction('estimateFees', estimateFees)
90
+
76
91
  /**
77
- * Apply slippage to a price for order execution.
92
+ * Apply slippage to an order-entry price with exact decimal math: a buy
93
+ * multiplies by `1 + slippagePercent / 100`, a sell divides by it. The result
94
+ * is not rounded; snap it to the market tick before it goes to a venue.
78
95
  *
79
- * @param price - Base price
80
- * @param slippagePercent - Slippage tolerance as percentage (e.g., 0.5 for 0.5%)
81
- * @param isBuy - True if buying (price goes up), false if selling (price goes down)
82
- * @returns Price adjusted for slippage
96
+ * @param slippagePercent - Slippage tolerance as a percentage (`'0.5'` is 0.5%).
97
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal
98
+ * string, or `slippagePercent` is -100 or less.
83
99
  * @public
84
100
  */
85
- export function applySlippage(
86
- price: number,
87
- slippagePercent: number,
101
+ export function applySlippageToPrice(
102
+ price: string,
103
+ slippagePercent: string,
88
104
  isBuy: boolean
89
- ): number {
90
- if (!areFinite(price, slippagePercent)) {
91
- return Number.NaN
92
- }
93
- const multiplier = new DivBig(slippagePercent).div(100).plus(1)
94
- if (isBuy) {
95
- return new DivBig(price).times(multiplier).toNumber()
96
- }
97
- if (multiplier.eq(0)) {
98
- return price / multiplier.toNumber()
105
+ ): string {
106
+ const base = decimalStringToDivBig(price)
107
+ const slippage = decimalStringToBig(slippagePercent)
108
+ if (slippage.lte(-100)) {
109
+ throw new PerpsError(
110
+ PerpsErrorCode.ValidationError,
111
+ `Invalid \`slippagePercent\`: ${slippagePercent}.`
112
+ )
99
113
  }
100
- return new DivBig(price).div(multiplier).toNumber()
114
+ const multiplier = new DivBig(slippage).div(100).plus(1)
115
+ return bigToDecimalString(
116
+ isBuy ? base.times(multiplier) : base.div(multiplier)
117
+ )
101
118
  }
102
119
 
120
+ /** @public */
121
+ export const safeApplySlippageToPrice = createSafeFunction(
122
+ 'applySlippageToPrice',
123
+ applySlippageToPrice
124
+ )
125
+
103
126
  /**
104
127
  * Signed expected PnL for a trigger price — see {@link calculateExpectedPnl}.
105
128
  *
@@ -107,131 +130,154 @@ export function applySlippage(
107
130
  */
108
131
  export interface ExpectedPnl {
109
132
  /** Signed expected profit/loss in USD. */
110
- amount: number
111
- /** Signed expected return as a percentage (10 means 10%). */
112
- percent: number
133
+ amount: string
134
+ /** Signed expected return as a percentage (`'10'` means 10%). */
135
+ percent: string
113
136
  }
114
137
 
115
138
  /**
116
- * Calculate expected gain/loss for a TP or SL trigger price.
117
- * Returns signed values — positive means profit, negative means loss.
139
+ * Expected gain or loss for a TP or SL trigger price. Positive is a profit,
140
+ * negative a loss.
118
141
  *
119
- * @param triggerPrice - The TP or SL target price
120
- * @param entryPrice - Position entry / current market price
121
- * @param leverage - Position leverage multiplier
122
- * @param isLong - True for long positions, false for short
123
- * @param margin - Margin amount in USD
142
+ * @param entryPrice - Position entry or current market price.
143
+ * @param margin - Margin amount in USD.
144
+ * @returns The expected PnL, or `null` when `triggerPrice`, `entryPrice` or
145
+ * `margin` is zero.
146
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
124
147
  * @public
125
148
  */
126
149
  export function calculateExpectedPnl(
127
- triggerPrice: number,
128
- entryPrice: number,
129
- leverage: number,
150
+ triggerPrice: string,
151
+ entryPrice: string,
152
+ leverage: string,
130
153
  isLong: boolean,
131
- margin: number
154
+ margin: string
132
155
  ): ExpectedPnl | null {
133
- if (!triggerPrice || entryPrice === 0 || margin === 0) {
156
+ const trigger = decimalStringToDivBig(triggerPrice)
157
+ const entry = decimalStringToBig(entryPrice)
158
+ const leverageBig = decimalStringToBig(leverage)
159
+ const marginBig = decimalStringToBig(margin)
160
+ if (trigger.eq(0) || entry.eq(0) || marginBig.eq(0)) {
134
161
  return null
135
162
  }
136
- if (!areFinite(triggerPrice, entryPrice, leverage, margin)) {
137
- return { amount: Number.NaN, percent: Number.NaN }
138
- }
139
163
  const priceDiff = isLong
140
- ? new DivBig(triggerPrice).minus(entryPrice)
141
- : new DivBig(entryPrice).minus(triggerPrice)
142
- const percent = priceDiff.div(entryPrice).times(leverage).times(100)
143
- const amount = percent.times(margin).div(100)
144
- return { amount: amount.toNumber(), percent: percent.toNumber() }
164
+ ? trigger.minus(entry)
165
+ : trigger.times(-1).plus(entry)
166
+ const percent = priceDiff.div(entry).times(leverageBig).times(100)
167
+ const amount = percent.times(marginBig).div(100)
168
+ return {
169
+ amount: bigToDecimalString(amount),
170
+ percent: bigToDecimalString(percent),
171
+ }
145
172
  }
146
173
 
174
+ /** @public */
175
+ export const safeCalculateExpectedPnl = createSafeFunction(
176
+ 'calculateExpectedPnl',
177
+ calculateExpectedPnl
178
+ )
179
+
147
180
  /**
148
- * Calculate the trigger price that realises a percentage gain/loss.
181
+ * Trigger price that realises a percentage gain or loss. A zero `entryPrice`
182
+ * or `leverage` gives `'0'`.
149
183
  *
150
- * @param percent - Target gain/loss percentage (positive = profitable direction)
151
- * @param entryPrice - Position entry price
152
- * @param leverage - Position leverage multiplier
153
- * @param isLong - True for long positions, false for short
184
+ * @param percent - Target gain or loss percentage (positive = profitable
185
+ * direction).
186
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
154
187
  * @public
155
188
  */
156
189
  export function calculateTriggerPrice(
157
- percent: number,
158
- entryPrice: number,
159
- leverage: number,
190
+ percent: string,
191
+ entryPrice: string,
192
+ leverage: string,
160
193
  isLong: boolean
161
- ): number {
162
- if (entryPrice === 0 || leverage === 0) {
163
- return 0
194
+ ): string {
195
+ const percentBig = decimalStringToDivBig(percent)
196
+ const entry = decimalStringToBig(entryPrice)
197
+ const leverageBig = decimalStringToBig(leverage)
198
+ if (entry.eq(0) || leverageBig.eq(0)) {
199
+ return '0'
164
200
  }
165
- if (!areFinite(percent, entryPrice, leverage)) {
166
- return Number.NaN
167
- }
168
- const priceDelta = new DivBig(percent)
169
- .times(entryPrice)
170
- .div(new DivBig(leverage).times(100))
171
- const entry = new DivBig(entryPrice)
172
- return (isLong ? entry.plus(priceDelta) : entry.minus(priceDelta)).toNumber()
201
+ const priceDelta = percentBig.times(entry).div(leverageBig.times(100))
202
+ return bigToDecimalString(
203
+ isLong ? entry.plus(priceDelta) : entry.minus(priceDelta)
204
+ )
173
205
  }
174
206
 
207
+ /** @public */
208
+ export const safeCalculateTriggerPrice = createSafeFunction(
209
+ 'calculateTriggerPrice',
210
+ calculateTriggerPrice
211
+ )
212
+
175
213
  /**
176
- * Calculate the percentage gain/loss a trigger price realises.
214
+ * Percentage gain or loss that a trigger price realises. A zero `entryPrice`
215
+ * or `leverage` gives `'0'`.
177
216
  *
178
- * @param price - Target price
179
- * @param entryPrice - Position entry price
180
- * @param leverage - Position leverage multiplier
181
- * @param isLong - True for long positions, false for short
217
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
182
218
  * @public
183
219
  */
184
220
  export function calculateTriggerPercent(
185
- price: number,
186
- entryPrice: number,
187
- leverage: number,
221
+ price: string,
222
+ entryPrice: string,
223
+ leverage: string,
188
224
  isLong: boolean
189
- ): number {
190
- if (entryPrice === 0 || leverage === 0) {
191
- return 0
192
- }
193
- if (!areFinite(price, entryPrice, leverage)) {
194
- return Number.NaN
225
+ ): string {
226
+ const priceBig = decimalStringToDivBig(price)
227
+ const entry = decimalStringToBig(entryPrice)
228
+ const leverageBig = decimalStringToBig(leverage)
229
+ if (entry.eq(0) || leverageBig.eq(0)) {
230
+ return '0'
195
231
  }
196
232
  const priceDiff = isLong
197
- ? new DivBig(price).minus(entryPrice)
198
- : new DivBig(entryPrice).minus(price)
199
- return priceDiff.div(entryPrice).times(leverage).times(100).toNumber()
233
+ ? priceBig.minus(entry)
234
+ : priceBig.times(-1).plus(entry)
235
+ return bigToDecimalString(priceDiff.div(entry).times(leverageBig).times(100))
200
236
  }
201
237
 
238
+ /** @public */
239
+ export const safeCalculateTriggerPercent = createSafeFunction(
240
+ 'calculateTriggerPercent',
241
+ calculateTriggerPercent
242
+ )
243
+
202
244
  /**
203
- * Calculate realized PnL as a percentage of position value at close.
245
+ * Realized PnL as a percentage of the position value at close:
246
+ * `realizedPnl ÷ (|size| × price) × 100`. A zero position value gives `'0'`.
204
247
  *
205
- * @param realizedPnl - The realized profit/loss in USD
206
- * @param size - Position size in asset units at close
207
- * @param price - Price at close
208
- * @returns PnL as a percentage of position value
248
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
209
249
  * @public
210
250
  */
211
251
  export function calculateRealizedPnlPercent(
212
- realizedPnl: number,
213
- size: number,
214
- price: number
215
- ): number {
216
- if (!areFinite(realizedPnl, size, price)) {
217
- return Number.NaN
218
- }
219
- const positionValue = new DivBig(size).abs().times(price)
252
+ realizedPnl: string,
253
+ size: string,
254
+ price: string
255
+ ): string {
256
+ const pnl = decimalStringToDivBig(realizedPnl)
257
+ const positionValue = decimalStringToBig(size)
258
+ .abs()
259
+ .times(decimalStringToBig(price))
220
260
  if (positionValue.eq(0)) {
221
- return 0
261
+ return '0'
222
262
  }
223
- return new DivBig(realizedPnl).div(positionValue).times(100).toNumber()
263
+ return bigToDecimalString(pnl.div(positionValue).times(100))
224
264
  }
225
265
 
266
+ /** @public */
267
+ export const safeCalculateRealizedPnlPercent = createSafeFunction(
268
+ 'calculateRealizedPnlPercent',
269
+ calculateRealizedPnlPercent
270
+ )
271
+
226
272
  /** Result of {@link walkOrderbook} — the fill obtained for a USD-notional walk. */
227
273
  interface BookWalk {
228
274
  /** Base amount filled. */
229
- baseSize: number
275
+ baseSize: string
230
276
  /** Notional actually filled in USD (equals the requested size unless the book ran dry). */
231
- filledNotional: number
232
- /** Volume-weighted average fill price, or 0 when the book is empty. */
233
- vwap: number
234
- /** True when the levels could not absorb the requested notional, and for a non-finite notional. */
277
+ filledNotional: string
278
+ /** Volume-weighted average fill price, or `'0'` when the book is empty. */
279
+ vwap: string
280
+ /** True when the levels could not absorb the requested notional. */
235
281
  insufficientLiquidity: boolean
236
282
  }
237
283
 
@@ -241,34 +287,25 @@ interface BookWalk {
241
287
  * in array order — the caller passes asks for a buy and bids for a sell, each
242
288
  * already ordered best-price-first. When the book cannot absorb the full
243
289
  * notional, the walk stops at the last level and flags `insufficientLiquidity`,
244
- * returning the best obtainable fill. A non-finite `sizeUsd` gives NaN fill
245
- * fields with `insufficientLiquidity` set.
290
+ * returning the best obtainable fill.
246
291
  *
247
- * @throws {PerpsError} `ValidationError` when a level's `price` or `size`
248
- * does not parse to a finite number.
292
+ * @throws {PerpsError} `ValidationError` when `sizeUsd` or a level's `price`
293
+ * or `size` is not a decimal string.
249
294
  * @public
250
295
  */
251
296
  export function walkOrderbook(
252
297
  levels: OrderbookLevel[],
253
- sizeUsd: number
298
+ sizeUsd: string
254
299
  ): BookWalk {
255
- if (!Number.isFinite(sizeUsd)) {
256
- return {
257
- baseSize: Number.NaN,
258
- filledNotional: Number.NaN,
259
- vwap: Number.NaN,
260
- insufficientLiquidity: true,
261
- }
262
- }
263
- let remaining = new DivBig(sizeUsd)
300
+ let remaining = decimalStringToDivBig(sizeUsd)
264
301
  let baseSize = new DivBig(0)
265
302
  let filledNotional = new DivBig(0)
266
303
  for (const level of levels) {
267
304
  if (remaining.lte(0)) {
268
305
  break
269
306
  }
270
- const { price, size } = parseLevel(level)
271
- const levelNotional = size.times(price)
307
+ const price = decimalStringToDivBig(level.price)
308
+ const levelNotional = decimalStringToBig(level.size).times(price)
272
309
  const take = remaining.lt(levelNotional) ? remaining : levelNotional
273
310
  if (take.eq(0)) {
274
311
  continue
@@ -278,23 +315,20 @@ export function walkOrderbook(
278
315
  remaining = remaining.minus(take)
279
316
  }
280
317
  return {
281
- baseSize: baseSize.toNumber(),
282
- filledNotional: filledNotional.toNumber(),
283
- vwap: baseSize.eq(0) ? 0 : filledNotional.div(baseSize).toNumber(),
318
+ baseSize: bigToDecimalString(baseSize),
319
+ filledNotional: bigToDecimalString(filledNotional),
320
+ vwap: baseSize.eq(0)
321
+ ? '0'
322
+ : bigToDecimalString(filledNotional.div(baseSize)),
284
323
  insufficientLiquidity: remaining.gt(0),
285
324
  }
286
325
  }
287
326
 
288
- function parseLevel(level: OrderbookLevel): { price: Big; size: Big } {
289
- try {
290
- return { price: new DivBig(level.price), size: new DivBig(level.size) }
291
- } catch {
292
- throw new PerpsError(
293
- PerpsErrorCode.ValidationError,
294
- `Malformed orderbook level: price='${level.price}', size='${level.size}'`
295
- )
296
- }
297
- }
327
+ /** @public */
328
+ export const safeWalkOrderbook = createSafeFunction(
329
+ 'walkOrderbook',
330
+ walkOrderbook
331
+ )
298
332
 
299
333
  /** Inputs to {@link buildQuote} — the resolved market, its live price, its book, and the trade ask. */
300
334
  interface BuildQuoteInput {
@@ -302,7 +336,7 @@ interface BuildQuoteInput {
302
336
  symbol: string
303
337
  type: TradeType
304
338
  side: QuoteSide
305
- sizeUsd: number
339
+ sizeUsd: string
306
340
  market: Market
307
341
  price: MarketContext
308
342
  bids: OrderbookLevel[]
@@ -319,44 +353,45 @@ interface BuildQuoteInput {
319
353
  * versus mark, applies the base taker fee on the filled notional, and carries
320
354
  * the market's `funding` (`null` for spot, which has none).
321
355
  *
322
- * @throws {PerpsError} `ValidationError` when a book level does not parse to
323
- * a finite number — see {@link walkOrderbook} — or when a quoted figure is
324
- * not finite (a non-finite `sizeUsd`, mark price or taker fee).
356
+ * @throws {PerpsError} `ValidationError` when `sizeUsd`, the mark price, the
357
+ * taker fee or a book level is not a decimal string.
325
358
  * @public
326
359
  */
327
360
  export function buildQuote(input: BuildQuoteInput): Quote {
328
361
  const { market, price, side, sizeUsd, feeTier } = input
329
- const markPrice = Number.parseFloat(price.markPrice)
362
+ const markPrice = decimalStringToDivBig(price.markPrice)
330
363
  const levels = side === 'buy' ? input.asks : input.bids
331
364
  const walk = walkOrderbook(levels, sizeUsd)
365
+ const vwap = decimalStringToBig(walk.vwap)
332
366
  const priceImpactBps =
333
- markPrice === 0 || walk.vwap === 0
334
- ? 0
335
- : Math.abs((walk.vwap - markPrice) / markPrice) * 10_000
336
- const feeUsd = estimateFees(
337
- walk.filledNotional,
338
- Number.parseFloat(feeTier.taker)
339
- )
367
+ markPrice.eq(0) || vwap.eq(0)
368
+ ? '0'
369
+ : bigToDecimalString(
370
+ vwap.minus(markPrice).div(markPrice).abs().times(10_000)
371
+ )
340
372
  return {
341
373
  provider: input.provider,
342
374
  symbol: input.symbol,
343
375
  marketId: market.id,
344
376
  type: input.type,
345
377
  side,
346
- sizeUsd: numberToDecimalString(sizeUsd),
347
- baseSize: numberToDecimalString(walk.baseSize),
378
+ sizeUsd: bigToDecimalString(decimalStringToBig(sizeUsd)),
379
+ baseSize: walk.baseSize,
348
380
  markPrice: price.markPrice,
349
- expectedFillPrice: numberToDecimalString(walk.vwap),
350
- priceImpactBps: numberToDecimalString(priceImpactBps),
381
+ expectedFillPrice: walk.vwap,
382
+ priceImpactBps,
351
383
  feeTier,
352
384
  isDefaultFeeTier: true,
353
- feeUsd: numberToDecimalString(feeUsd),
385
+ feeUsd: estimateFees(walk.filledNotional, feeTier.taker),
354
386
  funding: price.funding ?? null,
355
387
  insufficientLiquidity: walk.insufficientLiquidity,
356
388
  timestamp: input.timestamp,
357
389
  }
358
390
  }
359
391
 
392
+ /** @public */
393
+ export const safeBuildQuote = createSafeFunction('buildQuote', buildQuote)
394
+
360
395
  /**
361
396
  * Pick the matching open position for an order's market, if any.
362
397
  *
@@ -379,18 +414,28 @@ export function findMatchingPosition(
379
414
  * position can only close what's open.
380
415
  *
381
416
  * Inputs are non-negative magnitudes.
417
+ *
418
+ * @throws {PerpsError} `ValidationError` when an input is not a decimal string.
382
419
  * @public
383
420
  */
384
421
  export function resolveCloseSize(
385
- orderSize: number,
386
- positionSize: number
387
- ): number {
388
- if (orderSize === 0) {
389
- return positionSize
422
+ orderSize: string,
423
+ positionSize: string
424
+ ): string {
425
+ const order = decimalStringToBig(orderSize)
426
+ const position = decimalStringToBig(positionSize)
427
+ if (order.eq(0)) {
428
+ return bigToDecimalString(position)
390
429
  }
391
- return Math.min(orderSize, positionSize)
430
+ return bigToDecimalString(order.lt(position) ? order : position)
392
431
  }
393
432
 
433
+ /** @public */
434
+ export const safeResolveCloseSize = createSafeFunction(
435
+ 'resolveCloseSize',
436
+ resolveCloseSize
437
+ )
438
+
394
439
  /**
395
440
  * Expected rPnL for a resting limit order against a matching position.
396
441
  *
@@ -403,7 +448,7 @@ export function resolveCloseSize(
403
448
  function regularOrderRealizedPnl(
404
449
  order: RegularOrder,
405
450
  position: Position | undefined
406
- ): number | null {
451
+ ): string | null {
407
452
  if (!position || !isActiveOrderStatus(order.status)) {
408
453
  return null
409
454
  }
@@ -416,32 +461,29 @@ function regularOrderRealizedPnl(
416
461
  return null
417
462
  }
418
463
 
419
- const limitPrice = Number.parseFloat(order.price ?? '')
420
- const entryPrice = Number.parseFloat(position.entryPrice)
421
- const orderSize = Math.abs(Number.parseFloat(order.remainingSize))
422
- const positionSize = Math.abs(Number.parseFloat(position.size))
423
- if (
424
- !Number.isFinite(limitPrice) ||
425
- !Number.isFinite(entryPrice) ||
426
- !Number.isFinite(orderSize) ||
427
- !Number.isFinite(positionSize) ||
428
- positionSize <= 0
429
- ) {
464
+ if (order.price === undefined) {
465
+ return null
466
+ }
467
+ const orderSize = decimalStringToBig(order.remainingSize).abs()
468
+ const positionSize = decimalStringToBig(position.size).abs()
469
+ if (positionSize.lte(0)) {
430
470
  return null
431
471
  }
432
472
 
433
473
  // `resolveCloseSize` reads a zero size as "close the whole position", a
434
474
  // convention that belongs to an order's submitted size. `remainingSize` is
435
475
  // the unfilled quantity, so zero means nothing is left to fill.
436
- if (orderSize === 0) {
476
+ if (orderSize.eq(0)) {
437
477
  return null
438
478
  }
439
479
 
440
- const closeSize = resolveCloseSize(orderSize, positionSize)
441
480
  return calculateRealizedPnl({
442
- entryPrice,
443
- closePrice: limitPrice,
444
- closeSize,
481
+ entryPrice: position.entryPrice,
482
+ closePrice: order.price,
483
+ closeSize: resolveCloseSize(
484
+ bigToDecimalString(orderSize),
485
+ bigToDecimalString(positionSize)
486
+ ),
445
487
  isLong,
446
488
  })
447
489
  }
@@ -450,9 +492,10 @@ function regularOrderRealizedPnl(
450
492
  function triggerOrderRealizedPnl(
451
493
  order: TriggerOrder,
452
494
  position: Position | undefined
453
- ): number | null {
495
+ ): string | null {
454
496
  if (
455
497
  !position ||
498
+ order.triggerPrice === undefined ||
456
499
  !isActiveOrderStatus(order.status) ||
457
500
  order.status === OrderStatus.PENDING
458
501
  ) {
@@ -466,31 +509,25 @@ function triggerOrderRealizedPnl(
466
509
  ) {
467
510
  return null
468
511
  }
469
- const triggerPrice = Number.parseFloat(order.triggerPrice)
470
- const entryPrice = Number.parseFloat(position.entryPrice)
471
- const orderSize = Math.abs(Number.parseFloat(order.remainingSize))
472
- const positionSize = Math.abs(Number.parseFloat(position.size))
473
- if (
474
- !Number.isFinite(triggerPrice) ||
475
- !Number.isFinite(entryPrice) ||
476
- !Number.isFinite(orderSize) ||
477
- !Number.isFinite(positionSize) ||
478
- positionSize <= 0
479
- ) {
512
+ const orderSize = decimalStringToBig(order.remainingSize).abs()
513
+ const positionSize = decimalStringToBig(position.size).abs()
514
+ if (positionSize.lte(0)) {
480
515
  return null
481
516
  }
482
517
  if (
483
- orderSize === 0 &&
484
- (Number.parseFloat(order.originalSize) !== 0 || !order.reduceOnly)
518
+ orderSize.eq(0) &&
519
+ (!decimalStringToBig(order.originalSize).eq(0) || !order.reduceOnly)
485
520
  ) {
486
521
  return null
487
522
  }
488
523
 
489
- const closeSize = resolveCloseSize(orderSize, positionSize)
490
524
  return calculateRealizedPnl({
491
- entryPrice,
492
- closePrice: triggerPrice,
493
- closeSize,
525
+ entryPrice: position.entryPrice,
526
+ closePrice: order.triggerPrice,
527
+ closeSize: resolveCloseSize(
528
+ bigToDecimalString(orderSize),
529
+ bigToDecimalString(positionSize)
530
+ ),
494
531
  isLong,
495
532
  })
496
533
  }
@@ -503,12 +540,14 @@ function triggerOrderRealizedPnl(
503
540
  *
504
541
  * @returns Realised PnL if the order would reduce the position, otherwise
505
542
  * `null`.
543
+ * @throws {PerpsError} `ValidationError` when a price or size on the order or
544
+ * the position is not a decimal string.
506
545
  * @public
507
546
  */
508
547
  export function estimateRealizedPnl(
509
548
  order: Order,
510
549
  position: Position | undefined
511
- ): number | null {
550
+ ): string | null {
512
551
  if (isTriggerOrder(order)) {
513
552
  return triggerOrderRealizedPnl(order, position)
514
553
  }
@@ -517,3 +556,9 @@ export function estimateRealizedPnl(
517
556
  }
518
557
  return null
519
558
  }
559
+
560
+ /** @public */
561
+ export const safeEstimateRealizedPnl = createSafeFunction(
562
+ 'estimateRealizedPnl',
563
+ estimateRealizedPnl
564
+ )