mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-26 13:18:31 +08:00
WIP: Poloniex exchange - balances, candles & cancel
This commit is contained in:
@@ -9,7 +9,8 @@ import numpy as np
|
||||
import pandas as pd
|
||||
import pytz
|
||||
import requests
|
||||
import six
|
||||
#import six
|
||||
from six import iteritems
|
||||
from catalyst.assets._assets import TradingPair
|
||||
from logbook import Logger
|
||||
|
||||
@@ -37,12 +38,13 @@ class Poloniex(Exchange):
|
||||
self.api = Poloniex_api(key=key, secret=secret.encode('UTF-8'))
|
||||
self.name = 'poloniex'
|
||||
self.assets = {}
|
||||
#self.load_assets()
|
||||
self.load_assets()
|
||||
self.base_currency = base_currency
|
||||
self._portfolio = portfolio
|
||||
self.minute_writer = None
|
||||
self.minute_reader = None
|
||||
|
||||
|
||||
def sanitize_curency_symbol(self, exchange_symbol):
|
||||
"""
|
||||
Helper method used to build the universal pair.
|
||||
@@ -53,47 +55,51 @@ class Poloniex(Exchange):
|
||||
"""
|
||||
return exchange_symbol.lower()
|
||||
|
||||
'''
|
||||
|
||||
def _create_order(self, order_status):
|
||||
"""
|
||||
Create a Catalyst order object from a Bitfinex order dictionary
|
||||
Create a Catalyst order object from the Exchange order dictionary
|
||||
:param order_status:
|
||||
:return: Order
|
||||
"""
|
||||
if order_status['is_cancelled']:
|
||||
status = ORDER_STATUS.CANCELLED
|
||||
elif not order_status['is_live']:
|
||||
log.info('found executed order {}'.format(order_status))
|
||||
status = ORDER_STATUS.FILLED
|
||||
else:
|
||||
status = ORDER_STATUS.OPEN
|
||||
#if order_status['is_cancelled']:
|
||||
# status = ORDER_STATUS.CANCELLED
|
||||
#elif not order_status['is_live']:
|
||||
# log.info('found executed order {}'.format(order_status))
|
||||
# status = ORDER_STATUS.FILLED
|
||||
#else:
|
||||
status = ORDER_STATUS.OPEN
|
||||
|
||||
amount = float(order_status['original_amount'])
|
||||
filled = float(order_status['executed_amount'])
|
||||
amount = float(order_status['amount'])
|
||||
#filled = float(order_status['executed_amount'])
|
||||
filled = None
|
||||
|
||||
if order_status['side'] == 'sell':
|
||||
if order_status['type'] == 'sell':
|
||||
amount = -amount
|
||||
filled = -filled
|
||||
#filled = -filled
|
||||
|
||||
price = float(order_status['price'])
|
||||
price = float(order_status['rate'])
|
||||
order_type = order_status['type']
|
||||
|
||||
stop_price = None
|
||||
limit_price = None
|
||||
|
||||
# TODO: is this comprehensive enough?
|
||||
if order_type.endswith('limit'):
|
||||
limit_price = price
|
||||
elif order_type.endswith('stop'):
|
||||
stop_price = price
|
||||
#if order_type.endswith('limit'):
|
||||
# limit_price = price
|
||||
#elif order_type.endswith('stop'):
|
||||
# stop_price = price
|
||||
|
||||
executed_price = float(order_status['avg_execution_price'])
|
||||
#executed_price = float(order_status['avg_execution_price'])
|
||||
executed_price = price
|
||||
|
||||
# TODO: bitfinex does not specify comission. I could calculate it but not sure if it's worth it.
|
||||
commission = None
|
||||
|
||||
date = pd.Timestamp.utcfromtimestamp(float(order_status['timestamp']))
|
||||
date = pytz.utc.localize(date)
|
||||
#date = pd.Timestamp.utcfromtimestamp(float(order_status['timestamp']))
|
||||
#date = pytz.utc.localize(date)
|
||||
date = None
|
||||
|
||||
order = Order(
|
||||
dt=date,
|
||||
asset=self.assets[order_status['symbol']],
|
||||
@@ -101,36 +107,34 @@ class Poloniex(Exchange):
|
||||
stop=stop_price,
|
||||
limit=limit_price,
|
||||
filled=filled,
|
||||
id=str(order_status['id']),
|
||||
id=str(order_status['orderNumber']),
|
||||
commission=commission
|
||||
)
|
||||
order.status = status
|
||||
|
||||
return order, executed_price
|
||||
'''
|
||||
|
||||
|
||||
def get_balances(self):
|
||||
pass
|
||||
'''
|
||||
log.debug('retrieving wallets balances')
|
||||
try:
|
||||
response = self._request('balances', None)
|
||||
balances = response.json()
|
||||
balances = self.api.returnbalances()
|
||||
except Exception as e:
|
||||
log.debug(e)
|
||||
raise ExchangeRequestError(error=e)
|
||||
|
||||
if 'message' in balances:
|
||||
if 'error' in balances:
|
||||
raise ExchangeRequestError(
|
||||
error='unable to fetch balance {}'.format(balances['message'])
|
||||
error='unable to fetch balance {}'.format(balances['error'])
|
||||
)
|
||||
|
||||
std_balances = dict()
|
||||
for balance in balances:
|
||||
currency = balance['currency'].lower()
|
||||
std_balances[currency] = float(balance['available'])
|
||||
for (key, value) in iteritems(balances):
|
||||
currency = key.lower()
|
||||
std_balances[currency] = float(value)
|
||||
|
||||
return std_balances
|
||||
'''
|
||||
|
||||
|
||||
@property
|
||||
def account(self):
|
||||
@@ -166,10 +170,8 @@ class Poloniex(Exchange):
|
||||
return None
|
||||
|
||||
def get_candles(self, data_frequency, assets, bar_count=None):
|
||||
pass
|
||||
'''
|
||||
"""
|
||||
Retrieve OHLVC candles from Bitfinex
|
||||
Retrieve OHLVC candles from Poloniex
|
||||
|
||||
:param data_frequency:
|
||||
:param assets:
|
||||
@@ -178,33 +180,22 @@ class Poloniex(Exchange):
|
||||
|
||||
Available Frequencies
|
||||
---------------------
|
||||
'1m', '5m', '15m', '30m', '1h', '3h', '6h', '12h', '1D', '7D', '14D',
|
||||
'1M'
|
||||
'5m', '15m', '30m', '2h', '4h', '1D'
|
||||
"""
|
||||
|
||||
# TODO: use BcolzMinuteBarReader to read from cache
|
||||
freq_match = re.match(r'([0-9].*)(m|h|d)', data_frequency, re.M | re.I)
|
||||
if freq_match:
|
||||
number = int(freq_match.group(1))
|
||||
unit = freq_match.group(2)
|
||||
|
||||
if unit == 'd':
|
||||
converted_unit = 'D'
|
||||
else:
|
||||
converted_unit = unit
|
||||
|
||||
frequency = '{}{}'.format(number, converted_unit)
|
||||
allowed_frequencies = ['1m', '5m', '15m', '30m', '1h', '3h', '6h',
|
||||
'12h', '1D', '7D', '14D', '1M']
|
||||
|
||||
if frequency not in allowed_frequencies:
|
||||
raise InvalidHistoryFrequencyError(
|
||||
frequency=data_frequency
|
||||
)
|
||||
elif data_frequency == 'minute':
|
||||
frequency = '1m'
|
||||
elif data_frequency == 'daily':
|
||||
frequency = '1D'
|
||||
if(data_frequency == '5m' or data_frequency == 'minute'): #TODO: Polo does not have '1m'
|
||||
frequency = 300
|
||||
elif(data_frequency == '15m'):
|
||||
frequency = 900
|
||||
elif(data_frequency == '30m'):
|
||||
frequency = 1800
|
||||
elif(data_frequency == '2h'):
|
||||
frequency = 7200
|
||||
elif(data_frequency == '4h'):
|
||||
frequency = 14400
|
||||
elif(data_frequency == '1D' or data_frequency == 'daily'):
|
||||
frequency = 86400
|
||||
else:
|
||||
raise InvalidHistoryFrequencyError(
|
||||
frequency=data_frequency
|
||||
@@ -213,63 +204,51 @@ class Poloniex(Exchange):
|
||||
# Making sure that assets are iterable
|
||||
asset_list = [assets] if isinstance(assets, TradingPair) else assets
|
||||
ohlc_map = dict()
|
||||
|
||||
for asset in asset_list:
|
||||
symbol = self._get_v2_symbol(asset)
|
||||
url = '{url}/v2/candles/trade:{frequency}:{symbol}'.format(
|
||||
url=self.url,
|
||||
frequency=frequency,
|
||||
symbol=symbol
|
||||
)
|
||||
|
||||
if bar_count:
|
||||
is_list = True
|
||||
url += '/hist?limit={}'.format(int(bar_count))
|
||||
end = int(time.time())
|
||||
if(bar_count is None):
|
||||
start = end - 2 * frequency
|
||||
else:
|
||||
is_list = False
|
||||
url += '/last'
|
||||
start = end - bar_count * frequency
|
||||
|
||||
try:
|
||||
response = requests.get(url)
|
||||
try:
|
||||
response = self.api.returnchartdata(self.get_symbol(asset),frequency, start, end)
|
||||
except Exception as e:
|
||||
raise ExchangeRequestError(error=e)
|
||||
|
||||
if 'error' in response.content:
|
||||
if 'error' in response:
|
||||
raise ExchangeRequestError(
|
||||
error='Unable to retrieve candles: {}'.format(
|
||||
response.content)
|
||||
)
|
||||
|
||||
candles = response.json()
|
||||
|
||||
def ohlc_from_candle(candle):
|
||||
ohlc = dict(
|
||||
open=np.float64(candle[1]),
|
||||
high=np.float64(candle[3]),
|
||||
low=np.float64(candle[4]),
|
||||
close=np.float64(candle[2]),
|
||||
volume=np.float64(candle[5]),
|
||||
price=np.float64(candle[2]),
|
||||
last_traded=pd.Timestamp.utcfromtimestamp(
|
||||
candle[0] / 1000.0)
|
||||
open=np.float64(candle['open']),
|
||||
high=np.float64(candle['high']),
|
||||
low=np.float64(candle['low']),
|
||||
close=np.float64(candle['close']),
|
||||
volume=np.float64(candle['volume']),
|
||||
price=np.float64(candle['close']),
|
||||
last_traded=pd.Timestamp.utcfromtimestamp( candle['date'] )
|
||||
)
|
||||
|
||||
return ohlc
|
||||
|
||||
if is_list:
|
||||
if bar_count is None:
|
||||
ohlc_map[asset] = ohlc_from_candle(response[0])
|
||||
else:
|
||||
ohlc_bars = []
|
||||
# We can to list candles from old to new
|
||||
for candle in reversed(candles):
|
||||
for candle in response:
|
||||
ohlc = ohlc_from_candle(candle)
|
||||
ohlc_bars.append(ohlc)
|
||||
|
||||
ohlc_map[asset] = ohlc_bars
|
||||
|
||||
else:
|
||||
ohlc = ohlc_from_candle(candles)
|
||||
ohlc_map[asset] = ohlc
|
||||
|
||||
return ohlc_map[assets] \
|
||||
if isinstance(assets, TradingPair) else ohlc_map
|
||||
'''
|
||||
|
||||
|
||||
def create_order(self, asset, amount, is_buy, style):
|
||||
pass
|
||||
@@ -338,7 +317,7 @@ class Poloniex(Exchange):
|
||||
return order
|
||||
'''
|
||||
|
||||
def get_open_orders(self, asset=None):
|
||||
def get_open_orders(self, asset='all'):
|
||||
"""Retrieve all of the current open orders.
|
||||
|
||||
Parameters
|
||||
@@ -355,28 +334,29 @@ class Poloniex(Exchange):
|
||||
If an asset is passed then this will return a list of the open
|
||||
orders for this asset.
|
||||
"""
|
||||
pass
|
||||
'''
|
||||
try:
|
||||
response = self._request('orders', None)
|
||||
order_statuses = response.json()
|
||||
if(asset=='all'):
|
||||
response = self.api.returnopenorders('all')
|
||||
else:
|
||||
response = self.api.returnopenorders(self.get_symbol(asset))
|
||||
except Exception as e:
|
||||
raise ExchangeRequestError(error=e)
|
||||
|
||||
if 'message' in order_statuses:
|
||||
if 'error' in response:
|
||||
raise ExchangeRequestError(
|
||||
error='Unable to retrieve open orders: {}'.format(
|
||||
order_statuses['message'])
|
||||
)
|
||||
|
||||
#TODO: Need to handle openOrders for 'all'
|
||||
orders = list()
|
||||
for order_status in order_statuses:
|
||||
for order_status in response:
|
||||
order, executed_price = self._create_order(order_status)
|
||||
if asset is None or asset == order.sid:
|
||||
orders.append(order)
|
||||
|
||||
return orders
|
||||
'''
|
||||
|
||||
|
||||
def get_order(self, order_id):
|
||||
"""Lookup an order based on the order id returned from one of the
|
||||
@@ -417,24 +397,21 @@ class Poloniex(Exchange):
|
||||
order_param : str or Order
|
||||
The order_id or order object to cancel.
|
||||
"""
|
||||
pass
|
||||
'''
|
||||
order_id = order_param.id \
|
||||
if isinstance(order_param, Order) else order_param
|
||||
|
||||
try:
|
||||
response = self._request('order/cancel', {'order_id': order_id})
|
||||
status = response.json()
|
||||
response = self.api.cancelorder(order_id)
|
||||
except Exception as e:
|
||||
raise ExchangeRequestError(error=e)
|
||||
|
||||
if 'message' in status:
|
||||
if 'error' in response:
|
||||
raise OrderCancelError(
|
||||
order_id=order_id,
|
||||
exchange=self.name,
|
||||
error=status['message']
|
||||
error=response['error']
|
||||
)
|
||||
'''
|
||||
|
||||
|
||||
def tickers(self, assets):
|
||||
"""
|
||||
|
||||
@@ -29,19 +29,22 @@ class Poloniex_api(object):
|
||||
'cancelLoanOffer','returnOpenLoanOffers','returnActiveLoans',
|
||||
'returnLendingHistory','toggleAutoRenew']
|
||||
|
||||
def query(self, method, values={}):
|
||||
def query(self, method, req={}):
|
||||
|
||||
if method in self.public:
|
||||
url = 'https://poloniex.com/public?command=' + method + urllib.parse.urlencode(values)
|
||||
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.urlencode(req)
|
||||
post_data = urllib.parse.urlencode(req)
|
||||
print(post_data)
|
||||
signature = hmac.new(self.secret, post_data, 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')
|
||||
|
||||
req = urllib.request.Request(url, data=post_data, headers=headers)
|
||||
return json.loads(urlopen(req).read())
|
||||
@@ -70,7 +73,16 @@ class Poloniex_api(object):
|
||||
return self.query('returnCurrencies')
|
||||
|
||||
def returnloadorders(self, market):
|
||||
return self.query('returnLoanOrders', {'market': market})
|
||||
return self.query('returnLoanOrders', {'currency': market})
|
||||
|
||||
def returnbalances(self):
|
||||
return self.query('returnBalances')
|
||||
|
||||
def returnopenorders(self, market):
|
||||
return self.query('returnOpenOrders', {'currencyPair': market})
|
||||
|
||||
def cancelorder(self, ordernumber):
|
||||
return self.query('cancelOrder', {'orderNumber': ordernumber})
|
||||
|
||||
'''
|
||||
def buylimit(self, market, quantity, rate):
|
||||
@@ -89,15 +101,6 @@ class Poloniex_api(object):
|
||||
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})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user