Exemple #1
0
class Engine:
    """
    Main class for Simulation Engine
    """

    arg_parser = configargparse.get_argument_parser()
    arg_parser.add("--strategy", help="Name of strategy to be run (if not set, the default one will be used")
    arg_parser.add("--plot", help="Generate a candle stick plot at simulation end", action='store_true')
    arg_parser.add("--ticker_size", help="Simulation ticker size", default=5)
    arg_parser.add("--root_report_currency", help="Root currency used in final plot")
    arg_parser.add("--buffer_size", help="Buffer size in days", default=30)
    arg_parser.add("--prefetch", help="Prefetch data from history DB",  action='store_true')
    arg_parser.add("--plot_pair", help="Plot pair")
    arg_parser.add("--all", help="Include all currencies/tickers")
    arg_parser.add("--days", help="Days to pre-fill")

    buffer_size = None
    interval = None
    pairs = None
    verbosity = None
    ticker = None
    look_back = None
    history = None
    bot = None
    report = None
    plot = None
    plot_pair = None
    trade_mode = None
    root_report_currency = None
    config_strategy_name = None
    actions = None
    prefetch = None
    first_ticker = None
    last_valid_ticker = None

    def __init__(self):
        self.args = self.arg_parser.parse_known_args()[0]
        self.parse_config()
        strategy_class = common.load_module('strategies.', self.args.strategy)
        self.wallet = Wallet()
        self.history = pd.DataFrame()
        trade_columns = ['date', 'pair', 'close_price', 'action']
        self.trades = pd.DataFrame(columns=trade_columns, index=None)
        if self.args.backtest:
            self.bot = Backtest(self.wallet.initial_balance.copy())
            self.trade_mode = TradeMode.backtest
        elif self.args.paper:
            self.bot = Paper(self.wallet.initial_balance.copy())
            self.trade_mode = TradeMode.paper
            self.wallet.initial_balance = self.bot.get_balance()
            self.wallet.current_balance = self.bot.get_balance()
        elif self.args.live:
            self.bot = Live()
            self.trade_mode = TradeMode.live
            self.wallet.initial_balance = self.bot.get_balance()
            self.wallet.current_balance = self.bot.get_balance()
        self.strategy = strategy_class()
        self.pairs = self.bot.get_pairs()
        self.look_back = pd.DataFrame()
        self.max_lookback_size = int(self.buffer_size*(1440/self.interval)*len(self.pairs))
        self.initialize()

    def initialize(self):
        # Initialization
        self.report = Report(self.wallet.initial_balance,
                             self.pairs,
                             self.root_report_currency,
                             self.bot.get_pair_delimiter())
        self.report.set_verbosity(self.verbosity)
        self.plot = Plot()

    def parse_config(self):
        """
        Parsing of config.ini file
        """
        self.root_report_currency = self.args.root_report_currency
        self.buffer_size = self.args.buffer_size
        self.prefetch = self.args.prefetch
        if self.buffer_size != '':
            self.buffer_size = int(self.buffer_size)
        self.interval = self.args.ticker_size
        if self.interval != '':
            self.interval = int(self.interval)
        self.config_strategy_name = self.args.strategy
        self.plot_pair = self.args.plot_pair
        self.verbosity = self.args.verbosity

    def on_simulation_done(self):
        """
        Last function called when the simulation is finished
        """
        print('shutting down and writing final statistics!')
        strategy_info = self.report.write_final_stats(self.first_ticker.copy(),
                                                      self.last_valid_ticker.copy(),
                                                      self.wallet, self.trades)
        if self.args.plot:
            plot_title = ['Simulation: ' + str(self.trade_mode) + ' Strategy: ' + self.config_strategy_name + ', Pair: '
                          + str(self.pairs)]
            strategy_info = plot_title + strategy_info
            self.plot.draw(self.history,
                           self.trades,
                           self.plot_pair,
                           strategy_info)

    @staticmethod
    def validate_ticker(df):
        """
        Validates if the given dataframe contains mandatory fields
        """
        columns_to_check = ['close', 'volume']
        df_column_names = list(df)
        if not set(columns_to_check).issubset(df_column_names):
            return False
        nan_columns = df.columns[df.isnull().any()].tolist()
        nans = [i for i in nan_columns if i in columns_to_check]
        if len(nans) > 0:
            return False
        return True

    def simulation_finished(self, new_ticker):
        """
        Checks if simulation is finished based on new data
        """
        # If we got an empty dataset finish simulation
        if new_ticker.empty:
            print('New ticker is empty,..')
            return True

        # If we have not data just continue
        if self.ticker is None:
            return False

        # If we have received the same date,..we assume that we have no more data,..finish simulation
        if not self.ticker.empty and (self.ticker is not None and new_ticker.equals(self.ticker)):
            print(colored('Received ticker data are the same as previous data (this can happen,..but not too often): '
                          + str(new_ticker.date[0]), 'yellow'))
            if self.trade_mode == TradeMode.backtest:
                return True
            else:
                return False

        return False

    def run(self):
        """
        This is the main simulation loop
        """
        if self.bot is None:
            print(colored('The bots type is NOT specified. You need to choose one action (--sim, --paper, --trade)', 'red'))
            exit(0)

        print(colored('Starting simulation: ' + str(self.trade_mode) + ', Strategy: ' + self.config_strategy_name, 'yellow'))

        # Prefetch Buffer Data (if enabled in config)
        if self.prefetch:
            self.history = self.bot.prefetch(self.strategy.get_min_history_ticks(), self.interval)
            self.look_back = self.history.copy()

        try:
            while True:
                # Get next ticker
                new_ticker = self.bot.get_next(self.interval)
                # print('new_ticker____:', new_ticker)
                if self.simulation_finished(new_ticker):
                    print("No more data,..simulation done,. quitting")
                    exit(0)

                self.ticker = new_ticker

                # Check if ticker is valid
                if not self.validate_ticker(self.ticker):
                    print(colored('Received invalid ticker, will have to skip it! Details:\n' + str(self.ticker), 'red'))
                    continue

                # Save ticker to buffer
                self.history = self.history.append(self.ticker, ignore_index=True)
                self.look_back = self.look_back.append(self.ticker, ignore_index=True)

                # Check if buffer is not overflown
                self.look_back = common.handle_buffer_limits(self.look_back, self.max_lookback_size)

                if self.first_ticker is None or self.first_ticker.empty:
                    self.first_ticker = self.ticker.copy()
                self.last_valid_ticker = self.ticker.copy()

                # Get next actions
                self.actions = self.strategy.calculate(self.look_back, self.wallet)

                # Set trade
                self.actions = self.bot.trade(self.actions,
                                              self.wallet.current_balance,
                                              self.trades)

                # Get wallet balance
                self.wallet.current_balance = self.bot.get_balance()

                # Write report
                self.report.calc_stats(self.ticker, self.wallet)

        except KeyboardInterrupt:
            self.on_simulation_done()

        except SystemExit:
            self.on_simulation_done()
