Exemplo n.º 1
0
    def orderBook(self, subscriptions: List[Instrument]):
        '''fetch level 3 order book for each Instrument in our subscriptions'''
        for sub in subscriptions:
            # fetch the order book
            # order book is of form:
            #       {'bids': [[price, volume, id]],
            #        'asks': [[price, volume, id]],
            #        'sequence': <some positive integer>}
            ob = self._orderBook(sub.brokerId)

            # set the last sequence number for when we
            # connect to websocket later
            self.seqnum[sub] = ob['sequence']  # type: ignore

            # generate an open limit order for each bid
            for (bid, qty, id) in ob['bids']:
                o = Order(float(qty),
                          float(bid),
                          Side.BUY,
                          sub,
                          self.exchange,
                          order_type=OrderType.LIMIT)
                yield Event(type=EventType.OPEN, target=o)

            # generate an open limit order for each ask
            for (bid, qty, id) in ob['asks']:
                o = Order(float(qty),
                          float(bid),
                          Side.SELL,
                          sub,
                          self.exchange,
                          order_type=OrderType.LIMIT)
                yield Event(type=EventType.OPEN, target=o)
Exemplo n.º 2
0
    async def onTrade(self, event: Event) -> None:
        '''Called whenever a `Trade` event is received'''
        trade: Trade = event.target  # type: ignore

        # no current orders, no past trades
        if not self.orders(trade.instrument) and not self.trades(
                trade.instrument):
            req = Order(side=Side.BUY,
                        price=trade.price,
                        volume=math.ceil(1000 / trade.price),
                        instrument=trade.instrument,
                        order_type=Order.Types.MARKET,
                        exchange=trade.exchange)

            print('requesting buy : {}'.format(req))
            await self.newOrder(req)

        else:
            # no current orders, 1 past trades, and stop set
            if not self.orders(trade.instrument) and len(self.trades(trade.instrument)) == 1 and \
                    trade.instrument in self._stop and \
                    (trade.price >= self._stop[trade.instrument][0] or
                     trade.price <= self._stop[trade.instrument][1]):
                req = Order(side=Side.SELL,
                            price=trade.price,
                            volume=self._stop[trade.instrument][2],
                            instrument=trade.instrument,
                            order_type=Order.Types.MARKET,
                            exchange=trade.exchange)

                print('requesting sell : {}'.format(req))
                await self.newOrder(req)
Exemplo n.º 3
0
    async def newOrder(self, order: Order) -> bool:
        """given an aat Order, construct a coinbase order json"""
        jsn: Dict[str, Union[str, int, float]] = {}
        jsn["product_id"] = order.instrument.name

        if order.order_type == OrderType.LIMIT:
            jsn["type"] = "limit"
            jsn["side"] = order.side.value.lower()
            jsn["price"] = order.price
            jsn["size"] = round(order.volume, 8)

            # From the coinbase docs
            if order.flag == OrderFlag.FILL_OR_KILL:
                jsn["time_in_force"] = "FOK"
            elif order.flag == OrderFlag.IMMEDIATE_OR_CANCEL:
                jsn["time_in_force"] = "IOC"
            else:
                jsn["time_in_force"] = "GTC"

        elif order.order_type == OrderType.MARKET:
            jsn["type"] = "market"
            jsn["side"] = order.side.value.lower()
            jsn["size"] = round(order.volume, 8)

        else:
            stop_order: Order = order.stop_target  # type: ignore
            jsn["type"] = stop_order.side.value.lower()
            jsn["price"] = stop_order.price
            jsn["size"] = round(stop_order.volume, 8)

            if stop_order.side == Side.BUY:
                jsn["stop"] = "entry"
            else:
                jsn["stop"] = "loss"

            jsn["stop_price"] = order.price

            if stop_order.order_type == OrderType.LIMIT:
                jsn["type"] = "limit"
                if order.flag == OrderFlag.FILL_OR_KILL:
                    jsn["time_in_force"] = "FOK"
                elif order.flag == OrderFlag.IMMEDIATE_OR_CANCEL:
                    jsn["time_in_force"] = "IOC"
                else:
                    jsn["time_in_force"] = "GTC"

            elif stop_order.order_type == OrderType.MARKET:
                jsn["type"] = "market"

        # submit the order json
        id = self._newOrder(jsn)
        if id != "":
            # successful, set id on the order and return true
            order.id = str(id)
            self._order_map[order.id] = order
            return True

        # otherwise return false indicating rejected
        return False
