added returns for easier notebook plotting

This commit is contained in:
Jonathan Raviotta 2019-09-14 22:04:52 -04:00
parent 0477e7938d
commit 85a0d2f6cb
2 changed files with 97 additions and 84 deletions

View File

@ -332,13 +332,14 @@ def load_and_plot_trades(config: Dict[str, Any]):
- Load trades excecuted during the selected period - Load trades excecuted during the selected period
- Generate Plotly plot objects - Generate Plotly plot objects
- Generate plot files - Generate plot files
:return: None :return: Dict of fig, data, trades for each pair and interval
""" """
strategy = StrategyResolver(config).strategy strategy = StrategyResolver(config).strategy
plot_elements = init_plotscript(config) plot_elements = init_plotscript(config)
trades = plot_elements['trades'] trades = plot_elements['trades']
pair_counter = 0 pair_counter = 0
plot_data = {}
for pair, data in plot_elements["tickers"].items(): for pair, data in plot_elements["tickers"].items():
pair_counter += 1 pair_counter += 1
logger.info("analyse pair %s", pair) logger.info("analyse pair %s", pair)
@ -359,16 +360,23 @@ def load_and_plot_trades(config: Dict[str, Any]):
store_plot_file(fig, filename=generate_plot_filename(pair, config['ticker_interval']), store_plot_file(fig, filename=generate_plot_filename(pair, config['ticker_interval']),
directory=config['user_data_dir'] / "plot") directory=config['user_data_dir'] / "plot")
plot_data[generate_plot_filename(pair,
config['ticker_interval'])] = {"fig": fig,
"data": dataframe,
"trades": trades}
logger.info('End of plotting process. %s plots generated', pair_counter) logger.info('End of plotting process. %s plots generated', pair_counter)
logger.info(f'fig, data, trades are available with the keys {list(plot_data)}')
return plot_data
def plot_profit(config: Dict[str, Any]) -> None: def plot_profit(config: Dict[str, Any], auto_open: bool = False) -> None:
""" """
Plots the total profit for all pairs. Plots the total profit for all pairs.
Note, the profit calculation isn't realistic. Note, the profit calculation isn't realistic.
But should be somewhat proportional, and therefor useful But should be somewhat proportional, and therefor useful
in helping out to find a good algorithm. in helping out to find a good algorithm.
:return: profit plot
""" """
plot_elements = init_plotscript(config) plot_elements = init_plotscript(config)
trades = load_trades(config['trade_source'], trades = load_trades(config['trade_source'],
@ -382,4 +390,5 @@ def plot_profit(config: Dict[str, Any]) -> None:
# this could be useful to gauge the overall market trend # this could be useful to gauge the overall market trend
fig = generate_profit_graph(plot_elements["pairs"], plot_elements["tickers"], trades) fig = generate_profit_graph(plot_elements["pairs"], plot_elements["tickers"], trades)
store_plot_file(fig, filename='freqtrade-profit-plot.html', store_plot_file(fig, filename='freqtrade-profit-plot.html',
directory=config['user_data_dir'] / "plot", auto_open=True) directory=config['user_data_dir'] / "plot", auto_open=auto_open)
return fig

View File

@ -89,31 +89,38 @@
"# Specify values for use in this script\n", "# Specify values for use in this script\n",
"############### Customize to match your needs. ##################\n", "############### Customize to match your needs. ##################\n",
"config_files = [\n", "config_files = [\n",
" Path('user_data', 'config.json'),\n", " Path('user_data', 'user_repo', 'config.json'),\n",
" Path(Path.home(), '.freqtrade', 'config.json')\n", " Path(Path.home(), '.freqtrade', 'exchange-config.json')\n",
"]\n", "]\n",
"# Create config object\n", "# Create config object\n",
"config = Configuration.from_files(config_files)\n", "config = Configuration.from_files(config_files)\n",
"\n", "\n",
"############### Customize to match your needs. ##################\n", "############### Customize to match your needs. ##################\n",
"# Define some constants\n", "# Define some constants\n",
"ticker_interval = \"5m\"\n", "\n",
"# Path to user data\n", "# These values must be included in config.json or set here\n",
"user_data_dir = Path('user_data')\n",
"# Location of the ticker data\n",
"datadir = Path(user_data_dir, 'data/binance')\n",
"# Name of the strategy class\n", "# Name of the strategy class\n",
"strategy_name = 'DefaultStrategy'\n", "config['strategy'] = 'DefaultStrategy'\n",
"# Path to user data\n",
"config['user_data_dir'] = Path('user_data')\n",
"# Location of the ticker data\n",
"config['datadir'] = Path('user_data', 'data/binance')\n",
"# Location of the strategy\n", "# Location of the strategy\n",
"strategy_path = Path(user_data_dir, 'strategies')\n", "config['strategy_path'] = Path('user_data', 'strategies')\n",
"# Specify backtest results to load\n", "# Specify backtest results to load\n",
"trade_source = 'file'\n", "config['trade_source'] = 'file'\n",
"exportfilename = Path(user_data_dir, 'backtest_results/backtest-result.json')\n", "config['exportfilename'] = Path('user_data', 'backtest_results/backtest-result.json')\n",
"db_url = 'sqlite://'\n", "config['db_url'] = 'sqlite://'\n",
"\n",
"# Specify interval to analyze\n",
"ticker_interval = \"5m\"\n",
"# Specify timerange to test\n", "# Specify timerange to test\n",
"timerange = '-100'\n", "timerange = '-100'\n",
"# Pair to analyze - Only use one pair here\n", "# Pair to analyze - Only use one pair here\n",
"pair = \"ETH/BTC\"" "pair = \"ETH/BTC\"\n",
"# Indicators from strategy to plot\n",
"overlay_indicators = ['ema50', 'ema100']\n",
"bottom_indicators = ['macd']"
] ]
}, },
{ {
@ -155,17 +162,14 @@
"outputs": [], "outputs": [],
"source": [ "source": [
"import logging\n", "import logging\n",
"\n", "import json\n",
"from freqtrade.loggers import setup_logging\n", "from freqtrade.loggers import setup_logging\n",
"\n", "\n",
"# Configure logging\n", "# Configure logging\n",
"logger = logging.getLogger()\n", "logger = logging.getLogger()\n",
"setup_logging(config)\n", "setup_logging(config)\n",
"logger.setLevel(logging.INFO)\n", "logger.setLevel(logging.INFO)\n",
"logger.info(f'conf: {conf}')\n", "logger.info(f'conf: {conf}')"
"\n",
"# Show config in memory\n",
"logger.info(json.dumps(config, indent=1))"
] ]
}, },
{ {
@ -177,8 +181,10 @@
"* Load the history of the specified pair\n", "* Load the history of the specified pair\n",
"* Load the specified strategy\n", "* Load the specified strategy\n",
"* Generate buy and sell signals produced by the specified strategy\n", "* Generate buy and sell signals produced by the specified strategy\n",
"* Plot the results\n", "* Return a dictionary with the figure, figure dataframe and trades dataframe\n",
"\n", " * Key is the same as the figure filename. EG: 'freqtrade-plot-ETH_BTC-15m.html'\n",
" * Display elements with dict indexing as demonstrated below\n",
" * `fig.show()` displays the chart inline `fig.show(renderer=\"browser\")` displays the chart in a tab.\n",
"\n", "\n",
"*Note: `data.head()` may include empty values for indicators that require a startup period. This is expected behavior. For example `ma5` requires 5 candles before computing the first average*\n", "*Note: `data.head()` may include empty values for indicators that require a startup period. This is expected behavior. For example `ma5` requires 5 candles before computing the first average*\n",
"\n", "\n",
@ -199,66 +205,21 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"from pathlib import Path\n", "from freqtrade.plot.plotting import load_and_plot_trades\n",
"\n", "\n",
"import pandas as pd\n", "plot_data = load_and_plot_trades({'strategy': config['strategy'],\n",
"\n", " 'strategy_path': Path(config['strategy_path']),\n",
"from freqtrade.data.btanalysis import load_trades\n", " 'timerange': timerange,\n",
"from freqtrade.data.history import load_pair_history\n", " 'ticker_interval': ticker_interval,\n",
"from freqtrade.resolvers import StrategyResolver\n", " 'strategy_path': Path(config['strategy_path']),\n",
"from freqtrade.plot.plotting import extract_trades_of_period, generate_candlestick_graph\n", " 'datadir': Path(config['datadir']),\n",
"# Load ticker history\n", " 'user_data_dir': Path(config['user_data_dir']),\n",
"tickers = load_pair_history(pair=pair,\n", " 'exchange': config['exchange'],\n",
" ticker_interval=ticker_interval,\n", " 'trade_source': config['trade_source'],\n",
" datadir=datadir,\n", " 'exportfilename': config['exportfilename'],\n",
" timerange=TimeRange.parse_timerange(timerange))\n", " 'indicators1': overlay_indicators,\n",
"\n", " 'indicators2': bottom_indicators\n",
"# Confirm success\n", " })"
"print(\"Loaded \" + str(len(tickers)) +\n",
" f\" rows of data for {pair} from {datadir}\")\n",
"\n",
"# Load strategy\n",
"strategy = StrategyResolver({\n",
" 'strategy': strategy_name,\n",
" 'user_data_dir': user_data_dir,\n",
" 'strategy_path': strategy_path\n",
"}).strategy\n",
"\n",
"# Generate buy/sell signals using strategy\n",
"data = strategy.analyze_ticker(tickers, {'pair': pair})\n",
"logger.info(f'Indicators: {list(data)[6:-2]}')\n",
"\n",
"# Collect trades if a backtest has been completed\n",
"try:\n",
" trades = load_trades(source=trade_source,\n",
" db_url=db_url,\n",
" exportfilename=exportfilename)\n",
" trades = trades.loc[trades['pair'] == pair]\n",
" trades = extract_trades_of_period(data, trades)\n",
"except:\n",
" trades = pd.DataFrame()\n",
"\n",
"# Build and display plot\n",
"# Specify the indicators to plot as lists\n",
"# indicators1 is a list of indicators to overlay on the price chart\n",
"# indicators2 is a list of indicators to plot below the price chart\n",
"fig = generate_candlestick_graph(\n",
" pair=pair,\n",
" data=data,\n",
" trades=trades,\n",
" indicators1=['ema20', 'ema50', 'ema100'],\n",
" indicators2=['macd', 'macdsignal'])\n",
"\n",
"fig.show()\n",
"display(data.tail())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Run Backtest\n",
"Once you are happy with your strategy signals, run a backtest then plot again."
] ]
}, },
{ {
@ -267,8 +228,51 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"# Run backtest\n", "# Display charts and data inline\n",
"!freqtrade {conf} backtesting --timerange={timerange} --ticker-interval {ticker_interval} --export=trades --export-filename={exportfilename}" "for idx in list(plot_data.keys()):\n",
" print(idx)\n",
" plot_data[idx]['fig'].show()\n",
" display(plot_data[idx]['data'].tail())\n",
" display(plot_data[idx]['trades'].head())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Detailed analysis\n",
"Slice and dice your data to generate insights"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def show_indicator_extremes(df, indicator):\n",
" '''\n",
" Sorts simulation dataframe by specified indicator\n",
" '''\n",
" df = df[df[indicator].notna()].sort_values(by=indicator, ascending=False)\n",
" return df\n",
"\n",
"# Demonstrate with first item\n",
"idx = list(plot_data.keys())[0]\n",
"indicator = overlay_indicators[0]\n",
"print(idx)\n",
"data = plot_data[idx]['data']\n",
"extremes = show_indicator_extremes(data, indicator)\n",
"display(extremes.head(10))\n",
"display(extremes.tail(10))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Run Backtest\n",
"Once you are happy with your strategy signals, run a backtest then plot again."
] ]
}, },
{ {