diff --git a/.gitignore b/.gitignore index dc87402b4..a066001db 100644 --- a/.gitignore +++ b/.gitignore @@ -109,4 +109,5 @@ target/ !config_examples/config_ftx.example.json !config_examples/config_full.example.json !config_examples/config_kraken.example.json -!config_examples/config_freqai.example.json +!config_examples/config_freqai_futures.example.json +!config_examples/config_freqai_spot.example.json diff --git a/config_examples/config_freqai_futures.example.json b/config_examples/config_freqai_futures.example.json new file mode 100644 index 000000000..cb545acdc --- /dev/null +++ b/config_examples/config_freqai_futures.example.json @@ -0,0 +1,93 @@ +{ + "trading_mode": "futures", + "margin_mode": "isolated", + "max_open_trades": 5, + "stake_currency": "USDT", + "stake_amount": 200, + "tradable_balance_ratio": 1, + "fiat_display_currency": "USD", + "dry_run": true, + "timeframe": "3m", + "dry_run_wallet": 1000, + "cancel_open_orders_on_exit": true, + "unfilledtimeout": { + "entry": 10, + "exit": 30 + }, + "exchange": { + "name": "okx", + "key": "", + "secret": "", + "ccxt_config": { + "enableRateLimit": true + }, + "ccxt_async_config": { + "enableRateLimit": true, + "rateLimit": 200 + }, + "pair_whitelist": [ + "AGLD/USDT:USDT", + "1INCH/USDT:USDT", + "AAVE/USDT:USDT", + "ALGO/USDT:USDT", + "ALPHA/USDT:USDT", + "API3/USDT:USDT", + "AVAX/USDT:USDT", + "AXS/USDT:USDT", + "BCH/USDT:USDT" + ], + "pair_blacklist": [] + }, + "entry_pricing": { + "price_side": "same", + "use_order_book": true, + "order_book_top": 1, + "price_last_balance": 0.0, + "check_depth_of_market": { + "enabled": false, + "bids_to_ask_delta": 1 + } + }, + "exit_pricing": { + "price_side": "other", + "use_order_book": true, + "order_book_top": 1 + }, + "pairlists": [ + { + "method": "StaticPairList" + } + ], + "freqai": { + "timeframes": [ + "3m", + "15m", + "1h" + ], + "train_period": 20, + "backtest_period": 2, + "identifier": "example", + "live_trained_timestamp": 0, + "corr_pairlist": [ + "BTC/USDT:USDT", + "ETH/USDT:USDT" + ], + "feature_parameters": { + "period": 20, + "shift": 2, + "DI_threshold": 0, + "weight_factor": 0.9, + "principal_component_analysis": false, + "use_SVM_to_remove_outliers": true, + "stratify": 0 + }, + "data_split_parameters": { + "test_size": 0.33, + "random_state": 1 + }, + "model_training_parameters": { + "n_estimators": 1000, + "task_type": "CPU" + } + } +} diff --git a/config_examples/config_freqai.example.json b/config_examples/config_freqai_spot.example.json similarity index 89% rename from config_examples/config_freqai.example.json rename to config_examples/config_freqai_spot.example.json index b6c7ba7d8..e311fe280 100644 --- a/config_examples/config_freqai.example.json +++ b/config_examples/config_freqai_spot.example.json @@ -6,7 +6,7 @@ "fiat_display_currency": "USD", "dry_run": true, "timeframe": "5m", - "dry_run_wallet": 1000, + "dry_run_wallet": 4000, "cancel_open_orders_on_exit": true, "unfilledtimeout": { "entry": 10, @@ -51,7 +51,8 @@ "freqai": { "timeframes": [ "5m", - "15m" + "15m", + "4h" ], "train_period": 30, "backtest_period": 7, @@ -60,16 +61,18 @@ "corr_pairlist": [ "BTC/USDT", "ETH/USDT", - "DOT/USDT" + "DOT/USDT", + "MATIC/USDT", + "SOL/USDT" ], "feature_parameters": { - "period": 24, + "period": 20, "shift": 1, "DI_threshold": 0, "weight_factor": 0.9, "principal_component_analysis": false, - "use_SVM_to_remove_outliers": true, - "stratify": 3 + "use_SVM_to_remove_outliers": false, + "stratify": 0 }, "data_split_parameters": { "test_size": 0.33, @@ -77,8 +80,6 @@ }, "model_training_parameters": { "n_estimators": 1000, - "random_state": 1, - "learning_rate": 0.1, "task_type": "CPU" } }, diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index ac0ad9d5a..0d5b27385 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -56,6 +56,7 @@ class IFreqaiModel(ABC): self.set_full_path() self.data_drawer = FreqaiDataDrawer(Path(self.full_path), self.config['exchange']['pair_whitelist']) + self.lock = threading.Lock() def assert_config(self, config: Dict[str, Any]) -> None: diff --git a/freqtrade/templates/FreqaiExampleStrategy.py b/freqtrade/templates/FreqaiExampleStrategy.py index ed7c828cc..94fa0266d 100644 --- a/freqtrade/templates/FreqaiExampleStrategy.py +++ b/freqtrade/templates/FreqaiExampleStrategy.py @@ -7,8 +7,10 @@ import talib.abstract as ta from pandas import DataFrame from technical import qtpylib +from freqtrade.exchange import timeframe_to_prev_date from freqtrade.freqai.strategy_bridge import CustomModel -from freqtrade.strategy import merge_informative_pair +from freqtrade.persistence import Trade +from freqtrade.strategy import DecimalParameter, IntParameter, merge_informative_pair from freqtrade.strategy.interface import IStrategy @@ -46,6 +48,11 @@ class FreqaiExampleStrategy(IStrategy): stoploss = -0.05 use_sell_signal = True startup_candle_count: int = 300 + can_short = False + + linear_roi_offset = DecimalParameter(0.00, 0.02, default=0.005, space='sell', + optimize=False, load=True) + max_roi_time_long = IntParameter(0, 800, default=400, space='sell', optimize=False, load=True) def informative_pairs(self): whitelist_pairs = self.dp.current_whitelist() @@ -182,25 +189,88 @@ class FreqaiExampleStrategy(IStrategy): dataframe["sell_roi"] = dataframe["target_mean"] - dataframe["target_std"] return dataframe - def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame: - buy_conditions = [ - (dataframe["prediction"] > dataframe["target_roi"]) & (dataframe["do_predict"] == 1) + enter_long_conditions = [ + df['do_predict'] == 1, + df['prediction'] > df["target_roi"] ] - if buy_conditions: - dataframe.loc[reduce(lambda x, y: x | y, buy_conditions), "buy"] = 1 + if enter_long_conditions: + df.loc[reduce(lambda x, y: x & y, + enter_long_conditions), ["enter_long", "enter_tag"]] = (1, 'long') - return dataframe - - def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - sell_conditions = [ - (dataframe["do_predict"] <= 0) + enter_short_conditions = [ + df['do_predict'] == 1, + df['prediction'] < df["sell_roi"] ] - if sell_conditions: - dataframe.loc[reduce(lambda x, y: x | y, sell_conditions), "sell"] = 1 - return dataframe + if enter_short_conditions: + df.loc[reduce(lambda x, y: x & y, + enter_short_conditions), ["enter_short", "enter_tag"]] = (1, 'short') + + return df + + def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame: + exit_long_conditions = [ + df['do_predict'] == 1, + df['prediction'] < df['sell_roi'] * 0.25 + ] + if exit_long_conditions: + df.loc[reduce(lambda x, y: x & y, exit_long_conditions), "exit_long"] = 1 + + exit_short_conditions = [ + df['do_predict'] == 1, + df['prediction'] > df['target_roi'] * 0.25 + ] + if exit_short_conditions: + df.loc[reduce(lambda x, y: x & y, exit_short_conditions), "exit_short"] = 1 + + return df def get_ticker_indicator(self): return int(self.config["timeframe"][:-1]) + + def custom_exit(self, pair: str, trade: Trade, current_time, current_rate, + current_profit, **kwargs): + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + + trade_date = timeframe_to_prev_date(self.config['timeframe'], trade.open_date_utc) + trade_candle = dataframe.loc[(dataframe['date'] == trade_date)] + + if trade_candle.empty: + return None + trade_candle = trade_candle.squeeze() + pair_dict = self.model.bridge.data_drawer.pair_dict + entry_tag = trade.enter_tag + + if 'prediction' + entry_tag not in pair_dict[pair]: + with self.model.bridge.lock: + self.model.bridge.data_drawer.pair_dict[pair][ + 'prediction' + entry_tag] = abs(trade_candle['prediction']) + self.model.bridge.data_drawer.save_drawer_to_disk() + else: + if pair_dict[pair]['prediction' + entry_tag] > 0: + roi_price = abs(trade_candle['prediction' + entry_tag]) + else: + with self.model.bridge.lock: + self.model.bridge.data_drawer.pair_dict[pair][ + 'prediction' + entry_tag] = abs(trade_candle['prediction']) + self.model.bridge.data_drawer.save_drawer_to_disk() + + roi_price = abs(trade_candle['prediction']) + roi_time = self.max_roi_time_long.value + + roi_decay = roi_price * (1 - ((current_time - trade.open_date_utc).seconds) / + (roi_time * 60)) + if roi_decay < 0: + roi_decay = self.linear_roi_offset.value + else: + roi_decay += self.linear_roi_offset.value + + if current_profit > roi_price: # roi_decay: + with self.model.bridge.lock: + self.model.bridge.data_drawer.pair_dict[pair]['prediction' + entry_tag] = 0 + self.model.bridge.data_drawer.save_drawer_to_disk() + return 'roi_custom_win'