Merge pull request #3597 from freqtrade/fix/3579

consistently use filled before amount from orders
This commit is contained in:
Matthias 2020-08-12 19:56:57 +02:00 committed by GitHub
commit 5d61c56650
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 107 additions and 56 deletions

View File

@ -24,7 +24,7 @@ from freqtrade.exceptions import (DDosProtection, ExchangeError,
InvalidOrderException, OperationalException,
RetryableOrderError, TemporaryError)
from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async
from freqtrade.misc import deep_merge_dicts, safe_value_fallback
from freqtrade.misc import deep_merge_dicts, safe_value_fallback2
CcxtModuleType = Any
@ -480,6 +480,7 @@ class Exchange:
"id": order_id,
'pair': pair,
'price': rate,
'average': rate,
'amount': _amount,
'cost': _amount * rate,
'type': ordertype,
@ -1144,7 +1145,7 @@ class Exchange:
if fee_curr in self.get_pair_base_currency(order['symbol']):
# Base currency - divide by amount
return round(
order['fee']['cost'] / safe_value_fallback(order, order, 'filled', 'amount'), 8)
order['fee']['cost'] / safe_value_fallback2(order, order, 'filled', 'amount'), 8)
elif fee_curr in self.get_pair_quote_currency(order['symbol']):
# Quote currency - divide by cost
return round(order['fee']['cost'] / order['cost'], 8) if order['cost'] else None
@ -1157,7 +1158,7 @@ class Exchange:
comb = self.get_valid_pair_combination(fee_curr, self._config['stake_currency'])
tick = self.fetch_ticker(comb)
fee_to_quote_rate = safe_value_fallback(tick, tick, 'last', 'ask')
fee_to_quote_rate = safe_value_fallback2(tick, tick, 'last', 'ask')
return round((order['fee']['cost'] * fee_to_quote_rate) / order['cost'], 8)
except ExchangeError:
return None
@ -1172,7 +1173,6 @@ class Exchange:
return (order['fee']['cost'],
order['fee']['currency'],
self.calculate_fee_rate(order))
# calculate rate ? (order['fee']['cost'] / (order['amount'] * order['price']))
def is_exchange_bad(exchange_name: str) -> bool:

View File

