RussianInvestments / invest-python

Invest Python gRPC client
https://russianinvestments.github.io/invest-python/
Apache License 2.0
66 stars 21 forks source link

[Feature] Order_Id в PostStopOrder #31

Open aidonchuk opened 5 months ago

aidonchuk commented 5 months ago

Описание

Скажите пожалуйста, правильно ли я понимаю что если назначить в это поле айдишник, то после срабатывания триггера в вебсокете я увижу простой ордер с моим uuid?

Если да, то сделайте плз эту функцию, очень помогла бы в работе.

Желаемое решение

No response

Дополнительно

No response

miko1ann commented 4 months ago

Необходима возможность выяснить идентификатор дочерней заявки которая создаётся трейлингом. В апихе вижу только stop_order_id - Уникальный идентификатор стоп-заявки и order_request_id - Идентификатор ключа идемпотентности, переданный клиентом. задача выяснить исполнился ли стоп-ордер и по какой цене. есть предположение что order_request_id - станет order_id для новой заявки?

Получается что в текущей реализации невозможно нормально(без костылей) работать со стоп ордерами.

Второй вопрос. Может есть воркераунд для создания стопа с request_id?

miko1ann commented 4 months ago

Описание

Скажите пожалуйста, правильно ли я понимаю что если назначить в это поле айдишник, то после срабатывания триггера в вебсокете я увижу простой ордер с моим uuid?

Если да, то сделайте плз эту функцию, очень помогла бы в работе.

Желаемое решение

No response

Дополнительно

No response

Вроде как в реквесте даже есть поле

@dataclass(eq=False, repr=True)
class PostStopOrderRequest(_grpc_helpers.Message):
  figi: Optional[str] = _grpc_helpers.string_field(1)
  quantity: int = _grpc_helpers.int64_field(2)
  price: Optional["Quotation"] = _grpc_helpers.message_field(3)
  stop_price: Optional["Quotation"] = _grpc_helpers.message_field(4)
  direction: "StopOrderDirection" = _grpc_helpers.enum_field(5)
  account_id: str = _grpc_helpers.string_field(6)
  expiration_type: "StopOrderExpirationType" = _grpc_helpers.enum_field(7)
  stop_order_type: "StopOrderType" = _grpc_helpers.enum_field(8)
  expire_date: Optional[datetime] = _grpc_helpers.message_field(9)
  instrument_id: str = _grpc_helpers.string_field(10)
  exchange_order_type: "ExchangeOrderType" = _grpc_helpers.message_field(11)
  take_profit_type: "TakeProfitType" = _grpc_helpers.message_field(12)
  trailing_data: "PostStopOrderRequestTrailingData" = _grpc_helpers.message_field(13)
  price_type: "PriceType" = _grpc_helpers.message_field(14)
  **order_id**: str = _grpc_helpers.string_field(15)

Я предполагаю что если пропачить async_services -> StopOrdersService и дописать туда request_id должно заехать.

Попробую, отпишусь по итогу.

miko1ann commented 4 months ago

В общем завелось. Но надо заинжектиться - костыль

Делаем копию StopOrdersService

.... 
class StopOrdersServicePatched(_grpc_helpers.Service):
    _stub_factory = stoporders_pb2_grpc.StopOrdersServiceStub

    @handle_aio_request_error("PostStopOrder")
    async def post_stop_order_patched(

.... 

        request.instrument_id = instrument_id
        request.quantity = quantity
       -> if order_id:
       ->     request.order_id = order_id
        if price is not None:
            request.price = price
....

Дальше патчим сервис

class AsyncClientPatched(AsyncClient):

    async def __aenter__(self) -> AsyncServices:
        channel = await self._channel.__aenter__()
        async_services = AsyncServices(
            channel,
            token=self._token,
            sandbox_token=self._sandbox_token,
            app_name=self._app_name,
        )
        # todo: Rollback me after implementing changes: https://github.com/RussianInvestments/invest-python/issues/31
       -> metadata = get_metadata(self._token, self._app_name,)
       -> async_services.stop_orders_patched = StopOrdersServicePatched(channel, metadata)

        return async_services

Иимплементим патч

class TinkoffClient:
    """
    Wrapper for tinkoff.invest.AsyncClient.
    Takes responsibility for choosing correct function to call basing on sandbox mode flag.
    """

    def __init__(self, token: str, sandbox: bool = False):
        self.token = token
        self.sandbox = sandbox
        self.client: Optional[AsyncServices] = None
        self.sync_client: Optional[Services] = None
        self.market_data_cache: Optional[MarketDataCache] = None

    async def ainit(self, market_data_cache_path: str = "market_data_cache"):
        # todo: Rollback me after implementing changes: https://github.com/RussianInvestments/invest-python/issues/31
         ->  self.client = await AsyncClientPatched(token=self.token, app_name=settings.app_name).__aenter__()

        if settings.use_candle_history_cache:
            self.sync_client = Client(token=self.token, app_name=settings.app_name).__enter__()

            self.market_data_cache = MarketDataCache(
                settings=MarketDataCacheSettings(base_cache_dir=Path(market_data_cache_path)),
                services=self.sync_client,
            )

Готово Теперь можно использовать

...
 # stop_orders
    async def post_stop_order(self, **kwargs) -> PostStopOrderResponse:
        #    запроса — PostStopOrderReques t
        if self.sandbox:
            return await self.client.sandbox.post_stop_order(**kwargs)
        return await self.client.stop_orders.post_stop_order(**kwargs)

    # todo: Rollback me after implementing changes: https://github.com/RussianInvestments/invest-python/issues/31
    ->async def post_stop_order_patched(self, **kwargs) -> PostStopOrderResponse:
   ->     #    запроса — PostStopOrderReques t
   ->     if self.sandbox:
   ->        return await self.client.sandbox.post_stop_order(**kwargs)
   ->     return await self.client.stop_orders_patched.post_stop_order_patched(**kwargs)

...

В общем поковырявшись мне не удалось получить то что нужно.

По заявкам могу смотреть только те что активные. OrderState - по request_id(который передавал в stop_order) - не удалось получить дочернюю заявку по order_id.

По стоп ордерам другая штука - там нет привязки к выставленным дочерним заявкам. То есть по итогу трейлинг стопа не возможно понять была-ли сделка по инструменту, можно понять только был ли завершёy трейлинг стоп.

Апиха годится для простых операций вроде выставить заявку и всё, какую-то вменяемую логику запилить трудно без костылей если вообще возможно. жаль, ждём апдейта...

miko1ann commented 4 months ago

В общем поковырявшись мне не удалось. получить то что нужно.

По заявкам могу смотреть только те что активные. OrderState - по request_id - не удалось получить дочернюю заявку по order_id.