make mm work with mango-perp, deposit some funds into shared wallet mango account, fixed some ts perp issues

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
microwavedcola1 2022-12-09 20:23:30 +01:00
parent 13f6fe2343
commit d0c85f14e6
8 changed files with 250 additions and 74 deletions

View File

@ -48,6 +48,7 @@
"eslint-config-prettier": "^7.2.0",
"ftx-api": "^1.1.13",
"mocha": "^9.1.3",
"node-kraken-api": "^2.2.2",
"prettier": "^2.0.5",
"ts-mocha": "^10.0.0",
"ts-node": "^9.1.1",

View File

@ -299,9 +299,9 @@ export class PerpMarket {
res += ` ${this.name} OrderBook`;
let orders = await this?.loadAsks(client);
for (const order of orders!.items()) {
res += `\n ${order.clientId.toString()} ${order.uiPrice
.toFixed(5)
.padStart(10)}, ${order.uiSize.toString().padStart(10)} ${
res += `\n ${order.uiPrice.toFixed(5).padStart(10)}, ${order.uiSize
.toString()
.padStart(10)} ${
order.isOraclePegged && order.oraclePeggedProperties
? order.oraclePeggedProperties.pegLimit.toNumber() + ' (PegLimit)'
: ''
@ -310,9 +310,9 @@ export class PerpMarket {
res += `\n asks ↑ --------- ↓ bids`;
orders = await this?.loadBids(client);
for (const order of orders!.items()) {
res += `\n ${order.clientId.toString()} ${order.uiPrice
.toFixed(5)
.padStart(10)}, ${order.uiSize.toString().padStart(10)} ${
res += `\n ${order.uiPrice.toFixed(5).padStart(10)}, ${order.uiSize
.toString()
.padStart(10)} ${
order.isOraclePegged && order.oraclePeggedProperties
? order.oraclePeggedProperties.pegLimit.toNumber() + ' (PegLimit)'
: ''
@ -508,7 +508,7 @@ export class BookSide {
bookSideType: BookSideType,
obj: {
roots: OrderTreeRoot[];
orderTree: OrderTreeNodes;
nodes: OrderTreeNodes;
},
): BookSide {
return new BookSide(
@ -517,7 +517,7 @@ export class BookSide {
bookSideType,
obj.roots[0],
obj.roots[1],
obj.orderTree,
obj.nodes,
);
}
@ -527,14 +527,14 @@ export class BookSide {
public type: BookSideType,
public rootFixed: OrderTreeRoot,
public rootOraclePegged: OrderTreeRoot,
public orderTree: OrderTreeNodes,
public orderTreeNodes: OrderTreeNodes,
maxBookDelay?: number,
) {
// Determine the maxTimestamp found on the book to use for tif
// If maxBookDelay is not provided, use 3600 as a very large number
maxBookDelay = maxBookDelay === undefined ? 3600 : maxBookDelay;
let maxTimestamp = new BN(new Date().getTime() / 1000 - maxBookDelay);
for (const node of this.orderTree.nodes) {
for (const node of this.orderTreeNodes.nodes) {
if (node.tag !== BookSide.LEAF_NODE_TAG) {
continue;
}
@ -629,7 +629,7 @@ export class BookSide {
while (stack.length > 0) {
const index = stack.pop()!;
const node = this.orderTree.nodes[index];
const node = this.orderTreeNodes.nodes[index];
if (node.tag === BookSide.INNER_NODE_TAG) {
const innerNode = BookSide.toInnerNode(this.client, node.data);
stack.push(innerNode.children[right], innerNode.children[left]);
@ -659,7 +659,7 @@ export class BookSide {
while (stack.length > 0) {
const index = stack.pop()!;
const node = this.orderTree.nodes[index];
const node = this.orderTreeNodes.nodes[index];
if (node.tag === BookSide.INNER_NODE_TAG) {
const innerNode = BookSide.toInnerNode(this.client, node.data);
stack.push(innerNode.children[right], innerNode.children[left]);
@ -754,7 +754,6 @@ export class LeafNode {
key: BN;
owner: PublicKey;
quantity: BN;
clientOrderId: BN;
timestamp: BN;
pegLimit: BN;
}): LeafNode {
@ -765,7 +764,6 @@ export class LeafNode {
obj.key,
obj.owner,
obj.quantity,
obj.clientOrderId,
obj.timestamp,
obj.pegLimit,
);
@ -778,7 +776,6 @@ export class LeafNode {
public key: BN,
public owner: PublicKey,
public quantity: BN,
public clientOrderId: BN,
public timestamp: BN,
public pegLimit: BN,
) {}
@ -843,7 +840,6 @@ export class PerpOrder {
? new BN('18446744073709551615').sub(leafNode.key.maskn(64))
: leafNode.key.maskn(64),
leafNode.key,
leafNode.clientOrderId,
leafNode.owner,
leafNode.ownerSlot,
0,
@ -864,7 +860,6 @@ export class PerpOrder {
constructor(
public seqNum: BN,
public orderId: BN,
public clientId: BN,
public owner: PublicKey,
public openOrdersSlot: number,
public feeTier: 0,

View File

@ -3855,6 +3855,17 @@ export type MangoV4 = {
},
{
"name": "beingLiquidated",
"docs": [
"Tracks that this account should be liquidated until init_health >= 0.",
"",
"Normally accounts can not be liquidated while maint_health >= 0. But when an account",
"reaches maint_health < 0, liquidators will call a liquidation instruction and thereby",
"set this flag. Now the account may be liquidated until init_health >= 0.",
"",
"Many actions should be disabled while the account is being liquidated, even if",
"its maint health has recovered to positive. Creating new open orders would, for example,",
"confuse liquidators."
],
"type": "u8"
},
{
@ -5746,6 +5757,19 @@ export type MangoV4 = {
},
{
"name": "StablePriceModel",
"docs": [
"Maintains a \"stable_price\" based on the oracle price.",
"",
"The stable price follows the oracle price, but its relative rate of",
"change is limited (to `stable_growth_limit`) and futher reduced if",
"the oracle price is far from the `delay_price`.",
"",
"Conceptually the `delay_price` is itself a time delayed",
"(`24 * delay_interval_seconds`, assume 24h) and relative rate of change limited",
"function of the oracle price. It is implemented as averaging the oracle",
"price over every `delay_interval_seconds` (assume 1h) and then applying the",
"`delay_growth_limit` between intervals."
],
"type": {
"kind": "struct",
"fields": [
@ -5762,6 +5786,13 @@ export type MangoV4 = {
},
{
"name": "delayPrices",
"docs": [
"Stored delay_price for each delay_interval.",
"If we want the delay_price to be 24h delayed, we would store one for each hour.",
"This is used in a cyclical way: We use the maximally-delayed value at delay_interval_index",
"and once enough time passes to move to the next delay interval, that gets overwritten and",
"we use the next one."
],
"type": {
"array": [
"f64",
@ -5771,26 +5802,46 @@ export type MangoV4 = {
},
{
"name": "delayAccumulatorPrice",
"docs": [
"The delay price is based on an average over each delay_interval. The contributions",
"to the average are summed up here."
],
"type": "f64"
},
{
"name": "delayAccumulatorTime",
"docs": [
"Accumulating the total time for the above average."
],
"type": "u32"
},
{
"name": "delayIntervalSeconds",
"docs": [
"Length of a delay_interval"
],
"type": "u32"
},
{
"name": "delayGrowthLimit",
"docs": [
"Maximal relative difference between two delay_price in consecutive intervals."
],
"type": "f32"
},
{
"name": "stableGrowthLimit",
"docs": [
"Maximal per-second relative difference of the stable price.",
"It gets further reduced if stable and delay price disagree."
],
"type": "f32"
},
{
"name": "lastDelayIntervalIndex",
"docs": [
"The delay_interval_index that update() was last called on."
],
"type": "u8"
},
{
@ -5982,20 +6033,6 @@ export type MangoV4 = {
]
}
},
{
"name": "HealthType",
"type": {
"kind": "enum",
"variants": [
{
"name": "Init"
},
{
"name": "Maint"
}
]
}
},
{
"name": "OracleType",
"type": {
@ -6589,6 +6626,11 @@ export type MangoV4 = {
"name": "openInterest",
"type": "i64",
"index": false
},
{
"name": "instantaneousFundingRate",
"type": "i128",
"index": false
}
]
},
@ -6649,6 +6691,16 @@ export type MangoV4 = {
"name": "totalDeposits",
"type": "i128",
"index": false
},
{
"name": "borrowRate",
"type": "i128",
"index": false
},
{
"name": "depositRate",
"type": "i128",
"index": false
}
]
},
@ -11206,6 +11258,17 @@ export const IDL: MangoV4 = {
},
{
"name": "beingLiquidated",
"docs": [
"Tracks that this account should be liquidated until init_health >= 0.",
"",
"Normally accounts can not be liquidated while maint_health >= 0. But when an account",
"reaches maint_health < 0, liquidators will call a liquidation instruction and thereby",
"set this flag. Now the account may be liquidated until init_health >= 0.",
"",
"Many actions should be disabled while the account is being liquidated, even if",
"its maint health has recovered to positive. Creating new open orders would, for example,",
"confuse liquidators."
],
"type": "u8"
},
{
@ -13097,6 +13160,19 @@ export const IDL: MangoV4 = {
},
{
"name": "StablePriceModel",
"docs": [
"Maintains a \"stable_price\" based on the oracle price.",
"",
"The stable price follows the oracle price, but its relative rate of",
"change is limited (to `stable_growth_limit`) and futher reduced if",
"the oracle price is far from the `delay_price`.",
"",
"Conceptually the `delay_price` is itself a time delayed",
"(`24 * delay_interval_seconds`, assume 24h) and relative rate of change limited",
"function of the oracle price. It is implemented as averaging the oracle",
"price over every `delay_interval_seconds` (assume 1h) and then applying the",
"`delay_growth_limit` between intervals."
],
"type": {
"kind": "struct",
"fields": [
@ -13113,6 +13189,13 @@ export const IDL: MangoV4 = {
},
{
"name": "delayPrices",
"docs": [
"Stored delay_price for each delay_interval.",
"If we want the delay_price to be 24h delayed, we would store one for each hour.",
"This is used in a cyclical way: We use the maximally-delayed value at delay_interval_index",
"and once enough time passes to move to the next delay interval, that gets overwritten and",
"we use the next one."
],
"type": {
"array": [
"f64",
@ -13122,26 +13205,46 @@ export const IDL: MangoV4 = {
},
{
"name": "delayAccumulatorPrice",
"docs": [
"The delay price is based on an average over each delay_interval. The contributions",
"to the average are summed up here."
],
"type": "f64"
},
{
"name": "delayAccumulatorTime",
"docs": [
"Accumulating the total time for the above average."
],
"type": "u32"
},
{
"name": "delayIntervalSeconds",
"docs": [
"Length of a delay_interval"
],
"type": "u32"
},
{
"name": "delayGrowthLimit",
"docs": [
"Maximal relative difference between two delay_price in consecutive intervals."
],
"type": "f32"
},
{
"name": "stableGrowthLimit",
"docs": [
"Maximal per-second relative difference of the stable price.",
"It gets further reduced if stable and delay price disagree."
],
"type": "f32"
},
{
"name": "lastDelayIntervalIndex",
"docs": [
"The delay_interval_index that update() was last called on."
],
"type": "u8"
},
{
@ -13333,20 +13436,6 @@ export const IDL: MangoV4 = {
]
}
},
{
"name": "HealthType",
"type": {
"kind": "enum",
"variants": [
{
"name": "Init"
},
{
"name": "Maint"
}
]
}
},
{
"name": "OracleType",
"type": {
@ -13940,6 +14029,11 @@ export const IDL: MangoV4 = {
"name": "openInterest",
"type": "i64",
"index": false
},
{
"name": "instantaneousFundingRate",
"type": "i128",
"index": false
}
]
},
@ -14000,6 +14094,16 @@ export const IDL: MangoV4 = {
"name": "totalDeposits",
"type": "i128",
"index": false
},
{
"name": "borrowRate",
"type": "i128",
"index": false
},
{
"name": "depositRate",
"type": "i128",
"index": false
}
]
},

View File

@ -316,6 +316,7 @@ async function registerSerum3Markets() {
'SOL/USDC',
);
}
async function createUser(userKeypair: string) {
const result = await buildUserClient(userKeypair);
const client = result[0];
@ -329,24 +330,28 @@ async function createUser(userKeypair: string) {
}
console.log(`...created MangoAccount ${mangoAccount.publicKey.toBase58()}`);
}
async function depositForUser(userKeypair: string) {
const result = await buildUserClient(userKeypair);
const client = result[0];
const group = result[1];
const user = result[2];
const mangoAccount = await client.getMangoAccountForOwner(
group,
user.publicKey,
0,
)!;
await client.tokenDeposit(
group,
mangoAccount,
mangoAccount!,
new PublicKey(MAINNET_MINTS.get('USDC')!),
10,
);
await mangoAccount.reload(client);
await mangoAccount!.reload(client);
console.log(`...deposited 10 USDC`);
await client.tokenDeposit(
group,
mangoAccount,
new PublicKey(MAINNET_MINTS.get('SOL')!),
1,
);
await mangoAccount.reload(client);
console.log(`...deposited 1 SOL`);
}
async function registerPerpMarkets() {
@ -441,7 +446,7 @@ async function main() {
}
try {
// await createUser(MB_USER_KEYPAIR!);
// await createUser(MB_USER4_KEYPAIR!);
// depositForUser(MB_USER_KEYPAIR!);
} catch (error) {
console.log(error);
}

View File

@ -42,10 +42,16 @@ async function main() {
await new Promise((r) => setTimeout(r, 2000));
console.clear();
await group.reloadAll(client);
const perpMarket = group.getPerpMarketByName('BTC-PERP');
console.log(` perpMarket.uiPrice ${perpMarket.uiPrice}`);
const btcPerpMarket = group.getPerpMarketByName('BTC-PERP');
console.log(` perpMarket.uiPrice ${btcPerpMarket.uiPrice}`);
console.log(``);
console.log(await perpMarket.logOb(client));
console.log(await btcPerpMarket.logOb(client));
console.log(``);
const mngoPerpMarket = group.getPerpMarketByName('MNGO-PERP');
console.log(` perpMarket.uiPrice ${mngoPerpMarket.uiPrice}`);
console.log(``);
console.log(await mngoPerpMarket.logOb(client));
console.log(``);
}
}

View File

@ -8,6 +8,7 @@ import {
} from '@solana/web3.js';
import Binance from 'binance-api-node';
import fs from 'fs';
import { Kraken } from 'node-kraken-api';
import path from 'path';
import { Group } from '../../accounts/group';
import { MangoAccount } from '../../accounts/mangoAccount';
@ -68,8 +69,11 @@ type MarketContext = {
asks: BookSide;
lastBookUpdate: number;
binanceBid: number | undefined;
binanceAsk: number | undefined;
krakenBid: number | undefined;
krakenAsk: number | undefined;
// binanceBid: number | undefined;
// binanceAsk: number | undefined;
sequenceAccount: PublicKey;
sequenceAccountBump: number;
@ -80,6 +84,7 @@ type MarketContext = {
};
const binanceClient = Binance();
const krakenClient = new Kraken();
function getPerpMarketAssetsToTradeOn(group: Group) {
const allMangoGroupPerpMarketAssets = Array.from(
@ -102,10 +107,14 @@ async function refreshState(
const result = await Promise.all([
group.reloadAll(client),
mangoAccount.reload(client),
...Array.from(marketContexts.values()).map((mc) =>
binanceClient.book({
symbol: mc.perpMarket.name.replace('-PERP', 'USDT'),
}),
...Array.from(marketContexts.values()).map(
(mc) =>
krakenClient.depth({
pair: mc.params.krakenCode,
}),
// binanceClient.book({
// symbol: mc.perpMarket.name.replace('-PERP', 'USDT'),
// }),
),
]);
@ -118,8 +127,12 @@ async function refreshState(
mc.asks = await perpMarket.loadAsks(client, true);
mc.lastBookUpdate = ts;
mc.binanceAsk = parseFloat((result[i + 2] as any).asks[0].price);
mc.binanceBid = parseFloat((result[i + 2] as any).bids[0].price);
mc.krakenAsk = parseFloat(
(result[i + 2] as any)[mc.params.krakenCode].asks[0][0],
);
mc.krakenBid = parseFloat(
(result[i + 2] as any)[mc.params.krakenCode].bids[0][0],
);
});
return {
@ -284,8 +297,8 @@ async function fullMarketMaker() {
sentAskPrice: 0,
lastOrderUpdate: 0,
binanceBid: undefined,
binanceAsk: undefined,
krakenBid: undefined,
krakenAsk: undefined,
});
}
@ -322,7 +335,7 @@ async function fullMarketMaker() {
const pos = mangoAccount.perpPositionExistsForMarket(mc.perpMarket)
? mangoAccount.getPerpPositionUi(group, mc.perpMarket.perpMarketIndex)
: 0;
const mid = (mc.binanceBid! + mc.binanceAsk!) / 2;
const mid = (mc.krakenBid! + mc.krakenAsk!) / 2;
if (mid) {
pfQuoteValue += pos * mid;
} else {
@ -387,8 +400,8 @@ async function makeMarketUpdateInstructions(
const perpMarketIndex = mc.perpMarket.perpMarketIndex;
const perpMarket = mc.perpMarket;
const aggBid = mc.binanceBid;
const aggAsk = mc.binanceAsk;
const aggBid = mc.krakenBid;
const aggAsk = mc.krakenAsk;
if (aggBid === undefined || aggAsk === undefined) {
console.log(`No Aggregate Book for ${mc.perpMarket.name}!`);
return [];
@ -623,6 +636,10 @@ async function makeMarketUpdateInstructions(
20,
);
// console.log(
// `basePos ${basePos}, posAsTradeSizes ${posAsTradeSizes}, size ${size}`,
// );
const posAsTradeSizes = basePos / size;
instructions.push(cancelAllIx);

View File

@ -10,7 +10,19 @@
"bias": 0.0,
"requoteThresh": 0.0002,
"takeSpammers": true,
"spammerCharge": 2
"spammerCharge": 2,
"krakenCode": "XXBTZUSD"
}
},
"MNGO": {
"perp": {
"sizePerc": 0.05,
"leanCoeff": 0.00025,
"bias": 0.0,
"requoteThresh": 0.0002,
"takeSpammers": true,
"spammerCharge": 2,
"krakenCode": "MNGOUSD"
}
}
}

View File

@ -1116,6 +1116,13 @@ bufferutil@^4.0.1:
dependencies:
node-gyp-build "^4.3.0"
bufferutil@^4.0.6:
version "4.0.7"
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad"
integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==
dependencies:
node-gyp-build "^4.3.0"
callsites@^3.0.0:
version "3.1.0"
resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz"
@ -1229,6 +1236,11 @@ concat-map@0.0.1:
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
crc@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/crc/-/crc-4.1.1.tgz#cb926237b56739f82c8533da1b66925ed33e011f"
integrity sha512-2U3ZqJ2phJl9ANuP2q5VS53LMpNmYU9vcpmh6nutJmsqUREhtWpTRh9yYxG7sDg3xkwaEEXytSeffTxw4cgwPg==
create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz"
@ -2258,6 +2270,18 @@ node-gyp-build@^4.2.0, node-gyp-build@^4.3.0:
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40"
integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==
node-kraken-api@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/node-kraken-api/-/node-kraken-api-2.2.2.tgz#28bf56eec1f0ffe7e784b2ffa29b360ed98b0e4c"
integrity sha512-f+BZpgT1gD9705hlsRDDGj9m96Psb0Gxu3Td/P2fs0/gFy58YQONrTPiqfYzOBxvpmYnuAMxCRuRmdmkw04eRw==
dependencies:
crc "^4.1.0"
ts-ev "^0.4.0"
ws "^8.5.0"
optionalDependencies:
bufferutil "^4.0.6"
utf-8-validate "^5.0.9"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
@ -2740,6 +2764,11 @@ traverse-chain@~0.1.0:
resolved "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz"
integrity sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg==
ts-ev@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/ts-ev/-/ts-ev-0.4.0.tgz#b30bbab35bd57516efba7ab89b6417424a1ebf0e"
integrity sha512-rLX6QdkC1/jA9sS4y9/DxHABTcOussp33J90h+TxHmya9CWvbGc9uLqdM4c/N4pNRmSdtq9zqhz7sB9KcN1NFQ==
ts-mocha@^10.0.0:
version "10.0.0"
resolved "https://registry.npmjs.org/ts-mocha/-/ts-mocha-10.0.0.tgz"
@ -2854,6 +2883,13 @@ utf-8-validate@^5.0.2:
dependencies:
node-gyp-build "^4.3.0"
utf-8-validate@^5.0.9:
version "5.0.10"
resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2"
integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==
dependencies:
node-gyp-build "^4.3.0"
uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"