Skip to content

Instantly share code, notes, and snippets.

@snowkidind
Last active February 26, 2023 23:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save snowkidind/9545ca43962dc1251a226031ea797b06 to your computer and use it in GitHub Desktop.
Save snowkidind/9545ca43962dc1251a226031ea797b06 to your computer and use it in GitHub Desktop.
dydx nodejs typescript wrapper WIP
const { DydxClient } = require('@dydxprotocol/v3-client')
const Web3 = require('web3')
const web31 = new Web3(process.env.RPC_NODE)
const web32 = new Web3(process.env.RPC_NODE)
const signer1 = web31.eth.accounts.wallet.add(process.env.TRADING_ZERO_PVT_KEY)
const signer2 = web32.eth.accounts.wallet.add(process.env.TRADING_ONE_PVT_KEY)
const { dateutils } = require('../utils')
const { iso8601, dateNowUTC, timeFmtDb } = dateutils
client1 = new DydxClient('https://api.dydx.exchange', {
web3: web31,
web3Provider: process.env.RPC_NODE
})
client2 = new DydxClient('https://api.dydx.exchange', {
web3: web32,
web3Provider: process.env.RPC_NODE
})
let inited = false
const init = async () => {
try {
const apiCreds1 = await client1.onboarding.recoverDefaultApiCredentials(signer1.address)
client1.apiKeyCredentials = apiCreds1
const keyPairWithYCoordinate1 = await client1.onboarding.deriveStarkKey(signer1.address)
client1.starkPrivateKey = keyPairWithYCoordinate1.privateKey
const apiCreds2 = await client2.onboarding.recoverDefaultApiCredentials(signer2.address)
client2.apiKeyCredentials = apiCreds2
const keyPairWithYCoordinate2 = await client2.onboarding.deriveStarkKey(signer2.address)
client2.starkPrivateKey = keyPairWithYCoordinate2.privateKey
inited = true
} catch (error) {
console.log(error)
console.log('WARNING: An error occurred initializing dydx client')
}
}
module.exports = {
getFills: async (market, createdBeforeOrAt, limit = 100, orderId = undefined, clientId = 1) => {
try {
if (!inited) await init()
let req = {
market: market,
limit: limit,
createdBeforeOrAt: createdBeforeOrAt,
}
if (typeof orderid !== 'undefined') {
req['orderId'] = orderId
}
if (clientId === 1) {
const resp = await client1.private.getFills(req)
return resp.fills
} else if (clientId === 2) {
const resp = await client2.private.getFills(req)
return resp.fills
}
} catch (error) {
console.log(error)
console.log('Could not retrieve fills')
}
},
/*
Get an order by id from the active orderbook and order history
NOTE: a way to retrieve an unknown orderId is to poll for fills and then call for the exact orderId from the fill
*/
getOrderById: async (orderId, clientId = 1) => {
try {
if (!inited) await init()
if (clientId === 1) {
const resp = await client1.private.getOrderById(orderId)
return resp.order
} else if (clientId === 2) {
const resp = await client2.private.getOrderById(orderId)
return resp.order
}
} catch (error) {
console.log(error)
console.log('Could not retrieve order')
}
},
/*
Get active (not filled or canceled) orders for a user, stingey rate limit
*/
getOrders: async (market, createdBeforeOrAt, limit = 100, clientId = 1) => {
try {
if (!inited) await init()
let req = {
market: market,
limit: limit,
createdBeforeOrAt: createdBeforeOrAt,
returnLatestOrders: true
}
if (clientId === 1) {
const orders = await client1.private.getOrders(req)
return orders.orders
} else if (clientId === 2) {
const orders = await client2.private.getOrders(req)
return orders.orders
}
} catch (error) {
console.log(error)
console.log('Could not retrieve orders')
}
},
/*
Get active (not filled or canceled) orders for a user, relaxed rate limit
Bug: id is not optional, and should be
*/
getActiveOrders: async (market, side, orderId, clientId = 1) => {
try {
if (!inited) await init()
if (clientId === 1) {
const resp = await client1.private.getActiveOrders(market, side, orderId)
return resp.orders
} else if (clientId === 2) {
const resp = await client2.private.getActiveOrders(market, side, orderId)
return resp.orders
}
} catch (error) {
console.log(error)
console.log('Could not retrieve orders')
}
},
/*
contains price info for index and oracle
also contains future funding rate for market
*/
getMarket: async (market) => {
try {
if (!inited) await init()
const orders = await client1.public.getMarkets(market)
return orders.markets[market]
} catch (error) {
console.log(error)
console.log('Could not retrieve market')
}
},
getHistoricalPrices: async (market, resolution, fromISO, toISO, limit) => {
function translateCandle (resolution) {
let res
switch (resolution) {
case "1d": res = "1DAY"; break;
case "4h": res = "4HOURS"; break;
case "1h": res = "1HOUR"; break;
case "30m": res = "30MINS"; break;
case "15m": res = "15MINS"; break;
case "5m": res = "5MINS"; break;
case "1m": res = "1MIN"; break;
default: res = "1DAY"
}
return res
}
try {
if (!inited) await init()
const orders = await client1.public.getCandles({ market, resolution:translateCandle(resolution), fromISO, toISO, limit})
return orders
} catch (error) {
console.log(error)
console.log('Could not retrieve historical prices')
}
},
/*
gets funding rate for the past hours
*/
getPastFundingRates: async (market, effectiveBeforeOrAt) => {
try {
if (!inited) await init()
const orders = await client1.public.getHistoricalFunding({ market, effectiveBeforeOrAt })
return orders.historicalFunding
} catch (error) {
console.log(error)
console.log('Could not retrieve funding history')
}
},
getFundingPayments: async (market, limit, effectiveBeforeOrAt, clientId = 1) => {
try {
if (!inited) await init()
if (clientId === 1) {
const payments = await client1.private.getFundingPayments({ market, limit, effectiveBeforeOrAt })
return payments.fundingPayments
} else if (clientId === 2) {
const payments = await client2.private.getFundingPayments({ market, limit, effectiveBeforeOrAt })
return payments.fundingPayments
}
} catch (error) {
console.log(error)
console.log('Could not retrieve funding payments')
}
},
getHistoricalFunding: async (market, effectiveBeforeOrAt) => {
try {
if (!inited) await init()
const funding = await client1.public.getHistoricalFunding({ market, effectiveBeforeOrAt })
return funding.historicalFunding
} catch (error) {
console.log(error)
console.log('Could not retrieve funding payments')
}
},
getAccount: async(clientId = 1) => {
try {
if (!inited) await init()
if (clientId === 1) {
const account = (await client1.private.getAccount(signer1.address)).account
return account
} else if (clientId === 2) {
const account = (await client2.private.getAccount(signer2.address)).account
return account
}
} catch (error) {
console.log(error)
console.log('Could not retrieve account')
}
},
getAccounts: async(clientId = 1) => {
try {
if (!inited) await init()
if (clientId === 1) {
const account = await client1.private.getAccounts()
if (account.accounts.length > 0) {
return account.accounts
}
} else if (clientId === 2) {
const account = await client2.private.getAccounts()
if (account.accounts.length > 0) {
return account.accounts
}
}
return []
} catch (error) {
console.log(error)
console.log('Could not retrieve account')
}
},
getPositions: async (market, status, limit, createdBeforeOrAt, clientId = 1) => {
try {
if (!inited) await init()
if (clientId === 1) {
const response = await client1.private.getPositions({
market: market,
status: status,
limit: limit,
createdBeforeOrAt: createdBeforeOrAt,
})
if (response.positions.length > 0) {
return response.positions
}
} else if (clientId === 2) {
const response = await client2.private.getPositions({
market: market,
status: status,
limit: limit,
createdBeforeOrAt: createdBeforeOrAt,
})
if (response.positions.length > 0) {
return response.positions
}
}
return []
} catch (error) {
console.log(error)
console.log('Could not retrieve account')
}
},
/**
*@description place a new order
*
* @market BTC-USD ETH-USD etc
* @side BUY SELL
* @type MARKET LIMIT STOP_LIMIT TRAILING_STOP TAKE_PROFIT
* @timeInForce GTT FOK IOC
* @postOnly true false
* @size
* @price
* @limitFee highest accepted fee for the trade
* @expiration when the order will expire if not filled
* @cancelId if the order is replacing an existing one
* @triggerPrice price the order if the order is a triggerable order
* @trailingPercent percent that the triggerPrice trails the index price of the market
* @param positionId associated with the order
*/
placeOrder: async (market, side, type, timeInForce, postOnly, size, price, expiration, clientId = 1) => {
console.log('A dydx order was received:',market, side, type, timeInForce, postOnly, size, price, expiration)
if (!inited) await init()
try {
if (clientId === 1) {
const account = (await client1.private.getAccount(signer1.address)).account
const user = (await client1.private.getUser()).user
let req = {
market: market,
side: side.toUpperCase(),
type: type.toUpperCase(),
timeInForce: timeInForce,
postOnly: postOnly,
size: size,
price: price,
limitFee: user.takerFeeRate,
expiration: expiration
}
const order = await client1.private.createOrder(req, account.positionId)
return order
} else if (clientId === 2) {
const account = (await client2.private.getAccount(signer2.address)).account
const user = (await client2.private.getUser()).user
let req = {
market: market,
side: side.toUpperCase(),
type: type.toUpperCase(),
timeInForce: timeInForce,
postOnly: postOnly,
size: size,
price: price,
limitFee: user.takerFeeRate,
expiration: expiration
}
const order = await client2.private.createOrder(req, account.positionId)
return order
}
} catch (error) {
console.log(error)
console.log('An error occurred placing an order')
console.log(Object.keys(error))
}
},
cancelOrder: async (orderId, clientId = 1) => {
try {
if (!inited) await init()
if (clientId === 1) {
const result = await client1.private.cancelOrder(orderId)
return result.cancelOrder
} else if (clientId === 2) {
const result = await client2.private.cancelOrder(orderId)
return result.cancelOrder
}
} catch (error) {
console.log(error)
console.log('Could not retrieve apiKeys')
}
},
cancelAllOrders: async (market, clientId = 1) => {
try {
if (!inited) await init()
if (clientId === 1) {
const result = await client1.private.cancelAllOrders(market)
return result
} else if (clientId === 2) {
const result = await client2.private.cancelAllOrders(market)
return result
}
} catch (error) {
console.log(error)
console.log('Could not retrieve apiKeys')
}
},
getApikeys: async(clientId = 1) => {
try {
if (!inited) await init()
if (clientId === 1) {
const keys = await client1.private.getApiKeys()
return keys.apiKeys
} else if (clientId === 2) {
const keys = await client2.private.getApiKeys()
return keys.apiKeys
}
} catch (error) {
console.log(error)
console.log('Could not retrieve apiKeys')
}
}
}
// ; (async () => {
// await init()
// const orders = await module.exports.getActiveOrders('BTC-USD', 'BUY')
// console.log(orders)
// const fills = await module.exports.getFills('BTC-USD', iso8601(dateNowUTC()), 10)
// console.log(fills)
// for (let i = 0; i < fills.length; i++) {
// const order = await module.exports.getOrderById(fills[i].orderId)
// console.log(order)
// }
// const market = await module.exports.getMarket('BTC-USD')
// console.log(market)
// const days = 86400 * 1000 * 90
// const past = iso8601(dateNowUTC() - days)
// const histo = await module.exports.getHistoricalPrices('BTC-USD', '1h', past, iso8601(dateNowUTC()), 10)
// console.log(histo)
// const funding = await module.exports.getPastFundingRates('BTC-USD', iso8601(dateNowUTC()))
// console.log(funding)
// const effectiveBeforeOrAt = iso8601(dateNowUTC())
// const fundingPayments = await module.exports.getFundingPayments('BTC-USD', 10, effectiveBeforeOrAt)
// console.log(fundingPayments)
// const account = await module.exports.getAccount()
// console.log(account)
// const accounts = await module.exports.getAccounts()
// console.log(accounts)
// const positions = await module.exports.getPositions('BTC-USD', 'OPEN', 100, iso8601(dateNowUTC()))
// console.log(positions)
// const apiKeys = await module.exports.getApikeys()
// console.log(apiKeys)
/*
// create an order setup
const market = await module.exports.getMarket('BTC-USD') // last price of btc
const user = (await client1.private.getUser()).user // get fee for user
const account = (await client1.private.getAccount(signer1.address)).account // get positionId
const plus30m = iso8601(dateNowUTC() + 1800000) // expiration time
const response = await client1.private.getPositions({
market: 'BTC-USD',
status: 'OPEN',
limit: 10,
createdBeforeOrAt: iso8601(dateNowUTC()),
})
if (response.positions.length > 0) {
let p = response.positions[0]
console.log(p)
// lets close whatever is open at market:
let side
switch (p.side) {
case "SHORT":
side = "BUY"
break;
case "LONG":
side = "SELL"
break;
default: return;
}
const order = await module.exports.newOrder('BTC-USD', side, 'MARKET', 'FOK', false, String(Math.abs(p.size)), String(Math.floor(market.indexPrice)), user.takerFeeRate, plus30m, account.positionId)
console.log(order)
}
*/
// cancel order setup
// const orders = await module.exports.getActiveOrders('BTC-USD', 'BUY')
// const result = await module.exports.cancelOrder(orders[0].id)
// console.log(result)
// const result = await module.exports.cancelAllOrders('ETH-USD')
// console.log(result)
// })()
@snowkidind
Copy link
Author

snowkidind commented Apr 3, 2022

dateutils.js

module.exports = {
  iso8601: (e) => {
    let epoch = e
    if (epoch.length === 10) epoch = e * 1000
    const date = new Date(epoch) // this unfortunately uses the local time
    let minutes = date.getMinutes()
    if (minutes < 10) minutes = '0' + String(minutes)
    let seconds = date.getSeconds()
    if (seconds < 10) seconds = '0' + String(seconds)
    let hours = date.getHours()
    let day = date.getDate()
    if (day < 10) day = '0' + String(day)
    if (hours < 10) hours = '0' + String(hours)
    let month = date.getMonth() + 1
    if (month < 10) month = '0' + month
    return (Number(date.getFullYear())) + '-' + month + '-' + day + 'T' + hours + ':' + minutes + ':' + seconds + 'Z'
  },

  dateNowUTC: () => { 
    const now = Date.now()
    const offset = Number(process.env.UTC_TZ_OFFSET) || 1
    const backInTime = now - (3600000 * offset)
    return backInTime
  },

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment