예제 #1
0
    async def connect(self):
        with open(self._filename) as csvfile:
            self._reader = csv.DictReader(csvfile, delimiter=',')

            for row in self._reader:
                order = Order(volume=float(row['volume']),
                              price=float(row['close']),
                              side=Side.BUY,
                              exchange=self.exchange(),
                              instrument=Instrument(
                    row['symbol'].split('-')[0],
                    InstrumentType(row['symbol'].split('-')[1].upper())
                )
                )
                order.filled = float(row['volume'])
                if 'time' in row:
                    order.timestamp = datetime.fromtimestamp(float(row['time']))
                elif 'date' in row:
                    order.timestamp = datetime.fromisoformat(row['date'])
                elif 'datetime' in row:
                    order.timestamp = datetime.fromisoformat(row['datetime'])

                self._data.append(Trade(volume=float(row['volume']),
                                        price=float(row['close']),
                                        maker_orders=[],
                                        taker_order=order))
예제 #2
0
파일: harness.py 프로젝트: galdamour/aat
    async def tick(self):
        now = self._start
        for i in range(1000):
            if self._client_order:
                self._client_order.filled = self._client_order.volume
                t = Trade(self._client_order.volume, i, [], self._client_order)
                t.taker_order.timestamp = now
                self._client_order = None
                yield Event(type=EventType.TRADE, target=t)
                continue

            o = Order(1, i, Side.BUY, self._instrument, self.exchange())
            o.filled = 1
            o.timestamp = now
            t = Trade(1, i, [], o)
            yield Event(type=EventType.TRADE, target=t)
            now += timedelta(minutes=30)
