Merge pull request #6437 from freqtrade/update_trade

Migrate trade updating to use order model
This commit is contained in:
Matthias 2022-02-23 19:56:02 +01:00 committed by GitHub
commit 53452c8d64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 252 additions and 131 deletions

View File

@ -1357,9 +1357,14 @@ class FreqtradeBot(LoggingMixin):
# Handling of this will happen in check_handle_timedout. # Handling of this will happen in check_handle_timedout.
return True return True
order = self.handle_order_fee(trade, order) order_obj = trade.select_order_by_order_id(order_id)
if not order_obj:
raise DependencyException(
f"Order_obj not found for {order_id}. This should not have happened.")
self.handle_order_fee(trade, order_obj, order)
trade.update(order) trade.update_trade(order_obj)
# TODO: is the below necessary? it's already done in update_trade for filled buys
trade.recalc_trade_from_orders() trade.recalc_trade_from_orders()
Trade.commit() Trade.commit()
@ -1411,17 +1416,15 @@ class FreqtradeBot(LoggingMixin):
return real_amount return real_amount
return amount return amount
def handle_order_fee(self, trade: Trade, order: Dict[str, Any]) -> Dict[str, Any]: def handle_order_fee(self, trade: Trade, order_obj: Order, order: Dict[str, Any]) -> None:
# Try update amount (binance-fix) # Try update amount (binance-fix)
try: try:
new_amount = self.get_real_amount(trade, order) new_amount = self.get_real_amount(trade, order)
if not isclose(safe_value_fallback(order, 'filled', 'amount'), new_amount, if not isclose(safe_value_fallback(order, 'filled', 'amount'), new_amount,
abs_tol=constants.MATH_CLOSE_PREC): abs_tol=constants.MATH_CLOSE_PREC):
order['amount'] = new_amount order_obj.ft_fee_base = trade.amount - new_amount
order.pop('filled', None)
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)
return order
def get_real_amount(self, trade: Trade, order: Dict) -> float: def get_real_amount(self, trade: Trade, order: Dict) -> float:
""" """

View File

@ -57,7 +57,7 @@ def set_sequence_ids(engine, order_id, trade_id):
def migrate_trades_and_orders_table( def migrate_trades_and_orders_table(
decl_base, inspector, engine, decl_base, inspector, engine,
trade_back_name: str, cols: List, trade_back_name: str, cols: List,
order_back_name: str): order_back_name: str, cols_order: List):
fee_open = get_column_def(cols, 'fee_open', 'fee') fee_open = get_column_def(cols, 'fee_open', 'fee')
fee_open_cost = get_column_def(cols, 'fee_open_cost', 'null') fee_open_cost = get_column_def(cols, 'fee_open_cost', 'null')
fee_open_currency = get_column_def(cols, 'fee_open_currency', 'null') fee_open_currency = get_column_def(cols, 'fee_open_currency', 'null')
@ -141,7 +141,7 @@ def migrate_trades_and_orders_table(
from {trade_back_name} from {trade_back_name}
""")) """))
migrate_orders_table(engine, order_back_name, cols) migrate_orders_table(engine, order_back_name, cols_order)
set_sequence_ids(engine, order_id, trade_id) set_sequence_ids(engine, order_id, trade_id)
@ -171,17 +171,19 @@ def drop_orders_table(engine, table_back_name: str):
connection.execute(text("drop table orders")) connection.execute(text("drop table orders"))
def migrate_orders_table(engine, table_back_name: str, cols: List): def migrate_orders_table(engine, table_back_name: str, cols_order: List):
ft_fee_base = get_column_def(cols_order, 'ft_fee_base', 'null')
# let SQLAlchemy create the schema as required # let SQLAlchemy create the schema as required
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,
order_date, order_filled_date, order_update_date) order_date, order_filled_date, order_update_date, ft_fee_base)
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, null average, remaining, cost, status, symbol, order_type, side, price, amount, filled, null average, remaining, cost,
order_date, order_filled_date, order_update_date order_date, order_filled_date, order_update_date, {ft_fee_base}
from {table_back_name} from {table_back_name}
""")) """))
@ -200,6 +202,7 @@ def check_migrate(engine, decl_base, previous_tables) -> None:
inspector = inspect(engine) inspector = inspect(engine)
cols = inspector.get_columns('trades') cols = inspector.get_columns('trades')
cols_orders = inspector.get_columns('orders')
tabs = get_table_names_for_table(inspector, 'trades') tabs = get_table_names_for_table(inspector, 'trades')
table_back_name = get_backup_name(tabs, 'trades_bak') table_back_name = get_backup_name(tabs, 'trades_bak')
order_tabs = get_table_names_for_table(inspector, 'orders') order_tabs = get_table_names_for_table(inspector, 'orders')
@ -207,14 +210,12 @@ def check_migrate(engine, decl_base, previous_tables) -> None:
# Check if migration necessary # Check if migration necessary
# Migrates both trades and orders table! # Migrates both trades and orders table!
if not has_column(cols, 'buy_tag'): # if not has_column(cols, 'buy_tag'):
if 'orders' not in previous_tables or not has_column(cols_orders, 'ft_fee_base'):
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}")
migrate_trades_and_orders_table( migrate_trades_and_orders_table(
decl_base, inspector, engine, table_back_name, cols, order_table_bak_name) decl_base, inspector, engine, table_back_name, cols, order_table_bak_name, cols_orders)
# Reread columns - the above recreated the table!
inspector = inspect(engine)
cols = inspector.get_columns('trades')
if 'orders' not in previous_tables and 'trades' in previous_tables: if 'orders' not in previous_tables and 'trades' in previous_tables:
logger.info('Moving open orders to Orders table.') logger.info('Moving open orders to Orders table.')

