diff --git a/main.py b/main.py index 46547b305..b9eca62e0 100755 --- a/main.py +++ b/main.py @@ -158,20 +158,14 @@ def handle_trade(trade): # 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 and update trade record - order_id = api_wrapper.sell(trade.pair, current_rate, balance) - trade.close_rate = current_rate - trade.close_profit = current_profit - trade.close_date = datetime.utcnow() - trade.open_order_id = order_id - + # Execute sell + profit = trade.exec_sell_order(current_rate, balance) message = '*{}:* Selling [{}]({}) at rate `{:f} (profit: {}%)`'.format( trade.exchange.name, trade.pair.replace('_', '/'), api_wrapper.get_pair_detail_url(trade.pair), trade.close_rate, - round(current_profit, 2) + round(profit, 2) ) logger.info(message) TelegramHandler.send_msg(message) diff --git a/persistence.py b/persistence.py index 12779d94f..c5a1c593d 100644 --- a/persistence.py +++ b/persistence.py @@ -5,10 +5,11 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import scoped_session, sessionmaker from sqlalchemy.types import Enum -from exchange import Exchange +from exchange import Exchange, get_exchange_api from utils import get_conf -if get_conf().get('dry_run', False): +conf = get_conf() +if conf.get('dry_run', False): db_handle = 'sqlite:///tradesv2.dry_run.sqlite' else: db_handle = 'sqlite:///tradesv2.sqlite' @@ -45,4 +46,22 @@ class Trade(Base): 'closed' if not self.is_open else round((datetime.utcnow() - self.open_date).total_seconds() / 60, 2) ) + def exec_sell_order(self, rate, amount): + """ + Executes a sell for the given trade and updated the entity. + :param rate: rate to sell for + :param amount: amount to sell + :return: current profit as percentage + """ + profit = 100 * ((rate - self.open_rate) / self.open_rate) + + # Execute sell and update trade record + order_id = get_exchange_api(conf).sell(self.pair, rate, amount) + self.close_rate = rate + self.close_profit = profit + self.close_date = datetime.utcnow() + self.open_order_id = order_id + Session.flush() + return profit + Base.metadata.create_all(engine) diff --git a/rpc/telegram.py b/rpc/telegram.py index 49ab022ec..7a064f771 100644 --- a/rpc/telegram.py +++ b/rpc/telegram.py @@ -2,6 +2,7 @@ import logging from datetime import timedelta import arrow +from sqlalchemy import and_ from telegram.error import NetworkError from telegram.ext import CommandHandler, Updater from telegram import ParseMode, Bot, Update @@ -57,7 +58,7 @@ class TelegramHandler(object): trades = Trade.query.filter(Trade.is_open.is_(True)).all() from main import get_instance if not get_instance().is_alive(): - TelegramHandler.send_msg('*Status:* `trader stopped`', bot=bot) + TelegramHandler.send_msg('*Status:* `trader is not running`', bot=bot) elif not trades: TelegramHandler.send_msg('*Status:* `no active order`', bot=bot) else: @@ -198,6 +199,54 @@ class TelegramHandler(object): TelegramHandler.send_msg('*Order cancelled:* `{}`'.format(order_id), bot=bot) logger.info('Order cancelled: (order_id: {})'.format(order_id)) + @staticmethod + @authorized_only + def _forcesell(bot, update): + """ + Handler for /forcesell . + Sells the given trade at current price + :param bot: telegram bot + :param update: message update + :return: None + """ + from main import get_instance + if not get_instance().is_alive(): + TelegramHandler.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: + TelegramHandler.send_msg('There is no open trade with ID: `{}`'.format(trade_id)) + return + # Get current rate + current_rate = api_wrapper.get_ticker(trade.pair)['bid'] + # Get available balance + currency = trade.pair.split('_')[1] + balance = api_wrapper.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('_', '/'), + api_wrapper.get_pair_detail_url(trade.pair), + trade.close_rate, + round(profit, 2) + ) + logger.info(message) + TelegramHandler.send_msg(message) + + except ValueError: + TelegramHandler.send_msg('Invalid argument. Usage: `/forcesell `') + logger.warning('/forcesell: Invalid argument received') + @staticmethod @synchronized def get_updater(conf): @@ -224,6 +273,7 @@ class TelegramHandler(object): CommandHandler('start', TelegramHandler._start), CommandHandler('stop', TelegramHandler._stop), CommandHandler('cancel', TelegramHandler._cancel), + CommandHandler('forcesell', TelegramHandler._forcesell), ] for handle in handles: TelegramHandler.get_updater(conf).dispatcher.add_handler(handle)