Merge branch 'develop' into feat/freqai
This commit is contained in:
@@ -148,7 +148,7 @@ def get_patched_exchange(mocker, config, api_mock=None, id='binance',
|
||||
patch_exchange(mocker, api_mock, id, mock_markets, mock_supported_modes)
|
||||
config['exchange']['name'] = id
|
||||
try:
|
||||
exchange = ExchangeResolver.load_exchange(id, config)
|
||||
exchange = ExchangeResolver.load_exchange(id, config, load_leverage_tiers=True)
|
||||
except ImportError:
|
||||
exchange = Exchange(config)
|
||||
return exchange
|
||||
@@ -2609,7 +2609,7 @@ def open_trade_usdt():
|
||||
pair='ADA/USDT',
|
||||
open_rate=2.0,
|
||||
exchange='binance',
|
||||
open_order_id='123456789',
|
||||
open_order_id='123456789_exit',
|
||||
amount=30.0,
|
||||
fee_open=0.0,
|
||||
fee_close=0.0,
|
||||
@@ -2634,6 +2634,23 @@ def open_trade_usdt():
|
||||
cost=trade.open_rate * trade.amount,
|
||||
order_date=trade.open_date,
|
||||
order_filled_date=trade.open_date,
|
||||
),
|
||||
Order(
|
||||
ft_order_side='exit',
|
||||
ft_pair=trade.pair,
|
||||
ft_is_open=True,
|
||||
order_id='123456789_exit',
|
||||
status="open",
|
||||
symbol=trade.pair,
|
||||
order_type="limit",
|
||||
side="sell",
|
||||
price=trade.open_rate,
|
||||
average=trade.open_rate,
|
||||
filled=trade.amount,
|
||||
remaining=0,
|
||||
cost=trade.open_rate * trade.amount,
|
||||
order_date=trade.open_date,
|
||||
order_filled_date=trade.open_date,
|
||||
)
|
||||
]
|
||||
return trade
|
||||
|
@@ -137,7 +137,8 @@ def exchange_futures(request, exchange_conf, class_mocker):
|
||||
'freqtrade.exchange.binance.Binance.fill_leverage_tiers')
|
||||
class_mocker.patch('freqtrade.exchange.exchange.Exchange.fetch_trading_fees')
|
||||
class_mocker.patch('freqtrade.exchange.okx.Okx.additional_exchange_init')
|
||||
exchange = ExchangeResolver.load_exchange(request.param, exchange_conf, validate=True)
|
||||
exchange = ExchangeResolver.load_exchange(
|
||||
request.param, exchange_conf, validate=True, load_leverage_tiers=True)
|
||||
|
||||
yield exchange, request.param
|
||||
|
||||
|
@@ -1138,6 +1138,57 @@ def test_create_dry_run_order(default_conf, mocker, side, exchange_name, leverag
|
||||
assert order["cost"] == 1 * 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize('side,is_short,order_reason', [
|
||||
("buy", False, "entry"),
|
||||
("sell", False, "exit"),
|
||||
("buy", True, "exit"),
|
||||
("sell", True, "entry"),
|
||||
])
|
||||
@pytest.mark.parametrize("order_type,price_side,fee", [
|
||||
("limit", "same", 1.0),
|
||||
("limit", "other", 2.0),
|
||||
("market", "same", 2.0),
|
||||
("market", "other", 2.0),
|
||||
])
|
||||
def test_create_dry_run_order_fees(
|
||||
default_conf,
|
||||
mocker,
|
||||
side,
|
||||
order_type,
|
||||
is_short,
|
||||
order_reason,
|
||||
price_side,
|
||||
fee,
|
||||
):
|
||||
mocker.patch(
|
||||
'freqtrade.exchange.Exchange.get_fee',
|
||||
side_effect=lambda symbol, taker_or_maker: 2.0 if taker_or_maker == 'taker' else 1.0
|
||||
)
|
||||
mocker.patch('freqtrade.exchange.Exchange._is_dry_limit_order_filled',
|
||||
return_value=price_side == 'other')
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
|
||||
order = exchange.create_dry_run_order(
|
||||
pair='LTC/USDT',
|
||||
ordertype=order_type,
|
||||
side=side,
|
||||
amount=10,
|
||||
rate=2.0,
|
||||
leverage=1.0
|
||||
)
|
||||
if price_side == 'other' or order_type == 'market':
|
||||
assert order['fee']['rate'] == fee
|
||||
return
|
||||
else:
|
||||
assert order['fee'] is None
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange._is_dry_limit_order_filled',
|
||||
return_value=price_side != 'other')
|
||||
|
||||
order1 = exchange.fetch_dry_run_order(order['id'])
|
||||
assert order1['fee']['rate'] == fee
|
||||
|
||||
|
||||
@pytest.mark.parametrize("side,startprice,endprice", [
|
||||
("buy", 25.563, 25.566),
|
||||
("sell", 25.566, 25.563)
|
||||
|
@@ -90,28 +90,6 @@ def load_data_test(what, testdatadir):
|
||||
fill_missing=True)}
|
||||
|
||||
|
||||
def simple_backtest(config, contour, mocker, testdatadir) -> None:
|
||||
patch_exchange(mocker)
|
||||
config['timeframe'] = '1m'
|
||||
backtesting = Backtesting(config)
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
|
||||
data = load_data_test(contour, testdatadir)
|
||||
processed = backtesting.strategy.advise_all_indicators(data)
|
||||
min_date, max_date = get_timerange(processed)
|
||||
assert isinstance(processed, dict)
|
||||
results = backtesting.backtest(
|
||||
processed=processed,
|
||||
start_date=min_date,
|
||||
end_date=max_date,
|
||||
max_open_trades=1,
|
||||
position_stacking=False,
|
||||
enable_protections=config.get('enable_protections', False),
|
||||
)
|
||||
# results :: <class 'pandas.core.frame.DataFrame'>
|
||||
return results
|
||||
|
||||
|
||||
# FIX: fixturize this?
|
||||
def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC'):
|
||||
data = history.load_data(datadir=datadir, timeframe='1m', pairs=[pair])
|
||||
@@ -942,6 +920,7 @@ def test_backtest_dataprovider_analyzed_df(default_conf, fee, mocker, testdatadi
|
||||
def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatadir) -> None:
|
||||
# While this test IS a copy of test_backtest_pricecontours, it's needed to ensure
|
||||
# results do not carry-over to the next run, which is not given by using parametrize.
|
||||
patch_exchange(mocker)
|
||||
default_conf['protections'] = [
|
||||
{
|
||||
"method": "CooldownPeriod",
|
||||
@@ -949,6 +928,7 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad
|
||||
}]
|
||||
|
||||
default_conf['enable_protections'] = True
|
||||
default_conf['timeframe'] = '1m'
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
@@ -959,12 +939,27 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad
|
||||
['sine', 9],
|
||||
['raise', 10],
|
||||
]
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
|
||||
# While entry-signals are unrealistic, running backtesting
|
||||
# over and over again should not cause different results
|
||||
for [contour, numres] in tests:
|
||||
# Debug output for random test failure
|
||||
print(f"{contour}, {numres}")
|
||||
assert len(simple_backtest(default_conf, contour, mocker, testdatadir)['results']) == numres
|
||||
data = load_data_test(contour, testdatadir)
|
||||
processed = backtesting.strategy.advise_all_indicators(data)
|
||||
min_date, max_date = get_timerange(processed)
|
||||
assert isinstance(processed, dict)
|
||||
results = backtesting.backtest(
|
||||
processed=processed,
|
||||
start_date=min_date,
|
||||
end_date=max_date,
|
||||
max_open_trades=1,
|
||||
position_stacking=False,
|
||||
enable_protections=default_conf.get('enable_protections', False),
|
||||
)
|
||||
assert len(results['results']) == numres
|
||||
|
||||
|
||||
@pytest.mark.parametrize('protections,contour,expected', [
|
||||
@@ -990,7 +985,25 @@ def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir,
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
# While entry-signals are unrealistic, running backtesting
|
||||
# over and over again should not cause different results
|
||||
assert len(simple_backtest(default_conf, contour, mocker, testdatadir)['results']) == expected
|
||||
|
||||
patch_exchange(mocker)
|
||||
default_conf['timeframe'] = '1m'
|
||||
backtesting = Backtesting(default_conf)
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
|
||||
data = load_data_test(contour, testdatadir)
|
||||
processed = backtesting.strategy.advise_all_indicators(data)
|
||||
min_date, max_date = get_timerange(processed)
|
||||
assert isinstance(processed, dict)
|
||||
results = backtesting.backtest(
|
||||
processed=processed,
|
||||
start_date=min_date,
|
||||
end_date=max_date,
|
||||
max_open_trades=1,
|
||||
position_stacking=False,
|
||||
enable_protections=default_conf.get('enable_protections', False),
|
||||
)
|
||||
assert len(results['results']) == expected
|
||||
|
||||
|
||||
def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
|
||||
|
@@ -6,6 +6,7 @@ import pytest
|
||||
from freqtrade import constants
|
||||
from freqtrade.enums import ExitType
|
||||
from freqtrade.persistence import PairLocks, Trade
|
||||
from freqtrade.persistence.trade_model import Order
|
||||
from freqtrade.plugins.protectionmanager import ProtectionManager
|
||||
from tests.conftest import get_patched_freqtradebot, log_has_re
|
||||
|
||||
@@ -30,7 +31,37 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
|
||||
amount=0.01 / open_rate,
|
||||
exchange='binance',
|
||||
is_short=is_short,
|
||||
leverage=1,
|
||||
)
|
||||
|
||||
trade.orders.append(Order(
|
||||
ft_order_side=trade.entry_side,
|
||||
order_id=f'{pair}-{trade.entry_side}-{trade.open_date}',
|
||||
ft_pair=pair,
|
||||
amount=trade.amount,
|
||||
filled=trade.amount,
|
||||
remaining=0,
|
||||
price=open_rate,
|
||||
average=open_rate,
|
||||
status="closed",
|
||||
order_type="market",
|
||||
side=trade.entry_side,
|
||||
))
|
||||
if not is_open:
|
||||
trade.orders.append(Order(
|
||||
ft_order_side=trade.exit_side,
|
||||
order_id=f'{pair}-{trade.exit_side}-{trade.close_date}',
|
||||
ft_pair=pair,
|
||||
amount=trade.amount,
|
||||
filled=trade.amount,
|
||||
remaining=0,
|
||||
price=open_rate * (2 - profit_rate if is_short else profit_rate),
|
||||
average=open_rate * (2 - profit_rate if is_short else profit_rate),
|
||||
status="closed",
|
||||
order_type="market",
|
||||
side=trade.exit_side,
|
||||
))
|
||||
|
||||
trade.recalc_open_trade_value()
|
||||
if not is_open:
|
||||
trade.close(open_rate * (2 - profit_rate if is_short else profit_rate))
|
||||
|
@@ -830,6 +830,8 @@ def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None:
|
||||
assert cancel_order_mock.call_count == 2
|
||||
assert trade.amount == amount
|
||||
|
||||
trade = Trade.query.filter(Trade.id == '3').first()
|
||||
|
||||
# make an limit-sell open trade
|
||||
mocker.patch(
|
||||
'freqtrade.exchange.Exchange.fetch_order',
|
||||
|
@@ -686,6 +686,7 @@ def test_profit_handle(default_conf_usdt, update, ticker_usdt, ticker_sell_up, f
|
||||
# Simulate fulfilled LIMIT_SELL order for trade
|
||||
oobj = Order.parse_from_ccxt_object(
|
||||
limit_sell_order_usdt, limit_sell_order_usdt['symbol'], 'sell')
|
||||
trade.orders.append(oobj)
|
||||
trade.update_trade(oobj)
|
||||
|
||||
trade.close_date = datetime.now(timezone.utc)
|
||||
@@ -707,7 +708,7 @@ def test_profit_handle(default_conf_usdt, update, ticker_usdt, ticker_sell_up, f
|
||||
assert '*Best Performing:* `ETH/USDT: 9.45%`' in msg_mock.call_args_list[-1][0][0]
|
||||
assert '*Max Drawdown:*' in msg_mock.call_args_list[-1][0][0]
|
||||
assert '*Profit factor:*' in msg_mock.call_args_list[-1][0][0]
|
||||
assert '*Trading volume:* `60 USDT`' in msg_mock.call_args_list[-1][0][0]
|
||||
assert '*Trading volume:* `126 USDT`' in msg_mock.call_args_list[-1][0][0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
|
@@ -2060,8 +2060,9 @@ def test_update_trade_state_orderexception(mocker, default_conf_usdt, caplog) ->
|
||||
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_update_trade_state_sell(
|
||||
default_conf_usdt, trades_for_order, limit_order_open, limit_order, is_short, mocker,
|
||||
default_conf_usdt, trades_for_order, limit_order_open, limit_order, is_short, mocker
|
||||
):
|
||||
buy_order = limit_order[entry_side(is_short)]
|
||||
open_order = limit_order_open[exit_side(is_short)]
|
||||
l_order = limit_order[exit_side(is_short)]
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
|
||||
@@ -2088,6 +2089,9 @@ def test_update_trade_state_sell(
|
||||
leverage=1,
|
||||
is_short=is_short,
|
||||
)
|
||||
order = Order.parse_from_ccxt_object(buy_order, 'LTC/ETH', entry_side(is_short))
|
||||
trade.orders.append(order)
|
||||
|
||||
order = Order.parse_from_ccxt_object(open_order, 'LTC/ETH', exit_side(is_short))
|
||||
trade.orders.append(order)
|
||||
assert order.status == 'open'
|
||||
@@ -2135,8 +2139,6 @@ def test_handle_trade(
|
||||
assert trade
|
||||
|
||||
time.sleep(0.01) # Race condition fix
|
||||
oobj = Order.parse_from_ccxt_object(enter_order, enter_order['symbol'], entry_side(is_short))
|
||||
trade.update_trade(oobj)
|
||||
assert trade.is_open is True
|
||||
freqtrade.wallets.update()
|
||||
|
||||
@@ -2146,11 +2148,15 @@ def test_handle_trade(
|
||||
assert trade.open_order_id == exit_order['id']
|
||||
|
||||
# Simulate fulfilled LIMIT_SELL order for trade
|
||||
oobj = Order.parse_from_ccxt_object(exit_order, exit_order['symbol'], exit_side(is_short))
|
||||
trade.update_trade(oobj)
|
||||
trade.orders[-1].ft_is_open = False
|
||||
trade.orders[-1].status = 'closed'
|
||||
trade.orders[-1].filled = trade.orders[-1].remaining
|
||||
trade.orders[-1].remaining = 0.0
|
||||
|
||||
assert trade.close_rate == 2.0 if is_short else 2.2
|
||||
assert trade.close_profit == close_profit
|
||||
trade.update_trade(trade.orders[-1])
|
||||
|
||||
assert trade.close_rate == (2.0 if is_short else 2.2)
|
||||
assert pytest.approx(trade.close_profit) == close_profit
|
||||
assert trade.calc_profit(trade.close_rate) == 5.685
|
||||
assert trade.close_date is not None
|
||||
assert trade.exit_reason == 'sell_signal1'
|
||||
@@ -2753,6 +2759,8 @@ def test_check_handle_cancelled_exit(
|
||||
cancel_order_mock = MagicMock()
|
||||
limit_sell_order_old.update({"status": "canceled", 'filled': 0.0})
|
||||
limit_sell_order_old['side'] = 'buy' if is_short else 'sell'
|
||||
limit_sell_order_old['id'] = open_trade_usdt.open_order_id
|
||||
|
||||
patch_exchange(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
@@ -2787,6 +2795,7 @@ def test_manage_open_orders_partial(
|
||||
rpc_mock = patch_RPCManager(mocker)
|
||||
open_trade.is_short = is_short
|
||||
open_trade.leverage = leverage
|
||||
open_trade.orders[0].ft_order_side = 'sell' if is_short else 'buy'
|
||||
limit_buy_order_old_partial['id'] = open_trade.open_order_id
|
||||
limit_buy_order_old_partial['side'] = 'sell' if is_short else 'buy'
|
||||
limit_buy_canceled = deepcopy(limit_buy_order_old_partial)
|
||||
@@ -2872,6 +2881,7 @@ def test_manage_open_orders_partial_except(
|
||||
limit_buy_order_old_partial_canceled, mocker
|
||||
) -> None:
|
||||
open_trade.is_short = is_short
|
||||
open_trade.orders[0].ft_order_side = 'sell' if is_short else 'buy'
|
||||
rpc_mock = patch_RPCManager(mocker)
|
||||
limit_buy_order_old_partial_canceled['id'] = open_trade.open_order_id
|
||||
limit_buy_order_old_partial['id'] = open_trade.open_order_id
|
||||
@@ -3090,7 +3100,27 @@ def test_handle_cancel_exit_limit(mocker, default_conf_usdt, fee) -> None:
|
||||
close_date=arrow.utcnow().datetime,
|
||||
exit_reason="sell_reason_whatever",
|
||||
)
|
||||
order = {'remaining': 1,
|
||||
trade.orders = [
|
||||
Order(
|
||||
ft_order_side='buy',
|
||||
ft_pair=trade.pair,
|
||||
ft_is_open=True,
|
||||
order_id='123456',
|
||||
status="closed",
|
||||
symbol=trade.pair,
|
||||
order_type="market",
|
||||
side="buy",
|
||||
price=trade.open_rate,
|
||||
average=trade.open_rate,
|
||||
filled=trade.amount,
|
||||
remaining=0,
|
||||
cost=trade.open_rate * trade.amount,
|
||||
order_date=trade.open_date,
|
||||
order_filled_date=trade.open_date,
|
||||
),
|
||||
]
|
||||
order = {'id': "123456",
|
||||
'remaining': 1,
|
||||
'amount': 1,
|
||||
'status': "open"}
|
||||
reason = CANCEL_REASON['TIMEOUT']
|
||||
@@ -3626,7 +3656,7 @@ def test_execute_trade_exit_market_order(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker_usdt,
|
||||
get_fee=fee,
|
||||
_is_dry_limit_order_filled=MagicMock(return_value=False),
|
||||
_is_dry_limit_order_filled=MagicMock(return_value=True),
|
||||
)
|
||||
patch_whitelist(mocker, default_conf_usdt)
|
||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||
@@ -3642,7 +3672,8 @@ def test_execute_trade_exit_market_order(
|
||||
# Increase the price and sell it
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker_usdt_sell_up
|
||||
fetch_ticker=ticker_usdt_sell_up,
|
||||
_is_dry_limit_order_filled=MagicMock(return_value=False),
|
||||
)
|
||||
freqtrade.config['order_types']['exit'] = 'market'
|
||||
|
||||
@@ -3655,7 +3686,7 @@ def test_execute_trade_exit_market_order(
|
||||
assert not trade.is_open
|
||||
assert trade.close_profit == profit_ratio
|
||||
|
||||
assert rpc_mock.call_count == 3
|
||||
assert rpc_mock.call_count == 4
|
||||
last_msg = rpc_mock.call_args_list[-2][0][0]
|
||||
assert {
|
||||
'type': RPCMessageType.EXIT,
|
||||
|
@@ -481,6 +481,7 @@ def test_update_limit_order(fee, caplog, limit_buy_order_usdt, limit_sell_order_
|
||||
|
||||
trade.open_order_id = 'something'
|
||||
oobj = Order.parse_from_ccxt_object(enter_order, 'ADA/USDT', entry_side)
|
||||
trade.orders.append(oobj)
|
||||
trade.update_trade(oobj)
|
||||
assert trade.open_order_id is None
|
||||
assert trade.open_rate == open_rate
|
||||
@@ -496,11 +497,12 @@ def test_update_limit_order(fee, caplog, limit_buy_order_usdt, limit_sell_order_
|
||||
trade.open_order_id = 'something'
|
||||
time_machine.move_to("2022-03-31 21:45:05 +00:00")
|
||||
oobj = Order.parse_from_ccxt_object(exit_order, 'ADA/USDT', exit_side)
|
||||
trade.orders.append(oobj)
|
||||
trade.update_trade(oobj)
|
||||
|
||||
assert trade.open_order_id is None
|
||||
assert trade.close_rate == close_rate
|
||||
assert trade.close_profit == profit
|
||||
assert pytest.approx(trade.close_profit) == profit
|
||||
assert trade.close_date is not None
|
||||
assert log_has_re(f"LIMIT_{exit_side.upper()} has been fulfilled for "
|
||||
r"Trade\(id=2, pair=ADA/USDT, amount=30.00000000, "
|
||||
@@ -529,6 +531,7 @@ def test_update_market_order(market_buy_order_usdt, market_sell_order_usdt, fee,
|
||||
|
||||
trade.open_order_id = 'something'
|
||||
oobj = Order.parse_from_ccxt_object(market_buy_order_usdt, 'ADA/USDT', 'buy')
|
||||
trade.orders.append(oobj)
|
||||
trade.update_trade(oobj)
|
||||
assert trade.open_order_id is None
|
||||
assert trade.open_rate == 2.0
|
||||
@@ -543,10 +546,11 @@ def test_update_market_order(market_buy_order_usdt, market_sell_order_usdt, fee,
|
||||
trade.is_open = True
|
||||
trade.open_order_id = 'something'
|
||||
oobj = Order.parse_from_ccxt_object(market_sell_order_usdt, 'ADA/USDT', 'sell')
|
||||
trade.orders.append(oobj)
|
||||
trade.update_trade(oobj)
|
||||
assert trade.open_order_id is None
|
||||
assert trade.close_rate == 2.2
|
||||
assert trade.close_profit == round(0.0945137157107232, 8)
|
||||
assert pytest.approx(trade.close_profit) == 0.094513715710723
|
||||
assert trade.close_date is not None
|
||||
assert log_has_re(r"MARKET_SELL has been fulfilled for Trade\(id=1, "
|
||||
r"pair=ADA/USDT, amount=30.00000000, is_short=False, leverage=1.0, "
|
||||
@@ -624,14 +628,41 @@ def test_trade_close(limit_buy_order_usdt, limit_sell_order_usdt, fee):
|
||||
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10),
|
||||
interest_rate=0.0005,
|
||||
exchange='binance',
|
||||
trading_mode=margin
|
||||
trading_mode=margin,
|
||||
leverage=1.0,
|
||||
)
|
||||
trade.orders.append(Order(
|
||||
ft_order_side=trade.entry_side,
|
||||
order_id=f'{trade.pair}-{trade.entry_side}-{trade.open_date}',
|
||||
ft_pair=trade.pair,
|
||||
amount=trade.amount,
|
||||
filled=trade.amount,
|
||||
remaining=0,
|
||||
price=trade.open_rate,
|
||||
average=trade.open_rate,
|
||||
status="closed",
|
||||
order_type="limit",
|
||||
side=trade.entry_side,
|
||||
))
|
||||
trade.orders.append(Order(
|
||||
ft_order_side=trade.exit_side,
|
||||
order_id=f'{trade.pair}-{trade.exit_side}-{trade.open_date}',
|
||||
ft_pair=trade.pair,
|
||||
amount=trade.amount,
|
||||
filled=trade.amount,
|
||||
remaining=0,
|
||||
price=2.2,
|
||||
average=2.2,
|
||||
status="closed",
|
||||
order_type="limit",
|
||||
side=trade.exit_side,
|
||||
))
|
||||
assert trade.close_profit is None
|
||||
assert trade.close_date is None
|
||||
assert trade.is_open is True
|
||||
trade.close(2.2)
|
||||
assert trade.is_open is False
|
||||
assert trade.close_profit == round(0.0945137157107232, 8)
|
||||
assert pytest.approx(trade.close_profit) == 0.094513715
|
||||
assert trade.close_date is not None
|
||||
|
||||
new_date = arrow.Arrow(2020, 2, 2, 15, 6, 1).datetime,
|
||||
|
@@ -72,7 +72,7 @@ def test_add_indicators(default_conf, testdatadir, caplog):
|
||||
|
||||
strategy = StrategyResolver.load_strategy(default_conf)
|
||||
|
||||
# Generate buy/sell signals and indicators
|
||||
# Generate entry/exit signals and indicators
|
||||
data = strategy.analyze_ticker(data, {'pair': pair})
|
||||
fig = generate_empty_figure()
|
||||
|
||||
@@ -113,7 +113,7 @@ def test_add_areas(default_conf, testdatadir, caplog):
|
||||
ind_plain = {"macd": {"fill_to": "macdhist"}}
|
||||
strategy = StrategyResolver.load_strategy(default_conf)
|
||||
|
||||
# Generate buy/sell signals and indicators
|
||||
# Generate entry/exit signals and indicators
|
||||
data = strategy.analyze_ticker(data, {'pair': pair})
|
||||
fig = generate_empty_figure()
|
||||
|
||||
@@ -165,24 +165,24 @@ def test_plot_trades(testdatadir, caplog):
|
||||
fig = plot_trades(fig, trades)
|
||||
figure = fig1.layout.figure
|
||||
|
||||
# Check buys - color, should be in first graph, ...
|
||||
trade_buy = find_trace_in_fig_data(figure.data, 'Trade buy')
|
||||
assert isinstance(trade_buy, go.Scatter)
|
||||
assert trade_buy.yaxis == 'y'
|
||||
assert len(trades) == len(trade_buy.x)
|
||||
assert trade_buy.marker.color == 'cyan'
|
||||
assert trade_buy.marker.symbol == 'circle-open'
|
||||
assert trade_buy.text[0] == '3.99%, buy_tag, roi, 15 min'
|
||||
# Check entry - color, should be in first graph, ...
|
||||
trade_entries = find_trace_in_fig_data(figure.data, 'Trade entry')
|
||||
assert isinstance(trade_entries, go.Scatter)
|
||||
assert trade_entries.yaxis == 'y'
|
||||
assert len(trades) == len(trade_entries.x)
|
||||
assert trade_entries.marker.color == 'cyan'
|
||||
assert trade_entries.marker.symbol == 'circle-open'
|
||||
assert trade_entries.text[0] == '3.99%, buy_tag, roi, 15 min'
|
||||
|
||||
trade_sell = find_trace_in_fig_data(figure.data, 'Sell - Profit')
|
||||
assert isinstance(trade_sell, go.Scatter)
|
||||
assert trade_sell.yaxis == 'y'
|
||||
assert len(trades.loc[trades['profit_ratio'] > 0]) == len(trade_sell.x)
|
||||
assert trade_sell.marker.color == 'green'
|
||||
assert trade_sell.marker.symbol == 'square-open'
|
||||
assert trade_sell.text[0] == '3.99%, buy_tag, roi, 15 min'
|
||||
trade_exit = find_trace_in_fig_data(figure.data, 'Exit - Profit')
|
||||
assert isinstance(trade_exit, go.Scatter)
|
||||
assert trade_exit.yaxis == 'y'
|
||||
assert len(trades.loc[trades['profit_ratio'] > 0]) == len(trade_exit.x)
|
||||
assert trade_exit.marker.color == 'green'
|
||||
assert trade_exit.marker.symbol == 'square-open'
|
||||
assert trade_exit.text[0] == '3.99%, buy_tag, roi, 15 min'
|
||||
|
||||
trade_sell_loss = find_trace_in_fig_data(figure.data, 'Sell - Loss')
|
||||
trade_sell_loss = find_trace_in_fig_data(figure.data, 'Exit - Loss')
|
||||
assert isinstance(trade_sell_loss, go.Scatter)
|
||||
assert trade_sell_loss.yaxis == 'y'
|
||||
assert len(trades.loc[trades['profit_ratio'] <= 0]) == len(trade_sell_loss.x)
|
||||
|
Reference in New Issue
Block a user