Exemplo n.º 4
0
    def newOrder(self, order: Order):
        '''given an aat Order, construct a coinbase order json'''
        jsn: Dict[str, Union[str, int, float]] = {}
        jsn['product_id'] = order.instrument.name

        if order.order_type == OrderType.LIMIT:
            jsn['type'] = 'limit'
            jsn['side'] = order.side.value.lower()
            jsn['price'] = order.price
            jsn['size'] = round(order.volume, 8)

            # From the coinbase docs
            if order.flag == OrderFlag.FILL_OR_KILL:
                jsn['time_in_force'] = 'FOK'
            elif order.flag == OrderFlag.IMMEDIATE_OR_CANCEL:
                jsn['time_in_force'] = 'IOC'
            else:
                jsn['time_in_force'] = 'GTC'

        elif order.order_type == OrderType.MARKET:
            jsn['type'] = 'market'
            jsn['side'] = order.side.value.lower()
            jsn['size'] = round(order.volume, 8)

        else:
            stop_order: Order = order.stop_target  # type: ignore
            jsn['type'] = stop_order.side.value.lower()
            jsn['price'] = stop_order.price
            jsn['size'] = round(stop_order.volume, 8)

            if stop_order.side == Side.BUY:
                jsn['stop'] = 'entry'
            else:
                jsn['stop'] = 'loss'

            jsn['stop_price'] = order.price

            if stop_order.order_type == OrderType.LIMIT:
                jsn['type'] = 'limit'
                if order.flag == OrderFlag.FILL_OR_KILL:
                    jsn['time_in_force'] = 'FOK'
                elif order.flag == OrderFlag.IMMEDIATE_OR_CANCEL:
                    jsn['time_in_force'] = 'IOC'
                else:
                    jsn['time_in_force'] = 'GTC'

            elif stop_order.order_type == OrderType.MARKET:
                jsn['type'] = 'market'

        # submit the order json
        id = self._newOrder(jsn)
        if id != "":
            # successful, set id on the order and return true
            order.id = id
            self._order_map[id] = order
            return True

        # otherwise return false indicating rejected
        return False
Exemplo n.º 5
0
 async def onTrade(self, event: Event) -> None:
     pprint(event)
     trade: Trade = event.target  # type: ignore
     if self._trade and trade.my_order is None:
         await self.newOrder(
             Order(
                 1,
                 trade.price,
                 Side.BUY,
                 trade.instrument,
                 trade.exchange,
             ))
         self._trade = False
Exemplo n.º 6
0
 async def onTrade(self, event: Event) -> None:
     if self._count < 5:
         await self.newOrder(
             Order(
                 1,
                 10000000,
                 Side.SELL,
                 self.instruments()[0],
                 order_type=OrderType.LIMIT,
             ))
         self._count += 1
         assert len(self.orders()) == self._count
     else:
         await self.cancelAll()
         assert len(self.orders()) == 0
Exemplo n.º 7
0
    async def onTrade(self, event: Event) -> None:
        '''Called whenever a `Trade` event is received'''
        print('Trade:\n\t{}\n\tSlippage:{}\n\tTxnCost:{}'.format(event, event.target.slippage(), event.target.transactionCost()))

        # no past trades, no current orders
        if not self.orders(event.target.instrument) and not self.trades(event.target.instrument):
            # TODO await self.buy(...) ?
            req = Order(side=Side.BUY,
                        price=event.target.price + 10,
                        volume=1,
                        instrument=event.target.instrument,
                        order_type=Order.Types.MARKET,
                        exchange=event.target.exchange)
            print("requesting buy : {}".format(req))
            await self.newOrder(req)
Exemplo n.º 8
0
    async def onTrade(self, event: Event) -> None:
        '''Called whenever a `Trade` event is received'''
        trade: Trade = event.target  # type: ignore

        # no past trades, no current orders
        if not self.orders(trade.instrument) and not self.trades(trade.instrument):
            req = Order(side=Side.BUY,
                        price=trade.price,
                        volume=5000 // trade.price,
                        instrument=trade.instrument,
                        order_type=Order.Types.MARKET,
                        exchange=trade.exchange)

            print('requesting buy : {}'.format(req))

            await self.newOrder(req)
