Merge branch 'develop' into money_mgt
This commit is contained in:
commit
25daf3a0f7
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
|
||||||
```
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
""" FreqTrade bot """
|
""" FreqTrade bot """
|
||||||
__version__ = '0.17.2'
|
__version__ = '0.17.3'
|
||||||
|
|
||||||
|
|
||||||
class DependencyException(BaseException):
|
class DependencyException(BaseException):
|
||||||
|
@ -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
|
||||||
|
@ -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']
|
||||||
},
|
},
|
||||||
|
@ -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')
|
||||||
|
|
||||||
|
@ -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])
|
||||||
|
@ -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()
|
||||||
|
@ -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(
|
||||||
|
@ -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'),
|
||||||
|
@ -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`"
|
||||||
|
|
||||||
|
@ -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__)
|
||||||
|
|
||||||
|
0
freqtrade/tests/exchange/__init__.py
Normal file
0
freqtrade/tests/exchange/__init__.py
Normal 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)
|
||||||
|
0
freqtrade/tests/optimize/__init__.py
Normal file
0
freqtrade/tests/optimize/__init__.py
Normal 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],
|
||||||
|
@ -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'})
|
||||||
|
|
||||||
|
0
freqtrade/tests/rpc/__init__.py
Normal file
0
freqtrade/tests/rpc/__init__.py
Normal 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()
|
||||||
|
@ -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)
|
||||||
|
0
freqtrade/tests/strategy/__init__.py
Normal file
0
freqtrade/tests/strategy/__init__.py
Normal 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(
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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 = []
|
||||||
|
Loading…
Reference in New Issue
Block a user