Add ability to set unlimited stake_amount

This commit is contained in:
Anton 2018-05-23 13:15:03 +03:00
parent 13d6297b9f
commit 9be98cd8f7
5 changed files with 137 additions and 30 deletions

View File

@ -16,7 +16,7 @@ The table below will list all configuration parameters.
|----------|---------|----------|-------------| |----------|---------|----------|-------------|
| `max_open_trades` | 3 | Yes | Number of trades open your bot will have. | `max_open_trades` | 3 | Yes | Number of trades open your bot will have.
| `stake_currency` | BTC | Yes | Crypto-currency used for trading. | `stake_currency` | BTC | Yes | Crypto-currency used for trading.
| `stake_amount` | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. | `stake_amount` | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. 'unlimited' is used to allow a bot to use all avaliable balance.
| `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | No | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes | `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | No | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes
| `fiat_display_currency` | USD | Yes | Fiat currency used to show your profits. More information below. | `fiat_display_currency` | USD | Yes | Fiat currency used to show your profits. More information below.
| `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode. | `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode.

View File

@ -32,7 +32,10 @@ CONF_SCHEMA = {
'max_open_trades': {'type': 'integer', 'minimum': 0}, 'max_open_trades': {'type': 'integer', 'minimum': 0},
'ticker_interval': {'type': 'string', 'enum': list(TICKER_INTERVAL_MINUTES.keys())}, 'ticker_interval': {'type': 'string', 'enum': list(TICKER_INTERVAL_MINUTES.keys())},
'stake_currency': {'type': 'string', 'enum': ['BTC', 'ETH', 'USDT']}, 'stake_currency': {'type': 'string', 'enum': ['BTC', 'ETH', 'USDT']},
'stake_amount': {'type': 'number', 'minimum': 0.0005}, 'stake_amount': {'anyOf': [
{'type': 'integer', 'minimum': 0.0005},
{'constant': 'unlimited'}
]},
'fiat_display_currency': {'type': 'string', 'enum': ['AUD', 'BRL', 'CAD', 'CHF', 'fiat_display_currency': {'type': 'string', 'enum': ['AUD', 'BRL', 'CAD', 'CHF',
'CLP', 'CNY', 'CZK', 'DKK', 'CLP', 'CNY', 'CZK', 'DKK',
'EUR', 'GBP', 'HKD', 'HUF', 'EUR', 'GBP', 'HKD', 'HUF',

View File

@ -255,6 +255,24 @@ class FreqtradeBot(object):
balance = self.config['bid_strategy']['ask_last_balance'] balance = self.config['bid_strategy']['ask_last_balance']
return ticker['ask'] + balance * (ticker['last'] - ticker['ask']) return ticker['ask'] + balance * (ticker['last'] - ticker['ask'])
def _get_trade_stake_amount(self) -> float:
stake_amount = self.config['stake_amount']
avaliable_amount = exchange.get_balance(self.config['stake_currency'])
if stake_amount == 'unlimited':
open_trades = len(Trade.query.filter(Trade.is_open.is_(True)).all())
if open_trades == self.config['max_open_trades']:
return 0
return avaliable_amount / (self.config['max_open_trades'] - open_trades)
# Check if stake_amount is fulfilled
if avaliable_amount < stake_amount:
raise DependencyException(
'stake amount is not fulfilled (currency={})'.format(self.config['stake_currency'])
)
return stake_amount
def create_trade(self) -> bool: def create_trade(self) -> bool:
""" """
Checks the implemented trading indicator(s) for a randomly picked pair, Checks the implemented trading indicator(s) for a randomly picked pair,
@ -263,19 +281,14 @@ class FreqtradeBot(object):
:param interval: Ticker interval used for Analyze :param interval: Ticker interval used for Analyze
:return: True if a trade object has been created and persisted, False otherwise :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() interval = self.analyze.get_ticker_interval()
stake_amount = self._get_trade_stake_amount()
logger.info( logger.info(
'Checking buy signals to create a new trade with stake_amount: %f ...', 'Checking buy signals to create a new trade with stake_amount: %f ...',
stake_amount stake_amount
) )
whitelist = copy.deepcopy(self.config['exchange']['pair_whitelist']) 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 # Remove currently opened and latest pairs from whitelist
for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): for trade in Trade.query.filter(Trade.is_open.is_(True)).all():

View File

@ -13,7 +13,7 @@ from pandas import DataFrame
from tabulate import tabulate from tabulate import tabulate
import freqtrade.optimize as optimize import freqtrade.optimize as optimize
from freqtrade import exchange from freqtrade import exchange, DependencyException
from freqtrade.analyze import Analyze from freqtrade.analyze import Analyze
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration from freqtrade.configuration import Configuration
@ -296,6 +296,9 @@ def setup_configuration(args: Namespace) -> Dict[str, Any]:
config['exchange']['key'] = '' config['exchange']['key'] = ''
config['exchange']['secret'] = '' config['exchange']['secret'] = ''
if config['stake_amount'] == 'unlimited':
raise DependencyException('stake amount could not be "unlimited" for backtesting')
return config return config

View File

@ -220,6 +220,115 @@ def test_refresh_whitelist() -> None:
pass pass
def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None:
"""
Test get_trade_stake_amount() method
"""
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
)
conf = deepcopy(default_conf)
freqtrade = FreqtradeBot(conf, create_engine('sqlite://'))
result = freqtrade._get_trade_stake_amount()
assert(result == conf['stake_amount'])
def test_get_trade_stake_amount_no_stake_amount(default_conf,
ticker,
limit_buy_order,
fee,
mocker) -> None:
"""
Test get_trade_stake_amount() method
"""
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5),
get_fee=fee,
)
conf = deepcopy(default_conf)
freqtrade = FreqtradeBot(conf, create_engine('sqlite://'))
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
freqtrade._get_trade_stake_amount()
def test_get_trade_stake_amount_unlimited_amount(default_conf,
ticker,
limit_buy_order,
fee,
mocker) -> None:
"""
Test get_trade_stake_amount() method
"""
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_balance=MagicMock(return_value=default_conf['stake_amount']),
get_fee=fee,
)
conf = deepcopy(default_conf)
conf['stake_amount'] = 'unlimited'
conf['max_open_trades'] = 2
freqtrade = FreqtradeBot(conf, create_engine('sqlite://'))
# no open trades, order amount should be 'balance / max_open_trades'
result = freqtrade._get_trade_stake_amount()
assert(result == default_conf['stake_amount'] / conf['max_open_trades'])
# create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)'
freqtrade.create_trade()
result = freqtrade._get_trade_stake_amount()
assert(result == default_conf['stake_amount'] / (conf['max_open_trades'] - 1))
def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None:
"""
Test create_trade() method
"""
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5),
get_fee=fee,
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
freqtrade.create_trade()
def test_create_trade(default_conf, ticker, limit_buy_order, fee, mocker) -> None: def test_create_trade(default_conf, ticker, limit_buy_order, fee, mocker) -> None:
""" """
Test create_trade() method Test create_trade() method
@ -281,27 +390,6 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order, fee,
assert rate * amount >= conf['stake_amount'] assert rate * amount >= conf['stake_amount']
def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None:
"""
Test create_trade() method
"""
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5),
get_fee=fee,
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
freqtrade.create_trade()
def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, mocker) -> None: def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, mocker) -> None:
""" """
Test create_trade() method Test create_trade() method