예제 #1
0
class Base(ABC):
    """
    Base class for all simulation types (sim, paper, trade)
    """
    ticker_df = pd.DataFrame()
    pairs = []
    exchange = None
    balance = None
    arg_parser = configargparse.get_argument_parser()

    def __init__(self, trade_mode):
        super(Base, self).__init__()
        self.args = self.arg_parser.parse_known_args()[0]
        self.exchange = Exchange(trade_mode)
        self.transaction_fee = self.exchange.get_transaction_fee()
        self.ticker_df = pd.DataFrame()
        self.verbosity = self.args.verbosity
        self.pairs = common.parse_pairs(self.exchange, self.args.pairs)
        self.fixed_trade_amount = float(self.args.fixed_trade_amount)
        self.pair_delimiter = self.exchange.get_pair_delimiter()
        self.last_tick_epoch = 0

    def get_pair_delimiter(self):
        """
        Returns exchanges pair delimiter
        """
        return self.pair_delimiter

    def prefetch(self, min_ticker_size, ticker_interval):
        """
        Method pre-fetches data to ticker buffer
        """
        prefetch_epoch_size = ticker_interval * min_ticker_size * 60
        prefetch_days = math.ceil(prefetch_epoch_size / 86400)
        # Prefetch/Backfill data
        self.args.days = prefetch_days
        orig_pair = self.args.pairs
        backfill_candles = Candles()
        backfill_candles.run()
        self.args.pairs = orig_pair
        # Load data to our ticker buffer
        prefetch_epoch_size = ticker_interval * min_ticker_size * 60
        epoch_now = int(time.time())
        prefetch_epoch = epoch_now - prefetch_epoch_size
        print('Going to prefetch data of size (minutes): ',
              ticker_interval * min_ticker_size)
        df = pd.DataFrame()
        while prefetch_epoch < epoch_now:
            data = self.exchange.get_offline_ticker(prefetch_epoch, self.pairs)
            df = df.append(data, ignore_index=True)
            prefetch_epoch += (ticker_interval * 60)
        print('Fetching done..')
        return df

    def get_pairs(self):
        """
        Returns the pairs the bot is working with
        """
        return self.pairs

    @abstractmethod
    def get_next(self, interval_in_min):
        """
        Gets next data set
        :param interval_in_min:
        :return: New data in DataFrame
        """
        pass

    def get_balance(self):
        """
        Returns current balance
        """
        # Remove all items with zero amount
        self.balance = {k: v for k, v in self.balance.items() if v != 0}
        return self.balance

    def sell_all_assets(self, trades, wallet, pair_to_hold):
        """
        Sells all available assets in wallet
        """
        assets = wallet.copy()
        del assets['BTC']
        for asset, amount in assets.items():
            if amount == 0.0:
                continue
            pair = 'BTC' + self.pair_delimiter + asset
            # If we have the same pair that we want to buy, lets not sell it
            if pair == pair_to_hold:
                continue
            ticker = self.ticker_df.loc[self.ticker_df['pair'] == pair]
            if ticker.empty:
                print('No currency data for pair: ' + pair + ', skipping')
                continue
            close_price = ticker['close'].iloc[0]
            fee = self.transaction_fee * float(amount) / 100.0
            print('txn fee:', fee, ', balance before: ', amount, ', after: ',
                  amount - fee)
            print(
                colored(
                    'Sold: ' + str(amount) + ', pair: ' + pair + ', price: ' +
                    str(close_price), 'yellow'))
            amount -= fee
            earned_balance = close_price * amount
            root_symbol = 'BTC'
            currency = wallet[root_symbol]
            # Store trade history
            trades.loc[len(trades)] = [
                ticker['date'].iloc[0], pair, close_price, 'sell'
            ]
            wallet[root_symbol] = currency + earned_balance
            wallet[asset] = 0.0

    def trade(self, actions, wallet, trades, force_sell=True):
        """
        force_sell: Sells ALL assets before buying new one
        Simulate currency buy/sell (places fictive buy/sell orders).
        Returns remaining / not - processed actions
        """
        self.balance = wallet
        if self.ticker_df.empty:
            print('Can not trade with empty dataframe, skipping trade')
            return actions

        for action in actions:
            # If action is None, just skip it
            if action.action == TradeState.none:
                actions.remove(action)
                continue

            # If we are forcing_sell, we will first sell all our assets
            if force_sell:
                self.sell_all_assets(trades, wallet, action.pair)

            # Get pairs current closing price
            (currency_symbol,
             asset_symbol) = tuple(re.split('[-_]', action.pair))
            ticker = self.ticker_df.loc[self.ticker_df['pair'] == action.pair]

            if len(ticker.index) == 0:
                print('Could not find pairs ticker, skipping trade')
                continue

            close_price = action.rate

            currency_balance = asset_balance = 0.0
            if currency_symbol in wallet:
                currency_balance = wallet[currency_symbol]
            if asset_symbol in wallet:
                asset_balance = wallet[asset_symbol]

            if action.buy_sell_mode == BuySellMode.all:
                action.amount = self.get_buy_sell_all_amount(wallet, action)
            elif action.buy_sell_mode == BuySellMode.fixed:
                action.amount = self.get_fixed_trade_amount(wallet, action)

            fee = self.transaction_fee * float(action.amount) / 100.0
            # *** Buy ***
            if action.action == TradeState.buy:
                if currency_balance <= 0:
                    print('Want to buy ' + action.pair +
                          ', not enough money, or everything already bought..')
                    actions.remove(action)
                    continue
                print(
                    colored(
                        'Bought: ' + str(action.amount) + ', pair: ' +
                        action.pair + ', price: ' + str(close_price), 'green'))
                wallet[asset_symbol] = asset_balance + action.amount - fee
                wallet[currency_symbol] = currency_balance - (action.amount *
                                                              action.rate)
                # Append trade
                trades.loc[len(trades)] = [
                    ticker['date'].iloc[0], action.pair, close_price, 'buy'
                ]
                actions.remove(action)
                continue

            # *** Sell ***
            elif action.action == TradeState.sell:
                if asset_balance <= 0:
                    print('Want to sell ' + action.pair +
                          ', not enough assets, or everything already sold..')
                    actions.remove(action)
                    continue
                print(
                    colored(
                        'Sold: ' + str(action.amount) + '' + action.pair +
                        ', price: ' + str(close_price), 'yellow'))
                wallet[currency_symbol] = currency_balance + (
                    (action.amount - fee) * action.rate)
                wallet[asset_symbol] = asset_balance - action.amount
                # Append trade
                trades.loc[len(trades)] = [
                    ticker['date'].iloc[0], action.pair, close_price, 'sell'
                ]
                actions.remove(action)
                continue
        self.balance = wallet
        return actions

    def get_buy_sell_all_amount(self, wallet, action):
        """
        Calculates total amount for ALL assets in wallet
        """
        if action.action == TradeState.none:
            return 0.0

        if action.rate == 0.0:
            print(
                colored(
                    'Got zero rate!. Can not calc. buy_sell_amount for pair: '
                    + action.pair, 'red'))
            return 0.0

        (symbol_1, symbol_2) = tuple(action.pair.split(self.pair_delimiter))
        amount = 0.0
        if action.action == TradeState.buy and symbol_1 in wallet:
            assets = wallet.get(symbol_1)
            amount = assets / action.rate
        elif action.action == TradeState.sell and symbol_2 in wallet:
            assets = wallet.get(symbol_2)
            amount = assets

        if amount <= 0.0:
            return 0.0
        return amount

    def get_fixed_trade_amount(self, wallet, action):
        """
        Calculates fixed trade amount given action
        """
        if action.action == TradeState.none:
            return 0.0

        if action.rate == 0.0:
            print(
                colored(
                    'Got zero rate!. Can not calc. buy_sell_amount for pair: '
                    + action.pair, 'red'))
            return 0.0

        (symbol_1, symbol_2) = tuple(action.pair.split(self.pair_delimiter))
        amount = 0.0
        if action.action == TradeState.buy and symbol_1 in wallet:
            assets = self.fixed_trade_amount
            amount = assets / action.rate
        elif action.action == TradeState.sell and symbol_2 in wallet:
            assets = wallet.get(symbol_2)
            amount = assets

        if amount <= 0.0:
            return 0.0
        return amount
