Refactorized PriceFetcher + Strategy class hierarchy
This commit is contained in:
parent
659df98e39
commit
938dd484dd
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
* Pricecaster Service.
|
||||||
|
*
|
||||||
|
* Fetcher backend component.
|
||||||
|
*
|
||||||
|
* (c) 2021 Randlabs, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PriceFetcher } from './pricefetch'
|
||||||
|
|
||||||
|
const pricefetcher = new PriceFetcher()
|
||||||
|
pricefetcher.run()
|
|
@ -1,24 +1,69 @@
|
||||||
|
/**
|
||||||
|
* Pricecaster Service.
|
||||||
|
*
|
||||||
|
* Fetcher backend component.
|
||||||
|
*
|
||||||
|
* (c) 2021 Randlabs, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
import { getPythProgramKeyForCluster, PriceData, Product, PythConnection } from '@pythnetwork/client'
|
import { getPythProgramKeyForCluster, PriceData, Product, PythConnection } from '@pythnetwork/client'
|
||||||
import {Cluster, clusterApiUrl, Connection, PublicKey} from '@solana/web3.js'
|
import { Cluster, clusterApiUrl, Connection } from '@solana/web3.js'
|
||||||
|
import algosdk from 'algosdk'
|
||||||
|
const PricecasterLib = require('../lib/pricecaster')
|
||||||
|
const settings = require('../settings')
|
||||||
|
|
||||||
const SOLANA_CLUSTER_NAME: Cluster = 'devnet'
|
/**
|
||||||
const connection = new Connection(clusterApiUrl(SOLANA_CLUSTER_NAME))
|
* The main Price fetcher service class.
|
||||||
const pythPublicKey = getPythProgramKeyForCluster(SOLANA_CLUSTER_NAME)
|
*/
|
||||||
|
export class PriceFetcher {
|
||||||
|
private pclib: any
|
||||||
|
pythConnection: PythConnection
|
||||||
|
|
||||||
const algosdk = require('algosdk')
|
constructor () {
|
||||||
|
const SOLANA_CLUSTER_NAME: Cluster = settings.pyth.solanaClusterName as Cluster
|
||||||
|
const connection = new Connection(clusterApiUrl(SOLANA_CLUSTER_NAME))
|
||||||
|
const pythPublicKey = getPythProgramKeyForCluster(SOLANA_CLUSTER_NAME)
|
||||||
|
const algodClient = new algosdk.Algodv2(settings.token, settings.api, settings.port)
|
||||||
|
this.pythConnection = new PythConnection(connection, pythPublicKey)
|
||||||
|
this.pclib = new PricecasterLib.PricecasterLib(algodClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the service.
|
||||||
|
*/
|
||||||
|
run () {
|
||||||
|
console.log('Pricecaster Service Fetcher -- (c) 2021 Randlabs.io\n')
|
||||||
|
console.log('AlgoClient Configuration: ')
|
||||||
|
console.log(`API: '${settings.api}' PORT:'${settings.port}'`)
|
||||||
|
|
||||||
|
if (this.preflightCheck()) {
|
||||||
const pythConnection = new PythConnection(connection, pythPublicKey)
|
console.log('Preflight check passed, starting Pyth listener...')
|
||||||
pythConnection.onPriceChange((product: Product, price: PriceData) => {
|
this.pythConnection.start()
|
||||||
if (product.symbol == 'BTC/USD') {
|
this.pythConnection.onPriceChange((product: Product, price: PriceData) => {
|
||||||
// sample output:
|
this.onPriceChange(product, price)
|
||||||
// SRM/USD: $8.68725 ±$0.0131
|
})
|
||||||
// tslint:disable-next-line:no-console
|
|
||||||
console.log(`${product.symbol}: $${price.price} \xB1$${price.confidence}`)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
// tslint:disable-next-line:no-console
|
console.log('Booting done.')
|
||||||
console.log("Reading from Pyth price feed...")
|
}
|
||||||
pythConnection.start()
|
|
||||||
|
/**
|
||||||
|
* Executes a preflight check of configuration parameters.
|
||||||
|
* @returns True if parameters are ok, false otherwise.
|
||||||
|
*/
|
||||||
|
preflightCheck () {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Price reception handler.
|
||||||
|
* @param product The reported symbol/pair
|
||||||
|
* @param price The reported price data
|
||||||
|
*/
|
||||||
|
onPriceChange (product: Product, price: PriceData) {
|
||||||
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
|
if (settings.symbols.hasOwnProperty(product.symbol)) {
|
||||||
|
console.log(`${product.symbol}: $${price.price} \xB1$${price.confidence}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* Pricecaster Service.
|
||||||
|
*
|
||||||
|
* Fetcher backend component.
|
||||||
|
*
|
||||||
|
* (c) 2021 Randlabs, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PriceData } from '@pythnetwork/client'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements a strategy for obtaining an asset price from
|
||||||
|
* a set of received prices in a buffer.
|
||||||
|
*/
|
||||||
|
export interface IStrategy {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param size The size of the buffer
|
||||||
|
*/
|
||||||
|
createBuffer(size: number): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear price buffer
|
||||||
|
*/
|
||||||
|
clearBuffer(): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put a new price in buffer.
|
||||||
|
* @param priceData The price data to put
|
||||||
|
* @returns true if successful.
|
||||||
|
*/
|
||||||
|
put(priceData: PriceData): boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the calculated price according to selected strategy.
|
||||||
|
*/
|
||||||
|
getPrice(): number
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* Pricecaster Service.
|
||||||
|
*
|
||||||
|
* Fetcher backend component.
|
||||||
|
*
|
||||||
|
* (c) 2021 Randlabs, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { StrategyBaseQueue } from './strategyqueuebase'
|
||||||
|
|
||||||
|
class StrategyAveragePrice extends StrategyBaseQueue {
|
||||||
|
getPrice (): number {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* Pricecaster Service.
|
||||||
|
*
|
||||||
|
* Fetcher backend component.
|
||||||
|
*
|
||||||
|
* (c) 2021 Randlabs, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PriceData } from '@pythnetwork/client'
|
||||||
|
import { IStrategy } from './strategy'
|
||||||
|
|
||||||
|
export abstract class StrategyBase implements IStrategy {
|
||||||
|
protected symbol: string
|
||||||
|
constructor (symbol: string, bufSize: number) {
|
||||||
|
this.symbol = symbol
|
||||||
|
this.createBuffer(bufSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract put(priceData: PriceData): boolean
|
||||||
|
abstract createBuffer(size: number): void
|
||||||
|
abstract clearBuffer(): void
|
||||||
|
abstract getPrice(): number
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
* Pricecaster Service.
|
||||||
|
*
|
||||||
|
* Fetcher backend component.
|
||||||
|
*
|
||||||
|
* (c) 2021 Randlabs, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PriceData } from '@pythnetwork/client'
|
||||||
|
import { StrategyBase } from './strategyBase'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base class for queue-based buffer strategies
|
||||||
|
*/
|
||||||
|
export abstract class StrategyBaseQueue extends StrategyBase {
|
||||||
|
private buffer: PriceData[]
|
||||||
|
private bufSize: number
|
||||||
|
|
||||||
|
createBuffer (maxSize: number): void {
|
||||||
|
this.bufSize = maxSize
|
||||||
|
}
|
||||||
|
|
||||||
|
clearBuffer (): void {
|
||||||
|
this.buffer.length = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
put (priceData: PriceData): boolean {
|
||||||
|
if (this.buffer.length === this.bufSize) {
|
||||||
|
this.buffer.shift()
|
||||||
|
}
|
||||||
|
this.buffer.push(priceData)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract getPrice(): number
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
declare module 'PricecasterLib';
|
|
@ -4,7 +4,7 @@
|
||||||
"description": "Experimentation with pyth",
|
"description": "Experimentation with pyth",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npx ts-node backend/pricefetch.ts",
|
"start": "npx ts-node backend/main.ts",
|
||||||
"test": "mocha --timeout 60000"
|
"test": "mocha --timeout 60000"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|
27
settings.js
27
settings.js
|
@ -1,13 +1,26 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
pyth: {
|
||||||
|
solanaClusterName: 'devnet'
|
||||||
|
},
|
||||||
token: '',
|
token: '',
|
||||||
api: 'https://api.betanet.algoexplorer.io',
|
api: 'https://api.betanet.algoexplorer.io',
|
||||||
port: '',
|
port: '',
|
||||||
'BTC/USD': {
|
symbols: {
|
||||||
appId: 3020301,
|
'BTC/USD': {
|
||||||
verifierpk: 'OPDM7ACAW64Q4VBWAL77Z5SHSJVZZ44V3BAN7W44U43SUXEOUENZMZYOQU',
|
appId: 3020301,
|
||||||
buffersize: 50,
|
verifierpk: 'OPDM7ACAW64Q4VBWAL77Z5SHSJVZZ44V3BAN7W44U43SUXEOUENZMZYOQU',
|
||||||
ratio: 'asap',
|
buffersize: 50,
|
||||||
strategy: 'maxconf',
|
ratio: 'asap',
|
||||||
phony: false
|
strategy: 'maxconf',
|
||||||
|
phony: false
|
||||||
|
},
|
||||||
|
'ETH/USD': {
|
||||||
|
appId: 3020301,
|
||||||
|
verifierpk: 'OPDM7ACAW64Q4VBWAL77Z5SHSJVZZ44V3BAN7W44U43SUXEOUENZMZYOQU',
|
||||||
|
buffersize: 50,
|
||||||
|
ratio: 'asap',
|
||||||
|
strategy: 'maxconf',
|
||||||
|
phony: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue