Skip to content

Commit f061d3d

Browse files
authored
Merge pull request #1628 from Drakkar-Software/dev
Master update
2 parents bdb5224 + df8a5f3 commit f061d3d

File tree

6 files changed

+90
-36
lines changed

6 files changed

+90
-36
lines changed

Meta/Keywords/scripting_library/configuration/profile_data_configuration.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,8 @@ def get_oldest_historical_config_symbols_and_time(profile_data: commons_profiles
559559
first_traded_symbols = _get_all_tentacles_configured_traded_symbols(profile_data, first_historical_config_time)
560560
last_traded_symbols = _get_all_tentacles_configured_traded_symbols(profile_data, None)
561561
return list(first_traded_symbols), list(last_traded_symbols), first_historical_config_time
562+
563+
562564
def _get_all_tentacles_configured_traded_symbols(
563565
profile_data: commons_profiles.ProfileData, first_historical_config_time: typing.Optional[float]
564566
) -> set:

Trading/Exchange/coinbase/coinbase_exchange.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def _get_next_cursor(self, response: dict, func_name: str) -> str:
248248

249249
@ccxt_client_util.converted_ccxt_common_errors
250250
async def _ensure_auth(self):
251-
# Override of ccxt_connector._ensure_auth to use get_open_orders instead
251+
# Override of ccxt_connector._ensure_auth to use get_open_orders instead and propagate authentication errors
252252
try:
253253
# load markets before calling _ensure_auth() to avoid fetching markets status while they are cached
254254
with self.error_describer():
@@ -259,9 +259,9 @@ async def _ensure_auth(self):
259259
# replace self.exchange_manager.exchange.get_balance by get_open_orders
260260
# to mitigate coinbase balance cache side effect
261261
await self.exchange_manager.exchange.get_open_orders(symbol="BTC/USDC")
262-
except (octobot_trading.errors.AuthenticationError, ccxt.AuthenticationError) as e:
263-
await self.client.close()
264-
self.unauthenticated_exchange_fallback(e)
262+
except (octobot_trading.errors.AuthenticationError, ccxt.AuthenticationError):
263+
# this error is critical on coinbase as it prevents loading markets: propagate it
264+
raise
265265
except Exception as e:
266266
# Is probably handled in exchange tentacles, important thing here is that authentication worked
267267
self.logger.debug(f"Error when checking exchange connection: {e}. This should not be an issue.")

Trading/Exchange/hollaex/hollaex_exchange.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,47 @@ async def load_symbol_markets(
5252
market_filter: typing.Union[None, typing.Callable[[dict], bool]] = None
5353
):
5454
await super().load_symbol_markets(reload=reload, market_filter=market_filter)
55-
# all_tickers = await self.get_all_currencies_price_ticker()
56-
# tradable_symbols = set(
57-
# symbol
58-
# for symbol, values in all_tickers.items()
59-
# if values.get(trading_enums.ExchangeConstantsTickersColumns.CLOSE.value, None) != None
60-
# )
55+
await self.disable_quick_trade_only_pairs()
6156
# also refresh fee tiers when necessary
6257
if self.exchange_manager.exchange_name not in _REFRESHED_EXCHANGE_FEE_TIERS_BY_EXCHANGE_NAME:
6358
# always update fees cache using all markets to avoid market filter side effects from the current client
6459
all_markets = ccxt_clients_cache.get_exchange_parsed_markets(ccxt_clients_cache.get_client_key(self.client))
6560
await self._refresh_exchange_fee_tiers(all_markets)
6661

62+
async def disable_quick_trade_only_pairs(self):
63+
# on hollaex exchanges, a market can be "quick trade only" or "spot order book trade" as well.
64+
# a quick trade only market can't be traded like a spot market, disable it.
65+
exchange_constants = await self.client.publicGetConstants()
66+
for quick_trade_only_pairs in self._parse_quick_trades_only_pairs(exchange_constants):
67+
self._disable_pair(quick_trade_only_pairs)
68+
69+
def _disable_pair(self, symbol: str):
70+
if symbol in self.client.markets:
71+
self.logger.info(f"Disabling [{self.exchange_manager.exchange_name}] quick trade only pair: {symbol}")
72+
self.client.markets[symbol][trading_enums.ExchangeConstantsMarketStatusColumns.ACTIVE.value] = False
73+
74+
def _parse_quick_trades_only_pairs(self, exchange_constants: dict) -> list[str]:
75+
if 'quicktrade' not in exchange_constants:
76+
self.logger.error(
77+
f"Unexpected [{self.exchange_manager.exchange_name}] no 'quicktrade' key found in exchange constants"
78+
)
79+
return []
80+
quick_trade_details = exchange_constants['quicktrade']
81+
# format: [{'type': 'network', 'symbol': 'rune-usdt', 'active': True}, ...]
82+
quick_trade_only_pairs = []
83+
for pair_details in quick_trade_details:
84+
if "type" not in pair_details or "symbol" not in pair_details:
85+
self.logger.error(f"Ignored invalid quick trade only pair details: {pair_details}")
86+
continue
87+
# type=pro means this pair is traded in spot order book markets, otherwise it's a quick trade only pair
88+
if pair_details['type'] != "pro":
89+
market_id = pair_details["symbol"]
90+
market = self.client.safe_market(market_id, None, '-')
91+
quick_trade_only_pairs.append(
92+
market[trading_enums.ExchangeConstantsMarketStatusColumns.SYMBOL.value]
93+
)
94+
return quick_trade_only_pairs
95+
6796
async def _refresh_exchange_fee_tiers(self, all_markets: list[dict]):
6897
self.logger.info(f"Refreshing {self.exchange_manager.exchange_name} fee tiers")
6998
response = await self.client.publicGetTiers()

Trading/Exchange/hollaex_autofilled/hollaex_autofilled_exchange.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ async def _retry_fetch_when_rate_limit(cls, session, url):
135135
f"Error text: {await response.text()}"
136136
)
137137
response.raise_for_status()
138+
except aiohttp.ClientResponseError as err:
139+
if err.status == 404:
140+
raise errors.FailedRequest(f"{url} returned 404: not found: {err.message}") from err
141+
raise # forward unexpected errors
138142
except aiohttp.ClientConnectionError as err:
139143
raise errors.NetworkError(
140144
f"Failed to execute request: {err.__class__.__name__}: {html_util.get_html_summary_if_relevant(err)}"

Trading/Exchange/mexc/mexc_exchange.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import time
1919
import typing
2020
import ccxt
21+
from ccxt.base.types import Entry
2122

2223
import octobot_trading.exchanges as exchanges
2324
import octobot_trading.exchanges.connectors.ccxt.constants as ccxt_constants
@@ -77,8 +78,18 @@ def get_additional_connector_config(self):
7778
}
7879

