def load_trades(args: Namespace, pair: str, timerange: TimeRange) -> pd.DataFrame: trades: pd.DataFrame = pd.DataFrame() if args.db_url: persistence.init(_CONF) columns = [ "pair", "profit", "open_time", "close_time", "open_rate", "close_rate", "duration" ] for x in Trade.query.all(): print("date: {}".format(x.open_date)) trades = pd.DataFrame( [(t.pair, t.calc_profit(), t.open_date.replace(tzinfo=timeZone), t.close_date.replace(tzinfo=timeZone) if t.close_date else None, t.open_rate, t.close_rate, t.close_date.timestamp() - t.open_date.timestamp() if t.close_date else None) for t in Trade.query.filter(Trade.pair.is_(pair)).all()], columns=columns) elif args.exportfilename: file = Path(args.exportfilename) if file.exists(): load_backtest_data(file) else: trades = pd.DataFrame([], columns=BT_DATA_COLUMNS) return trades
def test_load_backtest_data_old_format(testdatadir, mocker): filename = testdatadir / "backtest-result_test222.json" mocker.patch('freqtrade.data.btanalysis.load_backtest_stats', return_value=[]) with pytest.raises( OperationalException, match= r"Backtest-results with only trades data are no longer supported." ): load_backtest_data(filename)
def test_load_backtest_data(): filename = make_testdata_path(None) / "backtest-result_test.json" bt_data = load_backtest_data(filename) assert isinstance(bt_data, DataFrame) assert list(bt_data.columns) == BT_DATA_COLUMNS + ["profitabs"] assert len(bt_data) == 179 # Test loading from string (must yield same result) bt_data2 = load_backtest_data(str(filename)) assert bt_data.equals(bt_data2) with pytest.raises(ValueError, match=r"File .* does not exist\."): load_backtest_data(str("filename") + "nofile")
def test_load_backtest_data_old_format(testdatadir): filename = testdatadir / "backtest-result_test.json" bt_data = load_backtest_data(filename) assert isinstance(bt_data, DataFrame) assert list(bt_data.columns ) == BT_DATA_COLUMNS_OLD + ['profit_abs', 'profit_ratio'] assert len(bt_data) == 179 # Test loading from string (must yield same result) bt_data2 = load_backtest_data(str(filename)) assert bt_data.equals(bt_data2) with pytest.raises(ValueError, match=r"File .* does not exist\."): load_backtest_data(str("filename") + "nofile")
def test_generate_profit_graph(): filename = history.make_testdata_path(None) / "backtest-result_test.json" trades = load_backtest_data(filename) timerange = Arguments.parse_timerange("20180110-20180112") pairs = ["POWR/BTC", "XLM/BTC"] tickers = history.load_data(datadir=None, pairs=pairs, ticker_interval='5m', timerange=timerange) trades = trades[trades['pair'].isin(pairs)] fig = generate_profit_graph(pairs, tickers, trades) assert isinstance(fig, go.Figure) assert fig.layout.title.text == "Profit plot" figure = fig.layout.figure assert len(figure.data) == 4 avgclose = find_trace_in_fig_data(figure.data, "Avg close price") assert isinstance(avgclose, go.Scattergl) profit = find_trace_in_fig_data(figure.data, "Profit") assert isinstance(profit, go.Scattergl) for pair in pairs: profit_pair = find_trace_in_fig_data(figure.data, f"Profit {pair}") assert isinstance(profit_pair, go.Scattergl)
def test_add_profit(testdatadir): filename = testdatadir / "backtest-result_test.json" bt_data = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") df = history.load_pair_history(pair="TRX/BTC", ticker_interval='5m', datadir=testdatadir, timerange=timerange) fig = generate_empty_figure() cum_profits = create_cum_profit(df.set_index('date'), bt_data[bt_data["pair"] == 'TRX/BTC'], "cum_profits", timeframe="5m") fig1 = add_profit(fig, row=2, data=cum_profits, column='cum_profits', name='Profits') figure = fig1.layout.figure profits = find_trace_in_fig_data(figure.data, "Profits") assert isinstance(profits, go.Scatter) assert profits.yaxis == "y2"
def test_generate_profit_graph(testdatadir): filename = testdatadir / "backtest-result_test.json" trades = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") pairs = ["TRX/BTC", "ADA/BTC"] tickers = history.load_data(datadir=testdatadir, pairs=pairs, ticker_interval='5m', timerange=timerange) trades = trades[trades['pair'].isin(pairs)] fig = generate_profit_graph(pairs, tickers, trades, timeframe="5m") assert isinstance(fig, go.Figure) assert fig.layout.title.text == "Freqtrade Profit plot" assert fig.layout.yaxis.title.text == "Price" assert fig.layout.yaxis2.title.text == "Profit" assert fig.layout.yaxis3.title.text == "Profit" figure = fig.layout.figure assert len(figure.data) == 4 avgclose = find_trace_in_fig_data(figure.data, "Avg close price") assert isinstance(avgclose, go.Scatter) profit = find_trace_in_fig_data(figure.data, "Profit") assert isinstance(profit, go.Scatter) for pair in pairs: profit_pair = find_trace_in_fig_data(figure.data, f"Profit {pair}") assert isinstance(profit_pair, go.Scatter)
def test_create_cum_profit1(testdatadir): filename = testdatadir / "backtest-result_test.json" bt_data = load_backtest_data(filename) # Move close-time to "off" the candle, to make sure the logic still works bt_data.loc[:, 'close_date'] = bt_data.loc[:, 'close_date'] + DateOffset( seconds=20) timerange = TimeRange.parse_timerange("20180110-20180112") df = load_pair_history(pair="TRX/BTC", timeframe='5m', datadir=testdatadir, timerange=timerange) cum_profits = create_cum_profit(df.set_index('date'), bt_data[bt_data["pair"] == 'TRX/BTC'], "cum_profits", timeframe="5m") assert "cum_profits" in cum_profits.columns assert cum_profits.iloc[0]['cum_profits'] == 0 assert cum_profits.iloc[-1]['cum_profits'] == 0.0798005 with pytest.raises(ValueError, match='Trade dataframe empty.'): create_cum_profit(df.set_index('date'), bt_data[bt_data["pair"] == 'NOTAPAIR'], "cum_profits", timeframe="5m")
def test_plot_trades(caplog): fig1 = generage_empty_figure() # nothing happens when no trades are available fig = plot_trades(fig1, None) assert fig == fig1 assert log_has("No trades found.", caplog.record_tuples) pair = "ADA/BTC" filename = history.make_testdata_path(None) / "backtest-result_test.json" trades = load_backtest_data(filename) trades = trades.loc[trades['pair'] == pair] 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 == 'green' trade_sell = find_trace_in_fig_data(figure.data, "trade_sell") assert isinstance(trade_sell, go.Scatter) assert trade_sell.yaxis == 'y' assert len(trades) == len(trade_sell.x) assert trade_sell.marker.color == 'red'
def analyse_and_plot_pairs(config: Dict[str, Any]): """ From arguments provided in cli: -Initialise backtest env -Get tickers data -Generate Dafaframes populated with indicators and signals -Load trades excecuted on same periods -Generate Plotly plot objects -Generate plot files :return: None """ exchange = ExchangeResolver(config.get('exchange', {}).get('name'), config).exchange strategy = StrategyResolver(config).strategy if "pairs" in config: pairs = config["pairs"].split(',') else: pairs = config["exchange"]["pair_whitelist"] # Set timerange to use timerange = Arguments.parse_timerange(config["timerange"]) ticker_interval = strategy.ticker_interval tickers = history.load_data( datadir=Path(str(config.get("datadir"))), pairs=pairs, ticker_interval=config['ticker_interval'], refresh_pairs=config.get('refresh_pairs', False), timerange=timerange, exchange=exchange, live=config.get("live", False), ) pair_counter = 0 for pair, data in tickers.items(): pair_counter += 1 logger.info("analyse pair %s", pair) tickers = {} tickers[pair] = data dataframe = generate_dataframe(strategy, tickers, pair) if config["trade_source"] == "DB": trades = load_trades_from_db(config["db_url"]) elif config["trade_source"] == "file": trades = load_backtest_data(Path(config["exportfilename"])) trades = trades.loc[trades['pair'] == pair] trades = extract_trades_of_period(dataframe, trades) fig = generate_graph( pair=pair, data=dataframe, trades=trades, indicators1=config["indicators1"].split(","), indicators2=config["indicators2"].split(",") ) generate_plot_file(fig, pair, ticker_interval) logger.info('End of ploting process %s plots generated', pair_counter)
def test_analyze_trade_parallelism(default_conf, mocker, testdatadir): filename = testdatadir / "backtest-result_test.json" bt_data = load_backtest_data(filename) res = analyze_trade_parallelism(bt_data, "5m") assert isinstance(res, DataFrame) assert 'open_trades' in res.columns assert res['open_trades'].max() == 3 assert res['open_trades'].min() == 0
def test_generate_profit_graph(testdatadir): filename = testdatadir / "backtest_results/backtest-result_new.json" trades = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") pairs = ["TRX/BTC", "XLM/BTC"] trades = trades[ trades['close_date'] < pd.Timestamp('2018-01-12', tz='UTC')] data = history.load_data(datadir=testdatadir, pairs=pairs, timeframe='5m', timerange=timerange) trades = trades[trades['pair'].isin(pairs)] fig = generate_profit_graph(pairs, data, trades, timeframe="5m", stake_currency='BTC') assert isinstance(fig, go.Figure) assert fig.layout.title.text == "Freqtrade Profit plot" assert fig.layout.yaxis.title.text == "Price" assert fig.layout.yaxis2.title.text == "Profit BTC" assert fig.layout.yaxis3.title.text == "Profit BTC" figure = fig.layout.figure assert len(figure.data) == 7 avgclose = find_trace_in_fig_data(figure.data, "Avg close price") assert isinstance(avgclose, go.Scatter) profit = find_trace_in_fig_data(figure.data, "Profit") assert isinstance(profit, go.Scatter) drawdown = find_trace_in_fig_data(figure.data, "Max drawdown 35.69%") assert isinstance(drawdown, go.Scatter) parallel = find_trace_in_fig_data(figure.data, "Parallel trades") assert isinstance(parallel, go.Scatter) underwater = find_trace_in_fig_data(figure.data, "Underwater Plot") assert isinstance(underwater, go.Scatter) for pair in pairs: profit_pair = find_trace_in_fig_data(figure.data, f"Profit {pair}") assert isinstance(profit_pair, go.Scatter) with pytest.raises(OperationalException, match=r"No trades found.*"): # Pair cannot be empty - so it's an empty dataframe. generate_profit_graph(pairs, data, trades.loc[trades['pair'].isnull()], timeframe="5m", stake_currency='BTC')
def test_calculate_csum(testdatadir): filename = testdatadir / "backtest-result_test.json" bt_data = load_backtest_data(filename) csum_min, csum_max = calculate_csum(bt_data) assert isinstance(csum_min, float) assert isinstance(csum_max, float) assert csum_min < 0.01 assert csum_max > 0.02 with pytest.raises(ValueError, match='Trade dataframe empty.'): csum_min, csum_max = calculate_csum(DataFrame())
def test_calculate_max_drawdown(testdatadir): filename = testdatadir / "backtest-result_test.json" bt_data = load_backtest_data(filename) drawdown, h, low = calculate_max_drawdown(bt_data) assert isinstance(drawdown, float) assert pytest.approx(drawdown) == 0.21142322 assert isinstance(h, Timestamp) assert isinstance(low, Timestamp) assert h == Timestamp('2018-01-24 14:25:00', tz='UTC') assert low == Timestamp('2018-01-30 04:45:00', tz='UTC') with pytest.raises(ValueError, match='Trade dataframe empty.'): drawdown, h, low = calculate_max_drawdown(DataFrame())
def test_create_cum_profit(): filename = make_testdata_path(None) / "backtest-result_test.json" bt_data = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") df = load_pair_history(pair="POWR/BTC", ticker_interval='5m', datadir=None, timerange=timerange) cum_profits = create_cum_profit(df.set_index('date'), bt_data[bt_data["pair"] == 'POWR/BTC'], "cum_profits") assert "cum_profits" in cum_profits.columns assert cum_profits.iloc[0]['cum_profits'] == 0 assert cum_profits.iloc[-1]['cum_profits'] == 0.0798005
def test_generate_trading_stats(testdatadir): filename = testdatadir / "backtest-result_new.json" bt_data = load_backtest_data(filename) res = generate_trading_stats(bt_data) assert isinstance(res, dict) assert res['winner_holding_avg'] == timedelta(seconds=1440) assert res['loser_holding_avg'] == timedelta(days=1, seconds=21420) assert 'wins' in res assert 'losses' in res assert 'draws' in res # Select empty dataframe! res = generate_trading_stats(bt_data.loc[bt_data['open_date'] == '2000-01-01', :]) assert res['wins'] == 0 assert res['losses'] == 0
def test_generate_periodic_breakdown_stats(testdatadir): filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_data(filename).to_dict(orient='records') res = generate_periodic_breakdown_stats(bt_data, 'day') assert isinstance(res, list) assert len(res) == 21 day = res[0] assert 'date' in day assert 'draws' in day assert 'loses' in day assert 'wins' in day assert 'profit_abs' in day # Select empty dataframe! res = generate_periodic_breakdown_stats([], 'day') assert res == []
def test_create_cum_profit(testdatadir): filename = testdatadir / "backtest-result_test.json" bt_data = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") df = load_pair_history(pair="TRX/BTC", timeframe='5m', datadir=testdatadir, timerange=timerange) cum_profits = create_cum_profit(df.set_index('date'), bt_data[bt_data["pair"] == 'TRX/BTC'], "cum_profits", timeframe="5m") assert "cum_profits" in cum_profits.columns assert cum_profits.iloc[0]['cum_profits'] == 0 assert cum_profits.iloc[-1]['cum_profits'] == 0.0798005
def test_generate_daily_stats(testdatadir): filename = testdatadir / "backtest-result_new.json" bt_data = load_backtest_data(filename) res = generate_daily_stats(bt_data) assert isinstance(res, dict) assert round(res['backtest_best_day'], 4) == 0.1796 assert round(res['backtest_worst_day'], 4) == -0.1468 assert res['winning_days'] == 14 assert res['draw_days'] == 4 assert res['losing_days'] == 3 # Select empty dataframe! res = generate_daily_stats(bt_data.loc[bt_data['open_date'] == '2000-01-01', :]) assert isinstance(res, dict) assert round(res['backtest_best_day'], 4) == 0.0 assert res['winning_days'] == 0 assert res['draw_days'] == 0 assert res['losing_days'] == 0
def test_calculate_max_drawdown(testdatadir): filename = testdatadir / "backtest-result_new.json" bt_data = load_backtest_data(filename) _, hdate, lowdate, hval, lval, drawdown = calculate_max_drawdown( bt_data, value_col="profit_abs") assert isinstance(drawdown, float) assert pytest.approx(drawdown) == 0.12071099 assert isinstance(hdate, Timestamp) assert isinstance(lowdate, Timestamp) assert isinstance(hval, float) assert isinstance(lval, float) assert hdate == Timestamp('2018-01-25 01:30:00', tz='UTC') assert lowdate == Timestamp('2018-01-25 03:50:00', tz='UTC') underwater = calculate_underwater(bt_data) assert isinstance(underwater, DataFrame) with pytest.raises(ValueError, match='Trade dataframe empty.'): calculate_max_drawdown(DataFrame()) with pytest.raises(ValueError, match='Trade dataframe empty.'): calculate_underwater(DataFrame())
def test_plot_trades(testdatadir, caplog): fig1 = generate_empty_figure() # nothing happens when no trades are available fig = plot_trades(fig1, None) assert fig == fig1 assert log_has("No trades found.", caplog) pair = "ADA/BTC" filename = testdatadir / "backtest-result_test.json" trades = load_backtest_data(filename) trades = trades.loc[trades['pair'] == pair] 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] == '4.0%, 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_percent'] > 0]) == len(trade_sell.x) assert trade_sell.marker.color == 'green' assert trade_sell.marker.symbol == 'square-open' assert trade_sell.text[0] == '4.0%, roi, 15 min' trade_sell_loss = find_trace_in_fig_data(figure.data, 'Sell - Loss') assert isinstance(trade_sell_loss, go.Scatter) assert trade_sell_loss.yaxis == 'y' assert len(trades.loc[trades['profit_percent'] <= 0]) == len( trade_sell_loss.x) assert trade_sell_loss.marker.color == 'red' assert trade_sell_loss.marker.symbol == 'square-open' assert trade_sell_loss.text[5] == '-10.4%, stop_loss, 720 min'
def test_load_backtest_data_new_format(testdatadir): filename = testdatadir / "backtest-result_new.json" bt_data = load_backtest_data(filename) assert isinstance(bt_data, DataFrame) assert set(bt_data.columns) == set(BT_DATA_COLUMNS_MID) assert len(bt_data) == 179 # Test loading from string (must yield same result) bt_data2 = load_backtest_data(str(filename)) assert bt_data.equals(bt_data2) # Test loading from folder (must yield same result) bt_data3 = load_backtest_data(testdatadir) assert bt_data.equals(bt_data3) with pytest.raises(ValueError, match=r"File .* does not exist\."): load_backtest_data(str("filename") + "nofile") with pytest.raises(ValueError, match=r"Unknown dataformat."): load_backtest_data(testdatadir / LAST_BT_RESULT_FN)
def test_load_backtest_data_multi(testdatadir): filename = testdatadir / "backtest-result_multistrat.json" for strategy in ('DefaultStrategy', 'TestStrategy'): bt_data = load_backtest_data(filename, strategy=strategy) assert isinstance(bt_data, DataFrame) assert set(bt_data.columns) == set(BT_DATA_COLUMNS_MID) assert len(bt_data) == 179 # Test loading from string (must yield same result) bt_data2 = load_backtest_data(str(filename), strategy=strategy) assert bt_data.equals(bt_data2) with pytest.raises( ValueError, match=r"Strategy XYZ not available in the backtest result\."): load_backtest_data(filename, strategy='XYZ') with pytest.raises( ValueError, match=r"Detected backtest result with more than one strategy.*"): load_backtest_data(filename)