Skip to content

Commit

Permalink
[Future] Add FutureContract
Browse files Browse the repository at this point in the history
  • Loading branch information
Herklos committed Oct 26, 2020
1 parent 83e8024 commit dfe4c43
Show file tree
Hide file tree
Showing 20 changed files with 469 additions and 132 deletions.
17 changes: 12 additions & 5 deletions octobot_trading/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ class TradeOrderType(enum.Enum):
UNKNOWN = "unknown" # default value when the order type info is missing in the exchange data


class MarginType(enum.Enum):
CROSS = "cross"
ISOLATE = "isolate"


class EvaluatorStates(enum.Enum):
SHORT = "SHORT"
VERY_SHORT = "VERY_SHORT"
Expand Down Expand Up @@ -329,3 +324,15 @@ class RestExchangePairsRefreshMaxThresholds(enum.Enum):
FAST = 5
MEDIUM = 10
SLOW = 20


class MarginType(enum.Enum):
CROSS = "cross"
ISOLATE = "isolate"


class FutureContractType(enum.Enum):
INVERSE_PERPETUAL = "inverse_perpetual"
PERPETUAL = "perpetual"
INVERSE_EXPIRABLE = "inverse_expirable"
EXPIRABLE = "expirable"
3 changes: 3 additions & 0 deletions octobot_trading/exchanges/types/future_exchange.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
cimport octobot_trading.exchanges.abstract_exchange as abstract_exchange

cdef class FutureExchange(abstract_exchange.AbstractExchange):
cdef public dict pair_contracts

cpdef double calculate_position_value(self, double quantity, double mark_price)
cpdef object get_pair_future_contract(self, str pair) # TODO : fix cimport positions.FutureContract

"""
Parsers
Expand Down
72 changes: 56 additions & 16 deletions octobot_trading/exchanges/types/future_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.
import asyncio

import octobot_trading.enums
import octobot_trading.exchanges.abstract_exchange as abstract_exchange
import octobot_trading.personal_data.positions.contracts as contracts


class FutureExchange(abstract_exchange.AbstractExchange):
Expand All @@ -29,33 +32,54 @@ class FutureExchange(abstract_exchange.AbstractExchange):
FUNDING_WITH_MARK_PRICE = False
FUNDING_IN_TICKER = False

async def get_symbol_open_positions(self, symbol: str, **kwargs: dict) -> list:
def __init__(self, config, exchange_manager):
super().__init__(config, exchange_manager)
self.pair_contracts = {}

async def load_pairs_future_contracts(self):
"""
Get the current position associated to the symbol
:param symbol: the symbol
:return: the user position associated to the symbol
Create a new Contract instance for each traded pair
"""
raise NotImplementedError("get_symbol_open_positions is not implemented")
for pair in self.exchange_manager.exchange_config.traded_symbol_pairs:
await self.load_pair_future_contract(pair)

async def get_open_positions(self, **kwargs: dict) -> dict:
async def load_pair_future_contract(self, pair: str):
"""
Get the user current positions
:return: the user positions
Create a new FutureContract for the pair
:param pair: the pair
"""
raise NotImplementedError("get_open_positions is not implemented")
self.pair_contracts[pair] = contracts.FutureContract(pair)
self.pair_contracts[pair].current_leverage = await self.get_symbol_leverage(pair)
self.pair_contracts[pair].margin_type = await self.get_margin_type_leverage(pair)

async def get_symbol_leverage(self, symbol: str, **kwargs: dict):
def get_pair_future_contract(self, pair):
"""
:param symbol: the symbol
:return: the leverage associated to the symbol
Return the FutureContract instance associated to the pair
TODO create if not exist
:param pair: the pair
:return: the FutureContract instance
"""
raise NotImplementedError("get_symbol_leverage is not implemented")
try:
return self.pair_contracts[pair]
except KeyError:
asyncio.run_coroutine_threadsafe(self.load_pair_future_contract(pair), asyncio.get_running_loop())
return self.pair_contracts[pair]

"""
Positions
"""

async def get_symbol_open_positions(self, symbol: str) -> list:
raise NotImplementedError("get_symbol_open_positions is not implemented")

async def get_mark_price(self, symbol: str, **kwargs: dict) -> dict:
async def get_open_positions(self, **kwargs: dict) -> dict:
"""
:param symbol: the symbol
:return: the current symbol mark price
Get the user current futures
:return: the user futures
"""
raise NotImplementedError("get_open_positions is not implemented")

async def get_mark_price(self, symbol: str) -> dict:
raise NotImplementedError("get_mark_price is not implemented")

async def get_mark_price_history(self, symbol: str, limit: int = 1, **kwargs: dict) -> list:
Expand Down Expand Up @@ -90,6 +114,19 @@ async def get_mark_price_and_funding(self, symbol: str, **kwargs: dict) -> tuple
"""
raise NotImplementedError("get_funding_and_mark_price is not implemented")