@ -20,7 +20,7 @@ from freqtrade.edge import Edge
from freqtrade.exceptions import (DependencyException, ExchangeError,
InvalidOrderException, PricingError)
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date
from freqtrade.misc import safe_value_fallback
from freqtrade.misc import safe_value_fallback, safe_value_fallback2
from freqtrade.pairlist.pairlistmanager import PairListManager
from freqtrade.persistence import Trade
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
@ -523,7 +523,7 @@ class FreqtradeBot:
time_in_force=time_in_force):
logger.info(f"User requested abortion of buying {pair}")
return False
amount = self.exchange.amount_to_precision(pair, amount)
order = self.exchange.buy(pair=pair, ordertype=order_type,
amount=amount, rate=buy_limit_requested,
time_in_force=time_in_force)
@ -532,6 +532,7 @@ class FreqtradeBot:
# we assume the order is executed at the price requested
buy_limit_filled_price = buy_limit_requested
amount_requested = amount
if order_status == 'expired' or order_status == 'rejected':
order_tif = self.strategy.order_time_in_force['buy']
@ -552,15 +553,15 @@ class FreqtradeBot:
order['filled'], order['amount'], order['remaining']
)
stake_amount = order['cost']
amount = order['amount']
buy_limit_filled_price = order['price']
amount = safe_value_fallback(order, 'filled', 'amount')
buy_limit_filled_price = safe_value_fallback(order, 'average', 'price')
order_id = None
# in case of FOK the order may be filled immediately and fully
elif order_status == 'closed':
stake_amount = order['cost']
amount = order['amount']
buy_limit_filled_price = order['price']
amount = safe_value_fallback(order, 'filled', 'amount')
buy_limit_filled_price = safe_value_fallback(order, 'average', 'price')
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
@ -568,6 +569,7 @@ class FreqtradeBot:
pair=pair,
stake_amount=stake_amount,
amount=amount,
amount_requested=amount_requested,
fee_open=fee,
fee_close=fee,
open_rate=buy_limit_filled_price,
@ -990,7 +992,7 @@ class FreqtradeBot:
logger.info('Buy order %s for %s.', reason, trade)
# Using filled to determine the filled amount
filled_amount = safe_value_fallback(corder, order, 'filled', 'filled')
filled_amount = safe_value_fallback2(corder, order, 'filled', 'filled')
if isclose(filled_amount, 0.0, abs_tol=constants.MATH_CLOSE_PREC):
logger.info('Buy order fully cancelled. Removing %s from database.', trade)
@ -1255,7 +1257,8 @@ class FreqtradeBot:
# Try update amount (binance-fix)
try:
new_amount = self.get_real_amount(trade, order, order_amount)
if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC):
if not isclose(safe_value_fallback(order, 'filled', 'amount'), new_amount,
abs_tol=constants.MATH_CLOSE_PREC):
order['amount'] = new_amount
order.pop('filled', None)
trade.recalc_open_trade_price()
@ -1301,7 +1304,7 @@ class FreqtradeBot:
"""
# Init variables
if order_amount is None:
order_amount = order['amount']
order_amount = safe_value_fallback(order, 'filled', 'amount')
# Only run for closed orders
if trade.fee_updated(order.get('side', '')) or order['status'] == 'open':
return order_amount

View File

@ -134,7 +134,21 @@ def round_dict(d, n):
return {k: (round(v, n) if isinstance(v, float) else v) for k, v in d.items()}
def safe_value_fallback(dict1: dict, dict2: dict, key1: str, key2: str, default_value=None):
def safe_value_fallback(obj: dict, key1: str, key2: str, default_value=None):
"""
Search a value in obj, return this if it's not None.
Then search key2 in obj - return that if it's not none - then use default_value.
Else falls back to None.
"""
if key1 in obj and obj[key1] is not None:
return obj[key1]
else:
if key2 in obj and obj[key2] is not None:
return obj[key2]
return default_value
def safe_value_fallback2(dict1: dict, dict2: dict, key1: str, key2: str, default_value=None):
"""
Search a value in dict1, return this if it's not None.
Fall back to dict2 - return key2 from dict2 if it's not None.

View File

