JPMorgan Chase Software Engineering Virtual Internship 2/4

Task 2: Use JPMorgan Chase frameworks and tools

Background

Typically, traders monitor stock prices and trading strategies by having data displayed visually on their screens in chart form. Often these charts will be accompanied by alerts that notify users when certain events occur or when preset price thresholds are hit.

JPMorgan Chase created the Perspective tool over many years to allows users to present and manipulate data feeds visually in web applications.

Perspective provides a set of flexible data transforms, such as pivots, filters, and aggregations. It utilizes bleeding-edge browser technology such as Web Assembly and Apache Arrow and is unmatched in browser performance. It is engineered for reliability and production-vetted on the JPMorgan Chase trading floor and is now available to the development community as Open Source. Chect it out on github page of perspective.

Set Up

1
2
3
4
5
6
7
8
9
10
xiaxii@cecilias-mbp JPMC % git --version
git version 2.26.1
xiaxii@cecilias-mbp JPMC % git clone https://github.com/insidesherpa/JPMC-tech-task-2-PY3.git
Cloning into 'JPMC-tech-task-2-PY3'...
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 57 (delta 4), reused 2 (delta 0), pack-reused 48
Receiving objects: 100% (57/57), 231.98 KiB | 2.37 MiB/s, done.
Resolving deltas: 100% (21/21), done.
1
2
3
4
xiaxii@cecilias-mbp JPMC-tech-task-2-PY3 % ls
README.md package.json test.csv
datafeed public tsconfig.json
package-lock.json src

Server

1
2
xiaxii@cecilias-mbp JPMC-tech-task-2-py3 % python3 datafeed/server3.py
HTTP server started on port 8080

Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
xiaxii@cecilias-mbp JPMC-tech-task-2-PY3 % curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.0/install.sh | bash
...

xiaxii@cecilias-mbp JPMC-tech-task-2-PY3 % nvm --version
0.35.3

xiaxii@cecilias-mbp JPMC-tech-task-2-PY3 % nvm install v11.0.0
Downloading and installing node v11.0.0...
Downloading https://nodejs.org/dist/v11.0.0/node-v11.0.0-darwin-x64.tar.xz...
######################################################################### 100.0%
Computing checksum with shasum -a 256
Checksums matched!
Now using node v11.0.0 (npm v6.4.1)
xiaxii@cecilias-mbp JPMC-tech-task-2-PY3 % nvm use v11.0.0
Now using node v11.0.0 (npm v6.4.1)

xiaxii@cecilias-mbp JPMC-tech-task-2-PY3 % node -v
v11.0.0
xiaxii@cecilias-mbp JPMC-tech-task-2-PY3 % npm -v
6.4.1

First Run

1
2
npm install
npm start

Result
start the client application

Troubleshooting
Error: type error: Cannot find module ‘@jpmorganchase/perspective’. TS2307
Solution:
go to the your project directory and just type the command:

1
npm i @finos/perspective

this will install the required files in your project directory
and then goto your graph.tsx file and replace

1
import { Table } from '@jpmorganchase/perspective';

with this below code

1
import { Table } from '@finos/perspective';

Ref: https://github.com/insidesherpa/JPMC-tech-task-2/issues/47

Code Changes

Prerequest

the app can run after npm start.
(1) Click on the botton Start Streaming Data, there is going to be a lot of stock records
(2) Click on the 3-dotted button on the upper left corner of the graph. The graph is configurable and there are different types of views we can use to visualize the data we have so far.
(3) But we can observe it’s just a bunch of duplicate data being printed for stocks ABC and DEF, instead ofnew data (i.e. different timestamp, ask_price and bid_price for ABC and DEF stocks).

Obejective

(1) Make the graph continuously update instead of having to click it a bunch of times. Also the kind of graph we want to serve as visual here is kind of a continuously updating line graph whose y axis is the stock’s top_ask_price and the x-axis is the timestamp of the stock
(2) Remove / disregard the duplicated data in ‘first run’

Making changes to the src code

(1) App.tsx, change the static table into a live / updating graph

Declare showGraph and include it in other methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* State declaration for <App />
*/
interface IState {
data: ServerRespond[],
showGraph: boolean,
}

