Merge pull request #7991 from freqtrade/order_amount_price
Separately store Order amount price
This commit is contained in:
commit
30bc45a1ba
@ -720,7 +720,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
time_in_force=time_in_force,
|
time_in_force=time_in_force,
|
||||||
leverage=leverage
|
leverage=leverage
|
||||||
)
|
)
|
||||||
order_obj = Order.parse_from_ccxt_object(order, pair, side)
|
order_obj = Order.parse_from_ccxt_object(order, pair, side, amount, enter_limit_requested)
|
||||||
order_id = order['id']
|
order_id = order['id']
|
||||||
order_status = order.get('status')
|
order_status = order.get('status')
|
||||||
logger.info(f"Order #{order_id} was created for {pair} and status is {order_status}.")
|
logger.info(f"Order #{order_id} was created for {pair} and status is {order_status}.")
|
||||||
@ -1094,7 +1094,8 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
leverage=trade.leverage
|
leverage=trade.leverage
|
||||||
)
|
)
|
||||||
|
|
||||||
order_obj = Order.parse_from_ccxt_object(stoploss_order, trade.pair, 'stoploss')
|
order_obj = Order.parse_from_ccxt_object(stoploss_order, trade.pair, 'stoploss',
|
||||||
|
trade.amount, stop_price)
|
||||||
trade.orders.append(order_obj)
|
trade.orders.append(order_obj)
|
||||||
trade.stoploss_order_id = str(stoploss_order['id'])
|
trade.stoploss_order_id = str(stoploss_order['id'])
|
||||||
trade.stoploss_last_update = datetime.now(timezone.utc)
|
trade.stoploss_last_update = datetime.now(timezone.utc)
|
||||||
@ -1595,7 +1596,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
self.handle_insufficient_funds(trade)
|
self.handle_insufficient_funds(trade)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
order_obj = Order.parse_from_ccxt_object(order, trade.pair, trade.exit_side)
|
order_obj = Order.parse_from_ccxt_object(order, trade.pair, trade.exit_side, amount, limit)
|
||||||
trade.orders.append(order_obj)
|
trade.orders.append(order_obj)
|
||||||
|
|
||||||
trade.open_order_id = order['id']
|
trade.open_order_id = order['id']
|
||||||
|
@ -214,17 +214,22 @@ def migrate_orders_table(engine, table_back_name: str, cols_order: List):
|
|||||||
average = get_column_def(cols_order, 'average', 'null')
|
average = get_column_def(cols_order, 'average', 'null')
|
||||||
stop_price = get_column_def(cols_order, 'stop_price', 'null')
|
stop_price = get_column_def(cols_order, 'stop_price', 'null')
|
||||||
funding_fee = get_column_def(cols_order, 'funding_fee', '0.0')
|
funding_fee = get_column_def(cols_order, 'funding_fee', '0.0')
|
||||||
|
ft_amount = get_column_def(cols_order, 'ft_amount', 'coalesce(amount, 0.0)')
|
||||||
|
ft_price = get_column_def(cols_order, 'ft_price', 'coalesce(price, 0.0)')
|
||||||
|
|
||||||
# sqlite does not support literals for booleans
|
# sqlite does not support literals for booleans
|
||||||
with engine.begin() as connection:
|
with engine.begin() as connection:
|
||||||
connection.execute(text(f"""
|
connection.execute(text(f"""
|
||||||
insert into orders (id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id,
|
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, average, remaining, cost,
|
status, symbol, order_type, side, price, amount, filled, average, remaining, cost,
|
||||||
stop_price, order_date, order_filled_date, order_update_date, ft_fee_base, funding_fee)
|
stop_price, order_date, order_filled_date, order_update_date, ft_fee_base, funding_fee,
|
||||||
|
ft_amount, ft_price
|
||||||
|
)
|
||||||
select id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id,
|
select id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id,
|
||||||
status, symbol, order_type, side, price, amount, filled, {average} average, remaining,
|
status, symbol, order_type, side, price, amount, filled, {average} average, remaining,
|
||||||
cost, {stop_price} stop_price, order_date, order_filled_date,
|
cost, {stop_price} stop_price, order_date, order_filled_date,
|
||||||
order_update_date, {ft_fee_base} ft_fee_base, {funding_fee} funding_fee
|
order_update_date, {ft_fee_base} ft_fee_base, {funding_fee} funding_fee,
|
||||||
|
{ft_amount} ft_amount, {ft_price} ft_price
|
||||||
from {table_back_name}
|
from {table_back_name}
|
||||||
"""))
|
"""))
|
||||||
|
|
||||||
@ -311,8 +316,8 @@ def check_migrate(engine, decl_base, previous_tables) -> None:
|
|||||||
# if ('orders' not in previous_tables
|
# if ('orders' not in previous_tables
|
||||||
# or not has_column(cols_orders, 'funding_fee')):
|
# or not has_column(cols_orders, 'funding_fee')):
|
||||||
migrating = False
|
migrating = False
|
||||||
# if not has_column(cols_orders, 'funding_fee'):
|
# if not has_column(cols_trades, 'max_stake_amount'):
|
||||||
if not has_column(cols_trades, 'max_stake_amount'):
|
if not has_column(cols_orders, 'ft_price'):
|
||||||
migrating = True
|
migrating = True
|
||||||
logger.info(f"Running database migration for trades - "
|
logger.info(f"Running database migration for trades - "
|
||||||
f"backup: {table_back_name}, {order_table_bak_name}")
|
f"backup: {table_back_name}, {order_table_bak_name}")
|
||||||
|
@ -49,6 +49,8 @@ class Order(_DECL_BASE):
|
|||||||
ft_order_side: str = Column(String(25), nullable=False)
|
ft_order_side: str = Column(String(25), nullable=False)
|
||||||
ft_pair: str = Column(String(25), nullable=False)
|
ft_pair: str = Column(String(25), nullable=False)
|
||||||
ft_is_open = Column(Boolean, nullable=False, default=True, index=True)
|
ft_is_open = Column(Boolean, nullable=False, default=True, index=True)
|
||||||
|
ft_amount = Column(Float, nullable=False)
|
||||||
|
ft_price = Column(Float, nullable=False)
|
||||||
|
|
||||||
order_id: str = Column(String(255), nullable=False, index=True)
|
order_id: str = Column(String(255), nullable=False, index=True)
|
||||||
status = Column(String(255), nullable=True)
|
status = Column(String(255), nullable=True)
|
||||||
@ -82,9 +84,13 @@ class Order(_DECL_BASE):
|
|||||||
self.order_filled_date.replace(tzinfo=timezone.utc) if self.order_filled_date else None
|
self.order_filled_date.replace(tzinfo=timezone.utc) if self.order_filled_date else None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def safe_amount(self) -> float:
|
||||||
|
return self.amount or self.ft_amount
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def safe_price(self) -> float:
|
def safe_price(self) -> float:
|
||||||
return self.average or self.price or self.stop_price
|
return self.average or self.price or self.stop_price or self.ft_price
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def safe_filled(self) -> float:
|
def safe_filled(self) -> float:
|
||||||
@ -94,7 +100,7 @@ class Order(_DECL_BASE):
|
|||||||
def safe_remaining(self) -> float:
|
def safe_remaining(self) -> float:
|
||||||
return (
|
return (
|
||||||
self.remaining if self.remaining is not None else
|
self.remaining if self.remaining is not None else
|
||||||
self.amount - (self.filled or 0.0)
|
self.safe_amount - (self.filled or 0.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -227,11 +233,20 @@ class Order(_DECL_BASE):
|
|||||||
logger.warning(f"Did not find order for {order}.")
|
logger.warning(f"Did not find order for {order}.")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_from_ccxt_object(order: Dict[str, Any], pair: str, side: str) -> 'Order':
|
def parse_from_ccxt_object(
|
||||||
|
order: Dict[str, Any], pair: str, side: str,
|
||||||
|
amount: Optional[float] = None, price: Optional[float] = None) -> 'Order':
|
||||||
"""
|
"""
|
||||||
Parse an order from a ccxt object and return a new order Object.
|
Parse an order from a ccxt object and return a new order Object.
|
||||||
|
Optional support for overriding amount and price is only used for test simplification.
|
||||||
"""
|
"""
|
||||||
o = Order(order_id=str(order['id']), ft_order_side=side, ft_pair=pair)
|
o = Order(
|
||||||
|
order_id=str(order['id']),
|
||||||
|
ft_order_side=side,
|
||||||
|
ft_pair=pair,
|
||||||
|
ft_amount=amount if amount else order['amount'],
|
||||||
|
ft_price=price if price else order['price'],
|
||||||
|
)
|
||||||
|
|
||||||
o.update_from_ccxt_object(order)
|
o.update_from_ccxt_object(order)
|
||||||
return o
|
return o
|
||||||
|
@ -2606,6 +2606,8 @@ def open_trade():
|
|||||||
ft_order_side='buy',
|
ft_order_side='buy',
|
||||||
ft_pair=trade.pair,
|
ft_pair=trade.pair,
|
||||||
ft_is_open=False,
|
ft_is_open=False,
|
||||||
|
ft_amount=trade.amount,
|
||||||
|
ft_price=trade.open_rate,
|
||||||
order_id='123456789',
|
order_id='123456789',
|
||||||
status="closed",
|
status="closed",
|
||||||
symbol=trade.pair,
|
symbol=trade.pair,
|
||||||
@ -2642,6 +2644,8 @@ def open_trade_usdt():
|
|||||||
ft_order_side='buy',
|
ft_order_side='buy',
|
||||||
ft_pair=trade.pair,
|
ft_pair=trade.pair,
|
||||||
ft_is_open=False,
|
ft_is_open=False,
|
||||||
|
ft_amount=trade.amount,
|
||||||
|
ft_price=trade.open_rate,
|
||||||
order_id='123456789',
|
order_id='123456789',
|
||||||
status="closed",
|
status="closed",
|
||||||
symbol=trade.pair,
|
symbol=trade.pair,
|
||||||
@ -2659,6 +2663,8 @@ def open_trade_usdt():
|
|||||||
ft_order_side='exit',
|
ft_order_side='exit',
|
||||||
ft_pair=trade.pair,
|
ft_pair=trade.pair,
|
||||||
ft_is_open=True,
|
ft_is_open=True,
|
||||||
|
ft_amount=trade.amount,
|
||||||
|
ft_price=trade.open_rate,
|
||||||
order_id='123456789_exit',
|
order_id='123456789_exit',
|
||||||
status="open",
|
status="open",
|
||||||
symbol=trade.pair,
|
symbol=trade.pair,
|
||||||
|
@ -1870,11 +1870,13 @@ def test_get_exit_order_count(fee, is_short):
|
|||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
def test_update_order_from_ccxt(caplog):
|
def test_update_order_from_ccxt(caplog):
|
||||||
# Most basic order return (only has orderid)
|
# Most basic order return (only has orderid)
|
||||||
o = Order.parse_from_ccxt_object({'id': '1234'}, 'ADA/USDT', 'buy')
|
o = Order.parse_from_ccxt_object({'id': '1234'}, 'ADA/USDT', 'buy', 20.01, 1234.6)
|
||||||
assert isinstance(o, Order)
|
assert isinstance(o, Order)
|
||||||
assert o.ft_pair == 'ADA/USDT'
|
assert o.ft_pair == 'ADA/USDT'
|
||||||
assert o.ft_order_side == 'buy'
|
assert o.ft_order_side == 'buy'
|
||||||
assert o.order_id == '1234'
|
assert o.order_id == '1234'
|
||||||
|
assert o.ft_price == 1234.6
|
||||||
|
assert o.ft_amount == 20.01
|
||||||
assert o.ft_is_open
|
assert o.ft_is_open
|
||||||
ccxt_order = {
|
ccxt_order = {
|
||||||
'id': '1234',
|
'id': '1234',
|
||||||
@ -1888,13 +1890,15 @@ def test_update_order_from_ccxt(caplog):
|
|||||||
'status': 'open',
|
'status': 'open',
|
||||||
'timestamp': 1599394315123
|
'timestamp': 1599394315123
|
||||||
}
|
}
|
||||||
o = Order.parse_from_ccxt_object(ccxt_order, 'ADA/USDT', 'buy')
|
o = Order.parse_from_ccxt_object(ccxt_order, 'ADA/USDT', 'buy', 20.01, 1234.6)
|
||||||
assert isinstance(o, Order)
|
assert isinstance(o, Order)
|
||||||
assert o.ft_pair == 'ADA/USDT'
|
assert o.ft_pair == 'ADA/USDT'
|
||||||
assert o.ft_order_side == 'buy'
|
assert o.ft_order_side == 'buy'
|
||||||
assert o.order_id == '1234'
|
assert o.order_id == '1234'
|
||||||
assert o.order_type == 'limit'
|
assert o.order_type == 'limit'
|
||||||
assert o.price == 1234.5
|
assert o.price == 1234.5
|
||||||
|
assert o.ft_price == 1234.6
|
||||||
|
assert o.ft_amount == 20.01
|
||||||
assert o.filled == 9
|
assert o.filled == 9
|
||||||
assert o.remaining == 11
|
assert o.remaining == 11
|
||||||
assert o.order_date is not None
|
assert o.order_date is not None
|
||||||
@ -2539,6 +2543,8 @@ def test_recalc_trade_from_orders_dca(data) -> None:
|
|||||||
ft_pair=trade.pair,
|
ft_pair=trade.pair,
|
||||||
order_id=f"order_{order[0]}_{idx}",
|
order_id=f"order_{order[0]}_{idx}",
|
||||||
ft_is_open=False,
|
ft_is_open=False,
|
||||||
|
ft_amount=amount,
|
||||||
|
ft_price=price,
|
||||||
status="closed",
|
status="closed",
|
||||||
symbol=trade.pair,
|
symbol=trade.pair,
|
||||||
order_type="market",
|
order_type="market",
|
||||||
|
@ -39,6 +39,8 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
|
|||||||
order_id=f'{pair}-{trade.entry_side}-{trade.open_date}',
|
order_id=f'{pair}-{trade.entry_side}-{trade.open_date}',
|
||||||
ft_is_open=False,
|
ft_is_open=False,
|
||||||
ft_pair=pair,
|
ft_pair=pair,
|
||||||
|
ft_amount=trade.amount,
|
||||||
|
ft_price=trade.open_rate,
|
||||||
amount=trade.amount,
|
amount=trade.amount,
|
||||||
filled=trade.amount,
|
filled=trade.amount,
|
||||||
remaining=0,
|
remaining=0,
|
||||||
@ -49,16 +51,19 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
|
|||||||
side=trade.entry_side,
|
side=trade.entry_side,
|
||||||
))
|
))
|
||||||
if not is_open:
|
if not is_open:
|
||||||
|
close_price = open_rate * (2 - profit_rate if is_short else profit_rate)
|
||||||
trade.orders.append(Order(
|
trade.orders.append(Order(
|
||||||
ft_order_side=trade.exit_side,
|
ft_order_side=trade.exit_side,
|
||||||
order_id=f'{pair}-{trade.exit_side}-{trade.close_date}',
|
order_id=f'{pair}-{trade.exit_side}-{trade.close_date}',
|
||||||
ft_is_open=False,
|
ft_is_open=False,
|
||||||
ft_pair=pair,
|
ft_pair=pair,
|
||||||
|
ft_amount=trade.amount,
|
||||||
|
ft_price=trade.open_rate,
|
||||||
amount=trade.amount,
|
amount=trade.amount,
|
||||||
filled=trade.amount,
|
filled=trade.amount,
|
||||||
remaining=0,
|
remaining=0,
|
||||||
price=open_rate * (2 - profit_rate if is_short else profit_rate),
|
price=close_price,
|
||||||
average=open_rate * (2 - profit_rate if is_short else profit_rate),
|
average=close_price,
|
||||||
status="closed",
|
status="closed",
|
||||||
order_type="market",
|
order_type="market",
|
||||||
side=trade.exit_side,
|
side=trade.exit_side,
|
||||||
@ -66,7 +71,7 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
|
|||||||
|
|
||||||
trade.recalc_open_trade_value()
|
trade.recalc_open_trade_value()
|
||||||
if not is_open:
|
if not is_open:
|
||||||
trade.close(open_rate * (2 - profit_rate if is_short else profit_rate))
|
trade.close(close_price)
|
||||||
trade.exit_reason = exit_reason
|
trade.exit_reason = exit_reason
|
||||||
|
|
||||||
Trade.query.session.add(trade)
|
Trade.query.session.add(trade)
|
||||||
|
@ -253,6 +253,8 @@ def test_telegram_status_multi_entry(default_conf, update, mocker, fee) -> None:
|
|||||||
ft_order_side='buy',
|
ft_order_side='buy',
|
||||||
ft_pair=trade.pair,
|
ft_pair=trade.pair,
|
||||||
ft_is_open=False,
|
ft_is_open=False,
|
||||||
|
ft_amount=trade.amount,
|
||||||
|
ft_price=trade.open_rate,
|
||||||
status="closed",
|
status="closed",
|
||||||
symbol=trade.pair,
|
symbol=trade.pair,
|
||||||
order_type="market",
|
order_type="market",
|
||||||
|
@ -1168,6 +1168,8 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_
|
|||||||
order_id='100',
|
order_id='100',
|
||||||
ft_pair=trade.pair,
|
ft_pair=trade.pair,
|
||||||
ft_is_open=True,
|
ft_is_open=True,
|
||||||
|
ft_amount=trade.amount,
|
||||||
|
ft_price=0.0,
|
||||||
))
|
))
|
||||||
assert trade
|
assert trade
|
||||||
|
|
||||||
@ -4615,6 +4617,7 @@ def test_get_real_amount_open_trade_usdt(default_conf_usdt, fee, mocker):
|
|||||||
'amount': amount,
|
'amount': amount,
|
||||||
'status': 'open',
|
'status': 'open',
|
||||||
'side': 'buy',
|
'side': 'buy',
|
||||||
|
'price': 0.245441,
|
||||||
}
|
}
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||||
order_obj = Order.parse_from_ccxt_object(order, 'LTC/ETH', 'buy')
|
order_obj = Order.parse_from_ccxt_object(order, 'LTC/ETH', 'buy')
|
||||||
|
Loading…
Reference in New Issue
Block a user