diff --git a/main.py b/main.py index cc6cb10cb..3b5e1a94e 100755 --- a/main.py +++ b/main.py @@ -24,7 +24,6 @@ __license__ = "custom" __version__ = "0.5.1" - conf = get_conf() api_wrapper = get_exchange_api(conf) @@ -79,33 +78,35 @@ class TradeThread(threading.Thread): :return: None """ # Query trades from persistence layer - trade = Trade.query.filter(Trade.is_open.is_(True)).first() - if not trade: + trades = Trade.query.filter(Trade.is_open.is_(True)).all() + if len(trades) < conf['max_open_trades']: # Create entity and execute trade - Session.add(create_trade(float(conf['stake_amount']), api_wrapper.exchange)) - return + try: + Session.add(create_trade(float(conf['stake_amount']), api_wrapper.exchange)) + except ValueError: + logger.exception('ValueError during trade creation') + for trade in trades: + # Check if there is already an open order for this trade + orders = api_wrapper.get_open_orders(trade.pair) + orders = [o for o in orders if o['id'] == trade.open_order_id] + if orders: + msg = 'There exists an open order for this trade: (total: {}, remaining: {}, type: {}, id: {})' \ + .format(round(orders[0]['amount'], 8), + round(orders[0]['remaining'], 8), + orders[0]['type'], + orders[0]['id']) + logger.info(msg) + continue - # Check if there is already an open order for this trade - orders = api_wrapper.get_open_orders(trade.pair) - orders = [o for o in orders if o['id'] == trade.open_order_id] - if orders: - msg = 'There exists an open order for this trade: (total: {}, remaining: {}, type: {}, id: {})' \ - .format(round(orders[0]['amount'], 8), - round(orders[0]['remaining'], 8), - orders[0]['type'], - orders[0]['id']) - logger.info(msg) - return + # Update state + trade.open_order_id = None + # Check if this trade can be marked as closed + if close_trade_if_fulfilled(trade): + logger.info('No open orders found and trade is fulfilled. Marking as closed ...') + continue - # Update state - trade.open_order_id = None - # Check if this trade can be marked as closed - if close_trade_if_fulfilled(trade): - logger.info('No open orders found and trade is fulfilled. Marking as closed ...') - return - - # Check if we can sell our current pair - handle_trade(trade) + # Check if we can sell our current pair + handle_trade(trade) # Initial stopped TradeThread instance _instance = TradeThread() @@ -178,16 +179,21 @@ def create_trade(stake_amount: float, exchange): :param stake_amount: amount of btc to spend :param exchange: exchange to use """ + logger.info('Creating new trade with stake_amount: {} ...'.format(stake_amount)) whitelist = conf[exchange.name.lower()]['pair_whitelist'] # Check if btc_amount is fulfilled if api_wrapper.get_balance('BTC') < stake_amount: raise ValueError('BTC amount is not fulfilled') - # Remove latest trade pair from whitelist - latest_trade = Trade.query.order_by(Trade.id.desc()).first() + # Remove currently opened and latest pairs from whitelist + latest_trade = Trade.query.filter(Trade.is_open.is_(False)).order_by(Trade.id.desc()).first() + open_trades = Trade.query.filter(Trade.is_open.is_(True)).all() if latest_trade and latest_trade.pair in whitelist: whitelist.remove(latest_trade.pair) logger.debug('Ignoring {} in pair whitelist'.format(latest_trade.pair)) + for trade in open_trades: + whitelist.remove(trade.pair) + logger.debug('Ignoring {} in pair whitelist'.format(trade.pair)) if not whitelist: raise ValueError('No pair in whitelist') diff --git a/rpc/telegram.py b/rpc/telegram.py index c5562d936..abd6310bc 100644 --- a/rpc/telegram.py +++ b/rpc/telegram.py @@ -54,19 +54,21 @@ class TelegramHandler(object): :return: None """ # Fetch open trade - trade = Trade.query.filter(Trade.is_open.is_(True)).first() + trades = Trade.query.filter(Trade.is_open.is_(True)).all() from main import get_instance if not get_instance().is_alive(): - message = '*Status:* `trader stopped`' - elif not trade: - message = '*Status:* `no active order`' + TelegramHandler.send_msg('*Status:* `trader stopped`', bot=bot) + elif not trades: + TelegramHandler.send_msg('*Status:* `no active order`', bot=bot) else: - # calculate profit and send message to user - current_rate = api_wrapper.get_ticker(trade.pair)['bid'] - current_profit = 100 * ((current_rate - trade.open_rate) / trade.open_rate) - open_orders = api_wrapper.get_open_orders(trade.pair) - order = open_orders[0] if open_orders else None - message = """ + for trade in trades: + # calculate profit and send message to user + current_rate = api_wrapper.get_ticker(trade.pair)['bid'] + current_profit = 100 * ((current_rate - trade.open_rate) / trade.open_rate) + orders = api_wrapper.get_open_orders(trade.pair) + orders = [o for o in orders if o['id'] == trade.open_order_id] + order = orders[0] if orders else None + message = """ *Current Pair:* [{pair}](https://bittrex.com/Market/Index?MarketName={pair}) *Open Since:* `{date}` *Amount:* `{amount}` @@ -76,18 +78,18 @@ class TelegramHandler(object): *Close Profit:* `{close_profit}` *Current Profit:* `{current_profit}%` *Open Order:* `{open_order}` - """.format( - pair=trade.pair.replace('_', '-'), - date=arrow.get(trade.open_date).humanize(), - open_rate=trade.open_rate, - close_rate=trade.close_rate, - current_rate=current_rate, - amount=round(trade.amount, 8), - close_profit='{}%'.format(round(trade.close_profit, 2)) if trade.close_profit else None, - current_profit=round(current_profit, 2), - open_order='{} ({})'.format(order['remaining'], order['type']) if order else None, - ) - TelegramHandler.send_msg(message, bot=bot) + """.format( + pair=trade.pair.replace('_', '-'), + date=arrow.get(trade.open_date).humanize(), + open_rate=trade.open_rate, + close_rate=trade.close_rate, + current_rate=current_rate, + amount=round(trade.amount, 8), + close_profit='{}%'.format(round(trade.close_profit, 2)) if trade.close_profit else None, + current_profit=round(current_profit, 2), + open_order='{} ({})'.format(order['remaining'], order['type']) if order else None, + ) + TelegramHandler.send_msg(message, bot=bot) @staticmethod @authorized_only @@ -167,6 +169,10 @@ class TelegramHandler(object): :param update: message update :return: None """ + # TODO: make compatible with max_open_orders + TelegramHandler.send_msg('`Currently not implemented`') + return + trade = Trade.query.filter(Trade.is_open.is_(True)).first() if not trade: TelegramHandler.send_msg('`There is no open trade`') diff --git a/utils.py b/utils.py index 684d16aea..5032d417c 100644 --- a/utils.py +++ b/utils.py @@ -28,6 +28,8 @@ def validate_conf(conf): :param conf: config as dict :return: None, raises ValueError if something is wrong """ + if not isinstance(conf.get('max_open_trades'), int): + raise ValueError('max_open_trades must be a int') if not isinstance(conf.get('stake_amount'), float): raise ValueError('stake_amount must be a float') if not isinstance(conf.get('dry_run'), bool):