/**
* The parent element of the react app.
* It renders title, button and Graph react element.
*/
class App extends Component<{}, IState> {
constructor(props: {}) {
super(props);

this.state = {
// data saves the server responds.
// We use this state to parse data down to the child element (Graph) as element property
data: [],
showGraph: false,
};
}

To ensure that the graph doesn’t render until a user clicks the ‘Start Streaming’ button, I should also edit the renderGraph method of the App. In there, I must add a condition to only render the graph when the
state’s showGraph property of the App’s state is true.

1
2
3
4
5
6
7
8
/**
* Render Graph react component with state.data parse as property data
*/
renderGraph() {
if (this.state.showGraph){
return (<Graph data={this.state.data}/>)
}
}

Finally, I must also modify the getDataFromServermethod to contact the server and get data from it continuously instead of just getting data from it once every time you click the button.

Javascript has a way to do things in intervals and that is via the setInterval function. What we can do to make it continuous (at least up to an extended period of time) is to have a guard value that we can check against when to stop / clear the interval process we started.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Get new data from server and update the state with the new data
*/
getDataFromServer() {
let x = 0;
const interval = setInterval(() => {
DataStreamer.getData((serverResponds: ServerRespond[]) => {
// getData() gets the data from the server and when that process is complete
// Update the state by creating a new array of data that consists of
// Previous data in the state and the new data from server
this.setState({
data: serverResponds,
showGraph: true,
// set showGraph to true
// as soon as the data from the server comes back to the requester
});
});
x++;
if (x > 1000){
clearInterval(interval);
}
},100);
}

The line DataStreamer.getData(… => …) is an asynchronous process that gets the data from the server and when that process is complete, it then performs what comes after the => as a callback function.

(2) Graph.tsx
To completely achieve the desired output, we must also make changes to the Graph.tsx file. This is the file that takes care of how the Graph component of our App will be rendered and react to the state changes that occur within the App.

Firstly, enable the PerspectiveViewerElement to behave like an HTMLElement. To do this, I should extend the HTMLElement class from the PerspectiveViewerElement interface.

1
2
3
4
5
6
7
/**
* Perspective library adds load to HTMLElement prototype.
* This interface acts as a wrapper for Typescript compiler.
*/
interface PerspectiveViewerElement extends HTMLElement{
load: (table: Table) => void,
}

Since I’ve changed the PerspectiveViewerElement to extend the HTMLElement earlier, I can now make the definition of the const elem simpler, i.e. I can just assign it straight to the result of the result of the
document.getElementsByTagName.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* React component that renders Perspective based on data
* parsed from its parent through data property.
*/
class Graph extends Component<IProps, {}> {
// Perspective table
table: Table | undefined;

render() {
return React.createElement('perspective-viewer');
}

componentDidMount() {
// Get element to attach the table from the DOM.
// the componentDidMount() method runs
// after the component output has been rendered to the DOM.
const elem = document.getElementsByTagName('perspective-viewer')[0] as unknown as PerspectiveViewerElement;
const schema = {
stock: 'string',
top_ask_price: 'float',
top_bid_price: 'float',
timestamp: 'date',
};

if (window.perspective && window.perspective.worker()) {
this.table = window.perspective.worker().table(schema);
}
if (this.table) {
// Load the `table` in the `<perspective-viewer>` DOM reference.
// Add more Perspective configurations here.
elem.load(this.table);
elem.setAttribute('view','y_line');
elem.setAttribute('column-pivots','["stock"]');
elem.setAttribute('row-pivots','["timestamp"]');
elem.setAttribute('columns','["top_ask_price"]');
elem.setAttribute('aggregates', `{"stock":"distinct count","top_ask_price":"avg","top_bid_price":"avg","timestamp":"distinct count"}`);
}
}

Troubleshoting
Problem:
Failed to Compile-Type Error TS2345
Solution:
in the aggregates property, the value should have been enclosed in back ticks i.e. value,
so it would look somethine like:

1
elem.setAttribute('aggregates', `{"stock":"distinct count","top_ask_price":"avg","top_bid_price":"avg","timestamp":"distinct count"}`);

Finished

Final Task2