diff --git a/.gitignore b/.gitignore index 4720ff5cb..af87efb38 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,17 @@ # Freqtrade rules -config*.json +config_offline*.json +config_realmoney*.json + *.sqlite logfile.txt -user_data/* -!user_data/strategy/sample_strategy.py -!user_data/notebooks + +user_data/backtest_results/* +user_data/data/* +user_data/hyperopt_results/* +user_data/plot/* user_data/notebooks/* +user_data/hyperopt.lock + freqtrade-plot.html freqtrade-profit-plot.html freqtrade/rpc/api_server/ui/* @@ -95,3 +101,6 @@ target/ #exceptions !*.gitkeep + +#annoying and honestly idk +.DS_Store diff --git a/config_emptybinance_example.json b/config_emptybinance_example.json new file mode 100644 index 000000000..753e0498f --- /dev/null +++ b/config_emptybinance_example.json @@ -0,0 +1,106 @@ +{ + "max_open_trades": 20, + "stake_currency": "BTC", + "stake_amount": 50, + "tradable_balance_ratio": 0.99, + "fiat_display_currency": "USD", + "timeframe": "1m", + "dry_run": true, + "cancel_open_orders_on_exit": false, + "trailing_stop": false, + "unfilledtimeout": { + "buy": 10, + "sell": 30 + }, + "bid_strategy": { + "ask_last_balance": 0.0, + "use_order_book": false, + "order_book_top": 1, + "check_depth_of_market": { + "enabled": false, + "bids_to_ask_delta": 1 + } + }, + "ask_strategy": { + "use_order_book": false, + "order_book_min": 1, + "order_book_max": 9, + "use_sell_signal": true, + "sell_profit_only": false, + "ignore_roi_if_buy_signal": false + }, + "exchange": { + "name": "binance", + "key": "m46Wzbo7jE8ERx195Maf4BiDotYpV2GB6uLDXC82Hnp9adtnW13rEv3flwad1Ah4", + "secret": "zn5D6EsGK7nQ4Xrnr5d2CZjWdiNadNUK3j4IGcHDygioMhOM9Emd0v2NSvsKsPJv", + "ccxt_config": { + "enableRateLimit": true + }, + "ccxt_async_config": { + "enableRateLimit": true, + "rateLimit": 500 + }, + "pair_whitelist": [ + "ETH/BTC", + "LTC/BTC", + "ETC/BTC", + "XLM/BTC", + "ADA/BTC", + "XMR/BTC", + "BNB/BTC", + "QLC/BTC", + "KMD/BTC", + "WTC/BTC", + "TRX/BTC", + "XRP/BTC", + "EOS/BTC", + "NEO/BTC", + "LINK/BTC", + "ONT/BTC", + "XEM/BTC", + "ICX/BTC" + ], + "pair_blacklist": [ + "DOGE/BTC" + ] + }, + "pairlists": [ + { + "method": "StaticPairList" + } + ], + "edge": { + "enabled": false, + "process_throttle_secs": 3600, + "calculate_since_number_of_days": 7, + "allowed_risk": 0.01, + "stoploss_range_min": -0.01, + "stoploss_range_max": -0.1, + "stoploss_range_step": -0.01, + "minimum_winrate": 0.60, + "minimum_expectancy": 0.20, + "min_trade_number": 10, + "max_trade_duration_minute": 1440, + "remove_pumps": false + }, + "telegram": { + "enabled": true, + "token": "1080082600:AAHQOQI4oObA1P3Sf9408OJVSlMc7t5pxhI", + "chat_id": "1009222459" + }, + "api_server": { + "enabled": false, + "listen_ip_address": "127.0.0.1", + "listen_port": 8080, + "verbosity": "info", + "jwt_secret_key": "somethingrandom", + "username": "", + "password": "" + }, + "bot_name": "Tradingbot", + "initial_state": "running", + "forcebuy_enable": false, + "internals": { + "process_throttle_secs": 5 + } +} \ No newline at end of file diff --git a/freqtrade/optimize/hyperopt_loss_sharpe_trades100.py b/freqtrade/optimize/hyperopt_loss_sharpe_trades100.py new file mode 100644 index 000000000..3d0167dad --- /dev/null +++ b/freqtrade/optimize/hyperopt_loss_sharpe_trades100.py @@ -0,0 +1,85 @@ +""" +SharpeHyperOptLossTrades + +This module defines the alternative HyperOptLoss class which can be used for +Hyperoptimization. + +The MINIMUM_TRADES and SLIPPAGE_PER_TRADE_RATIO can be altered to whatever you like. +The values that make up the maximum trade_grade can be altered as well. +""" +import math +from datetime import datetime + +from pandas import DataFrame, date_range + +from freqtrade.optimize.hyperopt import IHyperOptLoss + + +class SharpeHyperOptLossTrades100(IHyperOptLoss): + """ + Defines the loss function for hyperopt. + + This implementation uses the Sharpe Ratio Daily calculation and the Trade Grade calculation. + """ + + @staticmethod + def hyperopt_loss_function(results: DataFrame, trade_count: int, + min_date: datetime, max_date: datetime, + *args, **kwargs) -> float: + """ + Objective function, returns smaller number for more optimal results. + + Uses the Sharpe Ratio Daily calculation and the Trade Grade calculation. + """ + + # CONSTANTS + MINIMUM_TRADES = 100 + SLIPPAGE_PER_TRADE_RATIO = 0.001 + NUMERATOR_MAX_TRADEGRADE = 80 + DENOMINATOR_MAX_TRADEGRADE = 8 + RESAMPLE_FREQ = '1D' + DAYS_IN_YEAR = 365 + ANNUAL_RISK_FREE_RATE = 0.0 + + risk_free_rate = ANNUAL_RISK_FREE_RATE / DAYS_IN_YEAR + + """ + Sharpe Ratio Calculation + """ + # apply slippage per trade to profit_percent + results.loc[:, 'profit_percent_after_slippage'] = \ + results['profit_percent'] - SLIPPAGE_PER_TRADE_RATIO + + # create the index within the min_date and end max_date + t_index = date_range(start=min_date, end=max_date, freq=RESAMPLE_FREQ, + normalize=True) + + sum_daily = ( + results.resample(RESAMPLE_FREQ, on='close_time').agg( + {"profit_percent_after_slippage": sum}).reindex(t_index).fillna(0) + ) + + total_profit = sum_daily["profit_percent_after_slippage"] - risk_free_rate + expected_returns_mean = total_profit.mean() + up_stdev = total_profit.std() + + if up_stdev != 0: + sharp_ratio = expected_returns_mean / up_stdev * math.sqrt(DAYS_IN_YEAR) + else: + # Define high (negative) sharpe ratio to be clear that this is NOT optimal. + sharp_ratio = -30. + + """ + Trade Grade Calculation + This function has a maximum grade of 80/DENOMINATOR_MAX_TRADEGRADE. + A minimum of 105 trades. + """ + + if trade_count <= (MINIMUM_TRADES + 5): + # Define high (negative) trade grade tp be clear that this is NOT optimal + trade_grade = -30 + else: + trade_grade = ((1 / (-0.001 * (trade_count - MINIMUM_TRADES))) + + NUMERATOR_MAX_TRADEGRADE) / DENOMINATOR_MAX_TRADEGRADE + + return -(sharp_ratio + trade_grade) diff --git a/freqtrade/optimize/hyperopt_loss_sharpe_trades1000.py b/freqtrade/optimize/hyperopt_loss_sharpe_trades1000.py new file mode 100644 index 000000000..8ac93cc2a --- /dev/null +++ b/freqtrade/optimize/hyperopt_loss_sharpe_trades1000.py @@ -0,0 +1,85 @@ +""" +SharpeHyperOptLossTrades + +This module defines the alternative HyperOptLoss class which can be used for +Hyperoptimization. + +The MINIMUM_TRADES and SLIPPAGE_PER_TRADE_RATIO can be altered to whatever you like. +The values that make up the maximum trade_grade can be altered as well. +""" +import math +from datetime import datetime + +from pandas import DataFrame, date_range + +from freqtrade.optimize.hyperopt import IHyperOptLoss + + +class SharpeHyperOptLossTrades1000(IHyperOptLoss): + """ + Defines the loss function for hyperopt. + + This implementation uses the Sharpe Ratio Daily calculation and the Trade Grade calculation. + """ + + @staticmethod + def hyperopt_loss_function(results: DataFrame, trade_count: int, + min_date: datetime, max_date: datetime, + *args, **kwargs) -> float: + """ + Objective function, returns smaller number for more optimal results. + + Uses the Sharpe Ratio Daily calculation and the Trade Grade calculation. + """ + + # CONSTANTS + MINIMUM_TRADES = 1000 + SLIPPAGE_PER_TRADE_RATIO = 0.001 + NUMERATOR_MAX_TRADEGRADE = 80 + DENOMINATOR_MAX_TRADEGRADE = 8 + RESAMPLE_FREQ = '1D' + DAYS_IN_YEAR = 365 + ANNUAL_RISK_FREE_RATE = 0.0 + + risk_free_rate = ANNUAL_RISK_FREE_RATE / DAYS_IN_YEAR + + """ + Sharpe Ratio Calculation + """ + # apply slippage per trade to profit_percent + results.loc[:, 'profit_percent_after_slippage'] = \ + results['profit_percent'] - SLIPPAGE_PER_TRADE_RATIO + + # create the index within the min_date and end max_date + t_index = date_range(start=min_date, end=max_date, freq=RESAMPLE_FREQ, + normalize=True) + + sum_daily = ( + results.resample(RESAMPLE_FREQ, on='close_time').agg( + {"profit_percent_after_slippage": sum}).reindex(t_index).fillna(0) + ) + + total_profit = sum_daily["profit_percent_after_slippage"] - risk_free_rate + expected_returns_mean = total_profit.mean() + up_stdev = total_profit.std() + + if up_stdev != 0: + sharp_ratio = expected_returns_mean / up_stdev * math.sqrt(DAYS_IN_YEAR) + else: + # Define high (negative) sharpe ratio to be clear that this is NOT optimal. + sharp_ratio = -30. + + """ + Trade Grade Calculation + This function has a maximum grade of 80/DENOMINATOR_MAX_TRADEGRADE. + A minimum of 1005 trades. + """ + + if trade_count <= (MINIMUM_TRADES + 5): + # Define high (negative) trade grade tp be clear that this is NOT optimal + trade_grade = -30 + else: + trade_grade = ((1 / (-0.001 * (trade_count - MINIMUM_TRADES))) + + NUMERATOR_MAX_TRADEGRADE) / DENOMINATOR_MAX_TRADEGRADE + + return -(sharp_ratio + trade_grade) diff --git a/freqtrade/optimize/hyperopt_loss_sharpe_trades20.py b/freqtrade/optimize/hyperopt_loss_sharpe_trades20.py new file mode 100644 index 000000000..bf3ff20ca --- /dev/null +++ b/freqtrade/optimize/hyperopt_loss_sharpe_trades20.py @@ -0,0 +1,85 @@ +""" +SharpeHyperOptLossTrades + +This module defines the alternative HyperOptLoss class which can be used for +Hyperoptimization. + +The MINIMUM_TRADES and SLIPPAGE_PER_TRADE_RATIO can be altered to whatever you like. +The values that make up the maximum trade_grade can be altered as well. +""" +import math +from datetime import datetime + +from pandas import DataFrame, date_range + +from freqtrade.optimize.hyperopt import IHyperOptLoss + + +class SharpeHyperOptLossTrades20(IHyperOptLoss): + """ + Defines the loss function for hyperopt. + + This implementation uses the Sharpe Ratio Daily calculation and the Trade Grade calculation. + """ + + @staticmethod + def hyperopt_loss_function(results: DataFrame, trade_count: int, + min_date: datetime, max_date: datetime, + *args, **kwargs) -> float: + """ + Objective function, returns smaller number for more optimal results. + + Uses the Sharpe Ratio Daily calculation and the Trade Grade calculation. + """ + + # CONSTANTS + MINIMUM_TRADES = 20 + SLIPPAGE_PER_TRADE_RATIO = 0.001 + NUMERATOR_MAX_TRADEGRADE = 80 + DENOMINATOR_MAX_TRADEGRADE = 8 + RESAMPLE_FREQ = '1D' + DAYS_IN_YEAR = 365 + ANNUAL_RISK_FREE_RATE = 0.0 + + risk_free_rate = ANNUAL_RISK_FREE_RATE / DAYS_IN_YEAR + + """ + Sharpe Ratio Calculation + """ + # apply slippage per trade to profit_percent + results.loc[:, 'profit_percent_after_slippage'] = \ + results['profit_percent'] - SLIPPAGE_PER_TRADE_RATIO + + # create the index within the min_date and end max_date + t_index = date_range(start=min_date, end=max_date, freq=RESAMPLE_FREQ, + normalize=True) + + sum_daily = ( + results.resample(RESAMPLE_FREQ, on='close_time').agg( + {"profit_percent_after_slippage": sum}).reindex(t_index).fillna(0) + ) + + total_profit = sum_daily["profit_percent_after_slippage"] - risk_free_rate + expected_returns_mean = total_profit.mean() + up_stdev = total_profit.std() + + if up_stdev != 0: + sharp_ratio = expected_returns_mean / up_stdev * math.sqrt(DAYS_IN_YEAR) + else: + # Define high (negative) sharpe ratio to be clear that this is NOT optimal. + sharp_ratio = -30. + + """ + Trade Grade Calculation + This function has a maximum grade of 80/DENOMINATOR_MAX_TRADEGRADE. + A minimum of 25 trades. + """ + + if trade_count <= (MINIMUM_TRADES + 5): + # Define high (negative) trade grade tp be clear that this is NOT optimal + trade_grade = -30 + else: + trade_grade = ((1 / (-0.001 * (trade_count - MINIMUM_TRADES))) + + NUMERATOR_MAX_TRADEGRADE) / DENOMINATOR_MAX_TRADEGRADE + + return -(sharp_ratio + trade_grade) diff --git a/freqtrade/optimize/hyperopt_loss_sharpe_trades250.py b/freqtrade/optimize/hyperopt_loss_sharpe_trades250.py new file mode 100644 index 000000000..f8b81f1fa --- /dev/null +++ b/freqtrade/optimize/hyperopt_loss_sharpe_trades250.py @@ -0,0 +1,85 @@ +""" +SharpeHyperOptLossTrades + +This module defines the alternative HyperOptLoss class which can be used for +Hyperoptimization. + +The MINIMUM_TRADES and SLIPPAGE_PER_TRADE_RATIO can be altered to whatever you like. +The values that make up the maximum trade_grade can be altered as well. +""" +import math +from datetime import datetime + +from pandas import DataFrame, date_range + +from freqtrade.optimize.hyperopt import IHyperOptLoss + + +class SharpeHyperOptLossTrades250(IHyperOptLoss): + """ + Defines the loss function for hyperopt. + + This implementation uses the Sharpe Ratio Daily calculation and the Trade Grade calculation. + """ + + @staticmethod + def hyperopt_loss_function(results: DataFrame, trade_count: int, + min_date: datetime, max_date: datetime, + *args, **kwargs) -> float: + """ + Objective function, returns smaller number for more optimal results. + + Uses the Sharpe Ratio Daily calculation and the Trade Grade calculation. + """ + + # CONSTANTS + MINIMUM_TRADES = 250 + SLIPPAGE_PER_TRADE_RATIO = 0.001 + NUMERATOR_MAX_TRADEGRADE = 80 + DENOMINATOR_MAX_TRADEGRADE = 8 + RESAMPLE_FREQ = '1D' + DAYS_IN_YEAR = 365 + ANNUAL_RISK_FREE_RATE = 0.0 + + risk_free_rate = ANNUAL_RISK_FREE_RATE / DAYS_IN_YEAR + + """ + Sharpe Ratio Calculation + """ + # apply slippage per trade to profit_percent + results.loc[:, 'profit_percent_after_slippage'] = \ + results['profit_percent'] - SLIPPAGE_PER_TRADE_RATIO + + # create the index within the min_date and end max_date + t_index = date_range(start=min_date, end=max_date, freq=RESAMPLE_FREQ, + normalize=True) + + sum_daily = ( + results.resample(RESAMPLE_FREQ, on='close_time').agg( + {"profit_percent_after_slippage": sum}).reindex(t_index).fillna(0) + ) + + total_profit = sum_daily["profit_percent_after_slippage"] - risk_free_rate + expected_returns_mean = total_profit.mean() + up_stdev = total_profit.std() + + if up_stdev != 0: + sharp_ratio = expected_returns_mean / up_stdev * math.sqrt(DAYS_IN_YEAR) + else: + # Define high (negative) sharpe ratio to be clear that this is NOT optimal. + sharp_ratio = -30. + + """ + Trade Grade Calculation + This function has a maximum grade of 80/DENOMINATOR_MAX_TRADEGRADE. + A minimum of 255 trades. + """ + + if trade_count <= (MINIMUM_TRADES + 5): + # Define high (negative) trade grade tp be clear that this is NOT optimal + trade_grade = -30 + else: + trade_grade = ((1 / (-0.001 * (trade_count - MINIMUM_TRADES))) + + NUMERATOR_MAX_TRADEGRADE) / DENOMINATOR_MAX_TRADEGRADE + + return -(sharp_ratio + trade_grade) diff --git a/freqtrade/optimize/hyperopt_loss_sharpe_trades50.py b/freqtrade/optimize/hyperopt_loss_sharpe_trades50.py new file mode 100644 index 000000000..854a92a52 --- /dev/null +++ b/freqtrade/optimize/hyperopt_loss_sharpe_trades50.py @@ -0,0 +1,85 @@ +""" +SharpeHyperOptLossTrades + +This module defines the alternative HyperOptLoss class which can be used for +Hyperoptimization. + +The MINIMUM_TRADES and SLIPPAGE_PER_TRADE_RATIO can be altered to whatever you like. +The values that make up the maximum trade_grade can be altered as well. +""" +import math +from datetime import datetime + +from pandas import DataFrame, date_range + +from freqtrade.optimize.hyperopt import IHyperOptLoss + + +class SharpeHyperOptLossTrades50(IHyperOptLoss): + """ + Defines the loss function for hyperopt. + + This implementation uses the Sharpe Ratio Daily calculation and the Trade Grade calculation. + """ + + @staticmethod + def hyperopt_loss_function(results: DataFrame, trade_count: int, + min_date: datetime, max_date: datetime, + *args, **kwargs) -> float: + """ + Objective function, returns smaller number for more optimal results. + + Uses the Sharpe Ratio Daily calculation and the Trade Grade calculation. + """ + + # CONSTANTS + MINIMUM_TRADES = 50 + SLIPPAGE_PER_TRADE_RATIO = 0.001 + NUMERATOR_MAX_TRADEGRADE = 80 + DENOMINATOR_MAX_TRADEGRADE = 8 + RESAMPLE_FREQ = '1D' + DAYS_IN_YEAR = 365 + ANNUAL_RISK_FREE_RATE = 0.0 + + risk_free_rate = ANNUAL_RISK_FREE_RATE / DAYS_IN_YEAR + + """ + Sharpe Ratio Calculation + """ + # apply slippage per trade to profit_percent + results.loc[:, 'profit_percent_after_slippage'] = \ + results['profit_percent'] - SLIPPAGE_PER_TRADE_RATIO + + # create the index within the min_date and end max_date + t_index = date_range(start=min_date, end=max_date, freq=RESAMPLE_FREQ, + normalize=True) + + sum_daily = ( + results.resample(RESAMPLE_FREQ, on='close_time').agg( + {"profit_percent_after_slippage": sum}).reindex(t_index).fillna(0) + ) + + total_profit = sum_daily["profit_percent_after_slippage"] - risk_free_rate + expected_returns_mean = total_profit.mean() + up_stdev = total_profit.std() + + if up_stdev != 0: + sharp_ratio = expected_returns_mean / up_stdev * math.sqrt(DAYS_IN_YEAR) + else: + # Define high (negative) sharpe ratio to be clear that this is NOT optimal. + sharp_ratio = -30. + + """ + Trade Grade Calculation + This function has a maximum grade of 80/DENOMINATOR_MAX_TRADEGRADE. + A minimum of 55 trades. + """ + + if trade_count <= (MINIMUM_TRADES + 5): + # Define high (negative) trade grade tp be clear that this is NOT optimal + trade_grade = -30 + else: + trade_grade = ((1 / (-0.001 * (trade_count - MINIMUM_TRADES))) + + NUMERATOR_MAX_TRADEGRADE) / DENOMINATOR_MAX_TRADEGRADE + + return -(sharp_ratio + trade_grade) diff --git a/freqtrade/optimize/hyperopt_loss_sharpe_trades_custom.py b/freqtrade/optimize/hyperopt_loss_sharpe_trades_custom.py new file mode 100644 index 000000000..6b001502c --- /dev/null +++ b/freqtrade/optimize/hyperopt_loss_sharpe_trades_custom.py @@ -0,0 +1,85 @@ +""" +SharpeHyperOptLossTrades + +This module defines the alternative HyperOptLoss class which can be used for +Hyperoptimization. + +The MINIMUM_TRADES and SLIPPAGE_PER_TRADE_RATIO can be altered to whatever you like. +The values that make up the maximum trade_grade can be altered as well. +""" +import math +from datetime import datetime + +from pandas import DataFrame, date_range + +from freqtrade.optimize.hyperopt import IHyperOptLoss + + +class SharpeHyperOptLossTradesCustom(IHyperOptLoss): + """ + Defines the loss function for hyperopt. + + This implementation uses the Sharpe Ratio Daily calculation and the Trade Grade calculation. + """ + + @staticmethod + def hyperopt_loss_function(results: DataFrame, trade_count: int, + min_date: datetime, max_date: datetime, + *args, **kwargs) -> float: + """ + Objective function, returns smaller number for more optimal results. + + Uses the Sharpe Ratio Daily calculation and the Trade Grade calculation. + """ + + # CONSTANTS + MINIMUM_TRADES = 0 + SLIPPAGE_PER_TRADE_RATIO = 0.001 + NUMERATOR_MAX_TRADEGRADE = 80 + DENOMINATOR_MAX_TRADEGRADE = 8 + RESAMPLE_FREQ = '1D' + DAYS_IN_YEAR = 365 + ANNUAL_RISK_FREE_RATE = 0.0 + + risk_free_rate = ANNUAL_RISK_FREE_RATE / DAYS_IN_YEAR + + """ + Sharpe Ratio Calculation + """ + # apply slippage per trade to profit_percent + results.loc[:, 'profit_percent_after_slippage'] = \ + results['profit_percent'] - SLIPPAGE_PER_TRADE_RATIO + + # create the index within the min_date and end max_date + t_index = date_range(start=min_date, end=max_date, freq=RESAMPLE_FREQ, + normalize=True) + + sum_daily = ( + results.resample(RESAMPLE_FREQ, on='close_time').agg( + {"profit_percent_after_slippage": sum}).reindex(t_index).fillna(0) + ) + + total_profit = sum_daily["profit_percent_after_slippage"] - risk_free_rate + expected_returns_mean = total_profit.mean() + up_stdev = total_profit.std() + + if up_stdev != 0: + sharp_ratio = expected_returns_mean / up_stdev * math.sqrt(DAYS_IN_YEAR) + else: + # Define high (negative) sharpe ratio to be clear that this is NOT optimal. + sharp_ratio = -30. + + """ + Trade Grade Calculation + This function has a maximum grade of 80/DENOMINATOR_MAX_TRADEGRADE. + A minimum of ... trades. + """ + + if trade_count <= (MINIMUM_TRADES + 5): + # Define high (negative) trade grade tp be clear that this is NOT optimal + trade_grade = -30 + else: + trade_grade = ((1 / (-0.001 * (trade_count - MINIMUM_TRADES))) + + NUMERATOR_MAX_TRADEGRADE) / DENOMINATOR_MAX_TRADEGRADE + + return -(sharp_ratio + trade_grade) diff --git a/user_data/hyperopts/BasicHyperopt.py b/user_data/hyperopts/BasicHyperopt.py new file mode 100644 index 000000000..9ffc507d4 --- /dev/null +++ b/user_data/hyperopts/BasicHyperopt.py @@ -0,0 +1,293 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +# --- Do not remove these libs --- +from functools import reduce +from typing import Any, Callable, Dict, List + +import numpy # noqa +import pandas # noqa +from pandas import DataFrame +from skopt.space import Categorical, Dimension, Integer, Real # noqa + +from freqtrade.optimize.hyperopt_interface import IHyperOpt + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta # noqa +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class BasicHyperopt(IHyperOpt): + """ + This is a Hyperopt template to get you started. + + More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/ + + You should: + - Add any lib you need to build your hyperopt. + + You must keep: + - The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator. + + The methods roi_space, generate_roi_table and stoploss_space are not required + and are provided by default. + However, you may override them if you need 'roi' and 'stoploss' spaces that + differ from the defaults offered by Freqtrade. + Sample implementation of these methods will be copied to `user_data/hyperopts` when + creating the user-data directory using `freqtrade create-userdir --userdir user_data`, + or is available online under the following URL: + https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py. + """ + + @staticmethod + def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Bollinger Bands stds 2 + bollinger2 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband2'] = bollinger2['lower'] + dataframe['bb_middleband2'] = bollinger2['mid'] + dataframe['bb_upperband2'] = bollinger2['upper'] + + return dataframe + + @staticmethod + def buy_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the buy strategy parameters to be used by Hyperopt. + """ + def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Buy strategy Hyperopt will build and use. + """ + conditions = [] + + # GUARDS AND TRENDS + if params.get('mfi-enabled'): + conditions.append(dataframe['mfi'] < params['mfi-value']) + if params.get('fastd-enabled'): + conditions.append(dataframe['fastd'] < params['fastd-value']) + if params.get('adx-enabled'): + conditions.append(dataframe['adx'] > params['adx-value']) + if params.get('rsi-enabled'): + conditions.append(dataframe['rsi'] < params['rsi-value']) + + # TRIGGERS + if 'trigger' in params: + if params['trigger'] == 'bb_lower': + conditions.append(dataframe['close'] < dataframe['bb_lowerband']) + if params['trigger'] == 'macd_cross_signal': + conditions.append(qtpylib.crossed_above( + dataframe['macd'], dataframe['macdsignal'] + )) + if params['trigger'] == 'sar_reversal': + conditions.append(qtpylib.crossed_above( + dataframe['close'], dataframe['sar'] + )) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy'] = 1 + + return dataframe + + return populate_buy_trend + + @staticmethod + def indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching buy strategy parameters. + """ + return [ + Integer(10, 25, name='mfi-value'), + Integer(15, 45, name='fastd-value'), + Integer(20, 50, name='adx-value'), + Integer(20, 40, name='rsi-value'), + Categorical([True, False], name='mfi-enabled'), + Categorical([True, False], name='fastd-enabled'), + Categorical([True, False], name='adx-enabled'), + Categorical([True, False], name='rsi-enabled'), + Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger') + ] + + @staticmethod + def sell_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the sell strategy parameters to be used by Hyperopt. + """ + def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Sell strategy Hyperopt will build and use. + """ + conditions = [] + + # GUARDS AND TRENDS + if params.get('sell-mfi-enabled'): + conditions.append(dataframe['mfi'] > params['sell-mfi-value']) + if params.get('sell-fastd-enabled'): + conditions.append(dataframe['fastd'] > params['sell-fastd-value']) + if params.get('sell-adx-enabled'): + conditions.append(dataframe['adx'] < params['sell-adx-value']) + if params.get('sell-rsi-enabled'): + conditions.append(dataframe['rsi'] > params['sell-rsi-value']) + + # TRIGGERS + if 'sell-trigger' in params: + if params['sell-trigger'] == 'sell-bb_upper': + conditions.append(dataframe['close'] > dataframe['bb_upperband']) + if params['sell-trigger'] == 'sell-macd_cross_signal': + conditions.append(qtpylib.crossed_above( + dataframe['macdsignal'], dataframe['macd'] + )) + if params['sell-trigger'] == 'sell-sar_reversal': + conditions.append(qtpylib.crossed_above( + dataframe['sar'], dataframe['close'] + )) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell'] = 1 + + return dataframe + + return populate_sell_trend + + @staticmethod + def sell_indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching sell strategy parameters. + """ + return [ + Integer(75, 100, name='sell-mfi-value'), + Integer(50, 100, name='sell-fastd-value'), + Integer(50, 100, name='sell-adx-value'), + Integer(60, 100, name='sell-rsi-value'), + Categorical([True, False], name='sell-mfi-enabled'), + Categorical([True, False], name='sell-fastd-enabled'), + Categorical([True, False], name='sell-adx-enabled'), + Categorical([True, False], name='sell-rsi-enabled'), + Categorical(['sell-bb_upper', + 'sell-macd_cross_signal', + 'sell-sar_reversal'], name='sell-trigger') + ] + + @staticmethod + def generate_roi_table(params: Dict) -> Dict[int, float]: + """ + Generate the ROI table that will be used by Hyperopt + This implementation generates the default legacy Freqtrade ROI tables. + Change it if you need different number of steps in the generated + ROI tables or other structure of the ROI tables. + Please keep it aligned with parameters in the 'roi' optimization + hyperspace defined by the roi_space method. + """ + roi_table = {} + roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3'] + roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2'] + roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1'] + roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0 + + return roi_table + + @staticmethod + def roi_space() -> List[Dimension]: + """ + Values to search for each ROI steps + Override it if you need some different ranges for the parameters in the + 'roi' optimization hyperspace. + Please keep it aligned with the implementation of the + generate_roi_table method. + """ + return [ + Integer(10, 120, name='roi_t1'), + Integer(10, 60, name='roi_t2'), + Integer(10, 40, name='roi_t3'), + Real(0.01, 0.04, name='roi_p1'), + Real(0.01, 0.07, name='roi_p2'), + Real(0.01, 0.20, name='roi_p3'), + ] + + @staticmethod + def stoploss_space() -> List[Dimension]: + """ + Stoploss Value to search + Override it if you need some different range for the parameter in the + 'stoploss' optimization hyperspace. + """ + return [ + Real(-0.35, -0.02, name='stoploss'), + ] + + @staticmethod + def trailing_space() -> List[Dimension]: + """ + Create a trailing stoploss space. + You may override it in your custom Hyperopt class. + """ + return [ + # It was decided to always set trailing_stop is to True if the 'trailing' hyperspace + # is used. Otherwise hyperopt will vary other parameters that won't have effect if + # trailing_stop is set False. + # This parameter is included into the hyperspace dimensions rather than assigning + # it explicitly in the code in order to have it printed in the results along with + # other 'trailing' hyperspace parameters. + Categorical([True], name='trailing_stop'), + + Real(0.01, 0.35, name='trailing_stop_positive'), + + # 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive', + # so this intermediate parameter is used as the value of the difference between + # them. The value of the 'trailing_stop_positive_offset' is constructed in the + # generate_trailing_params() method. + # This is similar to the hyperspace dimensions used for constructing the ROI tables. + Real(0.001, 0.1, name='trailing_stop_positive_offset_p1'), + + Categorical([True, False], name='trailing_only_offset_is_reached'), + ] + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators. + Can be a copy of the corresponding method from the strategy, + or will be loaded from the strategy. + Must align to populate_indicators used (either from this File, or from the strategy) + Only used when --spaces does not include buy + """ + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) & + (dataframe['mfi'] < 16) & + (dataframe['adx'] > 25) & + (dataframe['rsi'] < 21) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators. + Can be a copy of the corresponding method from the strategy, + or will be loaded from the strategy. + Must align to populate_indicators used (either from this File, or from the strategy) + Only used when --spaces does not include sell + """ + dataframe.loc[ + ( + (qtpylib.crossed_above( + dataframe['macdsignal'], dataframe['macd'] + )) & + (dataframe['fastd'] > 54) + ), + 'sell'] = 1 + return dataframe diff --git a/user_data/hyperopts/bbrsiopt.py b/user_data/hyperopts/bbrsiopt.py new file mode 100644 index 000000000..ab758a1d7 --- /dev/null +++ b/user_data/hyperopts/bbrsiopt.py @@ -0,0 +1,238 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +# --- Do not remove these libs --- +from functools import reduce +from typing import Any, Callable, Dict, List + +import numpy # noqa +import pandas # noqa +from pandas import DataFrame +from skopt.space import Categorical, Dimension, Integer, Real # noqa + +from freqtrade.optimize.hyperopt_interface import IHyperOpt + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta # noqa +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class BBRSIopt(IHyperOpt): + @staticmethod + def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Bollinger Bands stds 1-4 + bollinger1 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=1) + dataframe['bb_lowerband1'] = bollinger1['lower'] + dataframe['bb_middleband'] = bollinger1['mid'] + dataframe['bb_upperband1'] = bollinger1['upper'] + + bollinger2 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband2'] = bollinger2['lower'] + dataframe['bb_upperband2'] = bollinger2['upper'] + + bollinger3 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=3) + dataframe['bb_lowerband3'] = bollinger3['lower'] + dataframe['bb_upperband3'] = bollinger3['upper'] + + bollinger4 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=4) + dataframe['bb_lowerband4'] = bollinger4['lower'] + dataframe['bb_upperband4'] = bollinger4['upper'] + + return dataframe + + @staticmethod + def buy_strategy_generator(params: Dict[str, Any]) -> Callable: + + def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = [] + + # GUARDS AND TRENDS + if params.get('rsi-enabled'): + conditions.append(dataframe['rsi'] > params['rsi-value']) + + # TRIGGERS + if 'trigger' in params: + if params['trigger'] == 'bb_lower1': + conditions.append(dataframe['close'] < dataframe['bb_lowerband1']) + if params['trigger'] == 'bb_lower2': + conditions.append(dataframe['close'] < dataframe['bb_lowerband2']) + if params['trigger'] == 'bb_lower3': + conditions.append(dataframe['close'] < dataframe['bb_lowerband3']) + if params['trigger'] == 'bb_lower4': + conditions.append(dataframe['close'] < dataframe['bb_lowerband4']) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy'] = 1 + + return dataframe + + return populate_buy_trend + + @staticmethod + def indicator_space() -> List[Dimension]: + + return [ + Integer(5, 50, name='rsi-value'), + Categorical([True, False], name='rsi-enabled'), + Categorical(['bb_lower1', 'bb_lower2', 'bb_lower3', 'bb_lower4'], name='trigger') + ] + + @staticmethod + def sell_strategy_generator(params: Dict[str, Any]) -> Callable: + + def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = [] + + # GUARDS AND TRENDS + if params.get('sell-rsi-enabled'): + conditions.append(dataframe['rsi'] > params['sell-rsi-value']) + + # TRIGGERS + if 'sell-trigger' in params: + # All bollinger bands with stds 1-4 + if params['sell-trigger'] == 'sell-bb_lower3': + conditions.append(dataframe['close'] > dataframe['bb_lowerband3']) + if params['sell-trigger'] == 'sell-bb_lower2': + conditions.append(dataframe['close'] > dataframe['bb_lowerband2']) + if params['sell-trigger'] == 'sell-bb_lower1': + conditions.append(dataframe['close'] > dataframe['bb_lowerband1']) + if params['sell-trigger'] == 'sell-bb_middle': + conditions.append(dataframe['close'] > dataframe['bb_middleband']) + if params['sell-trigger'] == 'sell-bb_upper1': + conditions.append(dataframe['close'] > dataframe['bb_upperband1']) + if params['sell-trigger'] == 'sell-bb_upper2': + conditions.append(dataframe['close'] > dataframe['bb_upperband2']) + if params['sell-trigger'] == 'sell-bb_upper3': + conditions.append(dataframe['close'] > dataframe['bb_upperband3']) + if params['sell-trigger'] == 'sell-bb_upper4': + conditions.append(dataframe['close'] > dataframe['bb_upperband4']) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell'] = 1 + + return dataframe + + return populate_sell_trend + + @staticmethod + def sell_indicator_space() -> List[Dimension]: + + return [ + Integer(5, 100, name='sell-rsi-value'), + Categorical([True, False], name='sell-rsi-enabled'), + Categorical(['sell-bb_lower3', + 'sell-bb_lower2', + 'sell-bb_lower1', + 'sell-bb_middle', + 'sell-bb_upper1', + 'sell-bb_upper2', + 'sell-bb_upper3', + 'sell-bb_upper4', ], name='sell-trigger') + ] + + @staticmethod + def generate_roi_table(params: Dict) -> Dict[int, float]: + # did not touch + """ + Generate the ROI table that will be used by Hyperopt + This implementation generates the default legacy Freqtrade ROI tables. + Change it if you need different number of steps in the generated + ROI tables or other structure of the ROI tables. + Please keep it aligned with parameters in the 'roi' optimization + hyperspace defined by the roi_space method. + """ + roi_table = {} + roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3'] + roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2'] + roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1'] + roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0 + + return roi_table + + @staticmethod + def roi_space() -> List[Dimension]: + # did not touch + """ + Values to search for each ROI steps + Override it if you need some different ranges for the parameters in the + 'roi' optimization hyperspace. + Please keep it aligned with the implementation of the + generate_roi_table method. + """ + return [ + Integer(10, 120, name='roi_t1'), + Integer(10, 60, name='roi_t2'), + Integer(10, 40, name='roi_t3'), + Real(0.01, 0.04, name='roi_p1'), + Real(0.01, 0.07, name='roi_p2'), + Real(0.01, 0.20, name='roi_p3'), + ] + + @staticmethod + def stoploss_space() -> List[Dimension]: + return [ + Real(-0.50, -0.02, name='stoploss'), + ] + + @staticmethod + def trailing_space() -> List[Dimension]: + # did not touch + """ + Create a trailing stoploss space. + You may override it in your custom Hyperopt class. + """ + return [ + # It was decided to always set trailing_stop is to True if the 'trailing' hyperspace + # is used. Otherwise hyperopt will vary other parameters that won't have effect if + # trailing_stop is set False. + # This parameter is included into the hyperspace dimensions rather than assigning + # it explicitly in the code in order to have it printed in the results along with + # other 'trailing' hyperspace parameters. + Categorical([True], name='trailing_stop'), + + Real(0.01, 0.35, name='trailing_stop_positive'), + + # 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive', + # so this intermediate parameter is used as the value of the difference between + # them. The value of the 'trailing_stop_positive_offset' is constructed in the + # generate_trailing_params() method. + # This is similar to the hyperspace dimensions used for constructing the ROI tables. + Real(0.001, 0.1, name='trailing_stop_positive_offset_p1'), + + Categorical([True, False], name='trailing_only_offset_is_reached'), + ] + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # copy paste from bbrsi.py + dataframe.loc[ + ( + (dataframe['rsi'] > 30) & # RSI above 30 + (dataframe['close'] < dataframe['bb_lowerband']) # close price under low bb + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # copy paste from bbrsi.py + dataframe.loc[ + ( + (dataframe['close'] > dataframe['bb_middleband']) # close price above the middle bb + ), + 'sell'] = 1 + return dataframe diff --git a/user_data/hyperopts/examplefreqtrade1opt.py b/user_data/hyperopts/examplefreqtrade1opt.py new file mode 100644 index 000000000..a688f5415 --- /dev/null +++ b/user_data/hyperopts/examplefreqtrade1opt.py @@ -0,0 +1,247 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +# --- Do not remove these libs --- +from functools import reduce +from typing import Any, Callable, Dict, List + +import numpy # noqa +import pandas # noqa +from pandas import DataFrame +from skopt.space import Categorical, Dimension, Integer, Real # noqa + +from freqtrade.optimize.hyperopt_interface import IHyperOpt + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta # noqa +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class Examplestrategy1opt(IHyperOpt): + @staticmethod + def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe['ema20'] = ta.EMA(dataframe, timeperiod=20) + dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + heikinashi = qtpylib.heikinashi(dataframe) + dataframe['ha_open'] = heikinashi['open'] + dataframe['ha_close'] = heikinashi['close'] + + return dataframe + + @staticmethod + def buy_strategy_generator(params: Dict[str, Any]) -> Callable: + + def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = [] + + # GUARDS AND TRENDS + if params.get('ema20-enabled'): + conditions.append(dataframe['ha_close'] > dataframe['ema20']) + if params.get('ema50-enabled'): + conditions.append(dataframe['ha_close'] > dataframe['ema50']) + if params.get('greenbar-enabled'): + conditions.append(dataframe['ha_close'] > dataframe['ha_open']) + + # TRIGGERS + if 'trigger' in params: + if params['trigger'] == 'ema2050_crossabove': + conditions.append(qtpylib.crossed_above(dataframe['ema20'], dataframe['ema50'])) + if params['trigger'] == 'ema50100_crossabove': + conditions.append(qtpylib.crossed_above( + dataframe['ema50'], dataframe['ema100'])) + if params['trigger'] == 'ema20100_crossabove': + conditions.append(qtpylib.crossed_above( + dataframe['ema20'], dataframe['ema100'])) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy'] = 1 + + return dataframe + + return populate_buy_trend + + @staticmethod + def indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching buy strategy parameters. + """ + return [ + Categorical([True, False], name='ema20-enabled'), + Categorical([True, False], name='ema50-enabled'), + Categorical([True, False], name='greenbar-enabled'), + Categorical(['ema2050_crossabove', 'ema50100_crossabove', + 'ema20100_crossabove'], name='trigger') + ] + + @staticmethod + def sell_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the sell strategy parameters to be used by Hyperopt. + """ + def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Sell strategy Hyperopt will build and use. + """ + conditions = [] + + # GUARDS AND TRENDS + if params.get('sell-ema20-enabled'): + conditions.append(dataframe['ha_close'] < dataframe['ema20']) + if params.get('sell-ema50-enabled'): + conditions.append(dataframe['ha_close'] < dataframe['ema50']) + if params.get('sell-redbar-enabled'): + conditions.append(dataframe['ha_close'] < dataframe['ha_open']) + + # TRIGGERS + if 'sell-trigger' in params: + if params['sell-trigger'] == 'sell-ema2050_crossbelow': + conditions.append(qtpylib.crossed_below(dataframe['ema20'], dataframe['ema50'])) + if params['sell-trigger'] == 'sell-ema50100_crossbelow': + conditions.append(qtpylib.crossed_below( + dataframe['ema50'], dataframe['ema100'])) + if params['sell-trigger'] == 'sell-ema20100_crossbelow': + conditions.append(qtpylib.crossed_below( + dataframe['ema20'], dataframe['ema100'])) + if params['sell-trigger'] == 'sell-ema50100_crossabove': + conditions.append(qtpylib.crossed_above( + dataframe['ema50'], dataframe['ema100'])) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell'] = 1 + + return dataframe + + return populate_sell_trend + + @staticmethod + def sell_indicator_space() -> List[Dimension]: + + return [ + Categorical([True, False], name='sell-ema20-enabled'), + Categorical([True, False], name='sell-ema50-enabled'), + Categorical([True, False], name='sell-redbar-enabled'), + Categorical(['sell-ema2050_crossbelow', + 'sell-ema50100_crossbelow', + 'sell-ema20100_crossbelow', + 'sell-ema50100_crossabove'], name='sell-trigger') + ] + + @staticmethod + def generate_roi_table(params: Dict) -> Dict[int, float]: + """ + Generate the ROI table that will be used by Hyperopt + This implementation generates the default legacy Freqtrade ROI tables. + Change it if you need different number of steps in the generated + ROI tables or other structure of the ROI tables. + Please keep it aligned with parameters in the 'roi' optimization + hyperspace defined by the roi_space method. + """ + roi_table = {} + roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3'] + roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2'] + roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1'] + roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0 + + return roi_table + + @staticmethod + def roi_space() -> List[Dimension]: + """ + Values to search for each ROI steps + Override it if you need some different ranges for the parameters in the + 'roi' optimization hyperspace. + Please keep it aligned with the implementation of the + generate_roi_table method. + """ + return [ + Integer(10, 120, name='roi_t1'), + Integer(10, 60, name='roi_t2'), + Integer(10, 40, name='roi_t3'), + Real(0.01, 0.04, name='roi_p1'), + Real(0.01, 0.07, name='roi_p2'), + Real(0.01, 0.20, name='roi_p3'), + ] + + @staticmethod + def stoploss_space() -> List[Dimension]: + """ + Stoploss Value to search + Override it if you need some different range for the parameter in the + 'stoploss' optimization hyperspace. + """ + return [ + Real(-0.5, -0.02, name='stoploss'), + ] + + @staticmethod + def trailing_space() -> List[Dimension]: + """ + Create a trailing stoploss space. + You may override it in your custom Hyperopt class. + """ + return [ + # It was decided to always set trailing_stop is to True if the 'trailing' hyperspace + # is used. Otherwise hyperopt will vary other parameters that won't have effect if + # trailing_stop is set False. + # This parameter is included into the hyperspace dimensions rather than assigning + # it explicitly in the code in order to have it printed in the results along with + # other 'trailing' hyperspace parameters. + Categorical([True], name='trailing_stop'), + + Real(0.01, 0.35, name='trailing_stop_positive'), + + # 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive', + # so this intermediate parameter is used as the value of the difference between + # them. The value of the 'trailing_stop_positive_offset' is constructed in the + # generate_trailing_params() method. + # This is similar to the hyperspace dimensions used for constructing the ROI tables. + Real(0.001, 0.1, name='trailing_stop_positive_offset_p1'), + + Categorical([True, False], name='trailing_only_offset_is_reached'), + ] + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + qtpylib.crossed_above(dataframe['ema20'], dataframe['ema50']) & + (dataframe['ha_close'] > dataframe['ema20']) & + (dataframe['ha_open'] < dataframe['ha_close']) # green bar + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + qtpylib.crossed_above(dataframe['ema50'], dataframe['ema100']) & + (dataframe['ha_close'] < dataframe['ema20']) & + (dataframe['ha_open'] > dataframe['ha_close']) # red bar + ), + 'sell'] = 1 + + return dataframe diff --git a/user_data/hyperopts/examplefreqtrade2opt.py b/user_data/hyperopts/examplefreqtrade2opt.py new file mode 100644 index 000000000..7275695a8 --- /dev/null +++ b/user_data/hyperopts/examplefreqtrade2opt.py @@ -0,0 +1,281 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +# --- Do not remove these libs --- +from functools import reduce +from typing import Any, Callable, Dict, List + +import numpy # noqa +import pandas # noqa +from pandas import DataFrame +from skopt.space import Categorical, Dimension, Integer, Real # noqa + +from freqtrade.optimize.hyperopt_interface import IHyperOpt + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta # noqa +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class Examplestrategy2opt(IHyperOpt): + @staticmethod + def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Stoch + stoch = ta.STOCH(dataframe) + dataframe['slowk'] = stoch['slowk'] + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) + rsi = 0.1 * (dataframe['rsi'] - 50) + dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) + + # Bollinger bands + bollinger1 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=1) + dataframe['bb_lowerband1'] = bollinger1['lower'] + dataframe['bb_middleband'] = bollinger1['mid'] + dataframe['bb_upperband1'] = bollinger1['upper'] + + bollinger2 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband2'] = bollinger2['lower'] + dataframe['bb_upperband2'] = bollinger2['upper'] + + bollinger3 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=3) + dataframe['bb_lowerband3'] = bollinger3['lower'] + dataframe['bb_upperband3'] = bollinger3['upper'] + + bollinger4 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=4) + dataframe['bb_lowerband4'] = bollinger4['lower'] + dataframe['bb_upperband4'] = bollinger4['upper'] + + # SAR Parabol + dataframe['sar'] = ta.SAR(dataframe) + + # Hammer: values [0, 100] + dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe) + + return dataframe + + @staticmethod + def buy_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the buy strategy parameters to be used by Hyperopt. + """ + def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Buy strategy Hyperopt will build and use. + """ + conditions = [] + + # GUARDS AND TRENDS + if params.get('rsi-enabled'): + conditions.append(dataframe['rsi'] < params['rsi-value']) + + if params.get('slowk-enabled'): + conditions.append(dataframe['slowk'] < params['slowk-value']) + + if params.get('Hammer-enabled'): + conditions.append(dataframe['CDLHAMMER'] < params['Hammer-value']) + + # TRIGGERS + if 'trigger' in params: + if params['trigger'] == 'bb_lower1': + conditions.append(dataframe['close'] < dataframe['bb_lowerband1']) + if params['trigger'] == 'bb_lower2': + conditions.append(dataframe['close'] < dataframe['bb_lowerband2']) + if params['trigger'] == 'bb_lower3': + conditions.append(dataframe['close'] < dataframe['bb_lowerband3']) + if params['trigger'] == 'bb_lower4': + conditions.append(dataframe['close'] < dataframe['bb_lowerband4']) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy'] = 1 + + return dataframe + + return populate_buy_trend + + @staticmethod + def indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching buy strategy parameters. + """ + return [ + Integer(0, 100, name='slowk-value'), + Integer(20, 80, name='rsi-value'), + Integer(0, 100, name='Hammer-value'), + Categorical([True, False], name='slowk-enabled'), + Categorical([True, False], name='rsi-enabled'), + Categorical([True, False], name='Hammer-enabled'), + Categorical(['bb_lower1', 'bb_lower2', 'bb_lower3', 'bb_lower4'], name='trigger') + ] + + @staticmethod + def sell_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the sell strategy parameters to be used by Hyperopt. + """ + def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Sell strategy Hyperopt will build and use. + """ + conditions = [] + + # GUARDS AND TRENDS + if params.get('sell-fisher-enabled'): + conditions.append(dataframe['fisher_rsi'] > params['sell-fisher-value']) + + # TRIGGERS + if 'sell-trigger' in params: + if params['sell-trigger'] == 'sell-bb_middle': + conditions.append(dataframe['close'] > dataframe['bb_middleband']) + if params['sell-trigger'] == 'sell-bb_upper1': + conditions.append(dataframe['close'] > dataframe['bb_upperband1']) + if params['sell-trigger'] == 'sell-bb_upper2': + conditions.append(dataframe['close'] > dataframe['bb_upperband2']) + if params['sell-trigger'] == 'sell-bb_upper3': + conditions.append(dataframe['close'] > dataframe['bb_upperband3']) + if params['sell-trigger'] == 'sell-bb_upper4': + conditions.append(dataframe['close'] > dataframe['bb_upperband4']) + if params['sell-trigger'] == 'sell-sar': + conditions.append(dataframe['close'] < dataframe['sar']) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell'] = 1 + + return dataframe + + return populate_sell_trend + + @staticmethod + def sell_indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching sell strategy parameters. + """ + return [ + Integer(-1.0, 1.0, name='sell-fisher-value'), + Categorical([True, False], name='sell-fisher-enabled'), + Categorical(['sell-sar' + 'sell-bb_middle', + 'sell-bb_upper1', + 'sell-bb_upper2', + 'sell-bb_upper3', + 'sell-bb_upper4', ], name='sell-trigger') + ] + + @staticmethod + def generate_roi_table(params: Dict) -> Dict[int, float]: + """ + Generate the ROI table that will be used by Hyperopt + This implementation generates the default legacy Freqtrade ROI tables. + Change it if you need different number of steps in the generated + ROI tables or other structure of the ROI tables. + Please keep it aligned with parameters in the 'roi' optimization + hyperspace defined by the roi_space method. + """ + roi_table = {} + roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3'] + roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2'] + roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1'] + roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0 + + return roi_table + + @staticmethod + def roi_space() -> List[Dimension]: + """ + Values to search for each ROI steps + Override it if you need some different ranges for the parameters in the + 'roi' optimization hyperspace. + Please keep it aligned with the implementation of the + generate_roi_table method. + """ + return [ + Integer(10, 120, name='roi_t1'), + Integer(10, 60, name='roi_t2'), + Integer(10, 40, name='roi_t3'), + Real(0.01, 0.04, name='roi_p1'), + Real(0.01, 0.07, name='roi_p2'), + Real(0.01, 0.20, name='roi_p3'), + ] + + @staticmethod + def stoploss_space() -> List[Dimension]: + """ + Stoploss Value to search + Override it if you need some different range for the parameter in the + 'stoploss' optimization hyperspace. + """ + return [ + Real(-0.35, -0.02, name='stoploss'), + ] + + @staticmethod + def trailing_space() -> List[Dimension]: + """ + Create a trailing stoploss space. + You may override it in your custom Hyperopt class. + """ + return [ + # It was decided to always set trailing_stop is to True if the 'trailing' hyperspace + # is used. Otherwise hyperopt will vary other parameters that won't have effect if + # trailing_stop is set False. + # This parameter is included into the hyperspace dimensions rather than assigning + # it explicitly in the code in order to have it printed in the results along with + # other 'trailing' hyperspace parameters. + Categorical([True], name='trailing_stop'), + + Real(0.01, 0.35, name='trailing_stop_positive'), + + # 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive', + # so this intermediate parameter is used as the value of the difference between + # them. The value of the 'trailing_stop_positive_offset' is constructed in the + # generate_trailing_params() method. + # This is similar to the hyperspace dimensions used for constructing the ROI tables. + Real(0.001, 0.1, name='trailing_stop_positive_offset_p1'), + + Categorical([True, False], name='trailing_only_offset_is_reached'), + ] + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['rsi'] < 30) & + (dataframe['slowk'] < 20) & + (dataframe['bb_lowerband'] > dataframe['close']) & + (dataframe['CDLHAMMER'] == 100) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['sar'] > dataframe['close']) & + (dataframe['fisher_rsi'] > 0.3) + ), + 'sell'] = 1 + return dataframe diff --git a/user_data/hyperopts/examplefreqtrade3opt.py b/user_data/hyperopts/examplefreqtrade3opt.py new file mode 100644 index 000000000..127a52857 --- /dev/null +++ b/user_data/hyperopts/examplefreqtrade3opt.py @@ -0,0 +1,275 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +# --- Do not remove these libs --- +from functools import reduce +from typing import Any, Callable, Dict, List + +import numpy # noqa +import pandas # noqa +from pandas import DataFrame +from skopt.space import Categorical, Dimension, Integer, Real # noqa + +from freqtrade.optimize.hyperopt_interface import IHyperOpt + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta # noqa +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class Examplestrategy3opt(IHyperOpt): + @staticmethod + def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: + + # MFI + dataframe['mfi'] = ta.MFI(dataframe) + + # Stoch fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) + rsi = 0.1 * (dataframe['rsi'] - 50) + dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) + + # Bollinger bands + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband'] = bollinger['lower'] + + # EMA - Exponential Moving Average + dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5) + dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10) + dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # SAR Parabol + dataframe['sar'] = ta.SAR(dataframe) + + # SMA - Simple Moving Average + dataframe['sma'] = ta.SMA(dataframe, timeperiod=40) + + return dataframe + + @staticmethod + def buy_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the buy strategy parameters to be used by Hyperopt. + """ + def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Buy strategy Hyperopt will build and use. + """ + conditions = [] + + # GUARDS AND TRENDS + if params.get('mfi-enabled'): + conditions.append(dataframe['mfi'] < params['mfi-value']) + if params.get('fastd-enabled'): + conditions.append(dataframe['fastd'] > params['fastd-value']) + if params.get('rsi_min-enabled'): + conditions.append(dataframe['rsi'] > params['rsi_min-value']) + if params.get('rsi_max-enabled'): + conditions.append(dataframe['rsi'] < params['rsi_max-value']) + if params.get('fisher_max-enabled'): + conditions.append(dataframe['fisher_rsi'] < params['fisher_rsi_max-value']) + if params.get('fisher_min-enabled'): + conditions.append(dataframe['fisher_rsi'] > params['fisher_rsi_min-value']) + + # TRIGGERS + if 'trigger' in params: + if params['trigger'] == 'fastk': + conditions.append(dataframe['fastd'] > dataframe['fastk']) + if params['trigger'] == 'sma_reversal': + conditions.append(dataframe['close'] < dataframe['sma']) + if params['trigger'] == 'ema': + conditions.append((dataframe['ema50'] > dataframe['ema100']) | + (qtpylib.crossed_above(dataframe['ema5'], dataframe['ema10']))) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + + return dataframe + + return populate_buy_trend + + @ staticmethod + def indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching buy strategy parameters. + """ + return [ + Integer(5, 40, name='mfi-value'), + Integer(0, 50, name='fastd-value'), + Integer(0, 15, name='rsi_min-value'), + Integer(10, 50, name='rsi_max-value'), + Integer(-1, 0.5, name='fisher_rsi_min-value'), + Integer(-0.5, 1, name='fisher_rsi_max-value'), + Categorical([True, False], name='mfi-enabled'), + Categorical([True, False], name='fastd-enabled'), + Categorical([True, False], name='rsi_min-enabled'), + Categorical([True, False], name='rsi_max-enabled'), + Categorical([True, False], name='fisher_min-enabled'), + Categorical([True, False], name='fisher_max-enabled'), + Categorical(['fastk', 'sma_reversal', 'ema'], name='trigger') + ] + + @ staticmethod + def sell_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the sell strategy parameters to be used by Hyperopt. + """ + def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Sell strategy Hyperopt will build and use. + """ + conditions = [] + + # GUARDS AND TRENDS + if params.get('sell-fisher-enabled'): + conditions.append(dataframe['fisher_rsi'] > params['sell-fisher-value']) + + # TRIGGERS + if 'sell-trigger' in params: + if params['sell-trigger'] == 'sell-sar': + conditions.append(dataframe['sar'] > dataframe['close']) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell']=1 + + return dataframe + + return populate_sell_trend + + @ staticmethod + def sell_indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching sell strategy parameters. + """ + return [ + Integer(-1, 1, name='sell-fisher-value'), + Categorical([True, False], name='sell-fisher-enabled'), + Categorical(['sell-sar'], name='sell-trigger') + ] + + @ staticmethod + def generate_roi_table(params: Dict) -> Dict[int, float]: + """ + Generate the ROI table that will be used by Hyperopt + This implementation generates the default legacy Freqtrade ROI tables. + Change it if you need different number of steps in the generated + ROI tables or other structure of the ROI tables. + Please keep it aligned with parameters in the 'roi' optimization + hyperspace defined by the roi_space method. + """ + roi_table = {} + roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3'] + roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2'] + roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1'] + roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0 + + return roi_table + + @ staticmethod + def roi_space() -> List[Dimension]: + """ + Values to search for each ROI steps + Override it if you need some different ranges for the parameters in the + 'roi' optimization hyperspace. + Please keep it aligned with the implementation of the + generate_roi_table method. + """ + return [ + Integer(10, 120, name='roi_t1'), + Integer(10, 60, name='roi_t2'), + Integer(10, 40, name='roi_t3'), + Real(0.01, 0.04, name='roi_p1'), + Real(0.01, 0.07, name='roi_p2'), + Real(0.01, 0.20, name='roi_p3'), + ] + + @ staticmethod + def stoploss_space() -> List[Dimension]: + """ + Stoploss Value to search + Override it if you need some different range for the parameter in the + 'stoploss' optimization hyperspace. + """ + return [ + Real(-0.35, -0.02, name='stoploss'), + ] + + @ staticmethod + def trailing_space() -> List[Dimension]: + """ + Create a trailing stoploss space. + You may override it in your custom Hyperopt class. + """ + return [ + # It was decided to always set trailing_stop is to True if the 'trailing' hyperspace + # is used. Otherwise hyperopt will vary other parameters that won't have effect if + # trailing_stop is set False. + # This parameter is included into the hyperspace dimensions rather than assigning + # it explicitly in the code in order to have it printed in the results along with + # other 'trailing' hyperspace parameters. + Categorical([True], name='trailing_stop'), + + Real(0.01, 0.35, name='trailing_stop_positive'), + + # 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive', + # so this intermediate parameter is used as the value of the difference between + # them. The value of the 'trailing_stop_positive_offset' is constructed in the + # generate_trailing_params() method. + # This is similar to the hyperspace dimensions used for constructing the ROI tables. + Real(0.001, 0.1, name='trailing_stop_positive_offset_p1'), + + Categorical([True, False], name='trailing_only_offset_is_reached'), + ] + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['rsi'] < 28) & + (dataframe['rsi'] > 0) & + (dataframe['close'] < dataframe['sma']) & + (dataframe['fisher_rsi'] < -0.94) & + (dataframe['mfi'] < 16.0) & + ( + (dataframe['ema50'] > dataframe['ema100']) | + (qtpylib.crossed_above(dataframe['ema5'], dataframe['ema10'])) + ) & + (dataframe['fastd'] > dataframe['fastk']) & + (dataframe['fastd'] > 0) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators. + Can be a copy of the corresponding method from the strategy, + or will be loaded from the strategy. + Must align to populate_indicators used (either from this File, or from the strategy) + Only used when --spaces does not include sell + """ + dataframe.loc[ + ( + (dataframe['sar'] > dataframe['close']) & + (dataframe['fisher_rsi'] > 0.3) + ), + 'sell'] = 1 + return dataframe diff --git a/user_data/hyperopts/examplefreqtrade4opt.py b/user_data/hyperopts/examplefreqtrade4opt.py new file mode 100644 index 000000000..65301865f --- /dev/null +++ b/user_data/hyperopts/examplefreqtrade4opt.py @@ -0,0 +1,304 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +# --- Do not remove these libs --- +from functools import reduce +from typing import Any, Callable, Dict, List + +import numpy # noqa +import pandas # noqa +from pandas import DataFrame +from skopt.space import Categorical, Dimension, Integer, Real # noqa + +from freqtrade.optimize.hyperopt_interface import IHyperOpt + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta # noqa +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class Examplestrategy4opt(IHyperOpt): + @staticmethod + def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: + + # ADX + dataframe['adx'] = ta.ADX(dataframe) + dataframe['slowadx'] = ta.ADX(dataframe, 35) + + # Commodity Channel Index: values Oversold:<-100, Overbought:>100 + dataframe['cci'] = ta.CCI(dataframe) + + # Stoch + stoch = ta.STOCHF(dataframe, 5) + dataframe['fastd'] = stoch['fastd'] + dataframe['fastk'] = stoch['fastk'] + dataframe['fastk-previous'] = dataframe.fastk.shift(1) + dataframe['fastd-previous'] = dataframe.fastd.shift(1) + + # Slow Stoch + slowstoch = ta.STOCHF(dataframe, 50) + dataframe['slowfastd'] = slowstoch['fastd'] + dataframe['slowfastk'] = slowstoch['fastk'] + dataframe['slowfastk-previous'] = dataframe.slowfastk.shift(1) + dataframe['slowfastd-previous'] = dataframe.slowfastd.shift(1) + + # EMA - Exponential Moving Average + dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5) + + dataframe['mean-volume'] = dataframe['volume'].mean() + + return dataframe + + @staticmethod + def buy_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the buy strategy parameters to be used by Hyperopt. + """ + def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Buy strategy Hyperopt will build and use. + """ + conditions = [] + + # GUARDS AND TRENDS + if params.get('adx-enabled'): + conditions.append( + (dataframe['adx'] > params['adx-value']) | + (dataframe['slowadx'] > params['slowadx-value']) + ) + + if params.get('cci-enabled'): + conditions.append(dataframe['cci'] < params['cci-value']) + + if params.get('fastk-previous-enabled'): + conditions.append(dataframe['fastk-previous'] < params['fastk-previous-value']) + if params.get('fastd-previous-enabled'): + conditions.append(dataframe['fastd-previous'] < params['fastd-previous-value']) + + if params.get('slowfastk-previous-enabled'): + conditions.append(dataframe['slowfastk-previous'] < + params['slowfastk-previous-value']) + if params.get('slowfastd-previous-enabled'): + conditions.append(dataframe['slowfastd-previous'] < + params['slowfastd-previous-value']) + + if params.get('mean_volume-enabled'): + conditions.append(dataframe['mean-volume'] < params['mean-volume-value']) + + if params.get('close-enabled'): + conditions.append(dataframe['close'] < params['close-value']) + + # TRIGGERS + if 'trigger' in params: + if params['trigger'] == 'fast-previous': + conditions.append(dataframe['fastk-previous'] < dataframe['fastd-previous']) + if params['trigger'] == 'fast': + conditions.append(dataframe['fastk'] > dataframe['fastd']) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy'] = 1 + + return dataframe + + return populate_buy_trend + + @staticmethod + def indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching buy strategy parameters. + """ + return [ + Integer(0, 100, name='adx-value'), + Integer(-150, -50, name='cci-value'), + Integer(0, 50, name='fastk-previous-value'), + Integer(0, 50, name='fastd-previous-value'), + Integer(0, 50, name='slowfastk-previous-value'), + Integer(0, 50, name='slowfastd-previous-value'), + Integer(0.0, 1.5, name='mean-volume-value'), + Integer(0.00000000, 0.00000500, name='close-value'), + Categorical([True, False], name='adx-enabled'), + Categorical([True, False], name='cci-enabled'), + Categorical([True, False], name='fastk-previous-enabled'), + Categorical([True, False], name='fastd-previous-enabled'), + Categorical([True, False], name='slowfastk-previous-enabled'), + Categorical([True, False], name='slowfastd-previous-enabled'), + Categorical([True, False], name='mean-volume-enabled'), + Categorical([True, False], name='close-enabled'), + Categorical(['fast', 'fast-previous'], name='trigger') + ] + + @staticmethod + def sell_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the sell strategy parameters to be used by Hyperopt. + """ + def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Sell strategy Hyperopt will build and use. + """ + conditions = [] + + # GUARDS AND TRENDS + if params.get('sell-slowadx-enabled'): + conditions.append(dataframe['slowadx'] < dataframe['sell-slowadx-value']) + if params.get('sell-fast-enabled'): + conditions.append((dataframe['fastk'] > dataframe['sell-fastk-value']) + | (dataframe['fastd'] > dataframe['sell-fastd-value'])) + + # TRIGGERS + if 'sell-trigger' in params: + if params['sell-trigger'] == 'sell-fast-previous': + conditions.append(dataframe['fastk-previous'] < dataframe['fastd-previous']) + if params['sell-trigger'] == 'sell-ema': + conditions.append(dataframe['close'] > dataframe['ema5']) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell'] = 1 + + return dataframe + + return populate_sell_trend + + @staticmethod + def sell_indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching sell strategy parameters. + """ + return [ + Integer(0, 50, name='sell-slowadx-value'), + Integer(50, 100, name='sell-fastk-value'), + Integer(50, 100, name='sell-fastd-value'), + Categorical([True, False], name='sell-slowadx-enabled'), + Categorical([True, False], name='sell-fast-enabled'), + Categorical(['sell-ema', 'sell-fast-previous'], name='sell-trigger') + ] + + @staticmethod + def generate_roi_table(params: Dict) -> Dict[int, float]: + """ + Generate the ROI table that will be used by Hyperopt + This implementation generates the default legacy Freqtrade ROI tables. + Change it if you need different number of steps in the generated + ROI tables or other structure of the ROI tables. + Please keep it aligned with parameters in the 'roi' optimization + hyperspace defined by the roi_space method. + """ + roi_table = {} + roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3'] + roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2'] + roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1'] + roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0 + + return roi_table + + @staticmethod + def roi_space() -> List[Dimension]: + """ + Values to search for each ROI steps + Override it if you need some different ranges for the parameters in the + 'roi' optimization hyperspace. + Please keep it aligned with the implementation of the + generate_roi_table method. + """ + return [ + Integer(10, 120, name='roi_t1'), + Integer(10, 60, name='roi_t2'), + Integer(10, 40, name='roi_t3'), + Real(0.01, 0.04, name='roi_p1'), + Real(0.01, 0.07, name='roi_p2'), + Real(0.01, 0.20, name='roi_p3'), + ] + + @staticmethod + def stoploss_space() -> List[Dimension]: + """ + Stoploss Value to search + Override it if you need some different range for the parameter in the + 'stoploss' optimization hyperspace. + """ + return [ + Real(-0.35, -0.02, name='stoploss'), + ] + + @staticmethod + def trailing_space() -> List[Dimension]: + """ + Create a trailing stoploss space. + You may override it in your custom Hyperopt class. + """ + return [ + # It was decided to always set trailing_stop is to True if the 'trailing' hyperspace + # is used. Otherwise hyperopt will vary other parameters that won't have effect if + # trailing_stop is set False. + # This parameter is included into the hyperspace dimensions rather than assigning + # it explicitly in the code in order to have it printed in the results along with + # other 'trailing' hyperspace parameters. + Categorical([True], name='trailing_stop'), + + Real(0.01, 0.35, name='trailing_stop_positive'), + + # 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive', + # so this intermediate parameter is used as the value of the difference between + # them. The value of the 'trailing_stop_positive_offset' is constructed in the + # generate_trailing_params() method. + # This is similar to the hyperspace dimensions used for constructing the ROI tables. + Real(0.001, 0.1, name='trailing_stop_positive_offset_p1'), + + Categorical([True, False], name='trailing_only_offset_is_reached'), + ] + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + ( + (dataframe['adx'] > 50) | + (dataframe['slowadx'] > 26) + ) & + (dataframe['cci'] < -100) & + ( + (dataframe['fastk-previous'] < 20) & + (dataframe['fastd-previous'] < 20) + ) & + ( + (dataframe['slowfastk-previous'] < 30) & + (dataframe['slowfastd-previous'] < 30) + ) & + (dataframe['fastk-previous'] < dataframe['fastd-previous']) & + (dataframe['fastk'] > dataframe['fastd']) & + (dataframe['mean-volume'] > 0.75) & + (dataframe['close'] > 0.00000100) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['slowadx'] < 25) & + ((dataframe['fastk'] > 70) | (dataframe['fastd'] > 70)) & + (dataframe['fastk-previous'] < dataframe['fastd-previous']) & + (dataframe['close'] > dataframe['ema5']) + ), + 'sell'] = 1 + return dataframe diff --git a/user_data/hyperopts/examplefreqtrade5opt.py b/user_data/hyperopts/examplefreqtrade5opt.py new file mode 100644 index 000000000..390c9cb3b --- /dev/null +++ b/user_data/hyperopts/examplefreqtrade5opt.py @@ -0,0 +1,290 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +# --- Do not remove these libs --- +from functools import reduce +from typing import Any, Callable, Dict, List + +import numpy # noqa +import pandas # noqa +from pandas import DataFrame +from skopt.space import Categorical, Dimension, Integer, Real # noqa + +from freqtrade.optimize.hyperopt_interface import IHyperOpt + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta # noqa +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class Examplestrategy5opt(IHyperOpt): + @staticmethod + def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Adds several different TA indicators to the given DataFrame + Performance Note: For the best performance be frugal on the number of indicators + you are using. Let uncomment only the indicator you are using in your strategies + or your hyperopt configuration, otherwise you will waste your memory and CPU usage. + """ + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + + # Minus Directional Indicator / Movement + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) + rsi = 0.1 * (dataframe['rsi'] - 50) + dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) + # Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy) + dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1) + + # Stoch fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # Overlap Studies + # ------------------------------------ + + # SAR Parabol + dataframe['sar'] = ta.SAR(dataframe) + + # SMA - Simple Moving Average + dataframe['sma'] = ta.SMA(dataframe, timeperiod=40) + + return dataframe + + @staticmethod + def buy_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the buy strategy parameters to be used by Hyperopt. + """ + def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Buy strategy Hyperopt will build and use. + """ + conditions = [] + + # GUARDS AND TRENDS + if params.get('fastd-enabled'): + conditions.append(dataframe['fastd'] > params['fastd-value']) + + if params.get('rsi-enabled'): + conditions.append(dataframe['rsi'] > params['rsi-value']) + + if params.get('close-enabled'): + conditions.append(dataframe['close'] > params['close-value']) + + if params.get('fisher_rsi_norma-enabled'): + conditions.append(dataframe['fisher_rsi_norma'] < params['fisher_rsi_norma-value']) + + # TRIGGERS + if 'trigger' in params: + if params['trigger'] == 'sma': + conditions.append(dataframe['close'] < dataframe['sma']) + if params['trigger'] == 'fast': + conditions.append(dataframe['fastk'] < dataframe['fastd']) + if params['trigger'] == 'volume': + conditions.append(dataframe['volume'] > + dataframe['volume'].rolling(200).mean() * 4) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy'] = 1 + + return dataframe + + return populate_buy_trend + + @staticmethod + def indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching buy strategy parameters. + """ + return [ + Integer(0, 50, name='rsi-value'), + Integer(0.0, 100.0, name='fisher_rsi_norma-value'), + Integer(0, 50, name='fastd-value'), + Integer(0.00000000, 0.00000200, name='close-value'), + Categorical([True, False], name='rsi-enabled'), + Categorical([True, False], name='fastd-enabled'), + Categorical([True, False], name='fisher_rsi_norma-enabled'), + Categorical([True, False], name='close-enabled'), + Categorical(['fast', 'sma', 'volume'], name='trigger') + ] + + @staticmethod + def sell_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the sell strategy parameters to be used by Hyperopt. + """ + def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Sell strategy Hyperopt will build and use. + """ + conditions = [] + + # GUARDS AND TRENDS + if params.get('sell-enabled'): + conditions.append( + ( + (qtpylib.crossed_above(dataframe['rsi'], dataframe['sell-rsi-value'])) & + (dataframe['macd'] < dataframe['sell-macd-value']) & + (dataframe['minus_di'] > dataframe['sell-minus_di-value']) + ) | + ( + (dataframe['sar'] > dataframe['close']) & + (dataframe['fisher_rsi'] > dataframe['sell-fisher_rsi-value']) + ) + ) + + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell'] = 1 + + return dataframe + + return populate_sell_trend + + @staticmethod + def sell_indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching sell strategy parameters. + """ + return [ + Integer(25, 75, name='sell-rsi-value'), + Integer(-50, 50, name='sell-macd-value'), + Integer(-50, 50, name='sell-minus_di-value'), + Categorical([True, False], name='sell-enabled'), + ] + + @staticmethod + def generate_roi_table(params: Dict) -> Dict[int, float]: + """ + Generate the ROI table that will be used by Hyperopt + This implementation generates the default legacy Freqtrade ROI tables. + Change it if you need different number of steps in the generated + ROI tables or other structure of the ROI tables. + Please keep it aligned with parameters in the 'roi' optimization + hyperspace defined by the roi_space method. + """ + roi_table = {} + roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3'] + roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2'] + roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1'] + roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0 + + return roi_table + + @staticmethod + def roi_space() -> List[Dimension]: + """ + Values to search for each ROI steps + Override it if you need some different ranges for the parameters in the + 'roi' optimization hyperspace. + Please keep it aligned with the implementation of the + generate_roi_table method. + """ + return [ + Integer(10, 120, name='roi_t1'), + Integer(10, 60, name='roi_t2'), + Integer(10, 40, name='roi_t3'), + Real(0.01, 0.04, name='roi_p1'), + Real(0.01, 0.07, name='roi_p2'), + Real(0.01, 0.20, name='roi_p3'), + ] + + @staticmethod + def stoploss_space() -> List[Dimension]: + """ + Stoploss Value to search + Override it if you need some different range for the parameter in the + 'stoploss' optimization hyperspace. + """ + return [ + Real(-0.35, -0.02, name='stoploss'), + ] + + @staticmethod + def trailing_space() -> List[Dimension]: + """ + Create a trailing stoploss space. + You may override it in your custom Hyperopt class. + """ + return [ + # It was decided to always set trailing_stop is to True if the 'trailing' hyperspace + # is used. Otherwise hyperopt will vary other parameters that won't have effect if + # trailing_stop is set False. + # This parameter is included into the hyperspace dimensions rather than assigning + # it explicitly in the code in order to have it printed in the results along with + # other 'trailing' hyperspace parameters. + Categorical([True], name='trailing_stop'), + + Real(0.01, 0.35, name='trailing_stop_positive'), + + # 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive', + # so this intermediate parameter is used as the value of the difference between + # them. The value of the 'trailing_stop_positive_offset' is constructed in the + # generate_trailing_params() method. + # This is similar to the hyperspace dimensions used for constructing the ROI tables. + Real(0.001, 0.1, name='trailing_stop_positive_offset_p1'), + + Categorical([True, False], name='trailing_only_offset_is_reached'), + ] + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + # Prod + ( + (dataframe['close'] > 0.00000200) & + (dataframe['volume'] > dataframe['volume'].rolling(200).mean() * 4) & + (dataframe['close'] < dataframe['sma']) & + (dataframe['fastd'] > dataframe['fastk']) & + (dataframe['rsi'] > 0) & + (dataframe['fastd'] > 0) & + # (dataframe['fisher_rsi'] < -0.94) + (dataframe['fisher_rsi_norma'] < 38.900000000000006) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + # Prod + ( + (qtpylib.crossed_above(dataframe['rsi'], 50)) & + (dataframe['macd'] < 0) & + (dataframe['minus_di'] > 0) + ) | + ( + (dataframe['sar'] > dataframe['close']) & + (dataframe['fisher_rsi'] > 0.3) + ), + + 'sell'] = 1 + return dataframe diff --git a/user_data/strategies/BasicStrategy.py b/user_data/strategies/BasicStrategy.py new file mode 100644 index 000000000..85e09516a --- /dev/null +++ b/user_data/strategies/BasicStrategy.py @@ -0,0 +1,364 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +# --- Do not remove these libs --- +import numpy # noqa +import pandas # noqa +from pandas import DataFrame + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class BasicStrategy(IStrategy): + """ + This is a strategy template to get you started. + More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md + + You can: + :return: a Dataframe with all mandatory indicators for the strategies + - Rename the class name (Do not forget to update class_name) + - Add any methods you want to build your strategy + - Add any lib you need to build your strategy + + You must keep: + - the lib in the section "Do not remove these libs" + - the prototype for the methods: minimal_roi, stoploss, populate_indicators, populate_buy_trend, + populate_sell_trend, hyperopt_space, buy_strategy_generator + """ + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + minimal_roi = { + "60": 0.01, + "30": 0.02, + "0": 0.04 + } + + # Optimal stoploss designed for the strategy. + # This attribute will be overridden if the config file contains "stoploss". + stoploss = -0.10 + + # Trailing stoploss + trailing_stop = False + # trailing_only_offset_is_reached = False + # trailing_stop_positive = 0.01 + # trailing_stop_positive_offset = 0.0 # Disabled / not configured + + # Optimal timeframe for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 20 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'tema': {}, + 'sar': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "MACD": { + 'macd': {'color': 'blue'}, + 'macdsignal': {'color': 'orange'}, + }, + "RSI": { + 'rsi': {'color': 'red'}, + } + } + } + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Adds several different TA indicators to the given DataFrame + + Performance Note: For the best performance be frugal on the number of indicators + you are using. Let uncomment only the indicator you are using in your strategies + or your hyperopt configuration, otherwise you will waste your memory and CPU usage. + :param dataframe: Dataframe with data from the exchange + :param metadata: Additional information, like the currently traded pair + :return: a Dataframe with all mandatory indicators for the strategies + """ + + # Momentum Indicators + # ------------------------------------ + + # ADX + dataframe['adx'] = ta.ADX(dataframe) + + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # # Minus Directional Indicator / Movement + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # # Awesome Oscillator + # dataframe['ao'] = qtpylib.awesome_oscillator(dataframe) + + # # Keltner Channel + # keltner = qtpylib.keltner_channel(dataframe) + # dataframe["kc_upperband"] = keltner["upper"] + # dataframe["kc_lowerband"] = keltner["lower"] + # dataframe["kc_middleband"] = keltner["mid"] + # dataframe["kc_percent"] = ( + # (dataframe["close"] - dataframe["kc_lowerband"]) / + # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) + # ) + # dataframe["kc_width"] = ( + # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"] + # ) + + # # Ultimate Oscillator + # dataframe['uo'] = ta.ULTOSC(dataframe) + + # # Commodity Channel Index: values [Oversold:-100, Overbought:100] + # dataframe['cci'] = ta.CCI(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy) + # rsi = 0.1 * (dataframe['rsi'] - 50) + # dataframe['fisher_rsi'] = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1) + + # # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy) + # dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1) + + # # Stochastic Slow + # stoch = ta.STOCH(dataframe) + # dataframe['slowd'] = stoch['slowd'] + # dataframe['slowk'] = stoch['slowk'] + + # Stochastic Fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # # Stochastic RSI + # stoch_rsi = ta.STOCHRSI(dataframe) + # dataframe['fastd_rsi'] = stoch_rsi['fastd'] + # dataframe['fastk_rsi'] = stoch_rsi['fastk'] + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + dataframe['macdhist'] = macd['macdhist'] + + # MFI + dataframe['mfi'] = ta.MFI(dataframe) + + # # ROC + # dataframe['roc'] = ta.ROC(dataframe) + + # Overlap Studies + # ------------------------------------ + + # Bollinger Bands + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband'] = bollinger['lower'] + dataframe['bb_middleband'] = bollinger['mid'] + dataframe['bb_upperband'] = bollinger['upper'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + # dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5) + # dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10) + # dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + # dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + + # Parabolic SAR + dataframe['sar'] = ta.SAR(dataframe) + + # TEMA - Triple Exponential Moving Average + dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9) + + # Cycle Indicator + # ------------------------------------ + # Hilbert Transform Indicator - SineWave + hilbert = ta.HT_SINE(dataframe) + dataframe['htsine'] = hilbert['sine'] + dataframe['htleadsine'] = hilbert['leadsine'] + + # Pattern Recognition - Bullish candlestick patterns + # ------------------------------------ + # # Hammer: values [0, 100] + # dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe) + # # Inverted Hammer: values [0, 100] + # dataframe['CDLINVERTEDHAMMER'] = ta.CDLINVERTEDHAMMER(dataframe) + # # Dragonfly Doji: values [0, 100] + # dataframe['CDLDRAGONFLYDOJI'] = ta.CDLDRAGONFLYDOJI(dataframe) + # # Piercing Line: values [0, 100] + # dataframe['CDLPIERCING'] = ta.CDLPIERCING(dataframe) # values [0, 100] + # # Morningstar: values [0, 100] + # dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100] + # # Three White Soldiers: values [0, 100] + # dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100] + + # Pattern Recognition - Bearish candlestick patterns + # ------------------------------------ + # # Hanging Man: values [0, 100] + # dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe) + # # Shooting Star: values [0, 100] + # dataframe['CDLSHOOTINGSTAR'] = ta.CDLSHOOTINGSTAR(dataframe) + # # Gravestone Doji: values [0, 100] + # dataframe['CDLGRAVESTONEDOJI'] = ta.CDLGRAVESTONEDOJI(dataframe) + # # Dark Cloud Cover: values [0, 100] + # dataframe['CDLDARKCLOUDCOVER'] = ta.CDLDARKCLOUDCOVER(dataframe) + # # Evening Doji Star: values [0, 100] + # dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe) + # # Evening Star: values [0, 100] + # dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe) + + # Pattern Recognition - Bullish/Bearish candlestick patterns + # ------------------------------------ + # # Three Line Strike: values [0, -100, 100] + # dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe) + # # Spinning Top: values [0, -100, 100] + # dataframe['CDLSPINNINGTOP'] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100] + # # Engulfing: values [0, -100, 100] + # dataframe['CDLENGULFING'] = ta.CDLENGULFING(dataframe) # values [0, -100, 100] + # # Harami: values [0, -100, 100] + # dataframe['CDLHARAMI'] = ta.CDLHARAMI(dataframe) # values [0, -100, 100] + # # Three Outside Up/Down: values [0, -100, 100] + # dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100] + # # Three Inside Up/Down: values [0, -100, 100] + # dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100] + + # # Chart type + # # ------------------------------------ + # # Heikin Ashi Strategy + # heikinashi = qtpylib.heikinashi(dataframe) + # dataframe['ha_open'] = heikinashi['open'] + # dataframe['ha_close'] = heikinashi['close'] + # dataframe['ha_high'] = heikinashi['high'] + # dataframe['ha_low'] = heikinashi['low'] + + # Retrieve best bid and best ask from the orderbook + # ------------------------------------ + """ + # first check if dataprovider is available + if self.dp: + if self.dp.runmode in ('live', 'dry_run'): + ob = self.dp.orderbook(metadata['pair'], 1) + dataframe['best_bid'] = ob['bids'][0][0] + dataframe['best_ask'] = ob['asks'][0][0] + """ + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame populated with indicators + :param metadata: Additional information, like the currently traded pair + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (qtpylib.crossed_above(dataframe['rsi'], 30)) & # Signal: RSI crosses above 30 + (dataframe['tema'] <= dataframe['bb_middleband']) & # Guard: tema below BB middle + (dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard: tema is raising + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame populated with indicators + :param metadata: Additional information, like the currently traded pair + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (qtpylib.crossed_above(dataframe['rsi'], 70)) & # Signal: RSI crosses above 70 + (dataframe['tema'] > dataframe['bb_middleband']) & # Guard: tema above BB middle + (dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard: tema is falling + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + 'sell'] = 1 + return dataframe + \ No newline at end of file diff --git a/user_data/strategies/Ressup.py b/user_data/strategies/Ressup.py new file mode 100644 index 000000000..304a45cf7 --- /dev/null +++ b/user_data/strategies/Ressup.py @@ -0,0 +1,364 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +# --- Do not remove these libs --- +import numpy # noqa +import pandas # noqa +from pandas import DataFrame + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class ResistanceSupport(IStrategy): + """ + This is a strategy template to get you started. + More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md + + You can: + :return: a Dataframe with all mandatory indicators for the strategies + - Rename the class name (Do not forget to update class_name) + - Add any methods you want to build your strategy + - Add any lib you need to build your strategy + + You must keep: + - the lib in the section "Do not remove these libs" + - the prototype for the methods: minimal_roi, stoploss, populate_indicators, populate_buy_trend, + populate_sell_trend, hyperopt_space, buy_strategy_generator + """ + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + minimal_roi = { + "60": 0.01, + "30": 0.02, + "0": 0.04 + } + + # Optimal stoploss designed for the strategy. + # This attribute will be overridden if the config file contains "stoploss". + stoploss = -0.10 + + # Trailing stoploss + trailing_stop = False + # trailing_only_offset_is_reached = False + # trailing_stop_positive = 0.01 + # trailing_stop_positive_offset = 0.0 # Disabled / not configured + + # Optimal timeframe for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 20 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'tema': {}, + 'sar': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "MACD": { + 'macd': {'color': 'blue'}, + 'macdsignal': {'color': 'orange'}, + }, + "RSI": { + 'rsi': {'color': 'red'}, + } + } + } + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Adds several different TA indicators to the given DataFrame + + Performance Note: For the best performance be frugal on the number of indicators + you are using. Let uncomment only the indicator you are using in your strategies + or your hyperopt configuration, otherwise you will waste your memory and CPU usage. + :param dataframe: Dataframe with data from the exchange + :param metadata: Additional information, like the currently traded pair + :return: a Dataframe with all mandatory indicators for the strategies + """ + + # Momentum Indicators + # ------------------------------------ + + # ADX + dataframe['adx'] = ta.ADX(dataframe) + + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # # Minus Directional Indicator / Movement + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # # Awesome Oscillator + # dataframe['ao'] = qtpylib.awesome_oscillator(dataframe) + + # # Keltner Channel + # keltner = qtpylib.keltner_channel(dataframe) + # dataframe["kc_upperband"] = keltner["upper"] + # dataframe["kc_lowerband"] = keltner["lower"] + # dataframe["kc_middleband"] = keltner["mid"] + # dataframe["kc_percent"] = ( + # (dataframe["close"] - dataframe["kc_lowerband"]) / + # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) + # ) + # dataframe["kc_width"] = ( + # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"] + # ) + + # # Ultimate Oscillator + # dataframe['uo'] = ta.ULTOSC(dataframe) + + # # Commodity Channel Index: values [Oversold:-100, Overbought:100] + # dataframe['cci'] = ta.CCI(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy) + # rsi = 0.1 * (dataframe['rsi'] - 50) + # dataframe['fisher_rsi'] = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1) + + # # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy) + # dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1) + + # # Stochastic Slow + # stoch = ta.STOCH(dataframe) + # dataframe['slowd'] = stoch['slowd'] + # dataframe['slowk'] = stoch['slowk'] + + # Stochastic Fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # # Stochastic RSI + # stoch_rsi = ta.STOCHRSI(dataframe) + # dataframe['fastd_rsi'] = stoch_rsi['fastd'] + # dataframe['fastk_rsi'] = stoch_rsi['fastk'] + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + dataframe['macdhist'] = macd['macdhist'] + + # MFI + dataframe['mfi'] = ta.MFI(dataframe) + + # # ROC + # dataframe['roc'] = ta.ROC(dataframe) + + # Overlap Studies + # ------------------------------------ + + # Bollinger Bands + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband'] = bollinger['lower'] + dataframe['bb_middleband'] = bollinger['mid'] + dataframe['bb_upperband'] = bollinger['upper'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + # dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5) + # dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10) + # dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + # dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + + # Parabolic SAR + dataframe['sar'] = ta.SAR(dataframe) + + # TEMA - Triple Exponential Moving Average + dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9) + + # Cycle Indicator + # ------------------------------------ + # Hilbert Transform Indicator - SineWave + hilbert = ta.HT_SINE(dataframe) + dataframe['htsine'] = hilbert['sine'] + dataframe['htleadsine'] = hilbert['leadsine'] + + # Pattern Recognition - Bullish candlestick patterns + # ------------------------------------ + # # Hammer: values [0, 100] + # dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe) + # # Inverted Hammer: values [0, 100] + # dataframe['CDLINVERTEDHAMMER'] = ta.CDLINVERTEDHAMMER(dataframe) + # # Dragonfly Doji: values [0, 100] + # dataframe['CDLDRAGONFLYDOJI'] = ta.CDLDRAGONFLYDOJI(dataframe) + # # Piercing Line: values [0, 100] + # dataframe['CDLPIERCING'] = ta.CDLPIERCING(dataframe) # values [0, 100] + # # Morningstar: values [0, 100] + # dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100] + # # Three White Soldiers: values [0, 100] + # dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100] + + # Pattern Recognition - Bearish candlestick patterns + # ------------------------------------ + # # Hanging Man: values [0, 100] + # dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe) + # # Shooting Star: values [0, 100] + # dataframe['CDLSHOOTINGSTAR'] = ta.CDLSHOOTINGSTAR(dataframe) + # # Gravestone Doji: values [0, 100] + # dataframe['CDLGRAVESTONEDOJI'] = ta.CDLGRAVESTONEDOJI(dataframe) + # # Dark Cloud Cover: values [0, 100] + # dataframe['CDLDARKCLOUDCOVER'] = ta.CDLDARKCLOUDCOVER(dataframe) + # # Evening Doji Star: values [0, 100] + # dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe) + # # Evening Star: values [0, 100] + # dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe) + + # Pattern Recognition - Bullish/Bearish candlestick patterns + # ------------------------------------ + # # Three Line Strike: values [0, -100, 100] + # dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe) + # # Spinning Top: values [0, -100, 100] + # dataframe['CDLSPINNINGTOP'] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100] + # # Engulfing: values [0, -100, 100] + # dataframe['CDLENGULFING'] = ta.CDLENGULFING(dataframe) # values [0, -100, 100] + # # Harami: values [0, -100, 100] + # dataframe['CDLHARAMI'] = ta.CDLHARAMI(dataframe) # values [0, -100, 100] + # # Three Outside Up/Down: values [0, -100, 100] + # dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100] + # # Three Inside Up/Down: values [0, -100, 100] + # dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100] + + # # Chart type + # # ------------------------------------ + # # Heikin Ashi Strategy + # heikinashi = qtpylib.heikinashi(dataframe) + # dataframe['ha_open'] = heikinashi['open'] + # dataframe['ha_close'] = heikinashi['close'] + # dataframe['ha_high'] = heikinashi['high'] + # dataframe['ha_low'] = heikinashi['low'] + + # Retrieve best bid and best ask from the orderbook + # ------------------------------------ + """ + # first check if dataprovider is available + if self.dp: + if self.dp.runmode in ('live', 'dry_run'): + ob = self.dp.orderbook(metadata['pair'], 1) + dataframe['best_bid'] = ob['bids'][0][0] + dataframe['best_ask'] = ob['asks'][0][0] + """ + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame populated with indicators + :param metadata: Additional information, like the currently traded pair + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (qtpylib.crossed_above(dataframe['rsi'], 30)) & # Signal: RSI crosses above 30 + (dataframe['tema'] <= dataframe['bb_middleband']) & # Guard: tema below BB middle + (dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard: tema is raising + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame populated with indicators + :param metadata: Additional information, like the currently traded pair + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (qtpylib.crossed_above(dataframe['rsi'], 70)) & # Signal: RSI crosses above 70 + (dataframe['tema'] > dataframe['bb_middleband']) & # Guard: tema above BB middle + (dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard: tema is falling + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + 'sell'] = 1 + return dataframe + \ No newline at end of file diff --git a/user_data/strategies/bbrsi.py b/user_data/strategies/bbrsi.py new file mode 100644 index 000000000..e8ee8886f --- /dev/null +++ b/user_data/strategies/bbrsi.py @@ -0,0 +1,133 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +# --- Do not remove these libs --- +import numpy # noqa +import pandas # noqa +from pandas import DataFrame + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class BBRSI(IStrategy): + """ + You must keep: + - the lib in the section "Do not remove these libs" + - the prototype for the methods: minimal_roi, stoploss, populate_indicators, populate_buy_trend, + populate_sell_trend, hyperopt_space, buy_strategy_generator + """ + + # Strategy interface version - allow new iterations of the strategy interface. + INTERFACE_VERSION = 2 + + # Minimal ROI designed for the strategy. + # Will override config file. + minimal_roi = { + "0": 100 + } + + # Optimal stoploss designed for the strategy. + # Will override config file. + stoploss = -0.99 + + # Trailing stoploss + trailing_stop = False + # trailing_only_offset_is_reached = False + # trailing_stop_positive = 0.01 + # trailing_stop_positive_offset = 0.0 # Disabled / not configured + + # Optimal timeframe for the strategy. + timeframe = '1h' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 20 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'tema': {}, + 'sar': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "MACD": { + 'macd': {'color': 'blue'}, + 'macdsignal': {'color': 'orange'}, + }, + "RSI": { + 'rsi': {'color': 'red'}, + } + } + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Bollinger Bands + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband'] = bollinger['lower'] + dataframe['bb_middleband'] = bollinger['mid'] + dataframe['bb_upperband'] = bollinger['upper'] + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['rsi'] > 30) & # RSI above 30 + (dataframe['close'] < dataframe['bb_lowerband']) # close price under low bb + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['close'] > dataframe['bb_middleband']) # close price above the middle bb + ), + 'sell'] = 1 + return dataframe diff --git a/user_data/strategies/examplefreqtrade1.py b/user_data/strategies/examplefreqtrade1.py new file mode 100644 index 000000000..720f6001e --- /dev/null +++ b/user_data/strategies/examplefreqtrade1.py @@ -0,0 +1,120 @@ +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class Examplestrategy1(IStrategy): + """ + Strategy 001 + author@: Gerald Lonlas + github@: https://github.com/freqtrade/freqtrade-strategies + + How to use it? + > python3 ./freqtrade/main.py -s Strategy001 + """ + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "60": 0.01, + "30": 0.03, + "20": 0.04, + "0": 0.05 + } + + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + stoploss = -0.10 + + # Optimal ticker interval for the strategy + ticker_interval = '5m' + + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = 0.02 + + # run "populate_indicators" only for new candle + process_only_new_candles = False + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Optional order type mapping + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Adds several different TA indicators to the given DataFrame + + Performance Note: For the best performance be frugal on the number of indicators + you are using. Let uncomment only the indicator you are using in your strategies + or your hyperopt configuration, otherwise you will waste your memory and CPU usage. + """ + + dataframe['ema20'] = ta.EMA(dataframe, timeperiod=20) + dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + heikinashi = qtpylib.heikinashi(dataframe) + dataframe['ha_open'] = heikinashi['open'] + dataframe['ha_close'] = heikinashi['close'] + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + qtpylib.crossed_above(dataframe['ema20'], dataframe['ema50']) & + (dataframe['ha_close'] > dataframe['ema20']) & + (dataframe['ha_open'] < dataframe['ha_close']) # green bar + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + qtpylib.crossed_above(dataframe['ema50'], dataframe['ema100']) & + (dataframe['ha_close'] < dataframe['ema20']) & + (dataframe['ha_open'] > dataframe['ha_close']) # red bar + ), + 'sell'] = 1 + return dataframe diff --git a/user_data/strategies/examplefreqtrade2.py b/user_data/strategies/examplefreqtrade2.py new file mode 100644 index 000000000..aa951fa0e --- /dev/null +++ b/user_data/strategies/examplefreqtrade2.py @@ -0,0 +1,132 @@ + +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +import numpy # noqa + +class Examplestrategy2(IStrategy): + """ + Strategy 002 + author@: Gerald Lonlas + github@: https://github.com/freqtrade/freqtrade-strategies + How to use it? + > python3 ./freqtrade/main.py -s Strategy002 + """ + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "60": 0.01, + "30": 0.03, + "20": 0.04, + "0": 0.05 + } + + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + stoploss = -0.10 + + # Optimal ticker interval for the strategy + ticker_interval = '5m' + + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = 0.02 + + # run "populate_indicators" only for new candle + process_only_new_candles = False + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Optional order type mapping + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Adds several different TA indicators to the given DataFrame + Performance Note: For the best performance be frugal on the number of indicators + you are using. Let uncomment only the indicator you are using in your strategies + or your hyperopt configuration, otherwise you will waste your memory and CPU usage. + """ + + # Stoch + stoch = ta.STOCH(dataframe) + dataframe['slowk'] = stoch['slowk'] + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) + rsi = 0.1 * (dataframe['rsi'] - 50) + dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) + + # Bollinger bands + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband'] = bollinger['lower'] + + # SAR Parabol + dataframe['sar'] = ta.SAR(dataframe) + + # Hammer: values [0, 100] + dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['rsi'] < 30) & + (dataframe['slowk'] < 20) & + (dataframe['bb_lowerband'] > dataframe['close']) & + (dataframe['CDLHAMMER'] == 100) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['sar'] > dataframe['close']) & + (dataframe['fisher_rsi'] > 0.3) + ), + 'sell'] = 1 + return dataframe \ No newline at end of file diff --git a/user_data/strategies/examplefreqtrade3.py b/user_data/strategies/examplefreqtrade3.py new file mode 100644 index 000000000..865b48a30 --- /dev/null +++ b/user_data/strategies/examplefreqtrade3.py @@ -0,0 +1,150 @@ + +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +import numpy # noqa + + +class Examplestrategy3(IStrategy): + """ + Strategy 003 + author@: Gerald Lonlas + github@: https://github.com/freqtrade/freqtrade-strategies + How to use it? + > python3 ./freqtrade/main.py -s Strategy003 + """ + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "60": 0.01, + "30": 0.03, + "20": 0.04, + "0": 0.05 + } + + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + stoploss = -0.10 + + # Optimal ticker interval for the strategy + ticker_interval = '5m' + + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = 0.02 + + # run "populate_indicators" only for new candle + process_only_new_candles = False + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Optional order type mapping + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Adds several different TA indicators to the given DataFrame + Performance Note: For the best performance be frugal on the number of indicators + you are using. Let uncomment only the indicator you are using in your strategies + or your hyperopt configuration, otherwise you will waste your memory and CPU usage. + """ + + # MFI + dataframe['mfi'] = ta.MFI(dataframe) + + # Stoch fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) + rsi = 0.1 * (dataframe['rsi'] - 50) + dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) + + # Bollinger bands + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband'] = bollinger['lower'] + + # EMA - Exponential Moving Average + dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5) + dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10) + dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # SAR Parabol + dataframe['sar'] = ta.SAR(dataframe) + + # SMA - Simple Moving Average + dataframe['sma'] = ta.SMA(dataframe, timeperiod=40) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['rsi'] < 28) & + (dataframe['rsi'] > 0) & + (dataframe['close'] < dataframe['sma']) & + (dataframe['fisher_rsi'] < -0.94) & + (dataframe['mfi'] < 16.0) & + ( + (dataframe['ema50'] > dataframe['ema100']) | + (qtpylib.crossed_above(dataframe['ema5'], dataframe['ema10'])) + ) & + (dataframe['fastd'] > dataframe['fastk']) & + (dataframe['fastd'] > 0) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['sar'] > dataframe['close']) & + (dataframe['fisher_rsi'] > 0.3) + ), + 'sell'] = 1 + return dataframe diff --git a/user_data/strategies/examplefreqtrade4.py b/user_data/strategies/examplefreqtrade4.py new file mode 100644 index 000000000..bb2406323 --- /dev/null +++ b/user_data/strategies/examplefreqtrade4.py @@ -0,0 +1,152 @@ + +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta + + +class Examplestrategy4(IStrategy): + + """ + Strategy 004 + author@: Gerald Lonlas + github@: https://github.com/freqtrade/freqtrade-strategies + How to use it? + > python3 ./freqtrade/main.py -s Strategy004 + """ + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "60": 0.01, + "30": 0.03, + "20": 0.04, + "0": 0.05 + } + + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + stoploss = -0.10 + + # Optimal ticker interval for the strategy + ticker_interval = '5m' + + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = 0.02 + + # run "populate_indicators" only for new candle + process_only_new_candles = False + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Optional order type mapping + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Adds several different TA indicators to the given DataFrame + Performance Note: For the best performance be frugal on the number of indicators + you are using. Let uncomment only the indicator you are using in your strategies + or your hyperopt configuration, otherwise you will waste your memory and CPU usage. + """ + + # ADX + dataframe['adx'] = ta.ADX(dataframe) + dataframe['slowadx'] = ta.ADX(dataframe, 35) + + # Commodity Channel Index: values Oversold:<-100, Overbought:>100 + dataframe['cci'] = ta.CCI(dataframe) + + # Stoch + stoch = ta.STOCHF(dataframe, 5) + dataframe['fastd'] = stoch['fastd'] + dataframe['fastk'] = stoch['fastk'] + dataframe['fastk-previous'] = dataframe.fastk.shift(1) + dataframe['fastd-previous'] = dataframe.fastd.shift(1) + + # Slow Stoch + slowstoch = ta.STOCHF(dataframe, 50) + dataframe['slowfastd'] = slowstoch['fastd'] + dataframe['slowfastk'] = slowstoch['fastk'] + dataframe['slowfastk-previous'] = dataframe.slowfastk.shift(1) + dataframe['slowfastd-previous'] = dataframe.slowfastd.shift(1) + + # EMA - Exponential Moving Average + dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5) + + dataframe['mean-volume'] = dataframe['volume'].mean() + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + ( + (dataframe['adx'] > 50) | + (dataframe['slowadx'] > 26) + ) & + (dataframe['cci'] < -100) & + ( + (dataframe['fastk-previous'] < 20) & + (dataframe['fastd-previous'] < 20) + ) & + ( + (dataframe['slowfastk-previous'] < 30) & + (dataframe['slowfastd-previous'] < 30) + ) & + (dataframe['fastk-previous'] < dataframe['fastd-previous']) & + (dataframe['fastk'] > dataframe['fastd']) & + (dataframe['mean-volume'] > 0.75) & + (dataframe['close'] > 0.00000100) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['slowadx'] < 25) & + ((dataframe['fastk'] > 70) | (dataframe['fastd'] > 70)) & + (dataframe['fastk-previous'] < dataframe['fastd-previous']) & + (dataframe['close'] > dataframe['ema5']) + ), + 'sell'] = 1 + return dataframe diff --git a/user_data/strategies/examplefreqtrade5.py b/user_data/strategies/examplefreqtrade5.py new file mode 100644 index 000000000..1a73c3228 --- /dev/null +++ b/user_data/strategies/examplefreqtrade5.py @@ -0,0 +1,156 @@ + +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +import numpy # noqa + + +class Examplestrategy5(IStrategy): + """ + Strategy 005 + author@: Gerald Lonlas + github@: https://github.com/freqtrade/freqtrade-strategies + How to use it? + > python3 ./freqtrade/main.py -s Strategy005 + """ + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "1440": 0.01, + "80": 0.02, + "40": 0.03, + "20": 0.04, + "0": 0.05 + } + + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + stoploss = -0.10 + + # Optimal ticker interval for the strategy + ticker_interval = '5m' + + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = 0.02 + + # run "populate_indicators" only for new candle + process_only_new_candles = False + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Optional order type mapping + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Adds several different TA indicators to the given DataFrame + Performance Note: For the best performance be frugal on the number of indicators + you are using. Let uncomment only the indicator you are using in your strategies + or your hyperopt configuration, otherwise you will waste your memory and CPU usage. + """ + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + + # Minus Directional Indicator / Movement + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) + rsi = 0.1 * (dataframe['rsi'] - 50) + dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) + # Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy) + dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1) + + # Stoch fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # Overlap Studies + # ------------------------------------ + + # SAR Parabol + dataframe['sar'] = ta.SAR(dataframe) + + # SMA - Simple Moving Average + dataframe['sma'] = ta.SMA(dataframe, timeperiod=40) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + # Prod + ( + (dataframe['close'] > 0.00000200) & + (dataframe['volume'] > dataframe['volume'].rolling(200).mean() * 4) & + (dataframe['close'] < dataframe['sma']) & + (dataframe['fastd'] > dataframe['fastk']) & + (dataframe['rsi'] > 0) & + (dataframe['fastd'] > 0) & + # (dataframe['fisher_rsi'] < -0.94) + (dataframe['fisher_rsi_norma'] < 38.900000000000006) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + # Prod + ( + (qtpylib.crossed_above(dataframe['rsi'], 50)) & + (dataframe['macd'] < 0) & + (dataframe['minus_di'] > 0) + ) | + ( + (dataframe['sar'] > dataframe['close']) & + (dataframe['fisher_rsi'] > 0.3) + ), + + 'sell'] = 1 + return dataframe