diff --git a/catalyst/examples/simple_loop.py b/catalyst/examples/simple_loop.py index dc130b3a..46965b98 100644 --- a/catalyst/examples/simple_loop.py +++ b/catalyst/examples/simple_loop.py @@ -1,13 +1,13 @@ -import pandas as pd import talib +import pandas as pd from catalyst import run_algorithm from catalyst.api import symbol def initialize(context): print('initializing') - context.asset = symbol('xrp_btc') + context.asset = symbol('btc_usd') def handle_data(context, data): @@ -27,25 +27,25 @@ def handle_data(context, data): pass -# run_algorithm( -# capital_base=250, -# start=pd.to_datetime('2015-08-01', utc=True), -# end=pd.to_datetime('2017-9-30', utc=True), -# data_frequency='daily', -# initialize=initialize, -# handle_data=handle_data, -# analyze=None, -# exchange_name='poloniex', -# algo_namespace='simple_loop', -# base_currency='eth' -# ) run_algorithm( + capital_base=250, + start=pd.to_datetime('2017-08-01', utc=True), + end=pd.to_datetime('2017-9-30', utc=True), + data_frequency='daily', initialize=initialize, handle_data=handle_data, analyze=None, exchange_name='bitfinex', - live=True, algo_namespace='simple_loop', - base_currency='eth', - live_graph=False + base_currency='btc' ) +# run_algorithm( +# initialize=initialize, +# handle_data=handle_data, +# analyze=None, +# exchange_name='bitfinex', +# live=True, +# algo_namespace='simple_loop', +# base_currency='eth', +# live_graph=False +# ) diff --git a/catalyst/exchange/bittrex/bittrex.py b/catalyst/exchange/bittrex/bittrex.py index 6df3638e..1bca9422 100644 --- a/catalyst/exchange/bittrex/bittrex.py +++ b/catalyst/exchange/bittrex/bittrex.py @@ -24,7 +24,7 @@ URL2 = 'https://bittrex.com/Api/v2.0' class Bittrex(Exchange): def __init__(self, key, secret, base_currency, portfolio=None): - self.api = Bittrex_api(key=key, secret=secret.encode('UTF-8')) + self.api = Bittrex_api(key=key, secret=secret) self.name = 'bittrex' self.color = 'blue' self.base_currency = base_currency @@ -65,10 +65,10 @@ class Bittrex(Exchange): return exchange_symbol.lower() def get_balances(self): + balances = self.api.getbalances() try: log.debug('retrieving wallet balances') self.ask_request() - balances = self.api.getbalances() except Exception as e: raise ExchangeRequestError(error=e) @@ -208,7 +208,7 @@ class Bittrex(Exchange): ) def get_candles(self, data_frequency, assets, bar_count=None, - start_date=None): + start_dt=None, end_dt=None): """ Supported Intervals ------------------- diff --git a/catalyst/exchange/bittrex/bittrex_api.py b/catalyst/exchange/bittrex/bittrex_api.py index cda7581e..38e2a444 100644 --- a/catalyst/exchange/bittrex/bittrex_api.py +++ b/catalyst/exchange/bittrex/bittrex_api.py @@ -39,7 +39,10 @@ class Bittrex_api(object): 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() + + signature = hmac.new(self.secret.encode('utf-8'), + url.encode('utf-8'), + hashlib.sha512).hexdigest() headers = {'apisign': signature} else: headers = {} diff --git a/catalyst/exchange/bundle_utils.py b/catalyst/exchange/bundle_utils.py index fe622b52..8f07565b 100644 --- a/catalyst/exchange/bundle_utils.py +++ b/catalyst/exchange/bundle_utils.py @@ -134,7 +134,7 @@ def get_adj_dates(start, end, assets, data_frequency): if end is None or start >= end: raise NoDataAvailableOnExchange( exchange=asset.exchange.title(), - symbol=[asset.symbol.encode('utf-8')], + symbol=[asset.symbol], data_frequency=data_frequency, ) @@ -243,12 +243,12 @@ def find_most_recent_time(bundle_name): for folder in bundle_folders: date = from_bundle_ingest_dirname(folder) if not most_recent_bundle or date > \ - most_recent_bundle[most_recent_bundle.keys()[0]]: + most_recent_bundle[list(most_recent_bundle.keys())[0]]: most_recent_bundle = dict() most_recent_bundle[folder] = date if most_recent_bundle: - return most_recent_bundle.keys()[0] + return list(most_recent_bundle.keys())[0] else: return None diff --git a/catalyst/exchange/data_portal_exchange.py b/catalyst/exchange/data_portal_exchange.py index ce454303..3eebf3aa 100644 --- a/catalyst/exchange/data_portal_exchange.py +++ b/catalyst/exchange/data_portal_exchange.py @@ -80,7 +80,7 @@ class DataPortalExchangeBase(DataPortal): return pd.concat(df_list) else: - exchange = self.exchanges[exchange_assets.keys()[0]] + exchange = self.exchanges[list(exchange_assets.keys())[0]] return self.get_exchange_history_window( exchange, assets, @@ -165,8 +165,8 @@ class DataPortalExchangeBase(DataPortal): exchange_assets[asset.exchange].append(asset) - if len(exchange_assets.keys()) == 1: - exchange = self.exchanges[exchange_assets.keys()[0]] + if len(list(exchange_assets.keys())) == 1: + exchange = self.exchanges[list(exchange_assets.keys())[0]] return self.get_exchange_spot_value( exchange, assets, field, dt, data_frequency) diff --git a/catalyst/exchange/exchange.py b/catalyst/exchange/exchange.py index 25a11594..bb4018d5 100644 --- a/catalyst/exchange/exchange.py +++ b/catalyst/exchange/exchange.py @@ -87,7 +87,7 @@ class Exchange: self.request_cpt[now] = 0 return True - cpt_date = self.request_cpt.keys()[0] + cpt_date = list(self.request_cpt.keys())[0] cpt = self.request_cpt[cpt_date] if now > cpt_date + timedelta(minutes=1): @@ -167,8 +167,10 @@ class Exchange: asset = self.assets[key] if not asset: - supported_symbols = [pair.symbol.encode('utf-8') for pair in - self.assets.values()] + supported_symbols = [ + pair.symbol for pair in list(self.assets.values()) + ] + raise SymbolNotFoundOnExchange( symbol=symbol, exchange=self.name.title(), @@ -552,7 +554,7 @@ class Exchange: portfolio.starting_cash = portfolio.cash if portfolio.positions: - assets = portfolio.positions.keys() + assets = list(portfolio.positions.keys()) tickers = self.tickers(assets) portfolio.positions_value = 0.0 diff --git a/catalyst/exchange/exchange_algorithm.py b/catalyst/exchange/exchange_algorithm.py index 22c26ef2..a96f893c 100644 --- a/catalyst/exchange/exchange_algorithm.py +++ b/catalyst/exchange/exchange_algorithm.py @@ -113,7 +113,7 @@ class ExchangeTradingAlgorithmBase(TradingAlgorithm): else self.sim_params.end_session if exchange_name is None: - exchange = self.exchanges.values()[0] + exchange = list(self.exchanges.values())[0] else: exchange = self.exchanges[exchange_name] @@ -524,7 +524,7 @@ class ExchangeTradingAlgorithmLive(ExchangeTradingAlgorithmBase): self.add_pnl_stats(minute_stats) if self.recorded_vars: self.add_custom_signals_stats(minute_stats) - recorded_cols = self.recorded_vars.keys() + recorded_cols = list(self.recorded_vars.keys()) else: recorded_cols = None diff --git a/catalyst/exchange/exchange_bundle.py b/catalyst/exchange/exchange_bundle.py index 64084316..81d9833d 100644 --- a/catalyst/exchange/exchange_bundle.py +++ b/catalyst/exchange/exchange_bundle.py @@ -353,7 +353,7 @@ class ExchangeBundle: period_start, period_end = get_month_start_end(dt) asset_start_month, _ = get_month_start_end(asset_start) - if asset_start_month > period_start: + if asset.start_date > period_start: dt += timedelta(days=1) continue @@ -371,7 +371,7 @@ class ExchangeBundle: period_start, period_end = get_year_start_end(dt) asset_start_year, _ = get_year_start_end(asset_start) - if asset_start_year > period_start: + if asset.start_date > period_start: dt += timedelta(days=1) continue diff --git a/catalyst/exchange/exchange_errors.py b/catalyst/exchange/exchange_errors.py index c5baadec..6cd55014 100644 --- a/catalyst/exchange/exchange_errors.py +++ b/catalyst/exchange/exchange_errors.py @@ -6,12 +6,12 @@ from catalyst.errors import ZiplineError def silent_except_hook(exctype, excvalue, exctraceback): if exctype in [PricingDataBeforeTradingError, PricingDataNotLoadedError, - SymbolNotFoundOnExchange, NoDataAvailableOnExchange, - ExchangeAuthEmpty ]: + SymbolNotFoundOnExchange, NoDataAvailableOnExchange, + ExchangeAuthEmpty]: fn = traceback.extract_tb(exctraceback)[-1][0] ln = traceback.extract_tb(exctraceback)[-1][1] - print "Error traceback: {1} (line {2})\n" \ - "{0.__name__}: {3}".format(exctype, fn, ln, excvalue) + print("Error traceback: {1} (line {2})\n" + "{0.__name__}: {3}".format(exctype, fn, ln, excvalue)) else: sys.__excepthook__(exctype, excvalue, exctraceback) @@ -214,7 +214,9 @@ class PricingDataNotLoadedError(ZiplineError): class ApiCandlesError(ZiplineError): msg = ('Unable to fetch candles from the remote API: {error}.').strip() + class NoDataAvailableOnExchange(ZiplineError): - msg = ('Requested data for trading pair {symbol} is not available on exchange {exchange} ' - 'in `{data_frequency}` frequency at this time. ' - 'Check `http://enigma.co/catalyst/status` for market coverage.').strip() + msg = ( + 'Requested data for trading pair {symbol} is not available on exchange {exchange} ' + 'in `{data_frequency}` frequency at this time. ' + 'Check `http://enigma.co/catalyst/status` for market coverage.').strip() diff --git a/catalyst/exchange/exchange_utils.py b/catalyst/exchange/exchange_utils.py index b9e6cf21..f12f4df1 100644 --- a/catalyst/exchange/exchange_utils.py +++ b/catalyst/exchange/exchange_utils.py @@ -42,7 +42,9 @@ def get_exchange_symbols(exchange_name, environ=None): filename = get_exchange_symbols_filename(exchange_name) if not os.path.isfile(filename) or \ - pd.Timedelta(pd.Timestamp('now', tz='UTC') - last_modified_time(filename)).days > 1: + pd.Timedelta(pd.Timestamp('now', + tz='UTC') - last_modified_time( + filename)).days > 1: download_exchange_symbols(exchange_name, environ) if os.path.isfile(filename): @@ -67,9 +69,11 @@ def get_exchange_auth(exchange_name, environ=None): else: data = dict(name=exchange_name, key='', secret='') with open(filename, 'w') as f: - json.dump(data, f, sort_keys=False, indent=2, separators=(',', ':')) + json.dump(data, f, sort_keys=False, indent=2, + separators=(',', ':')) return data + def get_algo_folder(algo_name, environ=None): if not environ: environ = os.environ @@ -163,6 +167,7 @@ def get_exchange_minute_writer_root(exchange_name, environ=None): return minute_data_folder + def get_exchange_bundles_folder(exchange_name, environ=None): exchange_folder = get_exchange_folder(exchange_name, environ) diff --git a/catalyst/exchange/poloniex/poloniex.py b/catalyst/exchange/poloniex/poloniex.py index 43954036..76d51332 100644 --- a/catalyst/exchange/poloniex/poloniex.py +++ b/catalyst/exchange/poloniex/poloniex.py @@ -33,7 +33,7 @@ log = Logger('Poloniex', level=LOG_LEVEL) class Poloniex(Exchange): def __init__(self, key, secret, base_currency, portfolio=None): - self.api = Poloniex_api(key=key, secret=secret.encode('UTF-8')) + self.api = Poloniex_api(key=key, secret=secret) self.name = 'poloniex' self.assets = {} self.load_assets() @@ -119,9 +119,9 @@ class Poloniex(Exchange): return order, executed_price def get_balances(self): - log.debug('retrieving wallets balances') + balances = self.api.returnbalances() try: - balances = self.api.returnbalances() + log.debug('retrieving wallets balances') except Exception as e: log.debug(e) raise ExchangeRequestError(error=e) diff --git a/catalyst/exchange/poloniex/poloniex_api.py b/catalyst/exchange/poloniex/poloniex_api.py index 599a0b65..158b5fa2 100644 --- a/catalyst/exchange/poloniex/poloniex_api.py +++ b/catalyst/exchange/poloniex/poloniex_api.py @@ -19,19 +19,25 @@ class Poloniex_api(object): self.max_requests_per_second = 6 self.request_cpt = dict() - self.public = ['returnTicker', 'return24Volume', 'returnOrderBook', - 'returnTradeHistory', 'returnChartData', - 'returnCurrencies', 'returnLoanOrders'] - self.trading = ['returnBalances','returnCompleteBalances','returnDepositAddresses', - 'generateNewAddress','returnDepositsWithdrawals','returnOpenOrders', - 'returnTradeHistory','returnOrderTrades', + self.public = ['returnTicker', 'return24Volume', 'returnOrderBook', + 'returnTradeHistory', 'returnChartData', + 'returnCurrencies', 'returnLoanOrders'] + self.trading = ['returnBalances', 'returnCompleteBalances', + 'returnDepositAddresses', + 'generateNewAddress', 'returnDepositsWithdrawals', + 'returnOpenOrders', + 'returnTradeHistory', 'returnOrderTrades', 'buy', 'sell', 'cancelOrder', 'moveOrder', - 'withdraw', 'returnFeeInfo','returnAvailableAccountBalances', + 'withdraw', 'returnFeeInfo', + 'returnAvailableAccountBalances', 'returnTradableBalances', 'transferBalance', - 'returnMarginAccountSummary','marginBuy','marginSell', - 'getMarginPosition', 'closeMarginPosition','createLoanOffer', - 'cancelLoanOffer','returnOpenLoanOffers','returnActiveLoans', - 'returnLendingHistory','toggleAutoRenew'] + 'returnMarginAccountSummary', 'marginBuy', + 'marginSell', + 'getMarginPosition', 'closeMarginPosition', + 'createLoanOffer', + 'cancelLoanOffer', 'returnOpenLoanOffers', + 'returnActiveLoans', + 'returnLendingHistory', 'toggleAutoRenew'] def ask_request(self): """ @@ -50,7 +56,7 @@ class Poloniex_api(object): self.request_cpt[now] = 0 return True - cpt_date = self.request_cpt.keys()[0] + cpt_date = list(self.request_cpt.keys())[0] cpt = self.request_cpt[cpt_date] if now > cpt_date + 1: @@ -59,9 +65,8 @@ class Poloniex_api(object): return True if cpt >= self.max_requests_per_second: - - log.debug('max requests 6 reached, sleeping for 1 seconds') - sleep(1) + + time.sleep(1) now = time.time() self.request_cpt = dict() @@ -73,18 +78,23 @@ class Poloniex_api(object): def query(self, method, req={}): if method in self.public: - url = 'https://poloniex.com/public?command=' + method + '&' + urllib.parse.urlencode(req) + url = 'https://poloniex.com/public?command=' + method + '&' + \ + urllib.parse.urlencode(req) headers = {} post_data = None elif method in self.trading: url = 'https://poloniex.com/tradingApi' req['command'] = method - req['nonce'] = int(time.time()*1000) - post_data = urllib.parse.urlencode(req) - signature = hmac.new(self.secret, post_data, hashlib.sha512).hexdigest() - headers = { 'Sign': signature, 'Key': self.key} + req['nonce'] = int(time.time() * 1000) + post_data = urllib.parse.urlencode(req) + + signature = hmac.new(self.secret.encode('utf-8'), + post_data.encode('utf-8'), + hashlib.sha512).hexdigest() + headers = {'Sign': signature, 'Key': self.key} else: - raise ValueError('Method "' + method + '" not found in neither the Public API or Trading API endpoints') + raise ValueError( + 'Method "' + method + '" not found in neither the Public API or Trading API endpoints') self.ask_request() req = urllib.request.Request(url, data=post_data, headers=headers) @@ -100,15 +110,17 @@ class Poloniex_api(object): return self.query('returnOrderBook', {'currencyPair': market}) def returntradehistory(self, market, start=None, end=None): - if(start is not None and end is not None): - return self.query('returntradehistory', - {'currencyPair': market, 'start': start, 'end': end }) + if (start is not None and end is not None): + return self.query('returntradehistory', + {'currencyPair': market, 'start': start, + 'end': end}) else: - return self.query('returntradehistory', {'currencyPair': market }) + return self.query('returntradehistory', {'currencyPair': market}) def returnchartdata(self, market, period, start, end=9999999999): - return self.query('returnChartData', {'currencyPair': market, 'period': period, - 'start': start, 'end': end}) + return self.query('returnChartData', + {'currencyPair': market, 'period': period, + 'start': start, 'end': end}) def returncurrencies(self): return self.query('returnCurrencies', {}) @@ -120,7 +132,7 @@ class Poloniex_api(object): return self.query('returnBalances') def returncompletebalances(self, account): - if(account): + if (account): return self.query('returnCompleteBalances', {'account': account}) else: return self.query('returnCompleteBalances') @@ -132,43 +144,54 @@ class Poloniex_api(object): return self.query('generateNewAddress', {'currency': currency}) def returnDepositsWithdrawals(self, start, end): - return self.query('returnDepositsWithdrawals', {'start': start, 'end': end}) + return self.query('returnDepositsWithdrawals', + {'start': start, 'end': end}) def returnopenorders(self, market): return self.query('returnOpenOrders', {'currencyPair': market}) def returntradehistory(self, market): - #TODO: optional start and/or end and limit + # TODO: optional start and/or end and limit return self.query('returnTradeHistory', {'currencyPair': market}) def returnordertrades(self, ordernumber): return self.query('returnOrderTrades', {'orderNumber': ordernumber}) - def buy(self, market, amount, rate, fillorkill=0, immediateorcancel=0, postonly=0): - if(fillorkill): - return self.query('buy', {'currencyPair': market, 'rate':rate, 'amount': amount, + def buy(self, market, amount, rate, fillorkill=0, immediateorcancel=0, + postonly=0): + if (fillorkill): + return self.query('buy', {'currencyPair': market, 'rate': rate, + 'amount': amount, 'fillOrKill': fillorkill, }) - elif(immediateorcancel): - return self.query('buy', {'currencyPair': market, 'rate':rate, 'amount': amount, + elif (immediateorcancel): + return self.query('buy', {'currencyPair': market, 'rate': rate, + 'amount': amount, 'immediateOrCancel': immediateorcancel, }) - elif(postonly): - return self.query('buy', {'currencyPair': market, 'rate':rate, 'amount': amount, + elif (postonly): + return self.query('buy', {'currencyPair': market, 'rate': rate, + 'amount': amount, 'postOnly': postonly, }) else: - return self.query('buy', {'currencyPair': market, 'rate':rate, 'amount': amount, }) + return self.query('buy', {'currencyPair': market, 'rate': rate, + 'amount': amount, }) - def sell(self, market, amount, rate, fillorkill=0, immediateorcancel=0, postonly=0): - if(fillorkill): - return self.query('sell', {'currencyPair': market, 'rate':rate, 'amount': amount, - 'fillOrKill': fillorkill, }) - elif(immediateorcancel): - return self.query('sell', {'currencyPair': market, 'rate':rate, 'amount': amount, - 'immediateOrCancel': immediateorcancel, }) - elif(postonly): - return self.query('sell', {'currencyPair': market, 'rate':rate, 'amount': amount, - 'postOnly': postonly, }) + def sell(self, market, amount, rate, fillorkill=0, immediateorcancel=0, + postonly=0): + if (fillorkill): + return self.query('sell', {'currencyPair': market, 'rate': rate, + 'amount': amount, + 'fillOrKill': fillorkill, }) + elif (immediateorcancel): + return self.query('sell', {'currencyPair': market, 'rate': rate, + 'amount': amount, + 'immediateOrCancel': immediateorcancel, }) + elif (postonly): + return self.query('sell', {'currencyPair': market, 'rate': rate, + 'amount': amount, + 'postOnly': postonly, }) else: - return self.query('sell', {'currencyPair': market, 'rate':rate, 'amount': amount, }) + return self.query('sell', {'currencyPair': market, 'rate': rate, + 'amount': amount, }) def cancelorder(self, ordernumber): return self.query('cancelOrder', {'orderNumber': ordernumber}) @@ -180,4 +203,3 @@ class Poloniex_api(object): def returnfeeinfo(self): return self.query('returnFeeInfo') - diff --git a/tests/exchange/test_clock.py b/tests/exchange/test_clock.py deleted file mode 100644 index ff74986b..00000000 --- a/tests/exchange/test_clock.py +++ /dev/null @@ -1,50 +0,0 @@ -from unittest import TestCase -from logbook import Logger -from mock import patch, sentinel -from catalyst.exchange.simple_clock import SimpleClock -from catalyst.utils.calendars.trading_calendar import days_at_time -from datetime import time -from collections import defaultdict -from catalyst.utils.calendars import get_calendar -import pandas as pd - -log = Logger('ExchangeClockTestCase') - - -class ExchangeClockTestCase(TestCase): - @classmethod - def setUpClass(cls): - cls.open_calendar = get_calendar("OPEN") - - cls.sessions = pd.Timestamp.utcnow() - - def setUp(self): - self.internal_clock = None - self.events = defaultdict(list) - - def advance_clock(self, x): - """Mock function for sleep. Advances the internal clock by 1 min""" - # The internal clock advance time must be 1 minute to match - # MinutesSimulationClock's update frequency - self.internal_clock += pd.Timedelta('1 min') - - def get_clock(self, arg, *args, **kwargs): - """Mock function for pandas.to_datetime which is used to query the - current time in RealtimeClock""" - assert arg == "now" - return self.internal_clock - - def test_clock(self): - with patch('catalyst.exchange.simple_clock.pd.to_datetime') as to_dt, \ - patch('catalyst.exchange.simple_clock.sleep') as sleep: - clock = SimpleClock(sessions=self.sessions) - to_dt.side_effect = self.get_clock - sleep.side_effect = self.advance_clock - start_time = pd.Timestamp.utcnow() - self.internal_clock = start_time - - events = list(clock) - - # Event 0 is SESSION_START which always happens at 00:00. - ts, event_type = events[1] - pass