Merge pull request #25 from JacobPlaster/support-derivatives

Support derivatives
This commit is contained in:
Paolo Ardoino
2019-07-19 10:33:38 +01:00
committed by GitHub
15 changed files with 231 additions and 14 deletions

View File

@@ -1,3 +1,11 @@
1.0.1
- Added ws event `status_update`
- Added rest function `get_derivative_status`
- Added rest function `get_derivative_statuses`
- Added rest function `set_derivative_collateral`
- Added channel support `status`
1.0.0
- Removal of camel-casing file naming and git duplicates

View File

@@ -114,6 +114,7 @@ The websocket exposes a collection of events that are triggered when certain dat
- `order_snapshot` (array[Order]): Initial open orders (Fired once)
- `positions_snapshot` (array): Initial open positions (Fired once)
- `wallet_update` (Wallet): changes to the balance of wallets
- `status_update` (json): new platform status info
- `seed_candle` (json): initial past candle to prime strategy
- `seed_trade` (json): initial past trade to prime strategy
- `funding_offer_snapshot` (array): opening funding offer balances
@@ -227,6 +228,14 @@ Get the public orderbook of a given symbol
Get tickers for the given symbols. Tickers shows you the current best bid and ask,
as well as the last trade price.
### `get_derivative_status(symbol)`
Get derivative platform information for the given symbol.
### `get_derivative_statuses(symbols)`
Get derivative platform information for the given collection of symbols.
### `get_wallets()`
Get all wallets on account associated with API_KEY - Requires authentication.
@@ -277,6 +286,10 @@ Get the public orderbook of a given symbol
Get all of the funding credits between the start and end period associated with API_KEY - Requires authentication.
### `set_derivative_collateral(symbol, collateral)`
Set the amount of collateral used to back a derivative position.
# Examples
For more info on how to use this library please see the example scripts in the `bfxapi/examples` directory. Here you will find usage of all interface exposed functions for both the rest and websocket.

View File

@@ -12,8 +12,7 @@ API_SECRET=os.getenv("BFX_SECRET")
bfx = Client(
API_KEY=API_KEY,
API_SECRET=API_SECRET,
logLevel='DEBUG',
rest_host='https://test.bitfinex.com/v2'
logLevel='DEBUG'
)
now = int(round(time.time() * 1000))

View File

@@ -38,12 +38,18 @@ async def log_mul_tickers():
print ("Tickers:")
print (tickers)
async def log_derivative_status():
status = await bfx.rest.get_derivative_status('tBTCF0:USTF0')
print ("Deriv status:")
print (status)
async def run():
await log_historical_candles()
await log_historical_trades()
await log_books()
await log_ticker()
await log_mul_tickers()
await log_derivative_status()
t = asyncio.ensure_future(run())
asyncio.get_event_loop().run_until_complete(t)

View File

@@ -0,0 +1,23 @@
import os
import sys
sys.path.append('../')
from bfxapi import Client
bfx = Client(
logLevel='INFO'
)
@bfx.ws.on('error')
def log_error(err):
print ("Error: {}".format(err))
@bfx.ws.on('status_update')
def log_msg(msg):
print (msg)
async def start():
await bfx.ws.subscribe_derivative_status('tBTCF0:USTF0')
bfx.ws.on('connected', start)
bfx.ws.run()

View File