7980
async def get_account_id(self, **kwargs: dict) -> str:
80-
# current impossible to get account UID (10/01/25)
81-
return constants.DEFAULT_ACCOUNT_ID
81+
# https://www.mexc.com/api-docs/spot-v3/spot-account-trade#query-uid
82+
private_get_uid = Entry('uid', ['spot', 'private'], 'GET', {'cost': 10})
83+
try:
84+
resp = await private_get_uid.unbound_method(self.connector.client)
85+
return str(resp["uid"])
86+
except Exception as err:
87+
self.logger.exception(
88+
err,
89+
True,
90+
f"Unexpected error when getting {self.get_name()} account ID: {err}. Using default account ID."
91+
)
92+
return constants.DEFAULT_ACCOUNT_ID
8293

8394
def get_max_orders_count(self, symbol: str, order_type: trading_enums.TraderOrderType) -> int:
8495
# unknown (05/06/2025)

Trading/Mode/market_making_trading_mode/market_making_trading.py

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class OrdersUpdatePlan:
7474
order_actions: list[OrderAction] = dataclasses.field(default_factory=list)
7575
cancelled: bool = False
7676
cancellable: bool = True
77+
force_cancelled: bool = False
7778
processed: asyncio.Event = dataclasses.field(default_factory=asyncio.Event)
7879
trigger_source: str = ""
7980

@@ -297,7 +298,9 @@ async def _process_plan(self, order_actions_plan: OrdersUpdatePlan, current_pric
297298
while scheduled_actions:
298299
action = scheduled_actions.popleft()
299300
try:
300-
if order_actions_plan.cancelled and order_actions_plan.cancellable:
301+
if (
302+
(order_actions_plan.cancelled and order_actions_plan.cancellable) or order_actions_plan.force_cancelled
303+
):
301304
actions_class = action.__class__.__name__
302305
self.logger.debug(
303306
f"{self.trading_mode.symbol} {self.exchange_manager.exchange_name} "
@@ -1187,32 +1190,37 @@ def get_market_making_orders(self):
11871190
if isinstance(order, (trading_personal_data.BuyLimitOrder, trading_personal_data.SellLimitOrder))
11881191
]
11891192

1190-
async def _on_reference_price_update(self):
1193+
async def on_new_reference_price(self, reference_price: decimal.Decimal) -> bool:
11911194
trigger = False
1192-
if reference_price := await self._get_reference_price():
1193-
open_orders = self.get_market_making_orders()
1194-
buy_orders = [
1195-
order
1196-
for order in open_orders
1197-
if order.side == trading_enums.TradeOrderSide.BUY
1198-
]
1199-
if not buy_orders:
1195+
open_orders = self.get_market_making_orders()
1196+
buy_orders = [
1197+
order
1198+
for order in open_orders
1199+
if order.side == trading_enums.TradeOrderSide.BUY
1200+
]
1201+
if not buy_orders:
1202+
trigger = True
1203+
else:
1204+
max_buy_price = max(order.origin_price for order in buy_orders)
1205+
if max_buy_price > reference_price:
12001206
trigger = True
1201-
else:
1202-
max_buy_price = max(order.origin_price for order in buy_orders)
1203-
if max_buy_price > reference_price:
1204-
trigger = True
1205-
sell_orders = [
1206-
order
1207-
for order in open_orders
1208-
if order.side == trading_enums.TradeOrderSide.SELL
1209-
]
1210-
if not sell_orders:
1207+
sell_orders = [
1208+
order
1209+
for order in open_orders
1210+
if order.side == trading_enums.TradeOrderSide.SELL
1211+
]
1212+
if not sell_orders:
1213+
trigger = True
1214+
else:
1215+
min_sell_price = min(order.origin_price for order in sell_orders)
1216+
if min_sell_price < reference_price:
12111217
trigger = True
1212-
else:
1213-
min_sell_price = min(order.origin_price for order in sell_orders)
1214-
if min_sell_price < reference_price:
1215-
trigger = True
1218+
return trigger
1219+
1220+
async def _on_reference_price_update(self):
1221+
trigger = False
1222+
if reference_price := await self._get_reference_price():
1223+
trigger = await self.on_new_reference_price(reference_price)
12161224
if trigger:
12171225
await self._ensure_market_making_orders(f"reference price update: {float(reference_price)}")
12181226

0 commit comments

Comments
 (0)