View File

@ -16,7 +16,6 @@ from sqlalchemy.sql.schema import UniqueConstraint
from freqtrade.constants import DATETIME_PRINT_FORMAT, NON_OPEN_EXCHANGE_STATES from freqtrade.constants import DATETIME_PRINT_FORMAT, NON_OPEN_EXCHANGE_STATES
from freqtrade.enums import SellType from freqtrade.enums import SellType
from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.misc import safe_value_fallback
from freqtrade.persistence.migrations import check_migrate from freqtrade.persistence.migrations import check_migrate
@ -116,14 +115,15 @@ class Order(_DECL_BASE):
trade = relationship("Trade", back_populates="orders") trade = relationship("Trade", back_populates="orders")
ft_order_side = Column(String(25), nullable=False) # order_side can only be 'buy', 'sell' or 'stoploss'
ft_pair = Column(String(25), nullable=False) ft_order_side: 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)
order_id = Column(String(255), nullable=False, index=True) order_id = Column(String(255), nullable=False, index=True)
status = Column(String(255), nullable=True) status = Column(String(255), nullable=True)
symbol = Column(String(25), nullable=True) symbol = Column(String(25), nullable=True)
order_type = Column(String(50), nullable=True) order_type: str = Column(String(50), nullable=True)
side = Column(String(25), nullable=True) side = Column(String(25), nullable=True)
price = Column(Float, nullable=True) price = Column(Float, nullable=True)
average = Column(Float, nullable=True) average = Column(Float, nullable=True)
@ -135,10 +135,29 @@ class Order(_DECL_BASE):
order_filled_date = Column(DateTime, nullable=True) order_filled_date = Column(DateTime, nullable=True)
order_update_date = Column(DateTime, nullable=True) order_update_date = Column(DateTime, nullable=True)
ft_fee_base = Column(Float, nullable=True)
@property @property
def order_date_utc(self): def order_date_utc(self) -> datetime:
""" Order-date with UTC timezoneinfo"""
return self.order_date.replace(tzinfo=timezone.utc) return self.order_date.replace(tzinfo=timezone.utc)
@property
def safe_price(self) -> float:
return self.average or self.price
@property
def safe_filled(self) -> float:
return self.filled or self.amount or 0.0
@property
def safe_fee_base(self) -> float:
return self.ft_fee_base or 0.0
@property
def safe_amount_after_fee(self) -> float:
return self.safe_filled - self.safe_fee_base
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}, '
@ -455,40 +474,39 @@ class LocalTrade():
f"Trailing stoploss saved us: " f"Trailing stoploss saved us: "
f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f}.") f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f}.")
def update(self, order: Dict) -> None: def update_trade(self, order: Order) -> None:
""" """
Updates this entity with amount and actual open/close rates. Updates this entity with amount and actual open/close rates.
:param order: order retrieved by exchange.fetch_order() :param order: order retrieved by exchange.fetch_order()
:return: None :return: None
""" """
order_type = order['type']
# 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 order.safe_price is None:
return return
logger.info('Updating trade (id=%s) ...', self.id) logger.info(f'Updating trade (id={self.id}) ...')
if order_type in ('market', 'limit') and order['side'] == 'buy': if order.ft_order_side == 'buy':
# Update open rate and actual amount # Update open rate and actual amount
self.open_rate = float(safe_value_fallback(order, 'average', 'price')) self.open_rate = order.safe_price
self.amount = float(safe_value_fallback(order, 'filled', 'amount')) self.amount = order.safe_amount_after_fee
if self.is_open: if self.is_open:
logger.info(f'{order_type.upper()}_BUY has been fulfilled for {self}.') logger.info(f'{order.order_type.upper()}_BUY has been fulfilled for {self}.')
self.open_order_id = None self.open_order_id = None
self.recalc_trade_from_orders() self.recalc_trade_from_orders()
elif order_type in ('market', 'limit') and order['side'] == 'sell': elif order.ft_order_side == 'sell':
if self.is_open: if self.is_open:
logger.info(f'{order_type.upper()}_SELL has been fulfilled for {self}.') logger.info(f'{order.order_type.upper()}_SELL has been fulfilled for {self}.')
self.close(safe_value_fallback(order, 'average', 'price')) self.close(order.safe_price)
elif order_type in ('stop_loss_limit', 'stop-loss', 'stop-loss-limit', 'stop'): elif order.ft_order_side == 'stoploss':
self.stoploss_order_id = None self.stoploss_order_id = None
self.close_rate_requested = self.stop_loss self.close_rate_requested = self.stop_loss
self.sell_reason = SellType.STOPLOSS_ON_EXCHANGE.value self.sell_reason = SellType.STOPLOSS_ON_EXCHANGE.value
if self.is_open: if self.is_open:
logger.info(f'{order_type.upper()} is hit for {self}.') logger.info(f'{order.order_type.upper()} is hit for {self}.')
self.close(safe_value_fallback(order, 'average', 'price')) self.close(order.safe_price)
else: else:
raise ValueError(f'Unknown order type: {order_type}') raise ValueError(f'Unknown order type: {order.order_type}')
Trade.commit() Trade.commit()
def close(self, rate: float, *, show_msg: bool = True) -> None: def close(self, rate: float, *, show_msg: bool = True) -> None:
@ -631,7 +649,7 @@ class LocalTrade():
(o.status not in NON_OPEN_EXCHANGE_STATES)): (o.status not in NON_OPEN_EXCHANGE_STATES)):
continue continue
tmp_amount = o.amount tmp_amount = o.safe_amount_after_fee
tmp_price = o.average or o.price tmp_price = o.average or o.price
if o.filled is not None: if o.filled is not None:
tmp_amount = o.filled tmp_amount = o.filled