@ -17,6 +17,7 @@ from sqlalchemy.orm.session import sessionmaker
from sqlalchemy.pool import StaticPool
from freqtrade.exceptions import OperationalException
from freqtrade.misc import safe_value_fallback
logger = logging.getLogger(__name__)
@ -86,7 +87,7 @@ def check_migrate(engine) -> None:
logger.debug(f'trying {table_back_name}')
# Check for latest column
if not has_column(cols, 'timeframe'):
if not has_column(cols, 'amount_requested'):
logger.info(f'Running database migration - backup available as {table_back_name}')
fee_open = get_column_def(cols, 'fee_open', 'fee')
@ -119,6 +120,7 @@ def check_migrate(engine) -> None:
cols, 'close_profit_abs',
f"(amount * close_rate * (1 - {fee_close})) - {open_trade_price}")
sell_order_status = get_column_def(cols, 'sell_order_status', 'null')
amount_requested = get_column_def(cols, 'amount_requested', 'amount')
# Schema migration necessary
engine.execute(f"alter table trades rename to {table_back_name}")
@ -134,7 +136,7 @@ def check_migrate(engine) -> None:
fee_open, fee_open_cost, fee_open_currency,
fee_close, fee_close_cost, fee_open_currency, open_rate,
open_rate_requested, close_rate, close_rate_requested, close_profit,
stake_amount, amount, open_date, close_date, open_order_id,
stake_amount, amount, amount_requested, open_date, close_date, open_order_id,
stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct,
stoploss_order_id, stoploss_last_update,
max_rate, min_rate, sell_reason, sell_order_status, strategy,
@ -153,7 +155,7 @@ def check_migrate(engine) -> None:
{fee_close_cost} fee_close_cost, {fee_close_currency} fee_close_currency,
open_rate, {open_rate_requested} open_rate_requested, close_rate,
{close_rate_requested} close_rate_requested, close_profit,
stake_amount, amount, open_date, close_date, open_order_id,
stake_amount, amount, {amount_requested}, open_date, close_date, open_order_id,
{stop_loss} stop_loss, {stop_loss_pct} stop_loss_pct,
{initial_stop_loss} initial_stop_loss,
{initial_stop_loss_pct} initial_stop_loss_pct,
@ -215,6 +217,7 @@ class Trade(_DECL_BASE):
close_profit_abs = Column(Float)
stake_amount = Column(Float, nullable=False)
amount = Column(Float)
amount_requested = Column(Float)
open_date = Column(DateTime, nullable=False, default=datetime.utcnow)
close_date = Column(DateTime)
open_order_id = Column(String)
@ -256,6 +259,7 @@ class Trade(_DECL_BASE):
'is_open': self.is_open,
'exchange': self.exchange,
'amount': round(self.amount, 8),
'amount_requested': round(self.amount_requested, 8) if self.amount_requested else None,
'stake_amount': round(self.stake_amount, 8),
'strategy': self.strategy,
'ticker_interval': self.timeframe, # DEPRECATED
@ -273,7 +277,7 @@ class Trade(_DECL_BASE):
'open_timestamp': int(self.open_date.timestamp() * 1000),
'open_rate': self.open_rate,
'open_rate_requested': self.open_rate_requested,
'open_trade_price': self.open_trade_price,
'open_trade_price': round(self.open_trade_price, 8),
'close_date_hum': (arrow.get(self.close_date).humanize()
if self.close_date else None),
@ -365,20 +369,20 @@ class Trade(_DECL_BASE):
"""
order_type = order['type']
# Ignore open and cancelled orders
if order['status'] == 'open' or order['price'] is None:
if order['status'] == 'open' or safe_value_fallback(order, 'average', 'price') is None:
return
logger.info('Updating trade (id=%s) ...', self.id)
if order_type in ('market', 'limit') and order['side'] == 'buy':
# Update open rate and actual amount
self.open_rate = Decimal(order['price'])
self.amount = Decimal(order.get('filled', order['amount']))
self.open_rate = Decimal(safe_value_fallback(order, 'average', 'price'))
self.amount = Decimal(safe_value_fallback(order, 'filled', 'amount'))
self.recalc_open_trade_price()
logger.info('%s_BUY has been fulfilled for %s.', order_type.upper(), self)
self.open_order_id = None
elif order_type in ('market', 'limit') and order['side'] == 'sell':
self.close(order['price'])
self.close(safe_value_fallback(order, 'average', 'price'))
logger.info('%s_SELL has been fulfilled for %s.', order_type.upper(), self)
elif order_type in ('stop_loss_limit', 'stop-loss', 'stop'):
self.stoploss_order_id = None

View File

@ -176,6 +176,7 @@ def create_mock_trades(fee):
pair='ETH/BTC',
stake_amount=0.001,
amount=123.0,
amount_requested=123.0,
fee_open=fee.return_value,
fee_close=fee.return_value,
open_rate=0.123,
@ -188,6 +189,7 @@ def create_mock_trades(fee):
pair='ETC/BTC',
stake_amount=0.001,
amount=123.0,
amount_requested=123.0,
fee_open=fee.return_value,
fee_close=fee.return_value,
open_rate=0.123,
@ -218,6 +220,7 @@ def create_mock_trades(fee):
pair='ETC/BTC',
stake_amount=0.001,
amount=123.0,
amount_requested=124.0,
fee_open=fee.return_value,
fee_close=fee.return_value,
open_rate=0.123,

View File

@ -79,7 +79,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'open_rate': 1.098e-05,
'close_rate': None,
'current_rate': 1.099e-05,
'amount': 91.07468124,
'amount': 91.07468123,
'amount_requested': 91.07468123,
'stake_amount': 0.001,
'close_profit': None,
'close_profit_pct': None,
@ -142,7 +143,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'open_rate': 1.098e-05,
'close_rate': None,
'current_rate': ANY,
'amount': 91.07468124,
'amount': 91.07468123,
'amount_requested': 91.07468123,
'stake_amount': 0.001,
'close_profit': None,
'close_profit_pct': None,

View File

@ -566,7 +566,8 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
rc = client_get(client, f"{BASE_URI}/status")
assert_response(rc)
assert len(rc.json) == 1
assert rc.json == [{'amount': 91.07468124,
assert rc.json == [{'amount': 91.07468123,
'amount_requested': 91.07468123,
'base_currency': 'BTC',
'close_date': None,
'close_date_hum': None,
@ -688,6 +689,7 @@ def test_api_forcebuy(botclient, mocker, fee):
fbuy_mock = MagicMock(return_value=Trade(
pair='ETH/ETH',
amount=1,
amount_requested=1,
exchange='bittrex',
stake_amount=1,
open_rate=0.245441,
@ -704,6 +706,7 @@ def test_api_forcebuy(botclient, mocker, fee):
data='{"pair": "ETH/BTC"}')
assert_response(rc)
assert rc.json == {'amount': 1,
'amount_requested': 1,
'trade_id': None,
'close_date': None,
'close_date_hum': None,
@ -740,7 +743,7 @@ def test_api_forcebuy(botclient, mocker, fee):
'min_rate': None,
'open_order_id': '123456',
'open_rate_requested': None,
'open_trade_price': 0.2460546025,
'open_trade_price': 0.24605460,
'sell_reason': None,
'sell_order_status': None,
'strategy': None,

View File

@ -691,8 +691,8 @@ def test_reload_config_handle(default_conf, update, mocker) -> None:
assert 'reloading config' in msg_mock.call_args_list[0][0][0]
def test_forcesell_handle(default_conf, update, ticker, fee,
ticker_sell_up, mocker) -> None:
def test_telegram_forcesell_handle(default_conf, update, ticker, fee,
ticker_sell_up, mocker) -> None:
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
@ -731,7 +731,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
'pair': 'ETH/BTC',
'gain': 'profit',
'limit': 1.173e-05,
'amount': 91.07468123861567,
'amount': 91.07468123,
'order_type': 'limit',
'open_rate': 1.098e-05,
'current_rate': 1.173e-05,
@ -745,8 +745,8 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
} == last_msg
def test_forcesell_down_handle(default_conf, update, ticker, fee,
ticker_sell_down, mocker) -> None:
def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee,
ticker_sell_down, mocker) -> None:
mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
return_value=15000.0)
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
@ -791,7 +791,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
'pair': 'ETH/BTC',
'gain': 'loss',
'limit': 1.043e-05,
'amount': 91.07468123861567,
'amount': 91.07468123,
'order_type': 'limit',
'open_rate': 1.098e-05,
'current_rate': 1.043e-05,
@ -840,7 +840,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
'pair': 'ETH/BTC',
'gain': 'loss',
'limit': 1.099e-05,
'amount': 91.07468123861567,
'amount': 91.07468123,
'order_type': 'limit',
'open_rate': 1.098e-05,
'current_rate': 1.099e-05,

View File

@ -595,7 +595,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order,
freqtrade.create_trade('ETH/BTC')
rate, amount = buy_mock.call_args[1]['rate'], buy_mock.call_args[1]['amount']
assert rate * amount >= default_conf['stake_amount']
assert rate * amount <= default_conf['stake_amount']
def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_order,
@ -782,7 +782,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
assert trade.open_date is not None
assert trade.exchange == 'bittrex'
assert trade.open_rate == 0.00001098
assert trade.amount == 91.07468123861567
assert trade.amount == 91.07468123
assert log_has(
'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog
@ -1009,7 +1009,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
call_args = buy_mm.call_args_list[0][1]
assert call_args['pair'] == pair
assert call_args['rate'] == bid
assert call_args['amount'] == stake_amount / bid
assert call_args['amount'] == round(stake_amount / bid, 8)
buy_rate_mock.reset_mock()
# Should create an open trade with an open order id
@ -1029,7 +1029,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
call_args = buy_mm.call_args_list[1][1]
assert call_args['pair'] == pair
assert call_args['rate'] == fix_price
assert call_args['amount'] == stake_amount / fix_price
assert call_args['amount'] == round(stake_amount / fix_price, 8)
# In case of closed order
limit_buy_order['status'] = 'closed'
@ -1407,7 +1407,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
assert freqtrade.handle_stoploss_on_exchange(trade) is False
cancel_order_mock.assert_called_once_with(100, 'ETH/BTC')
stoploss_order_mock.assert_called_once_with(amount=85.32423208191126,
stoploss_order_mock.assert_called_once_with(amount=85.32423208,
pair='ETH/BTC',
order_types=freqtrade.strategy.order_types,
stop_price=0.00002346 * 0.95)
@ -1595,7 +1595,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
# stoploss should be set to 1% as trailing is on
assert trade.stop_loss == 0.00002346 * 0.99
cancel_order_mock.assert_called_once_with(100, 'NEO/BTC')
stoploss_order_mock.assert_called_once_with(amount=2132892.491467577,
stoploss_order_mock.assert_called_once_with(amount=2132892.49146757,
pair='NEO/BTC',
order_types=freqtrade.strategy.order_types,
stop_price=0.00002346 * 0.99)
@ -2598,7 +2598,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N
'pair': 'ETH/BTC',
'gain': 'profit',
'limit': 1.172e-05,
'amount': 91.07468123861567,
'amount': 91.07468123,
'order_type': 'limit',
'open_rate': 1.098e-05,
'current_rate': 1.173e-05,
@ -2648,7 +2648,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker)
'pair': 'ETH/BTC',
'gain': 'loss',
'limit': 1.044e-05,
'amount': 91.07468123861567,
'amount': 91.07468123,
'order_type': 'limit',
'open_rate': 1.098e-05,
'current_rate': 1.043e-05,
@ -2705,7 +2705,7 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
'pair': 'ETH/BTC',
'gain': 'loss',
'limit': 1.08801e-05,
'amount': 91.07468123861567,
'amount': 91.07468123,
'order_type': 'limit',
'open_rate': 1.098e-05,
'current_rate': 1.043e-05,
@ -2911,7 +2911,7 @@ def test_execute_sell_market_order(default_conf, ticker, fee,
'pair': 'ETH/BTC',
'gain': 'profit',
'limit': 1.172e-05,
'amount': 91.07468123861567,
'amount': 91.07468123,
'order_type': 'market',
'open_rate': 1.098e-05,
'current_rate': 1.173e-05,

View File

@ -11,7 +11,7 @@ from freqtrade.misc import (datesarray_to_datetimearray, file_dump_json,
file_load_json, format_ms_time, pair_to_filename,
plural, render_template,
render_template_with_fallback, safe_value_fallback,
shorten_date)
safe_value_fallback2, shorten_date)
def test_shorten_date() -> None:
@ -96,24 +96,40 @@ def test_format_ms_time() -> None:
def test_safe_value_fallback():
dict1 = {'keya': None, 'keyb': 2, 'keyc': 5, 'keyd': None}
assert safe_value_fallback(dict1, 'keya', 'keyb') == 2
assert safe_value_fallback(dict1, 'keyb', 'keya') == 2
assert safe_value_fallback(dict1, 'keyb', 'keyc') == 2
assert safe_value_fallback(dict1, 'keya', 'keyc') == 5
assert safe_value_fallback(dict1, 'keyc', 'keyb') == 5
assert safe_value_fallback(dict1, 'keya', 'keyd') is None
assert safe_value_fallback(dict1, 'keyNo', 'keyNo') is None
assert safe_value_fallback(dict1, 'keyNo', 'keyNo', 55) == 55
def test_safe_value_fallback2():
dict1 = {'keya': None, 'keyb': 2, 'keyc': 5, 'keyd': None}
dict2 = {'keya': 20, 'keyb': None, 'keyc': 6, 'keyd': None}
assert safe_value_fallback(dict1, dict2, 'keya', 'keya') == 20
assert safe_value_fallback(dict2, dict1, 'keya', 'keya') == 20
assert safe_value_fallback2(dict1, dict2, 'keya', 'keya') == 20
assert safe_value_fallback2(dict2, dict1, 'keya', 'keya') == 20
assert safe_value_fallback(dict1, dict2, 'keyb', 'keyb') == 2
assert safe_value_fallback(dict2, dict1, 'keyb', 'keyb') == 2
assert safe_value_fallback2(dict1, dict2, 'keyb', 'keyb') == 2
assert safe_value_fallback2(dict2, dict1, 'keyb', 'keyb') == 2
assert safe_value_fallback(dict1, dict2, 'keyc', 'keyc') == 5
assert safe_value_fallback(dict2, dict1, 'keyc', 'keyc') == 6
assert safe_value_fallback2(dict1, dict2, 'keyc', 'keyc') == 5
assert safe_value_fallback2(dict2, dict1, 'keyc', 'keyc') == 6
assert safe_value_fallback(dict1, dict2, 'keyd', 'keyd') is None
assert safe_value_fallback(dict2, dict1, 'keyd', 'keyd') is None
assert safe_value_fallback(dict2, dict1, 'keyd', 'keyd', 1234) == 1234
assert safe_value_fallback2(dict1, dict2, 'keyd', 'keyd') is None
assert safe_value_fallback2(dict2, dict1, 'keyd', 'keyd') is None
assert safe_value_fallback2(dict2, dict1, 'keyd', 'keyd', 1234) == 1234
assert safe_value_fallback(dict1, dict2, 'keyNo', 'keyNo') is None
assert safe_value_fallback(dict2, dict1, 'keyNo', 'keyNo') is None
assert safe_value_fallback(dict2, dict1, 'keyNo', 'keyNo', 1234) == 1234
assert safe_value_fallback2(dict1, dict2, 'keyNo', 'keyNo') is None
assert safe_value_fallback2(dict2, dict1, 'keyNo', 'keyNo') is None
assert safe_value_fallback2(dict2, dict1, 'keyNo', 'keyNo', 1234) == 1234
def test_plural() -> None:

View File

@ -457,6 +457,7 @@ def test_migrate_old(mocker, default_conf, fee):
assert trade.close_rate_requested is None
assert trade.is_open == 1
assert trade.amount == amount
assert trade.amount_requested == amount
assert trade.stake_amount == default_conf.get("stake_amount")
assert trade.pair == "ETC/BTC"
assert trade.exchange == "bittrex"
@ -546,6 +547,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
assert trade.close_rate_requested is None
assert trade.is_open == 1
assert trade.amount == amount
assert trade.amount_requested == amount
assert trade.stake_amount == default_conf.get("stake_amount")
assert trade.pair == "ETC/BTC"
assert trade.exchange == "binance"
@ -725,6 +727,7 @@ def test_to_json(default_conf, fee):
pair='ETH/BTC',
stake_amount=0.001,
amount=123.0,
amount_requested=123.0,
fee_open=fee.return_value,
fee_close=fee.return_value,
open_date=arrow.utcnow().shift(hours=-2).datetime,
@ -757,6 +760,7 @@ def test_to_json(default_conf, fee):
'close_rate': None,
'close_rate_requested': None,
'amount': 123.0,
'amount_requested': 123.0,
'stake_amount': 0.001,
'close_profit': None,
'close_profit_abs': None,
@ -786,6 +790,7 @@ def test_to_json(default_conf, fee):
pair='XRP/BTC',
stake_amount=0.001,
amount=100.0,
amount_requested=101.0,
fee_open=fee.return_value,
fee_close=fee.return_value,
open_date=arrow.utcnow().shift(hours=-2).datetime,
@ -808,6 +813,7 @@ def test_to_json(default_conf, fee):
'open_rate': 0.123,
'close_rate': 0.125,
'amount': 100.0,
'amount_requested': 101.0,
'stake_amount': 0.001,
'stop_loss': None,
'stop_loss_abs': None,