Skip to content

Commit ef81440

Browse files
committed
Add mid to stock quotes
1 parent 5fb38df commit ef81440

File tree

11 files changed

+204
-8
lines changed

11 files changed

+204
-8
lines changed

.changeset/violet-zoos-walk.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@chainlink/dxfeed-adapter': minor
3+
'@chainlink/finage-adapter': minor
4+
---
5+
6+
Add mid to stock quotes

packages/sources/dxfeed/src/endpoint/stock-quotes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export type BaseEndpointTypes = {
1414
Response: {
1515
Result: null
1616
Data: {
17+
mid_price: number
1718
bid_price: number
1819
bid_volume: number
1920
ask_price: number

packages/sources/dxfeed/src/transport/stock-quotes.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { BaseEndpointTypes } from '../endpoint/stock-quotes'
22
import { buildWsTransport } from './ws'
33

44
const eventSymbolIndex = 0
5+
const bidTimeIndex = 4
56
const bidPriceIndex = 6
67
const bidSizeIndex = 7
8+
const askTimeIndex = 8
79
const askPriceIndex = 10
810
const askSizeIndex = 11
911

@@ -16,17 +18,39 @@ export const transport = buildWsTransport<BaseEndpointTypes>(
1618

1719
const data = message[0].data[1]
1820

21+
const bidPrice = Number(data[bidPriceIndex])
22+
const askPrice = Number(data[askPriceIndex])
23+
24+
let midPrice: number
25+
26+
if (bidPrice == 0) {
27+
midPrice = askPrice
28+
} else if (askPrice == 0) {
29+
midPrice = bidPrice
30+
} else {
31+
midPrice =
32+
(bidPrice * Number(data[bidSizeIndex]) + askPrice * Number(data[askSizeIndex])) /
33+
(Number(data[bidSizeIndex]) + Number(data[askSizeIndex]))
34+
}
35+
1936
return [
2037
{
2138
params: { base: data[eventSymbolIndex] },
2239
response: {
2340
result: null,
2441
data: {
25-
bid_price: Number(data[bidPriceIndex]),
42+
mid_price: midPrice,
43+
bid_price: bidPrice,
2644
bid_volume: Number(data[bidSizeIndex]),
27-
ask_price: Number(data[askPriceIndex]),
45+
ask_price: askPrice,
2846
ask_volume: Number(data[askSizeIndex]),
2947
},
48+
timestamps: {
49+
providerIndicatedTimeUnixMs: Math.max(
50+
Number(data[bidTimeIndex]),
51+
Number(data[askTimeIndex]),
52+
),
53+
},
3054
},
3155
},
3256
]

packages/sources/dxfeed/test/integration/__snapshots__/adapter-ws.test.ts.snap

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,58 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`websocket quote endpoint should return ask when bid is 0 1`] = `
4+
{
5+
"data": {
6+
"ask_price": 172,
7+
"ask_volume": 100,
8+
"bid_price": 0,
9+
"bid_volume": 148,
10+
"mid_price": 172,
11+
},
12+
"result": null,
13+
"statusCode": 200,
14+
"timestamps": {
15+
"providerDataReceivedUnixMs": 1018,
16+
"providerDataStreamEstablishedUnixMs": 1010,
17+
"providerIndicatedTimeUnixMs": 1670868378000,
18+
},
19+
}
20+
`;
21+
22+
exports[`websocket quote endpoint should return bid when ask is 0 1`] = `
23+
{
24+
"data": {
25+
"ask_price": 0,
26+
"ask_volume": 100,
27+
"bid_price": 170,
28+
"bid_volume": 148,
29+
"mid_price": 170,
30+
},
31+
"result": null,
32+
"statusCode": 200,
33+
"timestamps": {
34+
"providerDataReceivedUnixMs": 1018,
35+
"providerDataStreamEstablishedUnixMs": 1010,
36+
"providerIndicatedTimeUnixMs": 1670868378000,
37+
},
38+
}
39+
`;
40+
341
exports[`websocket quote endpoint should return success 1`] = `
442
{
543
"data": {
644
"ask_price": 172,
745
"ask_volume": 100,
846
"bid_price": 170,
947
"bid_volume": 148,
48+
"mid_price": 170.80645161290323,
1049
},
1150
"result": null,
1251
"statusCode": 200,
1352
"timestamps": {
1453
"providerDataReceivedUnixMs": 1018,
1554
"providerDataStreamEstablishedUnixMs": 1010,
55+
"providerIndicatedTimeUnixMs": 1670868378000,
1656
},
1757
}
1858
`;

packages/sources/dxfeed/test/integration/adapter-ws.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('websocket', () => {
4545
// Send initial request to start background execute and wait for cache to be filled with result
4646
await testAdapter.request(quoteData)
4747
await testAdapter.request(stockData)
48-
await testAdapter.waitForCache(2)
48+
await testAdapter.waitForCache(4)
4949
})
5050

5151
afterAll(async () => {
@@ -67,5 +67,21 @@ describe('websocket', () => {
6767
const response = await testAdapter.request(quoteData)
6868
expect(response.json()).toMatchSnapshot()
6969
})
70+
71+
it('should return bid when ask is 0', async () => {
72+
const response = await testAdapter.request({
73+
base: 'NO_ASK',
74+
endpoint: 'stock_quotes',
75+
})
76+
expect(response.json()).toMatchSnapshot()
77+
})
78+
79+
it('should return ask when bid is 0', async () => {
80+
const response = await testAdapter.request({
81+
base: 'NO_BID',
82+
endpoint: 'stock_quotes',
83+
})
84+
expect(response.json()).toMatchSnapshot()
85+
})
7086
})
7187
})

packages/sources/dxfeed/test/integration/fixtures.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,25 @@ export const mockWebSocketServer = (URL: string): MockWebsocketServer => {
6262
channel: '/service/data',
6363
},
6464
]
65+
66+
const quoteReponse2 = [
67+
{
68+
data: [
69+
'Quote',
70+
['NO_BID', 0, 0, 0, 1670868378000, 'V', 0, 148.0, 1670868370000, 'V', 172.0, 100.0],
71+
],
72+
channel: '/service/data',
73+
},
74+
]
75+
const quoteReponse3 = [
76+
{
77+
data: [
78+
'Quote',
79+
['NO_ASK', 0, 0, 0, 1670868378000, 'V', 170.0, 148.0, 1670868370000, 'V', 0, 100.0],
80+
],
81+
channel: '/service/data',
82+
},
83+
]
6584
const tradeResponse = [
6685
{
6786
data: [
@@ -97,6 +116,8 @@ export const mockWebSocketServer = (URL: string): MockWebsocketServer => {
97116
)
98117
socket.on('message', () => {
99118
socket.send(JSON.stringify(quoteReponse))
119+
socket.send(JSON.stringify(quoteReponse2))
120+
socket.send(JSON.stringify(quoteReponse3))
100121
socket.send(JSON.stringify(tradeResponse))
101122
})
102123
})

packages/sources/finage/src/endpoint/stock-quotes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type BaseEndpointTypes = {
1111
Response: {
1212
Result: null
1313
Data: {
14+
mid_price: number
1415
bid_price: number
1516
bid_volume: number
1617
ask_price: number

packages/sources/finage/src/transport/stock-quotes.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,31 @@ export const transport = new WebSocketTransport<WsTransportTypes>({
4848
return []
4949
}
5050

51+
const bidPrice = isValidNumber(message.b) ? Number(message.b) : Number(message.bp)
52+
const bidVolume = Number(message.bs)
53+
const askPrice = isValidNumber(message.a) ? Number(message.a) : Number(message.ap)
54+
const askVolume = Number(message.as)
55+
56+
let midPrice: number
57+
if (bidPrice == 0) {
58+
midPrice = askPrice
59+
} else if (askPrice == 0) {
60+
midPrice = bidPrice
61+
} else {
62+
midPrice = (bidPrice * bidVolume + askPrice * askVolume) / (bidVolume + askVolume)
63+
}
64+
5165
return [
5266
{
5367
params: { base: message.s },
5468
response: {
5569
result: null,
5670
data: {
57-
bid_price: isValidNumber(message.b) ? Number(message.b) : Number(message.bp),
58-
bid_volume: Number(message.bs),
59-
ask_price: isValidNumber(message.a) ? Number(message.a) : Number(message.ap),
60-
ask_volume: Number(message.as),
71+
mid_price: midPrice,
72+
bid_price: bidPrice,
73+
bid_volume: bidVolume,
74+
ask_price: askPrice,
75+
ask_volume: askVolume,
6176
},
6277
timestamps: {
6378
providerIndicatedTimeUnixMs: message.t,

packages/sources/finage/test/integration/__snapshots__/adapter-stock-quote.test.ts.snap

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ exports[`stock quotes websocket stock quotes endpoint missing a and b fields sho
77
"ask_volume": 11,
88
"bid_price": 12,
99
"bid_volume": 13,
10+
"mid_price": 11.083333333333334,
1011
},
1112
"result": null,
1213
"statusCode": 200,
@@ -18,13 +19,52 @@ exports[`stock quotes websocket stock quotes endpoint missing a and b fields sho
1819
}
1920
`;
2021

22+
exports[`stock quotes websocket stock quotes endpoint should return ask when bid is 0 1`] = `
23+
{
24+
"data": {
25+
"ask_price": 5,
26+
"ask_volume": 6,
27+
"bid_price": 0,
28+
"bid_volume": 8,
29+
"mid_price": 5,
30+
},
31+
"result": null,
32+
"statusCode": 200,
33+
"timestamps": {
34+
"providerDataReceivedUnixMs": 1018,
35+
"providerDataStreamEstablishedUnixMs": 1010,
36+
"providerIndicatedTimeUnixMs": 7,
37+
},
38+
}
39+
`;
40+
41+
exports[`stock quotes websocket stock quotes endpoint should return bid when ask is 0 1`] = `
42+
{
43+
"data": {
44+
"ask_price": 0,
45+
"ask_volume": 6,
46+
"bid_price": 7,
47+
"bid_volume": 8,
48+
"mid_price": 7,
49+
},
50+
"result": null,
51+
"statusCode": 200,
52+
"timestamps": {
53+
"providerDataReceivedUnixMs": 1018,
54+
"providerDataStreamEstablishedUnixMs": 1010,
55+
"providerIndicatedTimeUnixMs": 5,
56+
},
57+
}
58+
`;
59+
2160
exports[`stock quotes websocket stock quotes endpoint should return success 1`] = `
2261
{
2362
"data": {
2463
"ask_price": 5,
2564
"ask_volume": 6,
2665
"bid_price": 7,
2766
"bid_volume": 8,
67+
"mid_price": 6.142857142857143,
2868
},
2969
"result": null,
3070
"statusCode": 200,

packages/sources/finage/test/integration/adapter-stock-quote.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe('stock quotes websocket', () => {
4141
// Send initial request to start background execute and wait for cache to be filled with results
4242
await testAdapter.request(data)
4343
await testAdapter.request(fallBackData)
44-
await testAdapter.waitForCache(2)
44+
await testAdapter.waitForCache(4)
4545
})
4646

4747
afterAll(async () => {
@@ -61,5 +61,21 @@ describe('stock quotes websocket', () => {
6161
const response = await testAdapter.request(fallBackData)
6262
expect(response.json()).toMatchSnapshot()
6363
})
64+
65+
it('should return bid when ask is 0', async () => {
66+
const response = await testAdapter.request({
67+
base: 'NO_ASK',
68+
endpoint: 'stock_quotes',
69+
})
70+
expect(response.json()).toMatchSnapshot()
71+
})
72+
73+
it('should return ask when bid is 0', async () => {
74+
const response = await testAdapter.request({
75+
base: 'NO_BID',
76+
endpoint: 'stock_quotes',
77+
})
78+
expect(response.json()).toMatchSnapshot()
79+
})
6480
})
6581
})

0 commit comments

Comments
 (0)