Ejemplo n.º 1
0
async def minimize_loss(pilot_name: str, passenger: AutoPilotTask,
                        etrade: AsyncEtrade, quote: dict):
    await follow_strategy(pilot_name, passenger, etrade, quote)

    if passenger.state == AutoPilotTask.SELLING:
        return

    bid = get_bid(quote)
    ref_price_thresdhold = passenger.loss_ref_price + passenger.loss_amount * 2
    if bid > ref_price_thresdhold:
        passenger.loss_ref_price = passenger.loss_ref_price + passenger.loss_amount
Ejemplo n.º 2
0
    def mutate_and_get_payload(cls,
                               root,
                               info,
                               symbol,
                               provider_id,
                               account_id=None):
        account = Account.objects.get(id=account_id) if account_id else None
        provider = ServiceProvider.objects.select_related('session') \
            .get(id=provider_id)
        account_key = account.account_key.strip() if account \
            else provider.account_key.strip()

        if not account_key:
            return StopLoss(
                error=StopLossError.ACCOUNT_NOT_PROVIDED,
                error_message=
                'Either specify an accountId that has a valid accountKey ' +
                'or configure a default accountKey on the provider.')

        autopilot = get_autopilot(info.context.user.id, symbol)
        if autopilot:
            return StopLoss(error=StopLossError.NOT_ALLOWED_ON_AUTOPILOT,
                            error_message='You must turn off autopilot first.')

        etrade = get_provider_instance(provider)
        position_quantity = etrade.get_position_quantity(account_key, symbol)
        quote = etrade.get_quote(symbol)
        last_price = get_bid(quote)
        stop_price = get_round_price(last_price -
                                     (last_price * Decimal('0.02')))
        limit_price = get_limit_price(OrderAction.SELL,
                                      stop_price,
                                      margin=Decimal('0.02'))

        order_params = {
            'account_key': account_key,
            'market_session': MarketSession.current().value,
            'action': OrderAction.SELL.value,
            'symbol': symbol,
            'price_type': PriceType.STOP_LIMIT.value,
            'quantity': position_quantity,
            'stop_price': stop_price,
            'limit_price': limit_price
        }

        preview_ids = etrade.preview_order(
            order_client_id=get_random_string(length=20), **order_params)
        etrade.place_order(order_client_id=get_random_string(length=20),
                           preview_ids=preview_ids,
                           **order_params)

        return StopLoss()
Ejemplo n.º 3
0
    def resolve_quote(self, info, symbol):
        etrade = get_provider_instance(self)
        quote_data = etrade.get_quote(symbol)

        if not quote_data:
            return None

        return QuoteType(
            volume=get_volume(quote_data),
            last=get_last(quote_data),
            bid=get_bid(quote_data),
            ask=get_ask(quote_data),
            last_trade_direction=Decimal(
                str(quote_data.get('All').get('dirLast'))),
            market_cap=Decimal(str(quote_data.get('All').get('marketCap'))),
            shares_outstanding=quote_data.get('All').get('sharesOutstanding'),
            primary_exchange=quote_data.get('All').get('primaryExchange'),
            company_name=quote_data.get('All').get('companyName'),
        )
Ejemplo n.º 4
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
Ejemplo n.º 5
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}'
Ejemplo n.º 6
0
    def mutate_and_get_payload(cls,
                               root,
                               info,
                               symbol,
                               strategy_id,
                               provider_id,
                               margin='0.00',
                               price='0.0000',
                               quantity=0,
                               account_id=None,
                               autopilot=False):
        strategy = TradingStrategy.objects.get(id=strategy_id)
        provider = ServiceProvider.objects.select_related('session') \
            .get(id=provider_id)
        account = Account.objects.get(id=account_id) if account_id else None
        account_key = account.account_key.strip() if account \
            else provider.account_key.strip()

        if not account_key:
            return BuyStock(
                error=BuyStockError.ACCOUNT_NOT_PROVIDED,
                error_message=
                'Either specify an accountId that has a valid accountKey ' +
                'or configure a default accountKey on the provider.')

        if not account:
            account = Account.objects.get(account_key=account_key)

        if not strategy.funded(account):
            return BuyStock(
                error=BuyStockError.INSUFFICIENT_FUNDS,
                error_message='Insufficient funds. Strategy selected ' +
                'requires more cash available for investment.')

        etrade = get_provider_instance(provider)

        if autopilot:
            quote = etrade.get_quote(symbol)
            is_otc = etrade.is_otc(quote)
            user = info.context.user
            settings = Settings.objects.filter(user_id=user.id).first()
            default_modifier = settings.default_autopilot_modifier if settings else None
            discord_webhook = settings.discord_webhook if settings else None
            task = AutoPilotTask(signal=AutoPilotTask.BUY,
                                 user=info.context.user,
                                 strategy=strategy,
                                 provider=provider,
                                 account=account,
                                 is_otc=is_otc,
                                 symbol=symbol,
                                 quantity=quantity,
                                 entry_price=price,
                                 base_price=price,
                                 loss_ref_price=price,
                                 profit_ref_price=price,
                                 ref_time=timezone.now(),
                                 modifier=default_modifier,
                                 discord_webhook=discord_webhook)
            task.save()
            return BuyStock()

        if Decimal(price):
            limit_price = Decimal(price).quantize(Decimal('0.0001'))
        else:
            quantized_margin = Decimal(margin).quantize(Decimal('0.001'))
            quote = etrade.get_quote(symbol)
            limit_price = get_limit_price(
                OrderAction.BUY, get_bid(quote),
                Decimal(quantized_margin) or strategy.price_margin)

        if not quantity:
            quantity = strategy.get_quantity_for(
                buying_power=account.real_value, price_per_share=limit_price)

        order_params = {
            'account_key': account_key,
            'market_session': MarketSession.current().value,
            'action': OrderAction.BUY.value,
            'symbol': symbol,
            'price_type': PriceType.LIMIT.value,
            'quantity': quantity,
            'limit_price': limit_price
        }

        preview_ids = etrade.preview_order(
            order_client_id=get_random_string(length=20), **order_params)
        etrade.place_order(order_client_id=get_random_string(length=20),
                           preview_ids=preview_ids,
                           **order_params)

        # TODO: HANDLE Code: 1527. Message: Opening orders for this security cannot be accepted online at this time. For assistance with placing this order, please contact Customer Service at 1-800-ETRADE-1 (1-800-387-2331).

        return BuyStock()