Exemplo n.º 9
0
    async def onTrade(self, event: Event) -> None:
        '''Called whenever a `Trade` event is received'''
        trade: Trade = event.target  # type: ignore
        if self.volume_ordered < self.target_volume:
            now = datetime.now()
            if now >= self.target_time and now < self.end_time:
                req = Order(side=Side.BUY,
                            price=trade.price + self.price_limit,
                            volume=self.step,
                            instrument=trade.instrument,
                            order_type=Order.Types.MARKET,
                            exchange=trade.exchange)

                print("requesting : {}".format(req))
                self.target_time += timedelta(seconds=self.delay)
                self.volume_ordered += self.step
                await self.newOrder(req)
            else:
                print("Waiting for time window")
        else:
            print("Orders launched, do nothing! vol: {}".format(self.volume_processed))
Exemplo n.º 10
0
    async def onTrade(self, event: Event) -> None:
        '''Called whenever a `Trade` event is received'''
        print('Trade:\n{}'.format(event))

        trade: Trade = event.target  # type: ignore

        # no past trades, no current orders
        if not self.orders(trade.instrument) and not self.trades(trade.instrument):
            req = Order(side=Side.BUY,
                        price=trade.price + 10,
                        volume=1,
                        instrument=trade.instrument,
                        order_type=Order.Types.MARKET,
                        exchange=trade.exchange)

            print("requesting buy : {}".format(req))

            await self.newOrder(req)

        else:
            print(self.positions())
            print(self.risk())
Exemplo n.º 11
0
    async def onTrade(self, event: Event):
        '''Called whenever a `Trade` event is received'''
        trade: Trade = event.target  # type: ignore

        # append prices
        self._long_ma_list.append(trade.price)
        self._short_ma_list.append(trade.price)

        # adding to list
        self._long_ma_list = self._long_ma_list[-self._long_ma:]
        self._short_ma_list = self._short_ma_list[-self._short_ma:]

        if len(self._long_ma_list) < self._long_ma:
            return

        # dont trade in first 15 minutes
        if self.now().hour == 9 and self.now().minute <= 45:
            return

        long_mvg_av = pd.Series(self._long_ma_list).rolling(
            self._long_ma, min_periods=self._long_ma).mean().iloc[-1]
        short_mvg_av = pd.Series(self._short_ma_list).rolling(
            self._short_ma, min_periods=self._short_ma).mean().iloc[-1]
        long_mvg_av = long_mvg_av + (long_mvg_av * .005)
        # States
        #
        # Not Entered, Not Triggered -> if long_ma > short_ma -> Not Entered, Triggered (Wait for short av to move above)
        # Not Entered, Not Triggered -> if long_ma <= short_ma -> Not Entered, Not Triggered (Not ready yet)
        # Not Entered, Triggered -> if long_ma > short_ma -> Not Entered, Triggered (Wait for short to move above)
        # Not Entered, Triggered -> if long_ma <= short_ma -> Entered, Triggered (BUY)
        # Entered, Triggered -> if long_ma > short_ma -> Not Entered, Triggered (SELL)
        # Entered, Triggered -> if long_ma <= short_ma -> Entered, Triggered (Wait for short to move below)
        if not self._entered:
            if not self._triggered:
                if long_mvg_av > short_mvg_av:
                    # Not Entered, Not Triggered -> if long_ma > short_ma -> Not Entered, Triggered (Wait for short av to move above)
                    self._triggered = True
                else:
                    # Not Entered, Not Triggered -> if long_ma <= short_ma -> Not Entered, Not Triggered (Not ready yet)
                    self._triggered = False
            else:
                if long_mvg_av > short_mvg_av:
                    # Not Entered, Triggered -> if long_ma > sh`ort_ma -> Not Entered, Triggered (Wait for short to move above)
                    pass
                else:
                    # Not Entered, Triggered -> if long_ma <= short_ma -> Entered, Triggered (BUY)
                    if not self.orders(trade.instrument):
                        self._volume = math.ceil(1000 / trade.price)
                        self._buy_order = Order(side=Side.BUY,
                                                price=trade.price,
                                                volume=self._volume,
                                                instrument=trade.instrument,
                                                order_type=Order.Types.MARKET,
                                                exchange=trade.exchange)
                        print('submitting buy order: {}'.format(
                            self._buy_order))
                        await self.newOrder(self._buy_order)
        else:
            if not self._triggered:
                raise Exception('Never in this state')
            else:
                if long_mvg_av > short_mvg_av:
                    # Entered, Triggered -> if long_ma > short_ma -> Not Entered, Triggered (SELL)
                    if not self._sell_order:
                        self._sell_order = Order(side=Side.SELL,
                                                 price=trade.price,
                                                 volume=self._volume,
                                                 instrument=trade.instrument,
                                                 order_type=Order.Types.MARKET,
                                                 exchange=trade.exchange)
                        print('submitting sell order: {}'.format(
                            self._sell_order))
                        await self.newOrder(self._sell_order)
                else:
                    # Entered, Triggered -> if long_ma <= short_ma -> Entered, Triggered (Wait for short to move below)

                    # exit if time to bail
                    if not self._sell_order and self.now(
                    ).hour == self._bail_hour and self.now(
                    ).minute >= self._bail_minute:
                        self._sell_order = Order(side=Side.SELL,
                                                 price=trade.price,
                                                 volume=self._volume,
                                                 instrument=trade.instrument,
                                                 order_type=Order.Types.MARKET,
                                                 exchange=trade.exchange)
                        print('submitting sell order: {}'.format(
                            self._sell_order))
                        await self.newOrder(self._sell_order)
