Introduction
I have recently been invited to a coding challenge which was required to be delivered in 7 days. The task was very simple 😎:
Coding challenge
The aim of this exercise is to implement an “alerting” service which will consume a file of currency conversion rates and produce alerts.
For the purpose of this coding exercise, you are allowed to choose a different programming language, provided that you provide us with a detailed instruction on how to build and run your program.
Input
The format of the file will simulate a stream of currency conversion rates. Each line will be properly structured JSON (http://jsonlines.org/):
{ “timestamp”: 1554933784.023, “currencyPair”: “CNYAUD”, “rate”: 0.39281 }
The fields in the JSON record are: - timestamp: the timestamp of the record in seconds since UNIX epoch, with fractional seconds specified - currencyPair: the sell and buy currencies which the rate relates to - rate: the conversion rate
You may assume that for each currency pair, currency conversion rates are streamed at a constant rate of one per second. ie. for two consecutive “CNYAUD” entries in in the input file, they will have timestamps that differ by one second:
{ "timestamp": 1554933784.023, "currencyPair": "CNYAUD", "rate": 0.39281 }{ “timestamp”: 1554933784.087, “currencyPair”: “USDAUD”, “rate”: 0.85641 } { “timestamp”: 1554933785.023, “currencyPair”: “CNYAUD”, “rate”: 0.39295 }
Output
The alerting service should produce the following alert as a JSON string output to standard output:
- when the spot rate for a currency pair changes by more than 10% from the 5 minute average for that currency pair
The format of the alert produced should be:
{ “timestamp”: 1554933784.023, “currencyPair”: “CNYAUD”, “alert”: “spotChange” }
As mentioned earlier, the task is very simple but I wanted to take the opportunity to improve the following aspects:
- Code readability
- Unit testing
- Deployability
Code readability
I use VSCODE for many tasks, and it is my main text editor so naturally, I looked for tools that play well with it. For automatic code styling, I used black which is a great code formatter. Additionally, I revised the PEP 8 guide to refresh my memory of best practices.
Unit Testing
The idea behind unit testing is that you have to arrange your code into non-coupled components to allow for testing. In the Python ecosystem there are few options such as the unittest which comes as part of the Python Standard Library and pytest. I ended up using pytest because I wanted to learn it.
Deployability
Python, similar to other interpreted languages, requires a compatible version of the interpreter and the same version of packages (excluding Javascript, where every computer nowadays comes with one). This is a common issue that has many solutions. Among those solutions are the virtual
nments such as (venv, virtualenv, conda env) and containers such as the well-known docker.
The simplicity and ubiquity of docker made it a simple choice 👍 for me in this challenge. Once the code is written and tested, docker image description file is all that is needed. The alternative path of virtual environment was also a viable one, I just had to write environment creation scripts, one for windows and one for Unix/Linux 🥱.
My solution
You can access the solution over here .
Assumptions
- One input file can be consumed at a time.
- The frequency at which updates arrive is fixed (1s). Hence the average, in the general case, is taken over 300 samples.
- A single stream (input file) can contain more than one currency pair.
Decisions
- To keep track of the exchange rates for each pair, I made the currency pairs keys of a dictionary which maps to sliding window deque data structure.
- The rationale behind choosing a dictionary is because, each new line can be new data point for a currency pair, which means the currency pairs data need to be accessed in random order and a dictionary is the best options here where an access operation is \(O(1)\)
- The rationale behind choosing a deque is that it allows for the easy creation of a sliding window. A deque has \(O(1)\) complexity when we append to or access the ends of the queue, which is what we are doing here.
- The
CurrencyPairDataclass is a subclass of theObservableclass. This allows me to easily add callbacks toCurrencyPairDatainstances.
In the end
The coding challenge was a fun opportunity to build something and get someone to give me feedback on my approach. Additionally, I had a ton of fun learning about pytest and docker which will definitely be used in my other projects.