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
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()
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'), )
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
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}'
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()