mirror of
https://github.com/wassname/catalyst.git
synced 2026-08-12 11:50:11 +08:00
Initial work on bittrex implementation
This commit is contained in:
@@ -201,8 +201,8 @@ def ipython_only(option):
|
||||
)
|
||||
@click.option(
|
||||
'-n',
|
||||
'--algo-name',
|
||||
help='A label assigned to the algorithm for tracking purposes.',
|
||||
'--algo-namespace',
|
||||
help='A label assigned to the algorithm for tracking purposes. '
|
||||
)
|
||||
@click.option(
|
||||
'-c',
|
||||
@@ -381,14 +381,14 @@ def ingest(bundle, compile_locally, assets_version, show_progress):
|
||||
'--before',
|
||||
type=Timestamp(),
|
||||
help='Clear all data before TIMESTAMP.'
|
||||
' This may not be passed with -k / --keep-last',
|
||||
' This may not be passed with -k / --keep-last',
|
||||
)
|
||||
@click.option(
|
||||
'-a',
|
||||
'--after',
|
||||
type=Timestamp(),
|
||||
help='Clear all data after TIMESTAMP'
|
||||
' This may not be passed with -k / --keep-last',
|
||||
' This may not be passed with -k / --keep-last',
|
||||
)
|
||||
@click.option(
|
||||
'-k',
|
||||
@@ -396,7 +396,7 @@ def ingest(bundle, compile_locally, assets_version, show_progress):
|
||||
type=int,
|
||||
metavar='N',
|
||||
help='Clear all but the last N downloads.'
|
||||
' This may not be passed with -e / --before or -a / --after',
|
||||
' This may not be passed with -e / --before or -a / --after',
|
||||
)
|
||||
def clean(bundle, before, after, keep_last):
|
||||
"""Clean up data downloaded with the ingest command.
|
||||
|
||||
@@ -41,7 +41,6 @@ class Bitfinex(Exchange):
|
||||
self.url = BITFINEX_URL
|
||||
self.key = key
|
||||
self.secret = secret
|
||||
self.id = 'b'
|
||||
self.name = 'bitfinex'
|
||||
self.assets = {}
|
||||
self.load_assets()
|
||||
@@ -220,26 +219,6 @@ class Bitfinex(Exchange):
|
||||
portfolio.portfolio_value = \
|
||||
portfolio.positions_value + portfolio.cash
|
||||
|
||||
@property
|
||||
def portfolio(self):
|
||||
"""
|
||||
Return the Portfolio
|
||||
|
||||
:return:
|
||||
"""
|
||||
# if self._portfolio is None:
|
||||
# portfolio = ExchangePortfolio(
|
||||
# start_date=pd.Timestamp.utcnow()
|
||||
# )
|
||||
# self.store.portfolio = portfolio
|
||||
# self.update_portfolio()
|
||||
#
|
||||
# portfolio.starting_cash = portfolio.cash
|
||||
# else:
|
||||
# portfolio = self.store.portfolio
|
||||
|
||||
return self._portfolio
|
||||
|
||||
@property
|
||||
def account(self):
|
||||
account = Account()
|
||||
@@ -273,8 +252,9 @@ class Bitfinex(Exchange):
|
||||
# TODO: research the time skew conditions
|
||||
return pd.Timedelta('0s')
|
||||
|
||||
def subscribe_to_market_data(self, symbol):
|
||||
pass
|
||||
def get_account(self):
|
||||
# TODO: fetch account data and keep in cache
|
||||
return None
|
||||
|
||||
def get_candles(self, data_frequency, assets, bar_count=None):
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
from logbook import Logger
|
||||
from six.moves import urllib
|
||||
import json
|
||||
import pandas as pd
|
||||
|
||||
from catalyst.exchange.exchange import Exchange
|
||||
from catalyst.exchange.bittrex.bittrex_api import Bittrex_api
|
||||
|
||||
log = Logger('Bittrex')
|
||||
|
||||
|
||||
class Bittrex(Exchange):
|
||||
def __init__(self, key, secret, base_currency, portfolio=None):
|
||||
self.api = Bittrex_api(key=key, secret=secret)
|
||||
self.name = 'bittrex'
|
||||
|
||||
self.assets = dict()
|
||||
self.load_assets()
|
||||
|
||||
@property
|
||||
def account(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def portfolio(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def positions(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def time_skew(self):
|
||||
pass
|
||||
|
||||
def sanitize_curency_symbol(self, exchange_symbol):
|
||||
"""
|
||||
Helper method used to build the universal pair.
|
||||
Include any symbol mapping here if appropriate.
|
||||
|
||||
:param exchange_symbol:
|
||||
:return universal_symbol:
|
||||
"""
|
||||
return exchange_symbol.lower()
|
||||
|
||||
def fetch_symbol_map(self):
|
||||
"""
|
||||
Since Bittrex gives us a complete dictionary of symbols,
|
||||
we can build the symbol map ad-hoc as opposed to maintaining
|
||||
a static file. We must be careful with mapping any unconventional
|
||||
symbol name as appropriate.
|
||||
|
||||
:return symbol_map:
|
||||
"""
|
||||
symbol_map = dict()
|
||||
|
||||
markets = self.api.getmarkets()
|
||||
for market in markets:
|
||||
exchange_symbol = market['MarketName']
|
||||
symbol = '{market}_{base}'.format(
|
||||
market=self.sanitize_curency_symbol(market['MarketCurrency']),
|
||||
base=self.sanitize_curency_symbol(market['BaseCurrency'])
|
||||
)
|
||||
symbol_map[exchange_symbol] = dict(
|
||||
symbol=symbol,
|
||||
start_date=pd.to_datetime(market['Created'], utc=True)
|
||||
)
|
||||
|
||||
return symbol_map
|
||||
|
||||
def update_portfolio(self):
|
||||
pass
|
||||
|
||||
def order(self):
|
||||
log.info('creating order')
|
||||
pass
|
||||
|
||||
def get_open_orders(self, asset):
|
||||
pass
|
||||
|
||||
def open_orders(self):
|
||||
log.info('retrieving open orders')
|
||||
pass
|
||||
|
||||
def get_order(self):
|
||||
log.info('retrieving order')
|
||||
pass
|
||||
|
||||
def cancel_order(self):
|
||||
log.info('cancel order')
|
||||
pass
|
||||
|
||||
def get_candles(self):
|
||||
log.info('retrieving candles')
|
||||
url = 'https://bittrex.com/Api/v2.0/pub/market/GetTicks?marketName=BTC-NEO&tickInterval=day&_=1499127220008'
|
||||
with urllib.request.urlopen(url) as url:
|
||||
data = json.loads(url.read().decode())
|
||||
result = data['result']
|
||||
pass
|
||||
|
||||
def tickers(self):
|
||||
log.info('retrieving tickers')
|
||||
pass
|
||||
|
||||
def get_account(self):
|
||||
log.info('retrieving account data')
|
||||
pass
|
||||
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env python
|
||||
import json
|
||||
import time
|
||||
import hmac
|
||||
import hashlib
|
||||
|
||||
from six.moves import urllib
|
||||
|
||||
# Workaround for backwards compatibility
|
||||
# https://stackoverflow.com/questions/3745771/urllib-request-in-python-2-7
|
||||
urlopen = urllib.request.urlopen
|
||||
|
||||
|
||||
class Bittrex_api(object):
|
||||
def __init__(self, key, secret):
|
||||
self.key = key
|
||||
self.secret = secret
|
||||
self.public = ['getmarkets', 'getcurrencies', 'getticker',
|
||||
'getmarketsummaries', 'getmarketsummary',
|
||||
'getorderbook', 'getmarkethistory']
|
||||
self.market = ['buylimit', 'buymarket', 'selllimit', 'sellmarket',
|
||||
'cancel', 'getopenorders']
|
||||
self.account = ['getbalances', 'getbalance', 'getdepositaddress',
|
||||
'withdraw', 'getorder', 'getorderhistory',
|
||||
'getwithdrawalhistory', 'getdeposithistory']
|
||||
|
||||
def query(self, method, values={}):
|
||||
if method in self.public:
|
||||
url = 'https://bittrex.com/api/v1.1/public/'
|
||||
elif method in self.market:
|
||||
url = 'https://bittrex.com/api/v1.1/market/'
|
||||
elif method in self.account:
|
||||
url = 'https://bittrex.com/api/v1.1/account/'
|
||||
else:
|
||||
return 'Something went wrong, sorry.'
|
||||
|
||||
url += method + '?' + urllib.parse.urlencode(values)
|
||||
|
||||
if method not in self.public:
|
||||
url += '&apikey=' + self.key
|
||||
url += '&nonce=' + str(int(time.time()))
|
||||
signature = hmac.new(self.secret, url, hashlib.sha512).hexdigest()
|
||||
headers = {'apisign': signature}
|
||||
else:
|
||||
headers = {}
|
||||
|
||||
req = urllib.request.Request(url, headers=headers)
|
||||
response = json.loads(urlopen(req).read())
|
||||
|
||||
if response["result"]:
|
||||
return response["result"]
|
||||
else:
|
||||
return response["message"]
|
||||
|
||||
def getmarkets(self):
|
||||
return self.query('getmarkets')
|
||||
|
||||
def getcurrencies(self):
|
||||
return self.query('getcurrencies')
|
||||
|
||||
def getticker(self, market):
|
||||
return self.query('getticker', {'market': market})
|
||||
|
||||
def getmarketsummaries(self):
|
||||
return self.query('getmarketsummaries')
|
||||
|
||||
def getmarketsummary(self, market):
|
||||
return self.query('getmarketsummary', {'market': market})
|
||||
|
||||
def getorderbook(self, market, type, depth=20):
|
||||
return self.query('getorderbook',
|
||||
{'market': market, 'type': type, 'depth': depth})
|
||||
|
||||
def getmarkethistory(self, market, count=20):
|
||||
return self.query('getmarkethistory',
|
||||
{'market': market, 'count': count})
|
||||
|
||||
def buylimit(self, market, quantity, rate):
|
||||
return self.query('buylimit', {'market': market, 'quantity': quantity,
|
||||
'rate': rate})
|
||||
|
||||
def buymarket(self, market, quantity):
|
||||
return self.query('buymarket',
|
||||
{'market': market, 'quantity': quantity})
|
||||
|
||||
def selllimit(self, market, quantity, rate):
|
||||
return self.query('selllimit', {'market': market, 'quantity': quantity,
|
||||
'rate': rate})
|
||||
|
||||
def sellmarket(self, market, quantity):
|
||||
return self.query('sellmarket',
|
||||
{'market': market, 'quantity': quantity})
|
||||
|
||||
def cancel(self, uuid):
|
||||
return self.query('cancel', {'uuid': uuid})
|
||||
|
||||
def getopenorders(self, market):
|
||||
return self.query('getopenorders', {'market': market})
|
||||
|
||||
def getbalances(self):
|
||||
return self.query('getbalances')
|
||||
|
||||
def getbalance(self, currency):
|
||||
return self.query('getbalance', {'currency': currency})
|
||||
|
||||
def getdepositaddress(self, currency):
|
||||
return self.query('getdepositaddress', {'currency': currency})
|
||||
|
||||
def withdraw(self, currency, quantity, address):
|
||||
return self.query('withdraw',
|
||||
{'currency': currency, 'quantity': quantity,
|
||||
'address': address})
|
||||
|
||||
def getorder(self, uuid):
|
||||
return self.query('getorder', {'uuid': uuid})
|
||||
|
||||
def getorderhistory(self, market, count):
|
||||
return self.query('getorderhistory',
|
||||
{'market': market, 'count': count})
|
||||
|
||||
def getwithdrawalhistory(self, currency, count):
|
||||
return self.query('getwithdrawalhistory',
|
||||
{'currency': currency, 'count': count})
|
||||
|
||||
def getdeposithistory(self, currency, count):
|
||||
return self.query('getdeposithistory',
|
||||
{'currency': currency, 'count': count})
|
||||
@@ -17,6 +17,7 @@ from catalyst.errors import (
|
||||
from catalyst.finance.order import ORDER_STATUS
|
||||
from catalyst.finance.transaction import Transaction
|
||||
from catalyst.exchange.exchange_utils import get_exchange_symbols
|
||||
from catalyst.exchange.exchange_portfolio import ExchangePortfolio
|
||||
|
||||
log = Logger('Exchange')
|
||||
|
||||
@@ -32,10 +33,6 @@ class Exchange:
|
||||
self.minute_writer = None
|
||||
self.minute_reader = None
|
||||
|
||||
@abstractmethod
|
||||
def subscribe_to_market_data(self, symbol):
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def positions(self):
|
||||
pass
|
||||
@@ -44,9 +41,20 @@ class Exchange:
|
||||
def update_portfolio(self):
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
@property
|
||||
def portfolio(self):
|
||||
pass
|
||||
"""
|
||||
Return the Portfolio
|
||||
|
||||
:return:
|
||||
"""
|
||||
if self._portfolio is None:
|
||||
self._portfolio = ExchangePortfolio(
|
||||
start_date=pd.Timestamp.utcnow()
|
||||
)
|
||||
self.update_portfolio()
|
||||
|
||||
return self._portfolio
|
||||
|
||||
@abstractproperty
|
||||
def account(self):
|
||||
@@ -106,6 +114,9 @@ class Exchange:
|
||||
|
||||
return asset
|
||||
|
||||
def fetch_symbol_map(self):
|
||||
return get_exchange_symbols(self.name)
|
||||
|
||||
def load_assets(self):
|
||||
"""
|
||||
Populate the 'assets' attribute with a dictionary of Assets.
|
||||
@@ -124,7 +135,7 @@ class Exchange:
|
||||
via its api.
|
||||
"""
|
||||
|
||||
symbol_map = get_exchange_symbols(self.name)
|
||||
symbol_map = self.fetch_symbol_map()
|
||||
for exchange_symbol in symbol_map:
|
||||
asset = symbol_map[exchange_symbol]
|
||||
symbol = asset['symbol']
|
||||
@@ -486,4 +497,12 @@ class Exchange:
|
||||
:param assets:
|
||||
:return:
|
||||
"""
|
||||
return
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_account(self):
|
||||
"""
|
||||
Retrieve the account parameters.
|
||||
:return:
|
||||
"""
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user