Merge branch 'develop' into tsl_on_exchange
This commit is contained in:
commit
f4ceeca438
@ -1,4 +1,4 @@
|
||||
FROM python:3.7.0-slim-stretch
|
||||
FROM python:3.7.2-slim-stretch
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get -y install curl build-essential \
|
||||
|
@ -19,10 +19,10 @@ The table below will list all configuration parameters.
|
||||
| `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode.
|
||||
| `process_only_new_candles` | false | No | If set to true indicators are processed only once a new candle arrives. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. Can be set either in Configuration or in the strategy.
|
||||
| `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file.
|
||||
| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file.
|
||||
| `trailing_stop` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file).
|
||||
| `trailing_stop_positve` | 0 | No | Changes stop-loss once profit has been reached.
|
||||
| `trailing_stop_positve_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive.
|
||||
| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. More details in the [stoploss documentation](stoploss.md).
|
||||
| `trailing_stop` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). More details in the [stoploss documentation](stoploss.md).
|
||||
| `trailing_stop_positve` | 0 | No | Changes stop-loss once profit has been reached. More details in the [stoploss documentation](stoploss.md).
|
||||
| `trailing_stop_positve_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md).
|
||||
| `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled.
|
||||
| `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled.
|
||||
| `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below.
|
||||
@ -223,7 +223,7 @@ creating trades.
|
||||
|
||||
```json
|
||||
"dry_run": true,
|
||||
"db_url": "sqlite///tradesv3.dryrun.sqlite",
|
||||
"db_url": "sqlite:///tradesv3.dryrun.sqlite",
|
||||
```
|
||||
|
||||
3. Remove your Exchange API key (change them by fake api credentials)
|
||||
|
@ -6,6 +6,9 @@ At this stage the bot contains the following stoploss support modes:
|
||||
2. trailing stop loss, defined in the configuration
|
||||
3. trailing stop loss, custom positive loss, defined in configuration
|
||||
|
||||
!!! Note
|
||||
All stoploss properties can be configured in eihter Strategy or configuration. Configuration values override strategy values.
|
||||
|
||||
## Static Stop Loss
|
||||
|
||||
This is very simple, basically you define a stop loss of x in your strategy file or alternative in the configuration, which
|
||||
|
@ -124,9 +124,6 @@ class Configuration(object):
|
||||
if self.args.db_url and self.args.db_url != constants.DEFAULT_DB_PROD_URL:
|
||||
config.update({'db_url': self.args.db_url})
|
||||
logger.info('Parameter --db-url detected ...')
|
||||
else:
|
||||
# Set default here
|
||||
config.update({'db_url': constants.DEFAULT_DB_PROD_URL})
|
||||
|
||||
if config.get('dry_run', False):
|
||||
logger.info('Dry run is enabled')
|
||||
|
@ -59,7 +59,7 @@ class Edge():
|
||||
|
||||
# checking max_open_trades. it should be -1 as with Edge
|
||||
# the number of trades is determined by position size
|
||||
if self.config['max_open_trades'] != -1:
|
||||
if self.config['max_open_trades'] != float('inf'):
|
||||
logger.critical('max_open_trades should be -1 in config !')
|
||||
|
||||
if self.config['stake_amount'] != constants.UNLIMITED_STAKE_AMOUNT:
|
||||
|
@ -38,60 +38,43 @@ class StrategyResolver(IResolver):
|
||||
self.strategy: IStrategy = self._load_strategy(strategy_name,
|
||||
config=config,
|
||||
extra_dir=config.get('strategy_path'))
|
||||
|
||||
# Set attributes
|
||||
# Check if we need to override configuration
|
||||
if 'minimal_roi' in config:
|
||||
self.strategy.minimal_roi = config['minimal_roi']
|
||||
logger.info("Override strategy 'minimal_roi' with value in config file: %s.",
|
||||
config['minimal_roi'])
|
||||
else:
|
||||
config['minimal_roi'] = self.strategy.minimal_roi
|
||||
attributes = ["minimal_roi",
|
||||
"ticker_interval",
|
||||
"stoploss",
|
||||
"trailing_stop",
|
||||
"trailing_stop_positive",
|
||||
"trailing_stop_positive_offset",
|
||||
"process_only_new_candles",
|
||||
"order_types",
|
||||
"order_time_in_force"
|
||||
]
|
||||
for attribute in attributes:
|
||||
self._override_attribute_helper(config, attribute)
|
||||
|
||||
if 'stoploss' in config:
|
||||
self.strategy.stoploss = config['stoploss']
|
||||
logger.info(
|
||||
"Override strategy 'stoploss' with value in config file: %s.", config['stoploss']
|
||||
)
|
||||
else:
|
||||
config['stoploss'] = self.strategy.stoploss
|
||||
# Loop this list again to have output combined
|
||||
for attribute in attributes:
|
||||
if attribute in config:
|
||||
logger.info("Strategy using %s: %s", attribute, config[attribute])
|
||||
|
||||
if 'ticker_interval' in config:
|
||||
self.strategy.ticker_interval = config['ticker_interval']
|
||||
logger.info(
|
||||
"Override strategy 'ticker_interval' with value in config file: %s.",
|
||||
config['ticker_interval']
|
||||
)
|
||||
else:
|
||||
config['ticker_interval'] = self.strategy.ticker_interval
|
||||
# Sort and apply type conversions
|
||||
self.strategy.minimal_roi = OrderedDict(sorted(
|
||||
{int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(),
|
||||
key=lambda t: t[0]))
|
||||
self.strategy.stoploss = float(self.strategy.stoploss)
|
||||
|
||||
if 'process_only_new_candles' in config:
|
||||
self.strategy.process_only_new_candles = config['process_only_new_candles']
|
||||
logger.info(
|
||||
"Override process_only_new_candles 'process_only_new_candles' "
|
||||
"with value in config file: %s.", config['process_only_new_candles']
|
||||
)
|
||||
else:
|
||||
config['process_only_new_candles'] = self.strategy.process_only_new_candles
|
||||
self._strategy_sanity_validations()
|
||||
|
||||
if 'order_types' in config:
|
||||
self.strategy.order_types = config['order_types']
|
||||
logger.info(
|
||||
"Override strategy 'order_types' with value in config file: %s.",
|
||||
config['order_types']
|
||||
)
|
||||
else:
|
||||
config['order_types'] = self.strategy.order_types
|
||||
|
||||
if 'order_time_in_force' in config:
|
||||
self.strategy.order_time_in_force = config['order_time_in_force']
|
||||
logger.info(
|
||||
"Override strategy 'order_time_in_force' with value in config file: %s.",
|
||||
config['order_time_in_force']
|
||||
)
|
||||
else:
|
||||
config['order_time_in_force'] = self.strategy.order_time_in_force
|
||||
def _override_attribute_helper(self, config, attribute: str):
|
||||
if attribute in config:
|
||||
setattr(self.strategy, attribute, config[attribute])
|
||||
logger.info("Override strategy '%s' with value in config file: %s.",
|
||||
attribute, config[attribute])
|
||||
elif hasattr(self.strategy, attribute):
|
||||
config[attribute] = getattr(self.strategy, attribute)
|
||||
|
||||
def _strategy_sanity_validations(self):
|
||||
if not all(k in self.strategy.order_types for k in constants.REQUIRED_ORDERTYPES):
|
||||
raise ImportError(f"Impossible to load Strategy '{self.strategy.__class__.__name__}'. "
|
||||
f"Order-types mapping is incomplete.")
|
||||
@ -100,12 +83,6 @@ class StrategyResolver(IResolver):
|
||||
raise ImportError(f"Impossible to load Strategy '{self.strategy.__class__.__name__}'. "
|
||||
f"Order-time-in-force mapping is incomplete.")
|
||||
|
||||
# Sort and apply type conversions
|
||||
self.strategy.minimal_roi = OrderedDict(sorted(
|
||||
{int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(),
|
||||
key=lambda t: t[0]))
|
||||
self.strategy.stoploss = float(self.strategy.stoploss)
|
||||
|
||||
def _load_strategy(
|
||||
self, strategy_name: str, config: dict, extra_dir: Optional[str] = None) -> IStrategy:
|
||||
"""
|
||||
|
@ -67,6 +67,11 @@ class IStrategy(ABC):
|
||||
# associated stoploss
|
||||
stoploss: float
|
||||
|
||||
# trailing stoploss
|
||||
trailing_stop: bool = False
|
||||
trailing_stop_positive: float
|
||||
trailing_stop_positive_offset: float
|
||||
|
||||
# associated ticker interval
|
||||
ticker_interval: str
|
||||
|
||||
|
@ -150,6 +150,45 @@ def test_strategy_override_stoploss(caplog):
|
||||
) in caplog.record_tuples
|
||||
|
||||
|
||||
def test_strategy_override_trailing_stop(caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
config = {
|
||||
'strategy': 'DefaultStrategy',
|
||||
'trailing_stop': True
|
||||
}
|
||||
resolver = StrategyResolver(config)
|
||||
|
||||
assert resolver.strategy.trailing_stop
|
||||
assert isinstance(resolver.strategy.trailing_stop, bool)
|
||||
assert ('freqtrade.resolvers.strategy_resolver',
|
||||
logging.INFO,
|
||||
"Override strategy 'trailing_stop' with value in config file: True."
|
||||
) in caplog.record_tuples
|
||||
|
||||
|
||||
def test_strategy_override_trailing_stop_positive(caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
config = {
|
||||
'strategy': 'DefaultStrategy',
|
||||
'trailing_stop_positive': -0.1,
|
||||
'trailing_stop_positive_offset': -0.2
|
||||
|
||||
}
|
||||
resolver = StrategyResolver(config)
|
||||
|
||||
assert resolver.strategy.trailing_stop_positive == -0.1
|
||||
assert ('freqtrade.resolvers.strategy_resolver',
|
||||
logging.INFO,
|
||||
"Override strategy 'trailing_stop_positive' with value in config file: -0.1."
|
||||
) in caplog.record_tuples
|
||||
|
||||
assert resolver.strategy.trailing_stop_positive_offset == -0.2
|
||||
assert ('freqtrade.resolvers.strategy_resolver',
|
||||
logging.INFO,
|
||||
"Override strategy 'trailing_stop_positive' with value in config file: -0.1."
|
||||
) in caplog.record_tuples
|
||||
|
||||
|
||||
def test_strategy_override_ticker_interval(caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
|
||||
@ -178,8 +217,7 @@ def test_strategy_override_process_only_new_candles(caplog):
|
||||
assert resolver.strategy.process_only_new_candles
|
||||
assert ('freqtrade.resolvers.strategy_resolver',
|
||||
logging.INFO,
|
||||
"Override process_only_new_candles 'process_only_new_candles' "
|
||||
"with value in config file: True."
|
||||
"Override strategy 'process_only_new_candles' with value in config file: True."
|
||||
) in caplog.record_tuples
|
||||
|
||||
|
||||
|
@ -73,7 +73,6 @@ def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) ->
|
||||
args = Arguments([], '').get_parsed_arg()
|
||||
configuration = Configuration(args)
|
||||
validated_conf = configuration.load_config()
|
||||
print(validated_conf)
|
||||
|
||||
assert validated_conf['max_open_trades'] > 999999999
|
||||
assert validated_conf['max_open_trades'] == float('inf')
|
||||
@ -125,6 +124,43 @@ def test_load_config_with_params(default_conf, mocker) -> None:
|
||||
assert validated_conf.get('strategy_path') == '/some/path'
|
||||
assert validated_conf.get('db_url') == 'sqlite:///someurl'
|
||||
|
||||
# Test conf provided db_url prod
|
||||
conf = default_conf.copy()
|
||||
conf["dry_run"] = False
|
||||
conf["db_url"] = "sqlite:///path/to/db.sqlite"
|
||||
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||
read_data=json.dumps(conf)
|
||||
))
|
||||
|
||||
arglist = [
|
||||
'--strategy', 'TestStrategy',
|
||||
'--strategy-path', '/some/path'
|
||||
]
|
||||
args = Arguments(arglist, '').get_parsed_arg()
|
||||
|
||||
configuration = Configuration(args)
|
||||
validated_conf = configuration.load_config()
|
||||
assert validated_conf.get('db_url') == "sqlite:///path/to/db.sqlite"
|
||||
|
||||
# Test conf provided db_url dry_run
|
||||
conf = default_conf.copy()
|
||||
conf["dry_run"] = True
|
||||
conf["db_url"] = "sqlite:///path/to/db.sqlite"
|
||||
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||
read_data=json.dumps(conf)
|
||||
))
|
||||
|
||||
arglist = [
|
||||
'--strategy', 'TestStrategy',
|
||||
'--strategy-path', '/some/path'
|
||||
]
|
||||
args = Arguments(arglist, '').get_parsed_arg()
|
||||
|
||||
configuration = Configuration(args)
|
||||
validated_conf = configuration.load_config()
|
||||
assert validated_conf.get('db_url') == "sqlite:///path/to/db.sqlite"
|
||||
|
||||
# Test args provided db_url prod
|
||||
conf = default_conf.copy()
|
||||
conf["dry_run"] = False
|
||||
del conf["db_url"]
|
||||
@ -142,7 +178,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
|
||||
validated_conf = configuration.load_config()
|
||||
assert validated_conf.get('db_url') == DEFAULT_DB_PROD_URL
|
||||
|
||||
# Test dry=run with ProdURL
|
||||
# Test args provided db_url dry_run
|
||||
conf = default_conf.copy()
|
||||
conf["dry_run"] = True
|
||||
conf["db_url"] = DEFAULT_DB_PROD_URL
|
||||
|
14
mkdocs.yml
14
mkdocs.yml
@ -4,18 +4,18 @@ nav:
|
||||
- Installation: installation.md
|
||||
- Configuration: configuration.md
|
||||
- Start the bot: bot-usage.md
|
||||
- Optimization: bot-optimization.md
|
||||
- Stoploss: stoploss.md
|
||||
- Custom Strategy: bot-optimization.md
|
||||
- Telegram: telegram-usage.md
|
||||
- Web Hook: webhook-config.md
|
||||
- Backtesting: backtesting.md
|
||||
- Hyperopt: hyperopt.md
|
||||
- Stoploss: stoploss.md
|
||||
- Telegram: telegram-usage.md
|
||||
- Plotting: plotting.md
|
||||
- Edge positioning: edge.md
|
||||
- Web Hook: webhook-config.md
|
||||
- Plotting: plotting.md
|
||||
- FAQ: faq.md
|
||||
- Contributors guide: developer.md
|
||||
- SQL Cheatsheeet: sql_cheatsheet.md
|
||||
- SQL Cheatsheet: sql_cheatsheet.md
|
||||
- Sanbox testing: sandbox-testing.md
|
||||
- Contributors guide: developer.md
|
||||
theme:
|
||||
name: material
|
||||
logo: 'images/logo.png'
|
||||
|
@ -1,4 +1,4 @@
|
||||
ccxt==1.18.101
|
||||
ccxt==1.18.102
|
||||
SQLAlchemy==1.2.15
|
||||
python-telegram-bot==11.1.0
|
||||
arrow==0.12.1
|
||||
|
43
setup.sh
43
setup.sh
@ -17,23 +17,27 @@ function check_installed_python() {
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -z ${PYTHON} ]; then
|
||||
echo "No usable python found. Please make sure to have python3.6 or python3.7 installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
function updateenv() {
|
||||
echo "-------------------------"
|
||||
echo "Update your virtual env"
|
||||
echo "Updating your virtual env"
|
||||
echo "-------------------------"
|
||||
source .env/bin/activate
|
||||
echo "pip3 install in-progress. Please wait..."
|
||||
pip3 install --quiet --upgrade pip
|
||||
pip3 install --quiet -r requirements.txt --upgrade
|
||||
pip3 install --quiet -r requirements.txt
|
||||
# Install numpy first to have py_find_1st install clean
|
||||
pip3 install --upgrade pip numpy
|
||||
pip3 install --upgrade -r requirements.txt
|
||||
|
||||
read -p "Do you want to install dependencies for dev [Y/N]? "
|
||||
read -p "Do you want to install dependencies for dev [y/N]? "
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
pip3 install --quiet -r requirements-dev.txt --upgrade
|
||||
pip3 install --quiet -r requirements-dev.txt
|
||||
pip3 install --upgrade -r requirements-dev.txt
|
||||
else
|
||||
echo "Dev dependencies ignored."
|
||||
fi
|
||||
@ -45,6 +49,11 @@ function updateenv () {
|
||||
|
||||
# Install tab lib
|
||||
function install_talib() {
|
||||
if [ -f /usr/local/lib/libta_lib.a ]; then
|
||||
echo "ta-lib already installed, skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
cd build_helpers
|
||||
tar zxvf ta-lib-0.4.0-src.tar.gz
|
||||
cd ta-lib
|
||||
@ -61,7 +70,7 @@ function install_macos () {
|
||||
if [ ! -x "$(command -v brew)" ]
|
||||
then
|
||||
echo "-------------------------"
|
||||
echo "Install Brew"
|
||||
echo "Installing Brew"
|
||||
echo "-------------------------"
|
||||
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
fi
|
||||
@ -87,7 +96,7 @@ function update () {
|
||||
# Reset Develop or Master branch
|
||||
function reset() {
|
||||
echo "----------------------------"
|
||||
echo "Reset branch and virtual env"
|
||||
echo "Reseting branch and virtual env"
|
||||
echo "----------------------------"
|
||||
if [ "1" == $(git branch -vv |grep -cE "\* develop|\* master") ]
|
||||
then
|
||||
@ -133,7 +142,7 @@ function config_generator () {
|
||||
|
||||
echo "Starting to generate config.json"
|
||||
echo
|
||||
echo "General configuration"
|
||||
echo "Generating General configuration"
|
||||
echo "-------------------------"
|
||||
default_max_trades=3
|
||||
read -p "Max open trades: (Default: $default_max_trades) " max_trades
|
||||
@ -152,13 +161,13 @@ function config_generator () {
|
||||
fiat_currency=${fiat_currency:-$default_fiat_currency}
|
||||
|
||||
echo
|
||||
echo "Exchange config generator"
|
||||
echo "Generating exchange config "
|
||||
echo "------------------------"
|
||||
read -p "Exchange API key: " api_key
|
||||
read -p "Exchange API Secret: " api_secret
|
||||
|
||||
echo
|
||||
echo "Telegram config generator"
|
||||
echo "Generating Telegram config"
|
||||
echo "-------------------------"
|
||||
read -p "Telegram Token: " token
|
||||
read -p "Telegram Chat_id: " chat_id
|
||||
@ -178,11 +187,11 @@ function config_generator () {
|
||||
function config() {
|
||||
|
||||
echo "-------------------------"
|
||||
echo "Config file generator"
|
||||
echo "Generating config file"
|
||||
echo "-------------------------"
|
||||
if [ -f config.json ]
|
||||
then
|
||||
read -p "A config file already exist, do you want to override it [Y/N]? "
|
||||
read -p "A config file already exist, do you want to override it [y/N]? "
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
config_generator
|
||||
@ -203,7 +212,7 @@ function config () {
|
||||
|
||||
function install() {
|
||||
echo "-------------------------"
|
||||
echo "Install mandatory dependencies"
|
||||
echo "Installing mandatory dependencies"
|
||||
echo "-------------------------"
|
||||
|
||||
if [ "$(uname -s)" == "Darwin" ]
|
||||
@ -224,7 +233,7 @@ function install () {
|
||||
reset
|
||||
config
|
||||
echo "-------------------------"
|
||||
echo "Run the bot"
|
||||
echo "Run the bot !"
|
||||
echo "-------------------------"
|
||||
echo "You can now use the bot by executing 'source .env/bin/activate; python freqtrade/main.py'."
|
||||
}
|
||||
@ -232,7 +241,7 @@ function install () {
|
||||
function plot() {
|
||||
echo "
|
||||
-----------------------------------------
|
||||
Install dependencies for Plotting scripts
|
||||
Installing dependencies for Plotting scripts
|
||||
-----------------------------------------
|
||||
"
|
||||
pip install plotly --upgrade
|
||||
|
@ -42,6 +42,11 @@ class TestStrategy(IStrategy):
|
||||
# This attribute will be overridden if the config file contains "stoploss"
|
||||
stoploss = -0.10
|
||||
|
||||
# trailing stoploss
|
||||
trailing_stop = False
|
||||
trailing_stop_positive = 0.01
|
||||
trailing_stop_positive_offset = None # Disabled / not configured
|
||||
|
||||
# Optimal ticker interval for the strategy
|
||||
ticker_interval = '5m'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user