Skip to content

Commit

Permalink
[Community] handle _futures exchange internal names
Browse files Browse the repository at this point in the history
  • Loading branch information
GuillaumeDSM committed Dec 17, 2024
1 parent 835debc commit 91a4183
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 9 deletions.
6 changes: 6 additions & 0 deletions octobot/community/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
CommunityDonation,
StartupInfo,
StrategyData,
get_exchange_type_from_availability,
to_bot_exchange_internal_name,
to_community_exchange_internal_name,
)
from octobot.community.supabase_backend import (
SyncConfigurationStorage,
Expand Down Expand Up @@ -105,6 +108,9 @@
"flush_tracker",
"StartupInfo",
"StrategyData",
"get_exchange_type_from_availability",
"to_bot_exchange_internal_name",
"to_community_exchange_internal_name",
"SyncConfigurationStorage",
"ASyncConfigurationStorage",
"AuthenticatedAsyncSupabaseClient",
Expand Down
6 changes: 6 additions & 0 deletions octobot/community/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
format_portfolio,
format_portfolio_history,
format_portfolio_with_profitability,
get_exchange_type_from_availability,
to_bot_exchange_internal_name,
to_community_exchange_internal_name,
)
from octobot.community.models.community_public_data import (
CommunityPublicData
Expand All @@ -66,6 +69,9 @@
"format_portfolio",
"format_portfolio_history",
"format_portfolio_with_profitability",
"get_exchange_type_from_availability",
"to_bot_exchange_internal_name",
"to_community_exchange_internal_name",
"CommunityPublicData",
"StrategyData",
]
38 changes: 38 additions & 0 deletions octobot/community/models/formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
import octobot.community.supabase_backend.enums as backend_enums
import octobot.community.supabase_backend as supabase_backend
import octobot_commons.constants as commons_constants
import octobot_commons.logging as commons_logging
import octobot_trading.enums as trading_enums
import octobot_trading.constants as trading_constants
import octobot_trading.personal_data as trading_personal_data


FUTURES_INTERNAL_NAME_SUFFIX = "_futures"


