diff --git a/pylivetrader/algorithm.py b/pylivetrader/algorithm.py index 28a70a0..96632ae 100644 --- a/pylivetrader/algorithm.py +++ b/pylivetrader/algorithm.py @@ -613,7 +613,19 @@ def get_open_orders(self, asset=None): return self.get_all_orders(asset=asset, status='open') @api_method - def get_all_orders(self, asset=None, before=None, status='all'): + def get_recent_orders(self, days_back=2): + ''' + Returns all orders from the past n days. + ''' + return self.get_all_orders(days_back=days_back) + + @api_method + def get_all_orders( + self, + asset=None, + before=None, + status='all', + days_back=None): ''' If asset is unspecified or None, returns a dictionary keyed by asset ID. The dictionary contains a list of orders for each ID, @@ -622,7 +634,7 @@ def get_all_orders(self, asset=None, before=None, status='all'): before will not be returned. If provided, only orders of type status ('closed' or 'open') will be returned. ''' - orders = self._backend.all_orders(before, status) + orders = self._backend.all_orders(before, status, days_back) omap = {} orders = sorted([ @@ -640,9 +652,7 @@ def get_all_orders(self, asset=None, before=None, status='all'): @api_method def get_order(self, order_id): - orders = self._backend.orders - if order_id in orders: - return orders[order_id].to_api_obj() + return self._backend.get_order(order_id).to_api_obj() @api_method def cancel_order(self, order_param): diff --git a/pylivetrader/backend/alpaca.py b/pylivetrader/backend/alpaca.py index 76fb039..5cb0d1b 100644 --- a/pylivetrader/backend/alpaca.py +++ b/pylivetrader/backend/alpaca.py @@ -287,20 +287,45 @@ def orders(self): for o in self._api.list_orders('all') } - def all_orders(self, before=None, status='all'): - until = pd.Timestamp.utcnow().isoformat() if before is None else before + def get_order(self, zp_order_id): + return self._order2zp( + self._api.get_order_by_client_order_id(zp_order_id) + ) + + def all_orders(self, before=None, status='all', days_back=None): + # Get all orders submitted days_back days before `before` or now. + now = pd.Timestamp.utcnow() + start = now.isoformat() if before is None else before.isoformat() + + # A session label refers to the market date that an order submitted + # at a given minute would be executed on. We'll need to keep track of + # this if the function is bounded by days_back. + start_session_label = self._cal.minute_to_session_label(start) + reached_end_date = False + all_orders = {} batch_size = 500 - orders = self._api.list_orders(status, batch_size, until=until) - while len(orders) > 0: - batch_orders = { - o.client_order_id: self._order2zp(o) - for o in orders - } - # get the timestamp of the earliest order in the batch - until = pd.Timestamp(orders[-1].submitted_at).isoformat() + + orders = self._api.list_orders(status, batch_size, until=start) + while len(orders) > 0 and not reached_end_date: + batch_orders = {} + for order in orders: + if days_back is not None: + # Verify that the order is not too old. + # `session_distance()` ignores holidays and weekends. + days_since_order = self._cal.session_distance( + self._cal.minute_to_session_label(order.submitted_at), + start_session_label + ) + if days_since_order > days_back: + reached_end_date = True + break + batch_orders[order.client_order_id] = self._order2zp(order) all_orders.update(batch_orders) - orders = self._api.list_orders(status, batch_size, until=until) + if not reached_end_date: + # Get the timestamp of the earliest order in the batch. + until = pd.Timestamp(orders[-1].submitted_at).isoformat() + orders = self._api.list_orders(status, batch_size, until=until) return all_orders def cancel_order(self, zp_order_id):