diff --git a/README.md b/README.md index 00871ab..42c52b0 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Make sure latest ruby version is installed: brew update brew install ruby ``` +(mac users will need to make sure their [`PATH` is set properly](https://mac.install.guide/ruby/13.html) to not use the default system ruby) Update gem (no `sudo`, if it fails running it again seems to work...): ``` diff --git a/source/includes/_historicaldata.md b/source/includes/_historicaldata.md index 99561b9..d2e5891 100644 --- a/source/includes/_historicaldata.md +++ b/source/includes/_historicaldata.md @@ -1,6 +1,6 @@ # Historical Data -Snapshots are collected by parsing on-chain transaction logs. For convience the below are parsed logs collected, stored as a CSV, and stored off-chain (~99% of records). +Snapshots are collected by parsing on-chain transaction logs. For convience the below are parsed logs collected, stored as a CSV, and stored off-chain (~99% of records). Please share any transaction signatures or time ranges you believe might be missing in Drift Protocol Discord. @@ -30,12 +30,41 @@ devnet: `https://drift-historical-data.s3.us-east-1.amazonaws.com/program/dRifty | marketSymbol | market name | SOL-PERP | | year | | 2023 | | month | | 4 | -| day | utc time | 25 | +| day | utc time | 25 | | candleResolution | | 1M | ## Examples +Get historical trades on `SOL-PERP` for August 5, 2023: +``` +https://drift-historical-data.s3.eu-west-1.amazonaws.com/program/dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH/market/SOL-PERP/trades/2023/8/5 +``` + +## Records Columns + +Below are definitions of the columns in each record type. + +### trades + +| variable | description | example | +| --- | --- | --- | +| accountKey | user sub account public key (not authority) | | + +### market-trades + +### funding-rates + +### funding-payments + +### deposits + +### liquidations + +### candles + +### settle-pnl-records + ```python import requests diff --git a/source/index.html.md b/source/index.html.md index 109ad79..0649b32 100644 --- a/source/index.html.md +++ b/source/index.html.md @@ -585,7 +585,7 @@ order_params = OrderParams( await drift_client.place_perp_order(order_params) ``` -Oracle market orders enable a user to define their auction params as an offset (or relative to) the oracle price. +Oracle market orders enable a user to define their auction params as an offset (or relative to) the oracle price. ## Canceling Order @@ -1078,75 +1078,115 @@ Leverage is the total liability value (borrows plus total perp position) divided # Orderbook (Blockchain) -## Slot Subscription +The drift orderbook is a collection of all open orders on the Drift protocol. There is no single source of truth for the orderbook. The most up to date view of the orderbook is one where you track user orders and maintain the orderbook structure yourself. This section shows how to do that with the various SDKs. + +## Dlob Source + +This is the main source of orders for maintaing the orderbook. + +### `UserMap` + +`UserMap` stores a complete map of all user accounts (idle users are commonly filtered ou). ```typescript - import {Connection} from "@solana/web3.js"; - import {SlotSubscriber} from "@drift-labs/sdk"; +import {Connection} from "@solana/web3.js"; +import {DriftClient, UserMap, Wallet, loadKeypair} from "@drift-labs/sdk"; - const connection = new Connection("https://api.mainnet-beta.solana.com"); - const slotSubscriber = new SlotSubscriber(connection); +const connection = new Connection("https://api.mainnet-beta.solana.com"); - await slotSubscriber.subscribe(); - const slot = slotSubscriber.getSlot(); +const keyPairFile = '~/.config/solana/my-keypair.json'; +const wallet = new Wallet(loadKeypair(privateKeyFile)) - slotSubscriber.eventEmitter.on('newSlot', async (slot) => { - console.log('new slot', slot); - }); -``` +const driftClient = new DriftClient({ + connection, + wallet, + env: 'mainnet-beta', +}); +await driftClient.subscribe(); + +// polling keep users updated with periodic calls to getProgramAccounts +// websocket keep users updated via programSubscribe +const subscriptionConfig: + | { + type: 'polling'; + frequency: number; + commitment?: Commitment; + } | { + type: 'websocket'; + resubTimeoutMs?: number; + commitment?: Commitment; + } = { + type: 'websocket', + resubTimeoutMs: 30_000, + commitment: stateCommitment, + }; -| Parameter | Description | Optional | Default | -| ----------- | ----------- | -------- | ------- | -| connection | Connection object specifying solana rpc url | No | | +const userMap = new UserMap({ + driftClient, + connection, + subscriptionConfig, + skipInitialLoad: false, // skips initial load of user accounts + includeIdle: false, // filters out idle users +}); + +await userMap.subscribe(); +``` -The slot subscriber subscribes to the latest slot and updates the slot value every time a new slot is received. The state of the orderbook is dependent on the slot value, so to build the orderbook you must keep track of the slot value. +### `OrderSubscriber` -## User Subscription +`OrderSubscriber` is a more efficient version of `UserMap`, only tracking user accounts that have orders. ```typescript - import {Connection} from "@solana/web3.js"; - import {DriftClient, UserMap, Wallet, loadKeypair} from "@drift-labs/sdk"; - - const connection = new Connection("https://api.mainnet-beta.solana.com"); +import {Connection} from "@solana/web3.js"; +import {DriftClient, OrderSubscriber, Wallet, loadKeypair} from "@drift-labs/sdk"; - const keyPairFile = '~/.config/solana/my-keypair.json'; - const wallet = new Wallet(loadKeypair(privateKeyFile)) +const connection = new Connection("https://api.mainnet-beta.solana.com"); - const driftClient = new DriftClient({ - connection, - wallet, - env: 'mainnet-beta', - }); +const keyPairFile = '~/.config/solana/my-keypair.json'; +const wallet = new Wallet(loadKeypair(privateKeyFile)) - await driftClient.subscribe(); +const driftClient = new DriftClient({ + connection, + wallet, + env: 'mainnet-beta', +}); +await driftClient.subscribe(); - const includeIdleUsers = false; - const userMap = new UserMap(driftClient, {type: 'websocket'}, false); - await userMap.subscribe(); -``` +const subscriptionConfig: + | { + type: 'polling', + frequency: ORDERBOOK_UPDATE_INTERVAL, + commitment: stateCommitment, + } + | { + type: 'websocket', + commitment: stateCommitment, + resyncIntervalMs: WS_FALLBACK_FETCH_INTERVAL, + } = { + type: 'websocket', + commitment: stateCommitment, + resyncIntervalMs: WS_FALLBACK_FETCH_INTERVAL, // periodically resyncs the orders in case of missed websocket messages + }; -| Parameter | Description | Optional | Default | -| ----------- | ----------- | -------- | ------- | -| driftClient | DriftClient object | No | | -| accountSubscription | Whether to use websocket or polling to subscribe to users | No | | -| includeIdle | Whether to include idle users. An idle user has had no orders, perp position or borrow for 7 days | Yes | | +const orderSubscriber = new OrderSubscriber({ + driftClient, + subscriptionConfig, +}); -Orders are stored on user accounts. To reconstruct the orderbook, you must keep track of the user accounts that have orders. -The user map subscribes to user account updates. +await orderSubscriber.subscribe(); +``` ## Orderbook Subscription +With a `DlobSource` you can then subscribe to the orderbook. + ```typescript import {DLOBSubscriber} from "@drift-labs/sdk"; -// on-chain subscription to users -const userMap = new UserMap(driftClient, {type: 'websocket'}, false); -await userMap.subscribe(); - const dlobSubscriber = new DLOBSubscriber({ driftClient, - dlobSource: userMap, - slotSource: slotSubscriber, + dlobSource: orderSubscriber, // or UserMap + slotSource: orderSubscriber, // or UserMap updateFrequency: 1000, }); @@ -1196,6 +1236,83 @@ const l3 = dlobSubscriber.getL3({ The L3 orderbook contains every maker order on drift dlob, including the address for the user that placed the order. +# Orderbook (Dlob Server) + +Drift runs a [`dlob-server`](https://github.com/drift-labs/dlob-server) to reduce the RPC load on UI users and traders. You can access this server (or run your own!) instead of [maintaing an order book from the blockchain](#orderbook-blockchain). + +## Polling + +The mainnet-beta http endpoint is: `https://dlob.drift.trade/` + +### Specifying a market + +All endpoints follow the same query parameter scheme to specify a market: + +| Parameter | Description | Optional | Default | +| ----------- | ----------- | -------- | ------- | +| marketName | The market name of the orderbook to get. If not set, marketIndex and marketType must be set | Yes | | +| marketIndex | The market index of the orderbook to get. If not set, marketName must be set | Yes | | +| marketType | The market type of the orderbook to get. If not set, marketName must be set | Yes | | + +### `GET /l2` and `Get /l3` + +Returns an L2/L3 orderbook for the specificed market. + +| Parameter | Description | Optional | Default | L2 only | +| ------------- | ------------------------------------------------ | -------- | ---------- | ------- | +| depth | Number of records to return per side | Yes | all orders | Yes | +| includeVamm | `true` to include vAMM liquidity in the response | Yes | `false` | Yes | +| includeOracle | `true` to include oracle data with the response | Yes | `false` | No | + +Example: https://dlob.drift.trade/l2?marketName=JTO-PERP&depth=10&includeOracle=true&includeVamm=true + +Example: https://dlob.drift.trade/l3?marketName=JTO-PERP&includeOracle=true + +### `GET /topMakers` + +Returns the top makers (currently returns an exhaustive list) for a given market (useful for `place_and_take` orders). + +| Parameter | Description | Optional | Default | +| ---------------- | ------------------------------------------------ | -------- | ---------- | +| side | Side to return makers for (`bid` or `ask`) | No | | +| limit | Limit number of makers to return | Yes | all | +| includeUserStats | `true` to include full UserStats | Yes | `false` | + +Example: https://dlob.drift.trade/topMakers?marketName=JTO-PERP&side=bid&limit=5 + +## Websocket + +The mainnet-beta websocket endpoint is: `wss://dlob.drift.trade/ws` + +The websocket server currently only sends L2 orderbook data, roughly every 1s. Ensure you have reconnect logc in place in case the connection is terminated by the server. + +```typescript +import WebSocket from 'ws'; + +const ws = new WebSocket('wss://dlob.drift.trade/ws'); +ws.on('open', async () => { + ws.send(JSON.stringify({ type: 'subscribe', marketType: 'perp', channel: 'orderbook', market: 'SOL-PERP' })); +}); + +ws.on('message', (data: WebSocket.Data) => { + try { + const message = JSON.parse(data.toString()); + console.log(`Received data from channel: ${JSON.stringify(message.channel)}`); + // book and trades data is in message.data + } catch (e) { + console.error('Invalid message:', data); + } +}); + +wsConn.on('close', () => { + console.log('Connection closed'); + // if you did not close the connection yourself, you should reconnect here +}); + +``` + + + # Events ## Event Subscription