diff --git a/octobot/community/authentication.py b/octobot/community/authentication.py index 4bade9c6a..c9ea9689c 100644 --- a/octobot/community/authentication.py +++ b/octobot/community/authentication.py @@ -850,6 +850,17 @@ async def update_orders(self, orders_by_exchange: dict[str, list]): await self.supabase_client.update_bot_orders(self.user_account.bot_id, formatted_orders) self.logger.info(f"Bot orders updated: using {len(formatted_orders)} orders") + @_bot_data_update + async def update_positions(self, positions_by_exchange: dict[str, list]): + """ + Updates authenticated account positions + """ + formatted_positions = [] + for exchange_name, positions in positions_by_exchange.items(): + formatted_positions += formatters.format_positions(positions, exchange_name) + await self.supabase_client.update_bot_positions(self.user_account.bot_id, formatted_positions) + self.logger.info(f"Bot positions updated: using {len(formatted_positions)} positions") + @_bot_data_update async def update_portfolio(self, current_value: dict, initial_value: dict, profitability: float, unit: str, content: dict, history: dict, price_by_asset: dict, diff --git a/octobot/community/models/formatters.py b/octobot/community/models/formatters.py index 2b910b89c..c5b492e85 100644 --- a/octobot/community/models/formatters.py +++ b/octobot/community/models/formatters.py @@ -57,6 +57,48 @@ def _format_trade(trade: dict, exchange_name: str, bot_id: str): } +def format_positions(positions: list, exchange_name: str) -> list: + return [ + { + backend_enums.PositionKeys.EXCHANGE.value: exchange_name, + backend_enums.PositionKeys.SYMBOL.value: position[trading_enums.ExchangeConstantsPositionColumns.SYMBOL.value], + backend_enums.PositionKeys.STATUS.value: position[trading_enums.ExchangeConstantsPositionColumns.STATUS.value], + backend_enums.PositionKeys.TIMESTAMP.value: position[trading_enums.ExchangeConstantsPositionColumns.TIMESTAMP.value], + backend_enums.PositionKeys.SIDE.value: position[trading_enums.ExchangeConstantsPositionColumns.SIDE.value], + backend_enums.PositionKeys.QUANTITY.value: float(position[trading_enums.ExchangeConstantsPositionColumns.QUANTITY.value]) + if position[trading_enums.ExchangeConstantsPositionColumns.QUANTITY.value] else 0, + backend_enums.PositionKeys.SIZE.value: float(position[trading_enums.ExchangeConstantsPositionColumns.SIZE.value]) + if position[trading_enums.ExchangeConstantsPositionColumns.SIZE.value] else 0, + backend_enums.PositionKeys.NOTIONAL.value: float(position[trading_enums.ExchangeConstantsPositionColumns.NOTIONAL.value]) + if position[trading_enums.ExchangeConstantsPositionColumns.NOTIONAL.value] else 0, + backend_enums.PositionKeys.INITIAL_MARGIN.value: float(position[trading_enums.ExchangeConstantsPositionColumns.INITIAL_MARGIN.value]) + if position[trading_enums.ExchangeConstantsPositionColumns.INITIAL_MARGIN.value] else 0, + backend_enums.PositionKeys.AUTO_DEPOSIT_MARGIN.value: + position[trading_enums.ExchangeConstantsPositionColumns.AUTO_DEPOSIT_MARGIN.value], + backend_enums.PositionKeys.COLLATERAL.value: float(position[trading_enums.ExchangeConstantsPositionColumns.COLLATERAL.value]) + if position[trading_enums.ExchangeConstantsPositionColumns.COLLATERAL.value] else 0, + backend_enums.PositionKeys.LEVERAGE.value: float(position[trading_enums.ExchangeConstantsPositionColumns.LEVERAGE.value]) + if position[trading_enums.ExchangeConstantsPositionColumns.LEVERAGE.value] else 0, + backend_enums.PositionKeys.MARGIN_TYPE.value: position[trading_enums.ExchangeConstantsPositionColumns.MARGIN_TYPE.value], + backend_enums.PositionKeys.POSITION_MODE.value: position[trading_enums.ExchangeConstantsPositionColumns.POSITION_MODE.value], + backend_enums.PositionKeys.ENTRY_PRICE.value: float(position[trading_enums.ExchangeConstantsPositionColumns.ENTRY_PRICE.value]) + if position[trading_enums.ExchangeConstantsPositionColumns.ENTRY_PRICE.value] else 0, + backend_enums.PositionKeys.MARK_PRICE.value: float(position[trading_enums.ExchangeConstantsPositionColumns.MARK_PRICE.value]) + if position[trading_enums.ExchangeConstantsPositionColumns.MARK_PRICE.value] else 0, + backend_enums.PositionKeys.LIQUIDATION_PRICE.value: float(position[trading_enums.ExchangeConstantsPositionColumns.LIQUIDATION_PRICE.value]) + if position[trading_enums.ExchangeConstantsPositionColumns.LIQUIDATION_PRICE.value] else 0, + backend_enums.PositionKeys.UNREALIZED_PNL.value: float(position[trading_enums.ExchangeConstantsPositionColumns.UNREALIZED_PNL.value]) + if position[trading_enums.ExchangeConstantsPositionColumns.UNREALIZED_PNL.value] else 0, + backend_enums.PositionKeys.REALISED_PNL.value: float(position[trading_enums.ExchangeConstantsPositionColumns.REALISED_PNL.value]) + if position[trading_enums.ExchangeConstantsPositionColumns.REALISED_PNL.value] else 0, + backend_enums.PositionKeys.MAINTENANCE_MARGIN_RATE.value: float(position[trading_enums.ExchangeConstantsPositionColumns.MAINTENANCE_MARGIN_RATE.value]) + if position[trading_enums.ExchangeConstantsPositionColumns.MAINTENANCE_MARGIN_RATE.value] else 0, + + } + for position in positions + ] + + def format_orders(orders: list, exchange_name: str) -> list: return [ { diff --git a/octobot/community/supabase_backend/community_supabase_client.py b/octobot/community/supabase_backend/community_supabase_client.py index f1eeaf5c6..a7af9ade0 100644 --- a/octobot/community/supabase_backend/community_supabase_client.py +++ b/octobot/community/supabase_backend/community_supabase_client.py @@ -354,6 +354,14 @@ async def update_bot_orders(self, bot_id, formatted_orders) -> dict: bot_id, bot_update ) + async def update_bot_positions(self, bot_id, formatted_positions) -> dict: + bot_update = { + enums.BotKeys.POSITIONS.value: formatted_positions + } + return await self.update_bot( + bot_id, bot_update + ) + async def fetch_bot_tentacles_data_based_config( self, bot_id: str, authenticator, auth_key: typing.Optional[str] ) -> (commons_profiles.ProfileData, list[commons_profiles.ExchangeAuthData]): diff --git a/octobot/community/supabase_backend/enums.py b/octobot/community/supabase_backend/enums.py index 5770f5bac..0fc8d6e37 100644 --- a/octobot/community/supabase_backend/enums.py +++ b/octobot/community/supabase_backend/enums.py @@ -28,6 +28,7 @@ class BotKeys(enum.Enum): LAST_TRADE_TIME = "last_trade_time" METADATA = "metadata" ORDERS = "orders" + POSITIONS = "positions" class ProductsSubscriptionsKeys(enum.Enum): @@ -165,6 +166,30 @@ class OrderKeys(enum.Enum): CHAINED = "chained" +class PositionKeys(enum.Enum): + EXCHANGE = "exchange" + # subset of octobot_trading.enums.ExchangeConstantsPositionColumns + TIMESTAMP = "timestamp" + SYMBOL = "symbol" + ENTRY_PRICE = "entry_price" + MARK_PRICE = "mark_price" + LIQUIDATION_PRICE = "liquidation_price" + UNREALIZED_PNL = "unrealised_pnl" + REALISED_PNL = "realised_pnl" + QUANTITY = "quantity" + SIZE = "size" + NOTIONAL = "notional" + INITIAL_MARGIN = "initial_margin" + AUTO_DEPOSIT_MARGIN = "auto_deposit_margin" + COLLATERAL = "collateral" + LEVERAGE = "leverage" + MARGIN_TYPE = "margin_type" + POSITION_MODE = "position_mode" + MAINTENANCE_MARGIN_RATE = "maintenance_margin_rate" + STATUS = "status" + SIDE = "side" + + class PortfolioKeys(enum.Enum): ID = "id" BOT_ID = "bot_id"