Files
Chan/strategies/PureRandomRuleStrategy.py

205 lines
6.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
纯随机规则策略 (PureRandomRuleStrategy)
完全不看 K 线、不看指标、不看量价、不看趋势、不看形态的纯规则交易系统。
规则:
- 固定时间周期开仓(例如:每 4 小时一单)
- 方向随机多空,不做任何行情判断
- 每次只开1个方向,不对冲
- 固定止盈:2%
- 固定止损:1%
- 到价立即平仓,不移动、不修改
- 单笔仓位:总资金的 5%
- 单笔最大风险:总资金的 0.05%
- 连续止损 3 次,当天停止交易
- 总持仓不超过 20%
使用命令:
freqtrade backtesting -c ./user_data/Chan/config/Local_Test.json --strategy PureRandomRuleStrategy --strategy-path ./user_data/Chan/strategies --timerange=20260101-
实盘命令:
freqtrade trade -c ./user_data/Chan/config/Chan.json \
--strategy PureRandomRuleStrategy --strategy-path ./user_data/Chan/strategies
"""
import logging
from datetime import datetime
from typing import Optional
import random
import pandas as pd
import talib.abstract as ta
from pandas import DataFrame
from freqtrade.strategy import IStrategy
logger = logging.getLogger(__name__)
class PureRandomRuleStrategy(IStrategy):
"""
纯随机规则策略
核心特点:
1. 不看任何行情数据
2. 固定时间开仓(可配置间隔)
3. 随机选择多空方向
4. 固定止盈止损
5. 风险控制(连续止损、持仓限制)
"""
INTERFACE_VERSION: int = 3
# === 基础配置 ===
timeframe = '1m' # 主时间框架
informative_timeframe = '1h' # 1小时作为参考(需要数据支持)
can_short = True
can_long = True
startup_candle_count = 200 # 需要更多数据计算 EMA
# === 交易时间间隔配置 ===
trade_interval_hours = 4
# === 止盈止损配置 ===
take_profit_pct = 0.024
stop_loss_pct = 0.01
# === 仓位配置 ===
entry_percent = 0.05
max_position_pct = 0.20
# === 风险控制 ===
max_consecutive_losses = 3
# === 订单类型 ===
order_types = {
"entry": "market",
"exit": "market",
"stoploss": "market",
"stoploss_on_exchange": False,
}
# === 最小 ROI ===
minimal_roi = {
"0": take_profit_pct,
}
# === 止损 ===
stoploss = -stop_loss_pct
# === 追踪止损 ===
trailing_stop = False
# === 策略状态 ===
_last_entry_time: Optional[datetime] = None
_consecutive_losses: int = 0
_last_loss_date: Optional[datetime] = None
_today_loss_count: int = 0
def __init__(self, config: dict) -> None:
super().__init__(config)
random.seed()
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
计算 1h EMA26 和波动幅度
使用 resampled_merge 合并 1h 数据
"""
from technical.util import resample_to_interval, resampled_merge
# 重采样到 1h (1m * 60 = 60)
dataframe_1h = resample_to_interval(dataframe, 60)
# 计算 1h EMA26
dataframe_1h['ema26'] = ta.EMA(dataframe_1h, timeperiod=26)
# 计算 1h 波动幅度: (high - low) / open * 100%
dataframe_1h['volatility'] = (dataframe_1h['high'] - dataframe_1h['low']) / dataframe_1h['open']
# 合并到主 dataframe
# 列名格式: resample_60_ema26, resample_60_volatility
dataframe = resampled_merge(dataframe, dataframe_1h)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
入场逻辑:固定时间 + 1h EMA26方向过滤 + 波动过滤 + 时间过滤
规则:
1. 1h EMA26 上方 -> 只做多
2. 1h EMA26 下方 -> 只做空
3. 固定时间间隔开仓(4小时)
4. 1h 波动幅度 > 0.5% 且 < 5%
5. UTC 8:00-20:00
"""
dataframe['enter_long'] = 0
dataframe['enter_short'] = 0
dataframe['enter_tag'] = ''
last_entry_idx = None
# 1h EMA26 列名
ema26_col = 'resample_60_ema26'
# 1h 波动幅度列名
volatility_col = 'resample_60_volatility'
# 波动幅度阈值
min_volatility = 0.005 # 0.5%
max_volatility = 0.05 # 5%
for i in range(len(dataframe)):
current_time = dataframe['date'].iloc[i]
current_price = dataframe['close'].iloc[i]
# 使用 resample 后的 EMA26 列
ema26_1h = dataframe[ema26_col].iloc[i]
# 波动幅度
volatility = dataframe[volatility_col].iloc[i]
# 跳过没有 EMA 数据的情况
if pd.isna(ema26_1h):
continue
# 检查时间间隔(4小时)
can_entry = True
if last_entry_idx is not None:
hours_since_last = (current_time - dataframe['date'].iloc[last_entry_idx]).total_seconds() / 3600
if hours_since_last < self.trade_interval_hours:
can_entry = False
# 检查当天连续止损
if self._today_loss_count >= self.max_consecutive_losses:
can_entry = False
# 检查波动幅度(>0.5% 且 <5%
if not pd.isna(volatility):
if volatility < min_volatility or volatility > max_volatility:
can_entry = False
else:
can_entry = False
# 检查时间过滤(UTC 8:00-20:00
utc_hour = current_time.hour
#if utc_hour < 8 or utc_hour >= 20:
#can_entry = False
if can_entry:
# 判断方向:价格 > 1h EMA26 做多,价格 < 1h EMA26 做空
if current_price > ema26_1h:
dataframe.loc[dataframe.index[i], 'enter_long'] = 1
dataframe.loc[dataframe.index[i], 'enter_tag'] = 'long_above_ema'
elif current_price < ema26_1h:
dataframe.loc[dataframe.index[i], 'enter_short'] = 1
dataframe.loc[dataframe.index[i], 'enter_tag'] = 'short_below_ema'
if dataframe.loc[dataframe.index[i], 'enter_long'] == 1 or dataframe.loc[dataframe.index[i], 'enter_short'] == 1:
last_entry_idx = i
self._last_entry_time = current_time
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['exit_long'] = 0
dataframe['exit_short'] = 0
return dataframe