예제 #3
0
    def add(self, order: Order) -> None:
        """add a new order to the order book, potentially triggering events:
            EventType.TRADE: if this order crosses the book and fills orders
            EventType.FILL: if this order crosses the book and fills orders
            EventType.CHANGE: if this order crosses the book and partially fills orders
        Args:
            order (Data): order to submit to orderbook
        """
        if order is None:
            raise Exception("Order cannot be None")

        # secondary triggered orders
        secondaries: List[Order] = []

        # get the top price on the opposite side of book
        top = self._getTop(order.side, self._collector.clearedLevels())

        # set levels to the right side
        levels = self._buy_levels if order.side == Side.BUY else self._sell_levels
        prices = self._buys if order.side == Side.BUY else self._sells
        prices_cross = self._sells if order.side == Side.BUY else self._buys

        # set order price appropriately
        if order.order_type == OrderType.MARKET:
            if order.flag in (None, OrderFlag.NONE):
                # price goes infinite "fill however you want"
                order_price = float(
                    "inf") if order.side == Side.BUY else float("-inf")
            else:
                # with a flag, the price dictates the "max allowed price" to AON or FOK under
                order_price = order.price
        else:
            order_price = order.price

        # check if crosses
        while top is not None and (order_price >= top if order.side == Side.BUY
                                   else order_price <= top):
            # execute order against level
            # if returns trade, it cleared the level
            # else, order was fully executed
            trade, new_secondaries = prices_cross[top].cross(order)

            if new_secondaries:
                # append to secondaries
                secondaries.extend(new_secondaries)

            if trade:
                # clear sell level
                top = self._getTop(
                    order.side, self._collector.clearLevel(prices_cross[top]))
                continue

            # trade is done, check if level was cleared exactly
            if not prices_cross[top]:
                # level cleared exactly
                self._collector.clearLevel(prices_cross[top])
            break

        # if order remaining, check rules/push to book
        if order.filled < order.volume:
            if order.order_type == OrderType.MARKET:
                # Market orders
                if order.flag in (OrderFlag.ALL_OR_NONE,
                                  OrderFlag.FILL_OR_KILL):
                    # cancel the order, do not execute any
                    self._collector.revert()

                    # cancel the order
                    self._collector.pushCancel(order)
                    self._collector.commit()
                else:
                    # market order, partial
                    if order.filled > 0:
                        self._collector.pushTrade(order, order.filled)

                    # clear levels
                    self._clearOrders(order, self._collector.clearedLevels())

                    # execute order, cancel the rest
                    self._collector.pushCancel(order)
                    self._collector.commit()

                    # execute secondaries
                    for secondary in secondaries:
                        secondary.timestamp = order.timestamp  # adjust trigger time
                        self.add(secondary)

            else:
                # Limit Orders
                if order.flag == OrderFlag.FILL_OR_KILL:
                    if order.filled > 0:
                        # reverse partial
                        # cancel the order, do not execute any
                        self._collector.revert()

                        # reset filled
                        order.filled = 0.0

                        # cancel the order
                        self._collector.pushCancel(order)
                        self._collector.commit()
                    else:
                        # add to book
                        self._collector.commit()

                        # limit order, put on books
                        if _insort(levels, order.price):
                            # new price level
                            prices[order.price] = _PriceLevel(  # type: ignore
                                order.price,
                                collector=self._collector)

                        # add order to price level
                        prices[order.price].add(order)

                        # execute secondaries
                        for secondary in secondaries:
                            secondary.timestamp = order.timestamp  # adjust trigger time
                            self.add(secondary)

                elif order.flag == OrderFlag.ALL_OR_NONE:
                    if order.filled > 0:
                        # order could not fill fully, revert
                        # cancel the order, do not execute any
                        self._collector.revert()

                        # reset filled
                        order.filled = 0.0

                        # cancel the order
                        self._collector.pushCancel(order)
                        self._collector.commit()

                    else:
                        # add to book
                        self._collector.commit()

                        # limit order, put on books
                        if _insort(levels, order.price):
                            # new price level
                            prices[order.price] = _PriceLevel(  # type: ignore
                                order.price,
                                collector=self._collector)

                        # add order to price level
                        prices[order.price].add(order)

                        # execute secondaries
                        for secondary in secondaries:
                            secondary.timestamp = order.timestamp  # adjust trigger time
                            self.add(secondary)

                elif order.flag == OrderFlag.IMMEDIATE_OR_CANCEL:
                    if order.filled > 0:
                        # clear levels
                        self._clearOrders(order,
                                          self._collector.clearedLevels())

                        # execute the ones that filled, kill the remainder
                        self._collector.pushCancel(order)

                        # commit
                        self._collector.commit()

                        # execute secondaries
                        for secondary in secondaries:
                            secondary.timestamp = order.timestamp  # adjust trigger time
                            self.add(secondary)

                    else:
                        # add to book
                        self._collector.commit()

                        # limit order, put on books
                        if _insort(levels, order.price):
                            # new price level
                            prices[order.price] = _PriceLevel(  # type: ignore
                                order.price,
                                collector=self._collector)

                        # add order to price level
                        prices[order.price].add(order)

                        # execute secondaries
                        for secondary in secondaries:
                            secondary.timestamp = order.timestamp  # adjust trigger time
                            self.add(secondary)

                else:
                    # clear levels
                    self._clearOrders(order, self._collector.clearedLevels())

                    # execute order
                    self._collector.commit()

                    # limit order, put on books
                    if _insort(levels, order.price):
                        # new price level
                        prices[order.price] = _PriceLevel(  # type: ignore
                            order.price,
                            collector=self._collector)

                    # add order to price level
                    prices[order.price].add(order)

                    # execute secondaries
                    for secondary in secondaries:
                        secondary.timestamp = order.timestamp  # adjust trigger time
                        self.add(secondary)
        else:
            if order.filled > order.volume:
                raise Exception(
                    "Unknown error occurred - order book is corrupt")

            # don't need to add trade as this is done in the price_levels
            # clear levels
            self._clearOrders(order, self._collector.clearedLevels())

            # execute all the orders
            self._collector.commit()

            # execute secondaries
            for secondary in secondaries:
                secondary.timestamp = order.timestamp  # adjust trigger time
                self.add(secondary)

        # clear the collector
        self._collector.clear()