def format_trades(trades: list, exchange_name: str, bot_id: str) -> list:
return [
_format_trade(trade, exchange_name, bot_id)
Expand Down Expand Up @@ -139,6 +143,36 @@ def _get_order_type(order_or_trade):
return order_type


def to_community_exchange_internal_name(bot_exchange_internal_name: str, exchange_type: str) -> str:
if exchange_type == commons_constants.CONFIG_EXCHANGE_FUTURE:
return f"{bot_exchange_internal_name}{FUTURES_INTERNAL_NAME_SUFFIX}"
return bot_exchange_internal_name


def to_bot_exchange_internal_name(community_exchange_internal_name: str) -> str:
if community_exchange_internal_name.endswith(FUTURES_INTERNAL_NAME_SUFFIX):
return community_exchange_internal_name[:-len(FUTURES_INTERNAL_NAME_SUFFIX)]
return community_exchange_internal_name


def get_exchange_type_from_availability(exchange_availability: dict) -> str:
if not exchange_availability:
# use spot by default
return commons_constants.CONFIG_EXCHANGE_SPOT
# 1. try futures
if exchange_availability.get("futures") == backend_enums.ExchangeSupportValues.SUPPORTED.value:
return commons_constants.CONFIG_EXCHANGE_FUTURE
# 2. try spot
if exchange_availability.get("spot") == backend_enums.ExchangeSupportValues.SUPPORTED.value:
return commons_constants.CONFIG_EXCHANGE_SPOT
# 3. something went wrong: select spot and log error
_get_logger().error(
f"Unknown exchange type from exchange availability: {exchange_availability}. "
f"Defaulting to {commons_constants.CONFIG_EXCHANGE_SPOT}"
)
return commons_constants.CONFIG_EXCHANGE_SPOT


def format_portfolio(
current_value: dict, initial_value: dict, profitability: float,
unit: str, content: dict, price_by_asset: dict,
Expand Down Expand Up @@ -196,3 +230,7 @@ def get_adapted_portfolio(usd_like_asset, portfolio):
currency = usd_like_asset
formatted[currency] = asset[backend_enums.PortfolioAssetKeys.VALUE.value]
return formatted


def _get_logger():
return commons_logging.get_logger("CommunityFormatter")
40 changes: 31 additions & 9 deletions octobot/community/supabase_backend/community_supabase_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,10 @@ async def _fetch_full_exchange_configs(
for config in (bot_config.get(enums.BotConfigKeys.EXCHANGES.value) or [])
if (
config.get(enums.ExchangeKeys.EXCHANGE_ID.value, None)
and not config.get(enums.ExchangeKeys.INTERNAL_NAME.value, None)
and not (
config.get(enums.ExchangeKeys.INTERNAL_NAME.value, None)
and config.get(enums.ExchangeKeys.AVAILABILITY.value, None)
)
)
}
if incomplete_exchange_config_by_id:
Expand All @@ -486,6 +489,7 @@ async def _fetch_full_exchange_configs(
**incomplete_exchange_config_by_id[exchange[enums.ExchangeKeys.ID.value]],
**{
enums.ExchangeKeys.INTERNAL_NAME.value: exchange[enums.ExchangeKeys.INTERNAL_NAME.value],
enums.ExchangeKeys.AVAILABILITY.value: exchange[enums.ExchangeKeys.AVAILABILITY.value],
}
}
for exchange in fetched_exchanges
Expand All @@ -509,6 +513,7 @@ async def _fetch_full_exchange_configs(
**{
enums.ExchangeKeys.EXCHANGE_ID.value: exchange[enums.ExchangeKeys.ID.value],
enums.ExchangeKeys.INTERNAL_NAME.value: exchange[enums.ExchangeKeys.INTERNAL_NAME.value],
enums.ExchangeKeys.AVAILABILITY.value: exchange[enums.ExchangeKeys.AVAILABILITY.value],
}
}
for credentials_id, exchange in exchanges_by_credential_ids.items()
Expand All @@ -528,30 +533,47 @@ async def _fetch_full_exchange_configs(
{
enums.ExchangeKeys.EXCHANGE_ID.value: exchange[enums.ExchangeKeys.ID.value],
enums.ExchangeKeys.INTERNAL_NAME.value: exchange[enums.ExchangeKeys.INTERNAL_NAME.value],
enums.ExchangeKeys.AVAILABILITY.value: exchange[enums.ExchangeKeys.AVAILABILITY.value],
}
for exchange in fetched_exchanges
# no way to differentiate futures and spot exchanges using internal_names only:
# use spot exchange here by default
if (
formatters.get_exchange_type_from_availability(
exchange.get(enums.ExchangeKeys.AVAILABILITY.value)
)
== commons_constants.CONFIG_EXCHANGE_SPOT
)
]
else:
commons_logging.get_logger(self.__class__.__name__).error(
f"Impossible to fetch exchange details for profile with bot id: {profile_data.profile_details.id}"
)
# Register exchange_type if provided, otherwise will use default value.
# Multi exchange type configurations are not yet supported
exchange_type = (
profile_data.exchanges[0].exchange_type if profile_data.exchanges
else commons_constants.DEFAULT_EXCHANGE_TYPE
)
# Register exchange_type from exchange availability
exchange_type = commons_constants.CONFIG_EXCHANGE_SPOT
if exchanges_configs:
# Multi exchange type configurations are not yet supported
exchange_type = formatters.get_exchange_type_from_availability(
exchanges_configs[0].get(
enums.ExchangeKeys.AVAILABILITY.value
)
)
for exchange_data in exchanges_configs:
exchange_data[enums.ExchangeKeys.EXCHANGE_TYPE.value] = exchange_type
exchange_data[enums.ExchangeKeys.INTERNAL_NAME.value] = formatters.to_bot_exchange_internal_name(
exchange_data[enums.ExchangeKeys.INTERNAL_NAME.value]
)
return [
commons_profiles.ExchangeData.from_dict(exchange_data)
for exchange_data in exchanges_configs
]

async def fetch_exchanges(self, exchange_ids: list, internal_names: typing.Optional[list] = None) -> list:
# WARNING: setting internal_names can result in duplicate (futures and spot) exchanges
select = self.table("exchanges").select(
f"{enums.ExchangeKeys.ID.value}, "
f"{enums.ExchangeKeys.INTERNAL_NAME.value}"
f"{enums.ExchangeKeys.INTERNAL_NAME.value}, "
f"{enums.ExchangeKeys.AVAILABILITY.value}"
)
if internal_names:
return (await select.in_(enums.ExchangeKeys.INTERNAL_NAME.value, internal_names).execute()).data
Expand All @@ -560,7 +582,7 @@ async def fetch_exchanges(self, exchange_ids: list, internal_names: typing.Optio
async def fetch_exchanges_by_credential_ids(self, exchange_credential_ids: list) -> dict:
exchanges = (await self.table("exchange_credentials").select(
"id,"
"exchange:exchanges(id, internal_name)"
f"exchange:exchanges(id, {enums.ExchangeKeys.INTERNAL_NAME.value}, {enums.ExchangeKeys.AVAILABILITY.value})"
).in_("id", exchange_credential_ids).execute()).data
return {
exchange["id"]: exchange["exchange"]
Expand Down
6 changes: 6 additions & 0 deletions octobot/community/supabase_backend/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ class ExchangeKeys(enum.Enum):
EXCHANGE_ID = "exchange_id"
EXCHANGE_TYPE = "exchange_type"
SANDBOXED = "sandboxed"
AVAILABILITY = "availability"


class ExchangeSupportValues(enum.Enum):
SUPPORTED = "supported"
UNSUPPORTED = "unsupported"


class SignalKeys(enum.Enum):
Expand Down

0 comments on commit 91a4183

Please sign in to comment.