mirror of
https://github.com/Rikj000/MoniGoMani.git
synced 2022-03-06 00:08:05 +03:00
1168 lines
56 KiB
Python
Executable File
1168 lines
56 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
# -* vim: syntax=python -*-
|
|
# pylint: disable=W0108
|
|
|
|
# _
|
|
# | |
|
|
# _ __ ___ __ _ _ __ ___ ______ | |__ _ _ _ __ _ __ _ _
|
|
# | '_ ` _ \ / _` || '_ ` _ \ |______|| '_ \ | | | || '__|| '__|| | | |
|
|
# | | | | | || (_| || | | | | | | | | || |_| || | | | | |_| |
|
|
# |_| |_| |_| \__, ||_| |_| |_| |_| |_| \__,_||_| |_| \__, |
|
|
# __/ | __/ |
|
|
# |___/ |___/
|
|
#
|
|
# --- ↑↓ Do not remove these libs ↑↓ -----------------------------------------------------------------------------------
|
|
|
|
"""mgm-hurry is a CLI Tool for setting up and managing Freqtrade in combination with MoniGoMani"""
|
|
|
|
import glob
|
|
import json
|
|
import logging
|
|
import os
|
|
import sys
|
|
from datetime import datetime
|
|
from string import Template
|
|
|
|
import fire
|
|
import numpy as np # noqa
|
|
import pandas as pd # noqa
|
|
import quantstats as qs
|
|
from art import tprint
|
|
from InquirerPy import prompt
|
|
from InquirerPy.validator import NumberValidator
|
|
|
|
from user_data.mgm_tools.mgm_hurry.CliColor import Color
|
|
from user_data.mgm_tools.mgm_hurry.FreqtradeCli import FreqtradeCli
|
|
from user_data.mgm_tools.mgm_hurry.MoniGoManiCli import MoniGoManiCli
|
|
from user_data.mgm_tools.mgm_hurry.MoniGoManiConfig import MoniGoManiConfig
|
|
from user_data.mgm_tools.mgm_hurry.MoniGoManiLogger import MoniGoManiLogger
|
|
|
|
# ---- ↑ Do not remove these libs ↑ ------------------------------------------------------------------------------------
|
|
|
|
|
|
class MGMHurry:
|
|
"""
|
|
💨 Your command is my objective 💨
|
|
|
|
CLI Tool for setting up and managing a Freqtrade instance containing the MoniGoMani Framework & Strategy
|
|
|
|
Attributes:
|
|
monigomani_config The monigomani_config object
|
|
freqtrade_cli The FreqtradeCli object
|
|
logger The logging function out of MoniGoManiLogger
|
|
"""
|
|
monigomani_config: MoniGoManiConfig
|
|
freqtrade_cli: FreqtradeCli
|
|
logger: logging
|
|
|
|
def __init__(self):
|
|
tprint('mgm-hurry')
|
|
|
|
self.basedir = os.getcwd()
|
|
self.logger = MoniGoManiLogger(self.basedir).get_logger()
|
|
self.monigomani_config = MoniGoManiConfig(self.basedir)
|
|
|
|
if self.monigomani_config.reload() is False:
|
|
self.logger.error(Color.red('⚠️ Failed to load or create ".hurry" config file.'))
|
|
|
|
self.freqtrade_cli = FreqtradeCli(self.basedir)
|
|
self.monigomani_cli = MoniGoManiCli(self.basedir)
|
|
|
|
def version(self):
|
|
if self.freqtrade_cli.install_type == 'source':
|
|
if self.freqtrade_cli.installation_exists(silent=True):
|
|
self.logger.info(Color.green(Color.bold(Color.underline('MoniGoMani Version:'))))
|
|
self.monigomani_cli.run_command(f'cd {self.basedir}/monigomani; git log -1; cd ..; echo "";')
|
|
else:
|
|
self.logger.warning(Color.yellow('"No MoniGoMani installation detected!'))
|
|
|
|
if self.freqtrade_cli.installation_exists(silent=True):
|
|
self.logger.info(Color.green(Color.bold(Color.underline('Freqtrade Version:'))))
|
|
self.monigomani_cli.run_command('git log -1; echo "";')
|
|
else:
|
|
self.logger.warning(Color.yellow('"No Freqtrade installation detected!'))
|
|
|
|
else:
|
|
self.logger.warning(Color.yellow('"version" command currently only supported on "source" installations.'))
|
|
|
|
def up(self): # pylint: disable=invalid-name
|
|
"""
|
|
The all in one command. Hurry up, turn it up!
|
|
"""
|
|
self.logger.info(Color.title('👉 The all in one command. Hurry up, turn it up!'))
|
|
|
|
answers = prompt(questions=[{
|
|
'type': 'confirm',
|
|
'name': 'install_freqtrade',
|
|
'message': f'💨 Do you want to '
|
|
f'{"update" if self.freqtrade_cli.installation_exists(silent=True) else "install"} Freqtrade?'
|
|
}, {
|
|
'type': 'confirm',
|
|
'name': 'install_mgm',
|
|
'message': f'💨 Do you want to '
|
|
f'{"update" if self.monigomani_cli.installation_exists(silent=True) else "install"} MoniGoMani?'
|
|
}, {
|
|
'type': 'confirm',
|
|
'name': 'run_setup',
|
|
'message': '💨 Do you want to configure it now?'
|
|
}, {
|
|
'type': 'confirm',
|
|
'name': 'download_static_pairlist',
|
|
'message': '💨 Do you want to generate a static pairlist now?'
|
|
}, {
|
|
'type': 'confirm',
|
|
'name': 'download_data',
|
|
'message': '💨 Do you want to download candle data now?'
|
|
}, {
|
|
'type': 'confirm',
|
|
'name': 'do_hyperopt',
|
|
'message': '💨 Do you want to hyperopt now?'
|
|
}, {
|
|
'type': 'confirm',
|
|
'name': 'do_backtest',
|
|
'message': '💨 Do you want to backtest now?'
|
|
}, {
|
|
'type': 'list',
|
|
'name': 'start_trading',
|
|
'message': '💨 Do you want to start trading?',
|
|
'choices': [
|
|
{'name': 'No', 'value': 0},
|
|
{'name': 'Yes, Dry-Run please', 'value': 1},
|
|
{'name': 'Yes, for real!', 'value': 2}
|
|
]
|
|
}])
|
|
|
|
if answers == {}:
|
|
sys.exit()
|
|
|
|
if answers.get('install_freqtrade') is True:
|
|
self.install_freqtrade()
|
|
|
|
if answers.get('install_mgm') is True:
|
|
self.install_mgm()
|
|
|
|
if answers.get('run_setup') is True:
|
|
self.setup()
|
|
|
|
if answers.get('download_static_pairlist') is True:
|
|
self.download_static_pairlist()
|
|
|
|
if answers.get('download_data') is True:
|
|
self.download_candle_data()
|
|
|
|
strategy = self.monigomani_config.config['hyperopt']['strategy']
|
|
if answers.get('do_hyperopt') is True:
|
|
ho_results_found = False
|
|
if strategy == 'MoniGoManiHyperStrategy':
|
|
mgm_config_ho = self.monigomani_config.config['mgm_config_names']['mgm-config-hyperopt']
|
|
if os.path.isfile(f'{self.basedir}/user_data/{mgm_config_ho}'):
|
|
ho_results_found = True
|
|
elif os.path.isfile(f'{self.basedir}/user_data/strategies/{strategy}.json'):
|
|
ho_results_found = True
|
|
|
|
if ho_results_found is True:
|
|
clean_start = prompt(questions=[{
|
|
'type': 'list',
|
|
'name': 'clean_start',
|
|
'message': 'Previous HyperOpt Results where found. Do you wish to remove these for a clean start?',
|
|
'choices': [
|
|
{'name': 'Yes', 'value': 1},
|
|
{'name': 'No', 'value': 0}
|
|
]}
|
|
])
|
|
|
|
if clean_start.get('clean_start') == 1:
|
|
self.cleanup(strategy)
|
|
|
|
if strategy == 'MoniGoManiHyperStrategy':
|
|
importance_report_answer = prompt(questions=[{
|
|
'type': 'confirm',
|
|
'name': 'importance_report',
|
|
'message': '💨 Do you want to generate signal importance reports?'
|
|
}])
|
|
report_answer = importance_report_answer.get('importance_report')
|
|
else:
|
|
report_answer = False
|
|
|
|
# First HO run
|
|
self.hyperopt(do_backtest=answers.get('do_backtest'), importance_report=report_answer)
|
|
ho_choice = prompt(questions=[{
|
|
'type': 'input',
|
|
'name': 'ho_epoch',
|
|
'message': '(HO 1) Choose the epoch which fits the best to your liking: (0-1000) ',
|
|
'filter': lambda val: int(val),
|
|
'validate': NumberValidator()
|
|
}])
|
|
self.hyperopt_show_epoch(ho_choice.get('ho_epoch'))
|
|
|
|
if strategy == 'MoniGoManiHyperStrategy':
|
|
# Second HO run
|
|
self.hyperopt(do_backtest=answers.get('do_backtest'), importance_report=report_answer)
|
|
ho_choice = prompt(questions=[{
|
|
'type': 'input',
|
|
'name': 'ho_epoch',
|
|
'message': '(HO 2) Choose the epoch which fits the best to your liking: (0-1000) ',
|
|
'filter': lambda val: int(val),
|
|
'validate': NumberValidator()
|
|
}])
|
|
self.hyperopt_show_epoch(ho_choice.get('ho_epoch'))
|
|
|
|
if answers.get('do_backtest') is True:
|
|
self.backtest()
|
|
|
|
if answers.get('start_trading') > 0:
|
|
if answers.get('start_trading') == 2:
|
|
self.start_trader(False)
|
|
else:
|
|
self.start_trader(True)
|
|
else:
|
|
self.logger.info(Color.green('💨 Executing mgm-hurry up finished.'))
|
|
|
|
def install_freqtrade(self, target_dir: str = None, branch: str = 'develop',
|
|
commit: str = '3503fdb4ec31be99f433fdce039543e0911964d6', install_ui: bool = True):
|
|
"""
|
|
Install or update Freqtrade the easy way.
|
|
|
|
:param target_dir: (string, optional) Specify a target_dir to install Freqtrade. Defaults to os.getcwd().
|
|
:param branch: (string, optional) Checkout a specific branch. Defaults to 'develop'.
|
|
:param commit: (str) Checkout a specific commit. Defaults to latest supported by MoniGoMani,
|
|
but 'latest' can also be used.
|
|
:param install_ui: (bool) Install FreqUI. Defaults to True.
|
|
"""
|
|
self.logger.info(Color.title('👉 Install Freqtrade'))
|
|
|
|
# Checking Freqtrade installation to confirm for overwrite
|
|
if self.freqtrade_cli.installation_exists() is True:
|
|
result = prompt(questions=[{
|
|
'type': 'confirm',
|
|
'message': 'It looks like Freqtrade is already installed. Proceed to overwrite?',
|
|
'name': 'proceed',
|
|
'default': False
|
|
}])
|
|
|
|
if result.get('proceed') is False:
|
|
self.logger.info('Skipping overwriting Freqtrade installation.')
|
|
return False
|
|
|
|
if target_dir is None:
|
|
target_dir = self.basedir
|
|
|
|
self.freqtrade_cli.download_setup_freqtrade(target_dir=target_dir, branch=branch,
|
|
commit=commit, install_ui=install_ui)
|
|
|
|
self.logger.info(Color.green('🍺 Freqtrade has been installed. You can now proceed to install MoniGoMani. '
|
|
'(Hint: mgm-hurry install_mgm)'))
|
|
|
|
return True
|
|
|
|
def install_mgm(self, target_dir: str = None, branch: str = 'development', commit: str = None):
|
|
"""
|
|
Install or update the MoniGoMani Framework & Strategy.
|
|
|
|
:param branch: (string, optional) Checkout a specific branch. Defaults to 'development'.
|
|
:param target_dir: (string, optional) Specify a target_dir to install MoniGoMani. Defaults to os.getcwd().
|
|
:param commit: (str) Checkout a specific commit. Defaults to None aka 'latest'.
|
|
"""
|
|
self.logger.info(Color.title('👉 Install MoniGoMani'))
|
|
|
|
# Checking MoniGoMani installation to confirm for overwrite
|
|
if self.monigomani_cli.installation_exists() is True:
|
|
result = prompt(questions=[{
|
|
'type': 'confirm',
|
|
'message': 'It looks like MoniGoMani is already installed. Proceed to overwrite?',
|
|
'name': 'proceed',
|
|
'default': False
|
|
}])
|
|
|
|
if result.get('proceed') is False:
|
|
self.logger.info('Skipping overwriting MoniGoMani installation.')
|
|
return False
|
|
|
|
# Check if the private config file already exists.
|
|
cfg_filename = self.monigomani_config.get_config_filepath('mgm-config-private')
|
|
mgm_private_json_name = self.monigomani_config.config['mgm_config_names']['mgm-config-private']
|
|
|
|
result = prompt(questions=[{
|
|
'type': 'confirm',
|
|
'message': f'Should I print your "{mgm_private_json_name}" contents '
|
|
f'so you can copy it over to new installation?',
|
|
'name': 'print',
|
|
'default': False,
|
|
'when': lambda result: os.path.isfile(cfg_filename)
|
|
}, {
|
|
'type': 'confirm',
|
|
'message': Color.yellow('WARNING: You are printing sensitive information '
|
|
'which you should NEVER SHARE with anybody. Really proceed?'),
|
|
'name': 'print_confirmed',
|
|
'default': False,
|
|
'when': lambda result: result['print']
|
|
}])
|
|
|
|
if result.get('print_confirmed') is True:
|
|
# Read 'mgm-config-private' and pretty print it
|
|
with open(f'{self.basedir}/user_data/{mgm_private_json_name}', ) as mgm_private_config:
|
|
print(json.dumps(json.load(mgm_private_config), indent=4))
|
|
|
|
if target_dir is None:
|
|
target_dir = self.basedir
|
|
|
|
self.monigomani_cli.download_setup_mgm(target_dir, branch, commit)
|
|
self.monigomani_config.create_config_files(self.basedir)
|
|
self.logger.info(Color.green('🍺 MoniGoMani has been installed. You can now proceed to setup your instance. '
|
|
'(Hint: mgm-hurry setup)'))
|
|
|
|
def setup(self):
|
|
"""
|
|
Saves setup data based on your answers
|
|
"""
|
|
self.logger.info(Color.title('👉 Setup'))
|
|
|
|
self.logger.info('🤓 Let\'s answer some questions to make your life easier.')
|
|
|
|
strategies = map(lambda f: os.path.basename(f).replace('.py', ''),
|
|
glob.glob(f'{self.basedir}/user_data/strategies/*.py'))
|
|
|
|
strategies_choices = []
|
|
for strat in strategies:
|
|
if strat != 'MasterMoniGoManiHyperStrategy':
|
|
strategies_choices.append({'name': strat, 'value': strat})
|
|
|
|
if len(strategies_choices) == 0:
|
|
strat_dir = f'{self.basedir}/user_data/strategies/'
|
|
self.logger.error(Color.red(f'🤷 I could not find any strategy in {strat_dir}. '
|
|
f'Did you install Freqtrade & MoniGoMani?'))
|
|
sys.exit(1)
|
|
|
|
self.monigomani_config.create_config_files(self.basedir)
|
|
|
|
hyperopts = map(lambda f: os.path.basename(f).replace('.py', ''),
|
|
glob.glob(f'{self.basedir}/user_data/hyperopts/*.py'))
|
|
|
|
hyperopts_choices = []
|
|
for hyperopt in hyperopts:
|
|
hyperopts_choices.append({'name': hyperopt, 'value': hyperopt})
|
|
|
|
if len(hyperopts_choices) == 0:
|
|
ho_dir = f'{self.basedir}/user_data/hyperopts/'
|
|
self.logger.error(Color.red(f'🤷 I could not find any hyperopts in {ho_dir}. '
|
|
f'Did you install Freqtrade & MoniGoMani?'))
|
|
sys.exit(1)
|
|
|
|
self.monigomani_config.reload()
|
|
|
|
"""
|
|
'type': 'list',
|
|
'name': 'install_type',
|
|
'message': 'Which way you want to use Freqtrade?',
|
|
'choices': ['docker', 'source'],
|
|
'default': self.monigomani_config.get('install_type') or 'source'
|
|
}, {
|
|
"""
|
|
answers = prompt(questions=[{
|
|
'type': 'input',
|
|
'name': 'timerange',
|
|
'message': 'Please enter the default timerange you want to use:',
|
|
'default': self.monigomani_config.get('timerange') or '20210501-20210616'
|
|
}, {
|
|
'type': 'list',
|
|
'name': 'ho_strategy',
|
|
'message': 'Which HyperOpt Strategy do you want to use?',
|
|
'choices': strategies_choices,
|
|
'default': self.monigomani_config.get('hyperopt')['strategy'] or 'MoniGoManiHyperStrategy'
|
|
}, {
|
|
'type': 'list',
|
|
'name': 'ho_loss',
|
|
'message': 'Which HyperOpt Loss do you want to use?',
|
|
'choices': hyperopts_choices,
|
|
'default': self.monigomani_config.get('hyperopt')['loss'] or 'MGM_WinRatioAndProfitRatioHyperOptLoss'
|
|
}, {
|
|
'type': 'checkbox',
|
|
'name': 'ho_spaces',
|
|
'message': 'Which spaces do you want to HyperOpt?',
|
|
'choices': map(
|
|
lambda x: {'enabled': True, 'name': x['name'], 'value': x['value']}
|
|
if x['value'] in self.monigomani_config.get('hyperopt')['spaces'].split(' ')
|
|
else {'enabled': False, 'name': x['name'], 'value': x['value']},
|
|
[
|
|
{'enabled': False, 'name': 'default', 'value': 'default'},
|
|
{'enabled': False, 'name': 'buy', 'value': 'buy'},
|
|
{'enabled': False, 'name': 'sell', 'value': 'sell'},
|
|
{'enabled': False, 'name': 'roi', 'value': 'roi'},
|
|
{'enabled': False, 'name': 'stoploss', 'value': 'stoploss'},
|
|
{'enabled': False, 'name': 'trailing', 'value': 'trailing'},
|
|
{'enabled': False, 'name': 'all', 'value': 'all'}
|
|
]
|
|
),
|
|
'filter': lambda val: ' '.join(val)
|
|
}, {
|
|
'type': 'input',
|
|
'name': 'stake_currency',
|
|
'message': 'Please enter the default stake currency you want to use:',
|
|
'default': self.monigomani_config.get('hyperopt')['stake_currency'] or 'USDT'
|
|
}, {
|
|
'type': 'input',
|
|
'name': 'ho_epochs',
|
|
'message': 'Please enter the amount of epochs you want to HyperOpt:',
|
|
'default': str(self.monigomani_config.get('hyperopt')['epochs']) or '1000',
|
|
'filter': lambda val: int(val)
|
|
}, {
|
|
'type': 'confirm',
|
|
'message': 'Do you want to also setup your exchange?',
|
|
'name': 'proceed_exchange',
|
|
'default': True,
|
|
}, {
|
|
'type': 'list',
|
|
'name': 'exchange',
|
|
'message': 'Which exchange do you want to use?',
|
|
'choices': ['binance', 'bittrex', 'bitvavo', 'ftx', 'gateio', 'kraken', 'kucoin', 'other'],
|
|
'when': lambda result: result['proceed_exchange']
|
|
}, {
|
|
'type': 'password',
|
|
'name': 'api_key',
|
|
'message': 'Please enter the exchange API key: ',
|
|
'when': lambda result: result['proceed_exchange']
|
|
}, {
|
|
'type': 'password',
|
|
'name': 'api_secret',
|
|
'message': 'Please enter the exchange API secret: ',
|
|
'when': lambda result: result['proceed_exchange']
|
|
}, {
|
|
'type': 'input',
|
|
'name': 'username',
|
|
'message': 'Please enter the username you want to use to share test results with the MoniGoMani community:',
|
|
'default': self.monigomani_config.get('username') or 'MoniGoMani Community'
|
|
}])
|
|
|
|
if answers == {}:
|
|
sys.exit(1)
|
|
|
|
exchange = answers.get('exchange')
|
|
if exchange == 'other':
|
|
answer = prompt(questions=[{
|
|
'type': 'input',
|
|
'name': 'exchange',
|
|
'message': 'Please enter CCXT ID of the other exchange that you\'d like to try:\n'
|
|
'(WARNING: Support may vary on these!)',
|
|
'default': self.monigomani_config.get('exchange') or ''
|
|
}])
|
|
exchange = answer.get('exchange')
|
|
|
|
"""
|
|
if answers.get('install_type') == 'source':
|
|
ft_binary = f'source {self.basedir}/.env/bin/activate; freqtrade'
|
|
else:
|
|
ft_binary = 'docker-compose run --rm freqtrade'
|
|
"""
|
|
new_config = {
|
|
'config': {
|
|
'username': answers.get('username'),
|
|
'install_type': 'source', # answers.get('install_type'),
|
|
'ft_binary': f'source {self.basedir}/.env/bin/activate; freqtrade', # ft_binary,
|
|
'timerange': answers.get('timerange'),
|
|
'exchange': exchange or 'none',
|
|
'hyperopt': {
|
|
'strategy': answers.get('ho_strategy'),
|
|
'loss': answers.get('ho_loss'),
|
|
'spaces': answers.get('ho_spaces') or 'default',
|
|
'stake_currency': answers.get('stake_currency'),
|
|
'epochs': answers.get('ho_epochs')
|
|
},
|
|
'mgm_config_names': {
|
|
'mgm-config': 'mgm-config.json',
|
|
'mgm-config-private': 'mgm-config-private.json',
|
|
'mgm-config-hyperopt': 'mgm-config-hyperopt.json'
|
|
}
|
|
}
|
|
}
|
|
|
|
self.monigomani_config.write_hurry_dotfile(new_config)
|
|
# Overwrite the new stake currency in '.hurry' & mgm-config[stake_currency]
|
|
self.monigomani_config.save_stake_currency(answers.get('stake_currency'))
|
|
|
|
if answers.get('proceed_exchange') is True:
|
|
self.monigomani_config.save_exchange_credentials({
|
|
'exchange': exchange,
|
|
'api_key': answers.get('api_key'),
|
|
'api_secret': answers.get('api_secret')
|
|
})
|
|
|
|
self.__setup_telegram()
|
|
|
|
def cleanup(self, strategy: str = 'MoniGoManiHyperStrategy'):
|
|
"""
|
|
Deletes stored HyperOpt config(s) from previous run(s)
|
|
|
|
:param strategy: (Optional, string) Name of the Strategy to cleanup, defaults to 'MoniGoManiHyperStrategy'
|
|
"""
|
|
self.logger.info(Color.title('👉 Cleanup'))
|
|
|
|
# Remove hyperopt files to cleanup
|
|
cleaned_up = self.monigomani_config.cleanup_hyperopt_files(strategy)
|
|
|
|
message = f'🍺 Cleanup successful, {strategy} ready for a fresh HyperOpt Run...' if cleaned_up is True \
|
|
else f'🍺 No Cleanup was needed, {strategy} ready for a fresh HyperOpt Run...'
|
|
self.logger.info(Color.green(message))
|
|
|
|
def download_candle_data(self, timerange: str = None):
|
|
"""
|
|
Downloads candle data for the given timerange.
|
|
|
|
:param timerange: (string, optional) Timerange from/to in string format (start-end): yyyymmdd-yyyymmdd.
|
|
Defaults to timerange out of ".hurry" config file. -or- for preset timerange: down, side, up
|
|
"""
|
|
self.logger.info(Color.title('👉 Download candle data'))
|
|
|
|
if self.monigomani_config.reload() is False:
|
|
self.logger.error(Color.red('🤷 No Hurry config file found. Please run: mgm-hurry setup'))
|
|
sys.exit(1)
|
|
|
|
timerange = self.monigomani_config.get_preset_timerange(timerange)
|
|
|
|
if self.freqtrade_cli.installation_exists() is False:
|
|
self.logger.error(Color.red('🤷 No Freqtrade installation found. I crash.'))
|
|
|
|
answers = prompt(questions=[{
|
|
'type': 'input',
|
|
'name': 'timerange',
|
|
'message': 'Please enter the timerange you want to use: ',
|
|
'default': timerange
|
|
}, {
|
|
'type': 'checkbox',
|
|
'name': 'tickers',
|
|
'message': 'Specify which tickers to download: ',
|
|
'choices': [
|
|
{'name': '1m', 'value': '1m', 'enabled': False},
|
|
{'name': '3m', 'value': '3m', 'enabled': False},
|
|
{'name': '5m', 'value': '5m', 'enabled': True},
|
|
{'name': '15m', 'value': '15m', 'enabled': False},
|
|
{'name': '30m', 'value': '30m', 'enabled': True},
|
|
{'name': '1h', 'value': '1h', 'enabled': False},
|
|
{'name': '2h', 'value': '2h', 'enabled': False},
|
|
{'name': '4h', 'value': '4h', 'enabled': False},
|
|
{'name': '6h', 'value': '6h', 'enabled': False},
|
|
{'name': '8h', 'value': '8h', 'enabled': False},
|
|
{'name': '12h', 'value': '12h', 'enabled': False},
|
|
{'name': '1d', 'value': '1d', 'enabled': False},
|
|
{'name': '3d', 'value': '3d', 'enabled': False},
|
|
{'name': '1w', 'value': '1w', 'enabled': False},
|
|
{'name': '2w', 'value': '2w', 'enabled': False},
|
|
{'name': '1M', 'value': '1M', 'enabled': False},
|
|
{'name': '1y', 'value': '1y', 'enabled': False}
|
|
],
|
|
'validate': lambda result: len(result) >= 1,
|
|
'invalid_message': 'should be at least 1 selection',
|
|
'instruction': '(select at least 1)',
|
|
'filter': lambda val: ' '.join(val)
|
|
}, {
|
|
'type': 'list',
|
|
'name': 'extra_data',
|
|
'message': 'Download extra data for "mgm-config"\'s defined "startup_candle_count" & "timerange"?',
|
|
'choices': [
|
|
{'name': 'Yes', 'value': 1},
|
|
{'name': 'No', 'value': 0}
|
|
]
|
|
}])
|
|
|
|
if answers == {}:
|
|
sys.exit()
|
|
|
|
timerange = answers.get('timerange')
|
|
tickers = answers.get('tickers')
|
|
extra_data = answers.get('extra_data')
|
|
|
|
if extra_data == 1:
|
|
new_timerange_dict = self.monigomani_cli.calculate_timerange_start_minus_startup_candle_count(timerange)
|
|
timerange = new_timerange_dict['new_timerange']
|
|
|
|
self.logger.info(f'👉 Downloading candle data ({tickers}) for timerange ({timerange})')
|
|
|
|
command = (f'{self.monigomani_config.config["ft_binary"]} download-data --timerange {timerange} '
|
|
f'-t {tickers} {self.monigomani_config.command_configs()}')
|
|
|
|
if self.monigomani_config.config['exchange'] == 'kraken':
|
|
command += ' --dl-trades'
|
|
|
|
self.logger.debug(command)
|
|
self.monigomani_cli.run_command(command)
|
|
self.logger.info(Color.green('🍺 Downloading candle data finished.'))
|
|
|
|
def download_static_pairlist(self, stake_currency: str = None, exchange: str = None,
|
|
pairlist_length: int = None, min_days_listed: int = None):
|
|
"""
|
|
Downloads StaticPairList and saves into 'mgm-config' and the 'mgm_pair_lists' folder.
|
|
|
|
:param stake_currency: (str) The stake currency to find the list of. Defaults to value in '.hurry' or 'USDT'
|
|
:param exchange: (str) The exchange to read the data from. Defaults to value in '.hurry' or 'binance'
|
|
:param pairlist_length (int) Amount of pairs wish to use in your pairlist. Prompts you for the amount
|
|
:param min_days_listed (int) The minimal days that coin pairs need to be listed on the exchange.
|
|
Defaults to the amount of days in between now and the start of
|
|
the timerange in '.hurry' minus the startup_candle_count
|
|
:return None
|
|
"""
|
|
self.logger.info(Color.title('👉 Download Top Volume Static Pairlist'))
|
|
|
|
if self.monigomani_config.reload() is False:
|
|
self.logger.error(Color.red('🤷 No Hurry config file found. Please run: mgm-hurry setup'))
|
|
sys.exit(1)
|
|
|
|
if self.freqtrade_cli.installation_exists() is False:
|
|
self.logger.error(Color.red('🤷 Whaaaa.. No Freqtrade installation found. Beats me...'))
|
|
sys.exit(1)
|
|
|
|
# Download and generate a static pairlist
|
|
self.logger.info('🤖 Generating new static pairlist...')
|
|
|
|
if stake_currency is None:
|
|
stake_currency = self.monigomani_config.config['hyperopt']['stake_currency']
|
|
if exchange is None:
|
|
exchange = self.monigomani_config.config['exchange'].title()
|
|
|
|
static_pairlist = self.freqtrade_cli.download_static_pairlist(
|
|
stake_currency=stake_currency, exchange=exchange,
|
|
pairlist_length=pairlist_length, min_days_listed=min_days_listed)
|
|
|
|
# Save new static pairlist to his own pairlist json file
|
|
relative_location = f'mgm_pair_lists/{exchange}-{stake_currency}-Top-Volume-StaticPairList.json'
|
|
self.logger.info(f'🤖 Saving fresh static pairlist into {relative_location}')
|
|
with open(f'{self.basedir}/user_data/{relative_location}', 'w+') \
|
|
as pairlist_file:
|
|
data_format = {'exchange': {'pair_whitelist': static_pairlist}}
|
|
json.dump(data_format, pairlist_file, indent=4)
|
|
|
|
mgm_json_name = self.monigomani_config.config['mgm_config_names']['mgm-config']
|
|
self.logger.info(Color.green(f'🍺 Saving pairlist as whitelist to "{mgm_json_name}"...'))
|
|
# Overwrite the new static pairlist to the exchange config in mgm-config[exchange][pair_whitelist]
|
|
mgm_config_file = self.monigomani_config.get_config_filepath('mgm-config')
|
|
with open(mgm_config_file, ) as mgm_config:
|
|
try:
|
|
mgm_config_object = json.load(mgm_config)
|
|
except Exception:
|
|
mgm_config_object = {'exchange': {'pair_whitelist': []}}
|
|
mgm_config.close()
|
|
|
|
with open(mgm_config_file, 'w') as mgm_config:
|
|
mgm_config_object['exchange']['pair_whitelist'] = static_pairlist
|
|
json.dump(mgm_config_object, mgm_config, indent=4)
|
|
mgm_config.close()
|
|
|
|
def hyperopt(self, timerange: str = None, strategy: str = None, loss: str = None, spaces: str = None,
|
|
enable_protections: bool = True, random_state: int = None, apply_best_results: bool = True,
|
|
clean_start: bool = False, do_backtest: bool = True, plot_profits: bool = True, plot_stats: bool = True,
|
|
importance_report: bool = True, export_csv: bool = True, output_file_name: str = None,
|
|
epochs: int = None, jobs: int = None, min_trades: int = None):
|
|
"""
|
|
HyperOpt Magic. Runs HyperOpt process to find out the most positive settings, automatically saves results.
|
|
|
|
:param timerange: (str, Optional): timerange from/to in format (start-end): yyyymmdd-yyyymmdd
|
|
or preset [up, down, side]. Defaults to value in '.hurry'
|
|
:param strategy: (str, Optional): Hyper Opt strategy to use. Defaults to value in '.hurry'
|
|
:param loss: (str, Optional): Specify the HyperOptLoss which you want to use. Defaults to value in '.hurry'
|
|
:param spaces: (str, Optional): Spaces (space delimited) to optimize
|
|
for [default, all, buy, sell, roi, stoploss, etc]. Defaults to value in '.hurry'
|
|
:param enable_protections: (bool, Optional): Add '--enable-protections' flag to HO-command. Defaults to True.
|
|
:param random_state: (int, Optional): Add '--random-state random_state' flag to HO-command. Defaults to None.
|
|
:param apply_best_results: (bool, Optional): Apply 'best' HO results direct after HO? Defaults to True.
|
|
:param clean_start: (bool, Optional): Cleanup MoniGoMani before starting the HO? Defaults to False.
|
|
:param do_backtest: (bool, Optional): Do a BackTest after the HO? Defaults to True.
|
|
:param plot_profits: (bool, Optional): Plot a Profits graph after the BackTest? Defaults to True.
|
|
:param plot_stats: (bool, Optional): Plot a QuantStats report after the BackTest? Defaults to True.
|
|
:param importance_report: (bool, Optional): Calculate a signal importance report after the HO? Defaults to True.
|
|
:param export_csv: (bool, Optional): Export the HO results into a '.csv' SpreadSheet? Defaults to True.
|
|
:param output_file_name: (str, Optional) Custom filename for the HyperOptResults '.log' file that will be
|
|
stored in the format: 'HyperOptResults-<output_file_name>.log'. Defaults to 'Strategy + Current DateTime'
|
|
:param epochs: (int) Amount of epochs to HyperOpt over. Defaults to value defined in '.hurry'
|
|
:param jobs: (int) Amount of parallel workers (CPU cores) to use, gets set automatically by default
|
|
:param min_trades: (int) Minimal amount of trades wished to be reached. Unused by default
|
|
"""
|
|
|
|
if strategy is None:
|
|
strategy = self.monigomani_config.config['hyperopt']['strategy']
|
|
if epochs is None:
|
|
epochs = self.monigomani_config.config['hyperopt']['epochs']
|
|
|
|
initial_run = True
|
|
if clean_start is True:
|
|
self.cleanup(strategy)
|
|
elif os.path.isfile(f'{self.basedir}/user_data/strategies/{strategy}.json') or (
|
|
(strategy == 'MoniGoManiHyperStrategy') and (
|
|
os.path.isfile(self.monigomani_config.get_config_filepath('mgm-config-hyperopt')) is True)):
|
|
initial_run = False
|
|
|
|
self.logger.info(Color.title('👉 Starting HyperOpt run. Keep calm while your computer burns 🔥'))
|
|
|
|
timerange = self.monigomani_config.get_preset_timerange(timerange)
|
|
|
|
if loss is None:
|
|
loss = self.monigomani_config.config['hyperopt']['loss']
|
|
if spaces is None:
|
|
spaces = self.monigomani_config.config['hyperopt']['spaces']
|
|
|
|
command = (f'$ft_binary hyperopt -s $ho_strategy {self.monigomani_config.command_configs()}'
|
|
f'--hyperopt-loss $ho_loss --spaces $ho_spaces -e $ho_epochs --timerange $timerange ')
|
|
|
|
if enable_protections is True:
|
|
command = f'{command.strip()} --enable-protections '
|
|
if random_state is not None:
|
|
command = f'{command.strip()} --random-state $random_state '
|
|
if jobs is not None:
|
|
command = f'{command.strip()} -j $jobs '
|
|
if min_trades is not None:
|
|
command = f'{command.strip()} --min-trades $min_trades '
|
|
|
|
command = Template(command).substitute(
|
|
ft_binary=self.monigomani_config.config['ft_binary'],
|
|
ho_strategy=strategy,
|
|
ho_loss=loss,
|
|
ho_spaces=spaces,
|
|
ho_epochs=epochs,
|
|
timerange=timerange,
|
|
random_state=random_state,
|
|
jobs=jobs,
|
|
min_trades=min_trades)
|
|
|
|
self.logger.debug(command)
|
|
if output_file_name is None:
|
|
output_file_name = f'{strategy}-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}'
|
|
hyperopt_file_name = f'HyperOptResults-{output_file_name}'
|
|
output_file_path = f'{self.basedir}/user_data/hyperopt_results/{hyperopt_file_name}.log'
|
|
|
|
self.monigomani_cli.run_command(command=command, output_file_name=output_file_path,hyperopt=True)
|
|
|
|
last_ho_results_path = f'{self.basedir}/user_data/hyperopt_results/.last_ho_results_table.log'
|
|
if os.stat(last_ho_results_path).st_size == 0:
|
|
self.logger.error(Color.red('🤷 No "best" results found during the HyperOpt, aborting further tests '
|
|
'since there are no HyperOpt results to test upon.'))
|
|
sys.exit(1)
|
|
|
|
if (apply_best_results is True) and (strategy == 'MoniGoManiHyperStrategy'):
|
|
self.monigomani_cli.apply_mgm_results(strategy)
|
|
self.monigomani_config.save_weak_strong_signal_overrides()
|
|
|
|
# Read 'mgm-config-hyperopt' or <StrategyName>.json and pretty print it
|
|
if strategy == 'MoniGoManiHyperStrategy':
|
|
ho_results_json_path = self.monigomani_config.get_config_filepath('mgm-config-hyperopt')
|
|
else:
|
|
ho_results_json_path = f'{self.basedir}/user_data/strategies/{strategy}.json'
|
|
with open(ho_results_json_path, ) as ho_results_json:
|
|
ho_results_json_string = f'\n{json.dumps(json.load(ho_results_json), indent=4)}'
|
|
self.logger.info(ho_results_json_string)
|
|
|
|
ho_results_log = open(output_file_path, 'a')
|
|
ho_results_log.write(ho_results_json_string)
|
|
ho_results_log.close()
|
|
|
|
if initial_run is True:
|
|
message = f'🚀 Fresh **{strategy}** Initial HyperOpt Run Results ⬇️\n'
|
|
elif strategy == 'MoniGoManiHyperStrategy':
|
|
message = f'🚀 Fresh **{strategy}** Refinement HyperOpt Run Results ⬇️\n'
|
|
else:
|
|
message = f'🚀 Fresh **{strategy}** HyperOpt Run Results ⬇️\n'
|
|
|
|
MoniGoManiLogger(self.basedir).post_message(username=self.monigomani_config.config['username'], message=message,
|
|
results_paths=[last_ho_results_path, output_file_path])
|
|
if (importance_report is True) and (strategy == 'MoniGoManiHyperStrategy'):
|
|
self.importance_report(output_file_name=output_file_name)
|
|
if apply_best_results is False:
|
|
self.cleanup(strategy)
|
|
if do_backtest is True:
|
|
self.logger.warning(Color.yellow('🤷 Backtesting without HyperOptResults applied?..'))
|
|
|
|
if export_csv is True:
|
|
self.export_csv(strategy=strategy, output_file_name=output_file_name)
|
|
if do_backtest is True:
|
|
self.backtest(timerange=timerange, strategy=strategy, enable_protections=enable_protections,
|
|
plot_profits=plot_profits, plot_stats=plot_stats, output_file_name=output_file_name)
|
|
else:
|
|
MoniGoManiLogger(self.basedir).post_setup(config=self.monigomani_config.config,
|
|
strategy=strategy, basedir=self.basedir)
|
|
|
|
self.logger.info(Color.green('🍺 HyperOpt run ready... 🥵'))
|
|
|
|
def hyperopt_show_results(self, only_best: bool = True, only_profitable: bool = False):
|
|
"""
|
|
Show hyperopt results after choosing a .fthypt file.
|
|
|
|
:param only_best: (bool, Optional); Show only best epochs. Defaults to True.
|
|
:param only_profitable: (bool, Optional); Show only profitable epochs. Defaults to False.
|
|
"""
|
|
|
|
choice = self.freqtrade_cli.choose_fthypt_file()
|
|
|
|
self.logger.info(Color.title(f'👉 Showing hyperopt results for: {choice}'))
|
|
|
|
best = '--best' if only_best is True else ''
|
|
profit = '--profitable' if only_profitable is True else ''
|
|
|
|
command = (f'{self.monigomani_config.config["ft_binary"]} hyperopt-list '
|
|
f'--hyperopt-filename "{choice}" {best} {profit}')
|
|
self.logger.debug(command)
|
|
|
|
self.monigomani_cli.run_command(command)
|
|
|
|
def hyperopt_show_epoch(self, epoch: int = 0, strategy: str = None, apply: bool = True, fthypt: str = None):
|
|
"""
|
|
Shows the HyperOpt results for a given epoch.
|
|
|
|
:param epoch: (int) The epoch number to show the results from
|
|
:param strategy: (str) The strategy used. Defaults to value in '.hurry'
|
|
:param apply: (bool) Apply the results of the epoch. Defaults to true
|
|
:param fthypt: (str) '.fthypt' file to show epoch. If 'true' is passed,
|
|
an interactive prompt will launch to pick an '.fthypt' file of choice. Defaults to latest'
|
|
"""
|
|
self.logger.info(Color.title(f'👉 Hyperopt show {"" if apply is False else "and apply "}results'))
|
|
|
|
fthypt_name = None
|
|
if fthypt is not None:
|
|
fthypt_name = self.freqtrade_cli.parse_fthypt_name(fthypt_name=fthypt)
|
|
|
|
if epoch == 0:
|
|
self.logger.error(Color.red('🤷 Please pass the epoch number through. '
|
|
'Hint: mgm-hurry hyperopt_show_epoch --epoch=837'))
|
|
sys.exit(1)
|
|
|
|
self.logger.info(f'👉 Showing {"" if apply is False else "and applying "}HyperOpt results for epoch #{epoch}')
|
|
|
|
command = (f'{self.monigomani_config.config["ft_binary"]} hyperopt-show -n {epoch} '
|
|
f'{self.monigomani_config.command_configs()}')
|
|
|
|
if fthypt_name is not None:
|
|
command += f' --hyperopt-filename {fthypt_name}'
|
|
|
|
self.monigomani_cli.run_command(command)
|
|
|
|
if strategy is None:
|
|
strategy = self.monigomani_config.config['hyperopt']['strategy']
|
|
if (apply is True) and (strategy == 'MoniGoManiHyperStrategy'):
|
|
self.monigomani_cli.apply_mgm_results(strategy)
|
|
|
|
if apply is True:
|
|
self.logger.info(Color.green(f'🍺 Hyperopt results of epoch #{epoch} are applied.'))
|
|
else:
|
|
self.cleanup(strategy)
|
|
|
|
def backtest(self, timerange: str = None, strategy: str = None, enable_protections: bool = True,
|
|
plot_profits: bool = True, plot_stats: bool = True, output_file_name: str = None):
|
|
"""
|
|
Run a backtest session and automatically save the results.
|
|
|
|
:param timerange: (string, Optional): The target timerange for backtesting. Defaults to timerange in '.hurry'.
|
|
:param strategy: (str, Optional): Strategy to use during BackTesting. Defaults to value in '.hurry'
|
|
:param enable_protections: (bool, Optional): Whether or not to enable protections. Defaults to True.
|
|
:param plot_profits: (bool, Optional): Automatically plot visual profit graphs in '.html'. Defaults to True.
|
|
:param plot_stats: (bool, Optional): Automatically plot visual stats report in '.html'. Defaults to True.
|
|
:param output_file_name: (str, Optional) Custom filename for the BackTestResults '.log' file that will be stored
|
|
in the format: 'BackTestResults-<output_file_name>.log'. Defaults to 'Strategy + Current DateTime'.
|
|
"""
|
|
self.logger.info(Color.title('👉 Start BackTesting. Lets see how it all turns out!'))
|
|
|
|
timerange = self.monigomani_config.get_preset_timerange(timerange)
|
|
command = (f'$ft_binary backtesting -s $ho_strategy {self.monigomani_config.command_configs()}'
|
|
f'--timerange $timerange')
|
|
|
|
if timerange is None:
|
|
timerange = self.monigomani_config.config['timerange']
|
|
if strategy is None:
|
|
strategy = self.monigomani_config.config['hyperopt']['strategy']
|
|
if enable_protections is True:
|
|
command = f'{command} --enable-protections'
|
|
|
|
if (strategy == 'MoniGoManiHyperStrategy') and (
|
|
os.path.isfile(self.monigomani_config.get_config_filepath('mgm-config-hyperopt')) is False):
|
|
self.logger.error(Color.red(f'🤷 MoniGoManiHyperStrategy needs to be HyperOpted before it will '
|
|
f'start to produce valid BackTest Results!\n(Hint: Run "mgm-hurry hyperopt")'))
|
|
return None
|
|
|
|
command = Template(command).substitute(
|
|
ft_binary=self.monigomani_config.config['ft_binary'],
|
|
ho_strategy=strategy,
|
|
timerange=timerange)
|
|
|
|
if output_file_name is None:
|
|
output_file_name = f'{strategy}-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}'
|
|
backtest_file_name = f'BackTestResults-{output_file_name}'
|
|
output_file_path = f'{self.basedir}/user_data/backtest_results/{backtest_file_name}.log'
|
|
|
|
self.monigomani_cli.run_command(command, output_file_path, backtest=True)
|
|
message = f'🚀 Fresh **{strategy}** BackTest Results ⬇\n'
|
|
|
|
last_sell_reasons_path = f'{self.basedir}/user_data/backtest_results/.last_backtest_sell_reasons.log'
|
|
|
|
MoniGoManiLogger(self.basedir).post_message(username=self.monigomani_config.config['username'], message=message,
|
|
results_paths=[last_sell_reasons_path, output_file_path])
|
|
|
|
if plot_profits is True:
|
|
self.plot_profit(choose_results=False, timerange=timerange,
|
|
strategy=strategy, output_file_name=output_file_name)
|
|
if plot_stats is True:
|
|
try:
|
|
self.plot_stats(choose_results=False, strategy=strategy, output_file_name=output_file_name)
|
|
except:
|
|
self.logger.error(Color.red(f'Create stats html report failed'))
|
|
|
|
MoniGoManiLogger(self.basedir).post_setup(config=self.monigomani_config.config,
|
|
strategy=strategy, basedir=self.basedir)
|
|
|
|
self.logger.info(Color.green('🍺 Backtesting ready... 🤑'))
|
|
|
|
def plot_profit(self, choose_results: bool = True, timerange: str = None,
|
|
timeframe: str = '1h', strategy: str = None, output_file_name: str = None) -> None:
|
|
"""
|
|
Plot the profits from a BackTest into visual graphs.
|
|
|
|
:param choose_results: (bool) If 'False' then the last BackTest result will be used.
|
|
Defaults to an interactive prompt to choose a 'backtest-result-<timestamp>.json' file.
|
|
:param timerange: (str) Timerange that corresponds to the timerange used for the backtest-result
|
|
:param timeframe: (str) Candle sizes to be used in the graphs that will be plotted. Defaults to '1h'
|
|
:param strategy: (str, Optional): Strategy to use during BackTesting. Defaults to value in '.hurry'
|
|
:param output_file_name: (str, Optional) Custom filename for the profit plot '.html' file that will be stored
|
|
in the format: 'PlotProfitResults-<output_file_name>.html'. Defaults to 'Strategy + Current DateTime'.
|
|
"""
|
|
|
|
self.logger.info(Color.title(f'👉 Plotting profit graph for BackTest results'))
|
|
|
|
if timerange is None:
|
|
timerange = self.monigomani_config.config['timerange']
|
|
if strategy is None:
|
|
strategy = self.monigomani_config.config['hyperopt']['strategy']
|
|
|
|
backtest_results_file = self.freqtrade_cli.choose_backtest_results_file(choose_results=choose_results)
|
|
backtest_results_path = f'{self.basedir}/user_data/backtest_results/{backtest_results_file}'
|
|
|
|
if os.path.isfile(backtest_results_path) is True:
|
|
# Load the 'backtest-result-<timestamp>.json' file as an object and parse it as a dictionary
|
|
file_object = open(backtest_results_path, )
|
|
backtest_results = json.load(file_object)
|
|
|
|
if len(backtest_results['strategy'][strategy]['trades']) == 0:
|
|
self.logger.error(Color.red(f'🤷 No trades where done in the given {backtest_results_file} file.\n'
|
|
f'Please provide a BackTest results file in which '
|
|
f'actual trading has been done!'))
|
|
return None
|
|
else:
|
|
self.logger.error(Color.red(f'🤷 {backtest_results_file} file could not be found.\nPlease make sure that '
|
|
f'the provided BackTest results file actually exists!'))
|
|
return None
|
|
|
|
if output_file_name is None:
|
|
output_file_name = f'{strategy}-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}'
|
|
plot_profit_file_name = f'PlotProfitResults-{output_file_name}'
|
|
output_file_path = f'{self.basedir}/user_data/plot/{plot_profit_file_name}.html'
|
|
|
|
self.monigomani_cli.run_command(
|
|
f'{self.monigomani_config.config["ft_binary"]} plot-profit {self.monigomani_config.command_configs()}'
|
|
f'--export-filename {backtest_results_path} --timerange {timerange} --timeframe {timeframe} && '
|
|
f'mv {self.basedir}/user_data/plot/freqtrade-profit-plot.html {output_file_path}')
|
|
|
|
MoniGoManiLogger(self.basedir).post_message(
|
|
username=self.monigomani_config.config['username'],
|
|
message=f'🚀 Fresh **{strategy}** Plot Profit Results ⬇️', results_paths=[output_file_path])
|
|
|
|
def plot_stats(self, choose_results: bool = True,
|
|
strategy: str = None, output_file_name: str = None) -> None:
|
|
"""
|
|
Plot the stats report from a BackTest into detail html file.
|
|
|
|
:param choose_results: (bool) If 'False' then the last BackTest result will be used.
|
|
Defaults to an interactive prompt to choose a 'backtest-result-<timestamp>.json' file.
|
|
:param strategy: (str, Optional): Strategy to use during BackTesting. Defaults to value in '.hurry'
|
|
:param output_file_name: (str, Optional) Custom filename for the profit plot '.html' file that will be stored
|
|
in the format: 'PlotProfitResults-<output_file_name>.html'. Defaults to 'Strategy + Current DateTime'.
|
|
"""
|
|
|
|
self.logger.info(Color.title(f'👉 Plotting profit graph for BackTest results'))
|
|
|
|
# extend pandas functionality with metrics, etc.
|
|
qs.extend_pandas()
|
|
|
|
if strategy is None:
|
|
strategy = self.monigomani_config.config['hyperopt']['strategy']
|
|
|
|
backtest_results_file = self.freqtrade_cli.choose_backtest_results_file(choose_results=choose_results)
|
|
backtest_results_path = f'{self.basedir}/user_data/backtest_results/{backtest_results_file}'
|
|
|
|
if os.path.isfile(backtest_results_path) is True:
|
|
# Load the 'backtest-result-<timestamp>.json' file as an object and parse it as a dictionary
|
|
file_object = open(backtest_results_path, )
|
|
backtest_results = json.load(file_object)
|
|
|
|
if len(backtest_results['strategy'][strategy]['trades']) == 0:
|
|
self.logger.error(Color.red(f'🤷 No trades where done in the given {backtest_results_file} file.\n'
|
|
f'Please provide a BackTest results file in which '
|
|
f'actual trading has been done!'))
|
|
return None
|
|
else:
|
|
self.logger.error(Color.red(f'🤷 {backtest_results_file} file could not be found.\nPlease make sure that '
|
|
f'the provided BackTest results file actually exists!'))
|
|
return None
|
|
|
|
if output_file_name is None:
|
|
output_file_name = f'{strategy}-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}'
|
|
plot_profit_file_name = f'PlotStatsReports-{output_file_name}'
|
|
output_file_path = f'{self.basedir}/user_data/plot/{plot_profit_file_name}.html'
|
|
|
|
starting_balance = backtest_results['strategy'][strategy]['starting_balance']
|
|
backtest_trades = pd.DataFrame(backtest_results['strategy'][strategy]['trades'])
|
|
|
|
backtest_trades['open_date'] = pd.to_datetime(backtest_trades['open_date'], utc=True, infer_datetime_format=True)
|
|
daily_returns = backtest_trades[['open_date', 'profit_abs']].groupby(pd.Grouper(key="open_date", freq='D')).sum('profit_abs')
|
|
daily_returns['balance'] = np.cumsum(daily_returns['profit_abs']) + starting_balance
|
|
|
|
dates = pd.date_range(daily_returns.index[0], daily_returns.index[-1], freq='D').tz_localize(None)
|
|
|
|
daily_returns = pd.Series(daily_returns.balance).pct_change(1).values
|
|
returns_time_series = pd.Series(daily_returns, index=pd.to_datetime(list(dates)))
|
|
|
|
qs.reports.html(returns=returns_time_series, title=f'{strategy} {backtest_results_file}', output=output_file_path)
|
|
|
|
MoniGoManiLogger(self.basedir).post_message(
|
|
username=self.monigomani_config.config['username'],
|
|
message=f'🚀 Fresh **{strategy}** Plot Stats Reports ⬇️', results_paths=[output_file_path])
|
|
|
|
def importance_report(self, output_file_name: str = None) -> None:
|
|
"""
|
|
Run the TotalOverallSignalImportanceCalculator to create a signal importance report for MoniGoMani
|
|
|
|
:param output_file_name: (str, Optional) Custom filename for the SignalImportanceResults '.log' file
|
|
that will be stored in the format: 'SignalImportanceResults-<output_file_name>.log'.
|
|
Defaults to 'MoniGoManiHyperStrategy + Current DateTime'.
|
|
"""
|
|
|
|
self.logger.info(Color.title(f'👉 Calculating Total Overall Signal Importance Report for MoniGoMani'))
|
|
|
|
mgm_config_files = self.monigomani_config.load_config_files()
|
|
if mgm_config_files["mgm-config"] is None:
|
|
self.logger.warning(Color.yellow(f'🤷 No "mgm-config" file found in the "user_data" directory. '
|
|
f'Please run: mgm-hurry setup'))
|
|
return None
|
|
elif mgm_config_files["mgm-config-hyperopt"] is None:
|
|
self.logger.warning(Color.yellow(f'🤷 No "mgm-config-hyperopt" file found in the "user_data" directory. '
|
|
f'Please run: mgm-hurry hyperopt'))
|
|
return None
|
|
|
|
if output_file_name is None:
|
|
output_file_name = f'MoniGoManiHyperStrategy-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}'
|
|
importance_report_file_name = f'SignalImportanceResults-{output_file_name}'
|
|
output_file_path = f'{self.basedir}/user_data/importance_results/{importance_report_file_name}.log'
|
|
|
|
self.monigomani_cli.run_command(
|
|
f'python3 {self.basedir}/user_data/mgm_tools/TotalOverallSignalImportanceCalculator.py '
|
|
f'-pu {mgm_config_files["mgm-config"]["monigomani_settings"]["precision"]} -cf {output_file_path}')
|
|
|
|
MoniGoManiLogger(self.basedir).post_message(
|
|
username=self.monigomani_config.config['username'],
|
|
message=f'🚀 Fresh **MoniGoManiHyperStrategy** Signal Importance Report ⬇️',
|
|
results_paths=[output_file_path])
|
|
|
|
def export_csv(self, strategy: str = 'MoniGoManiHyperStrategy',
|
|
output_file_name: str = None, fthypt: str = None) -> None:
|
|
"""
|
|
Export the '.fthypt' results to an easy to interpret/sort/filter '.csv' SpreadSheet.
|
|
|
|
:param strategy: Name of the used Strategy, defaults to 'MoniGoManiHyperStrategy'
|
|
:param output_file_name: (str, Optional) Custom filename for the CsvResults '.csv' file
|
|
that will be stored in the format: 'CsvResults-<output_file_name>.csv'.
|
|
Defaults to 'StrategyName + Current DateTime'.
|
|
:param fthypt: (str, Optional) '.fthypt' file to export results from.. If 'true' is passed,
|
|
an interactive prompt will launch to pick an '.fthypt' file of choice. Defaults to latest
|
|
"""
|
|
|
|
self.logger.info(Color.title(f'👉 Exporting HyperOpt Results to SpreadSheet (.csv) format.'))
|
|
|
|
mgm_config_files = self.monigomani_config.load_config_files()
|
|
if mgm_config_files["mgm-config"] is None:
|
|
self.logger.warning(Color.yellow(f'🤷 No "mgm-config" file found in the "user_data" directory. '
|
|
f'Please run: mgm-hurry setup'))
|
|
return None
|
|
|
|
fthypt_name = None
|
|
fthypt_file_path = None
|
|
if fthypt is not None:
|
|
fthypt_name = self.freqtrade_cli.parse_fthypt_name(fthypt_name=fthypt)
|
|
fthypt_file_path = f'{self.basedir}/user_data/hyperopt_results/{fthypt_name}'
|
|
|
|
if output_file_name is None:
|
|
output_file_name = f'MoniGoManiHyperStrategy-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}'
|
|
csv_results_file_name = f'CsvResults-{output_file_name}'
|
|
output_file_path = f'{self.basedir}/user_data/csv_results/{csv_results_file_name}.csv'
|
|
|
|
mgm_config = self.monigomani_config.get_config_filepath('mgm-config')
|
|
command = (f'python3 {self.basedir}/user_data/mgm_tools/ExportCsvResults.py '
|
|
f'-c {mgm_config} -o {output_file_path}')
|
|
|
|
if fthypt_name is not None:
|
|
command += f' -i {fthypt_file_path}'
|
|
|
|
self.monigomani_cli.run_command(command=command)
|
|
MoniGoManiLogger(self.basedir).post_message(username=self.monigomani_config.config['username'],
|
|
message=f'🚀 Fresh **{strategy}** SpreadSheet (.csv) Results ⬇️',
|
|
results_paths=[output_file_path])
|
|
|
|
def start_trader(self, dry_run: bool = True):
|
|
"""
|
|
Start the trader. Your ultimate goal!
|
|
|
|
:param dry_run: (bool, Optional): Use dry_run mode. Defaults to True
|
|
"""
|
|
self.logger.info(Color.title(f'👉 Starting the trader, '
|
|
f'{"in Dry-Run Mode!" if dry_run else "in Live-Run Mode, fingers crossed! 🤞"}'))
|
|
|
|
command = (f'{self.monigomani_config.config["ft_binary"]} trade {self.monigomani_config.command_configs()}'
|
|
f'--strategy {self.monigomani_config.config["hyperopt"]["strategy"]}')
|
|
|
|
if dry_run is True:
|
|
command += ' --dry-run'
|
|
|
|
self.monigomani_cli.run_command(command)
|
|
|
|
def __setup_telegram(self) -> bool:
|
|
"""
|
|
Questionnaire to setup Telegram Bot
|
|
|
|
:return bool: False if no answers are given, True if all went ok.
|
|
"""
|
|
answers = prompt(questions=[{
|
|
'type': 'confirm',
|
|
'message': 'Do you want to also setup your Telegram bot? ',
|
|
'name': 'proceed_telegram',
|
|
'default': True
|
|
}, {
|
|
'type': 'confirm',
|
|
'message': 'Do you want to enable the Telegram Bot?',
|
|
'name': 'enable_telegram',
|
|
'default': True,
|
|
'when': lambda result: result['proceed_telegram']
|
|
}, {
|
|
'type': 'password',
|
|
'name': 'telegram_token',
|
|
'message': 'Please enter your Telegram Bot token: ',
|
|
'default': '',
|
|
'when': lambda result: result['proceed_telegram'] and result['enable_telegram']
|
|
}, {
|
|
'type': 'input',
|
|
'name': 'telegram_chat_id',
|
|
'message': 'Please enter the chat ID: ',
|
|
'default': '',
|
|
'when': lambda result: result['proceed_telegram'] and result['enable_telegram']
|
|
}])
|
|
|
|
if answers == {}:
|
|
return False
|
|
|
|
if answers.get('proceed_telegram') is True:
|
|
self.monigomani_config.save_telegram_credentials({
|
|
'enable_telegram': answers.get('enable_telegram'),
|
|
'telegram_token': answers.get('telegram_token'),
|
|
'telegram_chat_id': answers.get('telegram_chat_id')
|
|
})
|
|
|
|
return True
|
|
|
|
|
|
if __name__ == '__main__':
|
|
try:
|
|
fire.Fire(MGMHurry)
|
|
except KeyboardInterrupt:
|
|
print('😼 KTHXBAI')
|
|
sys.exit(0)
|