Skip to content

Commit

Permalink
[BinanceExchange] Add future support
Browse files Browse the repository at this point in the history
  • Loading branch information
Herklos committed Aug 25, 2021
1 parent 6305973 commit f47a930
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 3 deletions.
138 changes: 136 additions & 2 deletions Trading/Exchange/binance/binance_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,42 @@
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.
import octobot_commons.constants as common_constants
import octobot_trading.enums as trading_enums
import octobot_trading.exchanges as exchanges


class Binance(exchanges.SpotCCXTExchange):
class Binance(exchanges.SpotCCXTExchange, exchanges.FutureCCXTExchange):
DESCRIPTION = ""

BUY_STR = "BUY"
SELL_STR = "SELL"

ACCOUNTS = {
trading_enums.AccountTypes.CASH: 'cash'
trading_enums.AccountTypes.CASH: 'cash',
trading_enums.AccountTypes.FUTURE: 'future'
}

MARK_PRICE_IN_POSITION = True

BINANCE_SYMBOL = "symbol"

BINANCE_FUTURE_QUANTITY = "positionAmt"
BINANCE_FUTURE_UNREALIZED_PNL = "unRealizedProfit"
BINANCE_FUTURE_LIQUIDATION_PRICE = "liquidationPrice"
BINANCE_FUTURE_VALUE = "liquidationPrice"

BINANCE_MARGIN_TYPE = "marginType"
BINANCE_MARGIN_TYPE_ISOLATED = "ISOLATED"
BINANCE_MARGIN_TYPE_CROSSED = "CROSSED"

BINANCE_FUNDING_RATE = "fundingRate"
BINANCE_LAST_FUNDING_TIME = "fundingTime"
BINANCE_FUNDING_DURATION = 8 * common_constants.HOURS_TO_SECONDS

BINANCE_TIME = "time"
BINANCE_MARK_PRICE = "markPrice"
BINANCE_ENTRY_PRICE = "entryPrice"

@classmethod
def get_name(cls):
Expand Down Expand Up @@ -98,3 +119,116 @@ def _fill_order_missing_data(self, order, trades):
if not order[trading_enums.ExchangeConstantsOrderColumns.FEE.value] and order_id in trades:
order[trading_enums.ExchangeConstantsOrderColumns.FEE.value] = \
trades[order_id][trading_enums.ExchangeConstantsOrderColumns.FEE.value]

async def get_open_positions(self) -> dict:
return [
self.parse_position(position)
for position in await self.connector.client.fapiPrivate_get_positionrisk()
]

async def get_funding_rate(self, symbol: str):
return (await self.get_funding_rate_history(symbol=symbol, limit=1))[-1]

async def get_funding_rate_history(self, symbol: str, limit: int = 100) -> list:
return [
self.parse_funding(funding_rate_dict)
for funding_rate_dict in (await self.connector.client.fapiPublic_get_fundingrate(
{self.BINANCE_SYMBOL: self.get_exchange_pair(symbol),
"limit": limit}))
]

async def set_symbol_leverage(self, symbol: str, leverage: int):
await self.connector.client.fapiPrivate_post_leverage(
{self.BINANCE_SYMBOL: self.get_exchange_pair(symbol),
"leverage": leverage})

async def set_symbol_margin_type(self, symbol: str, isolated: bool):
await self.connector.client.fapiPrivate_post_marginType(
{
self.BINANCE_SYMBOL: self.get_exchange_pair(symbol),
self.BINANCE_MARGIN_TYPE: self.BINANCE_MARGIN_TYPE_ISOLATED
if isolated else self.BINANCE_MARGIN_TYPE_CROSSED
})

