Merge branch 'handleNotEnoughFundsException' into develop-berlinguyinca

# Conflicts:
#	freqtrade/tests/testdata/BTC_ADA-5.json
#	freqtrade/tests/testdata/BTC_DASH-5.json
#	freqtrade/tests/testdata/BTC_ETC-5.json
#	freqtrade/tests/testdata/BTC_ETH-5.json
#	freqtrade/tests/testdata/BTC_LTC-5.json
#	freqtrade/tests/testdata/BTC_NXT-5.json
#	freqtrade/tests/testdata/BTC_POWR-5.json
#	freqtrade/tests/testdata/BTC_XLM-5.json
#	freqtrade/tests/testdata/BTC_XMR-5.json
#	freqtrade/tests/testdata/BTC_ZEC-5.json
This commit is contained in:
Gert Wohlgemuth 2018-05-02 20:43:36 -07:00
commit 5d59cd4d51
13 changed files with 123 additions and 81 deletions

View File

@ -14,3 +14,11 @@ class OperationalException(BaseException):
Requires manual intervention.
This happens when an exchange returns an unexpected error during runtime.
"""
class NotEnoughFundsExeption(BaseException):
"""
This happens when the exchange reports that not enough funds where available.
We do not want to stop the bot in this case and just keep it going and suppress
this message
"""

View File

@ -5,7 +5,7 @@ from bittrex.bittrex import API_V1_1, API_V2_0
from bittrex.bittrex import Bittrex as _Bittrex
from requests.exceptions import ContentDecodingError
from freqtrade import OperationalException
from freqtrade import OperationalException, NotEnoughFundsExeption
from freqtrade.exchange.interface import Exchange
logger = logging.getLogger(__name__)
@ -63,22 +63,37 @@ class Bittrex(Exchange):
data = _API.buy_limit(pair.replace('_', '-'), amount, rate)
if not data['success']:
Bittrex._validate_response(data)
raise OperationalException('{message} params=({pair}, {rate}, {amount})'.format(
message=data['message'],
pair=pair,
rate=rate,
amount=amount))
if data['message'] == "INSUFFICIENT_FUNDS":
raise NotEnoughFundsExeption('{message} params=({pair}, {rate}, {amount})'.format(
message=data['message'],
pair=pair,
rate=rate,
amount=amount))
else:
raise OperationalException('{message} params=({pair}, {rate}, {amount})'.format(
message=data['message'],
pair=pair,
rate=rate,
amount=amount))
return data['result']['uuid']
def sell(self, pair: str, rate: float, amount: float) -> str:
data = _API.sell_limit(pair.replace('_', '-'), amount, rate)
if not data['success']:
Bittrex._validate_response(data)
raise OperationalException('{message} params=({pair}, {rate}, {amount})'.format(
message=data['message'],
pair=pair,
rate=rate,
amount=amount))
if data['message'] == "INSUFFICIENT_FUNDS":
raise NotEnoughFundsExeption('{message} params=({pair}, {rate}, {amount})'.format(
message=data['message'],
pair=pair,
rate=rate,
amount=amount))
else:
raise OperationalException('{message} params=({pair}, {rate}, {amount})'.format(
message=data['message'],
pair=pair,
rate=rate,
amount=amount))
return data['result']['uuid']
def get_balance(self, currency: str) -> float:

View File

@ -15,8 +15,8 @@ import requests
from cachetools import cached, TTLCache
from freqtrade import (
DependencyException, OperationalException, exchange, persistence, __version__
)
DependencyException, OperationalException, exchange, persistence, __version__,
NotEnoughFundsExeption)
from freqtrade.analyze import Analyze
from freqtrade import constants
from freqtrade.fiat_convert import CryptoToFiatConverter
@ -24,7 +24,6 @@ from freqtrade.persistence import Trade
from freqtrade.rpc.rpc_manager import RPCManager
from freqtrade.state import State
logger = logging.getLogger(__name__)
@ -166,7 +165,11 @@ class FreqtradeBot(object):
# Then looking for buy opportunities
if len(trades) < self.config['max_open_trades']:
state_changed = self.process_maybe_execute_buy()
try:
state_changed = self.process_maybe_execute_buy()
except NotEnoughFundsExeption:
logger.warning('insufficient funds to execute this buy order, ignoring it!')
state_changed = False
if 'unfilledtimeout' in self.config:
# Check and handle any timed out open orders
@ -176,13 +179,14 @@ class FreqtradeBot(object):
except (requests.exceptions.RequestException, json.JSONDecodeError) as error:
logger.warning('%s, retrying in 30 seconds...', error)
time.sleep(constants.RETRY_TIMEOUT)
except OperationalException:
self.rpc.send_msg(
'*Status:* OperationalException:\n```\n{traceback}```{hint}'
.format(
traceback=traceback.format_exc(),
hint='Issue `/start` if you think it is safe to restart.'
)
.format(
traceback=traceback.format_exc(),
hint='Issue `/start` if you think it is safe to restart.'
)
)
logger.exception('OperationalException. Stopping trader ...')
self.state = State.STOPPED
@ -301,16 +305,16 @@ class FreqtradeBot(object):
# 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']
)
.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(
@ -468,59 +472,64 @@ class FreqtradeBot(object):
: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)
# check if we have these funds first
try:
order_id = exchange.sell(str(trade.pair), limit, trade.amount)
trade.open_order_id = order_id
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),
)
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)
# 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
)
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),
)
# Send the message
self.rpc.send_msg(message)
Trade.session.flush()
# 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()
except NotEnoughFundsExeption:
logger.warning('Sell order failed, due to not having enough funds for %s.', trade)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long