Open aidonchuk opened 5 months ago
Необходима возможность выяснить идентификатор дочерней заявки которая создаётся трейлингом. В апихе вижу только stop_order_id - Уникальный идентификатор стоп-заявки и order_request_id - Идентификатор ключа идемпотентности, переданный клиентом. задача выяснить исполнился ли стоп-ордер и по какой цене. есть предположение что order_request_id - станет order_id для новой заявки?
Получается что в текущей реализации невозможно нормально(без костылей) работать со стоп ордерами.
Второй вопрос. Может есть воркераунд для создания стопа с request_id?
Описание
Скажите пожалуйста, правильно ли я понимаю что если назначить в это поле айдишник, то после срабатывания триггера в вебсокете я увижу простой ордер с моим 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 должно заехать.
Попробую, отпишусь по итогу.
В общем завелось. Но надо заинжектиться - костыль
Делаем копию 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 трейлинг стоп.
Апиха годится для простых операций вроде выставить заявку и всё, какую-то вменяемую логику запилить трудно без костылей если вообще возможно. жаль, ждём апдейта...
В общем поковырявшись мне не удалось. получить то что нужно.
По заявкам могу смотреть только те что активные. OrderState - по request_id - не удалось получить дочернюю заявку по order_id.
Описание
Скажите пожалуйста, правильно ли я понимаю что если назначить в это поле айдишник, то после срабатывания триггера в вебсокете я увижу простой ордер с моим uuid?
Если да, то сделайте плз эту функцию, очень помогла бы в работе.
Желаемое решение
No response
Дополнительно
No response