Better and dynamic priceFeedMapping

This commit is contained in:
Ralfs 2021-06-03 14:52:35 +03:00
parent fbba3cc410
commit 552444f773
4 changed files with 210 additions and 121 deletions

View File

@ -22,7 +22,7 @@
"blockworks"
]
},
"sol:usd": {
"btc:usdt": {
"decimals": 2,
"minSubmissions": 1,
"maxSubmissions": 13,
@ -33,7 +33,7 @@
"blockworks"
]
},
"srm:usd": {
"eth:usdt": {
"decimals": 2,
"minSubmissions": 1,
"maxSubmissions": 13,
@ -43,6 +43,28 @@
"oracles": [
"blockworks"
]
},
"sol:usd": {
"decimals": 4,
"minSubmissions": 1,
"maxSubmissions": 13,
"restartDelay": 0,
"rewardAmount": 10000,
"rewardTokenAccount": "3oLHHTaRqNsuTMjsTtkVy8bock6Bx8gCmDxku4TurVj1",
"oracles": [
"blockworks"
]
},
"srm:usd": {
"decimals": 4,
"minSubmissions": 1,
"maxSubmissions": 13,
"restartDelay": 0,
"rewardAmount": 10000,
"rewardTokenAccount": "3oLHHTaRqNsuTMjsTtkVy8bock6Bx8gCmDxku4TurVj1",
"oracles": [
"blockworks"
]
}
},
"oracles": {

View File

@ -1,6 +1,6 @@
{
"aggregators": {
"btc:usd": {
"btc:usdt": {
"pubkey": {
"type": "PublicKey",
"base58": "6Xvk6VC423bbhwnCfMyPfE4C1vytoqsVMUY1Lbqeh6pf"
@ -10,7 +10,7 @@
"base58": "GXdZLSvnreqyUd92tXFShpF8DPvpqDBRkukAtraZ3rCf"
},
"config": {
"description": "btc:usd",
"description": "btc:usdt",
"decimals": 2,
"minSubmissions": 1,
"maxSubmissions": 13,
@ -34,7 +34,7 @@
}
}
},
"eth:usd": {
"eth:usdt": {
"pubkey": {
"type": "PublicKey",
"base58": "4CoKvk3NUXYiHKGbQvihadw6TC8LTN1qjfadPcsaURbW"
@ -44,7 +44,7 @@
"base58": "GXdZLSvnreqyUd92tXFShpF8DPvpqDBRkukAtraZ3rCf"
},
"config": {
"description": "eth:usd",
"description": "eth:usdt",
"decimals": 2,
"minSubmissions": 1,
"maxSubmissions": 13,
@ -68,17 +68,17 @@
}
}
},
"sol:usd": {
"btc:usd": {
"pubkey": {
"type": "PublicKey",
"base58": "Ej5FrNjhXaePK7cVMZtSooatzXMeunNsjxrnubefEyNC"
"base58": "FuEnReoxhqW8Li6EMLoaaUWbWAEjTfSRuBARo5GrGCqN"
},
"owner": {
"type": "PublicKey",
"base58": "GXdZLSvnreqyUd92tXFShpF8DPvpqDBRkukAtraZ3rCf"
},
"config": {
"description": "sol:usd",
"description": "btc:usd",
"decimals": 2,
"minSubmissions": 1,
"maxSubmissions": 13,
@ -93,7 +93,75 @@
"blockworks": {
"pubkey": {
"type": "PublicKey",
"base58": "4fuTrajqBHGGM1ig4b57DD9JxcN5Q2MmoDVnMtnucjS7"
"base58": "AhD1vCnNBgECpoBrfsqP83KSZsd4TtrWvP3FgebUDoRu"
},
"owner": {
"type": "PublicKey",
"base58": "GXdZLSvnreqyUd92tXFShpF8DPvpqDBRkukAtraZ3rCf"
}
}
}
},
"eth:usd": {
"pubkey": {
"type": "PublicKey",
"base58": "GzfYWGM1oeVrha9zvM1awnTJEUAuinpnVRUyYQYELzqg"
},
"owner": {
"type": "PublicKey",
"base58": "GXdZLSvnreqyUd92tXFShpF8DPvpqDBRkukAtraZ3rCf"
},
"config": {
"description": "eth:usd",
"decimals": 2,
"minSubmissions": 1,
"maxSubmissions": 13,
"restartDelay": 0,
"rewardTokenAccount": {
"type": "PublicKey",
"base58": "3oLHHTaRqNsuTMjsTtkVy8bock6Bx8gCmDxku4TurVj1"
},
"rewardAmount": 10000
},
"oracles": {
"blockworks": {
"pubkey": {
"type": "PublicKey",
"base58": "HhBmYDPRzJDCbtsTcaB2j6sbpcNDK41CUciev94eroUE"
},
"owner": {
"type": "PublicKey",
"base58": "GXdZLSvnreqyUd92tXFShpF8DPvpqDBRkukAtraZ3rCf"
}
}
}
},
"sol:usd": {
"pubkey": {
"type": "PublicKey",
"base58": "AshULbjkGvse8YW2ojjeqHdMbFGigLy2xxiGVhsLqX5T"
},
"owner": {
"type": "PublicKey",
"base58": "GXdZLSvnreqyUd92tXFShpF8DPvpqDBRkukAtraZ3rCf"
},
"config": {
"description": "sol:usd",
"decimals": 4,
"minSubmissions": 1,
"maxSubmissions": 13,
"restartDelay": 0,
"rewardTokenAccount": {
"type": "PublicKey",
"base58": "3oLHHTaRqNsuTMjsTtkVy8bock6Bx8gCmDxku4TurVj1"
},
"rewardAmount": 10000
},
"oracles": {
"blockworks": {
"pubkey": {
"type": "PublicKey",
"base58": "2K38TaiEJEiPZY3TGKjuLMoHgZgXxX7YX2wRD2WvfUDM"
},
"owner": {
"type": "PublicKey",
@ -105,7 +173,7 @@
"srm:usd": {
"pubkey": {
"type": "PublicKey",
"base58": "GR9tYpi8CM8u8sdRaJZoP32KoWBphyoWV3xoNt4XwmRV"
"base58": "B3nWGxqNQzJeRfpYSXU8qJaTQxspZmqAt91FRAhfoFQL"
},
"owner": {
"type": "PublicKey",
@ -113,7 +181,7 @@
},
"config": {
"description": "srm:usd",
"decimals": 2,
"decimals": 4,
"minSubmissions": 1,
"maxSubmissions": 13,
"restartDelay": 0,
@ -127,7 +195,7 @@
"blockworks": {
"pubkey": {
"type": "PublicKey",
"base58": "8tn58rnF1QzPqQea4xegck556s45DUGBJvWLNdsLpT69"
"base58": "CsyV4JuPjdYRusw4sa7Ba8Ab8yR7vjyp4wAPuC2neejS"
},
"owner": {
"type": "PublicKey",
@ -141,4 +209,4 @@
"type": "PublicKey",
"base58": "Agc6kwhaMw6rmAtfL7jfaFx1Su4QbjGBD5M42YwetqDe"
}
}
}

View File

@ -16,22 +16,44 @@ import { Submitter, SubmitterConfig } from "./Submitter"
import { log } from "./log"
import { conn } from "./context"
// 0 => Coinbase, BTC-USD
// 1 => BitStamp, live_trades_btcusd
// 2 => FTX, BTC/USD
// 3 => Binance, BTCBUSD
// 4 => OKEx, BTC-USDC
const priceFeedMapping = {
'btc:usdt': {
minValueChangeForNewRound: 5000,
useFeeds: [0,2,3,4],
pairNames: ['BTC-USDT', 'BTC/USDT', 'BTCUSDT', 'BTC-USDT']
},
'btc:usd': {
minValueChangeForNewRound: 5000,
useFeeds: [0,1,2,3]
useFeeds: [0,1,2,3,4],
pairNames: ['BTC-USD', 'live_trades_btcusd', 'BTC/USD', 'BTCBUSD', 'BTC-USDC']
},
'eth:usdt': {
minValueChangeForNewRound: 150,
useFeeds: [0,2,3,4],
pairNames: ['ETH-USDT', 'ETH/USDT', 'ETHUSDT', 'ETH-USDT']
},
'eth:usd': {
minValueChangeForNewRound: 150,
useFeeds: [0,1,2,3]
useFeeds: [0,1,2,3,4],
pairNames: ['ETH-USD', 'live_trades_ethusd', 'ETH/USD', 'ETHBUSD', 'ETH-USDC']
},
'sol:usd': {
minValueChangeForNewRound: 4,
useFeeds: [2,3,4]
useFeeds: [2,3,4],
pairNames: ['SOL/USD', 'SOLBUSD', 'SOL-USDT']
// uses USDT for OKEx
},
'srm:usd': {
minValueChangeForNewRound: 1,
useFeeds: [2,3,4]
useFeeds: [2,3,4],
pairNames: ['SRM/USD', 'SRMBUSD', 'SRM-USDT']
// uses USDT for OKEx
},
}
@ -77,7 +99,7 @@ export class PriceFeeder {
}
const useFeeds = (priceFeedMapping[name]) ? priceFeedMapping[name].useFeeds.map(x => this.feeds[x]) : this.feeds;
const feed = new AggregatedFeed(useFeeds, name)
const feed = new AggregatedFeed(useFeeds, priceFeedMapping[name].pairNames, name)
const priceFeed = feed.medians()
const minValueChangeForNewRound = priceFeedMapping[name].minValueChangeForNewRound || 100

View File

@ -97,6 +97,58 @@ export abstract class PriceFeed {
abstract handleSubscribe(pair: string): Promise<void>
}
export class CoinBase extends PriceFeed {
protected log = log.child({ class: CoinBase.name })
protected baseurl = "wss://ws-feed.pro.coinbase.com"
parseMessage(data) {
const payload = JSON.parse(data)
// {
// "type": "ticker",
// "sequence": 22772426228,
// "product_id": "BTC-USD",
// "price": "53784.59",
// "open_24h": "58795.78",
// "volume_24h": "35749.39437842",
// "low_24h": "53221",
// "high_24h": "58799.66",
// "volume_30d": "733685.27275521",
// "best_bid": "53784.58",
// "best_ask": "53784.59",
// "side": "buy",
// "time": "2021-03-16T06:26:06.791440Z",
// "trade_id": 145698988,
// "last_size": "0.00474597"
// }
if (payload.type != "ticker") {
return
}
const pair = payload.product_id as string
const price: IPrice = {
source: CoinBase.name,
pair,
decimals: 2,
value: Math.floor(payload.price * 100),
}
return price
}
async handleSubscribe(pair: string) {
this.conn.send(
JSON.stringify({
type: "subscribe",
product_ids: [pair],
channels: ["ticker"],
})
)
}
}
export class BitStamp extends PriceFeed {
protected log = log.child({ class: BitStamp.name })
protected baseurl = "wss://ws.bitstamp.net"
@ -125,10 +177,7 @@ export class BitStamp extends PriceFeed {
return
}
const channel = (payload.channel as string).replace("live_trades_", "")
// assume that the base symbol for the pair is 3 letters
const pair = channel.slice(0, 3) + ":" + channel.slice(3)
const pair = payload.channel as string
const price: IPrice = {
source: BitStamp.name,
@ -141,14 +190,11 @@ export class BitStamp extends PriceFeed {
}
async handleSubscribe(pair: string) {
// "btc:usd" => "BTCUSD"
const targetPair = pair.replace(":", "").toUpperCase()
this.conn.send(
JSON.stringify({
event: "bts:subscribe",
data: {
channel: `live_trades_${targetPair.replace("/", "").toLowerCase()}`,
channel: pair,
},
})
)
@ -180,7 +226,7 @@ export class FTX extends PriceFeed {
return
}
const pair = (payload.market as string).replace("/", ":").toLowerCase()
const pair = payload.market as string
const price: IPrice = {
source: FTX.name,
@ -193,70 +239,11 @@ export class FTX extends PriceFeed {
}
async handleSubscribe(pair: string) {
// "btc:usd" => "BTC-USD"
const targetPair = pair.replace(":", "/").toUpperCase()
this.conn.send(
JSON.stringify({
op: "subscribe",
channel: "ticker",
market: targetPair,
})
)
}
}
export class CoinBase extends PriceFeed {
protected log = log.child({ class: CoinBase.name })
protected baseurl = "wss://ws-feed.pro.coinbase.com"
parseMessage(data) {
const payload = JSON.parse(data)
// {
// "type": "ticker",
// "sequence": 22772426228,
// "product_id": "BTC-USD",
// "price": "53784.59",
// "open_24h": "58795.78",
// "volume_24h": "35749.39437842",
// "low_24h": "53221",
// "high_24h": "58799.66",
// "volume_30d": "733685.27275521",
// "best_bid": "53784.58",
// "best_ask": "53784.59",
// "side": "buy",
// "time": "2021-03-16T06:26:06.791440Z",
// "trade_id": 145698988,
// "last_size": "0.00474597"
// }
if (payload.type != "ticker") {
return
}
// "BTC-USD" => "btc:usd"
const pair = (payload.product_id as string).replace("-", ":").toLowerCase()
const price: IPrice = {
source: CoinBase.name,
pair,
decimals: 2,
value: Math.floor(payload.price * 100),
}
return price
}
async handleSubscribe(pair: string) {
// "btc:usd" => "BTC-USD"
const targetPair = pair.replace(":", "-").toUpperCase()
this.conn.send(
JSON.stringify({
type: "subscribe",
product_ids: [targetPair],
channels: ["ticker"],
market: pair,
})
)
}
@ -286,11 +273,8 @@ export class Binance extends PriceFeed {
if (payload.e != "trade") {
return
}
// "btcbusd" => "btc:usd"
// assume that the base symbol for the pair is 3 letters
const baseCurrency = payload.s.slice(0, 3).toLowerCase();
const quoteCurrency = payload.s.slice(3).toLowerCase();
const pair = `${baseCurrency}:${quoteCurrency == 'busd' ? 'usd' : quoteCurrency}`;
const pair = payload.s;
const price: IPrice = {
@ -304,9 +288,7 @@ export class Binance extends PriceFeed {
}
async handleSubscribe(pair: string) {
// "btc:usd" => "btcbusd"
const [baseCurrency, quoteCurrency] = pair.split(':')
const targetPair = `${baseCurrency}${(quoteCurrency.toLowerCase() === 'usd' ? 'busd' : quoteCurrency)}@trade`.toLowerCase()
const targetPair = `${pair}@trade`.toLowerCase()
this.conn.send(
JSON.stringify({
method: "SUBSCRIBE",
@ -354,10 +336,8 @@ export class OKEx extends PriceFeed {
return
}
// "BTC-USDT" => "btc:usd"
const [baseCurrency, quoteCurrency] = (payload.data[0].instrument_id as string).toLowerCase().split('-');
// assume that quote is always any form of usd/usdt/usdc so map to usd
const pair = `${baseCurrency}:${quoteCurrency.slice(0, 3)}`;
const pair = payload.data[0].instrument_id as string;
const price: IPrice = {
source: OKEx.name,
pair,
@ -369,9 +349,7 @@ export class OKEx extends PriceFeed {
}
async handleSubscribe(pair: string) {
// "btc:usd" => "BTC-USDT"
const [baseCurrency, quoteCurrency] = pair.split(':')
const targetPair = `spot/ticker:${baseCurrency.toUpperCase()}-${(quoteCurrency.toLowerCase() === 'usd' ? 'USDT' : quoteCurrency)}`
const targetPair = `spot/ticker:${pair}`
this.conn.send(
JSON.stringify({
"op": "subscribe",
@ -388,31 +366,30 @@ export class AggregatedFeed {
public prices: IPrice[] = []
// assume that the feeds are already connected
constructor(public feeds: PriceFeed[], public pair: string) {
constructor(public feeds: PriceFeed[], public pairMappings: string[], public pair: string) {
this.subscribe()
}
private subscribe() {
const pair = this.pair
const pairMappings = this.pairMappings;
let i = 0
for (let feed of this.feeds) {
feed.subscribe(pair)
let j = 0
const index = i
i++
// store the price updates in the ith position of `this.prices`
feed.emitter.on(UPDATE, (price: IPrice) => {
if (price.pair != pair) {
return
}
price.timestamp = Date.now()
this.prices[index] = price
this.onPriceUpdate(price)
})
for (let i = 0; i < this.feeds.length; i++) {
const feed = this.feeds[i];
feed.subscribe(pairMappings[i]);
const index = j;
j++;
// store the price updates in the ith position of `this.prices`
feed.emitter.on(UPDATE, (price: IPrice) => {
if (price.pair != pairMappings[i]) {
return
}
price.timestamp = Date.now()
this.prices[index] = price
this.onPriceUpdate(price)
})
}
}