Refactorized PriceFetcher + Strategy class hierarchy

This commit is contained in:
Hernán Di Pietro 2021-10-05 10:52:17 -03:00
parent 659df98e39
commit 938dd484dd
9 changed files with 208 additions and 25 deletions

12
backend/main.ts Normal file
View File

@ -0,0 +1,12 @@
/**
* Pricecaster Service.
*
* Fetcher backend component.
*
* (c) 2021 Randlabs, Inc.
*/
import { PriceFetcher } from './pricefetch'
const pricefetcher = new PriceFetcher()
pricefetcher.run()

View File

@ -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}`)
}
}
}

38
backend/strategy.ts Normal file
View File

@ -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
}

View File

@ -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
}
}

23
backend/strategyBase.ts Normal file
View File

@ -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
}

View File

@ -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
}

1
lib/pricecaster.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module 'PricecasterLib';

View File

@ -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": "",

View File

@ -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
}
}
}