From 8418dfbaed20a4794addff924585e1c2149cfbf3 Mon Sep 17 00:00:00 2001 From: Jonathan Raviotta Date: Tue, 6 Aug 2019 22:35:14 -0400 Subject: [PATCH 01/10] edits for jupyter notebook example --- .gitignore | 4 +- docs/data-analysis.md | 168 ++++++------------ environment.yml | 1 + user_data/notebooks/.gitkeep | 0 user_data/notebooks/analysis_example.ipynb | 187 +++++++++++++++++++++ 5 files changed, 239 insertions(+), 121 deletions(-) create mode 100644 user_data/notebooks/.gitkeep create mode 100644 user_data/notebooks/analysis_example.ipynb diff --git a/.gitignore b/.gitignore index 9ed046c40..3a9df9852 100644 --- a/.gitignore +++ b/.gitignore @@ -81,7 +81,6 @@ target/ # Jupyter Notebook .ipynb_checkpoints -*.ipynb # pyenv .python-version @@ -93,3 +92,6 @@ target/ .pytest_cache/ .mypy_cache/ + +#exceptions +!user_data/noteboks/*example.ipynb diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 2b6d6ed58..826c34747 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -1,164 +1,92 @@ # 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. +You can analyze the results of backtests and trading history easily using Jupyter notebooks. A sample notebook is located at `user_data/notebooks/analysis_example.ipynb`. For usage instructions, see [jupyter.org](https://jupyter.org/documentation). -A good way for this is using Jupyter (notebook or lab) - which provides an interactive environment to analyze the data. +## Strategy debugging example -The following helpers will help you loading the data into Pandas DataFrames, and may also give you some starting points in analyzing the results. +Debugging a strategy can be time-consuming. FreqTrade offers helper functions to visualize raw data. -## Strategy development problem analysis - -Debugging a strategy (are there no buy signals, ...) can be very time-consuming. -FreqTrade tries to help you by exposing a few helper-functions, which can be very handy. - -It's recommended using Juptyer Notebooks for analysis, since it offers a dynamic way to rerun certain parts of the code. - -The following is a full code-snippet, which will be explained by both comments, and step by step below. +### Import requirements and define variables used in the script ```python -# Some necessary imports +# Imports from pathlib import Path - +import os from freqtrade.data.history import load_pair_history from freqtrade.resolvers import StrategyResolver +from freqtrade.data.btanalysis import load_backtest_data +from freqtrade.data.btanalysis import load_trades_from_db + # Define some constants -ticker_interval = "5m" - +ticker_interval = "1m" # Name of the strategy class -strategyname = 'Awesomestrategy' +strategy_name = 'NewStrategy' +# Path to user data +user_data_dir = 'user_data' # Location of the strategy -strategy_location = '../xmatt/strategies' +strategy_location = os.path.join(user_data_dir, 'strategies') # Location of the data -data_location = '../freqtrade/user_data/data/binance/' +data_location = os.path.join(user_data_dir, 'data', 'binance') +# Pair to analyze # Only use one pair here -pair = "XRP_ETH" +pair = "BTC_USDT" +``` -### End constants +### Load exchange data -# Load data +```python +# Load data using values set above bt_data = load_pair_history(datadir=Path(data_location), - ticker_interval = ticker_interval, + ticker_interval=ticker_interval, pair=pair) -print(len(bt_data)) - -### Start strategy reload -# Load strategy - best done in a new cell -# Rerun each time the strategy-file is changed. -strategy = StrategyResolver({'strategy': strategyname, - 'user_data_dir': Path.cwd(), - 'strategy_path': location}).strategy - -# Run strategy (just like in backtesting) -df = strategy.analyze_ticker(bt_data, {'pair': pair}) -print(f"Generated {df['buy'].sum()} buy signals") - -# Reindex data to be "nicer" and show data -data = df.set_index('date', drop=True) -data.tail() +# Confirm success +print("Loaded " + str(len(bt_data)) + f" rows of data for {pair} from {data_location}") ``` -### Explanation +### Load and run strategy -#### Imports and constant definition +* Rerun each time the strategy file is changed +* Display the trade details. Note that using `data.head()` would also work, however most indicators have some "startup" data at the top of the dataframe. -``` python -# Some necessary imports -from pathlib import Path - -from freqtrade.data.history import load_pair_history -from freqtrade.resolvers import StrategyResolver -# Define some constants -ticker_interval = "5m" - -# Name of the strategy class -strategyname = 'Awesomestrategy' -# Location of the strategy -strategy_location = 'user_data/strategies' -# Location of the data -data_location = 'user_data/data/binance' -# Only use one pair here -pair = "XRP_ETH" -``` - -This first section imports necessary modules, and defines some constants you'll probably need to adjust for your case. - -#### Load candles - -``` python -# Load data -bt_data = load_pair_history(datadir=Path(data_location), - ticker_interval = ticker_interval, - pair=pair) -print(len(bt_data)) -``` - -This second section loads the historic data and prints the amount of candles in the DataFrame. -You can also inspect this dataframe by using `bt_data.head()` or `bt_data.tail()`. - -#### Run strategy and analyze results - -Now, it's time to load and run your strategy. -For this, I recommend using a new cell in your notebook, since you'll want to repeat this until you're satisfied with your strategy. - -``` python -# Load strategy - best done in a new cell -# Needs to be ran each time the strategy-file is changed. -strategy = StrategyResolver({'strategy': strategyname, - 'user_data_dir': Path.cwd(), - 'strategy_path': location}).strategy - -# Run strategy (just like in backtesting) -df = strategy.analyze_ticker(bt_data, {'pair': pair}) -print(f"Generated {df['buy'].sum()} buy signals") - -# Reindex data to be "nicer" and show data -data = df.set_index('date', drop=True) -data.tail() -``` - -The code snippet loads and analyzes the strategy, calculates and prints the number of buy signals. - -The last 2 lines serve to analyze the dataframe in detail. -This can be important if your strategy did not generate any buy signals. -Note that using `data.head()` would also work, however this is misleading since most indicators have some "startup" time at the start of a backtested dataframe. - -There can be many things wrong, some signs to look for are: +Some possible problems: * Columns with NaN values at the end of the dataframe * Columns used in `crossed*()` functions with completely different units -## Backtesting +```python +# Load strategy using values set above +strategy = StrategyResolver({'strategy': strategy_name, + 'user_data_dir': user_data_dir, + 'strategy_path': strategy_location}).strategy -To analyze your backtest results, you can [export the trades](#exporting-trades-to-file). -You can then load the trades to perform further analysis. +# Run strategy (just like in backtesting) +df = strategy.analyze_ticker(bt_data, {'pair': pair}) -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. +# Report results +print(f"Generated {df['buy'].sum()} buy signals") +data = df.set_index('date', drop=True) +data.tail() +``` -``` python -from freqtrade.data.btanalysis import load_backtest_data -df = load_backtest_data("user_data/backtest-result.json") +### Load backtest results into a pandas dataframe + +```python +# Load backtest results +df = load_backtest_data("user_data/backtest_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: +### Load live trading results into a pandas dataframe ``` python -from freqtrade.data.btanalysis import load_trades_from_db - +# Fetch trades from database df = load_trades_from_db("sqlite:///tradesv3.sqlite") +# Display results 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. diff --git a/environment.yml b/environment.yml index 8f43b6991..cd3350fd5 100644 --- a/environment.yml +++ b/environment.yml @@ -37,6 +37,7 @@ dependencies: - coveralls - mypy # Useful for jupyter + - jupyter - ipykernel - isort - yapf diff --git a/user_data/notebooks/.gitkeep b/user_data/notebooks/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/user_data/notebooks/analysis_example.ipynb b/user_data/notebooks/analysis_example.ipynb new file mode 100644 index 000000000..f9c9b27bc --- /dev/null +++ b/user_data/notebooks/analysis_example.ipynb @@ -0,0 +1,187 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Strategy debugging example" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Change directory\n", + "# Define all paths relative to the project root shown in the cell output\n", + "import os\n", + "try:\n", + "\tos.chdir(os.path.join(os.getcwd(), '../..'))\n", + "\tprint(os.getcwd())\n", + "except:\n", + "\tpass\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Import requirements and define variables used in the script" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Imports\n", + "from pathlib import Path\n", + "import os\n", + "from freqtrade.data.history import load_pair_history\n", + "from freqtrade.resolvers import StrategyResolver\n", + "from freqtrade.data.btanalysis import load_backtest_data\n", + "from freqtrade.data.btanalysis import load_trades_from_db\n", + "\n", + "# Define some constants\n", + "ticker_interval = \"1m\"\n", + "# Name of the strategy class\n", + "strategy_name = 'NewStrategy'\n", + "# Path to user data\n", + "user_data_dir = 'user_data'\n", + "# Location of the strategy\n", + "strategy_location = os.path.join(user_data_dir, 'strategies')\n", + "# Location of the data\n", + "data_location = os.path.join(user_data_dir, 'data', 'binance')\n", + "# Pair to analyze \n", + "# Only use one pair here\n", + "pair = \"BTC_USDT\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load exchange data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load data using values set above\n", + "bt_data = load_pair_history(datadir=Path(data_location),\n", + " ticker_interval=ticker_interval,\n", + " pair=pair)\n", + "\n", + "# Confirm success\n", + "print(\"Loaded \" + str(len(bt_data)) + f\" rows of data for {pair} from {data_location}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load and run strategy \n", + "\n", + "* Rerun each time the strategy file is changed\n", + "* Display the trade details. Note that using `data.head()` would also work, however most indicators have some \"startup\" data at the top of the dataframe.\n", + "\n", + "Some possible problems:\n", + "\n", + "* Columns with NaN values at the end of the dataframe\n", + "* Columns used in `crossed*()` functions with completely different units" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load strategy using values set above\n", + "strategy = StrategyResolver({'strategy': strategy_name,\n", + " 'user_data_dir': user_data_dir,\n", + " 'strategy_path': strategy_location}).strategy\n", + "\n", + "# Run strategy (just like in backtesting)\n", + "df = strategy.analyze_ticker(bt_data, {'pair': pair})\n", + "\n", + "# Report results\n", + "print(f\"Generated {df['buy'].sum()} buy signals\")\n", + "data = df.set_index('date', drop=True)\n", + "data.tail()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load backtest results into a pandas dataframe" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load backtest results\n", + "df = load_backtest_data(\"user_data/backtest_data/backtest-result.json\")\n", + "\n", + "# Show value-counts per pair\n", + "df.groupby(\"pair\")[\"sell_reason\"].value_counts()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load live trading results into a pandas dataframe" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Fetch trades from database\n", + "df = load_trades_from_db(\"sqlite:///tradesv3.sqlite\")\n", + "\n", + "# Display results\n", + "df.groupby(\"pair\")[\"sell_reason\"].value_counts()" + ] + } + ], + "metadata": { + "file_extension": ".py", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "mimetype": "text/x-python", + "name": "python", + "npconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": 3 + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 9df1c23c71639bd95a171b861ac604ae58d769a4 Mon Sep 17 00:00:00 2001 From: Jonathan Raviotta Date: Wed, 7 Aug 2019 19:48:55 -0400 Subject: [PATCH 02/10] changed Path, added jupyter --- setup.py | 2 ++ user_data/notebooks/analysis_example.ipynb | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 202e3fd0d..b46101c0f 100644 --- a/setup.py +++ b/setup.py @@ -68,6 +68,8 @@ setup(name='freqtrade', 'dev': all_extra, 'plot': plot, 'all': all_extra, + 'jupyter': [], + }, include_package_data=True, zip_safe=False, diff --git a/user_data/notebooks/analysis_example.ipynb b/user_data/notebooks/analysis_example.ipynb index f9c9b27bc..298269c7a 100644 --- a/user_data/notebooks/analysis_example.ipynb +++ b/user_data/notebooks/analysis_example.ipynb @@ -20,7 +20,7 @@ "\tos.chdir(os.path.join(os.getcwd(), '../..'))\n", "\tprint(os.getcwd())\n", "except:\n", - "\tpass\n" + "\tpass" ] }, { @@ -38,7 +38,6 @@ "source": [ "# Imports\n", "from pathlib import Path\n", - "import os\n", "from freqtrade.data.history import load_pair_history\n", "from freqtrade.resolvers import StrategyResolver\n", "from freqtrade.data.btanalysis import load_backtest_data\n", @@ -51,9 +50,9 @@ "# Path to user data\n", "user_data_dir = 'user_data'\n", "# Location of the strategy\n", - "strategy_location = os.path.join(user_data_dir, 'strategies')\n", + "strategy_location = Path(user_data_dir, 'strategies')\n", "# Location of the data\n", - "data_location = os.path.join(user_data_dir, 'data', 'binance')\n", + "data_location = Path(user_data_dir, 'data', 'binance')\n", "# Pair to analyze \n", "# Only use one pair here\n", "pair = \"BTC_USDT\"" From 2bc67b4a96876cefd55efc6ded6363e94968c670 Mon Sep 17 00:00:00 2001 From: Jonathan Raviotta Date: Wed, 7 Aug 2019 20:47:37 -0400 Subject: [PATCH 03/10] missed a call of os.path. removed it. --- user_data/notebooks/analysis_example.ipynb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/user_data/notebooks/analysis_example.ipynb b/user_data/notebooks/analysis_example.ipynb index 298269c7a..d14575e97 100644 --- a/user_data/notebooks/analysis_example.ipynb +++ b/user_data/notebooks/analysis_example.ipynb @@ -16,8 +16,9 @@ "# Change directory\n", "# Define all paths relative to the project root shown in the cell output\n", "import os\n", + "from pathlib import Path\n", "try:\n", - "\tos.chdir(os.path.join(os.getcwd(), '../..'))\n", + "\tos.chdir(Path(os.getcwd(), '../..'))\n", "\tprint(os.getcwd())\n", "except:\n", "\tpass" From ccf3c6987435827af3649d19909fe78a0a3f4f25 Mon Sep 17 00:00:00 2001 From: Jonathan Raviotta Date: Thu, 8 Aug 2019 22:09:15 -0400 Subject: [PATCH 04/10] edits to clarify backtesting analysis --- docs/data-analysis.md | 70 +++++--- setup.py | 12 +- user_data/notebooks/analysis_example.ipynb | 195 +++++++++++++-------- 3 files changed, 176 insertions(+), 101 deletions(-) diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 826c34747..3b07d77a6 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -2,11 +2,33 @@ You can analyze the results of backtests and trading history easily using Jupyter notebooks. A sample notebook is located at `user_data/notebooks/analysis_example.ipynb`. For usage instructions, see [jupyter.org](https://jupyter.org/documentation). +## Example snippets + +### Load backtest results into a pandas dataframe + +```python +# Load backtest results +df = load_backtest_data("user_data/backtest_data/backtest-result.json") + +# Show value-counts per pair +df.groupby("pair")["sell_reason"].value_counts() +``` + +### Load live trading results into a pandas dataframe + +``` python +# Fetch trades from database +df = load_trades_from_db("sqlite:///tradesv3.sqlite") + +# Display results +df.groupby("pair")["sell_reason"].value_counts() +``` + ## Strategy debugging example Debugging a strategy can be time-consuming. FreqTrade offers helper functions to visualize raw data. -### Import requirements and define variables used in the script +### Import requirements and define variables used in analyses ```python # Imports @@ -47,12 +69,6 @@ print("Loaded " + str(len(bt_data)) + f" rows of data for {pair} from {data_loca ### Load and run strategy * Rerun each time the strategy file is changed -* Display the trade details. Note that using `data.head()` would also work, however most indicators have some "startup" data at the top of the dataframe. - -Some possible problems: - -* Columns with NaN values at the end of the dataframe -* Columns used in `crossed*()` functions with completely different units ```python # Load strategy using values set above @@ -60,33 +76,31 @@ strategy = StrategyResolver({'strategy': strategy_name, 'user_data_dir': user_data_dir, 'strategy_path': strategy_location}).strategy -# Run strategy (just like in backtesting) +# Generate buy/sell signals using strategy df = strategy.analyze_ticker(bt_data, {'pair': pair}) +``` +### Display the trade details + +* Note that using `data.head()` would also work, however most indicators have some "startup" data at the top of the dataframe. + +#### Some possible problems + +* Columns with NaN values at the end of the dataframe +* Columns used in `crossed*()` functions with completely different units + +#### Comparison with full backtest + +having 200 buy signals as output for one pair from `analyze_ticker()` does not necessarily mean that 200 trades will be made during backtesting. + +Assuming you use only one condition such as, `df['rsi'] < 30` as buy condition, this will generate multiple "buy" signals for each pair in sequence (until rsi returns > 29). +The bot will only buy on the first of these signals (and also only if a trade-slot ("max_open_trades") is still available), or on one of the middle signals, as soon as a "slot" becomes available. + +```python # Report results print(f"Generated {df['buy'].sum()} buy signals") data = df.set_index('date', drop=True) data.tail() ``` -### Load backtest results into a pandas dataframe - -```python -# Load backtest results -df = load_backtest_data("user_data/backtest_data/backtest-result.json") - -# Show value-counts per pair -df.groupby("pair")["sell_reason"].value_counts() -``` - -### Load live trading results into a pandas dataframe - -``` python -# Fetch trades from database -df = load_trades_from_db("sqlite:///tradesv3.sqlite") - -# Display results -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. diff --git a/setup.py b/setup.py index b46101c0f..6ac7a117f 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,15 @@ develop = [ 'pytest-random-order', ] -all_extra = api + plot + develop +jupyter = [ + 'jupyter', + 'nbstripout', + 'ipykernel', + 'isort', + 'yapf', + ] + +all_extra = api + plot + develop + jupyter setup(name='freqtrade', version=__version__, @@ -68,7 +76,7 @@ setup(name='freqtrade', 'dev': all_extra, 'plot': plot, 'all': all_extra, - 'jupyter': [], + 'jupyter': jupyter, }, include_package_data=True, diff --git a/user_data/notebooks/analysis_example.ipynb b/user_data/notebooks/analysis_example.ipynb index d14575e97..b3a61bc51 100644 --- a/user_data/notebooks/analysis_example.ipynb +++ b/user_data/notebooks/analysis_example.ipynb @@ -4,31 +4,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Strategy debugging example" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Change directory\n", - "# Define all paths relative to the project root shown in the cell output\n", - "import os\n", - "from pathlib import Path\n", - "try:\n", - "\tos.chdir(Path(os.getcwd(), '../..'))\n", - "\tprint(os.getcwd())\n", - "except:\n", - "\tpass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Import requirements and define variables used in the script" + "# Analyzing bot data\n", + "\n", + "You can analyze the results of backtests and trading history easily using Jupyter notebooks. A sample notebook is located at `user_data/notebooks/analysis_example.ipynb`. For usage instructions, see [jupyter.org](https://jupyter.org/documentation)." ] }, { @@ -39,11 +17,97 @@ "source": [ "# Imports\n", "from pathlib import Path\n", + "import os\n", "from freqtrade.data.history import load_pair_history\n", "from freqtrade.resolvers import StrategyResolver\n", "from freqtrade.data.btanalysis import load_backtest_data\n", - "from freqtrade.data.btanalysis import load_trades_from_db\n", + "from freqtrade.data.btanalysis import load_trades_from_db" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Change directory\n", + "# Define all paths relative to the project root shown in the cell output\n", + "try:\n", + "\tos.chdir(Path(Path.cwd(), '../..'))\n", + "\tprint(Path.cwd())\n", + "except:\n", + "\tpass" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example snippets" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load backtest results into a pandas dataframe" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load backtest results\n", + "df = load_backtest_data(\"user_data/backtest_data/backtest-result.json\")\n", "\n", + "# Show value-counts per pair\n", + "df.groupby(\"pair\")[\"sell_reason\"].value_counts()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load live trading results into a pandas dataframe" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Fetch trades from database\n", + "df = load_trades_from_db(\"sqlite:///tradesv3.sqlite\")\n", + "\n", + "# Display results\n", + "df.groupby(\"pair\")[\"sell_reason\"].value_counts()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Strategy debugging example\n", + "\n", + "Debugging a strategy can be time-consuming. FreqTrade offers helper functions to visualize raw data." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Import requirements and define variables used in analyses" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "# Define some constants\n", "ticker_interval = \"1m\"\n", "# Name of the strategy class\n", @@ -51,9 +115,9 @@ "# Path to user data\n", "user_data_dir = 'user_data'\n", "# Location of the strategy\n", - "strategy_location = Path(user_data_dir, 'strategies')\n", + "strategy_location = os.path.join(user_data_dir, 'strategies')\n", "# Location of the data\n", - "data_location = Path(user_data_dir, 'data', 'binance')\n", + "data_location = os.path.join(user_data_dir, 'data', 'binance')\n", "# Pair to analyze \n", "# Only use one pair here\n", "pair = \"BTC_USDT\"" @@ -85,15 +149,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Load and run strategy \n", - "\n", - "* Rerun each time the strategy file is changed\n", - "* Display the trade details. Note that using `data.head()` would also work, however most indicators have some \"startup\" data at the top of the dataframe.\n", - "\n", - "Some possible problems:\n", - "\n", - "* Columns with NaN values at the end of the dataframe\n", - "* Columns used in `crossed*()` functions with completely different units" + "### Load and run strategy\n", + "* Rerun each time the strategy file is changed" ] }, { @@ -107,53 +164,49 @@ " 'user_data_dir': user_data_dir,\n", " 'strategy_path': strategy_location}).strategy\n", "\n", - "# Run strategy (just like in backtesting)\n", - "df = strategy.analyze_ticker(bt_data, {'pair': pair})\n", + "# Generate buy/sell signals using strategy\n", + "df = strategy.analyze_ticker(bt_data, {'pair': pair})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Display the trade details\n", + "* Note that using `data.head()` would also work, however most indicators have some \"startup\" data at the top of the dataframe.\n", "\n", + "#### Some possible problems\n", + "\n", + "* Columns with NaN values at the end of the dataframe\n", + "* Columns used in `crossed*()` functions with completely different units\n", + "\n", + "#### Comparison with full backtest\n", + "\n", + "having 200 buy signals as output for one pair from `analyze_ticker()` does not necessarily mean that 200 trades will be made during backtesting.\n", + "\n", + "Assuming you use only one condition such as, `df['rsi'] < 30` as buy condition, this will generate multiple \"buy\" signals for each pair in sequence (until rsi returns > 29).\n", + "The bot will only buy on the first of these signals (and also only if a trade-slot (\"max_open_trades\") is still available), or on one of the middle signals, as soon as a \"slot\" becomes available.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "# Report results\n", "print(f\"Generated {df['buy'].sum()} buy signals\")\n", "data = df.set_index('date', drop=True)\n", "data.tail()" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Load backtest results into a pandas dataframe" - ] - }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "# Load backtest results\n", - "df = load_backtest_data(\"user_data/backtest_data/backtest-result.json\")\n", - "\n", - "# Show value-counts per pair\n", - "df.groupby(\"pair\")[\"sell_reason\"].value_counts()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Load live trading results into a pandas dataframe" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Fetch trades from database\n", - "df = load_trades_from_db(\"sqlite:///tradesv3.sqlite\")\n", - "\n", - "# Display results\n", - "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." ] } ], From 51d59e673b277c5bd0fa4bb343b99feea8c6453c Mon Sep 17 00:00:00 2001 From: Jonathan Raviotta Date: Fri, 9 Aug 2019 11:36:53 -0400 Subject: [PATCH 05/10] fixed another instance of Path in docs and nb --- docs/data-analysis.md | 4 ++-- user_data/notebooks/analysis_example.ipynb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 3b07d77a6..f79d1674e 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -46,9 +46,9 @@ strategy_name = 'NewStrategy' # Path to user data user_data_dir = 'user_data' # Location of the strategy -strategy_location = os.path.join(user_data_dir, 'strategies') +strategy_location = Path(user_data_dir, 'strategies') # Location of the data -data_location = os.path.join(user_data_dir, 'data', 'binance') +data_location = Path(user_data_dir, 'data', 'binance') # Pair to analyze # Only use one pair here pair = "BTC_USDT" diff --git a/user_data/notebooks/analysis_example.ipynb b/user_data/notebooks/analysis_example.ipynb index b3a61bc51..b8bcdd7bf 100644 --- a/user_data/notebooks/analysis_example.ipynb +++ b/user_data/notebooks/analysis_example.ipynb @@ -115,9 +115,9 @@ "# Path to user data\n", "user_data_dir = 'user_data'\n", "# Location of the strategy\n", - "strategy_location = os.path.join(user_data_dir, 'strategies')\n", + "strategy_location = Path(user_data_dir, 'strategies')\n", "# Location of the data\n", - "data_location = os.path.join(user_data_dir, 'data', 'binance')\n", + "data_location = Path(user_data_dir, 'data', 'binance')\n", "# Pair to analyze \n", "# Only use one pair here\n", "pair = \"BTC_USDT\"" From 247d7475e17cff5f18e3dc8e31667cbeac44e3a9 Mon Sep 17 00:00:00 2001 From: Jonathan Raviotta Date: Fri, 9 Aug 2019 11:41:05 -0400 Subject: [PATCH 06/10] fixes to example notebook. --- user_data/notebooks/analysis_example.ipynb | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/user_data/notebooks/analysis_example.ipynb b/user_data/notebooks/analysis_example.ipynb index b8bcdd7bf..a92855add 100644 --- a/user_data/notebooks/analysis_example.ipynb +++ b/user_data/notebooks/analysis_example.ipynb @@ -33,10 +33,10 @@ "# Change directory\n", "# Define all paths relative to the project root shown in the cell output\n", "try:\n", - "\tos.chdir(Path(Path.cwd(), '../..'))\n", - "\tprint(Path.cwd())\n", + " os.chdir(Path(Path.cwd(), '../..'))\n", + " print(Path.cwd())\n", "except:\n", - "\tpass" + " pass" ] }, { @@ -109,7 +109,7 @@ "outputs": [], "source": [ "# Define some constants\n", - "ticker_interval = \"1m\"\n", + "ticker_interval = \"5m\"\n", "# Name of the strategy class\n", "strategy_name = 'NewStrategy'\n", "# Path to user data\n", @@ -201,10 +201,8 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ "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." ] From 3cc772c8e9eead8a11cc7208a84bdaca6f580240 Mon Sep 17 00:00:00 2001 From: Jonathan Raviotta Date: Fri, 9 Aug 2019 11:53:29 -0400 Subject: [PATCH 07/10] added reminders --- docs/data-analysis.md | 2 ++ user_data/notebooks/analysis_example.ipynb | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/data-analysis.md b/docs/data-analysis.md index f79d1674e..2e2ebc3c6 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -2,6 +2,8 @@ You can analyze the results of backtests and trading history easily using Jupyter notebooks. A sample notebook is located at `user_data/notebooks/analysis_example.ipynb`. For usage instructions, see [jupyter.org](https://jupyter.org/documentation). +*Pro tip - Don't forget to start a jupyter notbook server from within your conda or venv environment or use [nb_conda_kernels](https://github.com/Anaconda-Platform/nb_conda_kernels)* + ## Example snippets ### Load backtest results into a pandas dataframe diff --git a/user_data/notebooks/analysis_example.ipynb b/user_data/notebooks/analysis_example.ipynb index a92855add..30e9e1a97 100644 --- a/user_data/notebooks/analysis_example.ipynb +++ b/user_data/notebooks/analysis_example.ipynb @@ -6,7 +6,12 @@ "source": [ "# Analyzing bot data\n", "\n", - "You can analyze the results of backtests and trading history easily using Jupyter notebooks. A sample notebook is located at `user_data/notebooks/analysis_example.ipynb`. For usage instructions, see [jupyter.org](https://jupyter.org/documentation)." + "You can analyze the results of backtests and trading history easily using Jupyter notebooks. \n", + "**Copy this file so your changes don't get clobbered with the next freqtrade update!** \n", + "For usage instructions, see [jupyter.org](https://jupyter.org/documentation). \n", + "*Pro tip - Don't forget to start a jupyter notbook server from within your conda or venv environment or use [nb_conda_kernels](https://github.com/Anaconda-Platform/nb_conda_kernels)*\n", + "\n", + "\n" ] }, { From dd35ba5e812589ec1da130681df2fb38ba87cd26 Mon Sep 17 00:00:00 2001 From: Jonathan Raviotta Date: Fri, 9 Aug 2019 17:06:19 -0400 Subject: [PATCH 08/10] added imports to doc code blocks. --- docs/data-analysis.md | 9 +++++---- user_data/notebooks/analysis_example.ipynb | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 2e2ebc3c6..5db9e6c3b 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -9,6 +9,7 @@ You can analyze the results of backtests and trading history easily using Jupyte ### Load backtest results into a pandas dataframe ```python +from freqtrade.data.btanalysis import load_backtest_data # Load backtest results df = load_backtest_data("user_data/backtest_data/backtest-result.json") @@ -19,6 +20,8 @@ df.groupby("pair")["sell_reason"].value_counts() ### Load live trading results into a pandas dataframe ``` python +from freqtrade.data.btanalysis import load_trades_from_db + # Fetch trades from database df = load_trades_from_db("sqlite:///tradesv3.sqlite") @@ -38,13 +41,11 @@ from pathlib import Path import os from freqtrade.data.history import load_pair_history from freqtrade.resolvers import StrategyResolver -from freqtrade.data.btanalysis import load_backtest_data -from freqtrade.data.btanalysis import load_trades_from_db # Define some constants -ticker_interval = "1m" +ticker_interval = "5m" # Name of the strategy class -strategy_name = 'NewStrategy' +strategy_name = 'AwesomeStrategy' # Path to user data user_data_dir = 'user_data' # Location of the strategy diff --git a/user_data/notebooks/analysis_example.ipynb b/user_data/notebooks/analysis_example.ipynb index 30e9e1a97..f5e2c12d7 100644 --- a/user_data/notebooks/analysis_example.ipynb +++ b/user_data/notebooks/analysis_example.ipynb @@ -116,7 +116,7 @@ "# Define some constants\n", "ticker_interval = \"5m\"\n", "# Name of the strategy class\n", - "strategy_name = 'NewStrategy'\n", + "strategy_name = 'AwesomeStrategy'\n", "# Path to user data\n", "user_data_dir = 'user_data'\n", "# Location of the strategy\n", From 8eb39178ea2668af09a272128e3823d30910d059 Mon Sep 17 00:00:00 2001 From: Jonathan Raviotta Date: Fri, 9 Aug 2019 17:24:17 -0400 Subject: [PATCH 09/10] code block instructions. removed extra packages --- docs/data-analysis.md | 5 ++++- setup.py | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 5db9e6c3b..66bb50694 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -42,6 +42,9 @@ import os from freqtrade.data.history import load_pair_history from freqtrade.resolvers import StrategyResolver +# You can override strategy settings as demonstrated below. +# Customize these according to your needs. + # Define some constants ticker_interval = "5m" # Name of the strategy class @@ -66,7 +69,7 @@ bt_data = load_pair_history(datadir=Path(data_location), pair=pair) # Confirm success -print("Loaded " + str(len(bt_data)) + f" rows of data for {pair} from {data_location}") +print(f"Loaded {len(bt_data)} rows of data for {pair} from {data_location}") ``` ### Load and run strategy diff --git a/setup.py b/setup.py index 6ac7a117f..41e1b8f45 100644 --- a/setup.py +++ b/setup.py @@ -29,8 +29,6 @@ jupyter = [ 'jupyter', 'nbstripout', 'ipykernel', - 'isort', - 'yapf', ] all_extra = api + plot + develop + jupyter From ab092fc77f291e1b5de9356729bf692803d59ca3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 10 Aug 2019 15:45:41 +0200 Subject: [PATCH 10/10] Reinstate comment on backesting data --- docs/data-analysis.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 66bb50694..ecd94445b 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -17,6 +17,8 @@ df = load_backtest_data("user_data/backtest_data/backtest-result.json") 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. + ### Load live trading results into a pandas dataframe ``` python