Merge pull request #4054 from freqtrade/models_open_price
Models open price
This commit is contained in:
commit
c784e5780e
@ -1395,7 +1395,7 @@ class FreqtradeBot:
|
|||||||
abs_tol=constants.MATH_CLOSE_PREC):
|
abs_tol=constants.MATH_CLOSE_PREC):
|
||||||
order['amount'] = new_amount
|
order['amount'] = new_amount
|
||||||
order.pop('filled', None)
|
order.pop('filled', None)
|
||||||
trade.recalc_open_trade_price()
|
trade.recalc_open_trade_value()
|
||||||
except DependencyException as exception:
|
except DependencyException as exception:
|
||||||
logger.warning("Could not update trade amount: %s", exception)
|
logger.warning("Could not update trade amount: %s", exception)
|
||||||
|
|
||||||
|
@ -53,11 +53,11 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||||||
else:
|
else:
|
||||||
timeframe = get_column_def(cols, 'timeframe', 'null')
|
timeframe = get_column_def(cols, 'timeframe', 'null')
|
||||||
|
|
||||||
open_trade_price = get_column_def(cols, 'open_trade_price',
|
open_trade_value = get_column_def(cols, 'open_trade_value',
|
||||||
f'amount * open_rate * (1 + {fee_open})')
|
f'amount * open_rate * (1 + {fee_open})')
|
||||||
close_profit_abs = get_column_def(
|
close_profit_abs = get_column_def(
|
||||||
cols, 'close_profit_abs',
|
cols, 'close_profit_abs',
|
||||||
f"(amount * close_rate * (1 - {fee_close})) - {open_trade_price}")
|
f"(amount * close_rate * (1 - {fee_close})) - {open_trade_value}")
|
||||||
sell_order_status = get_column_def(cols, 'sell_order_status', 'null')
|
sell_order_status = get_column_def(cols, 'sell_order_status', 'null')
|
||||||
amount_requested = get_column_def(cols, 'amount_requested', 'amount')
|
amount_requested = get_column_def(cols, 'amount_requested', 'amount')
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||||||
stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct,
|
stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct,
|
||||||
stoploss_order_id, stoploss_last_update,
|
stoploss_order_id, stoploss_last_update,
|
||||||
max_rate, min_rate, sell_reason, sell_order_status, strategy,
|
max_rate, min_rate, sell_reason, sell_order_status, strategy,
|
||||||
timeframe, open_trade_price, close_profit_abs
|
timeframe, open_trade_value, close_profit_abs
|
||||||
)
|
)
|
||||||
select id, lower(exchange),
|
select id, lower(exchange),
|
||||||
case
|
case
|
||||||
@ -102,7 +102,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||||||
{max_rate} max_rate, {min_rate} min_rate, {sell_reason} sell_reason,
|
{max_rate} max_rate, {min_rate} min_rate, {sell_reason} sell_reason,
|
||||||
{sell_order_status} sell_order_status,
|
{sell_order_status} sell_order_status,
|
||||||
{strategy} strategy, {timeframe} timeframe,
|
{strategy} strategy, {timeframe} timeframe,
|
||||||
{open_trade_price} open_trade_price, {close_profit_abs} close_profit_abs
|
{open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs
|
||||||
from {table_back_name}
|
from {table_back_name}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ def check_migrate(engine, decl_base, previous_tables) -> None:
|
|||||||
table_back_name = get_backup_name(tabs, 'trades_bak')
|
table_back_name = get_backup_name(tabs, 'trades_bak')
|
||||||
|
|
||||||
# Check for latest column
|
# Check for latest column
|
||||||
if not has_column(cols, 'amount_requested'):
|
if not has_column(cols, 'open_trade_value'):
|
||||||
logger.info(f'Running database migration for trades - backup: {table_back_name}')
|
logger.info(f'Running database migration for trades - backup: {table_back_name}')
|
||||||
migrate_trades_table(decl_base, inspector, engine, table_back_name, cols)
|
migrate_trades_table(decl_base, inspector, engine, table_back_name, cols)
|
||||||
# Reread columns - the above recreated the table!
|
# Reread columns - the above recreated the table!
|
||||||
|
@ -217,8 +217,8 @@ class Trade(_DECL_BASE):
|
|||||||
fee_close_currency = Column(String, nullable=True)
|
fee_close_currency = Column(String, nullable=True)
|
||||||
open_rate = Column(Float)
|
open_rate = Column(Float)
|
||||||
open_rate_requested = Column(Float)
|
open_rate_requested = Column(Float)
|
||||||
# open_trade_price - calculated via _calc_open_trade_price
|
# open_trade_value - calculated via _calc_open_trade_value
|
||||||
open_trade_price = Column(Float)
|
open_trade_value = Column(Float)
|
||||||
close_rate = Column(Float)
|
close_rate = Column(Float)
|
||||||
close_rate_requested = Column(Float)
|
close_rate_requested = Column(Float)
|
||||||
close_profit = Column(Float)
|
close_profit = Column(Float)
|
||||||
@ -252,7 +252,7 @@ class Trade(_DECL_BASE):
|
|||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.recalc_open_trade_price()
|
self.recalc_open_trade_value()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
open_since = self.open_date.strftime(DATETIME_PRINT_FORMAT) if self.is_open else 'closed'
|
open_since = self.open_date.strftime(DATETIME_PRINT_FORMAT) if self.is_open else 'closed'
|
||||||
@ -284,7 +284,7 @@ class Trade(_DECL_BASE):
|
|||||||
'open_timestamp': int(self.open_date.replace(tzinfo=timezone.utc).timestamp() * 1000),
|
'open_timestamp': int(self.open_date.replace(tzinfo=timezone.utc).timestamp() * 1000),
|
||||||
'open_rate': self.open_rate,
|
'open_rate': self.open_rate,
|
||||||
'open_rate_requested': self.open_rate_requested,
|
'open_rate_requested': self.open_rate_requested,
|
||||||
'open_trade_price': round(self.open_trade_price, 8),
|
'open_trade_value': round(self.open_trade_value, 8),
|
||||||
|
|
||||||
'close_date_hum': (arrow.get(self.close_date).humanize()
|
'close_date_hum': (arrow.get(self.close_date).humanize()
|
||||||
if self.close_date else None),
|
if self.close_date else None),
|
||||||
@ -389,7 +389,7 @@ class Trade(_DECL_BASE):
|
|||||||
# Update open rate and actual amount
|
# Update open rate and actual amount
|
||||||
self.open_rate = Decimal(safe_value_fallback(order, 'average', 'price'))
|
self.open_rate = Decimal(safe_value_fallback(order, 'average', 'price'))
|
||||||
self.amount = Decimal(safe_value_fallback(order, 'filled', 'amount'))
|
self.amount = Decimal(safe_value_fallback(order, 'filled', 'amount'))
|
||||||
self.recalc_open_trade_price()
|
self.recalc_open_trade_value()
|
||||||
if self.is_open:
|
if self.is_open:
|
||||||
logger.info(f'{order_type.upper()}_BUY has been fulfilled for {self}.')
|
logger.info(f'{order_type.upper()}_BUY has been fulfilled for {self}.')
|
||||||
self.open_order_id = None
|
self.open_order_id = None
|
||||||
@ -464,7 +464,7 @@ class Trade(_DECL_BASE):
|
|||||||
Trade.session.delete(self)
|
Trade.session.delete(self)
|
||||||
Trade.session.flush()
|
Trade.session.flush()
|
||||||
|
|
||||||
def _calc_open_trade_price(self) -> float:
|
def _calc_open_trade_value(self) -> float:
|
||||||
"""
|
"""
|
||||||
Calculate the open_rate including open_fee.
|
Calculate the open_rate including open_fee.
|
||||||
:return: Price in of the open trade incl. Fees
|
:return: Price in of the open trade incl. Fees
|
||||||
@ -473,14 +473,14 @@ class Trade(_DECL_BASE):
|
|||||||
fees = buy_trade * Decimal(self.fee_open)
|
fees = buy_trade * Decimal(self.fee_open)
|
||||||
return float(buy_trade + fees)
|
return float(buy_trade + fees)
|
||||||
|
|
||||||
def recalc_open_trade_price(self) -> None:
|
def recalc_open_trade_value(self) -> None:
|
||||||
"""
|
"""
|
||||||
Recalculate open_trade_price.
|
Recalculate open_trade_value.
|
||||||
Must be called whenever open_rate or fee_open is changed.
|
Must be called whenever open_rate or fee_open is changed.
|
||||||
"""
|
"""
|
||||||
self.open_trade_price = self._calc_open_trade_price()
|
self.open_trade_value = self._calc_open_trade_value()
|
||||||
|
|
||||||
def calc_close_trade_price(self, rate: Optional[float] = None,
|
def calc_close_trade_value(self, rate: Optional[float] = None,
|
||||||
fee: Optional[float] = None) -> float:
|
fee: Optional[float] = None) -> float:
|
||||||
"""
|
"""
|
||||||
Calculate the close_rate including fee
|
Calculate the close_rate including fee
|
||||||
@ -507,11 +507,11 @@ class Trade(_DECL_BASE):
|
|||||||
If rate is not set self.close_rate will be used
|
If rate is not set self.close_rate will be used
|
||||||
:return: profit in stake currency as float
|
:return: profit in stake currency as float
|
||||||
"""
|
"""
|
||||||
close_trade_price = self.calc_close_trade_price(
|
close_trade_value = self.calc_close_trade_value(
|
||||||
rate=(rate or self.close_rate),
|
rate=(rate or self.close_rate),
|
||||||
fee=(fee or self.fee_close)
|
fee=(fee or self.fee_close)
|
||||||
)
|
)
|
||||||
profit = close_trade_price - self.open_trade_price
|
profit = close_trade_value - self.open_trade_value
|
||||||
return float(f"{profit:.8f}")
|
return float(f"{profit:.8f}")
|
||||||
|
|
||||||
def calc_profit_ratio(self, rate: Optional[float] = None,
|
def calc_profit_ratio(self, rate: Optional[float] = None,
|
||||||
@ -523,11 +523,11 @@ class Trade(_DECL_BASE):
|
|||||||
:param fee: fee to use on the close rate (optional).
|
:param fee: fee to use on the close rate (optional).
|
||||||
:return: profit ratio as float
|
:return: profit ratio as float
|
||||||
"""
|
"""
|
||||||
close_trade_price = self.calc_close_trade_price(
|
close_trade_value = self.calc_close_trade_value(
|
||||||
rate=(rate or self.close_rate),
|
rate=(rate or self.close_rate),
|
||||||
fee=(fee or self.fee_close)
|
fee=(fee or self.fee_close)
|
||||||
)
|
)
|
||||||
profit_ratio = (close_trade_price / self.open_trade_price) - 1
|
profit_ratio = (close_trade_value / self.open_trade_value) - 1
|
||||||
return float(f"{profit_ratio:.8f}")
|
return float(f"{profit_ratio:.8f}")
|
||||||
|
|
||||||
def select_order(self, order_side: str, is_open: Optional[bool]) -> Optional[Order]:
|
def select_order(self, order_side: str, is_open: Optional[bool]) -> Optional[Order]:
|
||||||
|
@ -62,7 +62,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||||||
'fee_close_cost': ANY,
|
'fee_close_cost': ANY,
|
||||||
'fee_close_currency': ANY,
|
'fee_close_currency': ANY,
|
||||||
'open_rate_requested': ANY,
|
'open_rate_requested': ANY,
|
||||||
'open_trade_price': 0.0010025,
|
'open_trade_value': 0.0010025,
|
||||||
'close_rate_requested': ANY,
|
'close_rate_requested': ANY,
|
||||||
'sell_reason': ANY,
|
'sell_reason': ANY,
|
||||||
'sell_order_status': ANY,
|
'sell_order_status': ANY,
|
||||||
@ -127,7 +127,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||||||
'fee_close_cost': ANY,
|
'fee_close_cost': ANY,
|
||||||
'fee_close_currency': ANY,
|
'fee_close_currency': ANY,
|
||||||
'open_rate_requested': ANY,
|
'open_rate_requested': ANY,
|
||||||
'open_trade_price': ANY,
|
'open_trade_value': ANY,
|
||||||
'close_rate_requested': ANY,
|
'close_rate_requested': ANY,
|
||||||
'sell_reason': ANY,
|
'sell_reason': ANY,
|
||||||
'sell_order_status': ANY,
|
'sell_order_status': ANY,
|
||||||
|
@ -707,7 +707,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
|
|||||||
'min_rate': 1.098e-05,
|
'min_rate': 1.098e-05,
|
||||||
'open_order_id': None,
|
'open_order_id': None,
|
||||||
'open_rate_requested': 1.098e-05,
|
'open_rate_requested': 1.098e-05,
|
||||||
'open_trade_price': 0.0010025,
|
'open_trade_value': 0.0010025,
|
||||||
'sell_reason': None,
|
'sell_reason': None,
|
||||||
'sell_order_status': None,
|
'sell_order_status': None,
|
||||||
'strategy': 'DefaultStrategy',
|
'strategy': 'DefaultStrategy',
|
||||||
@ -834,7 +834,7 @@ def test_api_forcebuy(botclient, mocker, fee):
|
|||||||
'min_rate': None,
|
'min_rate': None,
|
||||||
'open_order_id': '123456',
|
'open_order_id': '123456',
|
||||||
'open_rate_requested': None,
|
'open_rate_requested': None,
|
||||||
'open_trade_price': 0.24605460,
|
'open_trade_value': 0.24605460,
|
||||||
'sell_reason': None,
|
'sell_reason': None,
|
||||||
'sell_order_status': None,
|
'sell_order_status': None,
|
||||||
'strategy': None,
|
'strategy': None,
|
||||||
|
@ -177,10 +177,10 @@ def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order, fee):
|
|||||||
|
|
||||||
trade.open_order_id = 'something'
|
trade.open_order_id = 'something'
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
assert trade._calc_open_trade_price() == 0.0010024999999225068
|
assert trade._calc_open_trade_value() == 0.0010024999999225068
|
||||||
|
|
||||||
trade.update(limit_sell_order)
|
trade.update(limit_sell_order)
|
||||||
assert trade.calc_close_trade_price() == 0.0010646656050132426
|
assert trade.calc_close_trade_value() == 0.0010646656050132426
|
||||||
|
|
||||||
# Profit in BTC
|
# Profit in BTC
|
||||||
assert trade.calc_profit() == 0.00006217
|
assert trade.calc_profit() == 0.00006217
|
||||||
@ -233,7 +233,7 @@ def test_calc_close_trade_price_exception(limit_buy_order, fee):
|
|||||||
|
|
||||||
trade.open_order_id = 'something'
|
trade.open_order_id = 'something'
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
assert trade.calc_close_trade_price() == 0.0
|
assert trade.calc_close_trade_value() == 0.0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
@ -277,7 +277,7 @@ def test_update_invalid_order(limit_buy_order):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
def test_calc_open_trade_price(limit_buy_order, fee):
|
def test_calc_open_trade_value(limit_buy_order, fee):
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair='ETH/BTC',
|
pair='ETH/BTC',
|
||||||
stake_amount=0.001,
|
stake_amount=0.001,
|
||||||
@ -291,10 +291,10 @@ def test_calc_open_trade_price(limit_buy_order, fee):
|
|||||||
trade.update(limit_buy_order) # Buy @ 0.00001099
|
trade.update(limit_buy_order) # Buy @ 0.00001099
|
||||||
|
|
||||||
# Get the open rate price with the standard fee rate
|
# Get the open rate price with the standard fee rate
|
||||||
assert trade._calc_open_trade_price() == 0.0010024999999225068
|
assert trade._calc_open_trade_value() == 0.0010024999999225068
|
||||||
trade.fee_open = 0.003
|
trade.fee_open = 0.003
|
||||||
# Get the open rate price with a custom fee rate
|
# Get the open rate price with a custom fee rate
|
||||||
assert trade._calc_open_trade_price() == 0.001002999999922468
|
assert trade._calc_open_trade_value() == 0.001002999999922468
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
@ -312,14 +312,14 @@ def test_calc_close_trade_price(limit_buy_order, limit_sell_order, fee):
|
|||||||
trade.update(limit_buy_order) # Buy @ 0.00001099
|
trade.update(limit_buy_order) # Buy @ 0.00001099
|
||||||
|
|
||||||
# Get the close rate price with a custom close rate and a regular fee rate
|
# Get the close rate price with a custom close rate and a regular fee rate
|
||||||
assert trade.calc_close_trade_price(rate=0.00001234) == 0.0011200318470471794
|
assert trade.calc_close_trade_value(rate=0.00001234) == 0.0011200318470471794
|
||||||
|
|
||||||
# Get the close rate price with a custom close rate and a custom fee rate
|
# Get the close rate price with a custom close rate and a custom fee rate
|
||||||
assert trade.calc_close_trade_price(rate=0.00001234, fee=0.003) == 0.0011194704275749754
|
assert trade.calc_close_trade_value(rate=0.00001234, fee=0.003) == 0.0011194704275749754
|
||||||
|
|
||||||
# Test when we apply a Sell order, and ask price with a custom fee rate
|
# Test when we apply a Sell order, and ask price with a custom fee rate
|
||||||
trade.update(limit_sell_order)
|
trade.update(limit_sell_order)
|
||||||
assert trade.calc_close_trade_price(fee=0.005) == 0.0010619972701635854
|
assert trade.calc_close_trade_value(fee=0.005) == 0.0010619972701635854
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
@ -499,7 +499,7 @@ def test_migrate_old(mocker, default_conf, fee):
|
|||||||
assert trade.max_rate == 0.0
|
assert trade.max_rate == 0.0
|
||||||
assert trade.stop_loss == 0.0
|
assert trade.stop_loss == 0.0
|
||||||
assert trade.initial_stop_loss == 0.0
|
assert trade.initial_stop_loss == 0.0
|
||||||
assert trade.open_trade_price == trade._calc_open_trade_price()
|
assert trade.open_trade_value == trade._calc_open_trade_value()
|
||||||
assert trade.close_profit_abs is None
|
assert trade.close_profit_abs is None
|
||||||
assert trade.fee_open_cost is None
|
assert trade.fee_open_cost is None
|
||||||
assert trade.fee_open_currency is None
|
assert trade.fee_open_currency is None
|
||||||
@ -607,7 +607,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
|
|||||||
assert log_has("trying trades_bak1", caplog)
|
assert log_has("trying trades_bak1", caplog)
|
||||||
assert log_has("trying trades_bak2", caplog)
|
assert log_has("trying trades_bak2", caplog)
|
||||||
assert log_has("Running database migration for trades - backup: trades_bak2", caplog)
|
assert log_has("Running database migration for trades - backup: trades_bak2", caplog)
|
||||||
assert trade.open_trade_price == trade._calc_open_trade_price()
|
assert trade.open_trade_value == trade._calc_open_trade_value()
|
||||||
assert trade.close_profit_abs is None
|
assert trade.close_profit_abs is None
|
||||||
|
|
||||||
assert log_has("Moving open orders to Orders table.", caplog)
|
assert log_has("Moving open orders to Orders table.", caplog)
|
||||||
@ -677,7 +677,7 @@ def test_migrate_mid_state(mocker, default_conf, fee, caplog):
|
|||||||
assert trade.max_rate == 0.0
|
assert trade.max_rate == 0.0
|
||||||
assert trade.stop_loss == 0.0
|
assert trade.stop_loss == 0.0
|
||||||
assert trade.initial_stop_loss == 0.0
|
assert trade.initial_stop_loss == 0.0
|
||||||
assert trade.open_trade_price == trade._calc_open_trade_price()
|
assert trade.open_trade_value == trade._calc_open_trade_value()
|
||||||
assert log_has("trying trades_bak0", caplog)
|
assert log_has("trying trades_bak0", caplog)
|
||||||
assert log_has("Running database migration for trades - backup: trades_bak0", caplog)
|
assert log_has("Running database migration for trades - backup: trades_bak0", caplog)
|
||||||
|
|
||||||
@ -803,7 +803,7 @@ def test_to_json(default_conf, fee):
|
|||||||
'close_timestamp': None,
|
'close_timestamp': None,
|
||||||
'open_rate': 0.123,
|
'open_rate': 0.123,
|
||||||
'open_rate_requested': None,
|
'open_rate_requested': None,
|
||||||
'open_trade_price': 15.1668225,
|
'open_trade_value': 15.1668225,
|
||||||
'fee_close': 0.0025,
|
'fee_close': 0.0025,
|
||||||
'fee_close_cost': None,
|
'fee_close_cost': None,
|
||||||
'fee_close_currency': None,
|
'fee_close_currency': None,
|
||||||
@ -896,7 +896,7 @@ def test_to_json(default_conf, fee):
|
|||||||
'min_rate': None,
|
'min_rate': None,
|
||||||
'open_order_id': None,
|
'open_order_id': None,
|
||||||
'open_rate_requested': None,
|
'open_rate_requested': None,
|
||||||
'open_trade_price': 12.33075,
|
'open_trade_value': 12.33075,
|
||||||
'sell_reason': None,
|
'sell_reason': None,
|
||||||
'sell_order_status': None,
|
'sell_order_status': None,
|
||||||
'strategy': None,
|
'strategy': None,
|
||||||
|
Loading…
Reference in New Issue
Block a user