Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
bde13f7
create_l2_order_book removed
olned Jun 10, 2020
734fa12
Deribit is divided into Parser and Controller
olned Jun 10, 2020
d318566
the deribit_writer example added
olned Jun 10, 2020
b2472b1
conn.on_before_handling = dump
olned Jun 13, 2020
85cbd04
parser keeps books
olned Jun 13, 2020
d3ee919
removed asyn for some methods
olned Jun 16, 2020
7521cc4
handle_message replaced to parse
olned Jun 16, 2020
d53f94d
minor changes of book
olned Jun 16, 2020
57a9761
public/set_heartbeat added
olned Jun 16, 2020
5afde82
ssc2ce_cpp support
olned Jun 16, 2020
d2c9705
controller removed from parser
olned Jun 16, 2020
0692a6b
def instrument(self) -> str
olned Jun 16, 2020
6313ffb
IDeribitController removed
olned Jun 16, 2020
30bd2f5
IDeribitParser removed
olned Jun 16, 2020
02aceed
minor changes
olned Jun 16, 2020
8dc8181
async removed from some methods
olned Jun 16, 2020
4f2085a
IDeribitController removed
olned Jun 16, 2020
8d9d156
using ssc2ce_cpp_spec added
olned Jun 16, 2020
1426477
async removed from some methods
olned Jun 16, 2020
0b0622d
IDeribitController removed
olned Jun 16, 2020
1397c98
book.instrument becomes a method
olned Jun 16, 2020
41f8b45
AbstractL2Book removed
olned Jun 16, 2020
005ed30
README.md changed
olned Jun 16, 2020
62bf93e
minor changes
olned Jun 16, 2020
1125c9c
version="0.12.0"
olned Jun 16, 2020
df6ac6c
Merge pull request #16 from olned/master
olned Jun 16, 2020
cb673d4
Update README.md
olned Jun 16, 2020
e352b0c
bitfinex book fixed
olned Jun 16, 2020
5759388
some examples changed
olned Jun 16, 2020
88aa950
minor changes
olned Jun 16, 2020
a053d84
Run examples from a clone - updated
olned Jun 16, 2020
94652dd
Update README.md
olned Jun 16, 2020
509ebd5
version="0.12.1"
olned Jun 16, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
.idea/
dump.txt

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
A Set of Simple Connectors for access To Cryptocurrency Exchanges via websocket based on
[aiohttp](https://aiohttp.readthedocs.io) .

This is more of a pilot project, if you have any wishes for adding exchanges or expanding functionality, please register issues

## Installation
Install ssc2ce with:
```bash
$ pip install ssc2ce
```

You can run some examples with
## Bitfinex
### Description
API description look at [Websocket API v2](https://docs.bitfinex.com/v2/docs/ws-general)
Expand Down Expand Up @@ -64,7 +67,7 @@ async def subscribe():
})


async def handle_subscription(data):
def handle_subscription(data):
method = data.get("method")
if method and method == "subscription":
if data["params"]["channel"].startswith("deribit_price_index"):
Expand All @@ -88,21 +91,19 @@ except KeyboardInterrupt:
## Run examples from a clone

If you clone repository you can run examples from the root directory.

```bash
$ PYTHONPATH=.:$PYTHONPATH python examples/basic_example.py
$ PYTHONPATH=.:$PYTHONPATH python examples/bitfinex_basic_example.py
```

