├── .gitignore ├── Dockerfile ├── Dockerfile.pg ├── README.md ├── docker-compose.yml ├── docker-compose-pg.yml └── user_data └── strategies ├── ActionZone.py └── MultiActionZone.py /.gitignore: -------------------------------------------------------------------------------- 1 | user_data/config.json 2 | user_data/data* 3 | user_data/hyperopt_results* 4 | user_data/hyperopts* 5 | user_data/logs* 6 | user_data/notebooks* 7 | user_data/plot* 8 | user_data/backtest_results* -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM freqtradeorg/freqtrade:develop 2 | 3 | # Switch user to root if you must install something from apt 4 | # Don't forget to switch the user back below! 5 | # USER root 6 | 7 | # The below dependency - pyti - serves as an example. Please use whatever you need! 8 | RUN pip install --user pandas_ta plotly datetime 9 | 10 | # USER ftuser 11 | -------------------------------------------------------------------------------- /Dockerfile.pg: -------------------------------------------------------------------------------- 1 | FROM freqtradeorg/freqtrade:develop 2 | 3 | # Switch user to root if you must install something from apt 4 | # Don't forget to switch the user back below! 5 | # USER root 6 | 7 | # The below dependency - pyti - serves as an example. Please use whatever you need! 8 | RUN pip install --user pandas_ta plotly datetime psycopg2-binary 9 | 10 | # USER ftuser 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ft-action-zone 2 | 3 | trading strategy for [freqtrade](https://www.freqtrade.io/) crypto bot it base on [CDC-ActionZone](https://www.tradingview.com/script/rGpAOoLi-CDC-ActionZone-V3-2020/) Indicator by [piriya33](https://www.tradingview.com/u/piriya33/) 4 | 5 | ## Clone The Repository 6 | 7 | if you just clone this repository simple by `git clone` you need to create you self config like [this](https://www.freqtrade.io/en/latest/docker_quickstart/#docker-quick-start) 8 | 9 | jsut type 10 | 11 | ``` 12 | # Pull the freqtrade image 13 | docker-compose pull 14 | 15 | # Create configuration - Requires answering interactive questions 16 | docker-compose run --rm freqtrade new-config --config user_data/config.json 17 | ``` 18 | 19 | then 20 | 21 | ``` 22 | docker-compose up -d 23 | ``` 24 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | services: 4 | freqtrade: 5 | platform: linux/x86_64 6 | # image: freqtradeorg/freqtrade:stable 7 | image: freqtrade 8 | build: 9 | context: . 10 | dockerfile: "./Dockerfile" 11 | # image: freqtradeorg/freqtrade:develop 12 | # Use plotting image 13 | # image: freqtradeorg/freqtrade:develop_plot 14 | # Build step - only needed when additional dependencies are needed 15 | # build: 16 | # context: . 17 | # dockerfile: "./docker/Dockerfile.custom" 18 | restart: unless-stopped 19 | container_name: freqtrade 20 | volumes: 21 | - "./user_data:/freqtrade/user_data" 22 | # Expose api on port 8080 (localhost only) 23 | # Please read the https://www.freqtrade.io/en/latest/rest-api/ documentation 24 | # before enabling this. 25 | # ports: 26 | # - "127.0.0.1:8080:8080" 27 | # Default command used when running `docker compose up` 28 | command: > 29 | trade 30 | --logfile /freqtrade/user_data/logs/freqtrade.log 31 | --db-url sqlite:////freqtrade/user_data/tradesv3.sqlite 32 | --config /freqtrade/user_data/config.json 33 | --strategy ActionZone 34 | -------------------------------------------------------------------------------- /docker-compose-pg.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | services: 4 | freqtrade: 5 | depends_on: 6 | - db 7 | # platform: linux/x86_64 8 | # image: freqtradeorg/freqtrade:stable 9 | image: freqtrade 10 | build: 11 | context: . 12 | dockerfile: "./Dockerfile.pg" 13 | # image: freqtradeorg/freqtrade:develop 14 | # Use plotting image 15 | # image: freqtradeorg/freqtrade:develop_plot 16 | # Build step - only needed when additional dependencies are needed 17 | # build: 18 | # context: . 19 | # dockerfile: "./docker/Dockerfile.custom" 20 | restart: unless-stopped 21 | container_name: freqtrade 22 | volumes: 23 | - "./user_data:/freqtrade/user_data" 24 | # Expose api on port 8080 (localhost only) 25 | # Please read the https://www.freqtrade.io/en/latest/rest-api/ documentation 26 | # before enabling this. 27 | ports: 28 | - "8080:8080" 29 | # Default command used when running `docker compose up` 30 | command: > 31 | trade 32 | --logfile /freqtrade/user_data/logs/freqtrade.log 33 | --db-url postgresql+psycopg2://postgres:bj2mkrkmUx5YfAiRQ6xsyEDaw3YBn7@db:5432/freqtrade 34 | --config /freqtrade/user_data/config.json 35 | --strategy ActionZone 36 | 37 | db: 38 | image: postgres 39 | restart: always 40 | ports: 41 | - "5432:5432" 42 | environment: 43 | POSTGRES_INITDB_ARGS: --auth-host=md5 44 | POSTGRES_PASSWORD: bj2mkrkmUx5YfAiRQ6xsyEDaw3YBn7 45 | POSTGRES_DB: freqtrade 46 | POSTGRES_HOST_AUTH_METHOD: md5 47 | -------------------------------------------------------------------------------- /user_data/strategies/ActionZone.py: -------------------------------------------------------------------------------- 1 | # pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement 2 | # flake8: noqa: F401 3 | # isort: skip_file 4 | # --- Do not remove these libs --- 5 | import numpy as np # noqa 6 | import pandas as pd # noqa 7 | from pandas import DataFrame 8 | 9 | from freqtrade.strategy import IStrategy 10 | from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter 11 | from freqtrade.persistence import Trade 12 | 13 | # -------------------------------- 14 | # Add your lib to import here 15 | import talib.abstract as ta 16 | import freqtrade.vendor.qtpylib.indicators as qtpylib 17 | from datetime import datetime, timedelta 18 | 19 | class ActionZone(IStrategy): 20 | # Strategy interface version - allow new iterations of the strategy interface. 21 | # Check the documentation or the Sample strategy to get the latest version. 22 | INTERFACE_VERSION = 2 23 | 24 | # Minimal ROI designed for the strategy. 25 | # This attribute will be overridden if the config file contains "minimal_roi". 26 | minimal_roi = { 27 | "0": 100000 28 | } 29 | 30 | # Optimal stoploss designed for the strategy. 31 | # This attribute will be overridden if the config file contains "stoploss". 32 | stoploss = -1.00 33 | use_custom_stoploss = True 34 | 35 | # Trailing stoploss 36 | trailing_stop = False 37 | # trailing_only_offset_is_reached = False 38 | # trailing_stop_positive = 0.01 39 | # trailing_stop_positive_offset = 0.0 # Disabled / not configured 40 | 41 | 42 | # Optimal timeframe for the strategy. 43 | timeframe = '1d' 44 | 45 | # Run "populate_indicators()" only for new candle. 46 | process_only_new_candles = False 47 | 48 | # These values can be overridden in the "ask_strategy" section in the config. 49 | use_exit_signal = True 50 | exit_profit_only = False 51 | 52 | # Number of candles the strategy requires before producing valid signals 53 | startup_candle_count: int = 30 54 | 55 | # Number of candles used for calculations in lowest price of period 56 | min_price_period: int = 32 57 | 58 | # max loss able for calculation position size 59 | max_loss_per_trade = 10 # USD 60 | 61 | fast_ma_period = 12 62 | slow_ma_period = 26 63 | 64 | # Optional order type mapping. 65 | order_types = { 66 | 'entry': 'limit', 67 | 'exit': 'limit', 68 | 'stoploss': 'market', 69 | 'stoploss_on_exchange': False 70 | } 71 | 72 | # Optional order time in force. 73 | order_time_in_force = { 74 | 'entry': 'gtc', 75 | 'exit': 'gtc' 76 | } 77 | 78 | plot_config = { 79 | 'main_plot': { 80 | 'fastMA': { 81 | 'color': 'red', 82 | 'fill_to': 'slowMA', 83 | 'fill_color': 'rgba(232, 232, 232,0.2)' 84 | }, 85 | 'slowMA': { 86 | 'color': 'blue', 87 | }, 88 | }, 89 | } 90 | 91 | def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: float, max_stake: float, **kwargs) -> float: 92 | dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) 93 | last_candle = dataframe.iloc[-1].squeeze() 94 | 95 | stop_price = last_candle['lowest'] 96 | volume_for_buy = self.max_loss_per_trade / (current_rate - stop_price) 97 | use_money = volume_for_buy * current_rate 98 | 99 | return use_money 100 | 101 | def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: 102 | """ 103 | Adds several different TA indicators to the given DataFrame 104 | 105 | Performance Note: For the best performance be frugal on the number of indicators 106 | you are using. Let uncomment only the indicator you are using in your strategies 107 | or your hyperopt configuration, otherwise you will waste your memory and CPU usage. 108 | :param dataframe: Dataframe with data from the exchange 109 | :param metadata: Additional information, like the currently traded pair 110 | :return: a Dataframe with all mandatory indicators for the strategies 111 | """ 112 | 113 | # MIN - Lowest value over a specified period 114 | lowest = ta.MIN(dataframe, timeperiod=self.min_price_period) 115 | dataframe['lowest'] = lowest 116 | 117 | # EMA - Exponential Moving Average 118 | fastEMA = ta.EMA(dataframe, timeperiod=self.fast_ma_period) 119 | slowEMA = ta.EMA(dataframe, timeperiod=self.slow_ma_period) 120 | dataframe['fastMA'] = fastEMA 121 | dataframe['slowMA'] = slowEMA 122 | 123 | 124 | return dataframe 125 | 126 | def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: 127 | """ 128 | Based on TA indicators, populates the buy signal for the given dataframe 129 | :param dataframe: DataFrame populated with indicators 130 | :param metadata: Additional information, like the currently traded pair 131 | :return: DataFrame with buy column 132 | """ 133 | dataframe.loc[ 134 | ( 135 | (dataframe['fastMA'] > dataframe['slowMA']) & # Bull 136 | (dataframe['close'] > dataframe['fastMA'] ) & # Price Cross Up 137 | (dataframe['volume'] > 0) # Make sure Volume is not 0 138 | ), 139 | 'enter_long'] = 1 140 | 141 | return dataframe 142 | 143 | def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: 144 | """ 145 | Based on TA indicators, populates the sell signal for the given dataframe 146 | :param dataframe: DataFrame populated with indicators 147 | :param metadata: Additional information, like the currently traded pair 148 | :return: DataFrame with sell column 149 | """ 150 | dataframe.loc[ 151 | ( 152 | (dataframe['fastMA'] < dataframe['slowMA']) & # Bear 153 | (dataframe['close'] < dataframe['fastMA'] ) & # Price Cross Down 154 | (dataframe['volume'] > 0) # Make sure Volume is not 0 155 | ), 156 | 'exit_long'] = 1 157 | return dataframe 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /user_data/strategies/MultiActionZone.py: -------------------------------------------------------------------------------- 1 | # pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement 2 | # flake8: noqa: F401 3 | # isort: skip_file 4 | # --- Do not remove these libs --- 5 | import numpy as np # noqa 6 | import pandas as pd # noqa 7 | from pandas import DataFrame 8 | 9 | from freqtrade.strategy import IStrategy 10 | from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter 11 | from freqtrade.persistence import Trade 12 | from technical.util import resample_to_interval, resampled_merge 13 | 14 | # -------------------------------- 15 | # Add your lib to import here 16 | import talib.abstract as ta 17 | import freqtrade.vendor.qtpylib.indicators as qtpylib 18 | from datetime import datetime, timedelta 19 | 20 | class MultiActionZone(IStrategy): 21 | # Strategy interface version - allow new iterations of the strategy interface. 22 | # Check the documentation or the Sample strategy to get the latest version. 23 | INTERFACE_VERSION = 2 24 | 25 | # Minimal ROI designed for the strategy. 26 | # This attribute will be overridden if the config file contains "minimal_roi". 27 | minimal_roi = { 28 | "0": 100000 29 | } 30 | 31 | # Optimal stoploss designed for the strategy. 32 | # This attribute will be overridden if the config file contains "stoploss". 33 | stoploss = -1.00 34 | use_custom_stoploss = True 35 | 36 | # Trailing stoploss 37 | trailing_stop = False 38 | # trailing_only_offset_is_reached = False 39 | # trailing_stop_positive = 0.01 40 | # trailing_stop_positive_offset = 0.0 # Disabled / not configured 41 | 42 | 43 | # Optimal timeframe for the strategy. 44 | timeframe = '4h' 45 | 46 | # Run "populate_indicators()" only for new candle. 47 | process_only_new_candles = False 48 | 49 | # These values can be overridden in the "ask_strategy" section in the config. 50 | use_sell_signal = True 51 | sell_profit_only = False 52 | ignore_roi_if_buy_signal = False 53 | 54 | # Number of candles the strategy requires before producing valid signals 55 | startup_candle_count: int = 30 56 | 57 | # Number of candles used for calculations in lowest price of period 58 | min_price_period: int = 32 59 | 60 | # max loss able for calculation position size 61 | max_loss_per_trade = 10 # USD 62 | 63 | long_period = 360 64 | 65 | # Optional order type mapping. 66 | order_types = { 67 | 'buy': 'limit', 68 | 'sell': 'limit', 69 | 'stoploss': 'market', 70 | 'stoploss_on_exchange': False 71 | } 72 | 73 | # Optional order time in force. 74 | order_time_in_force = { 75 | 'buy': 'gtc', 76 | 'sell': 'gtc' 77 | } 78 | 79 | plot_config = { 80 | 'main_plot': { 81 | 'fastMA': { 82 | 'color': 'red', 83 | 'fill_to': 'slowMA', 84 | 'fill_color': 'rgba(232, 232, 232,0.2)' 85 | }, 86 | 'slowMA': { 87 | 'color': 'blue', 88 | }, 89 | 'resample_{}_fastMA'.format(4 * long_period): { 90 | 'color': '#ffccd5', 91 | }, 92 | 'resample_{}_slowMA'.format(4 * long_period): { 93 | 'color': '#89c2d9', 94 | }, 95 | 'lowest': { 96 | 'color': '#fff3b0', 97 | } 98 | }, 99 | } 100 | 101 | 102 | def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: 103 | dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) 104 | last_candle = dataframe.iloc[-1].squeeze() 105 | 106 | stoploss_price = last_candle['lowest'] 107 | 108 | # set stoploss when is new order 109 | if current_profit == 0 and current_time - timedelta(minutes=1) < trade.open_date_utc: 110 | # Convert absolute price to percentage relative to current_rate 111 | return (stoploss_price / current_rate) - 1 112 | 113 | return 1 # return a value bigger than the initial stoploss to keep using the initial stoploss 114 | 115 | def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: float, max_stake: float, **kwargs) -> float: 116 | dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) 117 | last_candle = dataframe.iloc[-1].squeeze() 118 | 119 | stop_price = last_candle['lowest'] 120 | volume_for_buy = self.max_loss_per_trade / (current_rate - stop_price) 121 | use_money = volume_for_buy * current_rate 122 | 123 | return use_money 124 | 125 | def informative_pairs(self): 126 | """ 127 | Define additional, informative pair/interval combinations to be cached from the exchange. 128 | These pair/interval combinations are non-tradeable, unless they are part 129 | of the whitelist as well. 130 | For more information, please consult the documentation 131 | :return: List of tuples in the format (pair, interval) 132 | Sample: return [("ETH/USDT", "5m"), 133 | ("BTC/USDT", "15m"), 134 | ] 135 | """ 136 | return [] 137 | 138 | def get_ticker_indicator(self): 139 | return int(self.timeframe[:-1]) 140 | 141 | def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: 142 | """ 143 | Adds several different TA indicators to the given DataFrame 144 | 145 | Performance Note: For the best performance be frugal on the number of indicators 146 | you are using. Let uncomment only the indicator you are using in your strategies 147 | or your hyperopt configuration, otherwise you will waste your memory and CPU usage. 148 | :param dataframe: Dataframe with data from the exchange 149 | :param metadata: Additional information, like the currently traded pair 150 | :return: a Dataframe with all mandatory indicators for the strategies 151 | """ 152 | # MIN - Lowest value over a specified period 153 | lowest = ta.MIN(dataframe, timeperiod=self.min_price_period) 154 | dataframe['lowest'] = lowest 155 | 156 | # EMA - Exponential Moving Average Short 157 | fastMA_short = ta.EMA(dataframe, timeperiod=12) 158 | slowMA_short = ta.EMA(dataframe, timeperiod=26) 159 | dataframe['fastMA'] = fastMA_short 160 | dataframe['slowMA'] = slowMA_short 161 | 162 | # :param dataframe: dataframe containing close/high/low/open/volume 163 | # :param interval: to which ticker value in minutes would you like to resample it 164 | dataframe_long = resample_to_interval(dataframe, self.get_ticker_indicator() * self.long_period) 165 | 166 | # EMA - Exponential Moving Average Long 167 | fastEMA_long = ta.EMA(dataframe_long, timeperiod=12) 168 | slowEMA_long = ta.EMA(dataframe_long, timeperiod=26) 169 | dataframe_long['fastMA'] = fastEMA_long 170 | dataframe_long['slowMA'] = slowEMA_long 171 | 172 | dataframe = resampled_merge(dataframe, dataframe_long) 173 | dataframe.fillna(method='ffill', inplace=True) 174 | 175 | return dataframe 176 | 177 | def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: 178 | """ 179 | Based on TA indicators, populates the buy signal for the given dataframe 180 | :param dataframe: DataFrame populated with indicators 181 | :param metadata: Additional information, like the currently traded pair 182 | :return: DataFrame with buy column 183 | """ 184 | 185 | dataframe.loc[ 186 | ( 187 | (dataframe['resample_{}_fastMA'.format(self.get_ticker_indicator() * self.long_period)] > dataframe['resample_{}_slowMA'.format(self.get_ticker_indicator() * self.long_period)]) & 188 | (dataframe['resample_{}_close'.format(self.get_ticker_indicator() * self.long_period)] > dataframe['resample_{}_fastMA'.format(self.get_ticker_indicator() * self.long_period)] ) & 189 | (dataframe['fastMA'] > dataframe['slowMA']) & 190 | (dataframe['close'] > dataframe['fastMA'] ) & 191 | (dataframe['volume'] > 0) # Make sure Volume is not 0 192 | ), 193 | 'buy'] = 1 194 | 195 | return dataframe 196 | 197 | def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: 198 | """ 199 | Based on TA indicators, populates the sell signal for the given dataframe 200 | :param dataframe: DataFrame populated with indicators 201 | :param metadata: Additional information, like the currently traded pair 202 | :return: DataFrame with sell column 203 | """ 204 | dataframe.loc[ 205 | ( 206 | (dataframe['resample_{}_fastMA'.format(self.get_ticker_indicator() * self.long_period)] < dataframe['resample_{}_slowMA'.format(self.get_ticker_indicator() * self.long_period)]) & 207 | (dataframe['resample_{}_close'.format(self.get_ticker_indicator() * self.long_period)] < dataframe['resample_{}_fastMA'.format(self.get_ticker_indicator() * self.long_period)] ) & 208 | (dataframe['fastMA'] < dataframe['slowMA']) & 209 | (dataframe['close'] < dataframe['fastMA'] ) & 210 | (dataframe['volume'] > 0) # Make sure Volume is not 0 211 | ), 212 | 'sell'] = 1 213 | return dataframe 214 | 215 | 216 | 217 | --------------------------------------------------------------------------------