loganrudd / implied-vol-plot

Using the LedgerX public websocket API we're able to get live top of the orderbook updates for an option chain with over 150 different contracts and save the data to a RedisTimeSeries type as well as publish updates to listening subscribers. An example subscriber, an interactive plot showing the implied volatility skew of the selected expiration and type (call or put).
7 stars 8 forks source link

Live BTC Implied Volatility Charts

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.

architectural diagram

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.

Data Access

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.

Setup

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.

screenshot of vol chart

Docker Install (WIP)

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