Exemple #2
0
class Engine:
    """
    Main class for Simulation Engine (main class where all is happening
    """
    buffer_size = None
    interval = None
    pairs = None
    verbosity = None
    ticker = None
    look_back = None
    history = None
    bot = None
    report = None
    plot = None
    plot_pair = None
    trade_mode = None
    root_report_currency = None
    config_strategy_name = None
    actions = None
    prefetch = None
    first_ticker = None
    last_valid_ticker = None

    def __init__(self, args, config_file):
        # Arguments should override config.ini file, so lets initialize
        # them only after config file parsing
        self.parse_config(config_file)
        self.args = args
        self.config = config_file
        strategy_class = self.load_strategy(args.strategy,
                                            self.config_strategy_name)
        self.strategy = strategy_class(args, self.verbosity)
        self.wallet = Wallet(config_file)
        self.history = pd.DataFrame()
        trade_columns = ['date', 'pair', 'close_price', 'action']
        self.trades = pd.DataFrame(columns=trade_columns, index=None)
        if args.backtest:
            self.bot = Backtest(args, config_file)
            self.trade_mode = TradeMode.backtest
        elif args.paper:
            self.bot = Paper(args, config_file)
            self.trade_mode = TradeMode.paper
            self.wallet.initial_balance = self.bot.get_balance()
            self.wallet.current_balance = self.bot.get_balance()
        elif args.live:
            self.bot = Live(args, config_file)
            self.trade_mode = TradeMode.live
            self.wallet.initial_balance = self.bot.get_balance()
            self.wallet.current_balance = self.bot.get_balance()
        self.pairs = self.bot.get_pairs()
        self.look_back = pd.DataFrame()
        self.max_lookback_size = int(self.buffer_size * (60 / self.interval) *
                                     len(self.pairs))
        self.initialize()

    def initialize(self):
        # Initialization
        self.report = Report(self.wallet.initial_balance, self.pairs,
                             self.root_report_currency,
                             self.bot.get_pair_delimiter())
        self.report.set_verbosity(self.verbosity)
        self.plot = Plot()

    @staticmethod
    def load_strategy(arg_strategy, config_strategy):
        """
        Loads strategy module based on given name.
        """
        if arg_strategy is None and config_strategy == '':
            print(
                colored(
                    'Not provided strategy,. please add it as an argument or in config file',
                    'red'))
            sys.exit()
        if arg_strategy is not None:
            strategy_name = arg_strategy
        else:
            strategy_name = config_strategy
        mod = import_module("strategies." + strategy_name)
        strategy_class = getattr(mod, strategy_name.capitalize())
        return strategy_class

    def parse_config(self, config_file):
        """
        Parsing of config.ini file
        """
        config = configparser.ConfigParser()
        config.read(config_file)
        self.root_report_currency = config['Trade']['root_report_currency']
        self.buffer_size = config['Trade']['buffer_size']
        self.prefetch = config.getboolean('Trade', 'prefetch')
        if self.buffer_size != '':
            self.buffer_size = int(self.buffer_size)
        self.interval = config['Trade']['interval']
        if self.interval != '':
            self.interval = int(self.interval)
        self.config_strategy_name = config['Trade']['strategy']
        self.plot_pair = config['Report']['plot_pair']
        self.verbosity = int(config['General']['verbosity'])

    def on_simulation_done(self):
        """
        Last function called when the simulation is finished
        """
        print('shutting down and writing final statistics!')
        if self.args.plot:
            self.plot.draw(self.history, self.trades, self.plot_pair)
        self.report.write_final_stats(self.first_ticker,
                                      self.last_valid_ticker, self.wallet,
                                      self.trades)

    @staticmethod
    def validate_ticker(df):
        """
        Validates if the given dataframe contains mandatory fields
        """
        columns_to_check = ['close', 'volume']
        df_column_names = list(df)
        if not set(columns_to_check).issubset(df_column_names):
            return False
        nan_columns = df.columns[df.isnull().any()].tolist()
        nans = [i for i in nan_columns if i in columns_to_check]
        if len(nans) > 0:
            return False
        return True

    def run(self):
        """
        This is the main simulation loop
        """
        if self.bot is None:
            print(
                colored(
                    'The bots type is NOT specified. You need to choose one action (--sim, --paper, --trade)',
                    'red'))
            sys.exit()

        print(
            colored(
                'Starting simulation: ' + str(self.trade_mode) +
                ', Strategy: ' + self.config_strategy_name, 'yellow'))

        # Prefetch Buffer Data (if enabled in config)
        if self.prefetch:
            self.history = self.bot.prefetch(
                self.strategy.get_min_history_ticks(), self.interval)
            self.look_back = self.history.copy()

        try:
            while True:
                # Get next ticker
                self.ticker = self.bot.get_next(self.interval)
                if self.ticker.empty:
                    print("No more data,..simulation done,. quitting")
                    exit(0)

                # Check if ticker is valid
                if not self.validate_ticker(self.ticker):
                    print(
                        colored(
                            'Received invalid ticker, will have to skip it! Details:\n'
                            + str(self.ticker), 'red'))
                    continue

                # Save ticker to buffer
                self.history = self.history.append(self.ticker,
                                                   ignore_index=True)
                self.look_back = self.look_back.append(self.ticker,
                                                       ignore_index=True)
                buffer_size = len(self.look_back.index)
                if buffer_size > self.max_lookback_size:
                    print('max memory exceeded, cleaning/cutting buffer')
                    rows_to_delete = buffer_size - self.max_lookback_size
                    self.look_back = self.look_back.ix[rows_to_delete:]
                    self.look_back = self.look_back.reset_index(drop=True)
                if self.first_ticker is None:
                    self.first_ticker = self.ticker
                self.last_valid_ticker = self.ticker

                # Get next actions
                self.actions = self.strategy.calculate(self.look_back,
                                                       self.wallet)

                # Set trade
                self.actions = self.bot.trade(self.actions,
                                              self.wallet.current_balance,
                                              self.trades)

                # Get wallet balance
                self.wallet.current_balance = self.bot.get_balance()

                # Write report
                self.report.calc_stats(self.ticker, self.wallet)

        except KeyboardInterrupt:
            self.on_simulation_done()

        except SystemExit:
            self.on_simulation_done()