View File

@ -1224,7 +1224,7 @@ def limit_sell_order_open():
'id': 'mocked_limit_sell', 'id': 'mocked_limit_sell',
'type': 'limit', 'type': 'limit',
'side': 'sell', 'side': 'sell',
'pair': 'mocked', 'symbol': 'mocked',
'datetime': arrow.utcnow().isoformat(), 'datetime': arrow.utcnow().isoformat(),
'timestamp': arrow.utcnow().int_timestamp, 'timestamp': arrow.utcnow().int_timestamp,
'price': 0.00001173, 'price': 0.00001173,
@ -2211,7 +2211,7 @@ def limit_sell_order_usdt_open():
'id': 'mocked_limit_sell_usdt', 'id': 'mocked_limit_sell_usdt',
'type': 'limit', 'type': 'limit',
'side': 'sell', 'side': 'sell',
'pair': 'mocked', 'symbol': 'mocked',
'datetime': arrow.utcnow().isoformat(), 'datetime': arrow.utcnow().isoformat(),
'timestamp': arrow.utcnow().int_timestamp, 'timestamp': arrow.utcnow().int_timestamp,
'price': 2.20, 'price': 2.20,

View File

@ -11,6 +11,7 @@ from freqtrade.edge import PairInfo
from freqtrade.enums import State from freqtrade.enums import State
from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.persistence.models import Order
from freqtrade.persistence.pairlock_middleware import PairLocks from freqtrade.persistence.pairlock_middleware import PairLocks
from freqtrade.rpc import RPC, RPCException from freqtrade.rpc import RPC, RPCException
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
@ -277,8 +278,10 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
assert trade assert trade
# Simulate buy & sell # Simulate buy & sell
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update(limit_sell_order) trade.update_trade(oobj)
oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -415,28 +418,32 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
freqtradebot.enter_positions() freqtradebot.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'sell')
trade.update_trade(oobj)
# Update the ticker with a market going up # Update the ticker with a market going up
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
fetch_ticker=ticker_sell_up fetch_ticker=ticker_sell_up
) )
trade.update(limit_sell_order) oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
freqtradebot.enter_positions() freqtradebot.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# Update the ticker with a market going up # Update the ticker with a market going up
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
fetch_ticker=ticker_sell_up fetch_ticker=ticker_sell_up
) )
trade.update(limit_sell_order) oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -495,14 +502,16 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee,
freqtradebot.enter_positions() freqtradebot.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# Update the ticker with a market going up # Update the ticker with a market going up
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
fetch_ticker=ticker_sell_up, fetch_ticker=ticker_sell_up,
get_fee=fee get_fee=fee
) )
trade.update(limit_sell_order) oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -754,13 +763,13 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
mocker.patch( mocker.patch(
'freqtrade.exchange.Exchange.fetch_order', 'freqtrade.exchange.Exchange.fetch_order',
side_effect=[{ side_effect=[{
'id': '1234', 'id': trade.orders[0].order_id,
'status': 'open', 'status': 'open',
'type': 'limit', 'type': 'limit',
'side': 'buy', 'side': 'buy',
'filled': filled_amount 'filled': filled_amount
}, { }, {
'id': '1234', 'id': trade.orders[0].order_id,
'status': 'closed', 'status': 'closed',
'type': 'limit', 'type': 'limit',
'side': 'buy', 'side': 'buy',
@ -840,10 +849,12 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
assert trade assert trade
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order) oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -874,10 +885,12 @@ def test_buy_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
assert trade assert trade
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order) oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -946,10 +959,12 @@ def test_sell_reason_performance_handle(default_conf, ticker, limit_buy_order, f
assert trade assert trade
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order) oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -1018,10 +1033,12 @@ def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
assert trade assert trade
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order) oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False

View File

@ -418,10 +418,12 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
assert trade assert trade
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order) oobjs = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobjs)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -461,8 +463,8 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
trades = Trade.query.all() trades = Trade.query.all()
for trade in trades: for trade in trades:
trade.update(limit_buy_order) trade.update_trade(oobj)
trade.update(limit_sell_order) trade.update_trade(oobjs)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -527,10 +529,12 @@ def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee,
assert trade assert trade
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order) oobjs = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobjs)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -574,8 +578,8 @@ def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee,
trades = Trade.query.all() trades = Trade.query.all()
for trade in trades: for trade in trades:
trade.update(limit_buy_order) trade.update_trade(oobj)
trade.update(limit_sell_order) trade.update_trade(oobjs)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -643,10 +647,12 @@ def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee,
assert trade assert trade
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order) oobjs = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobjs)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -690,8 +696,8 @@ def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee,
trades = Trade.query.all() trades = Trade.query.all()
for trade in trades: for trade in trades:
trade.update(limit_buy_order) trade.update_trade(oobj)
trade.update(limit_sell_order) trade.update_trade(oobjs)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -761,7 +767,9 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
trade = Trade.query.first() trade = Trade.query.first()
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
context = MagicMock() context = MagicMock()
# Test with invalid 2nd argument (should silently pass) # Test with invalid 2nd argument (should silently pass)
context.args = ["aaa"] context.args = ["aaa"]
@ -776,7 +784,9 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
# Update the ticker with a market going up # Update the ticker with a market going up
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', ticker_sell_up) mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', ticker_sell_up)
trade.update(limit_sell_order) # Simulate fulfilled LIMIT_SELL order for trade
oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.now(timezone.utc) trade.close_date = datetime.now(timezone.utc)
trade.is_open = False trade.is_open = False
@ -1286,10 +1296,12 @@ def test_telegram_performance_handle(default_conf, update, ticker, fee,
assert trade assert trade
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order) oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -1313,13 +1325,15 @@ def test_telegram_buy_tag_performance_handle(default_conf, update, ticker, fee,
freqtradebot.enter_positions() freqtradebot.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
assert trade assert trade
trade.buy_tag = "TESTBUY"
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order) oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
trade.buy_tag = "TESTBUY"
# Simulate fulfilled LIMIT_SELL order for trade # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order) oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -1356,13 +1370,14 @@ def test_telegram_sell_reason_performance_handle(default_conf, update, ticker, f
freqtradebot.enter_positions() freqtradebot.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
assert trade assert trade
# Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order)
trade.sell_reason = 'TESTSELL' trade.sell_reason = 'TESTSELL'
# Simulate fulfilled LIMIT_BUY order for trade
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order) oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False
@ -1399,15 +1414,16 @@ def test_telegram_mix_tag_performance_handle(default_conf, update, ticker, fee,
freqtradebot.enter_positions() freqtradebot.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
assert trade assert trade
# Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order)
trade.buy_tag = "TESTBUY" trade.buy_tag = "TESTBUY"
trade.sell_reason = "TESTSELL" trade.sell_reason = "TESTSELL"
# Simulate fulfilled LIMIT_BUY order for trade
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
trade.update_trade(oobj)
# Simulate fulfilled LIMIT_SELL order for trade # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order) oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
trade.update_trade(oobj)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
trade.is_open = False trade.is_open = False

View File

@ -227,7 +227,8 @@ def test_edge_overrides_stoploss(limit_buy_order_usdt, fee, caplog, mocker,
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.enter_positions() freqtrade.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
trade.update_trade(oobj)
############################################# #############################################
# stoploss shoud be hit # stoploss shoud be hit
@ -292,7 +293,8 @@ def test_create_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, fee,
assert trade.exchange == 'binance' assert trade.exchange == 'binance'
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
trade.update_trade(oobj)
assert trade.open_rate == 2.0 assert trade.open_rate == 2.0
assert trade.amount == 30.0 assert trade.amount == 30.0
@ -982,11 +984,17 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog,
trade = Trade.query.first() trade = Trade.query.first()
trade.is_open = True trade.is_open = True
trade.open_order_id = None trade.open_order_id = None
trade.stoploss_order_id = 100 trade.stoploss_order_id = "100"
trade.orders.append(Order(
ft_order_side='stoploss',
order_id='100',
ft_pair=trade.pair,
ft_is_open=True,
))
assert trade assert trade
stoploss_order_hit = MagicMock(return_value={ stoploss_order_hit = MagicMock(return_value={
'id': 100, 'id': "100",
'status': 'closed', 'status': 'closed',
'type': 'stop_loss_limit', 'type': 'stop_loss_limit',
'price': 3, 'price': 3,
@ -1632,9 +1640,9 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_buy_order_usdt, cap
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[]) mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
return_value=limit_buy_order_usdt['amount']) return_value=limit_buy_order_usdt['amount'])
order_id = limit_buy_order_usdt['id']
trade = Trade( trade = Trade(
open_order_id=123, open_order_id=order_id,
fee_open=0.001, fee_open=0.001,
fee_close=0.001, fee_close=0.001,
open_rate=0.01, open_rate=0.01,
@ -1642,29 +1650,35 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_buy_order_usdt, cap
amount=11, amount=11,
exchange="binance", exchange="binance",
) )
trade.orders.append(Order(
ft_order_side='buy',
price=0.01,
order_id=order_id,
))
assert not freqtrade.update_trade_state(trade, None) assert not freqtrade.update_trade_state(trade, None)
assert log_has_re(r'Orderid for trade .* is empty.', caplog) assert log_has_re(r'Orderid for trade .* is empty.', caplog)
caplog.clear() caplog.clear()
# Add datetime explicitly since sqlalchemy defaults apply only once written to database # Add datetime explicitly since sqlalchemy defaults apply only once written to database
freqtrade.update_trade_state(trade, '123') freqtrade.update_trade_state(trade, order_id)
# Test amount not modified by fee-logic # Test amount not modified by fee-logic
assert not log_has_re(r'Applying fee to .*', caplog) assert not log_has_re(r'Applying fee to .*', caplog)
caplog.clear() caplog.clear()
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.amount == limit_buy_order_usdt['amount'] assert trade.amount == limit_buy_order_usdt['amount']
trade.open_order_id = '123' trade.open_order_id = order_id
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
assert trade.amount != 90.81 assert trade.amount != 90.81
# test amount modified by fee-logic # test amount modified by fee-logic
freqtrade.update_trade_state(trade, '123') freqtrade.update_trade_state(trade, order_id)
assert trade.amount == 90.81 assert trade.amount == 90.81
assert trade.open_order_id is None assert trade.open_order_id is None
trade.is_open = True trade.is_open = True
trade.open_order_id = None trade.open_order_id = None
# Assert we call handle_trade() if trade is feasible for execution # Assert we call handle_trade() if trade is feasible for execution
freqtrade.update_trade_state(trade, '123') freqtrade.update_trade_state(trade, order_id)
assert log_has_re('Found open order for.*', caplog) assert log_has_re('Found open order for.*', caplog)
limit_buy_order_usdt_new = deepcopy(limit_buy_order_usdt) limit_buy_order_usdt_new = deepcopy(limit_buy_order_usdt)
@ -1673,7 +1687,7 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_buy_order_usdt, cap
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', side_effect=ValueError) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', side_effect=ValueError)
mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt_new) mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt_new)
res = freqtrade.update_trade_state(trade, '123') res = freqtrade.update_trade_state(trade, order_id)
# Cancelled empty # Cancelled empty
assert res is True assert res is True
@ -1685,6 +1699,8 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_buy_order_usdt, cap
def test_update_trade_state_withorderdict(default_conf_usdt, trades_for_order, limit_buy_order_usdt, def test_update_trade_state_withorderdict(default_conf_usdt, trades_for_order, limit_buy_order_usdt,
fee, mocker, initial_amount, has_rounding_fee, caplog): fee, mocker, initial_amount, has_rounding_fee, caplog):
trades_for_order[0]['amount'] = initial_amount trades_for_order[0]['amount'] = initial_amount
order_id = "oid_123456"
limit_buy_order_usdt['id'] = order_id
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
# fetch_order should not be called!! # fetch_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError)) mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError))
@ -1700,14 +1716,26 @@ def test_update_trade_state_withorderdict(default_conf_usdt, trades_for_order, l
open_date=arrow.utcnow().datetime, open_date=arrow.utcnow().datetime,
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
open_order_id="123456", open_order_id=order_id,
is_open=True, is_open=True,
) )
freqtrade.update_trade_state(trade, '123456', limit_buy_order_usdt) trade.orders.append(
Order(
ft_order_side='buy',
ft_pair=trade.pair,
ft_is_open=True,
order_id=order_id,
)
)
freqtrade.update_trade_state(trade, order_id, limit_buy_order_usdt)
assert trade.amount != amount assert trade.amount != amount
assert trade.amount == limit_buy_order_usdt['amount'] log_text = r'Applying fee on amount for .*'
if has_rounding_fee: if has_rounding_fee:
assert log_has_re(r'Applying fee on amount for .*', caplog) assert pytest.approx(trade.amount) == 29.992
assert log_has_re(log_text, caplog)
else:
assert pytest.approx(trade.amount) == limit_buy_order_usdt['amount']
assert not log_has_re(log_text, caplog)
def test_update_trade_state_exception(mocker, default_conf_usdt, def test_update_trade_state_exception(mocker, default_conf_usdt,
@ -1762,7 +1790,7 @@ def test_update_trade_state_sell(default_conf_usdt, trades_for_order, limit_sell
fee_open=0.0025, fee_open=0.0025,
fee_close=0.0025, fee_close=0.0025,
open_date=arrow.utcnow().datetime, open_date=arrow.utcnow().datetime,
open_order_id="123456", open_order_id=limit_sell_order_usdt_open['id'],
is_open=True, is_open=True,
) )
order = Order.parse_from_ccxt_object(limit_sell_order_usdt_open, 'LTC/ETH', 'sell') order = Order.parse_from_ccxt_object(limit_sell_order_usdt_open, 'LTC/ETH', 'sell')
@ -1803,7 +1831,8 @@ def test_handle_trade(default_conf_usdt, limit_buy_order_usdt, limit_sell_order_
assert trade assert trade
time.sleep(0.01) # Race condition fix time.sleep(0.01) # Race condition fix
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, limit_buy_order_usdt['symbol'], 'buy')
trade.update_trade(oobj)
assert trade.is_open is True assert trade.is_open is True
freqtrade.wallets.update() freqtrade.wallets.update()
@ -1812,7 +1841,9 @@ def test_handle_trade(default_conf_usdt, limit_buy_order_usdt, limit_sell_order_
assert trade.open_order_id == limit_sell_order_usdt['id'] assert trade.open_order_id == limit_sell_order_usdt['id']
# Simulate fulfilled LIMIT_SELL order for trade # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order_usdt) oobj = Order.parse_from_ccxt_object(
limit_sell_order_usdt, limit_sell_order_usdt['symbol'], 'sell')
trade.update_trade(oobj)
assert trade.close_rate == 2.2 assert trade.close_rate == 2.2
assert trade.close_profit == 0.09451372 assert trade.close_profit == 0.09451372
@ -1962,8 +1993,11 @@ def test_close_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt,
trade = Trade.query.first() trade = Trade.query.first()
assert trade assert trade
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, limit_buy_order_usdt['symbol'], 'buy')
trade.update(limit_sell_order_usdt) trade.update_trade(oobj)
oobj = Order.parse_from_ccxt_object(
limit_sell_order_usdt, limit_sell_order_usdt['symbol'], 'sell')
trade.update_trade(oobj)
assert trade.is_open is False assert trade.is_open is False
with pytest.raises(DependencyException, match=r'.*closed trade.*'): with pytest.raises(DependencyException, match=r'.*closed trade.*'):
@ -1986,7 +2020,7 @@ def test_bot_loop_start_called_once(mocker, default_conf_usdt, caplog):
def test_check_handle_timedout_buy_usercustom(default_conf_usdt, ticker_usdt, limit_buy_order_old, def test_check_handle_timedout_buy_usercustom(default_conf_usdt, ticker_usdt, limit_buy_order_old,
open_trade, fee, mocker) -> None: open_trade, fee, mocker) -> None:
default_conf_usdt["unfilledtimeout"] = {"buy": 1400, "sell": 30} default_conf_usdt["unfilledtimeout"] = {"buy": 1400, "sell": 30}
limit_buy_order_old['id'] = open_trade.open_order_id
rpc_mock = patch_RPCManager(mocker) rpc_mock = patch_RPCManager(mocker)
cancel_order_mock = MagicMock(return_value=limit_buy_order_old) cancel_order_mock = MagicMock(return_value=limit_buy_order_old)
cancel_buy_order = deepcopy(limit_buy_order_old) cancel_buy_order = deepcopy(limit_buy_order_old)
@ -2289,6 +2323,7 @@ def test_check_handle_timedout_partial_fee(default_conf_usdt, ticker_usdt, open_
limit_buy_order_old_partial_canceled, mocker) -> None: limit_buy_order_old_partial_canceled, mocker) -> None:
rpc_mock = patch_RPCManager(mocker) rpc_mock = patch_RPCManager(mocker)
limit_buy_order_old_partial['id'] = open_trade.open_order_id limit_buy_order_old_partial['id'] = open_trade.open_order_id
limit_buy_order_old_partial_canceled['id'] = open_trade.open_order_id
cancel_order_mock = MagicMock(return_value=limit_buy_order_old_partial_canceled) cancel_order_mock = MagicMock(return_value=limit_buy_order_old_partial_canceled)
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=0)) mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=0))
patch_exchange(mocker) patch_exchange(mocker)
@ -3103,7 +3138,8 @@ def test_sell_profit_only(
freqtrade.enter_positions() freqtrade.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, limit_buy_order_usdt['symbol'], 'buy')
trade.update_trade(oobj)
freqtrade.wallets.update() freqtrade.wallets.update()
patch_get_signal(freqtrade, value=(False, True, None, None)) patch_get_signal(freqtrade, value=(False, True, None, None))
assert freqtrade.handle_trade(trade) is handle_first assert freqtrade.handle_trade(trade) is handle_first
@ -3139,7 +3175,9 @@ def test_sell_not_enough_balance(default_conf_usdt, limit_buy_order_usdt, limit_
trade = Trade.query.first() trade = Trade.query.first()
amnt = trade.amount amnt = trade.amount
trade.update(limit_buy_order_usdt)
oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, limit_buy_order_usdt['symbol'], 'buy')
trade.update_trade(oobj)
patch_get_signal(freqtrade, value=(False, True, None, None)) patch_get_signal(freqtrade, value=(False, True, None, None))
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985)) mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985))
@ -3247,7 +3285,8 @@ def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usdt,
freqtrade.enter_positions() freqtrade.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, limit_buy_order_usdt['symbol'], 'buy')
trade.update_trade(oobj)
freqtrade.wallets.update() freqtrade.wallets.update()
patch_get_signal(freqtrade, value=(True, True, None, None)) patch_get_signal(freqtrade, value=(True, True, None, None))
assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_trade(trade) is False
@ -3350,7 +3389,8 @@ def test_trailing_stop_loss_positive(
freqtrade.enter_positions() freqtrade.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, limit_buy_order_usdt['symbol'], 'buy')
trade.update_trade(oobj)
caplog.set_level(logging.DEBUG) caplog.set_level(logging.DEBUG)
# stop-loss not reached # stop-loss not reached
assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_trade(trade) is False
@ -3437,7 +3477,8 @@ def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usd
freqtrade.enter_positions() freqtrade.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, limit_buy_order_usdt['symbol'], 'buy')
trade.update_trade(oobj)
# Sell due to min_roi_reached # Sell due to min_roi_reached
patch_get_signal(freqtrade, value=(True, False, None, None)) patch_get_signal(freqtrade, value=(True, False, None, None))
assert freqtrade.handle_trade(trade) is True assert freqtrade.handle_trade(trade) is True
@ -3812,7 +3853,8 @@ def test_order_book_depth_of_market(
assert len(Trade.query.all()) == 1 assert len(Trade.query.all()) == 1
# Simulate fulfilled LIMIT_BUY order for trade # Simulate fulfilled LIMIT_BUY order for trade
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
trade.update_trade(oobj)
assert trade.open_rate == 2.0 assert trade.open_rate == 2.0
assert whitelist == default_conf_usdt['exchange']['pair_whitelist'] assert whitelist == default_conf_usdt['exchange']['pair_whitelist']
@ -3906,7 +3948,8 @@ def test_order_book_ask_strategy(
assert trade assert trade
time.sleep(0.01) # Race condition fix time.sleep(0.01) # Race condition fix
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, limit_buy_order_usdt['symbol'], 'buy')
trade.update_trade(oobj)
freqtrade.wallets.update() freqtrade.wallets.update()
assert trade.is_open is True assert trade.is_open is True

View File

@ -4,6 +4,7 @@ import pytest
from freqtrade.enums import SellType from freqtrade.enums import SellType
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.persistence.models import Order
from freqtrade.rpc.rpc import RPC from freqtrade.rpc.rpc import RPC
from freqtrade.strategy.interface import SellCheckTuple from freqtrade.strategy.interface import SellCheckTuple
from tests.conftest import get_patched_freqtradebot, patch_get_signal from tests.conftest import get_patched_freqtradebot, patch_get_signal
@ -94,7 +95,11 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
trades = Trade.query.all() trades = Trade.query.all()
# Make sure stoploss-order is open and trade is bought (since we mock update_trade_state) # Make sure stoploss-order is open and trade is bought (since we mock update_trade_state)
for trade in trades: for trade in trades:
trade.stoploss_order_id = 3 stoploss_order_closed['id'] = '3'
oobj = Order.parse_from_ccxt_object(stoploss_order_closed, trade.pair, 'stoploss')
trade.orders.append(oobj)
trade.stoploss_order_id = '3'
trade.open_order_id = None trade.open_order_id = None
n = freqtrade.exit_positions(trades) n = freqtrade.exit_positions(trades)

View File

@ -112,7 +112,8 @@ def test_update_limit_order(limit_buy_order_usdt, limit_sell_order_usdt, fee, ca
assert trade.close_date is None assert trade.close_date is None
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
trade.update_trade(oobj)
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.open_rate == 2.00 assert trade.open_rate == 2.00
assert trade.close_profit is None assert trade.close_profit is None
@ -123,7 +124,8 @@ def test_update_limit_order(limit_buy_order_usdt, limit_sell_order_usdt, fee, ca
caplog.clear() caplog.clear()
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(limit_sell_order_usdt) oobj = Order.parse_from_ccxt_object(limit_sell_order_usdt, 'ADA/USDT', 'sell')
trade.update_trade(oobj)
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.close_rate == 2.20 assert trade.close_rate == 2.20
assert trade.close_profit == round(0.0945137157107232, 8) assert trade.close_profit == round(0.0945137157107232, 8)
@ -150,7 +152,8 @@ def test_update_market_order(market_buy_order_usdt, market_sell_order_usdt, fee,
) )
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(market_buy_order_usdt) oobj = Order.parse_from_ccxt_object(market_buy_order_usdt, 'ADA/USDT', 'buy')
trade.update_trade(oobj)
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.open_rate == 2.0 assert trade.open_rate == 2.0
assert trade.close_profit is None assert trade.close_profit is None
@ -162,7 +165,8 @@ def test_update_market_order(market_buy_order_usdt, market_sell_order_usdt, fee,
caplog.clear() caplog.clear()
trade.is_open = True trade.is_open = True
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(market_sell_order_usdt) oobj = Order.parse_from_ccxt_object(market_sell_order_usdt, 'ADA/USDT', 'sell')
trade.update_trade(oobj)
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.close_rate == 2.2 assert trade.close_rate == 2.2
assert trade.close_profit == round(0.0945137157107232, 8) assert trade.close_profit == round(0.0945137157107232, 8)
@ -185,9 +189,11 @@ def test_calc_open_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt
) )
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
trade.update_trade(oobj)
assert trade._calc_open_trade_value() == 60.15 assert trade._calc_open_trade_value() == 60.15
trade.update(limit_sell_order_usdt) oobj = Order.parse_from_ccxt_object(limit_sell_order_usdt, 'ADA/USDT', 'sell')
trade.update_trade(oobj)
assert isclose(trade.calc_close_trade_value(), 65.835) assert isclose(trade.calc_close_trade_value(), 65.835)
# Profit in USDT # Profit in USDT
@ -240,7 +246,8 @@ def test_calc_close_trade_price_exception(limit_buy_order_usdt, fee):
) )
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
trade.update_trade(oobj)
assert trade.calc_close_trade_value() == 0.0 assert trade.calc_close_trade_value() == 0.0
@ -261,7 +268,8 @@ def test_update_open_order(limit_buy_order_usdt):
assert trade.close_date is None assert trade.close_date is None
limit_buy_order_usdt['status'] = 'open' limit_buy_order_usdt['status'] = 'open'
trade.update(limit_buy_order_usdt) oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
trade.update_trade(oobj)
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
@ -280,8 +288,9 @@ def test_update_invalid_order(limit_buy_order_usdt):
exchange='binance', exchange='binance',
) )
limit_buy_order_usdt['type'] = 'invalid' limit_buy_order_usdt['type'] = 'invalid'
oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'meep')
with pytest.raises(ValueError, match=r'Unknown order type'): with pytest.raises(ValueError, match=r'Unknown order type'):
trade.update(limit_buy_order_usdt) trade.update_trade(oobj)
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
@ -308,7 +317,8 @@ def test_calc_open_trade_value(limit_buy_order_usdt, fee):
exchange='binance', exchange='binance',
) )
trade.open_order_id = 'open_trade' trade.open_order_id = 'open_trade'
trade.update(limit_buy_order_usdt) # Buy @ 2.0 oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
trade.update_trade(oobj) # Buy @ 2.0
# 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_value() == 60.15 assert trade._calc_open_trade_value() == 60.15
@ -329,14 +339,16 @@ def test_calc_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt, fee
exchange='binance', exchange='binance',
) )
trade.open_order_id = 'close_trade' trade.open_order_id = 'close_trade'
trade.update(limit_buy_order_usdt) # Buy @ 2.0 oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
trade.update_trade(oobj) # Buy @ 2.0
# 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_value(rate=2.5) == 74.8125 assert trade.calc_close_trade_value(rate=2.5) == 74.8125
# 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_value(rate=2.5, fee=0.003) == 74.775 assert trade.calc_close_trade_value(rate=2.5, fee=0.003) == 74.775
# 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_usdt) oobj = Order.parse_from_ccxt_object(limit_sell_order_usdt, 'ADA/USDT', 'sell')
trade.update_trade(oobj)
assert trade.calc_close_trade_value(fee=0.005) == 65.67 assert trade.calc_close_trade_value(fee=0.005) == 65.67
@ -413,7 +425,9 @@ def test_calc_profit(limit_buy_order_usdt, limit_sell_order_usdt, fee):
exchange='binance', exchange='binance',
) )
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(limit_buy_order_usdt) # Buy @ 2.0 oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
trade.update_trade(oobj) # Buy @ 2.0
# Custom closing rate and regular fee rate # Custom closing rate and regular fee rate
# Higher than open rate - 2.1 quote # Higher than open rate - 2.1 quote
@ -428,7 +442,8 @@ def test_calc_profit(limit_buy_order_usdt, limit_sell_order_usdt, fee):
assert trade.calc_profit(rate=1.9, fee=0.003) == round(-3.320999999999998, 8) assert trade.calc_profit(rate=1.9, fee=0.003) == round(-3.320999999999998, 8)
# Test when we apply a Sell order. Sell higher than open rate @ 2.2 # Test when we apply a Sell order. Sell higher than open rate @ 2.2
trade.update(limit_sell_order_usdt) oobj = Order.parse_from_ccxt_object(limit_sell_order_usdt, 'ADA/USDT', 'sell')
trade.update_trade(oobj)
assert trade.calc_profit() == round(5.684999999999995, 8) assert trade.calc_profit() == round(5.684999999999995, 8)
# Test with a custom fee rate on the close trade # Test with a custom fee rate on the close trade
@ -447,7 +462,9 @@ def test_calc_profit_ratio(limit_buy_order_usdt, limit_sell_order_usdt, fee):
exchange='binance' exchange='binance'
) )
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(limit_buy_order_usdt) # Buy @ 2.0
oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
trade.update_trade(oobj) # Buy @ 2.0
# Higher than open rate - 2.1 quote # Higher than open rate - 2.1 quote
assert trade.calc_profit_ratio(rate=2.1) == round(0.04476309226932673, 8) assert trade.calc_profit_ratio(rate=2.1) == round(0.04476309226932673, 8)
@ -461,7 +478,8 @@ def test_calc_profit_ratio(limit_buy_order_usdt, limit_sell_order_usdt, fee):
assert trade.calc_profit_ratio(rate=1.9, fee=0.003) == round(-0.05521197007481293, 8) assert trade.calc_profit_ratio(rate=1.9, fee=0.003) == round(-0.05521197007481293, 8)
# Test when we apply a Sell order. Sell higher than open rate @ 2.2 # Test when we apply a Sell order. Sell higher than open rate @ 2.2
trade.update(limit_sell_order_usdt) oobj = Order.parse_from_ccxt_object(limit_sell_order_usdt, 'ADA/USDT', 'sell')
trade.update_trade(oobj)
assert trade.calc_profit_ratio() == round(0.0945137157107232, 8) assert trade.calc_profit_ratio() == round(0.0945137157107232, 8)
# Test with a custom fee rate on the close trade # Test with a custom fee rate on the close trade