@openfeed/sdk-js 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,217 @@
1
+ # TS/JS SDK for Barchart OpenFeed
2
+
3
+ ## Core Concepts
4
+
5
+ There are only two objects that you will need in order to work with OpenFeed: a connection client (represented by the `IOpenFeedClient` interface) and a listener.
6
+
7
+ The connecton client object will connect to the OpenFeed servers and maintain that connection until disposed. It can be used to send requests to subscribe to symbols and exchanges.
8
+
9
+ The listener object (represented by the `OpenFeedListeners` class) contains five callback delegates that you can wire to your own callback functions in order to process messages coming from the connection.
10
+
11
+ ## The Listeners
12
+
13
+ The listener object, represented by the `OpenFeedListeners` class is dead-simple, just five variables, that point to functions that handle the connected, disconnected, credentials rejected and new message events sent from the connection client.
14
+
15
+ ```ts
16
+ class OpenFeedListeners {
17
+ public onConnected: (connection: IOpenFeedConnection) => void | Promise<void> = () => {};
18
+ public onCredentialsRejected: () => void | Promise<void> = () => {};
19
+ public onDisconnected: () => void | Promise<void> = () => {};
20
+ public onMessage: (message: OpenfeedGatewayMessage) => void | Promise<void> = () => {};
21
+ public onMessageWithMetadata: (
22
+ message: OpenfeedGatewayMessage,
23
+ symbolNames: string[],
24
+ instrument?: InstrumentDefinition
25
+ ) => void | Promise<void> = () => {};
26
+ }
27
+ ```
28
+
29
+ Each of the delegates contained in the listeners is either a void or returns a Promise. This allows your callback functions to be asynchronous if needed.
30
+ Here is an example of how you might implement a super-simple listener that simply outputs what's happening to the console:
31
+
32
+ ```ts
33
+ const listeners = new OpenFeedListeners();
34
+ listeners.onConnected = () => {
35
+ console.log("Connected");
36
+ };
37
+ listeners.onDisconnected = () => {
38
+ console.log("Disconnected");
39
+ };
40
+ listeners.onMessageWithMetadata = (msg, symbolNames, definition) => {
41
+ console.log(`Symbols: ${symbolNames.join(",")}\r\nMessage: ${JSON.stringify(msg)}\r\n\r\nDefinition: ${JSON.stringify(definition)}`);
42
+ };
43
+ listeners.onCredentialsRejected = () => {
44
+ console.log("Credentials Rejected");
45
+ };
46
+ ```
47
+
48
+ The first callback we wired was `onConnected`. It simply outputs "Connected." to the console.
49
+
50
+ The `onDisconnected` callback is similar, it simply outputs "Disconnected.". This will let you know that the connection you might have saved when `onConnected` occurred is no longer valid.
51
+
52
+ The `onMessageWithMetadata` callback will be called when a new message is received from the OpenFeed servers. For this use-case we connect the message with the symbol definition and symbol names in the background. This wiring uses the `onMessage` callback that's listed above in the background. If you point the `onMessage` callback to a different function the `onMessageWithMetadata` callback will not be called.
53
+
54
+ The last callback we wired is `onCredentialsRejected`, which will be called if you attempt to connect with wrong credentials or there was a second login with the same credentials. This is a terminal state - if you receive this callbacks no other callbacks will be called and no other connection attempts will be made. The only sensible thing to do in this case is to dispose the connection client object and then create a new one with the correct credentials.
55
+
56
+ It is OK not to implement all these callbacks as they all have an existing null implementation.
57
+
58
+ ### Asynchronous Handlers
59
+
60
+ Callbacks can also be implemented as asynchronous handlers. Say you want to print the message to the console after 2 seconds. The `onMessage` callback could then look like this:
61
+
62
+ ```ts
63
+ function resolveAfter2Seconds() {
64
+ return new Promise((resolve) => {
65
+ setTimeout(() => {
66
+ resolve("resolved");
67
+ }, 2000);
68
+ });
69
+ }
70
+ listeners.onMessage = async (msg) => {
71
+ await resolveAfter2Seconds();
72
+ console.log(JSON.stringify(msg));
73
+ };
74
+ ```
75
+
76
+ ## Connection Client
77
+
78
+ The connection client is represented by the `IOpenfeedClient` interface:
79
+
80
+ ```ts
81
+ interface IOpenFeedClient {
82
+ get connection(): Promise<IOpenfeedConnection>;
83
+
84
+ subscribe: (
85
+ service: Service,
86
+ subscriptionType: SubscriptionType,
87
+ symbols: string[] | null,
88
+ marketIds: Long[] | null,
89
+ exchanges: string[] | null,
90
+ channels: number[] | null
91
+ ) => Long;
92
+ unsubscribe: (subscriptionId: Long) => void;
93
+ }
94
+ ```
95
+
96
+ To create a new `IOpenFeedClient` object you can use the `OpenFeedClient` constructor:
97
+
98
+ ```ts
99
+ const url = "wss://openfeed.aws.barchart.com/ws";
100
+ const username = "<username>";
101
+ const password = "<password>";
102
+ const client = new OpenFeedClient(url, username, password, listeners, logger);
103
+
104
+ // when done
105
+ client.dispose();
106
+ ```
107
+
108
+ The first argument is the URL of the OpenFeed server, which is typically `"wss://openfeed.aws.barchart.com/ws"`. The second argument is the username, the third is the password, the fourth is the listeners object which we learned to set up in the previous section, and the fifth one is the logger you can optionally pass (you can just pass a `console` object to use it for displaying of your logs).
109
+
110
+ As soon as you create this object it will attempt to connect to the OpenFeed servers and issue the necessary callbacks to the listener.
111
+
112
+ When you are done with this object simply `dispose` it and it will disconnect and stop calling the callback listeners.
113
+
114
+ Once the object is created, you can simply subscribe to what you need and the listener's `onMessage` will be called with the received messages as they arrive. For example:
115
+
116
+ ```ts
117
+ const id = client.subscribe(Service.REAL_TIME, SubscriptionType.ALL, 1, ["MSFT"]);
118
+
119
+ // when done
120
+ client.unsubscribe(id);
121
+ ```
122
+
123
+ This subscribes to all message types from the real-time service, for MSFT. The number 1 represents a snapshot interval, but as in this case we are subscribing to real-time data and not periodic snapshots it's unused.
124
+
125
+ The call to Subscribe returns a subscription ID, which you can use to later unsubscribe by calling the `unsubscribe` function.
126
+
127
+ The available services are:
128
+
129
+ ```ts
130
+ enum Service {
131
+ UNKNOWN_SERVICE = 0,
132
+ REAL_TIME = 1,
133
+ DELAYED = 2,
134
+ REAL_TIME_SNAPSHOT = 3,
135
+ DELAYED_SNAPSHOT = 4,
136
+ END_OF_DAY = 5,
137
+ }
138
+ ```
139
+
140
+ The available subscription types are:
141
+
142
+ ```ts
143
+ enum SubscriptionType {
144
+ ALL = 0,
145
+ QUOTE = 1,
146
+ QUOTE_PARTICIPANT = 2,
147
+ DEPTH_PRICE = 3,
148
+ DEPTH_ORDER = 4,
149
+ TRADES = 5,
150
+ CUMLATIVE_VOLUME = 6,
151
+ OHLC = 7,
152
+ OHLC_NON_REGULAR = 8,
153
+ }
154
+ ```
155
+
156
+ ## Putting it all together
157
+
158
+ Now that we know how to use the listener and client objects, let's put together a little demo that subscribes to all MSFT messages and prints what's happening to the console:
159
+
160
+ ```ts
161
+ import { OpenFeedClient } from "./connection/connection";
162
+
163
+ import { Service } from "../generated/openfeed";
164
+ import { SubscriptionType } from "../generated/openfeed_api";
165
+ import { OpenFeedListeners } from "./connection/listeners";
166
+ import { IOpenFeedLogger } from "./connection/connection_interfaces";
167
+
168
+ const connect = async () => {
169
+ const logger: IOpenFeedLogger = console;
170
+ logger.log("Starting...");
171
+ const listeners = new OpenFeedListeners();
172
+ listeners.onConnected = () => {
173
+ logger.log("Connected");
174
+ };
175
+ listeners.onMessageWithMetadata = (msg, symbolNames, definition) => {
176
+ logger.log(
177
+ `Message -------\r\nSymbols: ${symbolNames.join(",")}\r\nMessage: ${JSON.stringify(msg)}\r\n\r\nDefinition: ${JSON.stringify(definition)}`
178
+ );
179
+ };
180
+ listeners.onDisconnected = () => {
181
+ logger.log("Disconnected");
182
+ };
183
+ listeners.onCredentialsRejected = () => {
184
+ logger.log("Credentials Rejected");
185
+ };
186
+
187
+ const url = "wss://openfeed.aws.barchart.com/ws";
188
+ const username = "<username>";
189
+ const password = "<password>";
190
+ const client = new OpenFeedClient(url, username, password, listeners, logger);
191
+ const id = client.subscribe(Service.REAL_TIME, SubscriptionType.ALL, 1, ["MSFT"]);
192
+ logger.log("Got the ID", id.toString());
193
+
194
+ setTimeout(() => {
195
+ logger.log("Unsubscribing...");
196
+ try {
197
+ client.unsubscribe(id);
198
+ } catch (e) {
199
+ logger.warn("Got the error when unsubscribing", e);
200
+ }
201
+ }, 10_000);
202
+
203
+ setTimeout(() => {
204
+ client.dispose();
205
+ }, 15_000);
206
+ };
207
+
208
+ connect();
209
+ ```
210
+
211
+ This program is a simple amalgamation of what we learned before. We first set up a listener and connect the callbacks that simply print everything that's happening to the console. We update the username and password with our credentials, and then finally we create a client, subscibe to all messages for the symbol MSFT, and dispose of the client after we have unsubscribed from the symbol feed.
212
+
213
+ ## More Information
214
+
215
+ To learn more about subscribing to symbols and exchanges click [here](SYMBOLS_EXCHANGES.md).
216
+
217
+ To learn more about various subscription types, click [here](SUBSCRIPTION_TYPES.md).
package/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ Copyright 2023, Barchart.com, Inc., http://www.barchart.com/
2
+
3
+ This software consists of voluntary contributions made by many
4
+ individuals. For exact contribution history, see the revision history
5
+ available at https://github.com/openfeed-org/sdk-js
6
+
7
+ The following license applies to all parts of this software.
8
+
9
+ ====
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining
12
+ a copy of this software and associated documentation files (the
13
+ "Software"), to deal in the Software without restriction, including
14
+ without limitation the rights to use, copy, modify, merge, publish,
15
+ distribute, sublicense, and/or sell copies of the Software, and to
16
+ permit persons to whom the Software is furnished to do so, subject to
17
+ the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be
20
+ included in all copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # TS/JS SDK for Barchart OpenFeed
2
+
3
+ TypeScript and JavaScript SDK for Barchart OpenFeed is a library that can be used to subscribe to market data messages served by the Barchart [OpenFeed](https://openfeed.com/) servers.
4
+
5
+ ## Obtaining the Library
6
+
7
+ Using npm:
8
+
9
+ ```shell
10
+ $ npm i --save <TBD>
11
+ ```
12
+
13
+ Using yarn:
14
+
15
+ ```shell
16
+ $ yarn add <TBD>
17
+ ```
18
+
19
+ ## User Guide
20
+
21
+ The User Guide for this project can be found in the [documentation](DOCUMENTATION.md) page.
22
+
23
+ ## Updating the Dependencies
24
+
25
+ To update the protobuf auto-generated files, follow the steps:
26
+
27
+ 1. Download the latest protoc executable from [here](https://github.com/protocolbuffers/protobuf/releases).
28
+ 2. Put the protoc.exe in the root folder of the project
29
+ 3. Run the following command: `yarn generate`
@@ -0,0 +1,200 @@
1
+ # Subscription Types
2
+
3
+ Here are a few example of various subscription types:
4
+
5
+ ## Trades Subscription
6
+
7
+ You can use the `SubscriptionType.TRADES` enumeration value to subscribe to trades and top of book messages:
8
+
9
+ ```ts
10
+ client.subscribe(Service.REAL_TIME, SubscriptionType.TRADES, 1, ["MSFT"]);
11
+ ```
12
+
13
+ This would result in a `subscriptionResponse` message, followed by an `instrumentDefinition` message, followed by a `marketSnapshot` (more or less a quote), followed by `makretUpdate` messages containing the top of the book and/or trades:
14
+
15
+ ```json
16
+ {
17
+ "subscriptionResponse": {
18
+ "correlationId": "2",
19
+ "status": {
20
+ "result": "SUCCESS",
21
+ "service": "REAL_TIME"
22
+ },
23
+ "symbol": "MSFT",
24
+ "marketId": "8262209587480783120",
25
+ "channelId": 16
26
+ }
27
+ }
28
+
29
+ ...
30
+
31
+ {
32
+ "instrumentDefinition": {
33
+ "marketId": "8262209587480783120",
34
+ "instrumentType": "EQUITY",
35
+ "vendorId": "NASDAQ_UTP",
36
+ "symbol": "MSFT",
37
+ "exchangeCode": "XNAS",
38
+ "recordCreateTime": "1616017119766000000",
39
+ "recordUpdateTime": "1616017119766000000",
40
+ "timeZoneName": "America/New_York",
41
+ "channel": 16,
42
+ "priceFormat": {
43
+ "denominator": 100,
44
+ "subDenominator": 1
45
+ },
46
+ "priceDenominator": 10000,
47
+ "quantityDenominator": 1,
48
+ "symbols": [
49
+ {
50
+ "vendor": "Barchart",
51
+ "symbol": "MSFT"
52
+ }
53
+ ],
54
+ "consolidatedFeedInstrument": true
55
+ }
56
+ }
57
+
58
+ ...
59
+
60
+ {
61
+ "marketSnapshot": {
62
+ ...
63
+ }
64
+ }
65
+
66
+ ...
67
+
68
+ {
69
+ "marketUpdate": {
70
+ "marketId": "8262209587480783120",
71
+ "symbol": "MSFT",
72
+ "transactionTime": "1616059694054478848",
73
+ "distributionTime": "1616059694054858551",
74
+ "marketSequence": "3882",
75
+ "originatorId": "UA==",
76
+ "bbo": {
77
+ "bidPrice": "2345000",
78
+ "bidQuantity": "11",
79
+ "bidOriginator": "UQ==",
80
+ "offerPrice": "2356000",
81
+ "offerQuantity": "1",
82
+ "offerOriginator": "UA==",
83
+ "quoteCondition": "Ug=="
84
+ }
85
+ }
86
+ }
87
+
88
+ ...
89
+
90
+ {
91
+ "marketUpdate": {
92
+ "marketId": "8262209587480783120",
93
+ "symbol": "MSFT",
94
+ "transactionTime": "1616059700690841000",
95
+ "marketSequence": "3883",
96
+ "session": {
97
+ "volume": {
98
+ "volume": "18330"
99
+ },
100
+ "numberOfTrades": {
101
+ "numberTrades": "374"
102
+ },
103
+ "monetaryValue": {
104
+ "value": "430158642"
105
+ }
106
+ },
107
+ "trades": {
108
+ "trades": [
109
+ {
110
+ "trade": {
111
+ "originatorId": "UQ==",
112
+ "transactionTime": "1616059700671550189",
113
+ "price": "2345900",
114
+ "quantity": "1",
115
+ "tradeId": "MjA4",
116
+ "tradeDate": 20210318,
117
+ "saleCondition": "QEZUSQ==",
118
+ "doesNotUpdateLast": true,
119
+ "session": "I",
120
+ "distributionTime": "1616059700671565461"
121
+ }
122
+ }
123
+ ]
124
+ }
125
+ }
126
+ }
127
+
128
+ ...
129
+ ```
130
+
131
+ ## OHLC Subscription
132
+
133
+ The `OHLC` susbscription type will give you periodic Open + High + Low + Close messages:
134
+
135
+ ```ts
136
+ client.subscribe(Service.REAL_TIME, SubscriptionType.OHLC, 1, ["MSFT"]);
137
+ ```
138
+
139
+ Just like the trades subscription above, this would result in a `subscriptionResponse` message, followed by an `instrumentDefinition` message, followed by by periodic OHLC messages:
140
+
141
+ ```json
142
+ ...
143
+
144
+ {
145
+ "ohlc": {
146
+ "marketId": "8262209587480783120",
147
+ "symbol": "MSFT",
148
+ "open": {
149
+ "price": "2350000"
150
+ },
151
+ "high": {
152
+ "price": "2350000"
153
+ },
154
+ "low": {
155
+ "price": "2350000"
156
+ },
157
+ "close": {
158
+ "price": "2350000"
159
+ }
160
+ }
161
+ }
162
+
163
+ ...
164
+ ```
165
+
166
+ ## Quote Subscrition
167
+
168
+ ```ts
169
+ client.subscribe(Service.REAL_TIME, SubscriptionType.QUOTE, 1, ["MSFT"]);
170
+ ```
171
+
172
+ Again, we first received the subscription response and the `instrumentDefinition` messages, followed by `marketSnapshot` and `marketUpdate` messages:
173
+
174
+ ```json
175
+ ...
176
+
177
+ {
178
+ "marketSnapshot": {
179
+ ...
180
+ }
181
+ }
182
+
183
+ ...
184
+
185
+ {
186
+ "marketUpdate": {
187
+ ...
188
+ }
189
+ }
190
+
191
+ ...
192
+ ```
193
+
194
+ ## All Subscription Types
195
+
196
+ ```ts
197
+ client.subscribe(Service.REAL_TIME, SubscriptionType.ALL, 1, ["MSFT"]);
198
+ ```
199
+
200
+ The `ALL` subscription type will give you all the messages (quotes, trades, book, depth, etc.) that you are permissioned for.
@@ -0,0 +1,17 @@
1
+ # Symbols and Exchanges
2
+
3
+ The `IOpenfeedClient.subscribe` method allows you to subscribe to individual symbols and exchanges, depending on your needs.
4
+
5
+ Subscriptions by symbol are the most lightweight as they only subscribe to a few symbols at a time. Subscriptions by exchanges will subscribe to everything on a single exchange at once.
6
+
7
+ To subscribe to just a few symbols, all you need is to pass the list of symbols in the Subscribe method symbols argument:
8
+
9
+ ```ts
10
+ client.subscribe(Service.REAL_TIME, SubscriptionType.ALL, 1, ["MSFT", "GOOG"]);
11
+ ```
12
+
13
+ To subscribe to a data coming from an exchange wholesale, pass the list of exchanges you are interested in in the call to Subscribe:
14
+
15
+ ```ts
16
+ client.subscribe(Service.REAL_TIME, SubscriptionType.ALL, 1, null, null, ["CME"]);
17
+ ```
@@ -0,0 +1,4 @@
1
+ const empty = {};
2
+ export {
3
+ empty as default
4
+ };
@@ -0,0 +1,6 @@
1
+ export { Result, SubscriptionType, SymbolType } from "./openfeed_api";
2
+ export type * from "./openfeed_api";
3
+ export { BookSide, InstrumentTradingStatus, RegulationSHOShortSalePriceTest, SettlementTerms, CrossType, OpenCloseSettlementFlag, SettlementSource, Service, MarketWideStatus, SnapshotRequestResult, ActionType, AdminMessage_Status, MarketSummary_ClearSet, MarketSummary_SummaryType, SnapshotRequest_SnapshotRequestType } from "./openfeed";
4
+ export type * from "./openfeed";
5
+ export { InstrumentDefinition_InstrumentType, InstrumentDefinition_BookType, InstrumentDefinition_OptionType, InstrumentDefinition_OptionStyle, InstrumentDefinition_State, InstrumentDefinition_EventType, InstrumentDefinition_PriceFormat_SubFormat } from "./openfeed_instrument";
6
+ export type * from "./openfeed_instrument";