Streaming prices into a tile
This tutorial aims to teach you how to connect to Liberator using StreamLink, and subscribe to prices in the Tile component.
Objectives
-
Connect to Liberator using StreamLink.
-
Stream prices into a tile.
Creating a StreamLink connection
So far we’ve been working with components we’ve created ourselves in our app, but sljs
is a package provided in the CT5 libraries. We can add sljs to the package.json
.
-
Add the following line to the dependencies section with the other dependencies:
"sljs": "workspace:*"
This adds
sljs
as a project dependency from the pnpm workspace, which includes the caplin packages ("myTradingApplication/packages-caplin/sljs"). The package and dependencies are linked in thenode_modules
directory found inmyTradingApplication/apps/tile
. -
Run the following command to update the node_modules
$ pnpm install
-
Amend
apps/tile/src/index.js
, which is the application entry point, to importsljs
and create a connection to Liberator. These lines should be added after the existing imports, and before thestartApp
function is defined:import { StreamLinkFactory } from "sljs"; const streamLink = StreamLinkFactory.create({ username: "admin", password: "admin", liberator_urls: "rttp://localhost:18080" }); streamLink.connect();
-
Now restart the app and refresh your browser page:
$ pnpm start
You are now successfully connected to Liberator. StreamLink comes with a built-in log window with detailed logs of all events. This can be useful for debugging or just to confirm that you’re connected to Liberator correctly.
To open the log window, simply add ?debug=fine
to the end of your URL (or &debug=fine
if you already have a URL parameter) e.g. http://localhost:8080/?debug=fine.
Also check your developer tools in your browser, in the network tab look out for RTTP-TYPE or websocket messages.
Creating an SLJSTile component
When creating React components it is good practice to keep the presentation logic (what the component looks like and what data it needs) in a separate component to state, this makes the component more reusable.
In our case, the previously created Tile component encapsulates the presentation of the tile. Now we want to create a new component, which connects to Liberator and retrieves price data. Let’s name this new component SLJSTile
, as it gets its data from the StreamLink API.
Building the SLJSTile container component
-
Create a new
SLJSTile.js
file in theapps/tile/src/Tile
directory. -
Add the following code to the
SLJSTile.js
file.import React, { useEffect, useState } from "react"; import Tile from "./Tile"; export default function SLJSTile({currencyPair, streamLink}) { const [rate, setRate] = useState({ bidRate: "-", askRate: "-", }); useEffect(() => { const subscription = streamLink.subscribe(`/FX/${currencyPair}`, { onRecordUpdate: (subscription, event) => { const fields = event.getFields(); setRate({ bidRate: fields.BidPrice, askRate: fields.AskPrice, }); }, onSubscriptionStatus(event) { console.log("subscribed:" + event); }, onSubscriptionError(event) { console.log("error:" + event); }, }); // Specify how to clean up after this effect: return () => { if (subscription) { subscription.unsubscribe(); console.log("unsubscribe"); } }; }, []); return ( <Tile currencyPair={currencyPair} bidRate={rate.bidRate} askRate={rate.askRate} /> ); }
Understanding the SLJSTile container component
Ok, let’s break down some of the code introduced above.
-
The currency pair and streamlink, which are needed to request the prices from StreamLink, are passed in as props.
export default function SLJSTile({currencyPair, streamLink}) { const [rate, setRate] = useState({ bidRate:"-", askRate:"-", }); }
We create state properties for
bidRate
andaskRate
, and initialise both of these to a dash. This provides a default value to display on the buttons before the price data has been received from Liberator. -
we pass the current rate values to the Tile component instead of the mock data from before
return ( < Tile currencyPair={currencyPair} bidRate={rate.bidRate} askRate={rate.askRate} /> );
-
We use the useEffect hook to make the subscription to streamlink. The
useEffect
hook should be used when code will create side effects (such as changing state) as react isn’t designed to handle state changing during rendering. Inside this method we subscribe to StreamLink for ourcurrencyPair
. We handle updating prices as well as unsubscribing from StreamLink if the component is unmounted.useEffect(() => { const subscription = streamLink.subscribe(`/FX/${currencyPair}`, { onRecordUpdate: (subscription, event) => { const fields = event.getFields(); setRate({ bidRate: fields.BidPrice, askRate: fields.AskPrice, }); }, onSubscriptionStatus(event) { console.log("subscribed:" + event); }, onSubscriptionError(event) { console.log("error:" + event); }, }); // Specify how to clean up after this effect: return () => { if (subscription) { subscription.unsubscribe(); console.log("unsubscribe"); } }; }, []);
-
Streamlink.subscribe takes a subject and a subscription listener with callbacks for
onRecordUpdate
onSubscriptionStatus
andonSubscriptionError
. -
onRecordUpdate
is called whenever a record is received from liberator.onSubscriptionStatus
andonSubscriptionError
are called when status messages and error messages are received respectively. -
In our onRecordUpdate callback we change the state to be the new data from the server. event.getFields() returns the fields sent on the message from the backend.
useEffect
will call any return function when the hook is cleaned up (either when it reruns or when the component is unmounted). We use this to unsubscribe from streamlink when the component is unmounted. Since we added a dependency array of []
to useEffect our function will only be called once. If we wanted it to be called again when the currency pair changed we’d add currency pair to the dependency array ([currencyPair]
).
Rendering the SLJSTile
The final step is to update the apps/tile/src/index.js
file to render the new SLJSTile
component instead of the old Tile component, passing in the StreamLink instance and currency pair.
-
Change the
import Tile
statement in theapps/tile/src/index.js
file toSLJSTile
, as illustrated below:import SLJSTile from "./tile/SLJSTile";
-
Inside the
ReactDOM.render
call, create instances of theSLJSTile
component instead of Tile, passing in the correct prop values below:function startApp() { ReactDOM.render ( <div> <SLJSTile currencyPair="GBPUSD" streamLink={streamLink} /> <SLJSTile currencyPair="EURUSD" streamLink={streamLink} /> <SLJSTile currencyPair="USDJPY" streamLink={streamLink} /> </div>, document.getElementById ("root") ); }
-
Open the app in your browser by going to http://localhost:8080, and you should see the prices on your tiles updating!
If the prices stay as dashes check that your backend is up and running.
Your output should look similar to the screen below.