async def green_light(pilot_name: str, passenger: AutoPilotTask, etrade: AsyncEtrade) -> bool: # handle override signal if passenger.signal == AutoPilotTask.MANUAL_OVERRIDE: logger.info("%s %s received signal MANUAL_OVERRIDE.", PREFIX, pilot_name) logger.info("%s %s releasing control...", PREFIX, pilot_name) passenger.status = AutoPilotTask.DONE return False # if no market session, sleep until market opens if MarketSession.current(passenger.is_otc) is None: logger.debug("%s %s market is closed. sleeping 1h", PREFIX, pilot_name) time_till_open = time_till_market_open(passenger.is_otc) await asyncio.sleep(time_till_open) return False # if no access token, sleep 1s (repeat until valid access) if not etrade.is_session_active(): logger.debug("%s %s waiting for valid %s session...", PREFIX, pilot_name, etrade.name) await asyncio.sleep(1) return False return True
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}'