def test_get_ticker(default_conf, mocker): maybe_init_api(default_conf, mocker) api_mock = MagicMock() tick = { "success": True, 'result': { 'Bid': 0.00001098, 'Ask': 0.00001099, 'Last': 0.0001 } } api_mock.get_ticker = MagicMock(return_value=tick) mocker.patch('freqtrade.exchange.bittrex._API', api_mock) # retrieve original ticker ticker = get_ticker(pair='BTC_ETH') assert ticker['bid'] == 0.00001098 assert ticker['ask'] == 0.00001099 # change the ticker tick = {"success": True, 'result': {"Bid": 0.5, "Ask": 1, "Last": 42}} api_mock.get_ticker = MagicMock(return_value=tick) mocker.patch('freqtrade.exchange.bittrex._API', api_mock) # if not caching the result we should get the same ticker # if not fetching a new result we should get the cached ticker ticker = get_ticker(pair='BTC_ETH', refresh=False) assert ticker['bid'] == 0.00001098 assert ticker['ask'] == 0.00001099 # force ticker refresh ticker = get_ticker(pair='BTC_ETH', refresh=True) assert ticker['bid'] == 0.5 assert ticker['ask'] == 1
def test_get_ticker(mocker, ticker): api_mock = MagicMock() tick = {"success": True, 'result': {'Bid': 0.00001098, 'Ask': 0.00001099, 'Last': 0.0001}} api_mock.get_ticker = MagicMock(return_value=tick) mocker.patch('freqtrade.exchange.bittrex._API', api_mock) # retrieve original ticker ticker = get_ticker(pair='BTC_ETH') assert ticker['bid'] == 0.00001098 assert ticker['ask'] == 0.00001099 # change the ticker tick = {"success": True, 'result': {"Bid": 0.5, "Ask": 1, "Last": 42}} api_mock.get_ticker = MagicMock(return_value=tick) mocker.patch('freqtrade.exchange.bittrex._API', api_mock) # if not caching the result we should get the same ticker ticker = get_ticker(pair='BTC_ETH', refresh=False) assert ticker['bid'] == 0.00001098 assert ticker['ask'] == 0.00001099 # force ticker refresh ticker = get_ticker(pair='BTC_ETH', refresh=True) assert ticker['bid'] == 0.5 assert ticker['ask'] == 1
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 trade_id = update.message.text.replace('/forcesell', '').strip() if trade_id == 'all': # Execute sell for all open orders for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): # Get current rate current_rate = exchange.get_ticker(trade.pair)['bid'] from freqtrade.main import execute_sell execute_sell(trade, current_rate) return # Query for trade trade = Trade.query.filter( and_(Trade.id == trade_id, Trade.is_open.is_(True))).first() if not trade: send_msg('Invalid argument. See `/help` to view usage') logger.warning('/forcesell: Invalid argument received') return # Get current rate current_rate = exchange.get_ticker(trade.pair)['bid'] from freqtrade.main import execute_sell execute_sell(trade, current_rate)
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 """ # Check if additional parameters are passed params = update.message.text.replace('/status', '').split(' ') \ if update.message.text else [] if 'table' in params: _status_table(bot, update) return # 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 trade`', bot=bot) else: for trade in trades: order = None if trade.open_order_id: order = exchange.get_order(trade.open_order_id) # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair)['bid'] current_profit = trade.calc_profit(current_rate) fmt_close_profit = '{:.2f}%'.format( round(trade.close_profit * 100, 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:.8f}` *Close Rate:* `{close_rate}` *Current Rate:* `{current_rate:.8f}` *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 * 100, 2), open_order='{} ({})'.format( order['remaining'], order['type'] ) if order else None, ) send_msg(message, bot=bot)
def _status_table(bot: Bot, update: Update) -> None: """ Handler for /status table. Returns the current TradeThread status in table format :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: trades_list = [] for trade in trades: # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair)['bid'] trades_list.append([ trade.id, trade.pair, shorten_date( arrow.get(trade.open_date).humanize(only_distance=True)), '{:.2f}'.format(100 * trade.calc_profit(current_rate)) ]) columns = ['ID', 'Pair', 'Since', 'Profit'] df_statuses = DataFrame.from_records(trades_list, columns=columns) df_statuses = df_statuses.set_index(columns[0]) message = tabulate(df_statuses, headers='keys', tablefmt='simple') message = "<pre>{}</pre>".format(message) send_msg(message, parse_mode=ParseMode.HTML)
def handle_trade(self, trade: Trade) -> bool: """ Sells the current pair if the threshold is reached and updates the trade record. :return: True if trade has been sold, False otherwise """ if not trade.is_open: raise ValueError( 'attempt to handle closed trade: {}'.format(trade)) logger.debug('Handling %s ...', trade) current_rate = exchange.get_ticker(trade.pair)['bid'] (buy, sell) = (False, False) if self.config.get('experimental', {}).get('use_sell_signal'): (buy, sell) = self.analyze.get_signal( trade.pair, self.analyze.get_ticker_interval()) if self.analyze.should_sell(trade, current_rate, datetime.utcnow(), buy, sell): self.execute_sell(trade, current_rate) return True logger.info( 'Found no sell signals for whitelisted currencies. Trying again..') return False
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 not trade.open_rate: continue 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 = trade.calc_profit(current_rate) profit_amounts.append(profit * trade.stake_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:.8f} ({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) * 100, 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 * 100, 2), ) send_msg(markdown_msg, bot=bot)
def handle_trade(trade: Trade) -> bool: """ Sells the current pair if the threshold is reached and updates the trade record. :return: True if trade has been sold, False otherwise """ if not trade.is_open: raise ValueError('attempt to handle closed trade: {}'.format(trade)) logger.debug('Handling %s ...', trade) current_rate = exchange.get_ticker(trade.pair)['bid'] # Check if minimal roi has been reached if min_roi_reached(trade, current_rate, datetime.utcnow()): logger.debug('Executing sell due to ROI ...') execute_sell(trade, current_rate) return True # Experimental: Check if sell signal has been enabled and triggered if _CONF.get('experimental', {}).get('use_sell_signal'): # Experimental: Check if the trade is profitable before selling it (avoid selling at loss) if _CONF.get('experimental', {}).get('sell_profit_only'): logger.debug('Checking if trade is profitable ...') if trade.calc_profit(rate=current_rate) <= 0: return False logger.debug('Checking sell_signal ...') if get_signal(trade.pair, SignalType.SELL): logger.debug('Executing sell due to sell signal ...') execute_sell(trade, current_rate) return True return False
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 """ # Check if additional parameters are passed params = update.message.text.replace('/status', '').split(' ') \ if update.message.text else [] if 'table' in params: _status_table(bot, update) return # 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 trade`', bot=bot) else: for trade in trades: order = None if trade.open_order_id: order = exchange.get_order(trade.open_order_id) # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair, False)['bid'] current_profit = trade.calc_profit_percent(current_rate) fmt_close_profit = '{:.2f}%'.format( round(trade.close_profit * 100, 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:.8f}` *Close Rate:* `{close_rate}` *Current Rate:* `{current_rate:.8f}` *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 * 100, 2), open_order='({} rem={:.8f})'.format( order['type'], order['remaining'] ) if order else None, ) send_msg(message, bot=bot)
def _status_table(bot: Bot, update: Update) -> None: """ Handler for /status table. Returns the current TradeThread status in table format :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: trades_list = [] for trade in trades: # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair, False)['bid'] trades_list.append([ trade.id, trade.pair, shorten_date(arrow.get(trade.open_date).humanize(only_distance=True)), '{:.2f}%'.format(100 * trade.calc_profit_percent(current_rate)) ]) columns = ['ID', 'Pair', 'Since', 'Profit'] df_statuses = DataFrame.from_records(trades_list, columns=columns) df_statuses = df_statuses.set_index(columns[0]) message = tabulate(df_statuses, headers='keys', tablefmt='simple') message = "<pre>{}</pre>".format(message) send_msg(message, parse_mode=ParseMode.HTML)
def rpc_status_table(self) -> Tuple[bool, Any]: trades = Trade.query.filter(Trade.is_open.is_(True)).all() if self.freqtrade.state != State.RUNNING: return True, '*Status:* `trader is not running`' elif not trades: return True, '*Status:* `no active order`' else: trades_list = [] for trade in trades: # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair, False)['bid'] trades_list.append([ trade.id, trade.pair, shorten_date( arrow.get( trade.open_date).humanize(only_distance=True)), '{:.2f}%'.format(100 * trade.calc_profit_percent(current_rate)) ]) columns = ['ID', 'Pair', 'Since', 'Profit'] df_statuses = DataFrame.from_records(trades_list, columns=columns) df_statuses = df_statuses.set_index(columns[0]) # The style used throughout is to return a tuple # consisting of (error_occured?, result) # Another approach would be to just return the # result, or raise error return False, df_statuses
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 create_trade(stake_amount: float) -> 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 """ logger.info( 'Checking buy signals to create a new trade with stake_amount: %f ...', stake_amount) whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist']) # Check if stake_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 for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): 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 # Calculate amount and subtract fee fee = exchange.get_fee() buy_limit = get_target_bid(exchange.get_ticker(pair)) amount = (1 - fee) * stake_amount / buy_limit order_id = exchange.buy(pair, buy_limit, amount) # Create trade entity and return message = '*{}:* Buying [{}]({}) with limit `{:.8f}`'.format( exchange.get_name().upper(), pair.replace('_', '/'), exchange.get_pair_detail_url(pair), buy_limit) logger.info(message) telegram.send_msg(message) # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL return Trade(pair=pair, stake_amount=stake_amount, amount=amount, fee=fee * 2, open_rate=buy_limit, open_date=datetime.utcnow(), exchange=exchange.get_name().upper(), open_order_id=order_id)
def create_trade(stake_amount: float) -> 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 """ logger.info('Creating new trade with stake_amount: %f ...', stake_amount) whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist']) # Check if stake_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 for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): 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 = get_target_bid(exchange.get_ticker(pair)) 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.EXCHANGE.name.upper(), pair.replace('_', '/'), exchange.get_pair_detail_url(pair), open_rate ) logger.info(message) telegram.send_msg(message) return Trade(pair=pair, stake_amount=stake_amount, open_rate=open_rate, open_date=datetime.utcnow(), amount=amount, exchange=exchange.EXCHANGE.name.upper(), open_order_id=order_id, is_open=True)
def rpc_trade_status(self) -> Tuple[bool, Any]: """ Below follows the RPC backend it is prefixed with rpc_ to raise awareness that it is a remotely exposed function :return: """ # Fetch open trade trades = Trade.query.filter(Trade.is_open.is_(True)).all() if self.freqtrade.state != State.RUNNING: return True, '*Status:* `trader is not running`' elif not trades: return True, '*Status:* `no active trade`' else: result = [] for trade in trades: order = None if trade.open_order_id: order = exchange.get_order(trade.open_order_id) # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair, False)['bid'] current_profit = trade.calc_profit_percent(current_rate) fmt_close_profit = '{:.2f}%'.format( round(trade.close_profit * 100, 2)) if trade.close_profit else None message = "*Trade ID:* `{trade_id}`\n" \ "*Current Pair:* [{pair}]({market_url})\n" \ "*Open Since:* `{date}`\n" \ "*Amount:* `{amount}`\n" \ "*Open Rate:* `{open_rate:.8f}`\n" \ "*Close Rate:* `{close_rate}`\n" \ "*Current Rate:* `{current_rate:.8f}`\n" \ "*Close Profit:* `{close_profit}`\n" \ "*Current Profit:* `{current_profit:.2f}%`\n" \ "*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 * 100, 2), open_order='({} rem={:.8f})'.format( order['type'], order['remaining'] ) if order else None, ) result.append(message) return False, result
def handle_trade(trade: Trade) -> bool: """ Sells the current pair if the threshold is reached and updates the trade record. :return: True if trade has been sold, False otherwise """ if not trade.is_open: raise ValueError('attempt to handle closed trade: {}'.format(trade)) logger.debug('Handling %s ...', trade) current_rate = exchange.get_ticker(trade.pair)['bid'] if should_sell(trade, current_rate, datetime.utcnow()): execute_sell(trade, current_rate) return True return False
def rpc_balance(self, fiat_display_currency: str) -> Tuple[bool, Any]: """ :return: current account balance per crypto """ balances = [ c for c in exchange.get_balances() if c['Balance'] or c['Available'] or c['Pending'] ] if not balances: return True, '`All balances are zero.`' output = [] total = 0.0 for currency in balances: coin = currency['Currency'] if coin == 'BTC': currency["Rate"] = 1.0 else: if coin == 'USDT': currency["Rate"] = 1.0 / exchange.get_ticker( 'USDT_BTC', False)['bid'] else: currency["Rate"] = exchange.get_ticker( 'BTC_' + coin, False)['bid'] currency['BTC'] = currency["Rate"] * currency["Balance"] total = total + currency['BTC'] output.append({ 'currency': currency['Currency'], 'available': currency['Available'], 'balance': currency['Balance'], 'pending': currency['Pending'], 'est_btc': currency['BTC'] }) fiat = self.freqtrade.fiat_converter symbol = fiat_display_currency value = fiat.convert_amount(total, 'BTC', symbol) return False, (output, total, symbol, value)
def handle_trade(trade: Trade) -> bool: """ Sells the current pair if the threshold is reached and updates the trade record. :return: True if trade has been sold, False otherwise """ if not trade.is_open: raise ValueError('attempt to handle closed trade: {}'.format(trade)) logger.debug('Handling %s ...', trade) # first determine if buy order that is still open if trade.open_order_id: order_info = exchange.get_order(trade.open_order_id) order_type = order_info['type'] if order_type == 'LIMIT_BUY': max_open_time = 10 current_time = datetime.utcnow() amount_minutes_open = (current_time - trade.open_date).total_seconds() / 60. if amount_minutes_open > max_open_time: health = exchange.get_wallet_health() if exchange.get_name() == 'HitBTC': token = trade.pair else: token = get_quote_token(trade.pair) token_healthy = False for status in health: if status['Currency'] == token: token_healthy = status['IsActive'] if token_healthy: logger.debug('Cancelling %s ...', trade) exchange.cancel_order(trade.open_order_id) # trade.is_open = 0 # trade.open_order_id = None Trade.session.delete(trade) else: logger.debug( 'Cancelling could not execute due to wallet heath for %s ...', trade) return False current_rate = exchange.get_ticker(trade.pair)['bid'] # ask? if current_rate is None: return False if should_sell(trade, current_rate, datetime.utcnow()): execute_sell(trade, current_rate) return True return False
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 _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, 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 _exec_forcesell(trade: Trade) -> None: # Check if there is there is an open order if trade.open_order_id: order = exchange.get_order(trade.open_order_id) # Cancel open LIMIT_BUY orders and close trade if order and not order['closed'] and order['type'] == 'LIMIT_BUY': exchange.cancel_order(trade.open_order_id) trade.close(order.get('rate') or trade.open_rate) # TODO: sell amount which has been bought already return # Ignore trades with an attached LIMIT_SELL order if order and not order['closed'] and order['type'] == 'LIMIT_SELL': return # Get current rate and execute sell current_rate = exchange.get_ticker(trade.pair, False)['bid'] from freqtrade.main import execute_sell execute_sell(trade, current_rate)
from freqtrade import exchange from freqtrade.analyze import analyze_ticker # ensure directory exists base_path = os.path.join(os.path.expanduser('~'), 'freqtrade') # get configuration with open(os.path.join(base_path, 'config.json')) as file: _CONF = json.load(file) # initialize the exchange exchange.init(_CONF) # get ticker data = exchange.get_ticker(pair='ETH_BTC') print(data) # get ticker history df = exchange.get_ticker_history(pair='ETH_BTC', tick_interval=1) print(pd.DataFrame(df)) # get markets data = exchange.get_markets() print(data) # get name print(exchange.get_name()) print(exchange.get_sleep_time())
def rpc_trade_statistics(self, stake_currency: str, fiat_display_currency: str) -> Tuple[bool, Any]: """ :return: cumulative profit statistics. """ trades = Trade.query.order_by(Trade.id).all() profit_all_coin = [] profit_all_percent = [] profit_closed_coin = [] profit_closed_percent = [] durations = [] for trade in trades: current_rate = None if not trade.open_rate: continue if trade.close_date: durations.append( (trade.close_date - trade.open_date).total_seconds()) if not trade.is_open: profit_percent = trade.calc_profit_percent() profit_closed_coin.append(trade.calc_profit()) profit_closed_percent.append(profit_percent) else: # Get current rate current_rate = exchange.get_ticker(trade.pair, False)['bid'] profit_percent = trade.calc_profit_percent(rate=current_rate) profit_all_coin.append( trade.calc_profit( rate=Decimal(trade.close_rate or current_rate))) profit_all_percent.append(profit_percent) best_pair = Trade.session.query( Trade.pair, sql.func.sum(Trade.close_profit).label('profit_sum') ).filter(Trade.is_open.is_(False)) \ .group_by(Trade.pair) \ .order_by(sql.text('profit_sum DESC')).first() if not best_pair: return True, '*Status:* `no closed trade`' bp_pair, bp_rate = best_pair # FIX: we want to keep fiatconverter in a state/environment, # doing this will utilize its caching functionallity, instead we reinitialize it here fiat = self.freqtrade.fiat_converter # Prepare data to display profit_closed_coin = round(sum(profit_closed_coin), 8) profit_closed_percent = round(sum(profit_closed_percent) * 100, 2) profit_closed_fiat = fiat.convert_amount(profit_closed_coin, stake_currency, fiat_display_currency) profit_all_coin = round(sum(profit_all_coin), 8) profit_all_percent = round(sum(profit_all_percent) * 100, 2) profit_all_fiat = fiat.convert_amount(profit_all_coin, stake_currency, fiat_display_currency) num = float(len(durations) or 1) return (False, { 'profit_closed_coin': profit_closed_coin, 'profit_closed_percent': profit_closed_percent, 'profit_closed_fiat': profit_closed_fiat, 'profit_all_coin': profit_all_coin, 'profit_all_percent': profit_all_percent, 'profit_all_fiat': profit_all_fiat, '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) / num)).split('.')[0], 'best_pair': bp_pair, 'best_rate': round(bp_rate * 100, 2) })
def execute_sell(self, trade: Trade, limit: float) -> None: """ Executes a limit sell for the given trade and limit :param trade: Trade instance :param limit: limit rate for the sell order :return: None """ # Execute sell and update trade record order_id = exchange.sell(str(trade.pair), limit, trade.amount) trade.open_order_id = order_id fmt_exp_profit = round(trade.calc_profit_percent(rate=limit) * 100, 2) profit_trade = trade.calc_profit(rate=limit) current_rate = exchange.get_ticker(trade.pair, False)['bid'] profit = trade.calc_profit_percent(current_rate) message = "*{exchange}:* Selling\n" \ "*Current Pair:* [{pair}]({pair_url})\n" \ "*Limit:* `{limit}`\n" \ "*Amount:* `{amount}`\n" \ "*Open Rate:* `{open_rate:.8f}`\n" \ "*Current Rate:* `{current_rate:.8f}`\n" \ "*Profit:* `{profit:.2f}%`" \ "".format( exchange=trade.exchange, pair=trade.pair, pair_url=exchange.get_pair_detail_url(trade.pair), limit=limit, open_rate=trade.open_rate, current_rate=current_rate, amount=round(trade.amount, 8), profit=round(profit * 100, 2), ) # For regular case, when the configuration exists if 'stake_currency' in self.config and 'fiat_display_currency' in self.config: fiat_converter = CryptoToFiatConverter() profit_fiat = fiat_converter.convert_amount( profit_trade, self.config['stake_currency'], self.config['fiat_display_currency']) message += '` ({gain}: {profit_percent:.2f}%, {profit_coin:.8f} {coin}`' \ '` / {profit_fiat:.3f} {fiat})`' \ ''.format( gain="profit" if fmt_exp_profit > 0 else "loss", profit_percent=fmt_exp_profit, profit_coin=profit_trade, coin=self.config['stake_currency'], profit_fiat=profit_fiat, fiat=self.config['fiat_display_currency'], ) # Because telegram._forcesell does not have the configuration # Ignore the FIAT value and does not show the stake_currency as well else: message += '` ({gain}: {profit_percent:.2f}%, {profit_coin:.8f})`'.format( gain="profit" if fmt_exp_profit > 0 else "loss", profit_percent=fmt_exp_profit, profit_coin=profit_trade) # Send the message self.rpc.send_msg(message) Trade.session.flush()
def create_trade(self) -> bool: """ 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 interval: Ticker interval used for Analyze :return: True if a trade object has been created and persisted, False otherwise """ stake_amount = self.config['stake_amount'] interval = self.analyze.get_ticker_interval() logger.info( 'Checking buy signals to create a new trade with stake_amount: %f ...', stake_amount) whitelist = copy.deepcopy(self.config['exchange']['pair_whitelist']) # Check if stake_amount is fulfilled if exchange.get_balance(self.config['stake_currency']) < stake_amount: raise DependencyException( 'stake amount is not fulfilled (currency={})'.format( self.config['stake_currency'])) # Remove currently opened and latest pairs from whitelist for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): if trade.pair in whitelist: whitelist.remove(trade.pair) logger.debug('Ignoring %s in pair whitelist', trade.pair) if not whitelist: raise DependencyException('No currency pairs in whitelist') # Pick pair based on StochRSI buy signals for _pair in whitelist: (buy, sell) = self.analyze.get_signal(_pair, interval) if buy and not sell: pair = _pair break else: return False # Calculate amount buy_limit = self.get_target_bid(exchange.get_ticker(pair)) amount = stake_amount / buy_limit order_id = exchange.buy(pair, buy_limit, amount) stake_amount_fiat = self.fiat_converter.convert_amount( stake_amount, self.config['stake_currency'], self.config['fiat_display_currency']) # Create trade entity and return self.rpc.send_msg( '*{}:* Buying [{}]({}) with limit `{:.8f} ({:.6f} {}, {:.3f} {})` ' .format(exchange.get_name().upper(), pair.replace('_', '/'), exchange.get_pair_detail_url(pair), buy_limit, stake_amount, self.config['stake_currency'], stake_amount_fiat, self.config['fiat_display_currency'])) # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL trade = Trade(pair=pair, stake_amount=stake_amount, amount=amount, fee=exchange.get_fee(), open_rate=buy_limit, open_date=datetime.utcnow(), exchange=exchange.get_name().upper(), open_order_id=order_id) Trade.session.add(trade) Trade.session.flush() return True
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_all_coin = [] profit_all_percent = [] profit_closed_coin = [] profit_closed_percent = [] durations = [] for trade in trades: current_rate = None if not trade.open_rate: continue if trade.close_date: durations.append((trade.close_date - trade.open_date).total_seconds()) if not trade.is_open: profit_percent = trade.calc_profit_percent() profit_closed_coin.append(trade.calc_profit()) profit_closed_percent.append(profit_percent) else: # Get current rate current_rate = exchange.get_ticker(trade.pair, False)['bid'] profit_percent = trade.calc_profit_percent(rate=current_rate) profit_all_coin.append(trade.calc_profit(rate=Decimal(trade.close_rate or current_rate))) profit_all_percent.append(profit_percent) 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 # Prepare data to display profit_closed_coin = round(sum(profit_closed_coin), 8) profit_closed_percent = round(sum(profit_closed_percent) * 100, 2) profit_closed_fiat = _FIAT_CONVERT.convert_amount( profit_closed_coin, _CONF['stake_currency'], _CONF['fiat_display_currency'] ) profit_all_coin = round(sum(profit_all_coin), 8) profit_all_percent = round(sum(profit_all_percent) * 100, 2) profit_all_fiat = _FIAT_CONVERT.convert_amount( profit_all_coin, _CONF['stake_currency'], _CONF['fiat_display_currency'] ) # Message to display markdown_msg = """ *ROI:* Close trades ∙ `{profit_closed_coin:.8f} {coin} ({profit_closed_percent:.2f}%)` ∙ `{profit_closed_fiat:.3f} {fiat}` *ROI:* All trades ∙ `{profit_all_coin:.8f} {coin} ({profit_all_percent:.2f}%)` ∙ `{profit_all_fiat:.3f} {fiat}` *Total 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( coin=_CONF['stake_currency'], fiat=_CONF['fiat_display_currency'], profit_closed_coin=profit_closed_coin, profit_closed_percent=profit_closed_percent, profit_closed_fiat=profit_closed_fiat, profit_all_coin=profit_all_coin, profit_all_percent=profit_all_percent, profit_all_fiat=profit_all_fiat, 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 * 100, 2), ) send_msg(markdown_msg, bot=bot)
def create_trade(stake_amount: float) -> 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 """ logger.info('Creating new trade with stake_amount: %f ...', stake_amount) whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist']) # Check if stake_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 for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): 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 if analyzer.name == 'danml': for _pair in whitelist: if get_buy_signal(_pair, exchange.get_name(), analyzer): pair = _pair break else: return None elif analyzer.name == 'cryptoml': update = False if datetime.utcnow().minute % 5 == 0: update = True pair = analyzer.get_buy_signal(whitelist, update=update, threshold=0.01, repeats=3) if pair is None: return None # Calculate amount and subtract fee fee = exchange.get_fee() buy_limit = get_target_bid(exchange.get_ticker(pair)) amount = (1 - fee) * stake_amount / buy_limit health = exchange.get_wallet_health() if exchange.get_name() == 'HitBTC': token = pair else: token = get_quote_token(pair) token_healthy = False for status in health: if status['Currency'] == token: token_healthy = status['IsActive'] if token_healthy: order_id = exchange.buy(pair, buy_limit, amount) # Create trade entity and return message = '*{}:* Buying [{}]({}) with limit `{:.8f}`'.format( exchange.get_name().upper(), pair.replace('_', '/'), exchange.get_pair_detail_url(pair), buy_limit) logger.info(message) telegram.send_msg(message) # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL return Trade( pair=pair, stake_amount=stake_amount, amount=amount, fee=fee * 2., open_rate=buy_limit, open_date=datetime.utcnow(), exchange=exchange.get_name().upper(), open_order_id=order_id, # open_order_type='buy' )
def create_trade(stake_amount: float) -> bool: """ 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 :return: True if a trade object has been created and persisted, False otherwise """ logger.info( 'Checking buy signals to create a new trade with stake_amount: %f ...', stake_amount ) whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist']) # Check if stake_amount is fulfilled if exchange.get_balance(_CONF['stake_currency']) < stake_amount: raise DependencyException( 'stake amount is not fulfilled (currency={})'.format(_CONF['stake_currency']) ) # Remove currently opened and latest pairs from whitelist for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): if trade.pair in whitelist: whitelist.remove(trade.pair) logger.debug('Ignoring %s in pair whitelist', trade.pair) if not whitelist: raise DependencyException('No pair in whitelist') # Pick pair based on StochRSI buy signals for _pair in whitelist: if get_signal(_pair, SignalType.BUY): pair = _pair break else: return False # Calculate amount buy_limit = get_target_bid(exchange.get_ticker(pair)) amount = stake_amount / buy_limit order_id = exchange.buy(pair, buy_limit, amount) fiat_converter = CryptoToFiatConverter() stake_amount_fiat = fiat_converter.convert_amount( stake_amount, _CONF['stake_currency'], _CONF['fiat_display_currency'] ) # Create trade entity and return rpc.send_msg('*{}:* Buying [{}]({}) with limit `{:.8f} ({:.6f} {}, {:.3f} {})` '.format( exchange.get_name().upper(), pair.replace('_', '/'), exchange.get_pair_detail_url(pair), buy_limit, stake_amount, _CONF['stake_currency'], stake_amount_fiat, _CONF['fiat_display_currency'] )) # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL trade = Trade( pair=pair, stake_amount=stake_amount, amount=amount, fee=exchange.get_fee(), open_rate=buy_limit, open_date=datetime.utcnow(), exchange=exchange.get_name().upper(), open_order_id=order_id ) Trade.session.add(trade) Trade.session.flush() return True
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_all_coin = [] profit_all_percent = [] profit_closed_coin = [] profit_closed_percent = [] durations = [] for trade in trades: current_rate = None if not trade.open_rate: continue if trade.close_date: durations.append( (trade.close_date - trade.open_date).total_seconds()) if not trade.is_open: profit_percent = trade.calc_profit_percent() profit_closed_coin.append(trade.calc_profit()) profit_closed_percent.append(profit_percent) else: # Get current rate current_rate = exchange.get_ticker(trade.pair, False)['bid'] profit_percent = trade.calc_profit_percent(rate=current_rate) profit_all_coin.append( trade.calc_profit(rate=Decimal(trade.close_rate or current_rate))) profit_all_percent.append(profit_percent) 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 # Prepare data to display profit_closed_coin = round(sum(profit_closed_coin), 8) profit_closed_percent = round(sum(profit_closed_percent) * 100, 2) profit_closed_fiat = _FIAT_CONVERT.convert_amount( profit_closed_coin, _CONF['stake_currency'], _CONF['fiat_display_currency']) profit_all_coin = round(sum(profit_all_coin), 8) profit_all_percent = round(sum(profit_all_percent) * 100, 2) profit_all_fiat = _FIAT_CONVERT.convert_amount( profit_all_coin, _CONF['stake_currency'], _CONF['fiat_display_currency']) # Message to display markdown_msg = """ *ROI:* Close trades ∙ `{profit_closed_coin:.8f} {coin} ({profit_closed_percent:.2f}%)` ∙ `{profit_closed_fiat:.3f} {fiat}` *ROI:* All trades ∙ `{profit_all_coin:.8f} {coin} ({profit_all_percent:.2f}%)` ∙ `{profit_all_fiat:.3f} {fiat}` *Total 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( coin=_CONF['stake_currency'], fiat=_CONF['fiat_display_currency'], profit_closed_coin=profit_closed_coin, profit_closed_percent=profit_closed_percent, profit_closed_fiat=profit_closed_fiat, profit_all_coin=profit_all_coin, profit_all_percent=profit_all_percent, profit_all_fiat=profit_all_fiat, 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 * 100, 2), ) send_msg(markdown_msg, bot=bot)