예제 #1
0
async def follow_strategy(pilot_name: str, passenger: AutoPilotTask,
                          etrade: AsyncEtrade, quote: dict):
    last = get_last(quote)
    bid = get_bid(quote)
    ask = get_ask(quote)

    if bid < passenger.loss_price or ask > passenger.profit_price or last < passenger.pullback_price:
        if bid < passenger.loss_price:
            logger.info(
                "%s %s bid reached the loss price, placing sell order at %s",
                PREFIX, pilot_name, ask)
        elif ask > passenger.profit_price:
            logger.info(
                "%s %s ask reached the profit price, placing sell order at %s",
                PREFIX, pilot_name, ask)
        else:
            logger.info(
                "%s %s last price reached the pullback price, placing sell order at %s",
                PREFIX, pilot_name, ask)

        order_id = await place_sell_order(passenger, ask, etrade)

        passenger.state = AutoPilotTask.SELLING
        passenger.tracking_order_id = order_id
예제 #2
0
async def sell_position(pilot_name: str, passenger: AutoPilotTask,
                        etrade: AsyncEtrade):
    if not passenger.tracking_order_id:
        quote = await etrade.get_quote(passenger.symbol)
        ask = get_ask(quote)
        await commit_sell(passenger, ask, etrade)
        return

    order = await etrade.get_order_details(passenger.account.account_key,
                                           passenger.tracking_order_id,
                                           passenger.symbol)
    if not order:
        logger.info("%s %s unable to track sell order %s. NOT FOUND.", PREFIX,
                    pilot_name, passenger.tracking_order_id)

        passenger.status = AutoPilotTask.PAUSED
        passenger.state = AutoPilotTask.ERROR
        passenger.error_message = f'unable to track selling order {passenger.tracking_order_id}. NOT FOUND'
        return

    details = order.get("OrderDetail")[0]
    status = OrderStatus(details.get("status"))
    limit_price = details.get("limitPrice")

    if status in (OrderStatus.OPEN, OrderStatus.PARTIAL):
        quote = await etrade.get_quote(passenger.symbol)
        bid = get_bid(quote)
        ask = get_ask(quote)
        placed_at = datetime.utcfromtimestamp(
            details.get("placedTime") / 1000).replace(tzinfo=pytz.utc)
        elapsed_time = timezone.now() - placed_at
        if elapsed_time >= timedelta(seconds=5) and (limit_price < bid
                                                     or limit_price > ask):
            try:
                await etrade.cancel_order(passenger.account.account_key,
                                          passenger.tracking_order_id)
            except ServiceError as e:
                if e.error_code == 5001:
                    # This order is currently being executed
                    # or rejected. It cannot be cancelled.
                    return

    elif status == OrderStatus.CANCEL_REQUESTED:
        logger.debug(
            "%s %s cancel for order %s has been requested. waiting...", PREFIX,
            pilot_name, passenger.tracking_order_id)

    elif status == OrderStatus.CANCELLED:
        logger.debug("%s %s order %s cancelled. placing new sell order...",
                     PREFIX, pilot_name, passenger.tracking_order_id)
        instrument = details.get("Instrument")[0]
        ordered_quantity = instrument.get("orderedQuantity")
        filled_quantity = instrument.get("filledQuantity") or 0
        pending_quantity = int(ordered_quantity) - int(filled_quantity)
        passenger.quantity = pending_quantity

        quote = await etrade.get_quote(passenger.symbol)
        ask = get_ask(quote)
        await commit_sell(passenger, ask, etrade)

    elif status == OrderStatus.REJECTED:
        logger.warning(
            "%s %s order %s rejected. this case is not being handled.", PREFIX,
            pilot_name, passenger.tracking_order_id)

    elif status == OrderStatus.EXPIRED:
        logger.warning(
            "%s %s order %s expired. this case is not being handled.", PREFIX,
            pilot_name, passenger.tracking_order_id)

    elif status == OrderStatus.EXECUTED:
        # get possition
        quantity = await etrade.get_position_quantity(
            passenger.account.account_key, passenger.symbol)
        if quantity in (None, 0):
            instrument = details.get("Instrument")[0]
            avg_execution_price = Decimal(
                instrument.get("averageExecutionPrice"))
            passenger.status = AutoPilotTask.DONE
            passenger.exit_price = avg_execution_price

            percent = passenger.exit_percent
            percent_label = "profit" if percent > Decimal(0) else "loss"
            logger.info("%s %s position sold for a %s%% %s", PREFIX,
                        pilot_name, percent, percent_label)
            if passenger.discord_webhook:
                await post_webhook(
                    passenger.discord_webhook,
                    f"{passenger.symbol} position sold for a {percent}% {percent_label}"
                )
        else:
            # TODO: place sell order for scale out quantity
            passenger.quantity = quantity

            quote = await etrade.get_quote(passenger.symbol)
            ask = get_ask(quote)
            await commit_sell(passenger, ask, etrade)
    else:
        logger.error("%s %s unhandled status %s for order %s", PREFIX,
                     pilot_name, status, passenger.tracking_order_id)
        passenger.status = AutoPilotTask.PAUSED
        passenger.state = AutoPilotTask.ERROR
        passenger.error_message = f'unhandled status {status} for order {passenger.tracking_order_id}'
예제 #3
0
async def commit_sell(passenger: AutoPilotTask, sell_price: Decimal,
                      etrade: AsyncEtrade):
    order_id = await place_sell_order(passenger, sell_price, etrade)

    passenger.state = AutoPilotTask.SELLING
    passenger.tracking_order_id = order_id