@@ -21,13 +21,13 @@ class Subscription:
such as unsibscribe and subscribe.
"""
def __init__(self, socket, channel_name, symbol, timeframe=None, **kwargs):
def __init__(self, socket, channel_name, symbol, key=None, timeframe=None, **kwargs):
self.socket = socket
self.channel_name = channel_name
self.symbol = symbol
self.timeframe = timeframe
self.is_subscribed_bool = False
self.key = None
self.key = key
self.chan_id = None
if timeframe:
self.key = 'trade:{}:{}'.format(self.timeframe, self.symbol)
@@ -79,7 +79,7 @@ class Subscription:
def _generate_payload(self, **kwargs):
payload = {'event': 'subscribe',
'channel': self.channel_name, 'symbol': self.symbol}
if self.timeframe:
if self.timeframe or self.key:
payload['key'] = self.key
payload.update(**kwargs)
return payload

1
bfxapi/rest/__init__.py Normal file
View File

@@ -0,0 +1 @@
NAME = 'rest'

View File

@@ -33,7 +33,7 @@ class BfxRest:
async def fetch(self, endpoint, params=""):
"""
Fetch a GET request from the bitfinex host
Send a GET request to the bitfinex api
@return reponse
"""
@@ -49,7 +49,7 @@ class BfxRest:
async def post(self, endpoint, data={}, params=""):
"""
Request a POST to the bitfinex host
Send a pre-signed POST request to the bitfinex api
@return response
"""
@@ -151,7 +151,7 @@ class BfxRest:
Get tickers for the given symbol. Tickers shows you the current best bid and ask,
as well as the last trade price.
@parms symbols symbol string: pair symbol i.e tBTCUSD
@param symbols symbol string: pair symbol i.e tBTCUSD
@return Array [ SYMBOL, BID, BID_SIZE, ASK, ASK_SIZE, DAILY_CHANGE,
DAILY_CHANGE_PERC, LAST_PRICE, VOLUME, HIGH, LOW ]
"""
@@ -164,7 +164,7 @@ class BfxRest:
Get tickers for the given symbols. Tickers shows you the current best bid and ask,
as well as the last trade price.
@parms symbols Array<string>: array of symbols i.e [tBTCUSD, tETHUSD]
@param symbols Array<string>: array of symbols i.e [tBTCUSD, tETHUSD]
@return Array [ SYMBOL, BID, BID_SIZE, ASK, ASK_SIZE, DAILY_CHANGE, DAILY_CHANGE_PERC,
LAST_PRICE, VOLUME, HIGH, LOW ]
"""
@@ -172,6 +172,31 @@ class BfxRest:
ticker = await self.fetch(endpoint)
return ticker
async def get_derivative_status(self, symbol):
"""
Gets platform information for derivative symbol.
@param derivativeSymbol string: i.e tBTCF0:USTF0
@return [KEY/SYMBOL, MTS, PLACEHOLDER, DERIV_PRICE, SPOT_PRICE, PLACEHOLDER, INSURANCE_FUND_BALANCE4,
PLACEHOLDER, PLACEHOLDER, FUNDING_ACCRUED, FUNDING_STEP, PLACEHOLDER]
"""
statuses = await self.get_derivative_statuses([symbol])
if len(statuses) > 0:
return statuses[0]
return []
async def get_derivative_statuses(self, symbols):
"""
Gets platform information for a collection of derivative symbols.
@param derivativeSymbols Array<string>: array of symbols i.e [tBTCF0:USTF0 ...] or ["ALL"]
@return [KEY/SYMBOL, MTS, PLACEHOLDER, DERIV_PRICE, SPOT_PRICE, PLACEHOLDER, INSURANCE_FUND_BALANCE4,
PLACEHOLDER, PLACEHOLDER, FUNDING_ACCRUED, FUNDING_STEP, PLACEHOLDER]
"""
endpoint = "status/deriv?keys={}".format(','.join(symbols))
status = await self.fetch(endpoint)
return status
##################################################
# Authenticated Data #
##################################################
@@ -327,7 +352,7 @@ class BfxRest:
return [FundingCredit.from_raw_credit(c) for c in credits]
##################################################
# Orders #
# Orders #
##################################################
async def __submit_order(self, symbol, amount, price, oType=Order.Type.LIMIT,
@@ -368,3 +393,20 @@ class BfxRest:
payload['sell_price_oco'] = stop_sell_price
endpoint = 'order/new'
return await self.post(endpoint, data=payload)
##################################################
# Derivatives #
##################################################
async def set_derivative_collateral(self, symbol, collateral):
"""
Update the amount of callateral used to back a derivative position.
@param symbol of the derivative i.e 'tBTCF0:USTF0'
@param collateral: amount of collateral/value to apply to the open position
"""
endpoint = 'auth/w/deriv/collateral/set'
payload = {}
payload['symbol'] = symbol
payload['collateral'] = collateral
return await self.post(endpoint, data=payload)

1
bfxapi/utils/__init__.py Normal file
View File

@@ -0,0 +1 @@
NAME = 'utils'

View File

@@ -2,4 +2,4 @@
This module contains the current version of the bfxapi lib
"""
__version__ = '1.0.0'
__version__ = '1.0.1'

View File

@@ -0,0 +1 @@
NAME = 'websockets'

View File

