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