Task 1: Interface with a stock price data feed
Background Information of the task:
This software is to add a chart to a trader’s dashboard allowing them to better identify under/over-valued stocks.
The trader would like to be able to monitor two historically correlated stocks and be able to visualize when the correlation between the two weakens (i.e. one stock moves proportionally more than the historical correlation would imply). This could indicate a potential trade strategy to simultaneously buy the relatively underperforming stock and sell the relatively outperforming stock. Assuming the two prices subsequently converge, the trade should be profitable.
Most data visualization for JPMorgan Chase’s traders is built on the data visualization software - Perspective, which is now open source. See: GitHub Repository of Perspective.
Before implementing this request using perspective, the first need is to interface with the relevant financial data feed and make the necessary adjustments to facilitate the monitoring of potential trade opportunities.
Local Setup
Python
1 | xiaxii@cecilias-mbp ~ % which python |
I am using Python3 for this project.
1 | xiaxii@cecilias-mbp ~ % pip3 list |
Clone repository
1 | git clone https://github.com/insidesherpa/JPMC-tech-task-1-py3.git |
Run the script
1 | python3 server3.py |
Code Changes
Interface with a stock price data feed and set up your system for analysis of the data
Prerequest
Run server and client
Server1
2
3
4
5
6
7
8xiaxii@cecilias-mbp JPMC-tech-task-1-py3 % python3 server3.py
HTTP server started on port 8080
Query received @ t2019-02-10 10:07:43.237974
Query received @ t2019-02-11 18:12:31.763344
Query received @ t2019-02-13 00:41:03.061658
Query received @ t2019-02-13 19:37:03.654348
Query received @ t2019-02-14 08:26:51.287677
...
Client 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16xiaxii@cecilias-mbp JPMC-tech-task-1-py3 % python3 client3.py
Quoted ABC at (bid:118.13, ask:116.63, price:118.13)
Quoted DEF at (bid:115.14, ask:117.87, price:115.14)
Ratio 1
Quoted ABC at (bid:118.13, ask:116.63, price:118.13)
Quoted DEF at (bid:115.14, ask:117.87, price:115.14)
Ratio 1
Quoted ABC at (bid:118.13, ask:116.63, price:118.13)
Quoted DEF at (bid:115.14, ask:117.87, price:115.14)
Ratio 1
Quoted ABC at (bid:118.13, ask:116.63, price:118.13)
Quoted DEF at (bid:115.14, ask:117.51, price:115.14)
Ratio 1
Quoted ABC at (bid:118.13, ask:116.63, price:118.13)
Quoted DEF at (bid:115.14, ask:117.51, price:115.14)
Ratio 1
Troubleshooting:
AttributeError: module ‘urllib’ has no attribute ‘urlopen’
Solution:
import urllib.request
urllib.request.urlopen(…)
Objectives
There are two incorrect things…
- Ratio is always 1
- The price of each stock is always the same as its bid_price.
Making changes to client3.py
(1) getDataPoint, change price1
price = (bid_price + ask_price)/2.
(2) getRatio, change the return1
2
3if (price_b==0): # avoid ZeroDivisionError
return
return price_a/price_b
(3) main, change1
2
3
4
5
6
7
8
9
10
11
12
13
14
15if __name__ == "__main__":
# Query the price once every N seconds.
# Get a price directory where the key-value pair is stock name-price
for _ in range(N):
quotes = json.loads(urllib.request.urlopen(QUERY.format(random.random())).read())
prices = {}
""" ----------- Update to get the ratio --------------- """
for quote in quotes:
stock, bid_price, ask_price, price = getDataPoint(quote)
prices[stock] = price
print ("Quoted %s at (bid:%s, ask:%s, price:%s)" % (stock, bid_price, ask_price, price))
print ("Ratio %s" % (getRatio(prices['ABC'],prices['DEF'])))
Finished
Run client3.py:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Quoted ABC at (bid:126.12, ask:125.84, price:125.98)
Quoted DEF at (bid:124.66, ask:125.15, price:124.905)
Ratio 1.008606540971138
Quoted ABC at (bid:126.12, ask:125.84, price:125.98)
Quoted DEF at (bid:124.66, ask:125.15, price:124.905)
Ratio 1.008606540971138
Quoted ABC at (bid:126.12, ask:125.05, price:125.58500000000001)
Quoted DEF at (bid:124.66, ask:125.15, price:124.905)
Ratio 1.005444137544534
Quoted ABC at (bid:126.12, ask:125.05, price:125.58500000000001)
Quoted DEF at (bid:124.66, ask:125.15, price:124.905)
Ratio 1.005444137544534
Quoted ABC at (bid:126.12, ask:125.05, price:125.58500000000001)
Quoted DEF at (bid:124.66, ask:125.05, price:124.85499999999999)
Ratio 1.0058467822674304
Bonus Task: Unit Test
Pattern - Build-Act-Assert:
- Build: We first build a simulated test scenario e.g. instantiating the dummy data we will pass in the methods we’ll test, importing the class
whose methods we want to test, etc. - Act: We then make some operations and call the method we want to test for
- Assert: We check if the output of the method we’re testing matches the expectation we have (e.g. dummy / static data of the outcome)
Unittest
1 | import unittest |
A testcase is created by subclassing unittest.TestCase
. The three individual tests are defined with methods whose names start with the letters test
. This naming convention informs the test runner about which methods represent tests.
Submit Patch file
git
git is a way for developers to manage code in a project especially if there’s other developers collaborating in that project too.git patch
A git patch file is just a file that you can apply to a repository to get the changes / modifications / additions another developer did on his / her machine onto your local machine.
This isn’t the only way to do that ofcourse but this is a viable method for a head/lead developer to check your code first before merging it into the repository’s main/master branch.Make a git patch file
Fire up a terminal, enter the repository via the terminal you opened (via the cdaka change directory command) and do the following commands: (one line, one command) 1
2
3
4
5git add -A
git config user.email "<your_email_address>"
git config user.name "<your_name>"
git commit -m 'Create Patch File'
git format-patch -1 HEAD
The final command, i.e. git format-patch -1 HEAD, should produce the .patch file you’d want to submit to complete this module. It will be located in the directory where you executed the command.
Other Assertions:
self.assertIsNone()
self.assertGreater(a, b) …a>b
self.assertLess(a, b) …a<b
self.assertRaises(TypeError)