Skip to content
This repository has been archived by the owner on Jun 3, 2022. It is now read-only.

Commit

Permalink
oracle.price.aggregated.interval (#283)
Browse files Browse the repository at this point in the history
* added dftx.indexer scaffolding

* rewrote oracle into forward aggregation

* added controller

* added client oracles.ts

* fix aggregated derivation

* separated price client

* refactor index

* added price ticker

* fixed dftx indexer issues

* completed prices tests

* fixed existing test failures

* completed oracle invalidate test

* fixed update.oracle.ts uncertainty test

* Added interval indexers

* Fixed tests

* Modified interval to use time

* Aggregate if no previous entries

* Invalidate changes

* Median time is in seconds

* Added passing tests for interval indexer

* Get rid of old controller names

* Added controller functionality

* Added controller functionality

* Added whale api and tests

* Fix code smell

* Improve test coverage

* Add greater than for prices

* Sum up previous prices

* Better aggregation

* Fixed tests

* Update packages/whale-api-client/src/api/prices.ts

Co-authored-by: Fuxing Loh <[email protected]>

* Change url

* Update packages/whale-api-client/src/api/oracles.ts

Co-authored-by: Fuxing Loh <[email protected]>

* Increase limit

* Fix test

* Fix tests

* Fix test url

* Update src/module.indexer/model/dftx/set.oracle.data.ts

Co-authored-by: Ivan Lee <[email protected]>

* Convert to key

* Averaged

* Fix client tests

* Implement forward aggregation

* Comments

* Add parse int pipe

* Fix typo in tests

* Reduce indentation depth and remove spread operator

* Added invalidation

* Revised algorithm

* Split out into another indexer

* Update src/module.indexer/model/dftx/set.oracle.data.interval.ts

Co-authored-by: Fuxing Loh <[email protected]>

* Update src/module.indexer/model/dftx/set.oracle.data.interval.ts

Co-authored-by: Fuxing Loh <[email protected]>

* PR Feedback

* Fix tests

Co-authored-by: Fuxing Loh <[email protected]>
Co-authored-by: Fuxing Loh <[email protected]>
Co-authored-by: Ivan Lee <[email protected]>
  • Loading branch information
4 people authored Oct 4, 2021
1 parent 29c05c6 commit c2ac70c
Show file tree
Hide file tree
Showing 11 changed files with 550 additions and 62 deletions.
134 changes: 111 additions & 23 deletions packages/whale-api-client/__tests__/api/prices.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,34 @@ import { StubService } from '../stub.service'
import { WhaleApiClient } from '../../src'
import { StubWhaleApiClient } from '../stub.client'
import { JsonRpcClient } from '@defichain/jellyfish-api-jsonrpc'
import { PriceFeedTimeInterval } from '@whale-api-client/api/prices'

let container: MasterNodeRegTestContainer
let service: StubService
let rpcClient: JsonRpcClient
let apiClient: WhaleApiClient
describe('oracles', () => {
let container: MasterNodeRegTestContainer
let service: StubService
let rpcClient: JsonRpcClient
let apiClient: WhaleApiClient

beforeAll(async () => {
container = new MasterNodeRegTestContainer()
service = new StubService(container)
apiClient = new StubWhaleApiClient(service)
beforeAll(async () => {
container = new MasterNodeRegTestContainer()
service = new StubService(container)
apiClient = new StubWhaleApiClient(service)

await container.start()
await container.waitForReady()
await container.waitForWalletCoinbaseMaturity()
await service.start()
await container.start()
await container.waitForReady()
await container.waitForWalletCoinbaseMaturity()
await service.start()

rpcClient = new JsonRpcClient(await container.getCachedRpcUrl())
})

afterAll(async () => {
try {
await service.stop()
} finally {
await container.stop()
}
})
rpcClient = new JsonRpcClient(await container.getCachedRpcUrl())
})

describe('oracles', () => {
afterAll(async () => {
try {
await service.stop()
} finally {
await container.stop()
}
})
interface OracleSetup {
id: string
address: string
Expand Down Expand Up @@ -236,3 +236,91 @@ describe('oracles', () => {
})
})
})

describe('pricefeed with interval', () => {
let container: MasterNodeRegTestContainer
let service: StubService
let client: JsonRpcClient
let apiClient: WhaleApiClient

beforeAll(async () => {
container = new MasterNodeRegTestContainer()
service = new StubService(container)
apiClient = new StubWhaleApiClient(service)

await container.start()
await container.waitForReady()
await container.waitForWalletCoinbaseMaturity()
await service.start()

client = new JsonRpcClient(await container.getCachedRpcUrl())
})

afterAll(async () => {
try {
await service.stop()
} finally {
await container.stop()
}
})

it('should get interval', async () => {
const address = await container.getNewAddress()
const oracleId = await client.oracle.appointOracle(address, [
{ token: 'S1', currency: 'USD' }
], {
weightage: 1
})
await container.generate(1)

const oneMinute = 60
const timeNow = Math.floor(new Date().getTime() / 1000)
for (let i = 0; i < 60; i++) {
const mockTime = timeNow + i * oneMinute
const price = (i + 1).toFixed(2)
await client.oracle.setOracleData(oracleId, timeNow + 5 * 60 - 1, {
prices: [
{ tokenAmount: `${price}@S1`, currency: 'USD' }
]
})
await client.call('setmocktime', [mockTime], 'number')
await container.generate(1)
}

const height = await container.getBlockCount()
await container.generate(1)
await service.waitForIndexedHeight(height)

const noInterval = await apiClient.prices.getFeed('S1', 'USD', 60)
expect(noInterval.length).toStrictEqual(60)

const interval5Minutes = await apiClient.prices.getFeedWithInterval('S1', 'USD', PriceFeedTimeInterval.FIVE_MINUTES, 60)
expect(interval5Minutes.length).toStrictEqual(10)
expect(interval5Minutes.map(x => x.aggregated.amount)).toStrictEqual(
[
'60.00000000',
'56.50000000',
'50.50000000',
'44.50000000',
'38.50000000',
'32.50000000',
'26.50000000',
'20.50000000',
'14.50000000',
'6.00000000'
]
)

const interval10Minutes = await apiClient.prices.getFeedWithInterval('S1', 'USD', PriceFeedTimeInterval.TEN_MINUTES, 60)
expect(interval10Minutes.length).toStrictEqual(5)
expect(interval10Minutes.map(x => x.aggregated.amount)).toStrictEqual(
[
'55.00000000',
'44.00000000',
'33.00000000',
'22.00000000',
'8.50000000'
]
)
})
})
41 changes: 41 additions & 0 deletions packages/whale-api-client/src/api/prices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ import { WhaleApiClient } from '../whale.api.client'
import { ApiPagedResponse } from '../whale.api.response'
import { OraclePriceFeed } from './oracles'

/**
* Time interval for graphing
*/
export enum PriceFeedTimeInterval {
FIVE_MINUTES = 5 * 60,
TEN_MINUTES = 10 * 60,
ONE_HOUR = 60 * 60,
ONE_DAY = 24 * 60 * 60
}

/**
* DeFi whale endpoint for price related services.
*/
Expand Down Expand Up @@ -46,6 +56,11 @@ export class Prices {
return await this.client.requestList('GET', `prices/${key}/feed`, size, next)
}

async getFeedWithInterval (token: string, currency: string, interval: PriceFeedTimeInterval, size: number = 30, next?: string): Promise<ApiPagedResponse<PriceFeedInterval>> {
const key = `${token}-${currency}`
return await this.client.requestList('GET', `prices/${key}/feed/interval/${interval}`, size, next)
}

/**
* Get a list of Oracles
*
Expand Down Expand Up @@ -92,6 +107,32 @@ export interface PriceFeed {
}
}

export interface PriceFeedInterval {
id: string
key: string
sort: string

token: string
currency: string

aggregated: {
amount: string
weightage: number
count: number
oracles: {
active: number
total: number
}
}

block: {
hash: string
height: number
time: number
medianTime: number
}
}

export interface PriceOracle {
id: string
key: string
Expand Down
19 changes: 17 additions & 2 deletions src/module.api/price.controller.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { Controller, Get, Param, Query } from '@nestjs/common'
import { Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common'
import { OraclePriceAggregated, OraclePriceAggregatedMapper } from '@src/module.model/oracle.price.aggregated'
import { OracleTokenCurrencyMapper } from '@src/module.model/oracle.token.currency'
import { ApiPagedResponse } from '@src/module.api/_core/api.paged.response'
import { PaginationQuery } from '@src/module.api/_core/api.query'
import { PriceTicker, PriceTickerMapper } from '@src/module.model/price.ticker'
import { PriceOracle } from '@whale-api-client/api/prices'
import { OraclePriceFeedMapper } from '@src/module.model/oracle.price.feed'
import { OraclePriceAggregatedInterval, OraclePriceAggregatedIntervalMapper } from '@src/module.model/oracle.price.aggregated.interval'

@Controller('/prices')
export class PriceController {
constructor (
protected readonly oraclePriceAggregatedMapper: OraclePriceAggregatedMapper,
protected readonly oracleTokenCurrencyMapper: OracleTokenCurrencyMapper,
protected readonly priceTickerMapper: PriceTickerMapper,
protected readonly priceFeedMapper: OraclePriceFeedMapper
protected readonly priceFeedMapper: OraclePriceFeedMapper,
protected readonly oraclePriceAggregatedIntervalMapper: OraclePriceAggregatedIntervalMapper
) {
}

Expand Down Expand Up @@ -45,6 +47,19 @@ export class PriceController {
})
}

@Get('/:key/feed/interval/:interval')
async getFeedWithInterval (
@Param('key') key: string,
@Param('interval', ParseIntPipe) interval: number,
@Query() query: PaginationQuery
): Promise<ApiPagedResponse<OraclePriceAggregatedInterval>> {
const priceKey = `${key}-${interval}`
const items = await this.oraclePriceAggregatedIntervalMapper.query(priceKey, query.size, query.next)
return ApiPagedResponse.of(items, query.size, item => {
return item.sort
})
}

@Get('/:key/oracles')
async listPriceOracles (
@Param('key') key: string,
Expand Down
3 changes: 3 additions & 0 deletions src/module.indexer/model/dftx.indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AppointOracleIndexer } from '@src/module.indexer/model/dftx/appoint.ora
import { RemoveOracleIndexer } from '@src/module.indexer/model/dftx/remove.oracle'
import { UpdateOracleIndexer } from '@src/module.indexer/model/dftx/update.oracle'
import { SetOracleDataIndexer } from '@src/module.indexer/model/dftx/set.oracle.data'
import { SetOracleDataIntervalIndexer } from '@src/module.indexer/model/dftx/set.oracle.data.interval'
import { CreateMasternodeIndexer } from '@src/module.indexer/model/dftx/create.masternode'
import { ResignMasternodeIndexer } from '@src/module.indexer/model/dftx/resign.masternode'
import { CreateTokenIndexer } from '@src/module.indexer/model/dftx/create.token'
Expand All @@ -24,6 +25,7 @@ export class MainDfTxIndexer extends Indexer {
private readonly removeOracle: RemoveOracleIndexer,
private readonly updateOracle: UpdateOracleIndexer,
private readonly setOracleData: SetOracleDataIndexer,
private readonly setOracleDataInterval: SetOracleDataIntervalIndexer,
private readonly createMasternode: CreateMasternodeIndexer,
private readonly resignMasternode: ResignMasternodeIndexer,
private readonly createToken: CreateTokenIndexer,
Expand All @@ -38,6 +40,7 @@ export class MainDfTxIndexer extends Indexer {
setOracleData,
createMasternode,
resignMasternode,
setOracleDataInterval,
createToken,
createPoolPair,
updatePoolPair
Expand Down
2 changes: 2 additions & 0 deletions src/module.indexer/model/dftx/_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AppointOracleIndexer } from '@src/module.indexer/model/dftx/appoint.ora
import { RemoveOracleIndexer } from '@src/module.indexer/model/dftx/remove.oracle'
import { UpdateOracleIndexer } from '@src/module.indexer/model/dftx/update.oracle'
import { SetOracleDataIndexer } from '@src/module.indexer/model/dftx/set.oracle.data'
import { SetOracleDataIntervalIndexer } from '@src/module.indexer/model/dftx/set.oracle.data.interval'
import { CreateMasternodeIndexer } from '@src/module.indexer/model/dftx/create.masternode'
import { ResignMasternodeIndexer } from '@src/module.indexer/model/dftx/resign.masternode'
import { CreateTokenIndexer } from '@src/module.indexer/model/dftx/create.token'
Expand All @@ -18,6 +19,7 @@ const indexers = [
UpdateOracleIndexer,
CreateMasternodeIndexer,
ResignMasternodeIndexer,
SetOracleDataIntervalIndexer,
CreateTokenIndexer,
CreatePoolPairIndexer,
UpdatePoolPairIndexer
Expand Down
Loading

0 comments on commit c2ac70c

Please sign in to comment.