예제 #4
0
    async def tick(self):
        '''return data from exchange'''

        if self._timeframe == 'live':
            data = deque()

            def _callback(record):
                data.append(record)

            self._client.tradesSSE(symbols=",".join(
                [i.name for i in self._subscriptions]),
                                   on_data=_callback)

            while True:
                while data:
                    record = data.popleft()
                    volume = record['volume']
                    price = record['price']
                    instrument = Instrument(record['symbol'],
                                            InstrumentType.EQUITY)

                    o = Order(volume=volume,
                              price=price,
                              side=Side.BUY,
                              instrument=instrument,
                              exchange=self.exchange())
                    t = Trade(volume=volume,
                              price=price,
                              taker_order=o,
                              maker_orders=[])
                    yield Event(type=EventType.TRADE, target=t)

                await asyncio.sleep(0)

        else:
            dfs = []
            insts = set()

            if self._timeframe != '1d':
                for i in tqdm(self._subscriptions, desc="Fetching data..."):
                    if i.name in insts:
                        # already fetched the data, multiple subscriptions
                        continue

                    if self._cache_data:
                        # first, check if we have this data and its cached already
                        os.makedirs('_aat_data', exist_ok=True)
                        data_filename = os.path.join(
                            '_aat_data', 'iex_{}_{}_{}_{}.pkl'.format(
                                i.name, self._timeframe,
                                datetime.now().strftime('%Y%m%d'),
                                'sand' if self._is_sandbox else ''))

                        if os.path.exists(data_filename):
                            print('using cached IEX data for {}'.format(
                                i.name))
                            df = pd.read_pickle(data_filename)
                        else:
                            df = self._client.chartDF(
                                i.name, timeframe=self._timeframe)
                            df.to_pickle(data_filename)

                    else:
                        df = self._client.chartDF(i.name,
                                                  timeframe=self._timeframe)

                    df = df[['close', 'volume']]
                    df.columns = [
                        'close:{}'.format(i.name), 'volume:{}'.format(i.name)
                    ]
                    dfs.append(df)
                    insts.add(i.name)

                data = pd.concat(dfs, axis=1)
                data.sort_index(inplace=True)
                data = data.groupby(data.index).last()
                data.drop_duplicates(inplace=True)
                data.fillna(method='ffill', inplace=True)

            else:
                for i in tqdm(self._subscriptions, desc="Fetching data..."):
                    if i.name in insts:
                        # already fetched the data, multiple subscriptions
                        continue

                    date = self._start_date
                    subdfs = []
                    while date <= self._end_date:
                        if self._cache_data:
                            # first, check if we have this data and its cached already
                            os.makedirs('_aat_data', exist_ok=True)
                            data_filename = os.path.join(
                                '_aat_data', 'iex_{}_{}_{}_{}.pkl'.format(
                                    i.name, self._timeframe, date,
                                    'sand' if self._is_sandbox else ''))

                            if os.path.exists(data_filename):
                                print(
                                    'using cached IEX data for {} - {}'.format(
                                        i.name, date))
                                df = pd.read_pickle(data_filename)
                            else:
                                df = self._client.chartDF(
                                    i.name,
                                    timeframe='1d',
                                    date=date.strftime('%Y%m%d'))
                                df.to_pickle(data_filename)
                        else:
                            df = self._client.chartDF(
                                i.name,
                                timeframe='1d',
                                date=date.strftime('%Y%m%d'))

                        if not df.empty:
                            df = df[['average', 'volume']]
                            df.columns = [
                                'close:{}'.format(i.name),
                                'volume:{}'.format(i.name)
                            ]
                            subdfs.append(df)

                        date += timedelta(days=1)

                    dfs.append(pd.concat(subdfs))
                    insts.add(i.name)

                data = pd.concat(dfs, axis=1)
                data.index = [
                    x + timedelta(hours=int(y.split(':')[0]),
                                  minutes=int(y.split(':')[1]))
                    for x, y in data.index
                ]
                data = data.groupby(data.index).last()
                data.drop_duplicates(inplace=True)
                data.fillna(method='ffill', inplace=True)

            for index in data.index:
                for i in self._subscriptions:
                    volume = data.loc[index]['volume:{}'.format(i.name)]
                    price = data.loc[index]['close:{}'.format(i.name)]
                    if volume == 0:
                        continue

                    o = Order(volume=volume,
                              price=price,
                              side=Side.BUY,
                              instrument=i,
                              exchange=self.exchange())
                    o.filled = volume
                    o.timestamp = index.to_pydatetime()

                    t = Trade(volume=volume,
                              price=price,
                              taker_order=o,
                              maker_orders=[])

                    yield Event(type=EventType.TRADE, target=t)
                    await asyncio.sleep(0)

                while self._queued_orders:
                    order = self._queued_orders.popleft()
                    order.timestamp = index
                    order.filled = order.volume

                    t = Trade(volume=order.volume,
                              price=order.price,
                              taker_order=order,
                              maker_orders=[])
                    t.my_order = order

                    yield Event(type=EventType.TRADE, target=t)
                    await asyncio.sleep(0)