예제 #2
0
class Blueprint:
    """
    Main module for generating and handling datasets for AI. Application will generate datasets including
    future target/output parameters.
    """
    arg_parser = configargparse.get_argument_parser()
    arg_parser.add('--days', help='Days to start blueprint from', default=30)
    arg_parser.add(
        '-f',
        '--features',
        help='Blueprints module name to be used to generated features',
        required=True)
    arg_parser.add('--ticker_size',
                   help='Size of the candle ticker (minutes)',
                   default=5)
    arg_parser.add('--pairs', help='Pairs to blueprint')
    arg_parser.add('-v', '--verbosity', help='Verbosity', action='store_true')
    arg_parser.add("--buffer_size",
                   help="Maximum Buffer size (days)",
                   default=30)
    arg_parser.add("--output_dir", help="Output directory")

    features_list = None
    exchange = None
    blueprint = None
    out_dir = 'out/blueprints/'

    def __init__(self):
        args = self.arg_parser.parse_known_args()[0]
        self.blueprint_days = args.days
        self.ticker_size = int(args.ticker_size)
        self.blueprint_end_time = int(time.time())
        self.start_time = self.blueprint_end_time - int(
            self.blueprint_days) * 86400
        self.ticker_epoch = self.start_time
        self.exchange = Exchange(None)
        self.pairs = common.parse_pairs(self.exchange, args.pairs)
        blueprints_module = common.load_module('ai.blueprints.', args.features)
        self.blueprint = blueprints_module(self.pairs)
        self.max_buffer_size = int(
            int(args.buffer_size) * (1440 / self.ticker_size) *
            len(self.pairs))
        self.df_buffer = pd.DataFrame()
        self.df_blueprint = pd.DataFrame()
        self.output_dir = args.output_dir
        self.export_file_name = self.get_output_file_path(
            self.output_dir, self.blueprint.name)
        self.export_file_initialized = False

        # Crete output dir
        if not os.path.exists(self.out_dir):
            os.makedirs(self.out_dir)

    @staticmethod
    def get_output_file_path(dir_path, blueprint_name):
        filename = 'blueprint_' + blueprint_name + '_' + str(int(
            time.time())) + '.csv'
        if dir_path:
            if not dir_path.endswith(os.path.sep):
                dir_path += os.path.sep
            filename = dir_path + filename
        return filename

    def print_progress_dot(self, counter):
        """
        Prints progress
        """
        if counter % 100 == 0:
            print('.', end='', flush=True)
        if counter > 101:
            counter = 0
            self.write_to_file()
        return counter + 1

    def write_to_file(self):
        """
        Writes df to file
        """
        if self.df_blueprint.empty:
            print('Blueprint is empty, nothing to write to file.')
            return

        export_df = self.df_blueprint.copy()
        dropping_columns = ['_id', 'id', 'curr_1', 'curr_2', 'exchange']
        df_columns = self.blueprint.get_feature_names()
        df_columns = [x for x in df_columns if x not in dropping_columns]
        export_df = export_df.drop(dropping_columns, axis=1)
        export_df = export_df[df_columns]
        dt = export_df.tail(1).date.iloc[0]
        dt_string = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(dt))
        print('saving,..(last df date: ' + dt_string + ')')
        if not self.export_file_initialized:
            export_df.to_csv(self.out_dir + self.export_file_name,
                             index=False,
                             columns=df_columns)
            self.export_file_initialized = True
        else:
            export_df.to_csv(self.out_dir + self.export_file_name,
                             mode='a',
                             header=False,
                             index=False,
                             columns=df_columns)

        self.df_blueprint = self.df_blueprint[0:0]

    def run(self):
        """
        Calculates and stores dataset
        """
        info_text = 'Starting generating data for Blueprint ' + self.blueprint.name + ' :back-days ' + \
                    self.blueprint_days + ' (This might take several hours/days,.so please stay back and relax)'
        print(colored(info_text, 'yellow'))
        dot_counter = 0
        while True:
            # Get new dataset
            df = self.exchange.get_offline_ticker(self.ticker_epoch,
                                                  self.pairs)

            # Check if the simulation is finished
            if self.ticker_epoch >= self.blueprint_end_time:
                self.write_to_file()
                return

            # Store df to buffer
            if not self.df_buffer.empty:
                df = df[list(self.df_buffer)]
                self.df_buffer = self.df_buffer.append(df, ignore_index=True)
            else:
                self.df_buffer = self.df_buffer.append(df, ignore_index=True)
            self.df_buffer = common.handle_buffer_limits(
                self.df_buffer, self.max_buffer_size)

            scan_df = self.blueprint.scan(self.df_buffer, self.ticker_size)
            if not scan_df.empty:
                dot_counter = self.print_progress_dot(dot_counter)
                self.df_blueprint = self.df_blueprint.append(scan_df,
                                                             ignore_index=True)

            self.ticker_epoch += self.ticker_size * 60