Skip to content

Commit

Permalink
Initial work on bittrex implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
fredfortier committed Aug 28, 2017
1 parent c40fd98 commit 1cfcb1d
Show file tree
Hide file tree
Showing 12 changed files with 536 additions and 38 deletions.
10 changes: 5 additions & 5 deletions catalyst/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ def _(*args, **kwargs):
)
@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',
Expand Down Expand Up @@ -381,22 +381,22 @@ 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',
'--keep-last',
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.
Expand Down
26 changes: 3 additions & 23 deletions catalyst/exchange/bitfinex/bitfinex.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def __init__(self, key, secret, base_currency, portfolio=None):
self.url = BITFINEX_URL
self.key = key
self.secret = secret
self.id = 'b'
self.name = 'bitfinex'
self.assets = {}
self.load_assets()
Expand Down Expand Up @@ -220,26 +219,6 @@ def update_portfolio(self):
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()
Expand Down Expand Up @@ -273,8 +252,9 @@ def time_skew(self):
# 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):
"""
Expand Down
107 changes: 107 additions & 0 deletions catalyst/exchange/bittrex/bittrex.py
Original file line number Diff line number Diff line change
@@ -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
127 changes: 127 additions & 0 deletions catalyst/exchange/bittrex/bittrex_api.py
Original file line number Diff line number Diff line change
@@ -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})
35 changes: 27 additions & 8 deletions catalyst/exchange/exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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')

Expand All @@ -32,10 +33,6 @@ def __init__(self):
self.minute_writer = None
self.minute_reader = None

@abstractmethod
def subscribe_to_market_data(self, symbol):
pass

@abstractproperty
def positions(self):
pass
Expand All @@ -44,9 +41,20 @@ def positions(self):
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):
Expand Down Expand Up @@ -106,6 +114,9 @@ def get_asset(self, symbol):

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.
Expand All @@ -124,7 +135,7 @@ def load_assets(self):
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']
Expand Down Expand Up @@ -486,4 +497,12 @@ def tickers(self, assets):
:param assets:
:return:
"""
return
pass

@abc.abstractmethod
def get_account(self):
"""
Retrieve the account parameters.
:return:
"""
pass
Loading

0 comments on commit 1cfcb1d

Please sign in to comment.