Python websocket client (SignalR) for getting live streaming data from Bittrex Exchange.
The library is mainly written in Python3 but should support Python2 with the same functionality, please report any issues.
I am not associated with Bittrex. Use the library at your own risk, I don't bear any responsibility if you end up losing your money.
As of 24 Nov 2017, there is still no official websocket documentation.
The code is licensed under the MIT license. Please consider the following message:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
- bittrex-websocket
- Table of contents
- Testing needed
- What can I use it for?
- Motivation
- Road map
- Dependencies
- Installation
- Methods
- Message channels
- Sample usage
- Change log
- Other libraries
- Support
It's a newly published library so please test it and report for any bugs. Much appreciated.
You can use it for various purposes, some examples include:
- maintaining live order book
- recording trade history
- analysing order flow
Use your imagination.
I am fairly new to Python and in my experience the best way to learn something is through actual practice. At the same time I am currently actively trading on Bittrex, hence the reason why I decided to build the Bittrex websocket client. I am publishing my code for a few reasons. Firstly, I want to make a contribution to the community, secondly the code needs lots of improvements and it would be great to work on it as a team, thirdly I haven't seen any other Python Bittrex websocket clients so far.
I have been largely motivated by the following projects and people:
-
Daniel Paquin: gdax-python - a websocket client for GDAX. The project really helped me around using threads and structuring the code.
-
David Parlevliet - saw his SignalR code initially which included Bittrex specific commands. Saved me a lot of time in researching.
-
Eric Somdahl: python-bittrex - great python bindings for Bittrex. Highly recommend it, I use it in conjuction with the websocket client.
Version 0.0.6 will change the names of the unsubscribe methods from:
unsubscribe_to_orderbook
unsubscribe_to_orderbook_update
unsubscribe_to_trades
unsubscribe_to_ticker_update
to
unsubscribe_from_orderbook
unsubscribe_from_orderbook_update
unsubscribe_from_trades
unsubscribe_from_ticker_update
- Socket reconnection handling
- Implemented but experimental
- Test scripts
- Code cleanup, optimization
- Lots of stuff, waiting for suggestions
More user friendly subscription to the exchange channels.Pypi
To successfully install the package the following dependencies must be met:
-
If you receive
Missing Node.js runtime. Node is required...
error as documented in Issue #12, you will have to install Node.js. This error usually shows for Windows users. -
- g++, make, libffi-dev, openssl-dev
-
- g++, make
I have added a Dockerfile for а quick setup. Please check the docker folder. The example.py is not always up to date.
I am only adding this as a precaution, in most case you will not have to do anything at all as these are prepackaged with your python installation.
pip install bittrex-websocket
pip install git+https://github.com/slazarov/python-bittrex-websocket.git
pip install git+https://github.com/slazarov/python-bittrex-websocket.git@next-version-number
def subscribe_to_orderbook(self, tickers, book_depth=10):
"""
Subscribe and maintain the live order book for a set of ticker(s).
:param tickers: A list of tickers you are interested in.
:type tickers: []
:param book_depth: The desired depth of the order book to be maintained.
:type book_depth: int
"""
def subscribe_to_orderbook_update(self, tickers):
"""
Subscribe to order book updates for a set of ticker(s).
:param tickers: A list of tickers you are interested in.
:type tickers: []
"""
def subscribe_to_trades(self, tickers):
"""
Subscribe and receive tick data(executed trades) for a set of ticker(s).
:param tickers: A list of tickers you are interested in.
:type tickers: []
"""
def subscribe_to_ticker_update(self, tickers):
"""
Subscribe and receive general data updates for a set of ticker(s). Example output:
{
'MarketName': 'BTC-ADA',
'High': 7.65e-06,
'Low': 4.78e-06,
'Volume': 1352355429.5288217,
'Last': 7.2e-06,
'BaseVolume': 7937.59243908,
'TimeStamp': '2017-11-28T15:02:17.7',
'Bid': 7.2e-06,
'Ask': 7.21e-06,
'OpenBuyOrders': 4452,
'OpenSellOrders': 3275,
'PrevDay': 5.02e-06,
'Created': '2017-09-29T07:01:58.873'
}
:param tickers: A list of tickers you are interested in.
:type tickers: []
"""
def unsubscribe_from_orderbook(self, tickers):
"""
Unsubscribe from real time order for specific set of ticker(s).
:param tickers: A list of tickers you are interested in.
:type tickers: []
"""
def unsubscribe_from_orderbook_update(self, tickers):
"""
Unsubscribe from order book updates for a set of ticker(s).
:param tickers: A list of tickers you are interested in.
:type tickers: []
"""
def unsubscribe_from_trades(self, tickers):
"""
Unsubscribe from receiving tick data(executed trades) for a set of ticker(s)
:param tickers: A list of tickers you are interested in.
:type tickers: []
"""
def unsubscribe_from_ticker_update(self, tickers):
"""
Unsubscribe from receiving general data updates for a set of ticker(s).
:param tickers: A list of tickers you are interested in.
:type tickers: []
"""
def get_order_book(self, ticker=None):
"""
Returns the most recently updated order book for the specific ticker.
If no ticker is specified, returns a dictionary with the order books of
all subscribed tickers.
:param ticker: The specific ticker you want the order book for.
:type ticker: str
"""
def get_order_book_sync_state(self, tickers=None):
"""
Returns the sync state of the order book for the specific ticker(s).
If no ticker is specified, returns the state for all tickers.
The sync states are:
Not initiated = 0
Invoked, not synced = 1
Received, not synced, not processing = 2
Received, synced, processing = 3
:param tickers: The specific ticker(s) and it's order book sync state you are interested in.
:type tickers: []
"""
def disconnect(self):
"""
Disconnects the connections and stops the websocket instance.
"""
def enable_log(file_name=None):
"""
Enables logging.
:param file_name: The name of the log file, located in the same directory as the executing script.
:type file_name: str
"""
def disable_log():
"""
Disables logging.
"""
The websocket clients starts a separate thread upon initialization with further subthreads for each connection (currently 20 tickers per connection). There are several methods which could be overwritten. Please check the actual code for further information and examples.
def on_open(self):
# Called before initiating the first websocket connection
# Use it when you want to add some opening logic.
def on_close(self):
# Called before closing the websocket instance.
# Use it when you want to add any closing logic.
def on_error(self, error):
# Error handler
def on_orderbook(self, msg):
# The main channel of subscribe_to_orderbook().
def on_orderbook_update(self, msg):
# The main channel of subscribe_to_orderbook_update().
def on_trades(self, msg):
# The main channel of subscribe_to_trades().
def on_ticker_update(self, msg):
# The main channel of subscribe_to_ticker_update().
To receive live data feed you must instantiate the websocket and use one of its subscribe methods. For the various subscription methods check above. Example:
# Tickers
tickers = ['ETH-ZEC'] # use lists
ws = MySocket()
ws.subscribe_to_orderbook(tickers)
# Do some stuff and trade for infinite profit
ws.disconnect()
# Tickers
tickers = ['BTC-ETH', 'BTC-NEO', 'BTC-ZEC', 'ETH-NEO', 'ETH-ZEC'] # use lists
ws = MySocket()
ws.subscribe_to_ticker_update(tickers)
# Do some stuff and trade for infinite profit
ws.disconnect()
Let's get some 'practical' examples.
Note that with library updates, the methods and data structure might change, so check the examples folder for the most up to date examples.
from __future__ import print_function
from time import sleep
from bittrex_websocket.websocket_client import BittrexSocket
def main():
class MySocket(BittrexSocket):
def on_open(self):
self.trade_history = {}
def on_trades(self, msg):
# Create entry for the ticker in the trade_history dict
if msg['ticker'] not in self.trade_history:
self.trade_history[msg['ticker']] = []
# Add history nounce
self.trade_history[msg['ticker']].append(msg)
# Ping
print('[Trades]: {}'.format(msg['ticker']))
# Create the socket instance
ws = MySocket()
# Enable logging
ws.enable_log()
# Define tickers
tickers = ['BTC-ETH', 'BTC-XMR']
# Subscribe to trade fills
ws.subscribe_to_trades(tickers)
while len(set(tickers) - set(ws.trade_history)) > 0:
sleep(1)
else:
for ticker in ws.trade_history.keys():
print('Printing {} trade history.'.format(ticker))
for trade in ws.trade_history[ticker]:
print(trade)
ws.disconnect()
if __name__ == "__main__":
main()
from __future__ import print_function
from time import sleep
from bittrex_websocket.websocket_client import BittrexSocket
def main():
class MySocket(BittrexSocket):
def on_orderbook(self, msg):
print('[OrderBook]: {}'.format(msg['MarketName']))
# Create the socket instance
ws = MySocket()
# Enable logging
ws.enable_log()
# Define tickers
tickers = ['BTC-ETH', 'BTC-NEO', 'BTC-ZEC', 'ETH-NEO', 'ETH-ZEC']
# Subscribe to live order book
ws.subscribe_to_orderbook(tickers)
while True:
i = 0
sync_states = ws.get_order_book_sync_state()
for state in sync_states.values():
if state == 3:
i += 1
if i == len(tickers):
print('We are fully synced. Hooray!')
for ticker in tickers:
ob = ws.get_order_book(ticker)
name = ob['MarketName']
quantity = str(ob['Buys'][0]['Quantity'])
price = str(ob['Buys'][0]['Rate'])
print('Ticker: ' + name + ', Bids depth 0: ' + quantity + '@' + price)
ws.disconnect()
break
else:
sleep(1)
if __name__ == "__main__":
main()
from __future__ import print_function
from time import sleep
from bittrex_websocket.websocket_client import BittrexSocket
def main():
class MySocket(BittrexSocket):
def on_open(self):
self.ticker_updates_container = {}
def on_ticker_update(self, msg):
name = msg['MarketName']
if name not in self.ticker_updates_container:
self.ticker_updates_container[name] = msg
print('Just received ticker update for {}.'.format(name))
# Create the socket instance
ws = MySocket()
# Enable logging
ws.enable_log()
# Define tickers
tickers = ['BTC-ETH', 'BTC-NEO', 'BTC-ZEC', 'ETH-NEO', 'ETH-ZEC']
# Subscribe to ticker information
ws.subscribe_to_ticker_update(tickers)
while len(ws.ticker_updates_container) < len(tickers):
sleep(1)
else:
print('We have received updates for all tickers. Closing...')
ws.disconnect()
if __name__ == "__main__":
main()
0.0.6.2.2
- Update cfscrape>=1.9.2 and gevent>=1.3a1
- Reorder imports in websocket_client to safeguard against SSL recursion errors.
0.0.6.2
- Every 5400s (1hr30) the script will force reconnection.
- Every reconnection (including the above) will be done with a fresh cookie
- Upon reconnection the script will check if the connection has been running for more than 600s (10mins). If it has been running for less it will use the backup url.
0.0.6.1
- Set websocket-client==0.46.0
0.0.6
- Reconnection - Experimental
- Fixed a bug when subscribing to multiple subscription types at once resulted in opening unnecessary connections even though there is sufficient capacity in the existing Commit 7fd21c
- Numerous code optimizations
0.0.5.1
- Updated cfscrape minimum version requirement (Issue #12).
0.0.5
- Fixed Issue #9 relating to
subscribe_to_orderbook_update
handling in internal method_on_tick_update
- Added custom logger as per PR #10 and Issue #8 in order to avoid conflicts with other
basicConfig
setups- Added two new methods
enable_log
anddisable_log
. Check Other Methods. - Logging is now disabled on startup. You have to enable it.
- Added two new methods
- Experimental: Calling
subscribe_to_ticker_update
without a specified ticker subscribes to all tickers in the message stream (Issue #4). - Minor code optimizations (removed unnecessary class Common)
0.0.4 - Changed the behaviour of how on_ticker_update channel works: The message now contains a single ticker instead of a dictionary of all subscribed tickers.
0.0.3 - Removed left over code from initial release version that was throwing errors (had no effect on performance).
0.0.2 - Major improvements:
- Additional un/subscribe and order book sync state querying methods added.
- Better connection and thread management.
- Code optimisations
- Better code documentation
- Added additional connection URLs
0.0.1 - Initial release on github.
Python CLI tool to auto sell coins on Bittrex.
It is used in the cases when you want to auto sell a specific coin for another, but there is no direct market, so you have to use an intermediate market.