Exemplo n.º 12
0
    async def websocket(self, subscriptions: List[Instrument]):  # type: ignore
        # copy the base subscription template
        subscription = _SUBSCRIPTION.copy()

        # for each subcription, add symbol to product_ids
        for sub in subscriptions:
            subscription["product_ids"].append(sub.brokerId)  # type: ignore

        # sign the message in a similar way to the rest api, but
        # using the message of GET/users/self/verify
        timestamp = str(time.time())
        message = timestamp + "GET/users/self/verify"
        hmac_key = base64.b64decode(self.secret_key)
        signature = hmac.new(hmac_key, message.encode(), hashlib.sha256)
        signature_b64 = base64.b64encode(signature.digest()).decode()

        # update the subscription message with the signing info
        subscription.update({
            "signature": signature_b64,
            "timestamp": timestamp,
            "key": self.api_key,
            "passphrase": self.passphrase,
        })

        # construct a new websocket session
        session = aiohttp.ClientSession()

        # connect to the websocket
        async with session.ws_connect(self.ws_url) as ws:
            # send the subscription
            await ws.send_str(json.dumps(subscription))

            # for each message returned
            async for msg in ws:
                # only handle text messages
                if msg.type == aiohttp.WSMsgType.TEXT:
                    # load the data as json
                    x = json.loads(msg.data)

                    # skip earlier messages that our order book
                    # already reflects
                    if "sequence" in x:
                        inst = Instrument(x["product_id"], InstrumentType.PAIR,
                                          self.exchange)
                        if x.get("sequence", float("inf")) < self.seqnum.get(
                                inst, 0):
                            # if msg has a sequence number, and that number is < the last sequence number
                            # of the order book snapshot, ignore
                            continue

                    # ignore subscription  and heartbeat messages
                    if x["type"] in ("subscriptions", "heartbeat"):
                        # TODO yield heartbeats?
                        continue

                    elif x["type"] == "received":
                        # generate new Open events
                        # A valid order has been received and is now active.
                        # This message is emitted for every single valid order as
                        # soon as the matching engine receives it whether it fills
                        # immediately or not.
                        #
                        # The received message does not indicate a resting order on
                        # the order book. It simply indicates a new incoming order
                        # which as been accepted by the matching engine for processing.
                        # Received orders may cause match message to follow if they
                        # are able to begin being filled (taker behavior). Self-trade
                        # prevention may also trigger change messages to follow if the
                        # order size needs to be adjusted. Orders which are not fully
                        # filled or canceled due to self-trade prevention result in an
                        # open message and become resting orders on the order book.
                        #
                        # Market orders (indicated by the order_type field) may have
                        # an optional funds field which indicates how much quote currency
                        # will be used to buy or sell. For example, a funds field of
                        # 100.00 for the BTC-USD product would indicate a purchase of
                        # up to 100.00 USD worth of bitcoin.
                        #
                        # {
                        #     "type": "received",
                        #     "time": "2014-11-07T08:19:27.028459Z",
                        #     "product_id": "BTC-USD",
                        #     "sequence": 10,
                        #     "order_id": "d50ec984-77a8-460a-b958-66f114b0de9b",
                        #     "size": "1.34",
                        #     "price": "502.1",
                        #     "side": "buy",
                        #     "order_type": "limit"
                        # }
                        # {
                        #     "type": "received",
                        #     "time": "2014-11-09T08:19:27.028459Z",
                        #     "product_id": "BTC-USD",
                        #     "sequence": 12,
                        #     "order_id": "dddec984-77a8-460a-b958-66f114b0de9b",
                        #     "funds": "3000.234",
                        #     "side": "buy",
                        #     "order_type": "market"
                        # }
                        id = x["order_id"]

                        # FIXME make sure we dont need this
                        # if id in self._order_map:
                        #     # yield a received event and get order from dict
                        #     o = self._order_map[id]
                        #     yield Event(type=EventType.RECEIVED, target=o)

                        if x["order_type"] == "market":
                            if "size" in x and float(x["size"]) <= 0:
                                # ignore zero size orders
                                # TODO why do we even get these?
                                continue
                            elif "size" not in x and "funds" in x:
                                print("TODO: funds")
                                # TODO can't handle these yet, no mapping from funds to size/price
                                continue

                            # create a market data order from the event data
                            # TODO set something for price? float('inf') ?
                            o = Order(
                                float(x["size"]),
                                0.0,
                                Side(x["side"].upper()),
                                Instrument(x["product_id"],
                                           InstrumentType.PAIR, self.exchange),
                                self.exchange,
                                id=id,
                            )

                        else:
                            # create limit order from the event data
                            o = Order(
                                float(x["size"]),
                                float(x["price"]),
                                Side(x["side"].upper()),
                                Instrument(x["product_id"],
                                           InstrumentType.PAIR, self.exchange),
                                self.exchange,
                            )

                        # yield an open event for the new order
                        e = Event(type=EventType.OPEN, target=o)
                        yield e

                    elif x["type"] == "done":
                        # The order is no longer on the order book. Sent for
                        # all orders for which there was a received message.
                        # This message can result from an order being canceled
                        # or filled. There will be no more messages for this
                        # order_id after a done message. remaining_size indicates
                        # how much of the order went unfilled; this will
                        # be 0 for filled orders.
                        #
                        # market orders will not have a remaining_size or price
                        # field as they are never on the open order book at a
                        # given price.
                        #
                        # {
                        #     "type": "done",
                        #     "time": "2014-11-07T08:19:27.028459Z",
                        #     "product_id": "BTC-USD",
                        #     "sequence": 10,
                        #     "price": "200.2",
                        #     "order_id": "d50ec984-77a8-460a-b958-66f114b0de9b",
                        #     "reason": "filled", // or "canceled"
                        #     "side": "sell",
                        #     "remaining_size": "0"
                        # }
                        if x["reason"] == "canceled":
                            id = x["order_id"]
                            # if cancelled
                            if "price" not in x:
                                # cancel this event if we have a full local order book
                                # where we can determine the original order
                                print("TODO: noprice")
                                continue

                            # FIXME don't use remaining_size, lookup original size in order book
                            o = Order(
                                float(x["remaining_size"]),
                                float(x["price"]),
                                Side(x["side"].upper()),
                                Instrument(
                                    x["product_id"],
                                    InstrumentType.PAIR,
                                    self.exchange,
                                ),
                                self.exchange,
                                id=id,
                            )

                            e = Event(type=EventType.CANCEL, target=o)
                            yield e

                        elif x["reason"] == "filled":
                            # Will have a match event
                            # TODO route these to full local order book
                            continue

                        else:
                            # TODO unhandled
                            # this should never print
                            print("TODO: unhandled", x)

                    elif x["type"] == "match":
                        # A trade occurred between two orders. The aggressor
                        # or taker order is the one executing immediately
                        # after being received and the maker order is a
                        # resting order on the book. The side field indicates
                        # the maker order side. If the side is sell this
                        # indicates the maker was a sell order and the match
                        # is considered an up-tick. A buy side match is a down-tick.
                        #
                        # If authenticated, and you were the taker, the message
                        # would also have the following fields:
                        # taker_user_id: "5844eceecf7e803e259d0365",
                        # user_id: "5844eceecf7e803e259d0365",
                        # taker_profile_id: "765d1549-9660-4be2-97d4-fa2d65fa3352",
                        # profile_id: "765d1549-9660-4be2-97d4-fa2d65fa3352",
                        # taker_fee_rate: "0.005"
                        #
                        # Similarly, if you were the maker, the message would have the following:
                        # maker_user_id: "5f8a07f17b7a102330be40a3",
                        # user_id: "5f8a07f17b7a102330be40a3",
                        # maker_profile_id: "7aa6b75c-0ff1-11eb-adc1-0242ac120002",
                        # profile_id: "7aa6b75c-0ff1-11eb-adc1-0242ac120002",
                        # maker_fee_rate: "0.001"
                        # {
                        #     "type": "match",
                        #     "trade_id": 10,
                        #     "sequence": 50,
                        #     "maker_order_id": "ac928c66-ca53-498f-9c13-a110027a60e8",
                        #     "taker_order_id": "132fb6ae-456b-4654-b4e0-d681ac05cea1",
                        #     "time": "2014-11-07T08:19:27.028459Z",
                        #     "product_id": "BTC-USD",
                        #     "size": "5.23512",
                        #     "price": "400.23",
                        #     "side": "sell"
                        # }

                        # Generate a trade event
                        # First, create an order from the event
                        if str(x.get("taker_order_id", "")) in self._order_map:
                            o = self._order_map[str(x.get("taker_order_id"))]

                            o.filled = float(x["size"])

                            # my order
                            mine = True

                        elif str(x.get("maker_order_id",
                                       "")) in self._order_map:
                            o = self._order_map[str(x.get("maker_order_id"))]
                            # TODO filled?

                            # my order
                            mine = True

                        else:
                            o = Order(
                                float(x["size"]),
                                float(x["price"]),
                                Side(x["side"].upper()),
                                Instrument(x["product_id"],
                                           InstrumentType.PAIR, self.exchange),
                                self.exchange,
                            )

                            # set filled to volume so we see it as "done"
                            o.filled = o.volume

                            # not my order
                            mine = False

                        # create a trader with this order as the taker
                        # makers would be accumulated via the
                        # `elif x['reason'] == 'filled'` block above
                        t = Trade(
                            float(x["size"]),
                            float(x["price"]),
                            taker_order=o,
                            maker_orders=[],
                        )

                        if mine:
                            t.my_order = o

                        e = Event(type=EventType.TRADE, target=t)
                        yield e

                    elif x["type"] == "open":
                        # The order is now open on the order book.
                        # This message will only be sent for orders
                        # which are not fully filled immediately.
                        # remaining_size will indicate how much of
                        # the order is unfilled and going on the book.
                        # {
                        #     "type": "open",
                        #     "time": "2014-11-07T08:19:27.028459Z",
                        #     "product_id": "BTC-USD",
                        #     "sequence": 10,
                        #     "order_id": "d50ec984-77a8-460a-b958-66f114b0de9b",
                        #     "price": "200.2",
                        #     "remaining_size": "1.00",
                        #     "side": "sell"
                        # }
                        # TODO how are these differentiated from received?
                        o = Order(
                            float(x["remaining_size"]),
                            float(x["price"]),
                            Side(x["side"].upper()),
                            Instrument(x["product_id"], InstrumentType.PAIR,
                                       self.exchange),
                            self.exchange,
                        )

                        e = Event(type=EventType.OPEN, target=o)
                        yield e
                    elif x["type"] == "change":
                        # TODO
                        # An order has changed. This is the result
                        # of self-trade prevention adjusting the
                        # order size or available funds. Orders can
                        # only decrease in size or funds. change
                        # messages are sent anytime an order changes
                        # in size; this includes resting orders (open)
                        # as well as received but not yet open.
                        # change messages are also sent when a new
                        # market order goes through self trade prevention
                        # and the funds for the market order have changed.
                        # {
                        #     "type": "change",
                        #     "time": "2014-11-07T08:19:27.028459Z",
                        #     "sequence": 80,
                        #     "order_id": "ac928c66-ca53-498f-9c13-a110027a60e8",
                        #     "product_id": "BTC-USD",
                        #     "new_size": "5.23512",
                        #     "old_size": "12.234412",
                        #     "price": "400.23",
                        #     "side": "sell"
                        # }
                        # {
                        #     "type": "change",
                        #     "time": "2014-11-07T08:19:27.028459Z",
                        #     "sequence": 80,
                        #     "order_id": "ac928c66-ca53-498f-9c13-a110027a60e8",
                        #     "product_id": "BTC-USD",
                        #     "new_funds": "5.23512",
                        #     "old_funds": "12.234412",
                        #     "price": "400.23",
                        #     "side": "sell"
                        # }
                        print("TODO: change", x)
                    else:
                        # TODO unhandled
                        # this should never print
                        print("TODO: unhandled2", x)
