def handle_trade(trade: Trade) -> None: """ Sells the current pair if the threshold is reached and updates the trade record. :return: None """ try: if not trade.is_open: raise ValueError('attempt to handle closed trade: {}'.format(trade)) logger.debug('Handling open trade %s ...', trade) # Get current rate current_rate = exchange.get_ticker(trade.pair)['bid'] current_profit = 100.0 * ((current_rate - trade.open_rate) / trade.open_rate) if 'stoploss' in _CONF and current_profit < float(_CONF['stoploss']) * 100.0: logger.debug('Stop loss hit.') execute_sell(trade, current_rate) return for duration, threshold in sorted(_CONF['minimal_roi'].items()): duration, threshold = float(duration), float(threshold) # Check if time matches and current rate is above threshold time_diff = (datetime.utcnow() - trade.open_date).total_seconds() / 60 if time_diff > duration and current_rate > (1 + threshold) * trade.open_rate: execute_sell(trade, current_rate) return logger.debug('Threshold not reached. (cur_profit: %1.2f%%)', current_profit) except ValueError: logger.exception('Unable to handle open order')
def _profit(bot: Bot, update: Update) -> None: """ Handler for /profit. Returns a cumulative profit statistics. :param bot: telegram bot :param update: message update :return: None """ trades = Trade.query.order_by(Trade.id).all() profit_amounts = [] profits = [] durations = [] for trade in trades: if trade.close_date: durations.append( (trade.close_date - trade.open_date).total_seconds()) if trade.close_profit: profit = trade.close_profit else: # Get current rate current_rate = exchange.get_ticker(trade.pair)['bid'] profit = 100 * ((current_rate - trade.open_rate) / trade.open_rate) profit_amounts.append((profit / 100) * trade.btc_amount) profits.append(profit) best_pair = Trade.session.query(Trade.pair, func.sum(Trade.close_profit).label('profit_sum')) \ .filter(Trade.is_open.is_(False)) \ .group_by(Trade.pair) \ .order_by(text('profit_sum DESC')) \ .first() if not best_pair: send_msg('*Status:* `no closed trade`', bot=bot) return bp_pair, bp_rate = best_pair markdown_msg = """ *ROI:* `{profit_btc:.2f} ({profit:.2f}%)` *Trade Count:* `{trade_count}` *First Trade opened:* `{first_trade_date}` *Latest Trade opened:* `{latest_trade_date}` *Avg. Duration:* `{avg_duration}` *Best Performing:* `{best_pair}: {best_rate:.2f}%` """.format( profit_btc=round(sum(profit_amounts), 8), profit=round(sum(profits), 2), trade_count=len(trades), first_trade_date=arrow.get(trades[0].open_date).humanize(), latest_trade_date=arrow.get(trades[-1].open_date).humanize(), avg_duration=str( timedelta(seconds=sum(durations) / float(len(durations)))).split('.')[0], best_pair=bp_pair, best_rate=round(bp_rate, 2), ) send_msg(markdown_msg, bot=bot)
def create_trade(stake_amount: float, _exchange: exchange.Exchange) -> Optional[Trade]: """ Checks the implemented trading indicator(s) for a randomly picked pair, if one pair triggers the buy_signal a new trade record gets created :param stake_amount: amount of btc to spend :param _exchange: exchange to use """ logger.info('Creating new trade with stake_amount: %f ...', stake_amount) whitelist = _CONF[_exchange.name.lower()]['pair_whitelist'] # Check if btc_amount is fulfilled if exchange.get_balance(_CONF['stake_currency']) < stake_amount: raise ValueError( 'stake amount is not fulfilled (currency={}'.format(_CONF['stake_currency']) ) # Remove currently opened and latest pairs from whitelist trades = Trade.query.filter(Trade.is_open.is_(True)).all() latest_trade = Trade.query.filter(Trade.is_open.is_(False)).order_by(Trade.id.desc()).first() if latest_trade: trades.append(latest_trade) for trade in trades: if trade.pair in whitelist: whitelist.remove(trade.pair) logger.debug('Ignoring %s in pair whitelist', trade.pair) if not whitelist: raise ValueError('No pair in whitelist') # Pick pair based on StochRSI buy signals for _pair in whitelist: if get_buy_signal(_pair): pair = _pair break else: return None open_rate = exchange.get_ticker(pair)['ask'] amount = stake_amount / open_rate order_id = exchange.buy(pair, open_rate, amount) # Create trade entity and return message = '*{}:* Buying [{}]({}) at rate `{:f}`'.format( _exchange.name, pair.replace('_', '/'), exchange.get_pair_detail_url(pair), open_rate ) logger.info(message) telegram.send_msg(message) return Trade(pair=pair, btc_amount=stake_amount, open_rate=open_rate, open_date=datetime.utcnow(), amount=amount, exchange=_exchange, open_order_id=order_id, is_open=True)
def _status(bot: Bot, update: Update) -> None: """ Handler for /status. Returns the current TradeThread status :param bot: telegram bot :param update: message update :return: None """ # Fetch open trade trades = Trade.query.filter(Trade.is_open.is_(True)).all() if get_state() != State.RUNNING: send_msg('*Status:* `trader is not running`', bot=bot) elif not trades: send_msg('*Status:* `no active order`', bot=bot) else: for trade in trades: # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair)['bid'] current_profit = 100 * ( (current_rate - trade.open_rate) / trade.open_rate) orders = exchange.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 fmt_close_profit = '{:.2f}%'.format(round( trade.close_profit, 2)) if trade.close_profit else None message = """ *Trade ID:* `{trade_id}` *Current Pair:* [{pair}]({market_url}) *Open Since:* `{date}` *Amount:* `{amount}` *Open Rate:* `{open_rate}` *Close Rate:* `{close_rate}` *Current Rate:* `{current_rate}` *Close Profit:* `{close_profit}` *Current Profit:* `{current_profit:.2f}%` *Open Order:* `{open_order}` """.format( trade_id=trade.id, pair=trade.pair, market_url=exchange.get_pair_detail_url(trade.pair), 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=fmt_close_profit, current_profit=round(current_profit, 2), open_order='{} ({})'.format(order['remaining'], order['type']) if order else None, ) send_msg(message, bot=bot)
def get_forcesell(self, trade): # Get current rate current_rate = exchange.get_ticker(trade.pair)['bid'] # Get available balance currency = trade.pair.split('_')[1] balance = exchange.get_balance(currency) # Execute sell profit = trade.exec_sell_order(current_rate, balance) message = '*{}:* Selling [{}]({}) at rate `{:f} (profit: {}%)`'.format( trade.exchange.name, trade.pair.replace('_', '/'), exchange.get_pair_detail_url(trade.pair), trade.close_rate, round(profit, 2)) return message
def handle_trade(trade: Trade) -> None: """ Sells the current pair if the threshold is reached and updates the trade record. :return: None """ try: if not trade.is_open: raise ValueError('attempt to handle closed trade: {}'.format(trade)) logger.debug('Handling open trade %s ...', trade) current_rate = exchange.get_ticker(trade.pair)['bid'] if should_sell(trade, current_rate, datetime.utcnow()): execute_sell(trade, current_rate) return except ValueError: logger.exception('Unable to handle open order')
def get_status(self, trades): messages = [] for trade in trades: # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair)['bid'] current_profit = 100 * ( (current_rate - trade.open_rate) / trade.open_rate) orders = exchange.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 fmt_close_profit = '{:.2f}%'.format(round( trade.close_profit, 2)) if trade.close_profit else None markdown_msg = """ *Trade ID:* `{trade_id}` *Current Pair:* [{pair}]({market_url}) *Open Since:* `{date}` *Amount:* `{amount}` *Open Rate:* `{open_rate}` *Close Rate:* `{close_rate}` *Current Rate:* `{current_rate}` *Close Profit:* `{close_profit}` *Current Profit:* `{current_profit:.2f}%` *Open Order:* `{open_order}` """.format( trade_id=trade.id, pair=trade.pair, market_url=exchange.get_pair_detail_url(trade.pair), 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=fmt_close_profit, current_profit=round(current_profit, 2), open_order='{} ({})'.format(order['remaining'], order['type']) if order else None, ) messages.append(markdown_msg) return messages
def _forcesellall(bot: Bot, update: Update) -> None: """ Handler for /forcesellall. Sell all trades at current price :param bot: telegram bot :param update: message update :return: None """ if get_state() != State.RUNNING: send_msg('`trader is not running`', bot=bot) return try: trades = Trade.query.filter(Trade.is_open.is_(True)).all() if len(trades) == 0: send_msg('`open trades not found`', bot=bot) return for trade in trades: # Get current rate current_rate = exchange.get_ticker(trade.pair)['bid'] # Get available balance currency = trade.pair.split('_')[1] balance = exchange.get_balance(currency) # Execute sell profit = trade.exec_sell_order(current_rate, balance) message = '*{}:* Selling [{}]({}) at rate `{:f} (profit: {}%)`'.format( trade.exchange.name, trade.pair.replace('_', '/'), exchange.get_pair_detail_url(trade.pair), trade.close_rate, round(profit, 2)) logger.info(message) send_msg(message) time.sleep(25) except ValueError: send_msg('Failed to sell all trades') logger.warning('/forcesellall: Failed to sell all trades')
def _forcesell(bot: Bot, update: Update) -> None: """ Handler for /forcesell <id>. Sells the given trade at current price :param bot: telegram bot :param update: message update :return: None """ if get_state() != State.RUNNING: send_msg('`trader is not running`', bot=bot) return try: trade_id = int(update.message.text.replace('/forcesell', '').strip()) # Query for trade trade = Trade.query.filter( and_(Trade.id == trade_id, Trade.is_open.is_(True))).first() if not trade: send_msg('There is no open trade with ID: `{}`'.format(trade_id)) return # Get current rate current_rate = exchange.get_ticker(trade.pair)['bid'] # Get available balance currency = trade.pair.split('_')[1] balance = exchange.get_balance(currency) # Execute sell profit = trade.exec_sell_order(current_rate, balance) message = '*{}:* Selling [{}]({}) at rate `{:f} (profit: {}%)`'.format( trade.exchange.name, trade.pair.replace('_', '/'), exchange.get_pair_detail_url(trade.pair), trade.close_rate, round(profit, 2)) logger.info(message) send_msg(message) except ValueError: send_msg('Invalid argument. Usage: `/forcesell <trade_id>`') logger.warning('/forcesell: Invalid argument received')
def get_log(self, trades, balances): profit_amounts = [] profits = [] durations = [] for trade in trades: if trade.close_date: durations.append( (trade.close_date - trade.open_date).total_seconds()) if trade.close_profit: profit = trade.close_profit else: # Get current rate current_rate = exchange.get_ticker(trade.pair)['bid'] profit = 100 * ( (current_rate - trade.open_rate) / trade.open_rate) profit_amounts.append((profit / 100) * trade.stake_amount) profits.append(profit) if len(durations) == 0: durations.append(1.0) best_pair = Trade.session.query(Trade.pair, func.sum(Trade.close_profit).label('profit_sum')) \ .filter(Trade.is_open.is_(False)) \ .group_by(Trade.pair) \ .order_by(text('profit_sum DESC')) \ .first() now = datetime.datetime.now() btc_wallet_balance = 0.0 for balance in balances: if balance['Currency'] == "BTC": btc_wallet_balance = balance['Balance'] # In USD current_btc_price = analyze.get_btc_current_price() usd_wallet_balance = btc_wallet_balance * current_btc_price bp_pair, bp_rate = 1.0, 1.0 if best_pair is not None: bp_pair, bp_rate = best_pair markdown_msg = "{date},{time},{trade_count},{best_pair}: {best_rate:.2f}%,{avg_duration},{profit_btc:.2f} ({profit:.2f}%),{btc_wallet}BTC ({usd_wallet:.2f}USD),{price_btc}".format( date=str(now.month) + "/" + str(now.day) + "/" + str(now.year), time=str(now.hour) + ":" + str(now.minute) + ":" + str(now.second), trade_count=len(trades), best_pair=bp_pair, best_rate=round(bp_rate, 2), avg_duration=str( timedelta(seconds=sum(durations) / float(len(durations)))).split('.')[0], profit_btc=round(sum(profit_amounts), 8), profit=round(sum(profits), 2), btc_wallet=btc_wallet_balance, usd_wallet=usd_wallet_balance, price_btc=current_btc_price, #btc_wallet,usd_wallet and price_btc are not implemented yet ) return markdown_msg