The deribit_private.py example uses [python-dotenv](https://github.com/theskumar/python-dotenv), you must either install it if you want the example to work right out of the box,
To run some examples, you may need additional modules, you can install them from the `requirements.txt` file.

```bash
$ pip install python-dotenv
```
or make the corresponding changes, removed followed code.
```python
from dotenv import load_dotenv
dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
load_dotenv(dotenv_path)
$ pip install -r requirements.txt
```

To run the private.py example, you must either fill in the .env file or set the environment variables DERIBIT_CLIENT_ID and DERIBIT_CLIENT_SECRET. Look at .env_default.

```bash
$ PYTHONPATH=.:$PYTHONPATH DERIBIT_CLIENT_ID=YOU_ACCESS_KEY DERIBIT_CLIENT_SECRET=YOU_ACCESS_SECRET python examples/private.py
$ PYTHONPATH=.:$PYTHONPATH DERIBIT_CLIENT_ID=YOU_ACCESS_KEY DERIBIT_CLIENT_SECRET=YOU_ACCESS_SECRET python examples/deribit_private.py
```
21 changes: 12 additions & 9 deletions examples/bitfinex_book.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@
import asyncio
import logging

from ssc2ce.bitfinex import Bitfinex, L2Book
from ssc2ce.bitfinex import Bitfinex, BitfinexL2Book

logging.basicConfig(format='%(asctime)s %(name)s %(funcName)s %(levelname)s %(message)s', level=logging.INFO)
logging.basicConfig(
format='%(asctime)s %(name)s %(funcName)s %(levelname)s %(message)s',
level=logging.INFO)
logger = logging.getLogger("bitfinex-basic-example")

conn = Bitfinex()


class BookMaintainer:
def __init__(self, instrument):
self.book = L2Book(instrument)
self.book = BitfinexL2Book(instrument)
self.check_sum = None
self.active = False
self.top_bid = [0, 0]
self.top_ask = [0, 0]
self.top_bid = 0.
self.top_ask = 0.

async def handle_book(self, message, connector):
if type(message[1]) is str:
Expand All @@ -29,10 +31,11 @@ async def handle_book(self, message, connector):
self.book.handle_snapshot(message)
self.book.time = message[-1]

if self.top_ask[0] != self.book.asks[0][0] or self.top_ask[0] != self.book.asks[0][0]:
self.top_ask = self.book.asks[0].copy()
self.top_bid = self.book.bids[0].copy()
print(f"{self.book.instrument} bid:{self.top_bid[0]} ask:{self.top_ask[0]}")
if self.top_ask != self.book.top_ask_price() or self.top_bid != self.book.top_bid_price():
self.top_ask = self.book.top_ask_price()
self.top_bid = self.book.top_bid_price()
logger.info(
f"{self.book.instrument()} bid:{self.top_bid} ask:{self.top_ask}")


btc = BookMaintainer("BTC-USD")
Expand Down
23 changes: 23 additions & 0 deletions examples/book_watcher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from ssc2ce.deribit.l2_book import L2Book


class BookWatcher:
def __init__(self, parser, print_it: bool = True):
self.betters = {}
self.print_it = print_it
parser.set_on_book_setup(self.handle_book_setup)
parser.set_on_book_update(self.handle_book_update)

def handle_book_setup(self, book: L2Book):
top = [book.top_bid_price(), book.top_ask_price()]
self.betters[book.instrument()] = top
if self.print_it:
print(f"{book.instrument()} bid:{top[0]} ask:{top[1]}")

def handle_book_update(self, book: L2Book):
top = self.betters[book.instrument()]
if top[1] != book.top_ask_price() or top[0] != book.top_bid_price():
top[1] = book.top_ask_price()
top[0] = book.top_bid_price()
if self.print_it:
print(f"{book.instrument()} bid:{top[0]} ask:{top[1]}")
22 changes: 13 additions & 9 deletions examples/deribit_basic_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@
pending = {}


async def handle_instruments(data: dict):
def handle_instruments(data: dict):
del pending[data["id"]]
print(json.dumps(data))
print("handle_instruments", json.dumps(data))
if not pending:
await subscribe()
asyncio.ensure_future(subscribe())


async def handle_currencies(data: dict):
async def get_instruments(symbol):
request_id = await conn.get_instruments(symbol, callback=handle_instruments)
pending[request_id] = symbol


def handle_currencies(data: dict):
for currency in data["result"]:
symbol = currency["currency"]
id = await conn.get_instruments(symbol, callback=handle_instruments)
pending[id] = symbol
asyncio.ensure_future(get_instruments(currency["currency"]))


async def get_currencies():
Expand All @@ -30,12 +33,13 @@ async def subscribe():
await conn.send_public(request={
"method": "public/subscribe",
"params": {
"channels": ["deribit_price_index.btc_usd"]
"channels": ["deribit_price_index.btc_usd",
"deribit_price_index.eth_usd"]
}
})


async def handle_subscription(data):
def handle_subscription(data):
method = data.get("method")
if method and method == "subscription":
if data["params"]["channel"].startswith("deribit_price_index"):
Expand Down
70 changes: 29 additions & 41 deletions examples/deribit_book.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,34 @@
import asyncio
import json
import logging
import sys

from examples.book_watcher import BookWatcher
from ssc2ce import Deribit
from ssc2ce.deribit.l2_book import L2Book

if len(sys.argv) > 1 and "cpp" in sys.argv:
from importlib import util

ssc2ce_cpp_spec = util.find_spec("ssc2ce_cpp")
if ssc2ce_cpp_spec:
from ssc2ce_cpp import DeribitParser
else:
print("You must install the ssc2ce_cpp module to use its features.\n pip install ssc2ce_cpp")
exit(1)
else:
from ssc2ce.deribit.parser import DeribitParser

conn = Deribit()
parser = DeribitParser()
watcher = BookWatcher(parser)

# parser.set_on_unprocessed_message(conn.handle_message)
conn.on_message = parser.parse

pending = {}
books = {}
instruments = []

logging.basicConfig(format='%(asctime)s %(name)s %(funcName)s %(levelname)s %(message)s', level=logging.INFO)
logging.basicConfig(format='%(asctime)s %(name)s %(funcName)s %(levelname)s %(message)s', level=logging.DEBUG)
logger = logging.getLogger("deribit-book")


Expand All @@ -19,17 +38,18 @@ async def handle_instruments(data: dict):
del pending[request_id]
print(json.dumps(data))
if not pending:
await subscribe_books(list(books.keys()))
await conn.send_public(request={
"method": "public/subscribe",
"params": {
"channels": [f"book.{i}.raw" for i in instruments]
}
})


async def handle_currencies(data: dict):
for currency in data["result"]:
symbol = currency["currency"]
instrument = symbol+"-PERPETUAL"
book = L2Book(instrument)
book.top_bid = [0., 0.]
book.top_ask = [0., 0.]
books[instrument] = book
instruments.append(symbol + "-PERPETUAL")
request_id = await conn.get_instruments(symbol, kind="future", callback=handle_instruments)
pending[request_id] = symbol

Expand All @@ -38,39 +58,7 @@ async def get_currencies():
await conn.get_currencies(handle_currencies)


async def subscribe_books(instruments: list):
await conn.send_public(request={
"method": "public/subscribe",
"params": {
"channels": [f"book.{i}.raw" for i in instruments]
}
})


async def handle_subscription(data):
method = data.get("method")
if method and method == "subscription":
params = data["params"]
channel = params["channel"]
if channel.startswith("book."):
params_data = params["data"]
instrument = params_data["instrument_name"]
book: L2Book = books[instrument]
if "prev_change_id" in params_data:
book.handle_update(params_data)
else:
book.handle_snapshot(params_data)

if book.top_ask[0] != book.asks[0][0] or book.top_bid[0] != book.bids[0][0]:
book.top_ask = book.asks[0].copy()
book.top_bid = book.bids[0].copy()
print(f"{instrument} bid:{book.top_bid[0]} ask:{book.top_ask[0]}")
else:
print("Unknown channel", json.dumps(data))


conn.on_connect_ws = get_currencies
conn.method_routes += [("subscription", handle_subscription)]

loop = asyncio.get_event_loop()

Expand Down
47 changes: 47 additions & 0 deletions examples/deribit_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import asyncio
import logging
from time import time

from examples.book_watcher import BookWatcher

import sys

if len(sys.argv) > 1 and "cpp" in sys.argv:
from importlib import util

ssc2ce_cpp_spec = util.find_spec("ssc2ce_cpp")
if ssc2ce_cpp_spec:
from ssc2ce_cpp import DeribitParser
else:
print("You must install the ssc2ce_cpp module to use its features.\n pip install ssc2ce_cpp")
exit(1)
else:
from ssc2ce.deribit.parser import DeribitParser

logging.basicConfig(format='%(asctime)s %(name)s %(funcName)s %(levelname)s %(message)s', level=logging.INFO)
logger = logging.getLogger("deribit-parser")


class FileController:
def __init__(self):
self.parser = DeribitParser()
self.counter = 0
self.watcher = BookWatcher(self.parser, False)

def run(self, filename: str):
start = time()
i = 0
with open(filename) as f:
for line in f:
i += 1
if not self.parser.parse(line):
self.handle_message(line)

logger.info(f"{i} in {time() - start} sec. {self.counter}")

def handle_message(self, message: str) -> None:
self.counter += 1
# logger.info(message[:-1])


FileController().run("dump.txt")
Loading