Merge pull request #6437 from freqtrade/update_trade
Migrate trade updating to use order model
This commit is contained in:
commit
53452c8d64
@ -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:
|
||||||
"""
|
"""
|
||||||
|
@ -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.')
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user