Exemplo n.º 13
0
    async def onTrade(self, event: Event) -> None:
        """Called whenever a `Trade` event is received"""
        trade: Trade = event.target  # type: ignore

        # set initial price to first trade
        if (self._initial_price is None or self._initial_price == 0.0
                or self._initial_price_day != trade.timestamp.day):
            self._initial_price = trade.price
            self._initial_price_day = trade.timestamp.day  # type: ignore

        # self last price to last traded price
        self._last_price = trade.price

        # determine if we trade
        cur_perf = ((self._last_price - self._initial_price) /
                    self._initial_price  # type: ignore
                    if self._initial_price != 0.0 else None)

        if cur_perf is None:
            # don't handle 0 price
            return

        if self._enter_trade is not None and self._exit_order is None:
            # already traded, look for exit
            if cur_perf > self._profit:
                # close position with sell
                print("exiting with sell order")
                self._exit_order = Order(
                    side=Side.SELL,
                    price=trade.price,
                    volume=self._quantity,
                    instrument=trade.instrument,
                    order_type=Order.Types.MARKET,
                    exchange=trade.exchange,
                )
                await self.newOrder(self._exit_order)

            elif cur_perf < self._stop:
                # close to stop
                print("exiting to stop loss")
                side = (
                    Side.SELL if self._enter_order.side == Side.BUY else
                    Side.BUY  # type: ignore
                )
                self._exit_order = Order(
                    side=side,
                    price=trade.price,
                    volume=self._quantity,
                    instrument=trade.instrument,
                    order_type=Order.Types.MARKET,
                    exchange=trade.exchange,
                )
                await self.newOrder(self._exit_order)

            else:
                # check if 15:45 or later
                if (trade.timestamp.hour >= self._exit_hour
                        and trade.timestamp.minute >= self._exit_minute):
                    # exit
                    print("exiting at EOD")
                    self._exit_order = Order(
                        side=Side.SELL,
                        price=trade.price,
                        volume=self._quantity,
                        instrument=trade.instrument,
                        order_type=Order.Types.MARKET,
                        exchange=trade.exchange,
                    )
                    await self.newOrder(self._exit_order)

        elif self._enter_trade is not None and self._exit_order is not None:
            # already ordered, wait for execution
            return

        elif self._enter_trade is None and self._exit_order is not None:
            raise Exception("Inconsistent state machine")

        else:
            if (trade.timestamp.hour >= self._exit_hour
                    and trade.timestamp.minute > self._exit_minute):
                # no more trading today
                return

            if (trade.timestamp.hour <= self._enter_hour
                    and trade.timestamp.minute < self._enter_minute):
                # no trading yet
                return

            if self._enter_order is not None:
                # already trying to enter, wait for execution
                return

            if cur_perf > self._trigger:
                # buy
                print("entering with buy order")
                if self._notional is not None:
                    self._quantity = max(self._notional // trade.price, 1)
                else:
                    self._quantity = 1  # type: ignore

                self._enter_order = Order(
                    side=Side.BUY,
                    price=trade.price,
                    volume=self._quantity,
                    instrument=trade.instrument,
                    order_type=Order.Types.MARKET,
                    exchange=trade.exchange,
                )
                await self.newOrder(self._enter_order)