mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-30 15:52:52 +08:00
Poloshing unit tests and finalizing Bittrex implementation
This commit is contained in:
@@ -166,13 +166,8 @@ class Bitfinex(Exchange):
|
||||
|
||||
return order, executed_price
|
||||
|
||||
def update_portfolio(self):
|
||||
"""
|
||||
Update the portfolio cash and position balances based on the
|
||||
latest ticker prices.
|
||||
|
||||
:return:
|
||||
"""
|
||||
def get_balances(self):
|
||||
log.debug('retrieving wallets balances')
|
||||
try:
|
||||
response = self._request('balances', None)
|
||||
balances = response.json()
|
||||
@@ -184,36 +179,12 @@ class Bitfinex(Exchange):
|
||||
error='unable to fetch balance {}'.format(balances['message'])
|
||||
)
|
||||
|
||||
base_position = None
|
||||
for position in balances:
|
||||
if not base_position and position['type'] == 'exchange' \
|
||||
and position['currency'] == self.base_currency:
|
||||
base_position = position
|
||||
std_balances = dict()
|
||||
for balance in balances:
|
||||
currency = balance['currency'].lower()
|
||||
std_balances[currency] = float(balance['available'])
|
||||
|
||||
if position is None:
|
||||
raise ValueError(
|
||||
error='Base currency %s not found in portfolio' % self.base_currency
|
||||
)
|
||||
|
||||
portfolio = self._portfolio
|
||||
portfolio.cash = float(base_position['available'])
|
||||
if portfolio.starting_cash is None:
|
||||
portfolio.starting_cash = portfolio.cash
|
||||
|
||||
if portfolio.positions:
|
||||
assets = portfolio.positions.keys()
|
||||
tickers = self.tickers(assets)
|
||||
portfolio.positions_value = 0.0
|
||||
for ticker in tickers:
|
||||
# TODO: convert if the position is not in the base currency
|
||||
position = portfolio.positions[ticker['asset']]
|
||||
position.last_sale_price = ticker['last_price']
|
||||
position.last_sale_date = ticker['timestamp']
|
||||
|
||||
portfolio.positions_value += \
|
||||
position.amount * position.last_sale_price
|
||||
portfolio.portfolio_value = \
|
||||
portfolio.positions_value + portfolio.cash
|
||||
return std_balances
|
||||
|
||||
@property
|
||||
def account(self):
|
||||
@@ -397,17 +368,17 @@ class Bitfinex(Exchange):
|
||||
date = pd.Timestamp.utcnow()
|
||||
try:
|
||||
response = self._request('order/new', req)
|
||||
exchange_order = response.json()
|
||||
order_status = response.json()
|
||||
except Exception as e:
|
||||
raise ExchangeRequestError(error=e)
|
||||
|
||||
if 'message' in exchange_order:
|
||||
if 'message' in order_status:
|
||||
raise ExchangeRequestError(
|
||||
error='unable to create Bitfinex order {}'.format(
|
||||
exchange_order['message'])
|
||||
order_status['message'])
|
||||
)
|
||||
|
||||
order_id = exchange_order['id']
|
||||
order_id = str(order_status['id'])
|
||||
order = Order(
|
||||
dt=date,
|
||||
asset=asset,
|
||||
@@ -538,15 +509,14 @@ class Bitfinex(Exchange):
|
||||
|
||||
tickers = response.json()
|
||||
|
||||
formatted_tickers = []
|
||||
ticks = dict()
|
||||
for index, ticker in enumerate(tickers):
|
||||
if not len(ticker) == 11:
|
||||
raise ExchangeRequestError(
|
||||
error='Invalid ticker in response: {}'.format(ticker)
|
||||
)
|
||||
|
||||
tick = dict(
|
||||
asset=assets[index],
|
||||
ticks[assets[index]] = dict(
|
||||
timestamp=pd.Timestamp.utcnow(),
|
||||
bid=ticker[1],
|
||||
ask=ticker[3],
|
||||
@@ -555,7 +525,6 @@ class Bitfinex(Exchange):
|
||||
high=ticker[9],
|
||||
volume=ticker[8],
|
||||
)
|
||||
formatted_tickers.append(tick)
|
||||
|
||||
log.debug('got tickers {}'.format(formatted_tickers))
|
||||
return formatted_tickers
|
||||
log.debug('got tickers {}'.format(ticks))
|
||||
return ticks
|
||||
|
||||
@@ -80,8 +80,17 @@ class Bittrex(Exchange):
|
||||
|
||||
return symbol_map
|
||||
|
||||
def update_portfolio(self):
|
||||
pass
|
||||
def get_balances(self):
|
||||
try:
|
||||
balances = self.api.getbalances()
|
||||
except Exception as e:
|
||||
raise ExchangeRequestError(error=e)
|
||||
|
||||
std_balances = dict()
|
||||
for balance in balances:
|
||||
currency = balance['Currency'].lower()
|
||||
std_balances[currency] = balance['Available']
|
||||
return std_balances
|
||||
|
||||
def create_order(self, asset, amount, is_buy, style):
|
||||
log.info('creating order')
|
||||
@@ -249,9 +258,34 @@ class Bittrex(Exchange):
|
||||
return ohlc_map[assets] \
|
||||
if isinstance(assets, TradingPair) else ohlc_map
|
||||
|
||||
def tickers(self):
|
||||
def tickers(self, assets):
|
||||
"""
|
||||
As of v1.1, Bittrex only allows one ticker at the time.
|
||||
So we have to make multiple calls to fetch multiple assets.
|
||||
|
||||
:param assets:
|
||||
:return:
|
||||
"""
|
||||
log.info('retrieving tickers')
|
||||
pass
|
||||
|
||||
ticks = dict()
|
||||
for asset in assets:
|
||||
symbol = self.get_symbol(asset)
|
||||
try:
|
||||
ticker = self.api.getticker(symbol)
|
||||
except Exception as e:
|
||||
raise ExchangeRequestError(error=e)
|
||||
|
||||
# TODO: catch invalid ticker
|
||||
ticks[asset] = dict(
|
||||
timestamp=pd.Timestamp.utcnow(),
|
||||
bid=ticker['Bid'],
|
||||
ask=ticker['Ask'],
|
||||
last_price=ticker['Last']
|
||||
)
|
||||
|
||||
log.debug('got tickers {}'.format(ticks))
|
||||
return ticks
|
||||
|
||||
def get_account(self):
|
||||
log.info('retrieving account data')
|
||||
|
||||
@@ -14,7 +14,7 @@ from catalyst.errors import (
|
||||
SymbolNotFound,
|
||||
)
|
||||
from catalyst.exchange.exchange_errors import MismatchingBaseCurrencies, \
|
||||
InvalidOrderStyle
|
||||
InvalidOrderStyle, BaseCurrencyNotFoundError
|
||||
from catalyst.exchange.exchange_execution import ExchangeStopLimitOrder, \
|
||||
ExchangeLimitOrder, ExchangeStopOrder
|
||||
from catalyst.exchange.exchange_portfolio import ExchangePortfolio
|
||||
@@ -35,15 +35,12 @@ class Exchange:
|
||||
self._portfolio = None
|
||||
self.minute_writer = None
|
||||
self.minute_reader = None
|
||||
self.base_currency = None
|
||||
|
||||
@abstractproperty
|
||||
def positions(self):
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def update_portfolio(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def portfolio(self):
|
||||
"""
|
||||
@@ -113,7 +110,7 @@ class Exchange:
|
||||
asset = self.assets[key]
|
||||
|
||||
if not asset:
|
||||
raise SymbolNotFound('Asset not found: %s' % symbol)
|
||||
raise SymbolNotFound(symbol=symbol)
|
||||
|
||||
return asset
|
||||
|
||||
@@ -379,6 +376,55 @@ class Exchange:
|
||||
df = pd.concat(series)
|
||||
return df
|
||||
|
||||
def update_portfolio(self):
|
||||
"""
|
||||
Update the portfolio cash and position balances based on the
|
||||
latest ticker prices.
|
||||
|
||||
:return:
|
||||
"""
|
||||
balances = self.get_balances()
|
||||
|
||||
base_position_available = balances[self.base_currency] \
|
||||
if self.base_currency in balances else None
|
||||
|
||||
if base_position_available is None:
|
||||
raise BaseCurrencyNotFoundError(
|
||||
base_currency=self.base_currency,
|
||||
exchange=self.name
|
||||
)
|
||||
|
||||
portfolio = self._portfolio
|
||||
portfolio.cash = base_position_available
|
||||
|
||||
if portfolio.starting_cash is None:
|
||||
portfolio.starting_cash = portfolio.cash
|
||||
|
||||
if portfolio.positions:
|
||||
assets = portfolio.positions.keys()
|
||||
tickers = self.tickers(assets)
|
||||
|
||||
portfolio.positions_value = 0.0
|
||||
for asset in tickers:
|
||||
# TODO: convert if the position is not in the base currency
|
||||
ticker = tickers[asset]
|
||||
position = portfolio.positions[asset]
|
||||
position.last_sale_price = ticker['last_price']
|
||||
position.last_sale_date = ticker['timestamp']
|
||||
|
||||
portfolio.positions_value += \
|
||||
position.amount * position.last_sale_price
|
||||
portfolio.portfolio_value = \
|
||||
portfolio.positions_value + portfolio.cash
|
||||
|
||||
@abstractmethod
|
||||
def get_balances(self):
|
||||
"""
|
||||
Retrieve wallet balances for the exchange
|
||||
:return balances: A dict of currency => available balance
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def create_order(self, asset, amount, is_buy, style):
|
||||
pass
|
||||
|
||||
@@ -99,6 +99,13 @@ class SidHashError(ZiplineError):
|
||||
).strip()
|
||||
|
||||
|
||||
class BaseCurrencyNotFoundError(ZiplineError):
|
||||
msg = (
|
||||
'Algorithm base currency {base_currency} not found in exchange '
|
||||
'{exchange}.'
|
||||
).strip()
|
||||
|
||||
|
||||
class MismatchingBaseCurrencies(ZiplineError):
|
||||
msg = (
|
||||
'Unable to trade with base currency {base_currency} when the '
|
||||
|
||||
@@ -30,5 +30,9 @@ class BaseExchangeTestCase():
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_account(self):
|
||||
def test_get_balances(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def test_get_account(self):
|
||||
pass
|
||||
|
||||
@@ -37,6 +37,7 @@ class BitfinexTestCase(BaseExchangeTestCase):
|
||||
|
||||
def test_open_orders(self):
|
||||
log.info('retrieving open orders')
|
||||
orders = self.exchange.get_open_orders()
|
||||
pass
|
||||
|
||||
def test_get_order(self):
|
||||
@@ -53,12 +54,21 @@ class BitfinexTestCase(BaseExchangeTestCase):
|
||||
|
||||
def test_tickers(self):
|
||||
log.info('retrieving tickers')
|
||||
tickers = self.exchange.tickers([
|
||||
self.exchange.get_asset('eth_usd'),
|
||||
self.exchange.get_asset('btc_usd')
|
||||
])
|
||||
pass
|
||||
|
||||
def get_account(self):
|
||||
def test_get_account(self):
|
||||
log.info('retrieving account data')
|
||||
pass
|
||||
|
||||
def test_get_balances(self):
|
||||
log.info('testing exchange balances')
|
||||
balances = self.exchange.get_balances()
|
||||
pass
|
||||
|
||||
# def test_order(self):
|
||||
# log.info('ordering from bitfinex')
|
||||
# bitfinex = Bitfinex()
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
from catalyst.exchange.bittrex.bittrex import Bittrex
|
||||
from catalyst.finance.order import Order
|
||||
from .base import BaseExchangeTestCase
|
||||
from logbook import Logger
|
||||
import pandas as pd
|
||||
from catalyst.finance.execution import (MarketOrder,
|
||||
LimitOrder,
|
||||
StopOrder,
|
||||
StopLimitOrder)
|
||||
from catalyst.exchange.exchange_utils import get_exchange_auth
|
||||
|
||||
log = Logger('test_bittrex')
|
||||
@@ -31,6 +27,7 @@ class BittrexTestCase(BaseExchangeTestCase):
|
||||
amount=1,
|
||||
)
|
||||
log.info('order created {}'.format(order_id))
|
||||
assert order_id is not None
|
||||
pass
|
||||
|
||||
def test_open_orders(self):
|
||||
@@ -39,10 +36,12 @@ class BittrexTestCase(BaseExchangeTestCase):
|
||||
|
||||
def test_get_order(self):
|
||||
log.info('retrieving order')
|
||||
order = self.exchange.get_order(u'2c584020-9caf-4af5-bde0-332c0bba17e2')
|
||||
order = self.exchange.get_order(
|
||||
u'2c584020-9caf-4af5-bde0-332c0bba17e2')
|
||||
assert isinstance(order, Order)
|
||||
pass
|
||||
|
||||
def test_cancel_order(self,):
|
||||
def test_cancel_order(self, ):
|
||||
log.info('cancel order')
|
||||
self.exchange.cancel_order(u'dc7bcca2-5219-4145-8848-8a593d2a72f9')
|
||||
pass
|
||||
@@ -65,8 +64,18 @@ class BittrexTestCase(BaseExchangeTestCase):
|
||||
|
||||
def test_tickers(self):
|
||||
log.info('retrieving tickers')
|
||||
tickers = self.exchange.tickers([
|
||||
self.exchange.get_asset('ubq_btc'),
|
||||
self.exchange.get_asset('neo_btc')
|
||||
])
|
||||
assert len(tickers) == 2
|
||||
pass
|
||||
|
||||
def get_account(self):
|
||||
log.info('retrieving account data')
|
||||
def test_get_balances(self):
|
||||
log.info('testing wallet balances')
|
||||
balances = self.exchange.get_balances()
|
||||
pass
|
||||
|
||||
def test_get_account(self):
|
||||
log.info('testing account data')
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user