@drift-labs/sdk 0.1.2 → 0.1.8
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 +9 -6
- package/lib/admin.d.ts +1 -1
- package/lib/admin.js +2 -2
- package/lib/clearingHouseUser.d.ts +1 -1
- package/lib/clearingHouseUser.js +19 -2
- package/lib/config.js +1 -1
- package/lib/constants/markets.js +14 -14
- package/lib/idl/clearing_house.json +33 -28
- package/lib/math/funding.d.ts +24 -1
- package/lib/math/funding.js +108 -12
- package/lib/math/trade.d.ts +3 -2
- package/lib/math/trade.js +5 -4
- package/lib/types.d.ts +1 -0
- package/package.json +6 -3
- package/src/admin.ts +4 -4
- package/src/clearingHouse.ts +12 -12
- package/src/clearingHouseUser.ts +23 -3
- package/src/config.ts +1 -2
- package/src/constants/markets.ts +14 -14
- package/src/idl/clearing_house.json +33 -28
- package/src/math/funding.ts +127 -14
- package/src/math/position.ts +3 -3
- package/src/math/trade.ts +12 -7
- package/src/types.ts +1 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
<p>
|
|
7
7
|
<a href="https://www.npmjs.com/package/@drift-labs/sdk"><img alt="SDK npm package" src="https://img.shields.io/npm/v/@drift-labs/sdk" /></a>
|
|
8
|
-
<a href="https://
|
|
8
|
+
<a href="https://drift-labs.github.io/protocol-v1/sdk/"><img alt="Docs" src="https://img.shields.io/badge/docs-tutorials-blueviolet" /></a>
|
|
9
9
|
<a href="https://discord.com/channels/849494028176588802/878700556904980500"><img alt="Discord Chat" src="https://img.shields.io/discord/889577356681945098?color=blueviolet" /></a>
|
|
10
10
|
<a href="https://opensource.org/licenses/Apache-2.0"><img alt="License" src="https://img.shields.io/github/license/project-serum/anchor?color=blueviolet" /></a>
|
|
11
11
|
</p>
|
|
@@ -17,7 +17,7 @@ This repository provides open source access to Drift's Typescript SDK, Solana Pr
|
|
|
17
17
|
|
|
18
18
|
# SDK Guide
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
The technical documentation for the SDK can be found [here](https://drift-labs.github.io/protocol-v1/sdk/), and you can visit Drift's general purpose documentation [here](https://docs.drift.trade/drift-sdk-documentation).
|
|
21
21
|
|
|
22
22
|
## Installation
|
|
23
23
|
|
|
@@ -38,7 +38,7 @@ solana address
|
|
|
38
38
|
|
|
39
39
|
# Put the private key into your .env to be used by your bot
|
|
40
40
|
cd {projectLocation}
|
|
41
|
-
echo BOT_PRIVATE_KEY=`cat
|
|
41
|
+
echo BOT_PRIVATE_KEY=`cat ~/.config/solana/id.json` >> .env
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
## Concepts
|
|
@@ -64,16 +64,19 @@ The Drift SDK uses some common precisions, which are available as constants to i
|
|
|
64
64
|
|
|
65
65
|
**Important Note for BigNum division**
|
|
66
66
|
|
|
67
|
-
Because BN only supports integers, you need to be conscious of the numbers you are using when dividing. BN will
|
|
67
|
+
Because BN only supports integers, you need to be conscious of the numbers you are using when dividing. BN will return the floor when using the regular division function; if you want to get the exact divison, you need to add the modulus of the two numbers as well. There is a helper function `convertToNumber` in the SDK which will do this for you.
|
|
68
68
|
|
|
69
|
-
```
|
|
70
|
-
|
|
69
|
+
```typescript
|
|
70
|
+
import {convertToNumber} from @drift-labs/sdk
|
|
71
71
|
|
|
72
72
|
// Gets the floor value
|
|
73
73
|
new BN(10500).div(new BN(1000)).toNumber(); // = 10
|
|
74
74
|
|
|
75
75
|
// Gets the exact value
|
|
76
76
|
new BN(10500).div(new BN(1000)).toNumber() + BN(10500).mod(new BN(1000)).toNumber(); // = 10.5
|
|
77
|
+
|
|
78
|
+
// Also gets the exact value
|
|
79
|
+
convertToNumber(new BN(10500), new BN(1000)); // = 10.5
|
|
77
80
|
```
|
|
78
81
|
|
|
79
82
|
## Examples
|
package/lib/admin.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ClearingHouse } from './clearingHouse';
|
|
|
6
6
|
export declare class Admin extends ClearingHouse {
|
|
7
7
|
static from(connection: Connection, wallet: IWallet, clearingHouseProgramId: PublicKey, opts?: ConfirmOptions): Admin;
|
|
8
8
|
initialize(usdcMint: PublicKey, adminControlsPrices: boolean): Promise<[TransactionSignature, TransactionSignature]>;
|
|
9
|
-
initializeMarket(marketIndex: BN, priceOracle: PublicKey,
|
|
9
|
+
initializeMarket(marketIndex: BN, priceOracle: PublicKey, baseAssetReserve: BN, quoteAssetReserve: BN, periodicity: BN, pegMultiplier?: BN): Promise<TransactionSignature>;
|
|
10
10
|
moveAmmPrice(baseAssetReserve: BN, quoteAssetReserve: BN, marketIndex: BN): Promise<TransactionSignature>;
|
|
11
11
|
updateK(sqrtK: BN, marketIndex: BN): Promise<TransactionSignature>;
|
|
12
12
|
moveAmmToPrice(marketIndex: BN, targetPrice: BN): Promise<TransactionSignature>;
|
package/lib/admin.js
CHANGED
|
@@ -122,12 +122,12 @@ class Admin extends clearingHouse_1.ClearingHouse {
|
|
|
122
122
|
return [initializeTxSig, initializeHistoryTxSig];
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
|
-
initializeMarket(marketIndex, priceOracle,
|
|
125
|
+
initializeMarket(marketIndex, priceOracle, baseAssetReserve, quoteAssetReserve, periodicity, pegMultiplier = numericConstants_1.PEG_PRECISION) {
|
|
126
126
|
return __awaiter(this, void 0, void 0, function* () {
|
|
127
127
|
if (this.getMarketsAccount().markets[marketIndex.toNumber()].initialized) {
|
|
128
128
|
throw Error(`MarketIndex ${marketIndex.toNumber()} already initialized`);
|
|
129
129
|
}
|
|
130
|
-
const initializeMarketTx = yield this.program.transaction.initializeMarket(marketIndex,
|
|
130
|
+
const initializeMarketTx = yield this.program.transaction.initializeMarket(marketIndex, baseAssetReserve, quoteAssetReserve, periodicity, pegMultiplier, {
|
|
131
131
|
accounts: {
|
|
132
132
|
state: yield this.getStatePublicKey(),
|
|
133
133
|
admin: this.wallet.publicKey,
|
|
@@ -72,7 +72,7 @@ export declare class ClearingHouseUser {
|
|
|
72
72
|
* calculates average exit price for closing 100% of position
|
|
73
73
|
* @returns : Precision MARK_PRICE_PRECISION
|
|
74
74
|
*/
|
|
75
|
-
getPositionEstimatedExitPrice(position: UserPosition): BN;
|
|
75
|
+
getPositionEstimatedExitPrice(position: UserPosition, amountToClose?: BN): BN;
|
|
76
76
|
/**
|
|
77
77
|
* calculates current user leverage across all positions
|
|
78
78
|
* @returns : Precision TEN_THOUSAND
|
package/lib/clearingHouseUser.js
CHANGED
|
@@ -60,7 +60,13 @@ class ClearingHouseUser {
|
|
|
60
60
|
* @returns userPosition
|
|
61
61
|
*/
|
|
62
62
|
getUserPosition(marketIndex) {
|
|
63
|
-
|
|
63
|
+
var _a;
|
|
64
|
+
return ((_a = this.getUserPositionsAccount().positions.find((position) => position.marketIndex.eq(marketIndex))) !== null && _a !== void 0 ? _a : {
|
|
65
|
+
baseAssetAmount: numericConstants_1.ZERO,
|
|
66
|
+
lastCumulativeFundingRate: numericConstants_1.ZERO,
|
|
67
|
+
marketIndex,
|
|
68
|
+
quoteAssetAmount: numericConstants_1.ZERO,
|
|
69
|
+
});
|
|
64
70
|
}
|
|
65
71
|
getUserAccountPublicKey() {
|
|
66
72
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -158,8 +164,19 @@ class ClearingHouseUser {
|
|
|
158
164
|
* calculates average exit price for closing 100% of position
|
|
159
165
|
* @returns : Precision MARK_PRICE_PRECISION
|
|
160
166
|
*/
|
|
161
|
-
getPositionEstimatedExitPrice(position) {
|
|
167
|
+
getPositionEstimatedExitPrice(position, amountToClose) {
|
|
162
168
|
const market = this.clearingHouse.getMarket(position.marketIndex);
|
|
169
|
+
if (amountToClose) {
|
|
170
|
+
if (amountToClose.eq(numericConstants_1.ZERO)) {
|
|
171
|
+
return _1.calculateMarkPrice(market);
|
|
172
|
+
}
|
|
173
|
+
position = {
|
|
174
|
+
baseAssetAmount: amountToClose,
|
|
175
|
+
lastCumulativeFundingRate: position.lastCumulativeFundingRate,
|
|
176
|
+
marketIndex: position.marketIndex,
|
|
177
|
+
quoteAssetAmount: position.quoteAssetAmount,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
163
180
|
const baseAssetValue = _1.calculateBaseAssetValue(market, position);
|
|
164
181
|
if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
|
|
165
182
|
return numericConstants_1.ZERO;
|
package/lib/config.js
CHANGED
|
@@ -5,7 +5,7 @@ exports.configs = {
|
|
|
5
5
|
devnet: {
|
|
6
6
|
ENV: 'devnet',
|
|
7
7
|
PYTH_ORACLE_MAPPING_ADDRESS: 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2',
|
|
8
|
-
CLEARING_HOUSE_PROGRAM_ID: '
|
|
8
|
+
CLEARING_HOUSE_PROGRAM_ID: 'AsW7LnXB9UA1uec9wi9MctYTgTz7YH9snhxd16GsFaGX',
|
|
9
9
|
USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
|
|
10
10
|
},
|
|
11
11
|
'mainnet-beta': {
|
package/lib/constants/markets.js
CHANGED
|
@@ -13,20 +13,20 @@ exports.Markets = [
|
|
|
13
13
|
devnetPythOracle: 'J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix',
|
|
14
14
|
mainnetPythOracle: 'H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG',
|
|
15
15
|
},
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
{
|
|
17
|
+
symbol: 'BTC-PERP',
|
|
18
|
+
baseAssetSymbol: 'BTC',
|
|
19
|
+
marketIndex: new bn_js_1.default(1),
|
|
20
|
+
devnetPythOracle: 'HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J',
|
|
21
|
+
mainnetPythOracle: 'GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
symbol: 'ETH-PERP',
|
|
25
|
+
baseAssetSymbol: 'ETH',
|
|
26
|
+
marketIndex: new bn_js_1.default(2),
|
|
27
|
+
devnetPythOracle: 'EdVCmQ9FSPcVe5YySXDPCRmc8aDQLKJ9xvYBMZPie1Vw',
|
|
28
|
+
mainnetPythOracle: 'JBu1AL4obBcCMqKBBxhpWCNUt136ijcuMZLFvTP7iWdB',
|
|
29
|
+
},
|
|
30
30
|
// {
|
|
31
31
|
// symbol: 'COPE-PERP',
|
|
32
32
|
// baseAssetSymbol: 'COPE',
|
|
@@ -1724,6 +1724,34 @@
|
|
|
1724
1724
|
}
|
|
1725
1725
|
],
|
|
1726
1726
|
"types": [
|
|
1727
|
+
{
|
|
1728
|
+
"name": "InitializeUserOptionalAccounts",
|
|
1729
|
+
"type": {
|
|
1730
|
+
"kind": "struct",
|
|
1731
|
+
"fields": [
|
|
1732
|
+
{
|
|
1733
|
+
"name": "whitelistToken",
|
|
1734
|
+
"type": "bool"
|
|
1735
|
+
}
|
|
1736
|
+
]
|
|
1737
|
+
}
|
|
1738
|
+
},
|
|
1739
|
+
{
|
|
1740
|
+
"name": "ManagePositionOptionalAccounts",
|
|
1741
|
+
"type": {
|
|
1742
|
+
"kind": "struct",
|
|
1743
|
+
"fields": [
|
|
1744
|
+
{
|
|
1745
|
+
"name": "discountToken",
|
|
1746
|
+
"type": "bool"
|
|
1747
|
+
},
|
|
1748
|
+
{
|
|
1749
|
+
"name": "referrer",
|
|
1750
|
+
"type": "bool"
|
|
1751
|
+
}
|
|
1752
|
+
]
|
|
1753
|
+
}
|
|
1754
|
+
},
|
|
1727
1755
|
{
|
|
1728
1756
|
"name": "CurveRecord",
|
|
1729
1757
|
"type": {
|
|
@@ -1938,34 +1966,6 @@
|
|
|
1938
1966
|
]
|
|
1939
1967
|
}
|
|
1940
1968
|
},
|
|
1941
|
-
{
|
|
1942
|
-
"name": "InitializeUserOptionalAccounts",
|
|
1943
|
-
"type": {
|
|
1944
|
-
"kind": "struct",
|
|
1945
|
-
"fields": [
|
|
1946
|
-
{
|
|
1947
|
-
"name": "whitelistToken",
|
|
1948
|
-
"type": "bool"
|
|
1949
|
-
}
|
|
1950
|
-
]
|
|
1951
|
-
}
|
|
1952
|
-
},
|
|
1953
|
-
{
|
|
1954
|
-
"name": "ManagePositionOptionalAccounts",
|
|
1955
|
-
"type": {
|
|
1956
|
-
"kind": "struct",
|
|
1957
|
-
"fields": [
|
|
1958
|
-
{
|
|
1959
|
-
"name": "discountToken",
|
|
1960
|
-
"type": "bool"
|
|
1961
|
-
},
|
|
1962
|
-
{
|
|
1963
|
-
"name": "referrer",
|
|
1964
|
-
"type": "bool"
|
|
1965
|
-
}
|
|
1966
|
-
]
|
|
1967
|
-
}
|
|
1968
|
-
},
|
|
1969
1969
|
{
|
|
1970
1970
|
"name": "LiquidationRecord",
|
|
1971
1971
|
"type": {
|
|
@@ -2757,6 +2757,11 @@
|
|
|
2757
2757
|
"code": 337,
|
|
2758
2758
|
"name": "InvalidFundingProfitability",
|
|
2759
2759
|
"msg": "AMM funding out of bounds pnl"
|
|
2760
|
+
},
|
|
2761
|
+
{
|
|
2762
|
+
"code": 338,
|
|
2763
|
+
"name": "CastingFailure",
|
|
2764
|
+
"msg": "Casting Failure"
|
|
2760
2765
|
}
|
|
2761
2766
|
]
|
|
2762
2767
|
}
|
package/lib/math/funding.d.ts
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
import { BN } from '@project-serum/anchor';
|
|
3
3
|
import { PythClient } from '../pythClient';
|
|
4
4
|
import { Market } from '../types';
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @param market
|
|
8
|
+
* @param pythClient
|
|
9
|
+
* @param periodAdjustment
|
|
10
|
+
* @returns Estimated funding rate. : Precision //TODO-PRECISION
|
|
11
|
+
*/
|
|
12
|
+
export declare function calculateAllEstimatedFundingRate(market: Market, pythClient: PythClient, periodAdjustment?: BN): Promise<[BN, BN, BN]>;
|
|
5
13
|
/**
|
|
6
14
|
*
|
|
7
15
|
* @param market
|
|
@@ -10,4 +18,19 @@ import { Market } from '../types';
|
|
|
10
18
|
* @param estimationMethod
|
|
11
19
|
* @returns Estimated funding rate. : Precision //TODO-PRECISION
|
|
12
20
|
*/
|
|
13
|
-
export declare function calculateEstimatedFundingRate(market: Market, pythClient: PythClient, periodAdjustment: BN, estimationMethod: 'interpolated' | 'lowerbound'): Promise<BN>;
|
|
21
|
+
export declare function calculateEstimatedFundingRate(market: Market, pythClient: PythClient, periodAdjustment: BN, estimationMethod: 'interpolated' | 'lowerbound' | 'capped'): Promise<BN>;
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
* @param market
|
|
25
|
+
* @param pythClient
|
|
26
|
+
* @param periodAdjustment
|
|
27
|
+
* @param estimationMethod
|
|
28
|
+
* @returns Estimated funding rate. : Precision //TODO-PRECISION
|
|
29
|
+
*/
|
|
30
|
+
export declare function calculateLongShortFundingRate(market: Market, pythClient: PythClient, periodAdjustment?: BN): Promise<[BN, BN]>;
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @param market
|
|
34
|
+
* @returns Estimated fee pool size
|
|
35
|
+
*/
|
|
36
|
+
export declare function calculateFundingPool(market: Market): BN;
|
package/lib/math/funding.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.calculateEstimatedFundingRate = void 0;
|
|
12
|
+
exports.calculateFundingPool = exports.calculateLongShortFundingRate = exports.calculateEstimatedFundingRate = exports.calculateAllEstimatedFundingRate = void 0;
|
|
13
13
|
const anchor_1 = require("@project-serum/anchor");
|
|
14
14
|
const numericConstants_1 = require("../constants/numericConstants");
|
|
15
15
|
const market_1 = require("./market");
|
|
@@ -18,10 +18,9 @@ const market_1 = require("./market");
|
|
|
18
18
|
* @param market
|
|
19
19
|
* @param pythClient
|
|
20
20
|
* @param periodAdjustment
|
|
21
|
-
* @param estimationMethod
|
|
22
21
|
* @returns Estimated funding rate. : Precision //TODO-PRECISION
|
|
23
22
|
*/
|
|
24
|
-
function
|
|
23
|
+
function calculateAllEstimatedFundingRate(market, pythClient, periodAdjustment = new anchor_1.BN(1)) {
|
|
25
24
|
return __awaiter(this, void 0, void 0, function* () {
|
|
26
25
|
// periodAdjustment
|
|
27
26
|
// 1: hourly
|
|
@@ -30,7 +29,7 @@ function calculateEstimatedFundingRate(market, pythClient, periodAdjustment = ne
|
|
|
30
29
|
const secondsInHour = new anchor_1.BN(3600);
|
|
31
30
|
const hoursInDay = new anchor_1.BN(24);
|
|
32
31
|
if (!market.initialized) {
|
|
33
|
-
return new anchor_1.BN(0);
|
|
32
|
+
return [new anchor_1.BN(0), new anchor_1.BN(0), new anchor_1.BN(0)];
|
|
34
33
|
}
|
|
35
34
|
const payFreq = new anchor_1.BN(market.amm.fundingPeriod);
|
|
36
35
|
const oraclePriceData = yield pythClient.getPriceData(market.amm.oracle);
|
|
@@ -51,19 +50,116 @@ function calculateEstimatedFundingRate(market, pythClient, periodAdjustment = ne
|
|
|
51
50
|
.mul(numericConstants_1.MARK_PRICE_PRECISION)
|
|
52
51
|
.mul(new anchor_1.BN(100))
|
|
53
52
|
.div(oracleTwapWithMantissa);
|
|
53
|
+
const lowerboundEst = twapSpreadPct
|
|
54
|
+
.mul(payFreq)
|
|
55
|
+
.mul(anchor_1.BN.min(secondsInHour, timeSinceLastUpdate))
|
|
56
|
+
.mul(periodAdjustment)
|
|
57
|
+
.div(secondsInHour)
|
|
58
|
+
.div(secondsInHour)
|
|
59
|
+
.div(hoursInDay);
|
|
60
|
+
const interpEst = twapSpreadPct.mul(periodAdjustment).div(hoursInDay);
|
|
61
|
+
const interpRateQuote = twapSpreadPct.mul(periodAdjustment).div(hoursInDay)
|
|
62
|
+
.div(numericConstants_1.MARK_PRICE_PRECISION.div(numericConstants_1.QUOTE_PRECISION));
|
|
63
|
+
let feePoolSize = calculateFundingPool(market);
|
|
64
|
+
if (interpRateQuote.lt(new anchor_1.BN(0))) {
|
|
65
|
+
feePoolSize = feePoolSize.mul(new anchor_1.BN(-1));
|
|
66
|
+
}
|
|
67
|
+
let cappedAltEst;
|
|
68
|
+
let largerSide;
|
|
69
|
+
let smallerSide;
|
|
70
|
+
if (market.baseAssetAmountLong.gt(market.baseAssetAmountShort)) {
|
|
71
|
+
largerSide = market.baseAssetAmountLong.abs();
|
|
72
|
+
smallerSide = market.baseAssetAmountShort.abs();
|
|
73
|
+
if (twapSpread.gt(new anchor_1.BN(0))) {
|
|
74
|
+
return [lowerboundEst, interpEst, interpEst];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else if (market.baseAssetAmountLong.lt(market.baseAssetAmountShort)) {
|
|
78
|
+
largerSide = market.baseAssetAmountShort.abs();
|
|
79
|
+
smallerSide = market.baseAssetAmountLong.abs();
|
|
80
|
+
if (twapSpread.lt(new anchor_1.BN(0))) {
|
|
81
|
+
return [lowerboundEst, interpEst, interpEst];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
return [lowerboundEst, interpEst, interpEst];
|
|
86
|
+
}
|
|
87
|
+
if (largerSide.gt(numericConstants_1.ZERO)) {
|
|
88
|
+
cappedAltEst = smallerSide.mul(twapSpread).div(largerSide);
|
|
89
|
+
const feePoolTopOff = feePoolSize.mul(numericConstants_1.MARK_PRICE_PRECISION.div(numericConstants_1.QUOTE_PRECISION))
|
|
90
|
+
.mul(numericConstants_1.AMM_RESERVE_PRECISION).div(largerSide);
|
|
91
|
+
cappedAltEst = cappedAltEst.add(feePoolTopOff);
|
|
92
|
+
cappedAltEst = cappedAltEst.mul(numericConstants_1.MARK_PRICE_PRECISION)
|
|
93
|
+
.mul(new anchor_1.BN(100))
|
|
94
|
+
.div(oracleTwapWithMantissa)
|
|
95
|
+
.mul(periodAdjustment).div(hoursInDay);
|
|
96
|
+
if (cappedAltEst.abs().gt(interpEst.abs())) {
|
|
97
|
+
cappedAltEst = interpEst;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
cappedAltEst = interpEst;
|
|
102
|
+
}
|
|
103
|
+
return [lowerboundEst, cappedAltEst, interpEst];
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
exports.calculateAllEstimatedFundingRate = calculateAllEstimatedFundingRate;
|
|
107
|
+
/**
|
|
108
|
+
*
|
|
109
|
+
* @param market
|
|
110
|
+
* @param pythClient
|
|
111
|
+
* @param periodAdjustment
|
|
112
|
+
* @param estimationMethod
|
|
113
|
+
* @returns Estimated funding rate. : Precision //TODO-PRECISION
|
|
114
|
+
*/
|
|
115
|
+
function calculateEstimatedFundingRate(market, pythClient, periodAdjustment = new anchor_1.BN(1), estimationMethod) {
|
|
116
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
const [lowerboundEst, cappedAltEst, interpEst] = yield calculateAllEstimatedFundingRate(market, pythClient, periodAdjustment);
|
|
54
118
|
if (estimationMethod == 'lowerbound') {
|
|
55
119
|
//assuming remaining funding period has no gap
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.div(secondsInHour)
|
|
61
|
-
.div(secondsInHour)
|
|
62
|
-
.div(hoursInDay);
|
|
120
|
+
return lowerboundEst;
|
|
121
|
+
}
|
|
122
|
+
else if (estimationMethod == 'capped') {
|
|
123
|
+
return cappedAltEst;
|
|
63
124
|
}
|
|
64
125
|
else {
|
|
65
|
-
return
|
|
126
|
+
return interpEst;
|
|
66
127
|
}
|
|
67
128
|
});
|
|
68
129
|
}
|
|
69
130
|
exports.calculateEstimatedFundingRate = calculateEstimatedFundingRate;
|
|
131
|
+
/**
|
|
132
|
+
*
|
|
133
|
+
* @param market
|
|
134
|
+
* @param pythClient
|
|
135
|
+
* @param periodAdjustment
|
|
136
|
+
* @param estimationMethod
|
|
137
|
+
* @returns Estimated funding rate. : Precision //TODO-PRECISION
|
|
138
|
+
*/
|
|
139
|
+
function calculateLongShortFundingRate(market, pythClient, periodAdjustment = new anchor_1.BN(1)) {
|
|
140
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
141
|
+
const [_, cappedAltEst, interpEst] = yield calculateAllEstimatedFundingRate(market, pythClient, periodAdjustment);
|
|
142
|
+
if (market.baseAssetAmountLong.gt(market.baseAssetAmountShort)) {
|
|
143
|
+
return [cappedAltEst, interpEst];
|
|
144
|
+
}
|
|
145
|
+
else if (market.baseAssetAmountLong.lt(market.baseAssetAmountShort)) {
|
|
146
|
+
return [interpEst, cappedAltEst];
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
return [interpEst, interpEst];
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
exports.calculateLongShortFundingRate = calculateLongShortFundingRate;
|
|
154
|
+
/**
|
|
155
|
+
*
|
|
156
|
+
* @param market
|
|
157
|
+
* @returns Estimated fee pool size
|
|
158
|
+
*/
|
|
159
|
+
function calculateFundingPool(market) {
|
|
160
|
+
const totalFeeLB = market.amm.totalFee.div(new anchor_1.BN(2));
|
|
161
|
+
const feePool = market.amm.totalFeeMinusDistributions.sub(totalFeeLB);
|
|
162
|
+
// return new BN(QUOTE_PRECISION.mul(new BN(2400)));
|
|
163
|
+
return feePool;
|
|
164
|
+
}
|
|
165
|
+
exports.calculateFundingPool = calculateFundingPool;
|
package/lib/math/trade.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="bn.js" />
|
|
2
2
|
import { Market, PositionDirection } from '../types';
|
|
3
3
|
import { BN } from '@project-serum/anchor';
|
|
4
|
+
import { AssetType } from './amm';
|
|
4
5
|
export declare type PriceImpactUnit = 'entryPrice' | 'maxPrice' | 'priceDelta' | 'priceDeltaAsNumber' | 'pctAvg' | 'pctMax' | 'quoteAssetAmount' | 'quoteAssetAmountPeg' | 'acquiredBaseAssetAmount' | 'acquiredQuoteAssetAmount';
|
|
5
6
|
/**
|
|
6
7
|
* Calculates avg/max slippage (price impact) for candidate trade
|
|
@@ -17,7 +18,7 @@ export declare type PriceImpactUnit = 'entryPrice' | 'maxPrice' | 'priceDelta' |
|
|
|
17
18
|
*
|
|
18
19
|
* 'newPrice' => the price of the asset after the trade : Precision MARK_PRICE_PRECISION
|
|
19
20
|
*/
|
|
20
|
-
export declare function calculateTradeSlippage(direction: PositionDirection, amount: BN, market: Market): [BN, BN, BN, BN];
|
|
21
|
+
export declare function calculateTradeSlippage(direction: PositionDirection, amount: BN, market: Market, inputAssetType?: AssetType): [BN, BN, BN, BN];
|
|
21
22
|
/**
|
|
22
23
|
* Calculates acquired amounts for trade executed
|
|
23
24
|
* @param direction
|
|
@@ -27,7 +28,7 @@ export declare function calculateTradeSlippage(direction: PositionDirection, amo
|
|
|
27
28
|
* | 'acquiredBase' => positive/negative change in user's base : BN TODO-PRECISION
|
|
28
29
|
* | 'acquiredQuote' => positive/negative change in user's quote : BN TODO-PRECISION
|
|
29
30
|
*/
|
|
30
|
-
export declare function calculateTradeAcquiredAmounts(direction: PositionDirection, amount: BN, market: Market): [BN, BN];
|
|
31
|
+
export declare function calculateTradeAcquiredAmounts(direction: PositionDirection, amount: BN, market: Market, inputAssetType?: AssetType): [BN, BN];
|
|
31
32
|
/**
|
|
32
33
|
* calculateTargetPriceTrade
|
|
33
34
|
* simple function for finding arbitraging trades
|
package/lib/math/trade.js
CHANGED
|
@@ -24,12 +24,12 @@ const MAXPCT = new anchor_1.BN(1000); //percentage units are [0,1000] => [0,1]
|
|
|
24
24
|
*
|
|
25
25
|
* 'newPrice' => the price of the asset after the trade : Precision MARK_PRICE_PRECISION
|
|
26
26
|
*/
|
|
27
|
-
function calculateTradeSlippage(direction, amount, market) {
|
|
27
|
+
function calculateTradeSlippage(direction, amount, market, inputAssetType = 'quote') {
|
|
28
28
|
const oldPrice = market_1.calculateMarkPrice(market);
|
|
29
29
|
if (amount.eq(numericConstants_1.ZERO)) {
|
|
30
30
|
return [numericConstants_1.ZERO, numericConstants_1.ZERO, oldPrice, oldPrice];
|
|
31
31
|
}
|
|
32
|
-
const [acquiredBase, acquiredQuote] = calculateTradeAcquiredAmounts(direction, amount, market);
|
|
32
|
+
const [acquiredBase, acquiredQuote] = calculateTradeAcquiredAmounts(direction, amount, market, inputAssetType);
|
|
33
33
|
const entryPrice = amm_1.calculatePrice(acquiredBase, acquiredQuote, market.amm.pegMultiplier).mul(new anchor_1.BN(-1));
|
|
34
34
|
const newPrice = amm_1.calculatePrice(market.amm.baseAssetReserve.sub(acquiredBase), market.amm.quoteAssetReserve.sub(acquiredQuote), market.amm.pegMultiplier);
|
|
35
35
|
if (direction == types_1.PositionDirection.SHORT) {
|
|
@@ -60,11 +60,11 @@ exports.calculateTradeSlippage = calculateTradeSlippage;
|
|
|
60
60
|
* | 'acquiredBase' => positive/negative change in user's base : BN TODO-PRECISION
|
|
61
61
|
* | 'acquiredQuote' => positive/negative change in user's quote : BN TODO-PRECISION
|
|
62
62
|
*/
|
|
63
|
-
function calculateTradeAcquiredAmounts(direction, amount, market) {
|
|
63
|
+
function calculateTradeAcquiredAmounts(direction, amount, market, inputAssetType = 'quote') {
|
|
64
64
|
if (amount.eq(numericConstants_1.ZERO)) {
|
|
65
65
|
return [numericConstants_1.ZERO, numericConstants_1.ZERO];
|
|
66
66
|
}
|
|
67
|
-
const [newQuoteAssetReserve, newBaseAssetReserve] = amm_1.calculateAmmReservesAfterSwap(market.amm,
|
|
67
|
+
const [newQuoteAssetReserve, newBaseAssetReserve] = amm_1.calculateAmmReservesAfterSwap(market.amm, inputAssetType, amount, amm_1.getSwapDirection(inputAssetType, direction));
|
|
68
68
|
const acquiredBase = market.amm.baseAssetReserve.sub(newBaseAssetReserve);
|
|
69
69
|
const acquiredQuote = market.amm.quoteAssetReserve.sub(newQuoteAssetReserve);
|
|
70
70
|
return [acquiredBase, acquiredQuote];
|
|
@@ -158,6 +158,7 @@ function calculateTargetPriceTrade(market, targetPrice, pct = MAXPCT) {
|
|
|
158
158
|
}
|
|
159
159
|
const entryPrice = tradeSize
|
|
160
160
|
.mul(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO)
|
|
161
|
+
.mul(numericConstants_1.MARK_PRICE_PRECISION)
|
|
161
162
|
.div(baseSize.abs());
|
|
162
163
|
assert_1.assert(tp1.sub(tp2).lte(originalDiff), 'Target Price Calculation incorrect');
|
|
163
164
|
assert_1.assert(tp2.lte(tp1) || tp2.sub(tp1).abs() < 100000, 'Target Price Calculation incorrect' +
|
package/lib/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drift-labs/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"author": "crispheaney",
|
|
@@ -11,10 +11,13 @@
|
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build": "yarn clean && tsc",
|
|
14
|
-
"clean": "rm -rf lib"
|
|
14
|
+
"clean": "rm -rf lib",
|
|
15
|
+
"patch-and-pub": "npm version patch --force && npm publish"
|
|
15
16
|
},
|
|
16
17
|
"keywords": [
|
|
17
|
-
"drift-labs",
|
|
18
|
+
"drift-labs",
|
|
19
|
+
"drift",
|
|
20
|
+
"perps"
|
|
18
21
|
],
|
|
19
22
|
"license": "Apache-2.0",
|
|
20
23
|
"directories": {
|
package/src/admin.ts
CHANGED
|
@@ -178,8 +178,8 @@ export class Admin extends ClearingHouse {
|
|
|
178
178
|
public async initializeMarket(
|
|
179
179
|
marketIndex: BN,
|
|
180
180
|
priceOracle: PublicKey,
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
baseAssetReserve: BN,
|
|
182
|
+
quoteAssetReserve: BN,
|
|
183
183
|
periodicity: BN,
|
|
184
184
|
pegMultiplier: BN = PEG_PRECISION
|
|
185
185
|
): Promise<TransactionSignature> {
|
|
@@ -189,8 +189,8 @@ export class Admin extends ClearingHouse {
|
|
|
189
189
|
|
|
190
190
|
const initializeMarketTx = await this.program.transaction.initializeMarket(
|
|
191
191
|
marketIndex,
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
baseAssetReserve,
|
|
193
|
+
quoteAssetReserve,
|
|
194
194
|
periodicity,
|
|
195
195
|
pegMultiplier,
|
|
196
196
|
{
|
package/src/clearingHouse.ts
CHANGED
|
@@ -52,10 +52,10 @@ import { wrapInTx } from './tx/utils';
|
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* # ClearingHouse
|
|
55
|
-
* This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more.
|
|
56
|
-
*
|
|
55
|
+
* This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more.
|
|
56
|
+
*
|
|
57
57
|
* The default way to construct a ClearingHouse instance is using the {@link from} method. This will create an instance using the static {@link DefaultClearingHouseAccountSubscriber}, which will use a websocket for each state account subscription.
|
|
58
|
-
* Alternatively, if you want to implement your own method of subscribing to the state accounts on the blockchain, you can implement a {@link ClearingHouseAccountSubscriber} and use it in the {@link ClearingHouse.constructor}
|
|
58
|
+
* Alternatively, if you want to implement your own method of subscribing to the state accounts on the blockchain, you can implement a {@link ClearingHouseAccountSubscriber} and use it in the {@link ClearingHouse.constructor}
|
|
59
59
|
*/
|
|
60
60
|
export class ClearingHouse {
|
|
61
61
|
connection: Connection;
|
|
@@ -200,7 +200,7 @@ export class ClearingHouse {
|
|
|
200
200
|
|
|
201
201
|
/**
|
|
202
202
|
* Update the wallet to use for clearing house transactions and linked user account
|
|
203
|
-
* @param newWallet
|
|
203
|
+
* @param newWallet
|
|
204
204
|
*/
|
|
205
205
|
public updateWallet(newWallet: IWallet): void {
|
|
206
206
|
const newProvider = new Provider(this.connection, newWallet, this.opts);
|
|
@@ -290,7 +290,7 @@ export class ClearingHouse {
|
|
|
290
290
|
userAccountPublicKey?: PublicKey;
|
|
291
291
|
/**
|
|
292
292
|
* Get the address for the Clearing House User's account. NOT the user's wallet address.
|
|
293
|
-
* @returns
|
|
293
|
+
* @returns
|
|
294
294
|
*/
|
|
295
295
|
public async getUserAccountPublicKey(): Promise<PublicKey> {
|
|
296
296
|
if (this.userAccountPublicKey) {
|
|
@@ -361,9 +361,9 @@ export class ClearingHouse {
|
|
|
361
361
|
|
|
362
362
|
/**
|
|
363
363
|
* Creates the Clearing House User account for a user, and deposits some initial collateral
|
|
364
|
-
* @param amount
|
|
365
|
-
* @param collateralAccountPublicKey
|
|
366
|
-
* @returns
|
|
364
|
+
* @param amount
|
|
365
|
+
* @param collateralAccountPublicKey
|
|
366
|
+
* @returns
|
|
367
367
|
*/
|
|
368
368
|
public async initializeUserAccountAndDepositCollateral(
|
|
369
369
|
amount: BN,
|
|
@@ -568,10 +568,10 @@ export class ClearingHouse {
|
|
|
568
568
|
|
|
569
569
|
/**
|
|
570
570
|
* Close an entire position. If you want to reduce a position, use the {@link openPosition} method in the opposite direction of the current position.
|
|
571
|
-
* @param marketIndex
|
|
572
|
-
* @param discountToken
|
|
573
|
-
* @param referrer
|
|
574
|
-
* @returns
|
|
571
|
+
* @param marketIndex
|
|
572
|
+
* @param discountToken
|
|
573
|
+
* @param referrer
|
|
574
|
+
* @returns
|
|
575
575
|
*/
|
|
576
576
|
public async closePosition(
|
|
577
577
|
marketIndex: BN,
|
package/src/clearingHouseUser.ts
CHANGED
|
@@ -86,8 +86,15 @@ export class ClearingHouseUser {
|
|
|
86
86
|
* @returns userPosition
|
|
87
87
|
*/
|
|
88
88
|
public getUserPosition(marketIndex: BN): UserPosition {
|
|
89
|
-
return
|
|
90
|
-
|
|
89
|
+
return (
|
|
90
|
+
this.getUserPositionsAccount().positions.find((position) =>
|
|
91
|
+
position.marketIndex.eq(marketIndex)
|
|
92
|
+
) ?? {
|
|
93
|
+
baseAssetAmount: ZERO,
|
|
94
|
+
lastCumulativeFundingRate: ZERO,
|
|
95
|
+
marketIndex,
|
|
96
|
+
quoteAssetAmount: ZERO,
|
|
97
|
+
}
|
|
91
98
|
);
|
|
92
99
|
}
|
|
93
100
|
|
|
@@ -217,8 +224,21 @@ export class ClearingHouseUser {
|
|
|
217
224
|
* calculates average exit price for closing 100% of position
|
|
218
225
|
* @returns : Precision MARK_PRICE_PRECISION
|
|
219
226
|
*/
|
|
220
|
-
public getPositionEstimatedExitPrice(position: UserPosition): BN {
|
|
227
|
+
public getPositionEstimatedExitPrice(position: UserPosition, amountToClose?: BN): BN {
|
|
221
228
|
const market = this.clearingHouse.getMarket(position.marketIndex);
|
|
229
|
+
|
|
230
|
+
if(amountToClose){
|
|
231
|
+
if(amountToClose.eq(ZERO)){
|
|
232
|
+
return calculateMarkPrice(market);
|
|
233
|
+
}
|
|
234
|
+
position = {
|
|
235
|
+
baseAssetAmount: amountToClose,
|
|
236
|
+
lastCumulativeFundingRate: position.lastCumulativeFundingRate,
|
|
237
|
+
marketIndex: position.marketIndex,
|
|
238
|
+
quoteAssetAmount: position.quoteAssetAmount,
|
|
239
|
+
} as UserPosition;
|
|
240
|
+
}
|
|
241
|
+
|
|
222
242
|
const baseAssetValue = calculateBaseAssetValue(market, position);
|
|
223
243
|
if (position.baseAssetAmount.eq(ZERO)) {
|
|
224
244
|
return ZERO;
|
package/src/config.ts
CHANGED
|
@@ -11,7 +11,7 @@ export const configs: { [key in DriftEnv]: DriftConfig } = {
|
|
|
11
11
|
devnet: {
|
|
12
12
|
ENV: 'devnet',
|
|
13
13
|
PYTH_ORACLE_MAPPING_ADDRESS: 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2',
|
|
14
|
-
CLEARING_HOUSE_PROGRAM_ID: '
|
|
14
|
+
CLEARING_HOUSE_PROGRAM_ID: 'AsW7LnXB9UA1uec9wi9MctYTgTz7YH9snhxd16GsFaGX',
|
|
15
15
|
USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
|
|
16
16
|
},
|
|
17
17
|
'mainnet-beta': {
|
|
@@ -37,7 +37,6 @@ export const initialize = (props: {
|
|
|
37
37
|
env: DriftEnv;
|
|
38
38
|
overrideEnv?: Partial<DriftConfig>;
|
|
39
39
|
}): DriftConfig => {
|
|
40
|
-
|
|
41
40
|
//@ts-ignore
|
|
42
41
|
if (props.env === 'master')
|
|
43
42
|
return { ...configs['devnet'], ...(props.overrideEnv ?? {}) };
|
package/src/constants/markets.ts
CHANGED
|
@@ -16,20 +16,20 @@ export const Markets: Market[] = [
|
|
|
16
16
|
devnetPythOracle: 'J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix',
|
|
17
17
|
mainnetPythOracle: 'H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG',
|
|
18
18
|
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
{
|
|
20
|
+
symbol: 'BTC-PERP',
|
|
21
|
+
baseAssetSymbol: 'BTC',
|
|
22
|
+
marketIndex: new BN(1),
|
|
23
|
+
devnetPythOracle: 'HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J',
|
|
24
|
+
mainnetPythOracle: 'GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
symbol: 'ETH-PERP',
|
|
28
|
+
baseAssetSymbol: 'ETH',
|
|
29
|
+
marketIndex: new BN(2),
|
|
30
|
+
devnetPythOracle: 'EdVCmQ9FSPcVe5YySXDPCRmc8aDQLKJ9xvYBMZPie1Vw',
|
|
31
|
+
mainnetPythOracle: 'JBu1AL4obBcCMqKBBxhpWCNUt136ijcuMZLFvTP7iWdB',
|
|
32
|
+
},
|
|
33
33
|
// {
|
|
34
34
|
// symbol: 'COPE-PERP',
|
|
35
35
|
// baseAssetSymbol: 'COPE',
|
|
@@ -1724,6 +1724,34 @@
|
|
|
1724
1724
|
}
|
|
1725
1725
|
],
|
|
1726
1726
|
"types": [
|
|
1727
|
+
{
|
|
1728
|
+
"name": "InitializeUserOptionalAccounts",
|
|
1729
|
+
"type": {
|
|
1730
|
+
"kind": "struct",
|
|
1731
|
+
"fields": [
|
|
1732
|
+
{
|
|
1733
|
+
"name": "whitelistToken",
|
|
1734
|
+
"type": "bool"
|
|
1735
|
+
}
|
|
1736
|
+
]
|
|
1737
|
+
}
|
|
1738
|
+
},
|
|
1739
|
+
{
|
|
1740
|
+
"name": "ManagePositionOptionalAccounts",
|
|
1741
|
+
"type": {
|
|
1742
|
+
"kind": "struct",
|
|
1743
|
+
"fields": [
|
|
1744
|
+
{
|
|
1745
|
+
"name": "discountToken",
|
|
1746
|
+
"type": "bool"
|
|
1747
|
+
},
|
|
1748
|
+
{
|
|
1749
|
+
"name": "referrer",
|
|
1750
|
+
"type": "bool"
|
|
1751
|
+
}
|
|
1752
|
+
]
|
|
1753
|
+
}
|
|
1754
|
+
},
|
|
1727
1755
|
{
|
|
1728
1756
|
"name": "CurveRecord",
|
|
1729
1757
|
"type": {
|
|
@@ -1938,34 +1966,6 @@
|
|
|
1938
1966
|
]
|
|
1939
1967
|
}
|
|
1940
1968
|
},
|
|
1941
|
-
{
|
|
1942
|
-
"name": "InitializeUserOptionalAccounts",
|
|
1943
|
-
"type": {
|
|
1944
|
-
"kind": "struct",
|
|
1945
|
-
"fields": [
|
|
1946
|
-
{
|
|
1947
|
-
"name": "whitelistToken",
|
|
1948
|
-
"type": "bool"
|
|
1949
|
-
}
|
|
1950
|
-
]
|
|
1951
|
-
}
|
|
1952
|
-
},
|
|
1953
|
-
{
|
|
1954
|
-
"name": "ManagePositionOptionalAccounts",
|
|
1955
|
-
"type": {
|
|
1956
|
-
"kind": "struct",
|
|
1957
|
-
"fields": [
|
|
1958
|
-
{
|
|
1959
|
-
"name": "discountToken",
|
|
1960
|
-
"type": "bool"
|
|
1961
|
-
},
|
|
1962
|
-
{
|
|
1963
|
-
"name": "referrer",
|
|
1964
|
-
"type": "bool"
|
|
1965
|
-
}
|
|
1966
|
-
]
|
|
1967
|
-
}
|
|
1968
|
-
},
|
|
1969
1969
|
{
|
|
1970
1970
|
"name": "LiquidationRecord",
|
|
1971
1971
|
"type": {
|
|
@@ -2757,6 +2757,11 @@
|
|
|
2757
2757
|
"code": 337,
|
|
2758
2758
|
"name": "InvalidFundingProfitability",
|
|
2759
2759
|
"msg": "AMM funding out of bounds pnl"
|
|
2760
|
+
},
|
|
2761
|
+
{
|
|
2762
|
+
"code": 338,
|
|
2763
|
+
"name": "CastingFailure",
|
|
2764
|
+
"msg": "Casting Failure"
|
|
2760
2765
|
}
|
|
2761
2766
|
]
|
|
2762
2767
|
}
|
package/src/math/funding.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { BN } from '@project-serum/anchor';
|
|
2
|
+
import {
|
|
3
|
+
AMM_RESERVE_PRECISION, MARK_PRICE_PRECISION, QUOTE_PRECISION, ZERO
|
|
4
|
+
} from '../constants/numericConstants';
|
|
2
5
|
import { PythClient } from '../pythClient';
|
|
3
|
-
import { MARK_PRICE_PRECISION } from '../constants/numericConstants';
|
|
4
6
|
import { Market } from '../types';
|
|
5
7
|
import { calculateMarkPrice } from './market';
|
|
6
8
|
|
|
@@ -9,15 +11,13 @@ import { calculateMarkPrice } from './market';
|
|
|
9
11
|
* @param market
|
|
10
12
|
* @param pythClient
|
|
11
13
|
* @param periodAdjustment
|
|
12
|
-
* @param estimationMethod
|
|
13
14
|
* @returns Estimated funding rate. : Precision //TODO-PRECISION
|
|
14
15
|
*/
|
|
15
|
-
export async function
|
|
16
|
+
export async function calculateAllEstimatedFundingRate(
|
|
16
17
|
market: Market,
|
|
17
18
|
pythClient: PythClient,
|
|
18
19
|
periodAdjustment: BN = new BN(1),
|
|
19
|
-
|
|
20
|
-
): Promise<BN> {
|
|
20
|
+
): Promise<[BN, BN, BN]> {
|
|
21
21
|
// periodAdjustment
|
|
22
22
|
// 1: hourly
|
|
23
23
|
// 24: daily
|
|
@@ -26,7 +26,7 @@ export async function calculateEstimatedFundingRate(
|
|
|
26
26
|
const hoursInDay = new BN(24);
|
|
27
27
|
|
|
28
28
|
if (!market.initialized) {
|
|
29
|
-
return new BN(0);
|
|
29
|
+
return [new BN(0), new BN(0), new BN(0)];
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const payFreq = new BN(market.amm.fundingPeriod);
|
|
@@ -61,16 +61,129 @@ export async function calculateEstimatedFundingRate(
|
|
|
61
61
|
.mul(new BN(100))
|
|
62
62
|
.div(oracleTwapWithMantissa);
|
|
63
63
|
|
|
64
|
+
|
|
65
|
+
const lowerboundEst = twapSpreadPct
|
|
66
|
+
.mul(payFreq)
|
|
67
|
+
.mul(BN.min(secondsInHour, timeSinceLastUpdate))
|
|
68
|
+
.mul(periodAdjustment)
|
|
69
|
+
.div(secondsInHour)
|
|
70
|
+
.div(secondsInHour)
|
|
71
|
+
.div(hoursInDay);
|
|
72
|
+
|
|
73
|
+
const interpEst = twapSpreadPct.mul(periodAdjustment).div(hoursInDay);
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
const interpRateQuote = twapSpreadPct.mul(periodAdjustment).div(hoursInDay)
|
|
77
|
+
.div(MARK_PRICE_PRECISION.div(QUOTE_PRECISION));
|
|
78
|
+
let feePoolSize = calculateFundingPool(market);
|
|
79
|
+
if(interpRateQuote.lt(new BN(0))){
|
|
80
|
+
feePoolSize = feePoolSize.mul(new BN(-1));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let cappedAltEst: BN;
|
|
84
|
+
let largerSide: BN;
|
|
85
|
+
let smallerSide: BN;
|
|
86
|
+
|
|
87
|
+
if(market.baseAssetAmountLong.gt(market.baseAssetAmountShort)){
|
|
88
|
+
largerSide = market.baseAssetAmountLong.abs();
|
|
89
|
+
smallerSide = market.baseAssetAmountShort.abs();
|
|
90
|
+
if(twapSpread.gt(new BN(0))){
|
|
91
|
+
return [lowerboundEst, interpEst, interpEst];
|
|
92
|
+
}
|
|
93
|
+
} else if(market.baseAssetAmountLong.lt(market.baseAssetAmountShort)){
|
|
94
|
+
largerSide = market.baseAssetAmountShort.abs();
|
|
95
|
+
smallerSide = market.baseAssetAmountLong.abs();
|
|
96
|
+
if(twapSpread.lt(new BN(0))){
|
|
97
|
+
return [lowerboundEst, interpEst, interpEst];
|
|
98
|
+
}
|
|
99
|
+
} else{
|
|
100
|
+
return [lowerboundEst, interpEst, interpEst];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if(largerSide.gt(ZERO)){
|
|
104
|
+
cappedAltEst = smallerSide.mul(twapSpread).div(largerSide);
|
|
105
|
+
const feePoolTopOff = feePoolSize.mul(MARK_PRICE_PRECISION.div(QUOTE_PRECISION))
|
|
106
|
+
.mul(AMM_RESERVE_PRECISION).div(largerSide);
|
|
107
|
+
cappedAltEst = cappedAltEst.add(feePoolTopOff);
|
|
108
|
+
|
|
109
|
+
cappedAltEst = cappedAltEst.mul(MARK_PRICE_PRECISION)
|
|
110
|
+
.mul(new BN(100))
|
|
111
|
+
.div(oracleTwapWithMantissa)
|
|
112
|
+
.mul(periodAdjustment).div(hoursInDay);
|
|
113
|
+
|
|
114
|
+
if(cappedAltEst.abs().gt(interpEst.abs())){
|
|
115
|
+
cappedAltEst = interpEst;
|
|
116
|
+
}
|
|
117
|
+
} else{
|
|
118
|
+
cappedAltEst = interpEst;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
return [lowerboundEst, cappedAltEst, interpEst];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
*
|
|
127
|
+
* @param market
|
|
128
|
+
* @param pythClient
|
|
129
|
+
* @param periodAdjustment
|
|
130
|
+
* @param estimationMethod
|
|
131
|
+
* @returns Estimated funding rate. : Precision //TODO-PRECISION
|
|
132
|
+
*/
|
|
133
|
+
export async function calculateEstimatedFundingRate(
|
|
134
|
+
market: Market,
|
|
135
|
+
pythClient: PythClient,
|
|
136
|
+
periodAdjustment: BN = new BN(1),
|
|
137
|
+
estimationMethod: 'interpolated' | 'lowerbound' | 'capped'
|
|
138
|
+
): Promise<BN> {
|
|
139
|
+
const [lowerboundEst, cappedAltEst, interpEst] =
|
|
140
|
+
await calculateAllEstimatedFundingRate(market, pythClient, periodAdjustment);
|
|
141
|
+
|
|
64
142
|
if (estimationMethod == 'lowerbound') {
|
|
65
143
|
//assuming remaining funding period has no gap
|
|
66
|
-
return
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
.mul(periodAdjustment)
|
|
70
|
-
.div(secondsInHour)
|
|
71
|
-
.div(secondsInHour)
|
|
72
|
-
.div(hoursInDay);
|
|
144
|
+
return lowerboundEst;
|
|
145
|
+
} else if (estimationMethod == 'capped') {
|
|
146
|
+
return cappedAltEst;
|
|
73
147
|
} else {
|
|
74
|
-
return
|
|
148
|
+
return interpEst;
|
|
75
149
|
}
|
|
76
150
|
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
*
|
|
155
|
+
* @param market
|
|
156
|
+
* @param pythClient
|
|
157
|
+
* @param periodAdjustment
|
|
158
|
+
* @param estimationMethod
|
|
159
|
+
* @returns Estimated funding rate. : Precision //TODO-PRECISION
|
|
160
|
+
*/
|
|
161
|
+
export async function calculateLongShortFundingRate(
|
|
162
|
+
market: Market,
|
|
163
|
+
pythClient: PythClient,
|
|
164
|
+
periodAdjustment: BN = new BN(1),
|
|
165
|
+
): Promise<[BN, BN]> {
|
|
166
|
+
const [_, cappedAltEst, interpEst] =
|
|
167
|
+
await calculateAllEstimatedFundingRate(market, pythClient, periodAdjustment);
|
|
168
|
+
|
|
169
|
+
if(market.baseAssetAmountLong.gt(market.baseAssetAmountShort)){
|
|
170
|
+
return [cappedAltEst, interpEst];
|
|
171
|
+
} else if(market.baseAssetAmountLong.lt(market.baseAssetAmountShort)){
|
|
172
|
+
return [interpEst, cappedAltEst];
|
|
173
|
+
} else{
|
|
174
|
+
return [interpEst, interpEst];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
*
|
|
181
|
+
* @param market
|
|
182
|
+
* @returns Estimated fee pool size
|
|
183
|
+
*/
|
|
184
|
+
export function calculateFundingPool(market: Market): BN {
|
|
185
|
+
const totalFeeLB = market.amm.totalFee.div(new BN(2));
|
|
186
|
+
const feePool = market.amm.totalFeeMinusDistributions.sub(totalFeeLB);
|
|
187
|
+
// return new BN(QUOTE_PRECISION.mul(new BN(2400)));
|
|
188
|
+
return feePool;
|
|
189
|
+
}
|
package/src/math/position.ts
CHANGED
|
@@ -102,9 +102,9 @@ export function calculatePositionPNL(
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
*
|
|
106
|
-
* @param market
|
|
107
|
-
* @param marketPosition
|
|
105
|
+
*
|
|
106
|
+
* @param market
|
|
107
|
+
* @param marketPosition
|
|
108
108
|
* @returns // TODO-PRECISION
|
|
109
109
|
*/
|
|
110
110
|
export function calculatePositionFundingPNL(
|
package/src/math/trade.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
calculateAmmReservesAfterSwap,
|
|
13
13
|
calculatePrice,
|
|
14
14
|
getSwapDirection,
|
|
15
|
+
AssetType,
|
|
15
16
|
} from './amm';
|
|
16
17
|
import { squareRootBN } from './utils';
|
|
17
18
|
|
|
@@ -47,7 +48,8 @@ export type PriceImpactUnit =
|
|
|
47
48
|
export function calculateTradeSlippage(
|
|
48
49
|
direction: PositionDirection,
|
|
49
50
|
amount: BN,
|
|
50
|
-
market: Market
|
|
51
|
+
market: Market,
|
|
52
|
+
inputAssetType: AssetType = 'quote',
|
|
51
53
|
): [BN, BN, BN, BN] {
|
|
52
54
|
const oldPrice = calculateMarkPrice(market);
|
|
53
55
|
if (amount.eq(ZERO)) {
|
|
@@ -56,7 +58,8 @@ export function calculateTradeSlippage(
|
|
|
56
58
|
const [acquiredBase, acquiredQuote] = calculateTradeAcquiredAmounts(
|
|
57
59
|
direction,
|
|
58
60
|
amount,
|
|
59
|
-
market
|
|
61
|
+
market,
|
|
62
|
+
inputAssetType
|
|
60
63
|
);
|
|
61
64
|
|
|
62
65
|
const entryPrice = calculatePrice(
|
|
@@ -103,7 +106,8 @@ export function calculateTradeSlippage(
|
|
|
103
106
|
export function calculateTradeAcquiredAmounts(
|
|
104
107
|
direction: PositionDirection,
|
|
105
108
|
amount: BN,
|
|
106
|
-
market: Market
|
|
109
|
+
market: Market,
|
|
110
|
+
inputAssetType: AssetType = 'quote',
|
|
107
111
|
): [BN, BN] {
|
|
108
112
|
if (amount.eq(ZERO)) {
|
|
109
113
|
return [ZERO, ZERO];
|
|
@@ -112,9 +116,9 @@ export function calculateTradeAcquiredAmounts(
|
|
|
112
116
|
const [newQuoteAssetReserve, newBaseAssetReserve] =
|
|
113
117
|
calculateAmmReservesAfterSwap(
|
|
114
118
|
market.amm,
|
|
115
|
-
|
|
119
|
+
inputAssetType,
|
|
116
120
|
amount,
|
|
117
|
-
getSwapDirection(
|
|
121
|
+
getSwapDirection(inputAssetType, direction)
|
|
118
122
|
);
|
|
119
123
|
|
|
120
124
|
const acquiredBase = market.amm.baseAssetReserve.sub(newBaseAssetReserve);
|
|
@@ -129,8 +133,8 @@ export function calculateTradeAcquiredAmounts(
|
|
|
129
133
|
* @param market
|
|
130
134
|
* @param targetPrice
|
|
131
135
|
* @param pct optional default is 100% gap filling, can set smaller.
|
|
132
|
-
* @returns trade direction/size in order to push price to a targetPrice,
|
|
133
|
-
*
|
|
136
|
+
* @returns trade direction/size in order to push price to a targetPrice,
|
|
137
|
+
*
|
|
134
138
|
* [
|
|
135
139
|
* direction => direction of trade required, TODO-PRECISION
|
|
136
140
|
* tradeSize => size of trade required, TODO-PRECISION
|
|
@@ -236,6 +240,7 @@ export function calculateTargetPriceTrade(
|
|
|
236
240
|
|
|
237
241
|
const entryPrice = tradeSize
|
|
238
242
|
.mul(AMM_TO_QUOTE_PRECISION_RATIO)
|
|
243
|
+
.mul(MARK_PRICE_PRECISION)
|
|
239
244
|
.div(baseSize.abs());
|
|
240
245
|
|
|
241
246
|
assert(tp1.sub(tp2).lte(originalDiff), 'Target Price Calculation incorrect');
|