Ejemplo n.º 1
0
class Base(ABC):
    """
    Base class for data back-filling
    """
    arg_parser = configargparse.get_argument_parser()
    arg_parser.add('-c',
                   '--config',
                   is_config_file=True,
                   help='config file path',
                   default='mosquito.ini')
    arg_parser.add(
        '--pairs',
        help=
        'Pairs to backfill. For ex. [BTC_ETH, BTC_* (to get all BTC_* prefixed pairs]'
    )
    arg_parser.add("--all",
                   help='Backfill data for ALL currencies',
                   action='store_true')
    arg_parser.add("--days",
                   help="Number of days to backfill",
                   required=True,
                   type=int,
                   default=1)
    arg_parser.add('-v', '--verbosity', help='Verbosity', action='store_true')
    logging.config.fileConfig('logging.ini')

    def __init__(self):
        super(Base, self).__init__()
        args = self.arg_parser.parse_known_args()[0]
        self.exchange = Exchange()
        self.exchange_name = self.exchange.get_exchange_name()
        self.db = self.initialize_db(args)

    @staticmethod
    def initialize_db(args):
        """
        DB Initializer
        """
        db = args.db
        port = int(args.db_port)
        url = args.db_url
        # Init DB
        client = MongoClient(url, port)
        return client[db]

    def get_backfill_pairs(self, backfill_all_pairs=False, pairs_list=None):
        """
        Returns list of exchange pairs that were ordered to backfill
        """
        all_pairs = self.exchange.get_pairs()
        if backfill_all_pairs:
            return all_pairs
        elif pairs_list is not None:
            # tmp_pairs = [pairs_list] # OLD
            tmp_pairs = pairs_list.replace(" ", "").split(',')  # MODIFIED
            pairs = []
            # Handle * suffix pairs
            for pair in tmp_pairs:
                if '*' in pair:
                    prefix = pair.replace('*', '')
                    pairs_list = [p for p in all_pairs if prefix in p]
                    pairs.extend(pairs_list)
                    # remove duplicates
                    pairs = list(set(pairs))
                else:
                    pairs.append(pair)
            return pairs

    @abstractmethod
    def run(self):
        """
        Backfill/fetch data
        """
        pass
Ejemplo n.º 2
0
class Base(ABC):
    """
    Base class for all simulation types (sim, paper, trade)
    """
    ticker_df = pd.DataFrame()
    pairs = []
    exchange = None
    balance = None

    def __init__(self, args, config_file, trade_mode):
        super(Base, self).__init__()
        self.args = args
        self.config = self.initialize_config(config_file)
        self.exchange = Exchange(args, config_file, trade_mode)
        self.transaction_fee = self.exchange.get_transaction_fee()
        self.ticker_df = pd.DataFrame()
        self.verbosity = int(self.config['General']['verbosity'])
        self.pairs = self.process_input_pairs(self.config['Trade']['pairs'])
        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 process_input_pairs(self, in_pairs):
        all_pairs = self.exchange.get_pairs()
        if in_pairs == 'all':
            print('setting_all_pairs')
            return all_pairs
        else:
            pairs = []
            parsed_pairs = in_pairs.replace(" ", "").split(',')
            for in_pair in parsed_pairs:
                if '*' in in_pair:
                    prefix = in_pair.replace('*', '')
                    pairs_list = [p for p in all_pairs if prefix in p]
                    pairs.extend(pairs_list)
                    # remove duplicates
                    pairs = list(set(pairs))
                else:
                    pairs.append(in_pair)
            return pairs

    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
        for pair in self.pairs:
            args = Namespace(pair=pair, days=prefetch_days, all=False)
            backfill(args)
        # 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

    @staticmethod
    def initialize_config(config_file):
        config = configparser.ConfigParser()
        config.read(config_file)
        return config

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

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

    def get_balance(self):
        """
        Returns current balance
        """
        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 == ts.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]
            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_all:
                action.amount = self.get_buy_sell_all_amount(wallet, action)

            fee = self.transaction_fee * float(action.amount) / 100.0
            # *** Buy ***
            if action.action == ts.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 == ts.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
