Set leverage and borowed to computed properties
This commit is contained in:
parent
692c55088a
commit
691a042e29
@ -133,7 +133,7 @@ class Order(_DECL_BASE):
|
|||||||
order_update_date = Column(DateTime, nullable=True)
|
order_update_date = Column(DateTime, nullable=True)
|
||||||
|
|
||||||
leverage = Column(Float, nullable=True, default=1.0)
|
leverage = Column(Float, nullable=True, default=1.0)
|
||||||
is_short = Column(Boolean, nullable=False, default=False)
|
is_short = Column(Boolean, nullable=True, default=False)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return (f'Order(id={self.id}, order_id={self.order_id}, trade_id={self.ft_trade_id}, '
|
return (f'Order(id={self.id}, order_id={self.order_id}, trade_id={self.ft_trade_id}, '
|
||||||
@ -264,40 +264,42 @@ class LocalTrade():
|
|||||||
timeframe: Optional[int] = None
|
timeframe: Optional[int] = None
|
||||||
|
|
||||||
# Margin trading properties
|
# Margin trading properties
|
||||||
leverage: Optional[float] = 1.0
|
|
||||||
borrowed: float = 0.0
|
|
||||||
borrowed_currency: str = None
|
borrowed_currency: str = None
|
||||||
collateral_currency: str = None
|
collateral_currency: str = None
|
||||||
interest_rate: float = 0.0
|
interest_rate: float = 0.0
|
||||||
liquidation_price: float = None
|
liquidation_price: float = None
|
||||||
is_short: bool = False
|
__leverage: float = 1.0 # * You probably want to use self.leverage instead |
|
||||||
|
__borrowed: float = 0.0 # * You probably want to use self.borrowed instead |
|
||||||
|
__is_short: bool = False # * You probably want to use self.is_short instead V
|
||||||
|
|
||||||
|
@property
|
||||||
|
def leverage(self) -> float:
|
||||||
|
return self.__leverage or 1.0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def borrowed(self) -> float:
|
||||||
|
return self.__borrowed or 0.0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_short(self) -> bool:
|
||||||
|
return self.__is_short or False
|
||||||
|
|
||||||
|
@is_short.setter
|
||||||
|
def is_short(self, val):
|
||||||
|
self.__is_short = val
|
||||||
|
|
||||||
|
@leverage.setter
|
||||||
|
def leverage(self, lev):
|
||||||
|
self.__leverage = lev
|
||||||
|
self.__borrowed = self.amount * (lev-1)
|
||||||
|
self.amount = self.amount * lev
|
||||||
|
|
||||||
|
@borrowed.setter
|
||||||
|
def borrowed(self, bor):
|
||||||
|
self.__leverage = self.amount / (self.amount - self.borrowed)
|
||||||
|
self.__borrowed = bor
|
||||||
# End of margin trading properties
|
# End of margin trading properties
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
lev = kwargs.get('leverage')
|
|
||||||
bor = kwargs.get('borrowed')
|
|
||||||
amount = kwargs.get('amount')
|
|
||||||
if lev and bor:
|
|
||||||
# TODO: should I raise an error?
|
|
||||||
raise OperationalException('Cannot pass both borrowed and leverage to Trade')
|
|
||||||
elif lev:
|
|
||||||
self.amount = amount * lev
|
|
||||||
self.borrowed = amount * (lev-1)
|
|
||||||
elif bor:
|
|
||||||
self.lev = (bor + amount)/amount
|
|
||||||
|
|
||||||
for key in kwargs:
|
|
||||||
setattr(self, key, kwargs[key])
|
|
||||||
if not self.is_short:
|
|
||||||
self.is_short = False
|
|
||||||
self.recalc_open_trade_value()
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
open_since = self.open_date.strftime(DATETIME_PRINT_FORMAT) if self.is_open else 'closed'
|
|
||||||
|
|
||||||
return (f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, '
|
|
||||||
f'open_rate={self.open_rate:.8f}, open_since={open_since})')
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def open_date_utc(self):
|
def open_date_utc(self):
|
||||||
return self.open_date.replace(tzinfo=timezone.utc)
|
return self.open_date.replace(tzinfo=timezone.utc)
|
||||||
@ -306,6 +308,20 @@ class LocalTrade():
|
|||||||
def close_date_utc(self):
|
def close_date_utc(self):
|
||||||
return self.close_date.replace(tzinfo=timezone.utc)
|
return self.close_date.replace(tzinfo=timezone.utc)
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
if kwargs.get('leverage') and kwargs.get('borrowed'):
|
||||||
|
# TODO-mg: should I raise an error?
|
||||||
|
raise OperationalException('Cannot pass both borrowed and leverage to Trade')
|
||||||
|
for key in kwargs:
|
||||||
|
setattr(self, key, kwargs[key])
|
||||||
|
self.recalc_open_trade_value()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
open_since = self.open_date.strftime(DATETIME_PRINT_FORMAT) if self.is_open else 'closed'
|
||||||
|
|
||||||
|
return (f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, '
|
||||||
|
f'open_rate={self.open_rate:.8f}, open_since={open_since})')
|
||||||
|
|
||||||
def to_json(self) -> Dict[str, Any]:
|
def to_json(self) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
'trade_id': self.id,
|
'trade_id': self.id,
|
||||||
@ -448,7 +464,7 @@ class LocalTrade():
|
|||||||
Determines if the trade is an opening (long buy or short sell) trade
|
Determines if the trade is an opening (long buy or short sell) trade
|
||||||
:param side (string): the side (buy/sell) that order happens on
|
:param side (string): the side (buy/sell) that order happens on
|
||||||
"""
|
"""
|
||||||
is_short = self.is_short
|
is_short = self.is_short or False
|
||||||
return (side == 'buy' and not is_short) or (side == 'sell' and is_short)
|
return (side == 'buy' and not is_short) or (side == 'sell' and is_short)
|
||||||
|
|
||||||
def is_closing_trade(self, side) -> bool:
|
def is_closing_trade(self, side) -> bool:
|
||||||
@ -456,7 +472,7 @@ class LocalTrade():
|
|||||||
Determines if the trade is an closing (long sell or short buy) trade
|
Determines if the trade is an closing (long sell or short buy) trade
|
||||||
:param side (string): the side (buy/sell) that order happens on
|
:param side (string): the side (buy/sell) that order happens on
|
||||||
"""
|
"""
|
||||||
is_short = self.is_short
|
is_short = self.is_short or False
|
||||||
return (side == 'sell' and not is_short) or (side == 'buy' and is_short)
|
return (side == 'sell' and not is_short) or (side == 'buy' and is_short)
|
||||||
|
|
||||||
def update(self, order: Dict) -> None:
|
def update(self, order: Dict) -> None:
|
||||||
@ -466,9 +482,14 @@ class LocalTrade():
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
order_type = order['type']
|
order_type = order['type']
|
||||||
|
|
||||||
|
# if ('leverage' in order and 'borrowed' in order):
|
||||||
|
# raise OperationalException('Cannot update a trade with both borrowed and leverage')
|
||||||
|
|
||||||
# TODO: I don't like this, but it might be the only way
|
# TODO: I don't like this, but it might be the only way
|
||||||
if 'is_short' in order and order['side'] == 'sell':
|
if 'is_short' in order and order['side'] == 'sell':
|
||||||
self.is_short = order['is_short']
|
self.is_short = order['is_short']
|
||||||
|
|
||||||
# Ignore open and cancelled orders
|
# Ignore open and cancelled orders
|
||||||
if order['status'] == 'open' or safe_value_fallback(order, 'average', 'price') is None:
|
if order['status'] == 'open' or safe_value_fallback(order, 'average', 'price') is None:
|
||||||
return
|
return
|
||||||
@ -477,8 +498,17 @@ class LocalTrade():
|
|||||||
|
|
||||||
if order_type in ('market', 'limit') and self.is_opening_trade(order['side']):
|
if order_type in ('market', 'limit') and self.is_opening_trade(order['side']):
|
||||||
# Update open rate and actual amount
|
# Update open rate and actual amount
|
||||||
|
|
||||||
|
# self.is_short = safe_value_fallback(order, 'is_short', default_value=False)
|
||||||
|
# self.borrowed = safe_value_fallback(order, 'is_short', default_value=False)
|
||||||
|
|
||||||
self.open_rate = float(safe_value_fallback(order, 'average', 'price'))
|
self.open_rate = float(safe_value_fallback(order, 'average', 'price'))
|
||||||
self.amount = float(safe_value_fallback(order, 'filled', 'amount'))
|
self.amount = float(safe_value_fallback(order, 'filled', 'amount'))
|
||||||
|
if 'borrowed' in order:
|
||||||
|
self.borrowed = order['borrowed']
|
||||||
|
elif 'leverage' in order:
|
||||||
|
self.leverage = order['leverage']
|
||||||
|
|
||||||
self.recalc_open_trade_value()
|
self.recalc_open_trade_value()
|
||||||
if self.is_open:
|
if self.is_open:
|
||||||
payment = "SELL" if self.is_short else "BUY"
|
payment = "SELL" if self.is_short else "BUY"
|
||||||
|
@ -2132,6 +2132,40 @@ def limit_exit_short_order(limit_exit_short_order_open):
|
|||||||
order['status'] = 'closed'
|
order['status'] = 'closed'
|
||||||
return order
|
return order
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def market_short_order():
|
||||||
|
return {
|
||||||
|
'id': 'mocked_market_buy',
|
||||||
|
'type': 'market',
|
||||||
|
'side': 'buy',
|
||||||
|
'symbol': 'mocked',
|
||||||
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
|
'price': 0.00004173,
|
||||||
|
'amount': 91.99181073,
|
||||||
|
'filled': 91.99181073,
|
||||||
|
'remaining': 0.0,
|
||||||
|
'status': 'closed',
|
||||||
|
'is_short': True,
|
||||||
|
'leverage': 3
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def market_exit_short_order():
|
||||||
|
return {
|
||||||
|
'id': 'mocked_limit_sell',
|
||||||
|
'type': 'market',
|
||||||
|
'side': 'sell',
|
||||||
|
'symbol': 'mocked',
|
||||||
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
|
'price': 0.00004099,
|
||||||
|
'amount': 91.99181073,
|
||||||
|
'filled': 91.99181073,
|
||||||
|
'remaining': 0.0,
|
||||||
|
'status': 'closed'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def interest_rate():
|
def interest_rate():
|
||||||
|
@ -659,12 +659,13 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
|
|||||||
order_date DATETIME,
|
order_date DATETIME,
|
||||||
order_filled_date DATETIME,
|
order_filled_date DATETIME,
|
||||||
order_update_date DATETIME,
|
order_update_date DATETIME,
|
||||||
|
leverage FLOAT,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT _order_pair_order_id UNIQUE (ft_pair, order_id),
|
CONSTRAINT _order_pair_order_id UNIQUE (ft_pair, order_id),
|
||||||
FOREIGN KEY(ft_trade_id) REFERENCES trades (id)
|
FOREIGN KEY(ft_trade_id) REFERENCES trades (id)
|
||||||
)
|
)
|
||||||
"""))
|
"""))
|
||||||
|
# TODO-mg @xmatthias: Had to add field leverage to this table, check that this is correct
|
||||||
connection.execute(text("""
|
connection.execute(text("""
|
||||||
insert into orders ( id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, status,
|
insert into orders ( id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, status,
|
||||||
symbol, order_type, side, price, amount, filled, remaining, cost, order_date,
|
symbol, order_type, side, price, amount, filled, remaining, cost, order_date,
|
||||||
@ -912,6 +913,14 @@ def test_to_json(default_conf, fee):
|
|||||||
'strategy': None,
|
'strategy': None,
|
||||||
'timeframe': None,
|
'timeframe': None,
|
||||||
'exchange': 'binance',
|
'exchange': 'binance',
|
||||||
|
|
||||||
|
'leverage': None,
|
||||||
|
'borrowed': None,
|
||||||
|
'borrowed_currency': None,
|
||||||
|
'collateral_currency': None,
|
||||||
|
'interest_rate': None,
|
||||||
|
'liquidation_price': None,
|
||||||
|
'is_short': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Simulate dry_run entries
|
# Simulate dry_run entries
|
||||||
@ -977,6 +986,14 @@ def test_to_json(default_conf, fee):
|
|||||||
'strategy': None,
|
'strategy': None,
|
||||||
'timeframe': None,
|
'timeframe': None,
|
||||||
'exchange': 'binance',
|
'exchange': 'binance',
|
||||||
|
|
||||||
|
'leverage': None,
|
||||||
|
'borrowed': None,
|
||||||
|
'borrowed_currency': None,
|
||||||
|
'collateral_currency': None,
|
||||||
|
'interest_rate': None,
|
||||||
|
'liquidation_price': None,
|
||||||
|
'is_short': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1315,7 +1332,7 @@ def test_Trade_object_idem():
|
|||||||
'get_overall_performance',
|
'get_overall_performance',
|
||||||
'get_total_closed_profit',
|
'get_total_closed_profit',
|
||||||
'total_open_trades_stakes',
|
'total_open_trades_stakes',
|
||||||
'get_sold_trades_without_assigned_fees',
|
'get_closed_trades_without_assigned_fees',
|
||||||
'get_open_trades_without_assigned_fees',
|
'get_open_trades_without_assigned_fees',
|
||||||
'get_open_order_trades',
|
'get_open_order_trades',
|
||||||
'get_trades',
|
'get_trades',
|
||||||
|
@ -58,19 +58,22 @@ def test_update_with_binance(limit_short_order, limit_exit_short_order, fee, int
|
|||||||
fee_open=fee.return_value,
|
fee_open=fee.return_value,
|
||||||
fee_close=fee.return_value,
|
fee_close=fee.return_value,
|
||||||
interest_rate=interest_rate.return_value,
|
interest_rate=interest_rate.return_value,
|
||||||
borrowed=90.99181073,
|
# borrowed=90.99181073,
|
||||||
exchange='binance',
|
exchange='binance'
|
||||||
is_short=True
|
|
||||||
)
|
)
|
||||||
#assert trade.open_order_id is None
|
#assert trade.open_order_id is None
|
||||||
assert trade.close_profit is None
|
assert trade.close_profit is None
|
||||||
assert trade.close_date is None
|
assert trade.close_date is None
|
||||||
|
assert trade.borrowed is None
|
||||||
|
assert trade.is_short is None
|
||||||
#trade.open_order_id = 'something'
|
#trade.open_order_id = 'something'
|
||||||
trade.update(limit_short_order)
|
trade.update(limit_short_order)
|
||||||
#assert trade.open_order_id is None
|
#assert trade.open_order_id is None
|
||||||
assert trade.open_rate == 0.00001173
|
assert trade.open_rate == 0.00001173
|
||||||
assert trade.close_profit is None
|
assert trade.close_profit is None
|
||||||
assert trade.close_date is None
|
assert trade.close_date is None
|
||||||
|
assert trade.borrowed == 90.99181073
|
||||||
|
assert trade.is_short is True
|
||||||
assert log_has_re(r"LIMIT_SELL has been fulfilled for Trade\(id=2, "
|
assert log_has_re(r"LIMIT_SELL has been fulfilled for Trade\(id=2, "
|
||||||
r"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001173, open_since=.*\).",
|
r"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001173, open_since=.*\).",
|
||||||
caplog)
|
caplog)
|
||||||
@ -89,7 +92,18 @@ def test_update_with_binance(limit_short_order, limit_exit_short_order, fee, int
|
|||||||
|
|
||||||
|
|
||||||
# @pytest.mark.usefixtures("init_persistence")
|
# @pytest.mark.usefixtures("init_persistence")
|
||||||
# def test_update_market_order(market_buy_order, market_sell_order, fee, caplog):
|
# def test_update_market_order(
|
||||||
|
# market_buy_order,
|
||||||
|
# market_sell_order,
|
||||||
|
# fee,
|
||||||
|
# interest_rate,
|
||||||
|
# ten_minutes_ago,
|
||||||
|
# caplog
|
||||||
|
# ):
|
||||||
|
# """Test Kraken and leverage arguments as well as update market order
|
||||||
|
|
||||||
|
|
||||||
|
# """
|
||||||
# trade = Trade(
|
# trade = Trade(
|
||||||
# id=1,
|
# id=1,
|
||||||
# pair='ETH/BTC',
|
# pair='ETH/BTC',
|
||||||
@ -99,11 +113,15 @@ def test_update_with_binance(limit_short_order, limit_exit_short_order, fee, int
|
|||||||
# is_open=True,
|
# is_open=True,
|
||||||
# fee_open=fee.return_value,
|
# fee_open=fee.return_value,
|
||||||
# fee_close=fee.return_value,
|
# fee_close=fee.return_value,
|
||||||
# open_date=arrow.utcnow().datetime,
|
# open_date=ten_minutes_ago,
|
||||||
# exchange='binance',
|
# exchange='kraken',
|
||||||
|
# interest_rate=interest_rate.return_value
|
||||||
# )
|
# )
|
||||||
# trade.open_order_id = 'something'
|
# trade.open_order_id = 'something'
|
||||||
# trade.update(market_buy_order)
|
# trade.update(market_buy_order)
|
||||||
|
# assert trade.leverage is 3
|
||||||
|
# assert trade.is_short is true
|
||||||
|
# assert trade.leverage is 3
|
||||||
# assert trade.open_order_id is None
|
# assert trade.open_order_id is None
|
||||||
# assert trade.open_rate == 0.00004099
|
# assert trade.open_rate == 0.00004099
|
||||||
# assert trade.close_profit is None
|
# assert trade.close_profit is None
|
||||||
@ -122,8 +140,6 @@ def test_update_with_binance(limit_short_order, limit_exit_short_order, fee, int
|
|||||||
# assert log_has_re(r"MARKET_SELL has been fulfilled for Trade\(id=1, "
|
# assert log_has_re(r"MARKET_SELL has been fulfilled for Trade\(id=1, "
|
||||||
# r"pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=.*\).",
|
# r"pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=.*\).",
|
||||||
# caplog)
|
# caplog)
|
||||||
# # TODO-mg: market short
|
|
||||||
# # TODO-mg: market leveraged long
|
|
||||||
|
|
||||||
|
|
||||||
# @pytest.mark.usefixtures("init_persistence")
|
# @pytest.mark.usefixtures("init_persistence")
|
||||||
|
Loading…
Reference in New Issue
Block a user