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 {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))
|
||||
const pythPublicKey = getPythProgramKeyForCluster(SOLANA_CLUSTER_NAME)
|
||||
/**
|
||||
* The main Price fetcher service class.
|
||||
*/
|
||||
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}'`)
|
||||
|
||||
|
||||
const pythConnection = new PythConnection(connection, pythPublicKey)
|
||||
pythConnection.onPriceChange((product: Product, price: PriceData) => {
|
||||
if (product.symbol == 'BTC/USD') {
|
||||
// sample output:
|
||||
// SRM/USD: $8.68725 ±$0.0131
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log(`${product.symbol}: $${price.price} \xB1$${price.confidence}`)
|
||||
if (this.preflightCheck()) {
|
||||
console.log('Preflight check passed, starting Pyth listener...')
|
||||
this.pythConnection.start()
|
||||
this.pythConnection.onPriceChange((product: Product, price: PriceData) => {
|
||||
this.onPriceChange(product, price)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log("Reading from Pyth price feed...")
|
||||
pythConnection.start()
|
||||
console.log('Booting done.')
|
||||
}
|
||||
|
||||
/**
|
||||
* 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",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "npx ts-node backend/pricefetch.ts",
|
||||
"start": "npx ts-node backend/main.ts",
|
||||
"test": "mocha --timeout 60000"
|
||||
},
|
||||
"author": "",
|
||||
|
|
27
settings.js
27
settings.js
|
@ -1,13 +1,26 @@
|
|||
module.exports = {
|
||||
pyth: {
|
||||
solanaClusterName: 'devnet'
|
||||
},
|
||||
token: '',
|
||||
api: 'https://api.betanet.algoexplorer.io',
|
||||
port: '',
|
||||
'BTC/USD': {
|
||||
appId: 3020301,
|
||||
verifierpk: 'OPDM7ACAW64Q4VBWAL77Z5SHSJVZZ44V3BAN7W44U43SUXEOUENZMZYOQU',
|
||||
buffersize: 50,
|
||||
ratio: 'asap',
|
||||
strategy: 'maxconf',
|
||||
phony: false
|
||||
symbols: {
|
||||
'BTC/USD': {
|
||||
appId: 3020301,
|
||||
verifierpk: 'OPDM7ACAW64Q4VBWAL77Z5SHSJVZZ44V3BAN7W44U43SUXEOUENZMZYOQU',
|
||||
buffersize: 50,
|
||||
ratio: 'asap',
|
||||
strategy: 'maxconf',
|
||||
phony: false
|
||||
},
|
||||
'ETH/USD': {
|
||||
appId: 3020301,
|
||||
verifierpk: 'OPDM7ACAW64Q4VBWAL77Z5SHSJVZZ44V3BAN7W44U43SUXEOUENZMZYOQU',
|
||||
buffersize: 50,
|
||||
ratio: 'asap',
|
||||
strategy: 'maxconf',
|
||||
phony: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue