Merge branch 'develop' into money_mgt

This commit is contained in:
misagh 2018-10-23 19:33:13 +02:00
commit 25daf3a0f7
31 changed files with 206 additions and 115 deletions

View File

@ -28,7 +28,10 @@
"name": "bittrex", "name": "bittrex",
"key": "your_exchange_key", "key": "your_exchange_key",
"secret": "your_exchange_secret", "secret": "your_exchange_secret",
"ccxt_rate_limit": true, "ccxt_config": {"enableRateLimit": true},
"ccxt_async_config": {
"enableRateLimit": false
},
"pair_whitelist": [ "pair_whitelist": [
"ETH/BTC", "ETH/BTC",
"LTC/BTC", "LTC/BTC",

View File

@ -37,7 +37,11 @@
"name": "bittrex", "name": "bittrex",
"key": "your_exchange_key", "key": "your_exchange_key",
"secret": "your_exchange_secret", "secret": "your_exchange_secret",
"ccxt_rate_limit": true, "ccxt_config": {"enableRateLimit": true},
"ccxt_async_config": {
"enableRateLimit": false,
"aiohttp_trust_env": false
},
"pair_whitelist": [ "pair_whitelist": [
"ETH/BTC", "ETH/BTC",
"LTC/BTC", "LTC/BTC",

View File

@ -44,7 +44,9 @@ The table below will list all configuration parameters.
| `exchange.secret` | secret | No | API secret to use for the exchange. Only required when you are in production mode. | `exchange.secret` | secret | No | API secret to use for the exchange. Only required when you are in production mode.
| `exchange.pair_whitelist` | [] | No | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param. | `exchange.pair_whitelist` | [] | No | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param.
| `exchange.pair_blacklist` | [] | No | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param. | `exchange.pair_blacklist` | [] | No | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param.
| `exchange.ccxt_rate_limit` | True | No | Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange. | `exchange.ccxt_rate_limit` | True | No | DEPRECATED!! Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange.
| `exchange.ccxt_config` | None | No | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation)
| `exchange.ccxt_async_config` | None | No | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation)
| `experimental.use_sell_signal` | false | No | Use your sell strategy in addition of the `minimal_roi`. | `experimental.use_sell_signal` | false | No | Use your sell strategy in addition of the `minimal_roi`.
| `experimental.sell_profit_only` | false | No | waits until you have made a positive profit before taking a sell decision. | `experimental.sell_profit_only` | false | No | waits until you have made a positive profit before taking a sell decision.
| `experimental.ignore_roi_if_buy_signal` | false | No | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal` | `experimental.ignore_roi_if_buy_signal` | false | No | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal`
@ -204,8 +206,29 @@ you run it in production mode.
} }
``` ```
If you have not your Bittrex API key yet, [see our tutorial](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md). If you have not your Bittrex API key yet, [see our tutorial](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md).
### Using proxy with FreqTrade
To use a proxy with freqtrade, add the kwarg `"aiohttp_trust_env"=true` to the `"ccxt_async_kwargs"` dict in the exchange section of the configuration.
An example for this can be found in `config_full.json.example`
``` json
"ccxt_async_config": {
"aiohttp_trust_env": true
}
```
Then, export your proxy settings using the variables `"HTTP_PROXY"` and `"HTTPS_PROXY"` set to the appropriate values
``` bash
export HTTP_PROXY="http://addr:port"
export HTTPS_PROXY="http://addr:port"
freqtrade
```
### Embedding Strategies ### Embedding Strategies
@ -213,7 +236,7 @@ FreqTrade provides you with with an easy way to embed the strategy into your con
This is done by utilizing BASE64 encoding and providing this string at the strategy configuration field, This is done by utilizing BASE64 encoding and providing this string at the strategy configuration field,
in your chosen config file. in your chosen config file.
##### Encoding a string as BASE64 #### Encoding a string as BASE64
This is a quick example, how to generate the BASE64 string in python This is a quick example, how to generate the BASE64 string in python

View File

@ -20,8 +20,8 @@ We recommend you start by taking a look at `hyperopt.py` file located in [freqtr
### Configure your Guards and Triggers ### Configure your Guards and Triggers
There are two places you need to change to add a new buy strategy for testing: There are two places you need to change to add a new buy strategy for testing:
- Inside [populate_buy_trend()](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/optimize/hyperopt.py#L278-L294). - Inside [populate_buy_trend()](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/optimize/hyperopt.py#L231-L264).
- Inside [hyperopt_space()](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/optimize/hyperopt.py#L218-L229) - Inside [hyperopt_space()](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/optimize/hyperopt.py#L213-L224)
and the associated methods `indicator_space`, `roi_space`, `stoploss_space`. and the associated methods `indicator_space`, `roi_space`, `stoploss_space`.
There you have two different type of indicators: 1. `guards` and 2. `triggers`. There you have two different type of indicators: 1. `guards` and 2. `triggers`.
@ -131,12 +131,12 @@ you have on-disk, use the `--datadir PATH` option. Default hyperopt will
use data from directory `user_data/data`. use data from directory `user_data/data`.
### Running Hyperopt with Smaller Testset ### Running Hyperopt with Smaller Testset
Use the `--timeperiod` argument to change how much of the testset Use the `--timerange` argument to change how much of the testset
you want to use. The last N ticks/timeframes will be used. you want to use. The last N ticks/timeframes will be used.
Example: Example:
```bash ```bash
python3 ./freqtrade/main.py hyperopt --timeperiod -200 python3 ./freqtrade/main.py hyperopt --timerange -200
``` ```
### Running Hyperopt with Smaller Search Space ### Running Hyperopt with Smaller Search Space

View File

@ -245,8 +245,8 @@ conda install python=3.6
conda create -n freqtrade python=3.6 conda create -n freqtrade python=3.6
conda install scipy pandas conda install scipy pandas
pip install -r requirements.txt python3 -m pip install -r requirements.txt
pip install -e . python3 -m pip install -e .
``` ```
### MacOS ### MacOS

View File

@ -48,4 +48,4 @@ Both values can be configured in the main configuration file and requires `"trai
The 0.01 would translate to a 1% stop loss, once you hit 1.1% profit. The 0.01 would translate to a 1% stop loss, once you hit 1.1% profit.
You should also make sure to have this value higher than your minimal ROI, otherwise minimal ROI will apply first and sell your trade. You should also make sure to have this value (`trailing_stop_positive_offset`) lower than your minimal ROI, otherwise minimal ROI will apply first and sell your trade.

View File

@ -129,9 +129,3 @@ Day Profit BTC Profit USD
## /version ## /version
> **Version:** `0.14.3` > **Version:** `0.14.3`
### using proxy with telegram
```
$ export HTTP_PROXY="http://addr:port"
$ export HTTPS_PROXY="http://addr:port"
$ freqtrade
```

View File

@ -1,5 +1,5 @@
""" FreqTrade bot """ """ FreqTrade bot """
__version__ = '0.17.2' __version__ = '0.17.3'
class DependencyException(BaseException): class DependencyException(BaseException):

View File

@ -271,6 +271,11 @@ class Configuration(object):
raise OperationalException( raise OperationalException(
exception_msg exception_msg
) )
# Depreciation warning
if 'ccxt_rate_limit' in config.get('exchange', {}):
logger.warning("`ccxt_rate_limit` has been deprecated in favor of "
"`ccxt_config` and `ccxt_async_config` and will be removed "
"in a future version.")
logger.debug('Exchange "%s" supported', exchange) logger.debug('Exchange "%s" supported', exchange)
return True return True

View File

@ -165,7 +165,9 @@ CONF_SCHEMA = {
}, },
'uniqueItems': True 'uniqueItems': True
}, },
'outdated_offset': {'type': 'integer', 'minimum': 1} 'outdated_offset': {'type': 'integer', 'minimum': 1},
'ccxt_config': {'type': 'object'},
'ccxt_async_config': {'type': 'object'}
}, },
'required': ['name', 'key', 'secret', 'pair_whitelist'] 'required': ['name', 'key', 'secret', 'pair_whitelist']
}, },

View File

@ -93,8 +93,9 @@ class Exchange(object):
logger.info('Instance is running with dry_run enabled') logger.info('Instance is running with dry_run enabled')
exchange_config = config['exchange'] exchange_config = config['exchange']
self._api = self._init_ccxt(exchange_config) self._api = self._init_ccxt(exchange_config, ccxt_kwargs=exchange_config.get('ccxt_config'))
self._api_async = self._init_ccxt(exchange_config, ccxt_async) self._api_async = self._init_ccxt(exchange_config, ccxt_async,
ccxt_kwargs=exchange_config.get('ccxt_async_config'))
logger.info('Using Exchange "%s"', self.name) logger.info('Using Exchange "%s"', self.name)
@ -114,7 +115,8 @@ class Exchange(object):
if self._api_async and inspect.iscoroutinefunction(self._api_async.close): if self._api_async and inspect.iscoroutinefunction(self._api_async.close):
asyncio.get_event_loop().run_until_complete(self._api_async.close()) asyncio.get_event_loop().run_until_complete(self._api_async.close())
def _init_ccxt(self, exchange_config: dict, ccxt_module=ccxt) -> ccxt.Exchange: def _init_ccxt(self, exchange_config: dict, ccxt_module=ccxt,
ccxt_kwargs: dict = None) -> ccxt.Exchange:
""" """
Initialize ccxt with given config and return valid Initialize ccxt with given config and return valid
ccxt instance. ccxt instance.
@ -124,14 +126,20 @@ class Exchange(object):
if name not in ccxt_module.exchanges: if name not in ccxt_module.exchanges:
raise OperationalException(f'Exchange {name} is not supported') raise OperationalException(f'Exchange {name} is not supported')
try:
api = getattr(ccxt_module, name.lower())({ ex_config = {
'apiKey': exchange_config.get('key'), 'apiKey': exchange_config.get('key'),
'secret': exchange_config.get('secret'), 'secret': exchange_config.get('secret'),
'password': exchange_config.get('password'), 'password': exchange_config.get('password'),
'uid': exchange_config.get('uid', ''), 'uid': exchange_config.get('uid', ''),
'enableRateLimit': exchange_config.get('ccxt_rate_limit', True) 'enableRateLimit': exchange_config.get('ccxt_rate_limit', True)
}) }
if ccxt_kwargs:
logger.info('Applying additional ccxt config: %s', ccxt_kwargs)
ex_config.update(ccxt_kwargs)
try:
api = getattr(ccxt_module, name.lower())(ex_config)
except (KeyError, AttributeError): except (KeyError, AttributeError):
raise OperationalException(f'Exchange {name} is not supported') raise OperationalException(f'Exchange {name} is not supported')

View File

@ -75,8 +75,6 @@ class Backtesting(object):
else: else:
# only one strategy # only one strategy
strat = StrategyResolver(self.config).strategy
self.strategylist.append(StrategyResolver(self.config).strategy) self.strategylist.append(StrategyResolver(self.config).strategy)
# Load one strategy # Load one strategy
self._set_strategy(self.strategylist[0]) self._set_strategy(self.strategylist[0])

View File

@ -152,7 +152,7 @@ class Hyperopt(Backtesting):
@staticmethod @staticmethod
def generate_roi_table(params: Dict) -> Dict[int, float]: def generate_roi_table(params: Dict) -> Dict[int, float]:
""" """
Generate the ROI table thqt will be used by Hyperopt Generate the ROI table that will be used by Hyperopt
""" """
roi_table = {} roi_table = {}
roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3'] roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3']
@ -402,6 +402,13 @@ def start(args: Namespace) -> None:
config['exchange']['key'] = '' config['exchange']['key'] = ''
config['exchange']['secret'] = '' config['exchange']['secret'] = ''
if config.get('strategy') and config.get('strategy') != 'DefaultStrategy':
logger.error("Please don't use --strategy for hyperopt.")
logger.error(
"Read the documentation at "
"https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md "
"to understand how to configure hyperopt.")
raise ValueError("--strategy configured but not supported for hyperopt")
# Initialize backtesting object # Initialize backtesting object
hyperopt = Hyperopt(config) hyperopt = Hyperopt(config)
hyperopt.start() hyperopt.start()

View File

@ -4,7 +4,7 @@ This module contains the class to persist trades into SQLite
import logging import logging
from datetime import datetime from datetime import datetime
from decimal import Decimal, getcontext from decimal import Decimal
from typing import Any, Dict, Optional from typing import Any, Dict, Optional
import arrow import arrow
@ -241,7 +241,6 @@ class Trade(_DECL_BASE):
logger.info('Updating trade (id=%d) ...', self.id) logger.info('Updating trade (id=%d) ...', self.id)
getcontext().prec = 8 # Bittrex do not go above 8 decimal
if order_type == 'limit' and order['side'] == 'buy': if order_type == 'limit' and order['side'] == 'buy':
# Update open rate and actual amount # Update open rate and actual amount
self.open_rate = Decimal(order['price']) self.open_rate = Decimal(order['price'])
@ -278,7 +277,6 @@ class Trade(_DECL_BASE):
If rate is not set self.fee will be used If rate is not set self.fee will be used
:return: Price in BTC of the open trade :return: Price in BTC of the open trade
""" """
getcontext().prec = 8
buy_trade = (Decimal(self.amount) * Decimal(self.open_rate)) buy_trade = (Decimal(self.amount) * Decimal(self.open_rate))
fees = buy_trade * Decimal(fee or self.fee_open) fees = buy_trade * Decimal(fee or self.fee_open)
@ -296,7 +294,6 @@ class Trade(_DECL_BASE):
If rate is not set self.close_rate will be used If rate is not set self.close_rate will be used
:return: Price in BTC of the open trade :return: Price in BTC of the open trade
""" """
getcontext().prec = 8
if rate is None and not self.close_rate: if rate is None and not self.close_rate:
return 0.0 return 0.0
@ -336,7 +333,6 @@ class Trade(_DECL_BASE):
:param fee: fee to use on the close rate (optional). :param fee: fee to use on the close rate (optional).
:return: profit in percentage as float :return: profit in percentage as float
""" """
getcontext().prec = 8
open_trade_price = self.calc_open_trade_price() open_trade_price = self.calc_open_trade_price()
close_trade_price = self.calc_close_trade_price( close_trade_price = self.calc_close_trade_price(

View File

@ -84,9 +84,7 @@ class RPC(object):
""" """
# Fetch open trade # Fetch open trade
trades = Trade.query.filter(Trade.is_open.is_(True)).all() trades = Trade.query.filter(Trade.is_open.is_(True)).all()
if self._freqtrade.state != State.RUNNING: if not trades:
raise RPCException('trader is not running')
elif not trades:
raise RPCException('no active trade') raise RPCException('no active trade')
else: else:
results = [] results = []
@ -118,9 +116,7 @@ class RPC(object):
def _rpc_status_table(self) -> DataFrame: def _rpc_status_table(self) -> DataFrame:
trades = Trade.query.filter(Trade.is_open.is_(True)).all() trades = Trade.query.filter(Trade.is_open.is_(True)).all()
if self._freqtrade.state != State.RUNNING: if not trades:
raise RPCException('trader is not running')
elif not trades:
raise RPCException('no active order') raise RPCException('no active order')
else: else:
trades_list = [] trades_list = []
@ -363,6 +359,7 @@ class RPC(object):
# Execute sell for all open orders # Execute sell for all open orders
for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
_exec_forcesell(trade) _exec_forcesell(trade)
Trade.session.flush()
return return
# Query for trade # Query for trade
@ -384,8 +381,6 @@ class RPC(object):
Handler for performance. Handler for performance.
Shows a performance statistic from finished trades Shows a performance statistic from finished trades
""" """
if self._freqtrade.state != State.RUNNING:
raise RPCException('trader is not running')
pair_rates = Trade.session.query(Trade.pair, pair_rates = Trade.session.query(Trade.pair,
sql.func.sum(Trade.close_profit).label('profit_sum'), sql.func.sum(Trade.close_profit).label('profit_sum'),

View File

@ -307,11 +307,14 @@ class Telegram(RPC):
result = self._rpc_balance(self._config.get('fiat_display_currency', '')) result = self._rpc_balance(self._config.get('fiat_display_currency', ''))
output = '' output = ''
for currency in result['currencies']: for currency in result['currencies']:
output += "*{currency}:*\n" \ if currency['est_btc'] > 0.0001:
"\t`Available: {available: .8f}`\n" \ output += "*{currency}:*\n" \
"\t`Balance: {balance: .8f}`\n" \ "\t`Available: {available: .8f}`\n" \
"\t`Pending: {pending: .8f}`\n" \ "\t`Balance: {balance: .8f}`\n" \
"\t`Est. BTC: {est_btc: .8f}`\n".format(**currency) "\t`Pending: {pending: .8f}`\n" \
"\t`Est. BTC: {est_btc: .8f}`\n".format(**currency)
else:
output += "*{currency}:* not showing <1$ amount \n".format(**currency)
output += "\n*Estimated Value*:\n" \ output += "\n*Estimated Value*:\n" \
"\t`BTC: {total: .8f}`\n" \ "\t`BTC: {total: .8f}`\n" \
@ -437,6 +440,7 @@ class Telegram(RPC):
"*/count:* `Show number of trades running compared to allowed number of trades`" \ "*/count:* `Show number of trades running compared to allowed number of trades`" \
"\n" \ "\n" \
"*/balance:* `Show account balance per currency`\n" \ "*/balance:* `Show account balance per currency`\n" \
"*/reload_conf:* `Reload configuration file` \n" \
"*/help:* `This help message`\n" \ "*/help:* `This help message`\n" \
"*/version:* `Show version`" "*/version:* `Show version`"

View File

@ -3,7 +3,8 @@ import sys
from copy import deepcopy from copy import deepcopy
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
# Import Default-Strategy to have hyperopt correctly resolve
from freqtrade.strategy.default_strategy import DefaultStrategy # noqa: F401
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

View File

@ -1,5 +1,6 @@
# pragma pylint: disable=missing-docstring, C0103, bad-continuation, global-statement # pragma pylint: disable=missing-docstring, C0103, bad-continuation, global-statement
# pragma pylint: disable=protected-access # pragma pylint: disable=protected-access
import copy
import logging import logging
from datetime import datetime from datetime import datetime
from random import randint from random import randint
@ -56,6 +57,32 @@ def test_init(default_conf, mocker, caplog):
assert log_has('Instance is running with dry_run enabled', caplog.record_tuples) assert log_has('Instance is running with dry_run enabled', caplog.record_tuples)
def test_init_ccxt_kwargs(default_conf, mocker, caplog):
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
caplog.set_level(logging.INFO)
conf = copy.deepcopy(default_conf)
conf['exchange']['ccxt_async_config'] = {'aiohttp_trust_env': True}
ex = Exchange(conf)
assert log_has("Applying additional ccxt config: {'aiohttp_trust_env': True}",
caplog.record_tuples)
assert ex._api_async.aiohttp_trust_env
assert not ex._api.aiohttp_trust_env
# Reset logging and config
caplog.clear()
conf = copy.deepcopy(default_conf)
conf['exchange']['ccxt_config'] = {'TestKWARG': 11}
ex = Exchange(conf)
assert not log_has("Applying additional ccxt config: {'aiohttp_trust_env': True}",
caplog.record_tuples)
assert not ex._api_async.aiohttp_trust_env
assert hasattr(ex._api, 'TestKWARG')
assert ex._api.TestKWARG == 11
assert not hasattr(ex._api_async, 'TestKWARG')
assert log_has("Applying additional ccxt config: {'TestKWARG': 11}",
caplog.record_tuples)
def test_destroy(default_conf, mocker, caplog): def test_destroy(default_conf, mocker, caplog):
caplog.set_level(logging.DEBUG) caplog.set_level(logging.DEBUG)
get_patched_exchange(mocker, default_conf) get_patched_exchange(mocker, default_conf)

View File

View File

@ -534,7 +534,7 @@ def test_backtest(default_conf, fee, mocker) -> None:
expected = pd.DataFrame( expected = pd.DataFrame(
{'pair': [pair, pair], {'pair': [pair, pair],
'profit_percent': [0.00029975, 0.00056708], 'profit_percent': [0.00029977, 0.00056716],
'profit_abs': [1.49e-06, 7.6e-07], 'profit_abs': [1.49e-06, 7.6e-07],
'open_time': [Arrow(2018, 1, 29, 18, 40, 0).datetime, 'open_time': [Arrow(2018, 1, 29, 18, 40, 0).datetime,
Arrow(2018, 1, 30, 3, 30, 0).datetime], Arrow(2018, 1, 30, 3, 30, 0).datetime],

View File

@ -65,6 +65,31 @@ def test_start(mocker, default_conf, caplog) -> None:
assert start_mock.call_count == 1 assert start_mock.call_count == 1
def test_start_failure(mocker, default_conf, caplog) -> None:
start_mock = MagicMock()
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
patch_exchange(mocker)
args = [
'--config', 'config.json',
'--strategy', 'TestStrategy',
'hyperopt',
'--epochs', '5'
]
args = get_args(args)
StrategyResolver({'strategy': 'DefaultStrategy'})
with pytest.raises(ValueError):
start(args)
assert log_has(
"Please don't use --strategy for hyperopt.",
caplog.record_tuples
)
def test_loss_calculation_prefer_correct_trade_count(hyperopt) -> None: def test_loss_calculation_prefer_correct_trade_count(hyperopt) -> None:
StrategyResolver({'strategy': 'DefaultStrategy'}) StrategyResolver({'strategy': 'DefaultStrategy'})

View File

View File

@ -40,10 +40,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
patch_get_signal(freqtradebot, (True, False)) patch_get_signal(freqtradebot, (True, False))
rpc = RPC(freqtradebot) rpc = RPC(freqtradebot)
freqtradebot.state = State.STOPPED
with pytest.raises(RPCException, match=r'.*trader is not running*'):
rpc._rpc_trade_status()
freqtradebot.state = State.RUNNING freqtradebot.state = State.RUNNING
with pytest.raises(RPCException, match=r'.*no active trade*'): with pytest.raises(RPCException, match=r'.*no active trade*'):
rpc._rpc_trade_status() rpc._rpc_trade_status()
@ -81,10 +77,6 @@ def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None:
patch_get_signal(freqtradebot, (True, False)) patch_get_signal(freqtradebot, (True, False))
rpc = RPC(freqtradebot) rpc = RPC(freqtradebot)
freqtradebot.state = State.STOPPED
with pytest.raises(RPCException, match=r'.*trader is not running*'):
rpc._rpc_status_table()
freqtradebot.state = State.RUNNING freqtradebot.state = State.RUNNING
with pytest.raises(RPCException, match=r'.*no active order*'): with pytest.raises(RPCException, match=r'.*no active order*'):
rpc._rpc_status_table() rpc._rpc_status_table()

View File

@ -250,9 +250,10 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No
telegram = Telegram(freqtradebot) telegram = Telegram(freqtradebot)
freqtradebot.state = State.STOPPED freqtradebot.state = State.STOPPED
# Status is also enabled when stopped
telegram._status(bot=MagicMock(), update=update) telegram._status(bot=MagicMock(), update=update)
assert msg_mock.call_count == 1 assert msg_mock.call_count == 1
assert 'trader is not running' in msg_mock.call_args_list[0][0][0] assert 'no active trade' in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock() msg_mock.reset_mock()
freqtradebot.state = State.RUNNING freqtradebot.state = State.RUNNING
@ -295,9 +296,10 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker)
telegram = Telegram(freqtradebot) telegram = Telegram(freqtradebot)
freqtradebot.state = State.STOPPED freqtradebot.state = State.STOPPED
# Status table is also enabled when stopped
telegram._status_table(bot=MagicMock(), update=update) telegram._status_table(bot=MagicMock(), update=update)
assert msg_mock.call_count == 1 assert msg_mock.call_count == 1
assert 'trader is not running' in msg_mock.call_args_list[0][0][0] assert 'no active order' in msg_mock.call_args_list[0][0][0]
msg_mock.reset_mock() msg_mock.reset_mock()
freqtradebot.state = State.RUNNING freqtradebot.state = State.RUNNING
@ -507,7 +509,12 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None:
'total': 10.0, 'total': 10.0,
'free': 10.0, 'free': 10.0,
'used': 0.0 'used': 0.0
} },
'XRP': {
'total': 1.0,
'free': 1.0,
'used': 0.0
}
} }
def mock_ticker(symbol, refresh): def mock_ticker(symbol, refresh):
@ -517,7 +524,12 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None:
'ask': 10000.00, 'ask': 10000.00,
'last': 10000.00, 'last': 10000.00,
} }
elif symbol == 'XRP/BTC':
return {
'bid': 0.00001,
'ask': 0.00001,
'last': 0.00001,
}
return { return {
'bid': 0.1, 'bid': 0.1,
'ask': 0.1, 'ask': 0.1,
@ -548,7 +560,8 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None:
assert '*USDT:*' in result assert '*USDT:*' in result
assert 'Balance:' in result assert 'Balance:' in result
assert 'Est. BTC:' in result assert 'Est. BTC:' in result
assert 'BTC: 14.00000000' in result assert 'BTC: 12.00000000' in result
assert '*XRP:* not showing <1$ amount' in result
def test_balance_handle_empty_response(default_conf, update, mocker) -> None: def test_balance_handle_empty_response(default_conf, update, mocker) -> None:
@ -712,7 +725,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
'open_rate': 1.099e-05, 'open_rate': 1.099e-05,
'current_rate': 1.172e-05, 'current_rate': 1.172e-05,
'profit_amount': 6.126e-05, 'profit_amount': 6.126e-05,
'profit_percent': 0.06110514, 'profit_percent': 0.0611052,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
'fiat_currency': 'USD', 'fiat_currency': 'USD',
} == last_msg } == last_msg
@ -765,7 +778,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
'open_rate': 1.099e-05, 'open_rate': 1.099e-05,
'current_rate': 1.044e-05, 'current_rate': 1.044e-05,
'profit_amount': -5.492e-05, 'profit_amount': -5.492e-05,
'profit_percent': -0.05478343, 'profit_percent': -0.05478342,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
'fiat_currency': 'USD', 'fiat_currency': 'USD',
} == last_msg } == last_msg
@ -810,7 +823,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker
'open_rate': 1.099e-05, 'open_rate': 1.099e-05,
'current_rate': 1.098e-05, 'current_rate': 1.098e-05,
'profit_amount': -5.91e-06, 'profit_amount': -5.91e-06,
'profit_percent': -0.00589292, 'profit_percent': -0.00589291,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
'fiat_currency': 'USD', 'fiat_currency': 'USD',
} == msg } == msg
@ -895,26 +908,6 @@ def test_performance_handle(default_conf, update, ticker, fee,
assert '<code>ETH/BTC\t6.20% (1)</code>' in msg_mock.call_args_list[0][0][0] assert '<code>ETH/BTC\t6.20% (1)</code>' in msg_mock.call_args_list[0][0][0]
def test_performance_handle_invalid(default_conf, update, mocker) -> None:
patch_coinmarketcap(mocker)
patch_exchange(mocker)
msg_mock = MagicMock()
mocker.patch.multiple(
'freqtrade.rpc.telegram.Telegram',
_init=MagicMock(),
_send_msg=msg_mock
)
freqtradebot = FreqtradeBot(default_conf)
patch_get_signal(freqtradebot, (True, False))
telegram = Telegram(freqtradebot)
# Trader is not running
freqtradebot.state = State.STOPPED
telegram._performance(bot=MagicMock(), update=update)
assert msg_mock.call_count == 1
assert 'not running' in msg_mock.call_args_list[0][0][0]
def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> None: def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> None:
patch_coinmarketcap(mocker) patch_coinmarketcap(mocker)
patch_exchange(mocker) patch_exchange(mocker)

View File

View File

@ -371,7 +371,7 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None:
assert log_has('Parameter -s/--spaces detected: [\'all\']', caplog.record_tuples) assert log_has('Parameter -s/--spaces detected: [\'all\']', caplog.record_tuples)
def test_check_exchange(default_conf) -> None: def test_check_exchange(default_conf, caplog) -> None:
configuration = Configuration(Namespace()) configuration = Configuration(Namespace())
# Test a valid exchange # Test a valid exchange
@ -392,6 +392,15 @@ def test_check_exchange(default_conf) -> None:
): ):
configuration.check_exchange(default_conf) configuration.check_exchange(default_conf)
# Test ccxt_rate_limit depreciation
default_conf.get('exchange').update({'name': 'binance'})
default_conf['exchange']['ccxt_rate_limit'] = True
configuration.check_exchange(default_conf)
assert log_has("`ccxt_rate_limit` has been deprecated in favor of "
"`ccxt_config` and `ccxt_async_config` and will be removed "
"in a future version.",
caplog.record_tuples)
def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None: def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open( mocker.patch('freqtrade.configuration.open', mocker.mock_open(

View File

@ -167,11 +167,6 @@ def test_gen_pair_whitelist_not_supported(mocker, default_conf, tickers) -> None
freqtrade._gen_pair_whitelist(base_currency='BTC') freqtrade._gen_pair_whitelist(base_currency='BTC')
@pytest.mark.skip(reason="Test not implemented")
def test_refresh_whitelist() -> None:
pass
def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None:
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
@ -905,7 +900,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order,
trade.update(limit_sell_order) trade.update(limit_sell_order)
assert trade.close_rate == 0.00001173 assert trade.close_rate == 0.00001173
assert trade.close_profit == 0.06201057 assert trade.close_profit == 0.06201058
assert trade.calc_profit() == 0.00006217 assert trade.calc_profit() == 0.00006217
assert trade.close_date is not None assert trade.close_date is not None
@ -1098,6 +1093,7 @@ def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_ord
fee, mocker) -> None: fee, mocker) -> None:
rpc_mock = patch_RPCManager(mocker) rpc_mock = patch_RPCManager(mocker)
cancel_order_mock = MagicMock() cancel_order_mock = MagicMock()
patch_exchange(mocker)
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
@ -1331,7 +1327,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc
'open_rate': 1.099e-05, 'open_rate': 1.099e-05,
'current_rate': 1.172e-05, 'current_rate': 1.172e-05,
'profit_amount': 6.126e-05, 'profit_amount': 6.126e-05,
'profit_percent': 0.06110514, 'profit_percent': 0.0611052,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
'fiat_currency': 'USD', 'fiat_currency': 'USD',
} == last_msg } == last_msg
@ -1377,7 +1373,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets,
'open_rate': 1.099e-05, 'open_rate': 1.099e-05,
'current_rate': 1.044e-05, 'current_rate': 1.044e-05,
'profit_amount': -5.492e-05, 'profit_amount': -5.492e-05,
'profit_percent': -0.05478343, 'profit_percent': -0.05478342,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
'fiat_currency': 'USD', 'fiat_currency': 'USD',
} == last_msg } == last_msg
@ -1424,7 +1420,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee,
'open_rate': 1.099e-05, 'open_rate': 1.099e-05,
'current_rate': 1.172e-05, 'current_rate': 1.172e-05,
'profit_amount': 6.126e-05, 'profit_amount': 6.126e-05,
'profit_percent': 0.06110514, 'profit_percent': 0.0611052,
} == last_msg } == last_msg
@ -1470,7 +1466,7 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee,
'open_rate': 1.099e-05, 'open_rate': 1.099e-05,
'current_rate': 1.044e-05, 'current_rate': 1.044e-05,
'profit_amount': -5.492e-05, 'profit_amount': -5.492e-05,
'profit_percent': -0.05478343, 'profit_percent': -0.05478342,
} == last_msg } == last_msg

View File

@ -113,7 +113,7 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order, fee):
trade.update(limit_sell_order) trade.update(limit_sell_order)
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.close_rate == 0.00001173 assert trade.close_rate == 0.00001173
assert trade.close_profit == 0.06201057 assert trade.close_profit == 0.06201058
assert trade.close_date is not None assert trade.close_date is not None
@ -129,16 +129,16 @@ def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order, fee):
trade.open_order_id = 'something' trade.open_order_id = 'something'
trade.update(limit_buy_order) trade.update(limit_buy_order)
assert trade.calc_open_trade_price() == 0.001002500 assert trade.calc_open_trade_price() == 0.0010024999999225068
trade.update(limit_sell_order) trade.update(limit_sell_order)
assert trade.calc_close_trade_price() == 0.0010646656 assert trade.calc_close_trade_price() == 0.0010646656050132426
# Profit in BTC # Profit in BTC
assert trade.calc_profit() == 0.00006217 assert trade.calc_profit() == 0.00006217
# Profit in percent # Profit in percent
assert trade.calc_profit_percent() == 0.06201057 assert trade.calc_profit_percent() == 0.06201058
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
@ -207,10 +207,10 @@ def test_calc_open_trade_price(limit_buy_order, fee):
trade.update(limit_buy_order) # Buy @ 0.00001099 trade.update(limit_buy_order) # Buy @ 0.00001099
# Get the open rate price with the standard fee rate # Get the open rate price with the standard fee rate
assert trade.calc_open_trade_price() == 0.001002500 assert trade.calc_open_trade_price() == 0.0010024999999225068
# Get the open rate price with a custom fee rate # Get the open rate price with a custom fee rate
assert trade.calc_open_trade_price(fee=0.003) == 0.001003000 assert trade.calc_open_trade_price(fee=0.003) == 0.001002999999922468
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
@ -226,14 +226,14 @@ def test_calc_close_trade_price(limit_buy_order, limit_sell_order, fee):
trade.update(limit_buy_order) # Buy @ 0.00001099 trade.update(limit_buy_order) # Buy @ 0.00001099
# Get the close rate price with a custom close rate and a regular fee rate # Get the close rate price with a custom close rate and a regular fee rate
assert trade.calc_close_trade_price(rate=0.00001234) == 0.0011200318 assert trade.calc_close_trade_price(rate=0.00001234) == 0.0011200318470471794
# Get the close rate price with a custom close rate and a custom fee rate # Get the close rate price with a custom close rate and a custom fee rate
assert trade.calc_close_trade_price(rate=0.00001234, fee=0.003) == 0.0011194704 assert trade.calc_close_trade_price(rate=0.00001234, fee=0.003) == 0.0011194704275749754
# Test when we apply a Sell order, and ask price with a custom fee rate # Test when we apply a Sell order, and ask price with a custom fee rate
trade.update(limit_sell_order) trade.update(limit_sell_order)
assert trade.calc_close_trade_price(fee=0.005) == 0.0010619972 assert trade.calc_close_trade_price(fee=0.005) == 0.0010619972701635854
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
@ -281,17 +281,17 @@ def test_calc_profit_percent(limit_buy_order, limit_sell_order, fee):
trade.update(limit_buy_order) # Buy @ 0.00001099 trade.update(limit_buy_order) # Buy @ 0.00001099
# Get percent of profit with a custom rate (Higher than open rate) # Get percent of profit with a custom rate (Higher than open rate)
assert trade.calc_profit_percent(rate=0.00001234) == 0.1172387 assert trade.calc_profit_percent(rate=0.00001234) == 0.11723875
# Get percent of profit with a custom rate (Lower than open rate) # Get percent of profit with a custom rate (Lower than open rate)
assert trade.calc_profit_percent(rate=0.00000123) == -0.88863827 assert trade.calc_profit_percent(rate=0.00000123) == -0.88863828
# Test when we apply a Sell order. Sell higher than open rate @ 0.00001173 # Test when we apply a Sell order. Sell higher than open rate @ 0.00001173
trade.update(limit_sell_order) trade.update(limit_sell_order)
assert trade.calc_profit_percent() == 0.06201057 assert trade.calc_profit_percent() == 0.06201058
# Test with a custom fee rate on the close trade # Test with a custom fee rate on the close trade
assert trade.calc_profit_percent(fee=0.003) == 0.0614782 assert trade.calc_profit_percent(fee=0.003) == 0.06147824
def test_clean_dry_run_db(default_conf, fee): def test_clean_dry_run_db(default_conf, fee):

View File

@ -1,18 +1,18 @@
ccxt==1.17.351 ccxt==1.17.402
SQLAlchemy==1.2.12 SQLAlchemy==1.2.12
python-telegram-bot==11.1.0 python-telegram-bot==11.1.0
arrow==0.12.1 arrow==0.12.1
cachetools==2.1.0 cachetools==2.1.0
requests==2.19.1 requests==2.20.0
urllib3==1.22 urllib3==1.24
wrapt==1.10.11 wrapt==1.10.11
pandas==0.23.4 pandas==0.23.4
scikit-learn==0.19.2 scikit-learn==0.20.0
scipy==1.1.0 scipy==1.1.0
jsonschema==2.6.0 jsonschema==2.6.0
numpy==1.15.2 numpy==1.15.3
TA-Lib==0.4.17 TA-Lib==0.4.17
pytest==3.8.1 pytest==3.9.2
pytest-mock==1.10.0 pytest-mock==1.10.0
pytest-asyncio==0.9.0 pytest-asyncio==0.9.0
pytest-cov==2.6.0 pytest-cov==2.6.0

View File

@ -10,7 +10,14 @@ from freqtrade import arguments
from freqtrade.arguments import TimeRange from freqtrade.arguments import TimeRange
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.optimize import download_backtesting_testdata from freqtrade.optimize import download_backtesting_testdata
from freqtrade.configuration import set_loggers
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
)
set_loggers(0)
DEFAULT_DL_PATH = 'user_data/data' DEFAULT_DL_PATH = 'user_data/data'
@ -54,7 +61,9 @@ exchange = Exchange({'key': '',
'exchange': { 'exchange': {
'name': args.exchange, 'name': args.exchange,
'pair_whitelist': [], 'pair_whitelist': [],
'ccxt_rate_limit': False 'ccxt_async_config': {
"enableRateLimit": False
}
} }
}) })
pairs_not_available = [] pairs_not_available = []