💬 Forum

AI_Strategy

Lunacub-sty/ai-trading-bot/user_data/strategies/AI_Strategy.py · first seen 2026-07-16 · repo updated 2026-02-21 · ⬇ 1 download

Basics mode: spot timeframe: 5m
Settings stoploss: -0.03 has minimal roi trailing process only new candles startup candle count: 30
Indicators ADX RSI Stochastic ta
Concepts ml trailing
Other ml scikit-learn joblib psutil
15 related strategies ( identical code, similar name)

Each tile is a different kind of check — from an instant code lint to full sandboxed backtests and forward tests on recent data. Not sure what a check actually proves? See the FAQ →

Source

Download Raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# AI交易策略
# 基于机器学习的加密货币交易策略

from freqtrade.strategy import IStrategy
from freqtrade.persistence import Trade
from freqtrade.strategy import informative
from pandas import DataFrame
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
import ta
import joblib
import os
from datetime import datetime, timedelta


class AI_Strategy(IStrategy):
    """
    AI交易策略
    基于机器学习的交易策略,使用技术指标作为特征,预测市场走势
    """
    
    # 策略配置
    timeframe = '5m'  # 交易时间周期
    minimal_roi = {
        "0": 0.05,  # 5%的回报率
        "30": 0.02,  # 30分钟后2%的回报率
        "60": 0.01,  # 60分钟后1%的回报率
        "120": 0.005  # 120分钟后0.5%的回报率
    }
    stoploss = -0.03  # 止损设置为3%
    trailing_stop = True  # 启用追踪止损
    trailing_stop_positive = 0.01  # 追踪止损的正偏移
    trailing_stop_positive_offset = 0.02  # 追踪止损的正偏移开始点
    trailing_only_offset_is_reached = True  # 只有达到偏移点才开始追踪
    process_only_new_candles = True  # 只处理新的K线
    use_exit_signal = True  # 使用退出信号
    exit_profit_only = False  # 不仅在盈利时退出
    ignore_roi_if_entry_signal = False  # 不忽略ROI如果有入场信号
    startup_candle_count = 30  # 启动所需的K线数量
    
    def __init__(self, config):
        """
        初始化策略
        """
        super().__init__(config)
        self.model = RandomForestClassifier(
            n_estimators=100, 
            random_state=42,
            n_jobs=-1,  # 使用所有可用CPU核心
            max_depth=10,  # 限制树深度,防止过拟合
            min_samples_split=5,  # 最小分裂样本数
            min_samples_leaf=2  # 最小叶节点样本数
        )
        self.scaler = StandardScaler()
        self.model_trained = False
        self.last_train_time = None
        self.train_interval = timedelta(hours=6)  # 模型训练间隔
        
        # 模型保存路径
        self.model_dir = os.path.join("user_data", "models")
        os.makedirs(self.model_dir, exist_ok=True)
        self.model_path = os.path.join(self.model_dir, "ai_strategy_model.joblib")
        self.scaler_path = os.path.join(self.model_dir, "ai_strategy_scaler.joblib")
        
        # 使用Freqtrade的日志系统
        import logging
        self.logger = logging.getLogger(__name__)
        self.logger.info("AI Strategy initialized")
        
        # 尝试加载预训练模型
        self.load_model()
    
    def load_model(self):
        """
        加载预训练模型
        """
        try:
            if os.path.exists(self.model_path) and os.path.exists(self.scaler_path):
                self.model = joblib.load(self.model_path)
                self.scaler = joblib.load(self.scaler_path)
                self.model_trained = True
                self.last_train_time = datetime.now()
                self.logger.info("成功加载预训练模型")
        except Exception as e:
            self.logger.error(f"加载模型失败: {e}")
    
    def save_model(self):
        """
        保存模型
        """
        try:
            # 检查模型目录大小
            self._check_model_directory_size()
            
            joblib.dump(self.model, self.model_path)
            joblib.dump(self.scaler, self.scaler_path)
            self.logger.info("模型保存成功")
        except Exception as e:
            self.logger.error(f"保存模型失败: {e}")
    
    def _check_model_directory_size(self):
        """
        检查并清理模型目录
        """
        try:
            # 模型目录最大大小(MB)
            max_model_dir_size = 100
            
            # 计算目录大小
            total_size = 0
            for root, dirs, files in os.walk(self.model_dir):
                for file in files:
                    file_path = os.path.join(root, file)
                    total_size += os.path.getsize(file_path)
            
            total_size_mb = total_size / 1024 / 1024
            self.logger.info(f"模型目录大小: {total_size_mb:.2f} MB")
            
            # 如果目录大小超过阈值,删除旧模型文件
            if total_size_mb > max_model_dir_size:
                self.logger.warning(f"模型目录大小超过限制,清理旧文件")
                
                # 获取所有模型文件并按修改时间排序
                model_files = []
                for root, dirs, files in os.walk(self.model_dir):
                    for file in files:
                        file_path = os.path.join(root, file)
                        if file.endswith('.joblib'):
                            model_files.append((file_path, os.path.getmtime(file_path)))
                
                # 按修改时间排序
                model_files.sort(key=lambda x: x[1])
                
                # 删除最旧的文件,直到目录大小低于阈值
                while total_size_mb > max_model_dir_size and model_files:
                    oldest_file, _ = model_files.pop(0)
                    file_size = os.path.getsize(oldest_file)
                    os.remove(oldest_file)
                    total_size -= file_size
                    total_size_mb = total_size / 1024 / 1024
                    self.logger.info(f"删除旧模型文件: {oldest_file}")
                    
        except Exception as e:
            self.logger.error(f"检查模型目录大小失败: {e}")
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        添加技术指标作为特征
        """
        # 基本技术指标
        dataframe['rsi'] = ta.momentum.rsi(dataframe['close'], window=14)
        dataframe['macd'] = ta.trend.macd_diff(dataframe['close'])
        dataframe['bb_upper'] = ta.volatility.bollinger_hband(dataframe['close'])
        dataframe['bb_lower'] = ta.volatility.bollinger_lband(dataframe['close'])
        dataframe['bb_middle'] = ta.volatility.bollinger_mavg(dataframe['close'])
        
        # 移动平均线
        dataframe['ma5'] = ta.trend.sma_indicator(dataframe['close'], window=5)
        dataframe['ma10'] = ta.trend.sma_indicator(dataframe['close'], window=10)
        dataframe['ma20'] = ta.trend.sma_indicator(dataframe['close'], window=20)
        
        # 额外技术指标
        dataframe['stoch'] = ta.momentum.stoch(dataframe['high'], dataframe['low'], dataframe['close'])
        dataframe['stoch_signal'] = ta.momentum.stoch_signal(dataframe['high'], dataframe['low'], dataframe['close'])
        dataframe['atr'] = ta.volatility.average_true_range(dataframe['high'], dataframe['low'], dataframe['close'])
        dataframe['adx'] = ta.trend.adx(dataframe['high'], dataframe['low'], dataframe['close'])
        
        # 价格变化
        dataframe['price_change'] = dataframe['close'].pct_change() * 100
        dataframe['volume_change'] = dataframe['volume'].pct_change() * 100
        
        # 特征工程
        dataframe['rsi_overbought'] = (dataframe['rsi'] > 70).astype(int)
        dataframe['rsi_oversold'] = (dataframe['rsi'] < 30).astype(int)
        dataframe['price_above_ma5'] = (dataframe['close'] > dataframe['ma5']).astype(int)
        dataframe['price_above_ma20'] = (dataframe['close'] > dataframe['ma20']).astype(int)
        dataframe['bb_width'] = (dataframe['bb_upper'] - dataframe['bb_lower']) / dataframe['bb_middle'] * 100
        dataframe['ma_diff'] = (dataframe['ma5'] - dataframe['ma20']) / dataframe['ma20'] * 100
        dataframe['stoch_overbought'] = (dataframe['stoch'] > 80).astype(int)
        dataframe['stoch_oversold'] = (dataframe['stoch'] < 20).astype(int)
        
        # 目标变量:预测下一个周期的价格变化方向
        dataframe['target'] = np.where(dataframe['close'].shift(-1) > dataframe['close'], 1, 0)
        
        # 训练模型
        current_time = datetime.now()
        if (len(dataframe) > 100 and not self.model_trained) or \
           (self.last_train_time and (current_time - self.last_train_time) > self.train_interval):
            self.train_model(dataframe)
            self.last_train_time = current_time
        
        # 预测
        if self.model_trained:
            features = self.get_features(dataframe)
            if len(features) > 0:
                # 只预测最后一行,提高性能
                last_features = features[-1:]
                prediction = self.model.predict(last_features)
                dataframe.loc[dataframe.index[-1], 'prediction'] = prediction[0]
                
                # 添加预测概率
                if hasattr(self.model, 'predict_proba'):
                    proba = self.model.predict_proba(last_features)
                    dataframe.loc[dataframe.index[-1], 'prediction_prob'] = proba[0][1]
        
        return dataframe
    
    def get_features(self, dataframe: DataFrame) -> np.array:
        """
        获取特征数据
        """
        feature_columns = [
            'rsi', 'macd', 'bb_width', 'price_change', 'volume_change',
            'rsi_overbought', 'rsi_oversold', 'price_above_ma5', 'price_above_ma20',
            'stoch', 'stoch_signal', 'atr', 'adx', 'ma_diff',
            'stoch_overbought', 'stoch_oversold'
        ]
        features = dataframe[feature_columns].dropna()
        if len(features) > 0:
            return self.scaler.transform(features)
        return np.array([])
    
    def train_model(self, dataframe: DataFrame) -> None:
        """
        训练机器学习模型
        """
        try:
            # 检查系统资源
            if not self._check_system_resources():
                self.logger.warning("系统资源不足,跳过模型训练")
                return
            
            # 准备训练数据
            feature_columns = [
                'rsi', 'macd', 'bb_width', 'price_change', 'volume_change',
                'rsi_overbought', 'rsi_oversold', 'price_above_ma5', 'price_above_ma20',
                'stoch', 'stoch_signal', 'atr', 'adx', 'ma_diff',
                'stoch_overbought', 'stoch_oversold'
            ]
            
            # 移除NaN值
            train_data = dataframe.dropna(subset=feature_columns + ['target'])
            
            # 限制训练数据量
            max_train_size = 5000  # 最多使用5000条数据,适合2核CPU
            if len(train_data) > max_train_size:
                train_data = train_data.tail(max_train_size)
                self.logger.info(f"训练数据量过大,限制为{max_train_size}条")
            
            if len(train_data) > 100:  # 增加最小训练数据量
                # 分割特征和目标变量
                X = train_data[feature_columns]
                y = train_data['target']
                
                # 分割训练集和测试集
                X_train, X_test, y_train, y_test = train_test_split(
                    X, y, test_size=0.2, random_state=42
                )
                
                # 标准化特征
                self.scaler.fit(X_train)
                X_train_scaled = self.scaler.transform(X_train)
                X_test_scaled = self.scaler.transform(X_test)
                
                # 训练模型
                self.model.fit(X_train_scaled, y_train)
                
                # 评估模型
                y_pred = self.model.predict(X_test_scaled)
                accuracy = accuracy_score(y_test, y_pred)
                
                # 生成详细的分类报告
                report = classification_report(y_test, y_pred, output_dict=True)
                precision = report['1']['precision']
                recall = report['1']['recall']
                f1_score = report['1']['f1-score']
                
                self.model_trained = True
                self.last_train_time = datetime.now()
                
                # 保存模型
                self.save_model()
                
                self.logger.info(f"模型训练完成,准确率: {accuracy:.2f}, 精确率: {precision:.2f}, 召回率: {recall:.2f}, F1分数: {f1_score:.2f}")
        except Exception as e:
            self.logger.error(f"模型训练失败: {e}")
    
    def _check_system_resources(self):
        """
        检查系统资源是否充足
        """
        try:
            import psutil
            
            # 检查内存使用
            memory = psutil.virtual_memory()
            memory_percent = memory.percent
            if memory_percent > 70:
                self.logger.warning(f"内存使用率过高: {memory_percent:.2f}%")
                return False
            
            # 检查CPU使用
            cpu_percent = psutil.cpu_percent(interval=1)
            if cpu_percent > 70:
                self.logger.warning(f"CPU使用率过高: {cpu_percent:.2f}%")
                return False
            
            # 检查存储使用
            stat = os.statvfs(self.model_dir)
            free_space = stat.f_bavail * stat.f_frsize
            total_space = stat.f_blocks * stat.f_frsize
            storage_percent = (total_space - free_space) / total_space * 100
            if storage_percent > 70:
                self.logger.warning(f"存储使用率过高: {storage_percent:.2f}%")
                return False
            
            return True
        except Exception as e:
            self.logger.error(f"检查系统资源失败: {e}")
            return True  # 检查失败时默认允许训练
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        生成入场信号
        """
        # 确保prediction列存在
        if 'prediction' not in dataframe.columns:
            dataframe['prediction'] = 0
        
        # 确保prediction_prob列存在
        if 'prediction_prob' not in dataframe.columns:
            dataframe['prediction_prob'] = 0.5
        
        dataframe.loc[
            (
                (dataframe['prediction'] == 1) &  # 模型预测上涨
                (dataframe['rsi'] < 70) &  # RSI不在超买区域
                (dataframe['volume'] > 0) &  # 有成交量
                (dataframe['price_above_ma5'] == 1) &  # 价格在5日均线之上
                (dataframe['prediction_prob'] > 0.6)  # 预测概率大于60%
            ),
            'enter_long'] = 1
        
        return dataframe
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        生成退出信号
        """
        # 确保prediction列存在
        if 'prediction' not in dataframe.columns:
            dataframe['prediction'] = 0
        
        dataframe.loc[
            (
                (dataframe['prediction'] == 0) &  # 模型预测下跌
                (dataframe['rsi'] > 30) &  # RSI不在超卖区域
                (dataframe['volume'] > 0)  # 有成交量
            ),
            'exit_long'] = 1
        
        return dataframe
    
    def custom_exit(self, trade: Trade, current_time, current_rate, current_profit, **kwargs):
        """
        自定义退出逻辑
        """
        # 可以在这里添加额外的退出逻辑
        # 例如基于当前市场状况的动态退出策略
        return None