Ejemplo n.º 1
0
    async def tick(self):
        """helper method to ensure periodic methods execute periodically in absence
        of market data"""

        if self._offline():
            # periodics injected manually, see main event loop above
            return

        while True:
            yield Event(type=EventType.HEARTBEAT, target=None)
            await asyncio.sleep(1)
Ejemplo n.º 2
0
    def start(self):
        try:
            # if self.event_loop.is_running():
            #     # return future
            #     return asyncio.create_task(self.run())
            # block until done
            self.event_loop.run_until_complete(self.run())
        except KeyboardInterrupt:
            pass

        # send exit event to all callbacks
        asyncio.ensure_future(
            self.processEvent(Event(type=EventType.EXIT, target=None)))
Ejemplo n.º 3
0
    async def processEvent(self,
                           event: Event,
                           strategy: Strategy = None) -> None:
        """send an event to all registered event handlers

        Arguments:
            event (Event): event to send
        """
        if event.type == EventType.HEARTBEAT:
            # ignore heartbeat
            return

        for callback, handler in self._handler_subscriptions[event.type]:
            # TODO make cleaner? move to somewhere not in critical path?
            if strategy is not None and (handler
                                         not in (strategy, self.manager)):
                continue

            # TODO make cleaner? move to somewhere not in critical path?
            if (event.type in (
                    EventType.TRADE,
                    EventType.OPEN,
                    EventType.CHANGE,
                    EventType.CANCEL,
                    EventType.DATA,
            ) and not self.manager.dataSubscriptions(handler, event)):
                continue

            try:
                await callback(event)
            except KeyboardInterrupt:
                raise
            except SystemExit:
                raise
            except BaseException as e:
                if event.type == EventType.ERROR:
                    # don't infinite error
                    raise
                await self.processEvent(
                    Event(
                        type=EventType.ERROR,
                        target=Error(
                            target=event,
                            handler=handler,
                            callback=callback,
                            exception=e,
                        ),
                    ))
                await asyncio.sleep(1)
Ejemplo n.º 4
0
    def pushTrade(self, taker_order, filled_in_txn):
        """push taker order trade"""
        if not self.orders:
            raise Exception("No maker orders provided")

        if taker_order.filled <= 0:
            raise Exception("No trade occurred")

        if filled_in_txn != self.volume:
            raise Exception("Accumulation error occurred")

        self.push(
            Event(
                type=EventType.TRADE,
                target=Trade(
                    volume=self.volume,
                    price=self.price,
                    maker_orders=self.orders.copy(),
                    taker_order=taker_order,
                ),
            ))

        self._taker_order = taker_order
Ejemplo n.º 5
0
    async def run(self):
        """run the engine"""
        # setup future queue
        self._queued_events = deque()
        self._queued_targeted_events = deque()

        # await all connections
        await asyncio.gather(*(asyncio.create_task(exch.connect())
                               for exch in self.exchanges))
        await asyncio.gather(*(asyncio.create_task(exch.instruments())
                               for exch in self.exchanges))

        # send start event to all callbacks
        await self.processEvent(Event(type=EventType.START, target=None))

        # **************** #
        # Main event loop
        # **************** #
        async with merge(
            *(exch.tick() for exch in self.exchanges + [self]
              if inspect.isasyncgenfunction(exch.tick))).stream() as stream:
            # stream through all events
            async for event in stream:
                # TODO move out of critical path
                if self._offline():
                    # inject periodics
                    # TODO optimize
                    # Manager should keep track of the intervals for its periodics,
                    # then we don't need to go through seconds (which is what the
                    # live engine's `tick` method does below). Instead we can just
                    # calculate exactly the intervals
                    if (self._latest != datetime.fromtimestamp(0)
                            and hasattr(event, "target")
                            and hasattr(event.target, "timestamp")):
                        # TODO in progress optimization
                        intervals = self.manager.periodicIntervals()

                        # not the first tick
                        for _ in range(
                                int((event.target.timestamp -
                                     self._latest).total_seconds() /
                                    intervals)):
                            self._latest = self._latest + timedelta(seconds=1 *
                                                                    intervals)
                            if any(
                                    p.expires(self._latest)
                                    for p in self.manager.periodics()):
                                await asyncio.gather(
                                    *(asyncio.create_task(
                                        p.execute(self._latest))
                                      for p in self.manager.periodics()
                                      if p.expires(self._latest)))

                # tick exchange event to handlers
                await self.processEvent(event)

                # TODO move out of critical path
                if self._offline():
                    # use time of last event
                    self._latest = (event.target.timestamp
                                    if hasattr(event, "target")
                                    and hasattr(event.target, "timestamp") else
                                    self._latest)
                else:
                    # use now
                    self._latest = datetime.now()

                # process any secondary events
                while self._queued_events:
                    event = self._queued_events.popleft()
                    await self.processEvent(event)

                # process any secondary callback-targeted events (e.g. order fills)
                # these need to route to a specific callback,
                # rather than all callbacks
                while self._queued_targeted_events:
                    strat, event = self._queued_targeted_events.popleft()

                    # send to the generating strategy
                    await self.processEvent(event, strat)

                # process any periodics
                await asyncio.gather(
                    *(asyncio.create_task(p.execute(self._latest))
                      for p in self.manager.periodics()
                      if p.expires(self._latest)))

        # Before engine shutdown, send an exit event
        await self.processEvent(Event(type=EventType.EXIT, target=None))
Ejemplo n.º 6
0
 def pushCancel(self, order, accumulate=False, filled_in_txn=0.0):
     """push order cancellation"""
     if accumulate:
         self.accumulate(order, filled_in_txn)
     self.push(Event(type=EventType.CANCEL, target=order))
Ejemplo n.º 7
0
 def pushChange(self, order, accumulate=False, filled_in_txn=0.0):
     """push order change"""
     if accumulate:
         self.accumulate(order, filled_in_txn)
     self.push(Event(type=EventType.CHANGE, target=order))
Ejemplo n.º 8
0
 def pushFill(self, order, accumulate=False, filled_in_txn=0.0):
     """push order fill"""
     if accumulate:
         self.accumulate(order, filled_in_txn)
     self.push(Event(type=EventType.FILL, target=order))
Ejemplo n.º 9
0
 def pushOpen(self, order):
     """push order open"""
     self.push(Event(type=EventType.OPEN, target=order))