Skip to content

Commit 5a63242

Browse files
committed
[Exchanges] support builtin dedicated stop order endpoints
1 parent 43bf7da commit 5a63242

File tree

3 files changed

+44
-23
lines changed

3 files changed

+44
-23
lines changed

octobot_trading/exchanges/connectors/ccxt/ccxt_connector.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,8 @@ async def _edit_order_by_cancel_and_create(
809809
self, exchange_order_id: str, symbol: str, order_type: enums.TraderOrderType,
810810
side: str, quantity: float, price: float, params: dict
811811
) -> dict:
812-
await self.client.cancel_order(exchange_order_id, symbol)
812+
extended_params = self.exchange_manager.exchange.order_request_kwargs_factory(exchange_order_id, order_type, **(params or {}))
813+
await self.client.cancel_order(exchange_order_id, symbol=symbol, params=extended_params)
813814
price_to_use = price
814815
local_params = copy.copy(params)
815816
ccxt_order_type = self.get_ccxt_order_type(order_type)
@@ -857,7 +858,7 @@ async def cancel_order(
857858
try:
858859
# make sure order is canceled
859860
cancelled_order = await self.exchange_manager.exchange.get_order(
860-
exchange_order_id, symbol=symbol
861+
exchange_order_id, symbol=symbol, order_type=order_type
861862
)
862863
if cancelled_order is None or personal_data.parse_is_cancelled(cancelled_order):
863864
return enums.OrderStatus.CANCELED

octobot_trading/exchanges/connectors/ccxt/enums.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,7 @@ class ExchangeColumns(enum.Enum):
153153
class ExchangeMarginTypes(enum.Enum):
154154
ISOLATED = "isolated"
155155
CROSS = "cross"
156+
157+
158+
class OrderFetchParams(enum.Enum):
159+
STOP = "stop" # bool: when true, is about a stop order

octobot_trading/exchanges/types/rest_exchange.py

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,23 @@
4141
import octobot_trading.personal_data.orders as orders
4242

4343

44+
45+
def fetching_orders_request(f):
46+
async def fetching_orders_request_wrapper(self, symbol: str = None, since: int = None, limit: int = None, **kwargs: dict) -> list:
47+
fetched_orders = await f(self, symbol=symbol, since=since, limit=limit, **kwargs)
48+
if ccxt_enums.OrderFetchParams.STOP.value not in kwargs and self.fetch_stop_order_in_different_request(symbol):
49+
# all order types need to be fetched and stop orders need to be fetched in a separate request
50+
stop_orders = await f(self, symbol=symbol, since=since, limit=limit, **{
51+
**kwargs, **{ccxt_enums.OrderFetchParams.STOP.value: True}
52+
})
53+
fetched_orders.extend(stop_orders)
54+
return await self.ensure_orders_completeness(
55+
fetched_orders, symbol, since=since, limit=limit, **kwargs
56+
)
57+
return fetching_orders_request_wrapper
58+
59+
60+
4461
class RestExchange(abstract_exchange.AbstractExchange):
4562
ORDER_NON_EMPTY_FIELDS = [ecoc.EXCHANGE_ID.value, ecoc.TIMESTAMP.value, ecoc.SYMBOL.value, ecoc.TYPE.value,
4663
ecoc.SIDE.value, ecoc.PRICE.value, ecoc.AMOUNT.value, ecoc.STATUS.value]
@@ -232,6 +249,10 @@ def get_adapter_class(self):
232249
# Override in tentacles when using a custom adapter
233250
return None
234251

252+
def fetch_stop_order_in_different_request(self, symbol: str) -> bool:
253+
# Override in tentacles when stop orders need to be fetched in a separate request from CCXT
254+
return False
255+
235256
async def create_order(self, order_type: enums.TraderOrderType, symbol: str, quantity: decimal.Decimal,
236257
price: decimal.Decimal = None, stop_price: decimal.Decimal = None,
237258
side: enums.TradeOrderSide = None, current_price: decimal.Decimal = None,
@@ -371,7 +392,7 @@ async def _verify_order(self, created_order, order_type, symbol, price, quantity
371392
self.logger.error(f"No order exchange id on created order: {created_order}")
372393
return None
373394
exchange_order_id = created_order[ecoc.EXCHANGE_ID.value]
374-
params = self._order_request_kwargs_factory(
395+
params = self.order_request_kwargs_factory(
375396
exchange_order_id, order_type, **(get_order_params or {})
376397
)
377398
fetched_order = await self.get_order(
@@ -691,7 +712,7 @@ async def get_price_ticker(self, symbol: str, **kwargs: dict) -> typing.Optional
691712
async def get_all_currencies_price_ticker(self, **kwargs: dict) -> typing.Optional[dict[str, dict]]:
692713
return await self.connector.get_all_currencies_price_ticker(**kwargs)
693714

694-
def _order_request_kwargs_factory(
715+
def order_request_kwargs_factory(
695716
self,
696717
exchange_order_id: str,
697718
order_type: typing.Optional[enums.TraderOrderType] = None,
@@ -707,7 +728,7 @@ async def get_order(
707728
order_type: typing.Optional[enums.TraderOrderType] = None,
708729
**kwargs: dict
709730
) -> dict:
710-
extended_kwargs = self._order_request_kwargs_factory(exchange_order_id, order_type, **(kwargs or {}))
731+
extended_kwargs = self.order_request_kwargs_factory(exchange_order_id, order_type, **(kwargs or {}))
711732
return await self._ensure_order_completeness(
712733
await self.connector.get_order(exchange_order_id, symbol=symbol, **extended_kwargs),
713734
symbol, **kwargs
@@ -732,39 +753,34 @@ async def get_order_from_trades(self, symbol, exchange_order_id, order_to_update
732753
return None #OrderNotFound
733754

734755
async def get_all_orders(self, symbol: str = None, since: int = None, limit: int = None, **kwargs: dict) -> list:
735-
return await self._ensure_orders_completeness(
736-
await self.connector.get_all_orders(symbol=symbol, since=since, limit=limit, **kwargs),
737-
symbol, since=since, limit=limit, **kwargs
738-
)
756+
return await self.connector.get_all_orders(symbol=symbol, since=since, limit=limit, **kwargs)
739757

758+
@fetching_orders_request
740759
async def get_open_orders(self, symbol: str = None, since: int = None, limit: int = None, **kwargs: dict) -> list:
741-
return await self._ensure_orders_completeness(
742-
await self.connector.get_open_orders(symbol=symbol, since=since, limit=limit, **kwargs),
743-
symbol, since=since, limit=limit, **kwargs
744-
)
760+
return await self.connector.get_open_orders(symbol=symbol, since=since, limit=limit, **kwargs)
761+
762+
@fetching_orders_request
763+
async def _get_closed_orders(self, symbol: str = None, since: int = None, limit: int = None, **kwargs: dict) -> list:
764+
return await self.connector.get_closed_orders(symbol=symbol, since=since, limit=limit, **kwargs)
745765

746766
async def get_closed_orders(self, symbol: str = None, since: int = None, limit: int = None, **kwargs: dict) -> list:
767+
# uses connector.get_closed_orders if supported, otherwise uses recent trades
747768
try:
748-
return await self._ensure_orders_completeness(
749-
await self.connector.get_closed_orders(symbol=symbol, since=since, limit=limit, **kwargs),
750-
symbol, since=since, limit=limit, **kwargs
751-
)
769+
return await self._get_closed_orders(symbol=symbol, since=since, limit=limit, **kwargs)
752770
except errors.NotSupported:
753771
if self.REQUIRE_CLOSED_ORDERS_FROM_RECENT_TRADES:
754772
return await self._get_closed_orders_from_my_recent_trades(
755773
symbol=symbol, since=since, limit=limit, **kwargs
756774
)
757775
raise
758776

777+
@fetching_orders_request
759778
async def get_cancelled_orders(
760779
self, symbol: str = None, since: int = None, limit: int = None, **kwargs: dict
761780
) -> list:
762781
if not self.SUPPORT_FETCHING_CANCELLED_ORDERS:
763782
raise errors.NotSupported(f"get_cancelled_orders is not supported")
764-
return await self._ensure_orders_completeness(
765-
await self.connector.get_cancelled_orders(symbol=symbol, since=since, limit=limit, **kwargs),
766-
symbol, since=since, limit=limit, **kwargs
767-
)
783+
return await self.connector.get_cancelled_orders(symbol=symbol, since=since, limit=limit, **kwargs)
768784

769785
async def _get_closed_orders_from_my_recent_trades(
770786
self, symbol: str = None, since: int = None, limit: int = None, **kwargs: dict
@@ -775,7 +791,7 @@ async def _get_closed_orders_from_my_recent_trades(
775791
for trade in trades
776792
]
777793

778-
async def _ensure_orders_completeness(
794+
async def _ensure_orders_ensure_orders_completenesscompleteness(
779795
self, raw_orders, symbol, since=None, limit=None, trades_by_exchange_order_id=None, **kwargs
780796
):
781797
if not self.REQUIRE_ORDER_FEES_FROM_TRADES \
@@ -825,7 +841,7 @@ async def cancel_all_orders(self, symbol: str = None, **kwargs: dict) -> None:
825841
async def cancel_order(
826842
self, exchange_order_id: str, symbol: str, order_type: enums.TraderOrderType, **kwargs: dict
827843
) -> enums.OrderStatus:
828-
extended_kwargs = self._order_request_kwargs_factory(exchange_order_id, order_type, **(kwargs or {}))
844+
extended_kwargs = self.order_request_kwargs_factory(exchange_order_id, order_type, **(kwargs or {}))
829845
return await self.connector.cancel_order(exchange_order_id, symbol, order_type, **extended_kwargs)
830846

831847
def get_trade_fee(self, symbol: str, order_type: enums.TraderOrderType, quantity, price, taker_or_maker) -> dict:

0 commit comments

Comments
 (0)