Using LedgerX public websocket API we're able to demonstrate using Redis as a multipurpose datastore. Using a server side Lua script to check the received updates counter we can appropriately publish PUBSUB messages to the listening Bokeh app and store the bid/ask prices to a RedisTimeSeries data type atomically.
The Bokeh app displays the implied volatility calculated from the best bid and offer prices received over websocket. We're using the Black-Scholes formula implemented by the vollib library.
We get the price of bitcoin from polling the coinbase API every 3 seconds.
This allows traders to do further analysis and find opportunities in possible mispricings in the volatility component of the options pricing model.
When you run ledgerx_ws.py
a websocket connection is opened with LedgerX and sends data from the incoming messages to a Lua script on the Redis instance that adds new data to a Time Series structure and atomically publishes the data to subscribers:
redis.call('SETNX', KEYS[1], 0)
local prev_clock = redis.call('GETSET', KEYS[1], ARGV[1])
local clock = tonumber(ARGV[1])
prev_clock = tonumber(prev_clock)
if(clock > prev_clock) then
redis.call('PUBLISH', ARGV[2], ARGV[5])
redis.call('TS.ADD', KEYS[2], '*', ARGV[3], 'ON_DUPLICATE', 'LAST')
redis.call('TS.ADD', KEYS[3], '*', ARGV[4], 'ON_DUPLICATE', 'LAST')
return 1
else
return 0
end
The subscribers listening to the Redis PUBSUB channel are charts generated by a Bokeh server app.
In this app we record bid ask prices to Time Series structures with keys formatted like so: <contract_id>:[bid|ask]
ie., 22227599:ask
you can access the time series data with the TS.RANGE
command from the redis-cli (docs):
127.0.0.1:6379> TS.RANGE "22227599:ask" - +
1) 1) (integer) 1639378065269
2) 65500
2) 1) (integer) 1639378209405
2) 65500
3) 1) (integer) 1639378219445
2) 65500
To subscribe to the live data being stored to the time series structs, simply subscribe to this wildcard channel: *.1
, like so:
127.0.0.1:6379> PSUBSCRIBE *.1
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "*.1"
3) (integer) 1
1) "pmessage"
2) "*.1"
3) "22229255.1"
4) "{\"bid\": 14600, \"ask\": 55900, \"contract_type\": 1, \"clock\": 46971, \"bid_size\": 2, \"type\": \"book_top\", \"contract_id\": 22229255, \"ask_size\": 1}"
1) "pmessage"
2) "*.1"
3) "22230741.1"
4) "{\"bid\": 300000, \"ask\": 387100, \"contract_type\": 1, \"clock\": 316717, \"bid_size\": 10, \"type\": \"book_top\", \"contract_id\": 22230741, \"ask_size\": 5}"
The subscriber will recieve JSON encoded messages with the contract id, best bid, ask and top of the book order sizes.
1) Install Redis and RedisTimeSeries
2) Clone the repository and install the dependencies in requirements.txt (pip -r requirements.txt
in a venv w/ python=3.7)
3) Create a LedgerX api key (https://app.ledgerx.com/profile/api-keys) and copy to a file in root of project named "secret"
4) Run ledgerx_ws.py
(the script consuming the websocket stream from LedgerX) and then in another terminal window run
bokeh serve --show iv_app.py
from the project root to start up the Bokeh server application and open a web browser to
the local URL (http://localhost:5006/iv_app by default).
There are pre-cache files contracts.pkl
and id_table.json
which are loaded so no authenticated requests are needed.
There is a small shell script clear_cache.sh
that simple deletes these files, the app will automatically create them
if missing, but will require a secret
file with your LedgerX API key on the first line.
Install docker: https://docs.docker.com/engine/install/
Run the command sysctl vm.overcommit_memory=1
to avoid background save failure under low memory condition
Install docker-compose: https://docs.docker.com/compose/install/
If you don't configure docker to be used without sudo you'll have to add sudo in front of any docker command
To build image: sudo docker build -t iv_app:dev .
To run image interactively, mapping port 5006 from the container to 5006 on your local machine:
sudo docker run -it -p 5006:5006 -w "/implied-vol-plot" -v "$(pwd):/implied-vol-plot" iv_app:dev bash
To run it in the background in detached mode:
sudo docker run -d -p 5006:5006 -w "/implied-vol-plot" -v "$(pwd):/implied-vol-plot" iv_app:dev
Docker-compose:
To start services: docker-compose up
#can add -d flag to run in background
To stop services: docker-compose down
To run a specific service interactively: docker-compose exec <name-of-service-in-docker-compose-yaml> sh