Friktion
Search…
Code to read Oracle Price
Typescript on how to read Pyth oracle prices and calculate TWAp
Here is a snippet of code in typescript on how you can calculate a 30 minute TWAP of the Pyth oracle prices:
import { Cluster, clusterApiUrl, Connection, PublicKey } from '@solana/web3.js'
import { PythConnection } from './PythConnection'
import { getPythProgramKeyForCluster } from './cluster'
import { PriceStatus, Product, PriceData, Price} from '.'
import { readFileSync } from 'fs';
​
const SOLANA_CLUSTER_NAME: Cluster = 'mainnet-beta'
const connection = new Connection(clusterApiUrl(SOLANA_CLUSTER_NAME))
const pythPublicKey = getPythProgramKeyForCluster(SOLANA_CLUSTER_NAME)
​
const pythConnection = new PythConnection(connection, pythPublicKey)
​
var END_TIMESTAMP = Date.now();
var day = 0;
if (new Date(END_TIMESTAMP).getUTCDay() == 4) {
day = 4;
}
else {
day = 5;
}
END_TIMESTAMP = new Date(new Date(END_TIMESTAMP).setDate(new Date(END_TIMESTAMP).getUTCDate() + (((day + 7 - new Date(END_TIMESTAMP).getUTCDay()) % 7) || 7))).setUTCHours(0, 0, 0, 0) + 2 * 60 * 60 * 1000;
​
console.log(END_TIMESTAMP);
const WINDOW_TIME = 60 * 60 * 1000 ;
​
export type PriceEntry = {
timestamp: number;
priceData: PriceData;
}
​
export class PythTwap {
symbol: string;
latestTimestamp: number;
windowLength: number;
data: PriceEntry[];
​
constructor(
symbol: string,
windowLength: number
) {
this.symbol = symbol;
this.windowLength = windowLength;
this.latestTimestamp = 0;
this.data = [];
}
​
process_data(price: PriceData , latestTimestamp: number) {
if (price == null) {
return;
}
if (!this.check_valid(price)) {
return;
}
this.latestTimestamp = latestTimestamp;
this.data.push({timestamp: latestTimestamp, priceData: price});
}
​
check_valid(price: PriceData): boolean {
if (price == null || price.price == null || price.confidence == null) {
return false;
}
else if (price.price <= 0) {
return false;
}
else if (price.confidence/price.price > 0.25) {
return false;
}
return true;
}
​
calc_twap(): [number, number] {
const threshold = this.latestTimestamp - this.windowLength;
const prices = this.data.filter((obj) => (obj.timestamp > threshold) && obj.priceData.price !== undefined && obj.priceData.price !== null)
const twap = prices.reduce((a: number, b: PriceEntry) => a + b.priceData.price!, 0)/prices.length;
return [twap, prices.length];
}
}
​
async function checkEnd(Oracles: Map<string, PythTwap>) {
const time = Date.now();
if (time > END_TIMESTAMP) {
pythConnection.stop();
console.log("Twap Calcuation Over. Stopping...");
Oracles.forEach((oracle, symbol) => console.log(time, symbol, oracle.calc_twap()));
return true;
}
return false;
}
​
async function checkEndTime(Oracles: Map<string, PythTwap>) {
const timeout = (time: number) => new Promise(resolve => setTimeout(resolve, time));
while (true) {
const shouldStop = await timeout(15 * 1000).then(() => checkEnd(Oracles));
if (shouldStop) {
return process.exit(0);
}
}
}
​
function main() {
const file = readFileSync('./oracle_symbols.txt', 'utf-8');
const symbols: string[] = file.split("\n");
const Oracles = new Map<string, PythTwap>();
symbols.forEach((symbol) => Oracles.set(symbol, new PythTwap(symbol, WINDOW_TIME)));
​
console.log(symbols);
console.log("End Timestamp: ", END_TIMESTAMP);
console.log("TWAP Length: ", WINDOW_TIME/1000/1000/60);
​
pythConnection.onPriceChange((product, price) => {
// sample output:
// SRM/USD: $8.68725 ±$0.0131
if (price.price && price.confidence) {
// tslint:disable-next-line:no-console
const parsed_symbol = product.symbol.split(".")[1].split("/")[0];
const time = Date.now();
Oracles.get(parsed_symbol)?.process_data(price, time);
console.log(`${time} ${parsed_symbol} ${price.price} ${price.confidence}`);
}
});
​
console.log('Reading from Pyth price feed...')
pythConnection.start();
checkEndTime(Oracles);
}
​
main()
Copy link