Implemented order operations

This commit is contained in:
Frederic Fortier
2017-08-14 10:04:14 -04:00
parent 383370f9d8
commit fcff8c7438
3 changed files with 88 additions and 45 deletions
+78 -30
View File
@@ -8,7 +8,7 @@ import requests
import pandas as pd
from catalyst.protocol import Portfolio, Account
# from websocket import create_connection
from catalyst.exchange.exchange import Exchange, RTVolumeBar, Position
from catalyst.exchange.exchange import Exchange
from logbook import Logger
from catalyst.finance.order import Order, ORDER_STATUS
from catalyst.finance.execution import (MarketOrder,
@@ -98,8 +98,62 @@ class Bitfinex(Exchange):
pair = asset.symbol.split('_')
symbol = 't' + pair[0].upper() + pair[1].upper()
v2_symbols.append(symbol)
return v2_symbols
def _create_order(self, order_status):
"""
Create a Catalyst order object from a Bitfinex 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 %s', order_status)
status = ORDER_STATUS.FILLED
else:
status = ORDER_STATUS.OPEN
amount = float(order_status['original_amount'])
filled = float(order_status['executed_amount'])
is_buy = (amount > 0)
price = float(order_status['price'])
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
executed_price = float(order_status['avg_execution_price'])
if executed_price > 0 and price > 0:
# TODO: This does not really work. Find a better way.
commission = executed_price - price \
if is_buy else price - executed_price
else:
commission = None
order = Order(
dt=pd.Timestamp.utcfromtimestamp(float(order_status['timestamp'])),
asset=self.assets[order_status['symbol']],
amount=amount,
stop=stop_price,
limit=limit_price,
filled=filled,
id=order_status['id'],
commission=commission
)
order.status = status
return order
@property
def portfolio(self):
"""
@@ -168,7 +222,6 @@ class Bitfinex(Exchange):
def get_spot_value(self, assets, field, dt, data_frequency):
raise NotImplementedError()
# TODO: why repeating prices if already in style?
def order(self, asset, amount, limit_price, stop_price, style):
"""Place an order.
@@ -253,20 +306,23 @@ class Bitfinex(Exchange):
'message']
)
order_id = exchange_order['id']
order = Order(
dt=pd.Timestamp.utcnow(),
asset=asset,
amount=amount,
stop=style.get_stop_price(is_buy),
limit=style.get_limit_price(is_buy),
id=order_id
)
# TODO: is this required?
order.broker_order_id = order_id
order_id = order.broker_order_id = exchange_order['id']
self.orders[order_id] = order
return order_id
def get_open_orders(self, asset):
def get_open_orders(self, asset=None):
"""Retrieve all of the current open orders.
Parameters
@@ -283,10 +339,19 @@ class Bitfinex(Exchange):
If an asset is passed then this will return a list of the open
orders for this asset.
"""
# TODO: map to asset
response = self._request('orders', None)
orders = response.json()
# TODO: what is the right format?
order_statuses = response.json()
if 'message' in order_statuses:
raise ValueError(
'Unable to retrieve open orders: %s' % order_statuses[
'message']
)
orders = list()
for order_status in order_statuses:
# TODO: filter by asset
orders.append(self._create_order(order_status))
return orders
def get_order(self, order_id):
@@ -305,32 +370,12 @@ class Bitfinex(Exchange):
"""
response = self._request('order/status', {'order_id': int(order_id)})
order_status = response.json()
if 'message' in order_status:
raise ValueError(
'Unable to retrieve order status: %s' % order_status['message']
)
result = dict(exchange=self.name)
if order_status['is_cancelled']:
warning_logger.warn(
'removing cancelled order from the open orders list %s',
order_status)
result['status'] = 'canceled'
elif not order_status['is_live']:
log.info('found executed order %s', order_status)
result['status'] = 'closed'
result['executed_price'] = \
float(order_status['avg_execution_price'])
result['executed_amount'] = \
float(order_status['executed_amount'])
else:
result['status'] = 'open'
# TODO: what's the right format?
return result
return self._create_order(order_status)
def cancel_order(self, order_id):
"""Cancel an open order.
@@ -342,7 +387,10 @@ class Bitfinex(Exchange):
"""
response = self._request('order/cancel', {'order_id': order_id})
status = response.json()
return status
if 'message' in status:
raise ValueError(
'Unable to cancel order: %s %s' % (order_id, status['message'])
)
def tickers(self, date, assets):
"""
+5 -12
View File
@@ -5,18 +5,6 @@ import json
import pandas as pd
from catalyst.assets._assets import Asset
RTVolumeBar = namedtuple('RTVolumeBar', ['last_trade_price',
'last_trade_size',
'last_trade_time',
'total_volume',
'vwap',
'single_trade_flag'])
Position = namedtuple('Position', ['contract', 'position', 'market_price',
'market_value', 'average_cost',
'unrealized_pnl', 'realized_pnl',
'account_name'])
class Exchange:
__metaclass__ = ABCMeta
@@ -43,6 +31,11 @@ class Exchange:
return symbol
def get_asset(self, symbol):
"""
Find an Asset on the current exchange based on its Catalyst symbol
:param symbol: the [target]_[base] currency pair symbol
:return: Asset
"""
asset = None
for key in self.assets:
+5 -3
View File
@@ -6,7 +6,6 @@ from catalyst.finance.execution import (MarketOrder,
LimitOrder,
StopOrder,
StopLimitOrder)
import catalyst.protocol as protocol
log = Logger('BitfinexTestCase')
@@ -33,6 +32,9 @@ class BitfinexTestCase(BaseExchangeTestCase):
def test_get_open_orders(self):
log.info('fetching open orders')
bitfinex = Bitfinex()
order_id = bitfinex.get_open_orders()
log.info('open orders: {}'.format(order_id))
pass
def test_order(self):
@@ -51,8 +53,8 @@ class BitfinexTestCase(BaseExchangeTestCase):
def test_get_order(self):
log.info('querying orders from bitfinex')
bitfinex = Bitfinex()
response = bitfinex.order_status(order_id=3330866978)
log.info('the orders: {}'.format(response))
response = bitfinex.get_order(order_id=3330866978)
log.info('the order: {}'.format(response))
pass
def test_cancel_order(self):