Skip to content

Commit

Permalink
sdk: add L2 and L3 orderbook view (#445)
Browse files Browse the repository at this point in the history
* inital L2 and L3 methods

* vamm order prototype

* l2 orderbook uses vamm

* simplify the getL2 and getL3 function signatures

* getL2/getL3 methods on DLOBSubscriber

* tweak L2/L3 types

* work for serum

* tweak imports

* fix falsey check

* add http status code if DLOBApiClient fails

* v2.26.0-beta.0

* ts-sdk: add @ellipsis-labs/phoenix-sdk

* Add phoenix subscriber (#444)

* Add phoenix subscriber

* Address PR comments

* update CHANGELOG.md

* v2.26.0-beta.1

* program: check max_token_deposits at the end of fill_spot_order (#441)

* bigz/spot-fill-max-check

* address feedback bugs

* CHANGELOG

---------

Co-authored-by: Chris Heaney <[email protected]>

* program: fix overflow error in if staking math (#443)

* bigz/fix-large-insurance-math-error

* update CHANGELOG.md

* CHANGELOG

* fix warning

* jsdocs

* use getMakerLimitBids/Asks for now

---------

Co-authored-by: wphan <[email protected]>
Co-authored-by: Jarry Xiao <[email protected]>
Co-authored-by: bigzPubkey <[email protected]>
  • Loading branch information
4 people committed Apr 27, 2023
1 parent 84ee075 commit 2f533ea
Show file tree
Hide file tree
Showing 12 changed files with 632 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- program: add L2 and L3 view of DLOB ([#445](https://github.com/drift-labs/protocol-v2/pull/445))
- ts-sdk: new DLOBSubscriber class to keep updated DLOB ([#439](https://github.com/drift-labs/protocol-v2/pull/439))
- program: add support for phoenix spot markets ([#437](https://github.com/drift-labs/protocol-v2/pull/437))
- sdk: ability to add stake from subaccount
Expand Down
3 changes: 2 additions & 1 deletion programs/drift/src/controller/insurance/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ pub fn large_num_seeded_stake_if_test() {
number_of_sub_accounts: 0,
..UserStats::default()
};
let amount = 199_000_001 as u64; // ~200M + 1

let amount = 199_000_001; // ~200M + 1

// all funds in revenue pool
let mut spot_market = SpotMarket {
Expand Down
194 changes: 177 additions & 17 deletions sdk/src/dlob/DLOB.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,48 @@
import { getOrderSignature, getVammNodeGenerator, NodeList } from './NodeList';
import {
MarketType,
BN,
calculateAskPrice,
calculateBidPrice,
DriftClient,
convertToNumber,
isOrderExpired,
DLOBNode,
DLOBNodeType,
DriftClient,
getLimitPrice,
getVariant,
isFallbackAvailableLiquiditySource,
isOneOfVariant,
isOrderExpired,
isRestingLimitOrder,
isTakingOrder,
isTriggered,
isVariant,
getVariant,
MarketType,
MarketTypeStr,
mustBeTriggered,
OraclePriceData,
Order,
PRICE_PRECISION,
SpotMarketAccount,
OrderActionRecord,
OrderRecord,
PerpMarketAccount,
OraclePriceData,
PRICE_PRECISION,
SlotSubscriber,
MarketTypeStr,
SpotMarketAccount,
StateAccount,
mustBeTriggered,
isTriggered,
getLimitPrice,
TriggerOrderNode,
UserMap,
OrderRecord,
OrderActionRecord,
isRestingLimitOrder,
isTakingOrder,
isFallbackAvailableLiquiditySource,
} from '..';
import { PublicKey } from '@solana/web3.js';
import { DLOBNode, DLOBNodeType, TriggerOrderNode } from '..';
import { ammPaused, exchangePaused, fillPaused } from '../math/exchangeStatus';
import { DLOBOrders } from './DLOBOrders';
import {
createL2Levels,
getL2GeneratorFromDLOBNodes,
L2OrderBook,
L2OrderBookGenerator,
L3Level,
L3OrderBook,
mergeL2LevelGenerators,
} from './orderBookLevels';

export type MarketNodeLists = {
restingLimit: {
Expand Down Expand Up @@ -1689,4 +1700,153 @@ export class DLOB {
yield nodeLists.trigger.below;
}
}

/**
* Get an L2 view of the order book for a given market.
*
* @param marketIndex
* @param marketType
* @param slot
* @param oraclePriceData
* @param depth how many levels of the order book to return
* @param fallbackAsk best ask for fallback liquidity, only relevant for perps
* @param fallbackBid best bid for fallback liquidity, only relevant for perps
* @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber}
*/
public getL2({
marketIndex,
marketType,
slot,
oraclePriceData,
depth,
fallbackAsk,
fallbackBid,
fallbackL2Generators = [],
}: {
marketIndex: number;
marketType: MarketType;
slot: number;
oraclePriceData: OraclePriceData;
depth: number;
fallbackAsk?: BN;
fallbackBid?: BN;
fallbackL2Generators?: L2OrderBookGenerator[];
}): L2OrderBook {
const makerAskL2LevelGenerator = getL2GeneratorFromDLOBNodes(
this.getMakerLimitAsks(
marketIndex,
slot,
marketType,
oraclePriceData,
fallbackBid
),
oraclePriceData,
slot
);

const fallbackAskGenerators = fallbackL2Generators.map(
(fallbackL2Generator) => {
return fallbackL2Generator.getL2Asks();
}
);

const askL2LevelGenerator = mergeL2LevelGenerators(
[makerAskL2LevelGenerator, ...fallbackAskGenerators],
(a, b) => {
return a.price.lt(b.price);
}
);

const asks = createL2Levels(askL2LevelGenerator, depth);

const makerBidGenerator = getL2GeneratorFromDLOBNodes(
this.getMakerLimitBids(
marketIndex,
slot,
marketType,
oraclePriceData,
fallbackAsk
),
oraclePriceData,
slot
);

const fallbackBidGenerators = fallbackL2Generators.map((fallbackOrders) => {
return fallbackOrders.getL2Bids();
});

const bidL2LevelGenerator = mergeL2LevelGenerators(
[makerBidGenerator, ...fallbackBidGenerators],
(a, b) => {
return a.price.gt(b.price);
}
);

const bids = createL2Levels(bidL2LevelGenerator, depth);

return {
bids,
asks,
};
}

/**
* Get an L3 view of the order book for a given market. Does not include fallback liquidity sources
*
* @param marketIndex
* @param marketType
* @param slot
* @param oraclePriceData
*/
public getL3({
marketIndex,
marketType,
slot,
oraclePriceData,
}: {
marketIndex: number;
marketType: MarketType;
slot: number;
oraclePriceData: OraclePriceData;
}): L3OrderBook {
const bids: L3Level[] = [];
const asks: L3Level[] = [];

const restingAsks = this.getRestingLimitAsks(
marketIndex,
slot,
marketType,
oraclePriceData
);

for (const ask of restingAsks) {
asks.push({
price: ask.getPrice(oraclePriceData, slot),
size: ask.order.baseAssetAmount.sub(ask.order.baseAssetAmountFilled),
maker: ask.userAccount,
orderId: ask.order.orderId,
});
}

const restingBids = this.getRestingLimitBids(
marketIndex,
slot,
marketType,
oraclePriceData
);

for (const bid of restingBids) {
bids.push({
price: bid.getPrice(oraclePriceData, slot),
size: bid.order.baseAssetAmount.sub(bid.order.baseAssetAmountFilled),
maker: bid.userAccount,
orderId: bid.order.orderId,
});
}

return {
bids,
asks,
};
}
}
4 changes: 3 additions & 1 deletion sdk/src/dlob/DLOBApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ export class DLOBApiClient {
public async getDLOB(slot: number): Promise<DLOB> {
const r = await fetch(this.url);
if (!r.ok) {
throw new Error(`Failed to fetch DLOB from ${this.url}`);
throw new Error(
`Failed to fetch DLOB from ${this.url}. Status: ${r.status}, ${r.statusText}`
);
}

const resp = await r.json();
Expand Down
Loading

0 comments on commit 2f533ea

Please sign in to comment.