Skip to content
This repository was archived by the owner on Mar 14, 2024. It is now read-only.

Commit 7fd8ae7

Browse files
ali-behjatiJayant Krishnamurthy
andauthored
Fix prevPrice logic (#5)
* Fix prevPrice logic * Add CI for build and test * rename getPrevPrice to getLatestAvailablePrice - Also add getLatestAvailablePriceWithinDuration Co-authored-by: Jayant Krishnamurthy <[email protected]>
1 parent 58421e4 commit 7fd8ae7

File tree

5 files changed

+128
-16
lines changed

5 files changed

+128
-16
lines changed

.github/workflows/node.js.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Node.js CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
build:
11+
12+
runs-on: ubuntu-latest
13+
14+
strategy:
15+
matrix:
16+
node-version: [14.x, 16.x, 18.x]
17+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
18+
19+
steps:
20+
- uses: actions/checkout@v2
21+
- name: Use Node.js ${{ matrix.node-version }}
22+
uses: actions/setup-node@v2
23+
with:
24+
node-version: ${{ matrix.node-version }}
25+
cache: 'npm'
26+
- run: npm ci
27+
- run: npm run build
28+
- run: npm run lint
29+
- run: npm test

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pythnetwork/pyth-sdk-js",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "Pyth Network SDK in JS",
55
"homepage": "https://pyth.network",
66
"main": "lib/index.js",

src/__tests__/PriceFeed.test.ts

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { Price, PriceFeed, PriceStatus } from "../index";
22

3+
beforeAll(() => {
4+
jest.useFakeTimers();
5+
});
6+
37
test("Parsing Price Feed works as expected", () => {
48
const data = {
59
conf: "1",
@@ -28,11 +32,17 @@ test("Parsing Price Feed works as expected", () => {
2832
expect(priceFeed.publishTime).toBe(11);
2933
expect(priceFeed.getCurrentPrice()).toStrictEqual(new Price("1", 4, "10"));
3034
expect(priceFeed.getEmaPrice()).toStrictEqual(new Price("2", 4, "3"));
31-
expect(priceFeed.getPrevPriceUnchecked()).toStrictEqual([
32-
new Price("7", 4, "8"),
33-
9,
35+
expect(priceFeed.getLatestAvailablePriceUnchecked()).toStrictEqual([
36+
new Price("1", 4, "10"),
37+
11,
3438
]);
3539

40+
jest.setSystemTime(20000);
41+
expect(priceFeed.getLatestAvailablePriceWithinDuration(15)).toStrictEqual(
42+
new Price("1", 4, "10")
43+
);
44+
expect(priceFeed.getLatestAvailablePriceWithinDuration(5)).toBeUndefined();
45+
3646
expect(priceFeed.toJson()).toStrictEqual(data);
3747
});
3848

@@ -57,3 +67,35 @@ test("getCurrentPrice returns undefined if status is not Trading", () => {
5767
const priceFeed = PriceFeed.fromJson(data);
5868
expect(priceFeed.getCurrentPrice()).toBeUndefined();
5969
});
70+
71+
test("getLatestAvailablePrice returns prevPrice when status is not Trading", () => {
72+
const data = {
73+
conf: "1",
74+
ema_conf: "2",
75+
ema_price: "3",
76+
expo: 4,
77+
id: "abcdef0123456789",
78+
max_num_publishers: 6,
79+
num_publishers: 5,
80+
prev_conf: "7",
81+
prev_price: "8",
82+
prev_publish_time: 9,
83+
price: "10",
84+
product_id: "0123456789abcdef",
85+
publish_time: 11,
86+
status: PriceStatus.Unknown,
87+
};
88+
89+
const priceFeed = PriceFeed.fromJson(data);
90+
91+
expect(priceFeed.getLatestAvailablePriceUnchecked()).toStrictEqual([
92+
new Price("7", 4, "8"),
93+
9,
94+
]);
95+
96+
jest.setSystemTime(20000);
97+
expect(priceFeed.getLatestAvailablePriceWithinDuration(15)).toStrictEqual(
98+
new Price("7", 4, "8")
99+
);
100+
expect(priceFeed.getLatestAvailablePriceWithinDuration(10)).toBeUndefined();
101+
});

src/index.ts

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Convert, PriceFeed as JsonPriceFeed } from "./schemas/PriceFeed";
22

33
export type UnixTimestamp = number;
4+
export type DurationInSeconds = number;
45
export type HexString = string;
56

67
/**
@@ -190,9 +191,14 @@ export class PriceFeed {
190191

191192
/**
192193
* Get the current price and confidence interval as fixed-point numbers of the form a * 10^e.
194+
* This function returns the current best estimate of the price at the time that this `PriceFeed` was
195+
* published (`publish_time`). This function returns `undefined` if the oracle was unable to determine
196+
* the price at that time; this condition can happen for various reasons, such as certain markets only
197+
* trading during certain times.
193198
*
194-
* @returns a struct containing the current price, confidence interval, and the exponent for
195-
* both numbers. Returns `undefined` if price information is currently unavailable for any reason.
199+
* @returns a struct containing the price and confidence interval as of `publish_time`, along with
200+
* the exponent for both numbers. Returns `undefined` if price information is currently unavailable
201+
* for any reason.
196202
*/
197203
getCurrentPrice(): Price | undefined {
198204
if (this.status !== PriceStatus.Trading) {
@@ -216,20 +222,55 @@ export class PriceFeed {
216222
}
217223

218224
/**
219-
* Get the "unchecked" previous price with Trading status, along with the timestamp at which it was generated.
225+
* Get the latest available price, along with the timestamp when it was generated.
226+
* This function returns the same price as `getCurrentPrice` in the case where a price was available
227+
* at the time this `PriceFeed` was published (`publish_time`). However, if a price was not available
228+
* at that time, this function returns the price from the latest time at which the price was available.
229+
* The returned price can be from arbitrarily far in the past; this function makes no guarantees that
230+
* the returned price is recent or useful for any particular application.
220231
*
221-
* @returns a struct containing the previous price, confidence interval, and the exponent for
222-
* both numbers along with the timestamp that the price was generated.
232+
* Users of this function should check the returned timestamp to ensure that the returned price is
233+
* sufficiently recent for their application. If you are considering using this function, it may be
234+
* safer / easier to use either `getCurrentPrice` or `getLatestAvailablePriceWithinDuration`.
223235
*
224-
* WARNING:
225-
* We make no guarantees about the unchecked price and confidence returned by
226-
* this function: it could differ significantly from the current price.
227-
* We strongly encourage you to use `get_current_price` instead.
236+
* @returns a struct containing the latest available price, confidence interval, and the exponent for
237+
* both numbers along with the timestamp when that price was generated.
228238
*/
229-
getPrevPriceUnchecked(): [Price, UnixTimestamp] {
239+
getLatestAvailablePriceUnchecked(): [Price, UnixTimestamp] {
240+
// If the price status is Trading then it's the latest price
241+
// with the Trading status.
242+
if (this.status === PriceStatus.Trading) {
243+
return [new Price(this.conf, this.expo, this.price), this.publishTime];
244+
}
245+
230246
return [
231247
new Price(this.prevConf, this.expo, this.prevPrice),
232248
this.prevPublishTime,
233249
];
234250
}
251+
252+
/**
253+
* Get the latest price as long as it was updated within `duration` seconds of the current time.
254+
* This function is a sanity-checked version of `getLatestAvailablePriceUnchecked` which is useful in
255+
* applications that require a sufficiently-recent price. Returns `undefined` if the price wasn't
256+
* updated sufficiently recently.
257+
*
258+
* @param duration return a price as long as it has been updated within this number of seconds
259+
* @returns a struct containing the latest available price, confidence interval and the exponent for
260+
* both numbers, or `undefined` if no price update occurred within `duration` seconds of the current time.
261+
*/
262+
getLatestAvailablePriceWithinDuration(duration: DurationInSeconds): Price | undefined {
263+
const [price, timestamp] = this.getLatestAvailablePriceUnchecked();
264+
265+
const currentTime: UnixTimestamp = Math.floor(Date.now() / 1000);
266+
267+
// This checks the absolute difference as a sanity check
268+
// for the cases that the system time is behind or price
269+
// feed timestamp happen to be in the future (a bug).
270+
if (Math.abs(currentTime - timestamp) > duration) {
271+
return undefined;
272+
}
273+
274+
return price;
275+
}
235276
}

0 commit comments

Comments
 (0)