Ejemplo n.º 3
0
class WalletLense:
    """
    Lense: Returns actual wallet statistics with simple daily digest (winners / losers)
    """
    arg_parser = configargparse.get_argument_parser()
    analysis_days = 1
    time_intervals_hours = [1, 3, 6, 12, 24]

    def __init__(self):
        self.args = self.arg_parser.parse_known_args()[0]
        print(colored('Starting lense on exchange: ' + self.args.exchange, 'yellow'))
        self.exchange = Exchange()
        self.postman = Postman()

    def get_stats(self):
        """
        Returns current statistics (market and wallet)
        """
        self.fetch_last_ticker(self.analysis_days)
        df_candles = self.get_ticker(self.exchange.get_pairs(), self.analysis_days)
        df_candles.to_csv('test_ticker.csv', index=False)
        # df_candles = pd.read_csv('test_ticker.csv')

        # Parse all df's to html
        html_body = ['<html><body>']
        html_body.append('<img src="https://user-images.githubusercontent.com/1301154/33856783-88f8e426-dec9-11e7-8371-ead4ef95006a.png" alt="Mosquito" width="330" height="258">')
        winners, losers = self.get_winners_losers(df_candles)
        html_body.append(self.parse_winners_losers_to_html(winners, losers))

        # wallet_stats = self.get_wallet_stats(ticker)
        print('wallet stats:')
        html_body.append('</body></html>')
        self.send_email(html_body)

    @staticmethod
    def df_to_html(df, header, bar_color='lightblue'):
        """
        Converts DataFrame to html text
        """
        df_header = '<h3>' + header + '</h1>'
        table = (df.style.set_properties(**{'font-size': '9pt', 'font-family': 'Calibri'})
                         .set_precision(3)
                         # .background_gradient(subset=['price_change'], cmap='PuBu', low=-100.0, high=100.0)
                         .set_table_styles([{'selector': '.row_heading, .blank', 'props': [('display', 'none;')]}])
                         .bar(subset=['price_change'], color=bar_color)
                         .render()
        )
        return df_header + table

    def send_email(self, body_list):
        """
        Sending email module
        """

        body = ''
        for body_item in body_list:
            body += '\n' + body_item

        self.postman.send_mail('mosquito_stats', body)

    def get_wallet_stats(self, ticker):
        """
        Returns simple wallet stats
        """
        wallet = self.exchange.get_balances()
        return wallet

    def parse_winners_losers_to_html(self, winners, losers):
        """
        Converts Winners and Losers df's to html
        """
        html = '<h2> Winners </h2>'
        grouped_winners = winners.groupby(['hour_spam'])
        for key, df in grouped_winners:
            df = df.drop(['hour_spam'], axis=1)
            # Reorder columns
            df = df[['price_change', 'pair', 'V', 'Vq']]
            html += self.df_to_html(df, str(key) + '-Hour', bar_color='lightgreen')

        html += '<hr>'

        html += '<h2> Losers </h2>'
        grouped_losers = losers.groupby(['hour_spam'])
        for key, df in grouped_losers:
            df = df.drop(['hour_spam'], axis=1)
            df = df.sort_values(['price_change'], ascending=[1])
            df['price_change'] = df['price_change'] * -1.0
            # Reorder columns
            df = df[['price_change', 'pair', 'V', 'Vq']]
            html += self.df_to_html(df, str(key) + '-Hour', bar_color='lightpink')

        return html

    def get_ticker(self, pairs, history_days):
        """
        Gets ticker for given list of pairs and given perion
        """
        print('Getting tickers.. (might take a while)')
        ticker_to = int(time.time())
        ticker_from = ticker_to - (24 * history_days * 3600)
        df_pairs_ticker = pd.DataFrame()
        for pair in pairs:
            ticker_list = self.exchange.get_candles(pair, ticker_from, ticker_to, period=1800)
            df_ticker = pd.DataFrame(ticker_list)
            df_ticker['pair'] = pair
            df_pairs_ticker = df_pairs_ticker.append(df_ticker, ignore_index=True)
        return df_pairs_ticker

    def get_winners_losers(self, df_ticker):
        """
        Get winners/losers
        """
        grouped = df_ticker.groupby(['pair'])
        df_stats = pd.DataFrame()
        for name, df_group in grouped:
            pair_stat = self.get_pair_stats(name, df_group, self.time_intervals_hours)
            df_s = pd.DataFrame(pair_stat)
            df_stats = df_stats.append(df_s, ignore_index=True)
        grouped_stats = df_stats.groupby(['hour_spam'])
        winners = pd.DataFrame()
        losers = pd.DataFrame()
        for interval, df_group in grouped_stats:
            sorted_intervals = df_group.sort_values('price_change', ascending=False)
            winners = winners.append(sorted_intervals.head(5))
            losers = losers.append(sorted_intervals.tail(5))
        return winners, losers

    def get_pair_stats(self, pair, df, hour_intervals):
        """
        Returns statistics summary
        """
        df_now = df.tail(1)
        date_end = df_now['date'].iloc[0]
        dates = df['date']
        stats = []
        for hour_interval in hour_intervals:
            next_epoch = hour_interval * 3600
            closest_date_idx = self.find_nearest(dates, date_end - next_epoch)
            closest_df = df.loc[closest_date_idx]
            df_interval = df.loc[df['date'] == closest_df.date]
            pair_stats = self.calc_pair_stats(df_now.iloc[0], df_interval.iloc[0])
            pair_stats['pair'] = pair
            pair_stats['hour_spam'] = hour_interval
            stats.append(pair_stats)
        return stats

    @staticmethod
    def calc_pair_stats(ticker_now, ticker_past):
        stats = dict()
        price_change = ((float(ticker_past.close) * 100.0) / float(ticker_now.close)) - 100.0
        volume_perc_change = ((float(ticker_past.volume) * 100.0) / float(ticker_now.volume)) - 100.0
        quote_volume_perc_change = ((float(ticker_past.quoteVolume) * 100.0) / float(ticker_now.quoteVolume)) - 100.0
        stats['price_change'] = price_change
        stats['V'] = volume_perc_change
        stats['Vq'] = quote_volume_perc_change
        return stats

    @staticmethod
    def find_nearest(array, value):
        idx = (np.abs(array - value)).argmin()
        return idx

    def fetch_last_ticker(self, prefetch_days):
        """
        Prefetch data for all pairs for given days
        """
        self.args.days = prefetch_days
        self.args.all = True
        backfill(self.args)