removing all codes from maindev branch in preparation for PR from freq/dev
This commit is contained in:
@@ -1,200 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to display when the bot will buy a specific pair
|
||||
|
||||
Mandatory Cli parameters:
|
||||
-p / --pair: pair to examine
|
||||
|
||||
Optional Cli parameters
|
||||
-d / --datadir: path to pair backtest data
|
||||
--timerange: specify what timerange of data to use.
|
||||
-l / --live: Live, to download the latest ticker for the pair
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
from os import path
|
||||
import glob
|
||||
import json
|
||||
import re
|
||||
from typing import List, Dict
|
||||
import gzip
|
||||
|
||||
from freqtrade.arguments import Arguments
|
||||
from freqtrade import misc, constants
|
||||
from pandas import DataFrame
|
||||
|
||||
import dateutil.parser
|
||||
|
||||
logger = logging.getLogger('freqtrade')
|
||||
|
||||
|
||||
def load_old_file(filename) -> (List[Dict], bool):
|
||||
if not path.isfile(filename):
|
||||
logger.warning("filename %s does not exist", filename)
|
||||
return (None, False)
|
||||
logger.debug('Loading ticker data from file %s', filename)
|
||||
|
||||
pairdata = None
|
||||
|
||||
if filename.endswith('.gz'):
|
||||
logger.debug('Loading ticker data from file %s', filename)
|
||||
is_zip = True
|
||||
with gzip.open(filename) as tickerdata:
|
||||
pairdata = json.load(tickerdata)
|
||||
else:
|
||||
is_zip = False
|
||||
with open(filename) as tickerdata:
|
||||
pairdata = json.load(tickerdata)
|
||||
return (pairdata, is_zip)
|
||||
|
||||
|
||||
def parse_old_backtest_data(ticker) -> DataFrame:
|
||||
"""
|
||||
Reads old backtest data
|
||||
Format: "O": 8.794e-05,
|
||||
"H": 8.948e-05,
|
||||
"L": 8.794e-05,
|
||||
"C": 8.88e-05,
|
||||
"V": 991.09056638,
|
||||
"T": "2017-11-26T08:50:00",
|
||||
"BV": 0.0877869
|
||||
"""
|
||||
|
||||
columns = {'C': 'close', 'V': 'volume', 'O': 'open',
|
||||
'H': 'high', 'L': 'low', 'T': 'date'}
|
||||
|
||||
frame = DataFrame(ticker) \
|
||||
.rename(columns=columns)
|
||||
if 'BV' in frame:
|
||||
frame.drop('BV', 1, inplace=True)
|
||||
if 'date' not in frame:
|
||||
logger.warning("Date not in frame - probably not a Ticker file")
|
||||
return None
|
||||
frame.sort_values('date', inplace=True)
|
||||
return frame
|
||||
|
||||
|
||||
def convert_dataframe(frame: DataFrame):
|
||||
"""Convert dataframe to new format"""
|
||||
# reorder columns:
|
||||
cols = ['date', 'open', 'high', 'low', 'close', 'volume']
|
||||
frame = frame[cols]
|
||||
|
||||
# Make sure parsing/printing data is assumed to be UTC
|
||||
frame['date'] = frame['date'].apply(
|
||||
lambda d: int(dateutil.parser.parse(d+'+00:00').timestamp()) * 1000)
|
||||
frame['date'] = frame['date'].astype('int64')
|
||||
# Convert columns one by one to preserve type.
|
||||
by_column = [frame[x].values.tolist() for x in frame.columns]
|
||||
return list(list(x) for x in zip(*by_column))
|
||||
|
||||
|
||||
def convert_file(filename: str, filename_new: str) -> None:
|
||||
"""Converts a file from old format to ccxt format"""
|
||||
(pairdata, is_zip) = load_old_file(filename)
|
||||
if pairdata and type(pairdata) is list:
|
||||
if type(pairdata[0]) is list:
|
||||
logger.error("pairdata for %s already in new format", filename)
|
||||
return
|
||||
|
||||
frame = parse_old_backtest_data(pairdata)
|
||||
# Convert frame to new format
|
||||
if frame is not None:
|
||||
frame1 = convert_dataframe(frame)
|
||||
misc.file_dump_json(filename_new, frame1, is_zip)
|
||||
|
||||
|
||||
def convert_main(args: Namespace) -> None:
|
||||
"""
|
||||
converts a folder given in --datadir from old to new format to support ccxt
|
||||
"""
|
||||
|
||||
workdir = path.join(args.datadir, "")
|
||||
logger.info("Workdir: %s", workdir)
|
||||
|
||||
for filename in glob.glob(workdir + "*.json"):
|
||||
# swap currency names
|
||||
ret = re.search(r'[A-Z_]{7,}', path.basename(filename))
|
||||
if args.norename:
|
||||
filename_new = filename
|
||||
else:
|
||||
if not ret:
|
||||
logger.warning("file %s could not be converted, could not extract currencies",
|
||||
filename)
|
||||
continue
|
||||
pair = ret.group(0)
|
||||
currencies = pair.split("_")
|
||||
if len(currencies) != 2:
|
||||
logger.warning("file %s could not be converted, could not extract currencies",
|
||||
filename)
|
||||
continue
|
||||
|
||||
ret_integer = re.search(r'\d+(?=\.json)', path.basename(filename))
|
||||
ret_string = re.search(r'(\d+[mhdw])(?=\.json)', path.basename(filename))
|
||||
|
||||
if ret_integer:
|
||||
minutes = int(ret_integer.group(0))
|
||||
# default to adding 'm' to end of minutes for new interval name
|
||||
interval = str(minutes) + 'm'
|
||||
# but check if there is a mapping between int and string also
|
||||
for str_interval, minutes_interval in constants.TICKER_INTERVAL_MINUTES.items():
|
||||
if minutes_interval == minutes:
|
||||
interval = str_interval
|
||||
break
|
||||
# change order on pairs if old ticker interval found
|
||||
|
||||
filename_new = path.join(path.dirname(filename),
|
||||
f"{currencies[1]}_{currencies[0]}-{interval}.json")
|
||||
|
||||
elif ret_string:
|
||||
interval = ret_string.group(0)
|
||||
filename_new = path.join(path.dirname(filename),
|
||||
f"{currencies[0]}_{currencies[1]}-{interval}.json")
|
||||
|
||||
else:
|
||||
logger.warning("file %s could not be converted, interval not found", filename)
|
||||
continue
|
||||
|
||||
logger.debug("Converting and renaming %s to %s", filename, filename_new)
|
||||
convert_file(filename, filename_new)
|
||||
|
||||
|
||||
def convert_parse_args(args: List[str]) -> Namespace:
|
||||
"""
|
||||
Parse args passed to the script
|
||||
:param args: Cli arguments
|
||||
:return: args: Array with all arguments
|
||||
"""
|
||||
arguments = Arguments(args, 'Convert datafiles')
|
||||
arguments.parser.add_argument(
|
||||
'-d', '--datadir',
|
||||
help='path to backtest data (default: %(default)s',
|
||||
dest='datadir',
|
||||
default=path.join('freqtrade', 'tests', 'testdata'),
|
||||
type=str,
|
||||
metavar='PATH',
|
||||
)
|
||||
arguments.parser.add_argument(
|
||||
'-n', '--norename',
|
||||
help='don''t rename files from BTC_<PAIR> to <PAIR>_BTC - '
|
||||
'Note that not renaming will overwrite source files',
|
||||
dest='norename',
|
||||
default=False,
|
||||
action='store_true'
|
||||
)
|
||||
|
||||
return arguments.parse_args()
|
||||
|
||||
|
||||
def main(sysargv: List[str]) -> None:
|
||||
"""
|
||||
This function will initiate the bot and start the trading loop.
|
||||
:return: None
|
||||
"""
|
||||
logger.info('Starting Dataframe conversation')
|
||||
convert_main(convert_parse_args(sysargv))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""This script generate json data from bittrex"""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import arrow
|
||||
|
||||
from freqtrade import arguments
|
||||
from freqtrade.arguments import TimeRange
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.optimize import download_backtesting_testdata
|
||||
|
||||
|
||||
DEFAULT_DL_PATH = 'user_data/data'
|
||||
|
||||
arguments = arguments.Arguments(sys.argv[1:], 'download utility')
|
||||
arguments.testdata_dl_options()
|
||||
args = arguments.parse_args()
|
||||
|
||||
timeframes = args.timeframes
|
||||
|
||||
dl_path = Path(DEFAULT_DL_PATH).joinpath(args.exchange)
|
||||
if args.export:
|
||||
dl_path = Path(args.export)
|
||||
|
||||
if not dl_path.is_dir():
|
||||
sys.exit(f'Directory {dl_path} does not exist.')
|
||||
|
||||
pairs_file = Path(args.pairs_file) if args.pairs_file else dl_path.joinpath('pairs.json')
|
||||
if not pairs_file.exists():
|
||||
sys.exit(f'No pairs file found with path {pairs_file}.')
|
||||
|
||||
with pairs_file.open() as file:
|
||||
PAIRS = list(set(json.load(file)))
|
||||
|
||||
PAIRS.sort()
|
||||
|
||||
|
||||
timerange = TimeRange()
|
||||
if args.days:
|
||||
time_since = arrow.utcnow().shift(days=-args.days).strftime("%Y%m%d")
|
||||
timerange = arguments.parse_timerange(f'{time_since}-')
|
||||
|
||||
|
||||
print(f'About to download pairs: {PAIRS} to {dl_path}')
|
||||
|
||||
|
||||
# Init exchange
|
||||
exchange = Exchange({'key': '',
|
||||
'secret': '',
|
||||
'stake_currency': '',
|
||||
'dry_run': True,
|
||||
'exchange': {
|
||||
'name': args.exchange,
|
||||
'pair_whitelist': []
|
||||
}
|
||||
})
|
||||
pairs_not_available = []
|
||||
|
||||
for pair in PAIRS:
|
||||
if pair not in exchange._api.markets:
|
||||
pairs_not_available.append(pair)
|
||||
print(f"skipping pair {pair}")
|
||||
continue
|
||||
for tick_interval in timeframes:
|
||||
pair_print = pair.replace('/', '_')
|
||||
filename = f'{pair_print}-{tick_interval}.json'
|
||||
dl_file = dl_path.joinpath(filename)
|
||||
if args.erase and dl_file.exists():
|
||||
print(f'Deleting existing data for pair {pair}, interval {tick_interval}')
|
||||
dl_file.unlink()
|
||||
|
||||
print(f'downloading pair {pair}, interval {tick_interval}')
|
||||
download_backtesting_testdata(str(dl_path), exchange=exchange,
|
||||
pair=pair,
|
||||
tick_interval=tick_interval,
|
||||
timerange=timerange)
|
||||
|
||||
|
||||
if pairs_not_available:
|
||||
print(f"Pairs [{','.join(pairs_not_available)}] not availble.")
|
||||
@@ -1,370 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to display when the bot will buy a specific pair
|
||||
|
||||
Mandatory Cli parameters:
|
||||
-p / --pair: pair to examine
|
||||
|
||||
Option but recommended
|
||||
-s / --strategy: strategy to use
|
||||
|
||||
|
||||
Optional Cli parameters
|
||||
-d / --datadir: path to pair backtest data
|
||||
--timerange: specify what timerange of data to use.
|
||||
-l / --live: Live, to download the latest ticker for the pair
|
||||
-db / --db-url: Show trades stored in database
|
||||
|
||||
|
||||
Indicators recommended
|
||||
Row 1: sma, ema3, ema5, ema10, ema50
|
||||
Row 3: macd, rsi, fisher_rsi, mfi, slowd, slowk, fastd, fastk
|
||||
|
||||
Example of usage:
|
||||
> python3 scripts/plot_dataframe.py --pair BTC/EUR -d user_data/data/ --indicators1 sma,ema3
|
||||
--indicators2 fastk,fastd
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
from argparse import Namespace
|
||||
from typing import Dict, List, Any
|
||||
|
||||
import pandas as pd
|
||||
import plotly.graph_objs as go
|
||||
from plotly import tools
|
||||
from plotly.offline import plot
|
||||
|
||||
import freqtrade.optimize as optimize
|
||||
from freqtrade import persistence
|
||||
from freqtrade.analyze import Analyze
|
||||
from freqtrade.arguments import Arguments, TimeRange
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.optimize.backtesting import setup_configuration
|
||||
from freqtrade.persistence import Trade
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
_CONF: Dict[str, Any] = {}
|
||||
|
||||
|
||||
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", "opents", "closets", "open_rate", "close_rate", "duration"]
|
||||
|
||||
trades = pd.DataFrame([(t.pair, t.calc_profit(),
|
||||
t.open_date, t.close_date,
|
||||
t.open_rate, t.close_rate,
|
||||
t.close_date.timestamp() - t.open_date.timestamp())
|
||||
for t in Trade.query.filter(Trade.pair.is_(pair)).all()],
|
||||
columns=columns)
|
||||
|
||||
if args.exportfilename:
|
||||
file = Path(args.exportfilename)
|
||||
# must align with columns in backtest.py
|
||||
columns = ["pair", "profit", "opents", "closets", "index", "duration",
|
||||
"open_rate", "close_rate", "open_at_end"]
|
||||
with file.open() as f:
|
||||
data = json.load(f)
|
||||
trades = pd.DataFrame(data, columns=columns)
|
||||
trades = trades.loc[trades["pair"] == pair]
|
||||
if timerange:
|
||||
if timerange.starttype == 'date':
|
||||
trades = trades.loc[trades["opents"] >= timerange.startts]
|
||||
if timerange.stoptype == 'date':
|
||||
trades = trades.loc[trades["opents"] <= timerange.stopts]
|
||||
|
||||
trades['opents'] = pd.to_datetime(trades['opents'],
|
||||
unit='s',
|
||||
utc=True,
|
||||
infer_datetime_format=True)
|
||||
trades['closets'] = pd.to_datetime(trades['closets'],
|
||||
unit='s',
|
||||
utc=True,
|
||||
infer_datetime_format=True)
|
||||
return trades
|
||||
|
||||
|
||||
def plot_analyzed_dataframe(args: Namespace) -> None:
|
||||
"""
|
||||
Calls analyze() and plots the returned dataframe
|
||||
:return: None
|
||||
"""
|
||||
global _CONF
|
||||
|
||||
# Load the configuration
|
||||
_CONF.update(setup_configuration(args))
|
||||
|
||||
# Set the pair to audit
|
||||
pair = args.pair
|
||||
|
||||
if pair is None:
|
||||
logger.critical('Parameter --pair mandatory;. E.g --pair ETH/BTC')
|
||||
exit()
|
||||
|
||||
if '/' not in pair:
|
||||
logger.critical('--pair format must be XXX/YYY')
|
||||
exit()
|
||||
|
||||
# Set timerange to use
|
||||
timerange = Arguments.parse_timerange(args.timerange)
|
||||
|
||||
# Load the strategy
|
||||
try:
|
||||
analyze = Analyze(_CONF)
|
||||
exchange = Exchange(_CONF)
|
||||
except AttributeError:
|
||||
logger.critical(
|
||||
'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"',
|
||||
args.strategy
|
||||
)
|
||||
exit()
|
||||
|
||||
# Set the ticker to use
|
||||
tick_interval = analyze.get_ticker_interval()
|
||||
|
||||
# Load pair tickers
|
||||
tickers = {}
|
||||
if args.live:
|
||||
logger.info('Downloading pair.')
|
||||
tickers[pair] = exchange.get_ticker_history(pair, tick_interval)
|
||||
else:
|
||||
tickers = optimize.load_data(
|
||||
datadir=_CONF.get("datadir"),
|
||||
pairs=[pair],
|
||||
ticker_interval=tick_interval,
|
||||
refresh_pairs=_CONF.get('refresh_pairs', False),
|
||||
timerange=timerange
|
||||
)
|
||||
|
||||
# No ticker found, or impossible to download
|
||||
if tickers == {}:
|
||||
exit()
|
||||
|
||||
if args.db_url and args.exportfilename:
|
||||
logger.critical("Can only specify --db-url or --export-filename")
|
||||
# Get trades already made from the DB
|
||||
trades = load_trades(args, pair, timerange)
|
||||
|
||||
dataframes = analyze.tickerdata_to_dataframe(tickers)
|
||||
dataframe = dataframes[pair]
|
||||
dataframe = analyze.populate_buy_trend(dataframe)
|
||||
dataframe = analyze.populate_sell_trend(dataframe)
|
||||
|
||||
if len(dataframe.index) > args.plot_limit:
|
||||
logger.warning('Ticker contained more than %s candles as defined '
|
||||
'with --plot-limit, clipping.', args.plot_limit)
|
||||
dataframe = dataframe.tail(args.plot_limit)
|
||||
trades = trades.loc[trades['opents'] >= dataframe.iloc[0]['date']]
|
||||
fig = generate_graph(
|
||||
pair=pair,
|
||||
trades=trades,
|
||||
data=dataframe,
|
||||
args=args
|
||||
)
|
||||
|
||||
plot(fig, filename=str(Path('user_data').joinpath('freqtrade-plot.html')))
|
||||
|
||||
|
||||
def generate_graph(pair, trades: pd.DataFrame, data: pd.DataFrame, args) -> tools.make_subplots:
|
||||
"""
|
||||
Generate the graph from the data generated by Backtesting or from DB
|
||||
:param pair: Pair to Display on the graph
|
||||
:param trades: All trades created
|
||||
:param data: Dataframe
|
||||
:param args: sys.argv that contrains the two params indicators1, and indicators2
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Define the graph
|
||||
fig = tools.make_subplots(
|
||||
rows=3,
|
||||
cols=1,
|
||||
shared_xaxes=True,
|
||||
row_width=[1, 1, 4],
|
||||
vertical_spacing=0.0001,
|
||||
)
|
||||
fig['layout'].update(title=pair)
|
||||
fig['layout']['yaxis1'].update(title='Price')
|
||||
fig['layout']['yaxis2'].update(title='Volume')
|
||||
fig['layout']['yaxis3'].update(title='Other')
|
||||
|
||||
# Common information
|
||||
candles = go.Candlestick(
|
||||
x=data.date,
|
||||
open=data.open,
|
||||
high=data.high,
|
||||
low=data.low,
|
||||
close=data.close,
|
||||
name='Price'
|
||||
)
|
||||
|
||||
df_buy = data[data['buy'] == 1]
|
||||
buys = go.Scattergl(
|
||||
x=df_buy.date,
|
||||
y=df_buy.close,
|
||||
mode='markers',
|
||||
name='buy',
|
||||
marker=dict(
|
||||
symbol='triangle-up-dot',
|
||||
size=9,
|
||||
line=dict(width=1),
|
||||
color='green',
|
||||
)
|
||||
)
|
||||
df_sell = data[data['sell'] == 1]
|
||||
sells = go.Scattergl(
|
||||
x=df_sell.date,
|
||||
y=df_sell.close,
|
||||
mode='markers',
|
||||
name='sell',
|
||||
marker=dict(
|
||||
symbol='triangle-down-dot',
|
||||
size=9,
|
||||
line=dict(width=1),
|
||||
color='red',
|
||||
)
|
||||
)
|
||||
|
||||
trade_buys = go.Scattergl(
|
||||
x=trades["opents"],
|
||||
y=trades["open_rate"],
|
||||
mode='markers',
|
||||
name='trade_buy',
|
||||
marker=dict(
|
||||
symbol='square-open',
|
||||
size=11,
|
||||
line=dict(width=2),
|
||||
color='green'
|
||||
)
|
||||
)
|
||||
trade_sells = go.Scattergl(
|
||||
x=trades["closets"],
|
||||
y=trades["close_rate"],
|
||||
mode='markers',
|
||||
name='trade_sell',
|
||||
marker=dict(
|
||||
symbol='square-open',
|
||||
size=11,
|
||||
line=dict(width=2),
|
||||
color='red'
|
||||
)
|
||||
)
|
||||
|
||||
# Row 1
|
||||
fig.append_trace(candles, 1, 1)
|
||||
|
||||
if 'bb_lowerband' in data and 'bb_upperband' in data:
|
||||
bb_lower = go.Scatter(
|
||||
x=data.date,
|
||||
y=data.bb_lowerband,
|
||||
name='BB lower',
|
||||
line={'color': "transparent"},
|
||||
)
|
||||
bb_upper = go.Scatter(
|
||||
x=data.date,
|
||||
y=data.bb_upperband,
|
||||
name='BB upper',
|
||||
fill="tonexty",
|
||||
fillcolor="rgba(0,176,246,0.2)",
|
||||
line={'color': "transparent"},
|
||||
)
|
||||
fig.append_trace(bb_lower, 1, 1)
|
||||
fig.append_trace(bb_upper, 1, 1)
|
||||
|
||||
fig = generate_row(fig=fig, row=1, raw_indicators=args.indicators1, data=data)
|
||||
fig.append_trace(buys, 1, 1)
|
||||
fig.append_trace(sells, 1, 1)
|
||||
fig.append_trace(trade_buys, 1, 1)
|
||||
fig.append_trace(trade_sells, 1, 1)
|
||||
|
||||
# Row 2
|
||||
volume = go.Bar(
|
||||
x=data['date'],
|
||||
y=data['volume'],
|
||||
name='Volume'
|
||||
)
|
||||
fig.append_trace(volume, 2, 1)
|
||||
|
||||
# Row 3
|
||||
fig = generate_row(fig=fig, row=3, raw_indicators=args.indicators2, data=data)
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
def generate_row(fig, row, raw_indicators, data) -> tools.make_subplots:
|
||||
"""
|
||||
Generator all the indicator selected by the user for a specific row
|
||||
"""
|
||||
for indicator in raw_indicators.split(','):
|
||||
if indicator in data:
|
||||
scattergl = go.Scattergl(
|
||||
x=data['date'],
|
||||
y=data[indicator],
|
||||
name=indicator
|
||||
)
|
||||
fig.append_trace(scattergl, row, 1)
|
||||
else:
|
||||
logger.info(
|
||||
'Indicator "%s" ignored. Reason: This indicator is not found '
|
||||
'in your strategy.',
|
||||
indicator
|
||||
)
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
def plot_parse_args(args: List[str]) -> Namespace:
|
||||
"""
|
||||
Parse args passed to the script
|
||||
:param args: Cli arguments
|
||||
:return: args: Array with all arguments
|
||||
"""
|
||||
arguments = Arguments(args, 'Graph dataframe')
|
||||
arguments.scripts_options()
|
||||
arguments.parser.add_argument(
|
||||
'--indicators1',
|
||||
help='Set indicators from your strategy you want in the first row of the graph. Separate '
|
||||
'them with a coma. E.g: ema3,ema5 (default: %(default)s)',
|
||||
type=str,
|
||||
default='sma,ema3,ema5',
|
||||
dest='indicators1',
|
||||
)
|
||||
|
||||
arguments.parser.add_argument(
|
||||
'--indicators2',
|
||||
help='Set indicators from your strategy you want in the third row of the graph. Separate '
|
||||
'them with a coma. E.g: fastd,fastk (default: %(default)s)',
|
||||
type=str,
|
||||
default='macd',
|
||||
dest='indicators2',
|
||||
)
|
||||
arguments.parser.add_argument(
|
||||
'--plot-limit',
|
||||
help='Specify tick limit for plotting - too high values cause huge files - '
|
||||
'Default: %(default)s',
|
||||
dest='plot_limit',
|
||||
default=750,
|
||||
type=int,
|
||||
)
|
||||
arguments.common_args_parser()
|
||||
arguments.optimizer_shared_options(arguments.parser)
|
||||
arguments.backtesting_options(arguments.parser)
|
||||
return arguments.parse_args()
|
||||
|
||||
|
||||
def main(sysargv: List[str]) -> None:
|
||||
"""
|
||||
This function will initiate the bot and start the trading loop.
|
||||
:return: None
|
||||
"""
|
||||
logger.info('Starting Plot Dataframe')
|
||||
plot_analyzed_dataframe(
|
||||
plot_parse_args(sysargv)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
@@ -1,228 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to display profits
|
||||
|
||||
Mandatory Cli parameters:
|
||||
-p / --pair: pair to examine
|
||||
|
||||
Optional Cli parameters
|
||||
-c / --config: specify configuration file
|
||||
-s / --strategy: strategy to use
|
||||
-d / --datadir: path to pair backtest data
|
||||
--timerange: specify what timerange of data to use
|
||||
--export-filename: Specify where the backtest export is located.
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from argparse import Namespace
|
||||
from typing import List, Optional
|
||||
import numpy as np
|
||||
|
||||
from plotly import tools
|
||||
from plotly.offline import plot
|
||||
import plotly.graph_objs as go
|
||||
|
||||
from freqtrade.arguments import Arguments
|
||||
from freqtrade.configuration import Configuration
|
||||
from freqtrade.analyze import Analyze
|
||||
from freqtrade import constants
|
||||
|
||||
import freqtrade.optimize as optimize
|
||||
import freqtrade.misc as misc
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# data:: [ pair, profit-%, enter, exit, time, duration]
|
||||
# data:: ["ETH/BTC", 0.0023975, "1515598200", "1515602100", "2018-01-10 07:30:00+00:00", 65]
|
||||
def make_profit_array(data: List, px: int, min_date: int,
|
||||
interval: int,
|
||||
filter_pairs: Optional[List] = None) -> np.ndarray:
|
||||
pg = np.zeros(px)
|
||||
filter_pairs = filter_pairs or []
|
||||
# Go through the trades
|
||||
# and make an total profit
|
||||
# array
|
||||
for trade in data:
|
||||
pair = trade[0]
|
||||
if filter_pairs and pair not in filter_pairs:
|
||||
continue
|
||||
profit = trade[1]
|
||||
trade_sell_time = int(trade[3])
|
||||
|
||||
ix = define_index(min_date, trade_sell_time, interval)
|
||||
if ix < px:
|
||||
logger.debug('[%s]: Add profit %s on %s', pair, profit, trade[4])
|
||||
pg[ix] += profit
|
||||
|
||||
# rewrite the pg array to go from
|
||||
# total profits at each timeframe
|
||||
# to accumulated profits
|
||||
pa = 0
|
||||
for x in range(0, len(pg)):
|
||||
p = pg[x] # Get current total percent
|
||||
pa += p # Add to the accumulated percent
|
||||
pg[x] = pa # write back to save memory
|
||||
|
||||
return pg
|
||||
|
||||
|
||||
def plot_profit(args: Namespace) -> None:
|
||||
"""
|
||||
Plots the total profit for all pairs.
|
||||
Note, the profit calculation isn't realistic.
|
||||
But should be somewhat proportional, and therefor useful
|
||||
in helping out to find a good algorithm.
|
||||
"""
|
||||
|
||||
# We need to use the same pairs, same tick_interval
|
||||
# and same timeperiod as used in backtesting
|
||||
# to match the tickerdata against the profits-results
|
||||
timerange = Arguments.parse_timerange(args.timerange)
|
||||
|
||||
config = Configuration(args).get_config()
|
||||
|
||||
# Init strategy
|
||||
try:
|
||||
analyze = Analyze({'strategy': config.get('strategy')})
|
||||
except AttributeError:
|
||||
logger.critical(
|
||||
'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"',
|
||||
config.get('strategy')
|
||||
)
|
||||
exit(1)
|
||||
|
||||
# Load the profits results
|
||||
try:
|
||||
filename = args.exportfilename
|
||||
with open(filename) as file:
|
||||
data = json.load(file)
|
||||
except FileNotFoundError:
|
||||
logger.critical(
|
||||
'File "backtest-result.json" not found. This script require backtesting '
|
||||
'results to run.\nPlease run a backtesting with the parameter --export.')
|
||||
exit(1)
|
||||
|
||||
# Take pairs from the cli otherwise switch to the pair in the config file
|
||||
if args.pair:
|
||||
filter_pairs = args.pair
|
||||
filter_pairs = filter_pairs.split(',')
|
||||
else:
|
||||
filter_pairs = config['exchange']['pair_whitelist']
|
||||
|
||||
tick_interval = analyze.strategy.ticker_interval
|
||||
pairs = config['exchange']['pair_whitelist']
|
||||
|
||||
if filter_pairs:
|
||||
pairs = list(set(pairs) & set(filter_pairs))
|
||||
logger.info('Filter, keep pairs %s' % pairs)
|
||||
|
||||
tickers = optimize.load_data(
|
||||
datadir=config.get('datadir'),
|
||||
pairs=pairs,
|
||||
ticker_interval=tick_interval,
|
||||
refresh_pairs=False,
|
||||
timerange=timerange
|
||||
)
|
||||
dataframes = analyze.tickerdata_to_dataframe(tickers)
|
||||
|
||||
# NOTE: the dataframes are of unequal length,
|
||||
# 'dates' is an merged date array of them all.
|
||||
|
||||
dates = misc.common_datearray(dataframes)
|
||||
min_date = int(min(dates).timestamp())
|
||||
max_date = int(max(dates).timestamp())
|
||||
num_iterations = define_index(min_date, max_date, tick_interval) + 1
|
||||
|
||||
# Make an average close price of all the pairs that was involved.
|
||||
# this could be useful to gauge the overall market trend
|
||||
# We are essentially saying:
|
||||
# array <- sum dataframes[*]['close'] / num_items dataframes
|
||||
# FIX: there should be some onliner numpy/panda for this
|
||||
avgclose = np.zeros(num_iterations)
|
||||
num = 0
|
||||
for pair, pair_data in dataframes.items():
|
||||
close = pair_data['close']
|
||||
maxprice = max(close) # Normalize price to [0,1]
|
||||
logger.info('Pair %s has length %s' % (pair, len(close)))
|
||||
for x in range(0, len(close)):
|
||||
avgclose[x] += close[x] / maxprice
|
||||
# avgclose += close
|
||||
num += 1
|
||||
avgclose /= num
|
||||
|
||||
# make an profits-growth array
|
||||
pg = make_profit_array(data, num_iterations, min_date, tick_interval, filter_pairs)
|
||||
|
||||
#
|
||||
# Plot the pairs average close prices, and total profit growth
|
||||
#
|
||||
|
||||
avgclose = go.Scattergl(
|
||||
x=dates,
|
||||
y=avgclose,
|
||||
name='Avg close price',
|
||||
)
|
||||
|
||||
profit = go.Scattergl(
|
||||
x=dates,
|
||||
y=pg,
|
||||
name='Profit',
|
||||
)
|
||||
|
||||
fig = tools.make_subplots(rows=3, cols=1, shared_xaxes=True, row_width=[1, 1, 1])
|
||||
|
||||
fig.append_trace(avgclose, 1, 1)
|
||||
fig.append_trace(profit, 2, 1)
|
||||
|
||||
for pair in pairs:
|
||||
pg = make_profit_array(data, num_iterations, min_date, tick_interval, pair)
|
||||
pair_profit = go.Scattergl(
|
||||
x=dates,
|
||||
y=pg,
|
||||
name=pair,
|
||||
)
|
||||
fig.append_trace(pair_profit, 3, 1)
|
||||
|
||||
plot(fig, filename=os.path.join('user_data', 'freqtrade-profit-plot.html'))
|
||||
|
||||
|
||||
def define_index(min_date: int, max_date: int, interval: str) -> int:
|
||||
"""
|
||||
Return the index of a specific date
|
||||
"""
|
||||
interval_minutes = constants.TICKER_INTERVAL_MINUTES[interval]
|
||||
return int((max_date - min_date) / (interval_minutes * 60))
|
||||
|
||||
|
||||
def plot_parse_args(args: List[str]) -> Namespace:
|
||||
"""
|
||||
Parse args passed to the script
|
||||
:param args: Cli arguments
|
||||
:return: args: Array with all arguments
|
||||
"""
|
||||
arguments = Arguments(args, 'Graph profits')
|
||||
arguments.scripts_options()
|
||||
arguments.common_args_parser()
|
||||
arguments.optimizer_shared_options(arguments.parser)
|
||||
arguments.backtesting_options(arguments.parser)
|
||||
|
||||
return arguments.parse_args()
|
||||
|
||||
|
||||
def main(sysargv: List[str]) -> None:
|
||||
"""
|
||||
This function will initiate the bot and start the trading loop.
|
||||
:return: None
|
||||
"""
|
||||
logger.info('Starting Plot Dataframe')
|
||||
plot_profit(
|
||||
plot_parse_args(sysargv)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
Reference in New Issue
Block a user