def parse_position(self, position_dict):
try:
position_dict.update({
trading_enums.ExchangeConstantsPositionColumns.SYMBOL.value:
self.get_pair_from_exchange(
position_dict[trading_enums.ExchangeConstantsPositionColumns.SYMBOL.value]),
trading_enums.ExchangeConstantsPositionColumns.ID.value:
self.connector.client.safe_string(position_dict,
trading_enums.ExchangeConstantsPositionColumns.ID.value,
position_dict[
trading_enums.ExchangeConstantsPositionColumns.SYMBOL.value]),
trading_enums.ExchangeConstantsPositionColumns.QUANTITY.value:
self.connector.client.safe_float(position_dict, self.BINANCE_FUTURE_QUANTITY, 0),
trading_enums.ExchangeConstantsPositionColumns.MARGIN_TYPE.value:
position_dict.get(self.BINANCE_MARGIN_TYPE, None),
trading_enums.ExchangeConstantsPositionColumns.VALUE.value:
self.calculate_position_value(
self.connector.client.safe_float(
position_dict, trading_enums.ExchangeConstantsPositionColumns.QUANTITY.value, 0),
self.connector.client.safe_float(
position_dict, trading_enums.ExchangeConstantsPositionColumns.MARK_PRICE.value, 1)),
trading_enums.ExchangeConstantsPositionColumns.MARGIN.value:
# TODO
self.connector.client.safe_float(position_dict,
trading_enums.ExchangeConstantsPositionColumns.MARGIN.value,
0),
trading_enums.ExchangeConstantsPositionColumns.UNREALISED_PNL.value:
self.connector.client.safe_float(position_dict, self.BINANCE_FUTURE_UNREALIZED_PNL, 0),
trading_enums.ExchangeConstantsPositionColumns.REALISED_PNL.value:
# TODO
self.connector.client.safe_float(position_dict,
trading_enums.ExchangeConstantsPositionColumns.REALISED_PNL.value,
0),
trading_enums.ExchangeConstantsPositionColumns.LIQUIDATION_PRICE.value:
self.connector.client.safe_float(position_dict, self.BINANCE_FUTURE_LIQUIDATION_PRICE, 0),
trading_enums.ExchangeConstantsPositionColumns.MARK_PRICE.value:
self.connector.client.safe_float(position_dict, self.BINANCE_MARK_PRICE, 0),
trading_enums.ExchangeConstantsPositionColumns.ENTRY_PRICE.value:
self.connector.client.safe_float(position_dict, self.BINANCE_ENTRY_PRICE, 0),
trading_enums.ExchangeConstantsPositionColumns.TIMESTAMP.value:
# TODO
self.connector.get_uniform_timestamp(
self.connector.client.safe_float(position_dict,
trading_enums.ExchangeConstantsPositionColumns.TIMESTAMP.value,
self.connector.get_exchange_current_time())),
trading_enums.ExchangeConstantsPositionColumns.STATUS.value:
self.parse_position_status(self.connector.client.safe_string(position_dict,
trading_enums.ExchangeConstantsPositionColumns.STATUS.value,
default_value=trading_enums.PositionStatus.OPEN.value)),
trading_enums.ExchangeConstantsPositionColumns.SIDE.value:
self.parse_position_side(self.connector.client.safe_string(position_dict,
trading_enums.ExchangeConstantsPositionColumns.SIDE.value,
default_value=trading_enums.PositionSide.UNKNOWN.value)),
})
except KeyError as e:
self.logger.error(f"Fail to parse position dict ({e})")
return position_dict

def parse_funding(self, funding_dict, from_ticker=False):
try:
last_funding_time = self.connector.get_uniform_timestamp(
self.connector.client.safe_float(funding_dict, self.BINANCE_LAST_FUNDING_TIME))
funding_dict = {
trading_enums.ExchangeConstantsFundingColumns.LAST_FUNDING_TIME.value: last_funding_time,
trading_enums.ExchangeConstantsFundingColumns.FUNDING_RATE.value:
self.connector.client.safe_float(funding_dict, self.BINANCE_FUNDING_RATE),
trading_enums.ExchangeConstantsFundingColumns.NEXT_FUNDING_TIME.value:
last_funding_time + self.BINANCE_FUNDING_DURATION
}
except KeyError as e:
self.logger.error(f"Fail to parse funding dict ({e})")
return funding_dict

def parse_mark_price(self, mark_price_dict, from_ticker=False):
try:
mark_price_dict = {
trading_enums.ExchangeConstantsMarkPriceColumns.MARK_PRICE.value:
self.connector.client.safe_float(mark_price_dict, self.BINANCE_MARK_PRICE, 0)
}
except KeyError as e:
self.logger.error(f"Fail to parse mark_price dict ({e})")
return mark_price_dict
2 changes: 1 addition & 1 deletion Trading/Exchange/binance/resources/binance.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Binance is a SpotExchange adaptation for Binance exchange using the REST API.
Binance is a SpotExchange and FutureExchange adaptation for Binance exchange using the REST API.

0 comments on commit f47a930

Please sign in to comment.