"""
Margin and leverage
"""

async def get_symbol_leverage(self, symbol: str):
raise NotImplementedError("get_symbol_leverage is not implemented")

async def get_margin_type_leverage(self, symbol: str):
raise NotImplementedError("get_margin_type_leverage is not implemented")

async def get_symbol_leverage_info(self, symbol: str, limit: int = 1) -> list:
raise NotImplementedError("get_symbol_leverage_info is not implemented")

async def set_symbol_leverage(self, symbol: str, leverage: int):
"""
Set the symbol leverage
Expand All @@ -108,6 +145,9 @@ async def set_symbol_margin_type(self, symbol: str, isolated: bool):
"""
raise NotImplementedError("set_symbol_margin_type is not implemented")

async def set_position_isolated_margin(self, symbol: str, quantity: float, increase_quantity=True):
raise NotImplementedError("set_position_isolated_margin is not implemented")

"""
Parsers
"""
Expand Down
2 changes: 2 additions & 0 deletions octobot_trading/personal_data/__init__.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ from octobot_trading.personal_data.positions cimport (
LongPosition,
PositionsUpdater,
PositionsManager,
FutureContract,
)
from octobot_trading.personal_data cimport trades
from octobot_trading.personal_data.trades cimport (
Expand Down Expand Up @@ -125,6 +126,7 @@ __all__ = [
"Position",
"ShortPosition",
"LongPosition",
"FutureContract",
"PositionsUpdater",
"PositionsManager",
"TradesManager",
Expand Down
2 changes: 2 additions & 0 deletions octobot_trading/personal_data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
LongPosition,
PositionsUpdater,
PositionsManager,
FutureContract,
)
from octobot_trading.personal_data import trades
from octobot_trading.personal_data.trades import (
Expand Down Expand Up @@ -211,6 +212,7 @@
"LongPosition",
"PositionsUpdater",
"PositionsManager",
"FutureContract",
"TradesManager",
"TradesProducer",
"TradesChannel",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,11 @@
cimport octobot_trading.personal_data.portfolios.portfolio as portfolio_class

cdef class FuturePortfolio(portfolio_class.Portfolio):
pass
cdef void _update_portfolio_from_future_order(self,
object order,
double order_quantity,
double order_price,
bint subtract_fees=*,
bint inverse_calculation=*,
bint update_available=*,
bint update_total=*)
71 changes: 66 additions & 5 deletions octobot_trading/personal_data/portfolios/types/future_portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,87 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this library.
import octobot_trading.personal_data.portfolios.portfolio as portfolio_class
import octobot_trading.constants as constants


class FuturePortfolio(portfolio_class.Portfolio):
def update_portfolio_data_from_order(self, order, currency, market):
"""
Call update_portfolio_data for order currency and market
:param order: the order that updated the portfolio
:param currency: the order currency
:param market: the order market
"""
self._update_portfolio_from_future_order(order,
order_quantity=order.filled_quantity,
order_price=order.filled_price,
subtract_fees=True,
inverse_calculation=False,
update_available=True,
update_total=False)

def update_portfolio_available_from_order(self, order, increase_quantity=True):
"""
Realise portfolio availability update
TODO support leverage
:param order: the order that triggers the portfolio update
:param increase_quantity: True when increasing quantity
"""
self._update_portfolio_from_future_order(order,
order_quantity=order.origin_quantity,
order_price=order.origin_price,
subtract_fees=False,
inverse_calculation=not increase_quantity,
update_available=True,
update_total=False)

def _update_portfolio_from_future_order(self, order,
order_quantity,
order_price,
subtract_fees=False,
inverse_calculation=False,
update_available=False,
update_total=False):
"""
Update future portfolio from an order
:param order: the order
:param order_quantity: the order quantity to use for calculation
:param order_price: the order price to use for calculation
:param subtract_fees: when True, subtract fees to order quantity
:param inverse_calculation: when True, inverse calculation (for example when a cancel occurred)
:param update_available: when True, update the available quantity of the portfolio
:param update_total: when True, update the total quantity of the portfolio
"""
currency, market = order.get_currency_and_market()
pair_future_contract = order.exchange_manager.exchange.get_pair_future_contract(order.symbol)

is_inverse_contract = True # TODO
# calculates the real order quantity depending on the current contract leverage
real_order_quantity = ((order_quantity - (order.get_total_fees(currency) if subtract_fees else 0))
/ pair_future_contract.current_leverage)

# When inverse contract, decrease a currency market equivalent quantity from currency balance
if is_inverse_contract:
if pair_future_contract.is_inverse_contract():
# decrease currency market equivalent quantity from currency available balance
self._update_portfolio_data(currency, -(order.origin_quantity / order.origin_price), False, True)
self._update_portfolio_data(currency,
-(real_order_quantity / order_price) * (-1 if inverse_calculation else 1),
total=update_total,
available=update_available)

# When non-inverse contract, decrease directly market quantity
else:
# decrease market quantity from market available balance
self._update_portfolio_data(market, -order.origin_quantity, False, True)
self._update_portfolio_data(market,
-real_order_quantity * (-1 if inverse_calculation else 1),
total=update_total,
available=update_available)

def log_portfolio_update_from_order(self, order, currency, market):
"""
TODO
:param order: the order to log
:param currency: the order currency
:param market: the order market
"""
self.logger.debug(f"Portfolio updated from order "
f"| {currency} TODO "
f"| {market} TODO "
f"| {constants.CURRENT_PORTFOLIO_STRING} {self.portfolio}")
6 changes: 6 additions & 0 deletions octobot_trading/personal_data/positions/__init__.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ from octobot_trading.personal_data.positions.positions_manager cimport (
PositionsManager,
)

from octobot_trading.personal_data.positions cimport contracts
from octobot_trading.personal_data.positions.contracts cimport (
FutureContract,
)

__all__ = [
"PositionsProducer",
"PositionsChannel",
Expand All @@ -43,4 +48,5 @@ __all__ = [
"LongPosition",
"PositionsUpdater",
"PositionsManager",
"FutureContract",
]
6 changes: 6 additions & 0 deletions octobot_trading/personal_data/positions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
PositionsManager,
)

from octobot_trading.personal_data.positions import contracts
from octobot_trading.personal_data.positions.contracts import (
FutureContract,
)

__all__ = [
"PositionsProducer",
"PositionsChannel",
Expand All @@ -43,4 +48,5 @@
"LongPosition",
"PositionsUpdater",
"PositionsManager",
"FutureContract",
]
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import octobot_commons.logging as logging

import octobot_trading.exchanges as exchanges
import octobot_trading.exchanges.channel as exchanges_channel
import octobot_trading.constants as constants
import octobot_trading.personal_data.positions.channel.positions_updater as positions_updater
import octobot_trading.personal_data.positions as positions
Expand All @@ -32,8 +32,8 @@ def __init__(self, channel):
async def start(self):
self.exchange_manager = self.channel.exchange_manager
self.logger = logging.get_logger(f"{self.__class__.__name__}[{self.exchange_manager.exchange.name}]")
await exchanges.get_chan(constants.MARK_PRICE_CHANNEL,
self.channel.exchange_manager.id).new_consumer(self.handle_mark_price)
await exchanges_channel.get_chan(constants.MARK_PRICE_CHANNEL, self.channel.exchange_manager.id) \
.new_consumer(self.handle_mark_price)

async def handle_mark_price(self, exchange: str, exchange_id: str, cryptocurrency: str, symbol: str, mark_price):
"""
Expand Down Expand Up @@ -62,19 +62,16 @@ async def _update_positions_status(self, cryptocurrency: str, symbol: str, mark_
finally:
# ensure always call fill callback
if position_closed:
await exchanges.get_chan(constants.POSITIONS_CHANNEL,
self.channel.exchange_manager.id).get_internal_producer() \
.send(cryptocurrency=cryptocurrency,
symbol=position.symbol,
order=position.to_dict(),
is_from_bot=True,
is_liquidated=position.is_liquidated(),
is_closed=True,
is_updated=False)
await exchanges_channel.get_chan(constants.POSITIONS_CHANNEL, self.channel.exchange_manager.id) \
.get_internal_producer().send(cryptocurrency=cryptocurrency,
symbol=position.symbol,
order=position.to_dict(),
is_from_bot=True,
is_liquidated=position.is_liquidated(),
is_closed=True,
is_updated=False)

async def _update_position_status(self,
position: positions.Position,
mark_price):
async def _update_position_status(self, position: positions.Position, mark_price):
"""
Call position status update
"""
Expand Down
24 changes: 24 additions & 0 deletions octobot_trading/personal_data/positions/contracts/__init__.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Drakkar-Software OctoBot-Trading
# Copyright (c) Drakkar-Software, All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.

from octobot_trading.personal_data.positions.contracts cimport future_contract
from octobot_trading.personal_data.positions.contracts.future_contract cimport (
FutureContract,
)

__all__ = [
"FutureContract",
]

0 comments on commit dfe4c43

Please sign in to comment.