Merge pull request #1959 from freqtrade/split_btanalysis_load_trades

Split btanalysis load trades
This commit is contained in:
Matthias 2019-06-24 19:41:56 +02:00 committed by GitHub
commit 31a2aac627
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 86 additions and 60 deletions

View File

@ -221,24 +221,8 @@ strategies, your configuration, and the crypto-currency you have set up.
### Further backtest-result analysis
To further analyze your backtest results, you can [export the trades](#exporting-trades-to-file).
You can then load the trades to perform further analysis.
You can then load the trades to perform further analysis as shown in our [data analysis](data-analysis.md#backtesting) backtesting section.
A good way for this is using Jupyter (notebook or lab) - which provides an interactive environment to analyze the data.
Freqtrade provides an easy to load the backtest results, which is `load_backtest_data` - and takes a path to the backtest-results file.
``` python
from freqtrade.data.btanalysis import load_backtest_data
df = load_backtest_data("user_data/backtest-result.json")
# Show value-counts per pair
df.groupby("pair")["sell_reason"].value_counts()
```
This will allow you to drill deeper into your backtest results, and perform analysis which would make the regular backtest-output unreadable.
If you have some ideas for interesting / helpful backtest data analysis ideas, please submit a PR so the community can benefit from it.
## Backtesting multiple strategies

42
docs/data-analysis.md Normal file
View File

@ -0,0 +1,42 @@
# Analyzing bot data
After performing backtests, or after running the bot for some time, it will be interesting to analyze the results your bot generated.
A good way for this is using Jupyter (notebook or lab) - which provides an interactive environment to analyze the data.
The following helpers will help you loading the data into Pandas DataFrames, and may also give you some starting points in analyzing the results.
## Backtesting
To analyze your backtest results, you can [export the trades](#exporting-trades-to-file).
You can then load the trades to perform further analysis.
Freqtrade provides the `load_backtest_data()` helper function to easily load the backtest results, which takes the path to the the backtest-results file as parameter.
``` python
from freqtrade.data.btanalysis import load_backtest_data
df = load_backtest_data("user_data/backtest-result.json")
# Show value-counts per pair
df.groupby("pair")["sell_reason"].value_counts()
```
This will allow you to drill deeper into your backtest results, and perform analysis which otherwise would make the regular backtest-output very difficult to digest due to information overload.
If you have some ideas for interesting / helpful backtest data analysis ideas, please submit a Pull Request so the community can benefit from it.
## Live data
To analyze the trades your bot generated, you can load them to a DataFrame as follows:
``` python
from freqtrade.data.btanalysis import load_trades_from_db
df = load_trades_from_db("sqlite:///tradesv3.sqlite")
df.groupby("pair")["sell_reason"].value_counts()
```
Feel free to submit an issue or Pull Request enhancing this document if you would like to share ideas on how to best analyze the data.

View File

@ -58,7 +58,7 @@ Timerange doesn't work with live data.
To plot trades stored in a database use `--db-url` argument:
``` bash
python3 scripts/plot_dataframe.py --db-url sqlite:///tradesv3.dry_run.sqlite -p BTC/ETH
python3 scripts/plot_dataframe.py --db-url sqlite:///tradesv3.dry_run.sqlite -p BTC/ETH --trade-source DB
```
To plot trades from a backtesting result, use `--export-filename <filename>`

View File

@ -516,3 +516,11 @@ class Arguments(object):
default=750,
type=int,
)
parser.add_argument(
'--trade-source',
help='Specify the source for trades (Can be DB or file (backtest file)) '
'Default: %(default)s',
dest='trade_source',
default="file",
choices=["DB", "file"]
)

View File

@ -358,7 +358,8 @@ class Configuration(object):
self._args_to_config(config, argname='plot_limit',
logstring='Limiting plot to: {}')
self._args_to_config(config, argname='trade_source',
logstring='Using trades from: {}')
return config
def _validate_config_schema(self, conf: Dict[str, Any]) -> Dict[str, Any]:

View File

@ -73,37 +73,30 @@ def evaluate_result_multi(results: pd.DataFrame, freq: str, max_open_trades: int
return df_final[df_final['pair'] > max_open_trades]
def load_trades(db_url: str = None, exportfilename: str = None) -> pd.DataFrame:
def load_trades_from_db(db_url: str) -> pd.DataFrame:
"""
Load trades, either from a DB (using dburl) or via a backtest export file.
Load trades from a DB (using dburl)
:param db_url: Sqlite url (default format sqlite:///tradesv3.dry-run.sqlite)
:param exportfilename: Path to a file exported from backtesting
:return: Dataframe containing Trades
"""
timeZone = pytz.UTC
trades: pd.DataFrame = pd.DataFrame([], columns=BT_DATA_COLUMNS)
persistence.init(db_url, clean_open_orders=False)
columns = ["pair", "profit", "open_time", "close_time",
"open_rate", "close_rate", "duration", "sell_reason",
"max_rate", "min_rate"]
if db_url:
persistence.init(db_url, clean_open_orders=False)
columns = ["pair", "profit", "open_time", "close_time",
"open_rate", "close_rate", "duration"]
for x in Trade.query.all():
logger.info("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.all()],
columns=columns)
elif exportfilename:
trades = load_backtest_data(Path(exportfilename))
trades = pd.DataFrame([(t.pair, t.calc_profit(),
t.open_date.replace(tzinfo=pytz.UTC),
t.close_date.replace(tzinfo=pytz.UTC) 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,
t.sell_reason,
t.max_rate,
t.min_rate,
)
for t in Trade.query.all()],
columns=columns)
return trades

View File

@ -81,6 +81,8 @@ def plot_trades(fig, trades: pd.DataFrame):
)
fig.append_trace(trade_buys, 1, 1)
fig.append_trace(trade_sells, 1, 1)
else:
logger.warning("No trades found.")
return fig

View File

@ -7,7 +7,7 @@ from pandas import DataFrame, to_datetime
from freqtrade.arguments import TimeRange
from freqtrade.data.btanalysis import (BT_DATA_COLUMNS,
extract_trades_of_period,
load_backtest_data, load_trades)
load_backtest_data, load_trades_from_db)
from freqtrade.data.history import load_pair_history, make_testdata_path
from freqtrade.tests.test_persistence import create_mock_trades
@ -28,14 +28,6 @@ def test_load_backtest_data():
load_backtest_data(str("filename") + "nofile")
def test_load_trades_file(default_conf, fee, mocker):
# Real testing of load_backtest_data is done in test_load_backtest_data
lbt = mocker.patch("freqtrade.data.btanalysis.load_backtest_data", MagicMock())
filename = make_testdata_path(None) / "backtest-result_test.json"
load_trades(db_url=None, exportfilename=filename)
assert lbt.call_count == 1
@pytest.mark.usefixtures("init_persistence")
def test_load_trades_db(default_conf, fee, mocker):
@ -43,7 +35,7 @@ def test_load_trades_db(default_conf, fee, mocker):
# remove init so it does not init again
init_mock = mocker.patch('freqtrade.persistence.init', MagicMock())
trades = load_trades(db_url=default_conf['db_url'], exportfilename=None)
trades = load_trades_from_db(db_url=default_conf['db_url'])
assert init_mock.call_count == 1
assert len(trades) == 3
assert isinstance(trades, DataFrame)

View File

@ -67,11 +67,12 @@ def test_generate_row(default_conf, caplog):
assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog.record_tuples)
def test_plot_trades():
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)

View File

@ -17,6 +17,7 @@ nav:
- Plotting: plotting.md
- Deprecated features: deprecated.md
- FAQ: faq.md
- Data Analysis: data-analysis.md
- SQL Cheatsheet: sql_cheatsheet.md
- Sandbox testing: sandbox-testing.md
- Contributors guide: developer.md

View File

@ -33,10 +33,10 @@ import pandas as pd
from freqtrade.arguments import Arguments
from freqtrade.data import history
from freqtrade.data.btanalysis import load_trades, extract_trades_of_period
from freqtrade.data.btanalysis import (extract_trades_of_period,
load_backtest_data, load_trades_from_db)
from freqtrade.optimize import setup_configuration
from freqtrade.plot.plotting import (generate_graph,
generate_plot_file)
from freqtrade.plot.plotting import generate_graph, generate_plot_file
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
from freqtrade.state import RunMode
@ -97,9 +97,11 @@ def analyse_and_plot_pairs(config: Dict[str, Any]):
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 = load_trades(db_url=config["db_url"],
exportfilename=config["exportfilename"])
trades = trades.loc[trades['pair'] == pair]
trades = extract_trades_of_period(dataframe, trades)