@@ -65,6 +65,23 @@ def _parse_trade(tData, symbol):
'symbol': symbol
}
def _parse_deriv_status_update(sData, symbol):
return {
'symbol': symbol,
'status_type': 'deriv',
'mts': sData[0],
# placeholder
'deriv_price': sData[2],
'spot_price': sData[3],
# placeholder
'insurance_fund_balance': sData[5],
# placeholder
# placeholder
'funding_accrued': sData[8],
'funding_step': sData[9],
# placeholder
}
class BfxWebsocket(GenericWebsocket):
"""
@@ -167,6 +184,8 @@ class BfxWebsocket(GenericWebsocket):
await self._order_book_handler(data, raw_message_str)
if subscription.channel_name == 'trades':
await self._trade_handler(data)
if subscription.channel_name == 'status':
await self._status_handler(data)
else:
self.logger.warn(
"Unknown data event: '{}' {}".format(dataEvent, data))
@@ -293,6 +312,18 @@ class BfxWebsocket(GenericWebsocket):
self._emit('funding_credit_snapshot', data[2])
self.logger.info("Funding credit snapshot: {}".format(data))
async def _status_handler(self, data):
sub = self.subscriptionManager.get(data[0])
symbol = sub.symbol
status_type = sub.key.split(":")[0]
rstatus = data[1]
if status_type == "deriv":
status = _parse_deriv_status_update(rstatus, symbol)
if status:
self._emit('status_update', status)
else:
self.logger.warn('Unknown status data type: {}'.format(data))
async def _trade_handler(self, data):
symbol = self.subscriptionManager.get(data[0]).symbol
if type(data[1]) is list:
@@ -439,6 +470,22 @@ class BfxWebsocket(GenericWebsocket):
if socket.isConnected:
await socket.ws.send(json.dumps(payload))
async def subscribe_order_book(self, symbol):
return await self.subscribe('book', symbol)
async def subscribe_candles(self, symbol, timeframe):
return await self.subscribe('candles', symbol, timeframe=timeframe)
async def subscribe_trades(self, symbol):
return await self.subscribe('trades', symbol)
async def subscribe_ticker(self, symbol):
return await self.subscribe('ticker', symbol)
async def subscribe_derivative_status(self, symbol):
key = 'deriv:{}'.format(symbol)
return await self.subscribe('status', symbol, key=key)
async def subscribe(self, *args, **kwargs):
return await self.subscriptionManager.subscribe(*args, **kwargs)

View File

@@ -32,7 +32,7 @@ class SubscriptionManager:
count += 1
return count
async def subscribe(self, channel_name, symbol, timeframe=None, **kwargs):
async def subscribe(self, channel_name, symbol, key=None, timeframe=None, **kwargs):
"""
Subscribe to a new channel
@@ -51,7 +51,7 @@ class SubscriptionManager:
socket = self.bfxapi.get_most_available_socket()
# create a new subscription
subscription = Subscription(
socket, channel_name, symbol, timeframe, **kwargs)
socket, channel_name, symbol, key, timeframe, **kwargs)
self.logger.info("Subscribing to channel {}".format(channel_name))
self.pending_subscriptions[subscription.get_key()] = subscription

View File

@@ -10,4 +10,4 @@ disable=too-few-public-methods,
too-many-instance-attributes,
invalid-name
ignore=tests
ignore=tests,websockets,rest,utils

76
setup.py Normal file
View File

@@ -0,0 +1,76 @@
"""A setuptools based setup module.
See:
https://packaging.python.org/guides/distributing-packages-using-setuptools/
https://github.com/pypa/sampleproject
"""
# Always prefer setuptools over distutils
from setuptools import setup, find_packages
from os import path
# io.open is needed for projects that support Python 2.7
# It ensures open() defaults to text mode with universal newlines,
# and accepts an argument to specify the text encoding
# Python 3 only projects can skip this import
from io import open
here = path.abspath(path.dirname(__file__))
# Arguments marked as "Required" below must be included for upload to PyPI.
# Fields marked as "Optional" may be commented out.
setup(
name='bitfinex-api-py',
version='1.0.1', # Required
description='Official Bitfinex API', # Optional
long_description='This is an official python library that is used to connect interact with the Bitfinex api.', # Optional
long_description_content_type='text/markdown', # Optional
url='https://github.com/bitfinexcom/bitfinex-api-py', # Optional
author='Bitfinex', # Optional
author_email='support@bitfinex.com', # Optional
classifiers=[ # Optional
# How mature is this project? Common values are
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
'Development Status :: 4 - Beta',
# Indicate who your project is intended for
'Intended Audience :: Developers',
'Topic :: Software Development :: Build Tools',
# Pick your license as you wish
'License :: OSI Approved :: Apache 2.0',
# Specify the Python versions you support here. In particular, ensure
# that you indicate whether you support Python 2, Python 3 or both.
# These classifiers are *not* checked by 'pip install'. See instead
# 'python_requires' below.
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
],
keywords='bitfinex', # Optional
packages=find_packages(exclude=['examples', 'tests']), # Required
# Specify which Python versions you support. In contrast to the
# 'Programming Language' classifiers above, 'pip install' will check this
# and refuse to install the project if the version does not match. If you
# do not support Python 2, you can simplify this to '>=3.5' or similar, see
# https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4',
# This field lists other packages that your project depends on to run.
# Any package you put here will be installed by pip when your project is
# installed, so they must be valid existing projects.
#
# For an analysis of "install_requires" vs pip's requirements files see:
# https://packaging.python.org/en/latest/requirements.html
install_requires=['eventemitter', 'asyncio', 'websockets', 'pylint', 'six', 'pyee', 'aiohttp'], # Optional
project_urls={ # Optional
'Bug Reports': 'https://github.com/bitfinexcom/bitfinex-api-py/issues',
'Source': 'https://github.com/bitfinexcom/bitfinex-api-py',
},
)