mirror of
https://github.com/silencesdg/mt5_python_ea_suite.git
synced 2026-08-07 16:07:56 +00:00
add files
This commit is contained in:
+14
-12
@@ -9,9 +9,7 @@ class Strategy:
|
||||
self.symbol = "XAUUSD"
|
||||
self.timeframe = mt5.TIMEFRAME_M1
|
||||
self.kdj_period = 9
|
||||
self.kdj_buy_threshold = 10
|
||||
self.kdj_sell_threshold = 90
|
||||
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
计算KDJ指标
|
||||
@@ -27,7 +25,7 @@ class Strategy:
|
||||
def generate_signal(self):
|
||||
"""
|
||||
KDJ策略实盘
|
||||
J值小于10买入,大于90卖出
|
||||
K线向上穿越D线(金叉)买入,K线向下穿越D线(死叉)卖出
|
||||
"""
|
||||
rates = get_rates(self.symbol, self.timeframe, self.kdj_period + 30)
|
||||
if rates is None or len(rates) < self.kdj_period:
|
||||
@@ -35,26 +33,30 @@ class Strategy:
|
||||
df = pd.DataFrame(rates)
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
if df['j'].iloc[-2] < self.kdj_buy_threshold:
|
||||
logger.info(f"J值小于{self.kdj_buy_threshold},产生买入信号: {self.symbol}")
|
||||
# Golden cross
|
||||
if df['k'].iloc[-2] < df['d'].iloc[-2] and df['k'].iloc[-1] > df['d'].iloc[-1]:
|
||||
logger.info(f"KDJ Golden Cross, creating buy signal: {self.symbol}")
|
||||
return 1
|
||||
elif df['j'].iloc[-2] > self.kdj_sell_threshold:
|
||||
logger.info(f"J值大于{self.kdj_sell_threshold},产生卖出信号: {self.symbol}")
|
||||
# Dead cross
|
||||
elif df['k'].iloc[-2] > df['d'].iloc[-2] and df['k'].iloc[-1] < df['d'].iloc[-1]:
|
||||
logger.info(f"KDJ Dead Cross, creating sell signal: {self.symbol}")
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
KDJ回测方法
|
||||
根据J值极端生成信号
|
||||
根据金叉和死叉生成信号
|
||||
"""
|
||||
df = df.copy()
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
for i in range(self.kdj_period, len(df)):
|
||||
if df['j'].iloc[i-1] < self.kdj_buy_threshold:
|
||||
for i in range(1, len(df)):
|
||||
# Golden cross
|
||||
if df['k'].iloc[i-1] < df['d'].iloc[i-1] and df['k'].iloc[i] > df['d'].iloc[i]:
|
||||
signals.iat[i] = 1
|
||||
elif df['j'].iloc[i-1] > self.kdj_sell_threshold:
|
||||
# Dead cross
|
||||
elif df['k'].iloc[i-1] > df['d'].iloc[i-1] and df['k'].iloc[i] < df['d'].iloc[i]:
|
||||
signals.iat[i] = -1
|
||||
return signals
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
|
||||
import pandas as pd
|
||||
from logger import logger
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
# --- 策略核心参数 ---
|
||||
# 固定止损线:亏损10%则卖出
|
||||
self.stop_loss_pct = -0.10
|
||||
# 利润回撤百分比:从最高利润点回撤30%则卖出
|
||||
self.profit_retracement_pct = 0.30
|
||||
# 追踪止损的激活阈值:当利润超过5%后,才开始启动追踪止损逻辑
|
||||
self.min_profit_for_trailing = 0.05
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
此策略为资金管理和退出策略,不产生独立的买入信号。
|
||||
实盘逻辑应与其他策略结合,此处仅为框架完整性。
|
||||
"""
|
||||
logger.warning("ProfitProtect策略是一个退出策略,不应单独用于实盘产生信号。")
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
盈利保护策略回测:
|
||||
- 固定止损:亏损10%卖出。
|
||||
- 追踪止损:利润超过5%后启动,从最高利润点回撤30%卖出。
|
||||
为了独立回测,本策略会在一开始买入,然后应用退出逻辑。
|
||||
"""
|
||||
df = df.copy()
|
||||
signals = pd.Series(0, index=df.index)
|
||||
|
||||
if len(df) < 2:
|
||||
return signals
|
||||
|
||||
# --- 回测状态变量 ---
|
||||
position_open = False
|
||||
entry_price = 0.0
|
||||
peak_profit_pct = 0.0 # 记录达到的最高利润百分比
|
||||
|
||||
for i in range(len(df)):
|
||||
# 如果没有持仓,就在第一个机会买入(用于独立回测)
|
||||
if not position_open:
|
||||
position_open = True
|
||||
entry_price = df['close'].iloc[i]
|
||||
signals.iat[i] = 1 # 买入信号
|
||||
peak_profit_pct = 0.0 # 重置最高利润
|
||||
continue
|
||||
|
||||
# 如果有持仓,则执行退出逻辑
|
||||
if position_open:
|
||||
current_price = df['close'].iloc[i]
|
||||
current_profit_pct = (current_price - entry_price) / entry_price
|
||||
|
||||
# 1. 更新最高利润点
|
||||
peak_profit_pct = max(peak_profit_pct, current_profit_pct)
|
||||
|
||||
# 2. 检查固定止损条件
|
||||
if current_profit_pct <= self.stop_loss_pct:
|
||||
logger.info(f"索引 {i}: 触发固定止损。入场价: {entry_price:.2f}, 当前价: {current_price:.2f}, 亏损: {current_profit_pct:.2%}")
|
||||
signals.iat[i] = -1 # 卖出信号
|
||||
position_open = False # 平仓
|
||||
continue
|
||||
|
||||
# 3. 检查追踪止损条件
|
||||
# 只有当最高利润超过了激活阈值,才开始计算回撤
|
||||
if peak_profit_pct > self.min_profit_for_trailing:
|
||||
retracement_from_peak = (peak_profit_pct - current_profit_pct)
|
||||
|
||||
# 避免除以零或负数的情况
|
||||
if peak_profit_pct > 0:
|
||||
retracement_pct = retracement_from_peak / peak_profit_pct
|
||||
if retracement_pct >= self.profit_retracement_pct:
|
||||
logger.info(f"索引 {i}: 触发追踪止损。最高利润: {peak_profit_pct:.2%}, 当前利润: {current_profit_pct:.2%}, 回撤超过30%")
|
||||
signals.iat[i] = -1 # 卖出信号
|
||||
position_open = False # 平仓
|
||||
continue
|
||||
return signals
|
||||
@@ -1,113 +0,0 @@
|
||||
|
||||
import pandas as pd
|
||||
from logger import logger
|
||||
from utils import get_rates
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
# --- 策略核心参数 ---
|
||||
self.trend_period = 50
|
||||
self.retracement_tolerance = 0.30
|
||||
|
||||
# --- 策略状态变量 ---
|
||||
self.current_trend = "none" # none, uptrend, downtrend
|
||||
self.trend_peak = 0.0 # 上升趋势中的最高价
|
||||
self.trend_trough = float('inf') # 下降趋势中的最低价
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
带状态维护的实盘信号生成方法。
|
||||
"""
|
||||
# 获取足够的数据来计算滚动高低点
|
||||
rates = get_rates(self.symbol, 1, self.trend_period + 5)
|
||||
if rates is None or len(rates) < self.trend_period:
|
||||
return 0 # 数据不足,不产生信号
|
||||
|
||||
df = pd.DataFrame(rates)
|
||||
|
||||
# 获取当前价格和用于判断突破的历史高低点
|
||||
current_price = df['close'].iloc[-1]
|
||||
high_period = df['high'].rolling(self.trend_period).max().iloc[-2]
|
||||
low_period = df['low'].rolling(self.trend_period).min().iloc[-2]
|
||||
|
||||
signal = 0
|
||||
|
||||
# 状态 1: 当前无趋势,等待趋势开始
|
||||
if self.current_trend == "none":
|
||||
if current_price > high_period:
|
||||
self.current_trend = "uptrend"
|
||||
self.trend_peak = current_price
|
||||
signal = 1
|
||||
logger.info(f"实盘: 突破进入上升趋势,买入价: {current_price:.2f}")
|
||||
elif current_price < low_period:
|
||||
self.current_trend = "downtrend"
|
||||
self.trend_trough = current_price
|
||||
signal = -1
|
||||
logger.info(f"实盘: 跌破进入下降趋势,卖出价: {current_price:.2f}")
|
||||
|
||||
# 状态 2: 当前处于上升趋势
|
||||
elif self.current_trend == "uptrend":
|
||||
if current_price < self.trend_peak * (1 - self.retracement_tolerance):
|
||||
logger.info(f"实盘: 上升趋势结束。最高点: {self.trend_peak:.2f}, 当前价: {current_price:.2f}。平仓卖出。")
|
||||
signal = -1
|
||||
self.current_trend = "none" # 重置状态
|
||||
else:
|
||||
self.trend_peak = max(self.trend_peak, current_price)
|
||||
|
||||
# 状态 3: 当前处于下降趋势
|
||||
elif self.current_trend == "downtrend":
|
||||
if current_price > self.trend_trough * (1 + self.retracement_tolerance):
|
||||
logger.info(f"实盘: 下降趋势结束。最低点: {self.trend_trough:.2f}, 当前价: {current_price:.2f}。平仓买入。")
|
||||
signal = 1
|
||||
self.current_trend = "none" # 重置状态
|
||||
else:
|
||||
self.trend_trough = min(self.trend_trough, current_price)
|
||||
|
||||
return signal
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
带容错的趋势跟踪策略回测:
|
||||
- 突破N周期高点,进入上升趋势,回撤30%则趋势结束。
|
||||
- 跌破N周期低点,进入下降趋势,反弹30%则趋势结束。
|
||||
"""
|
||||
df = df.copy()
|
||||
signals = pd.Series(0, index=df.index)
|
||||
|
||||
df['high_period'] = df['high'].rolling(self.trend_period).max().shift(1)
|
||||
df['low_period'] = df['low'].rolling(self.trend_period).min().shift(1)
|
||||
|
||||
# 回测时使用局部变量来管理状态,避免干扰实盘状态
|
||||
backtest_trend = "none"
|
||||
backtest_peak = 0.0
|
||||
backtest_trough = float('inf')
|
||||
|
||||
for i in range(self.trend_period, len(df)):
|
||||
current_price = df['close'].iloc[i]
|
||||
|
||||
if backtest_trend == "none":
|
||||
if current_price > df['high_period'].iloc[i]:
|
||||
backtest_trend = "uptrend"
|
||||
backtest_peak = current_price
|
||||
signals.iat[i] = 1
|
||||
elif current_price < df['low_period'].iloc[i]:
|
||||
backtest_trend = "downtrend"
|
||||
backtest_trough = current_price
|
||||
signals.iat[i] = -1
|
||||
|
||||
elif backtest_trend == "uptrend":
|
||||
if current_price < backtest_peak * (1 - self.retracement_tolerance):
|
||||
signals.iat[i] = -1
|
||||
backtest_trend = "none"
|
||||
else:
|
||||
backtest_peak = max(backtest_peak, current_price)
|
||||
|
||||
elif backtest_trend == "downtrend":
|
||||
if current_price > backtest_trough * (1 + self.retracement_tolerance):
|
||||
signals.iat[i] = 1
|
||||
backtest_trend = "none"
|
||||
else:
|
||||
backtest_trough = min(backtest_trough, current_price)
|
||||
|
||||
return signals
|
||||
@@ -0,0 +1,352 @@
|
||||
import MetaTrader5 as mt5
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from utils import get_rates, has_open_position, get_current_price
|
||||
from logger import logger
|
||||
from config import RISK_CONFIG, SYMBOL
|
||||
|
||||
class Strategy:
|
||||
"""
|
||||
风险管理策略
|
||||
基于市场状态和持仓情况生成风险管理信号
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.symbol = SYMBOL
|
||||
self.timeframe = mt5.TIMEFRAME_M1
|
||||
self.data_count = 100 # 用于分析的数据量
|
||||
|
||||
# 从配置文件读取风险管理参数
|
||||
self.stop_loss_pct = RISK_CONFIG.get("stop_loss_pct", -0.001)
|
||||
self.profit_retracement_pct = RISK_CONFIG.get("profit_retracement_pct", 0.10)
|
||||
self.min_profit_for_trailing = RISK_CONFIG.get("min_profit_for_trailing", 0.05)
|
||||
self.take_profit_pct = RISK_CONFIG.get("take_profit_pct", 0.20)
|
||||
self.max_daily_loss = RISK_CONFIG.get("max_daily_loss", -0.10)
|
||||
|
||||
# 策略参数
|
||||
self.volatility_period = 20
|
||||
self.trend_period = 50
|
||||
self.rsi_period = 14
|
||||
|
||||
# 内部状态
|
||||
self.positions = {}
|
||||
self.daily_pnl = 0.0
|
||||
self.last_position_time = None
|
||||
self.min_trade_interval = 5 # 最小交易间隔(分钟)
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
计算技术指标
|
||||
"""
|
||||
# 计算移动平均线
|
||||
df['ma_short'] = df['close'].rolling(10).mean()
|
||||
df['ma_long'] = df['close'].rolling(30).mean()
|
||||
|
||||
# 计算RSI
|
||||
df['rsi'] = self._calculate_rsi(df['close'], self.rsi_period)
|
||||
|
||||
# 计算波动率
|
||||
df['volatility'] = df['close'].rolling(self.volatility_period).std() / df['close'].rolling(self.volatility_period).mean()
|
||||
|
||||
# 计算ATR
|
||||
df['atr'] = self._calculate_atr(df, 14)
|
||||
|
||||
# 计算价格变化
|
||||
df['price_change'] = df['close'].pct_change()
|
||||
|
||||
return df
|
||||
|
||||
def _calculate_rsi(self, prices, period):
|
||||
"""
|
||||
计算RSI指标
|
||||
"""
|
||||
delta = prices.diff()
|
||||
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
|
||||
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
|
||||
rs = gain / loss
|
||||
rsi = 100 - (100 / (1 + rs))
|
||||
return rsi
|
||||
|
||||
def _calculate_atr(self, df, period):
|
||||
"""
|
||||
计算ATR指标
|
||||
"""
|
||||
high = df['high']
|
||||
low = df['low']
|
||||
close = df['close']
|
||||
|
||||
tr1 = high - low
|
||||
tr2 = abs(high - close.shift(1))
|
||||
tr3 = abs(low - close.shift(1))
|
||||
|
||||
tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
|
||||
atr = tr.rolling(window=period).mean()
|
||||
|
||||
return atr
|
||||
|
||||
def _get_position_info(self):
|
||||
"""
|
||||
获取当前持仓信息
|
||||
"""
|
||||
return self.positions.get(self.symbol, None)
|
||||
|
||||
def _calculate_position_pnl(self, current_price, position_info):
|
||||
"""
|
||||
计算持仓盈亏
|
||||
"""
|
||||
if not position_info:
|
||||
return 0.0
|
||||
|
||||
entry_price = position_info['entry_price']
|
||||
position_type = position_info['position_type']
|
||||
|
||||
if position_type == 'long':
|
||||
return (current_price - entry_price) / entry_price
|
||||
else:
|
||||
return (entry_price - current_price) / entry_price
|
||||
|
||||
def _check_risk_conditions(self, df):
|
||||
"""
|
||||
检查风险管理条件
|
||||
"""
|
||||
current_price = df['close'].iloc[-1]
|
||||
current_volatility = df['volatility'].iloc[-1]
|
||||
current_rsi = df['rsi'].iloc[-1]
|
||||
current_atr = df['atr'].iloc[-1]
|
||||
|
||||
# 检查是否有持仓
|
||||
position_info = self._get_position_info()
|
||||
|
||||
# 如果没有持仓,检查开仓条件
|
||||
if not position_info:
|
||||
return self._check_entry_conditions(df, current_price, current_volatility, current_rsi, current_atr)
|
||||
|
||||
# 如果有持仓,检查平仓条件
|
||||
else:
|
||||
return self._check_exit_conditions(df, current_price, position_info)
|
||||
|
||||
def _check_entry_conditions(self, df, current_price, volatility, rsi, atr):
|
||||
"""
|
||||
检查开仓条件
|
||||
"""
|
||||
# 检查日亏损限制
|
||||
if self.daily_pnl <= self.max_daily_loss:
|
||||
logger.info(f"日亏损已达{self.daily_pnl:.2%},禁止开仓")
|
||||
return 0
|
||||
|
||||
# 检查最小交易间隔
|
||||
if self.last_position_time:
|
||||
time_diff = (pd.Timestamp.now() - self.last_position_time).total_seconds() / 60
|
||||
if time_diff < self.min_trade_interval:
|
||||
return 0
|
||||
|
||||
# 基于市场状态的开仓条件
|
||||
ma_short = df['ma_short'].iloc[-1]
|
||||
ma_long = df['ma_long'].iloc[-1]
|
||||
|
||||
# 低波动率且趋势明确时开仓
|
||||
if volatility < 0.02: # 低波动率
|
||||
if ma_short > ma_long and rsi < 70: # 上升趋势且未超买
|
||||
logger.info(f"风险管理策略:低波动率上升趋势,买入信号")
|
||||
return 1
|
||||
elif ma_short < ma_long and rsi > 30: # 下降趋势且未超卖
|
||||
logger.info(f"风险管理策略:低波动率下降趋势,卖出信号")
|
||||
return -1
|
||||
|
||||
# 高波动率时等待机会
|
||||
elif volatility > 0.05: # 高波动率
|
||||
if rsi < 30: # 超卖反弹机会
|
||||
logger.info(f"风险管理策略:高波动率超卖反弹,买入信号")
|
||||
return 1
|
||||
elif rsi > 70: # 超买回调机会
|
||||
logger.info(f"风险管理策略:高波动率超买回调,卖出信号")
|
||||
return -1
|
||||
|
||||
return 0
|
||||
|
||||
def _check_exit_conditions(self, df, current_price, position_info):
|
||||
"""
|
||||
检查平仓条件
|
||||
"""
|
||||
position_type = position_info['position_type']
|
||||
entry_price = position_info['entry_price']
|
||||
current_pnl = self._calculate_position_pnl(current_price, position_info)
|
||||
|
||||
# 更新最高利润
|
||||
self.positions[self.symbol]['peak_profit'] = max(
|
||||
position_info.get('peak_profit', 0),
|
||||
current_pnl
|
||||
)
|
||||
peak_profit = self.positions[self.symbol]['peak_profit']
|
||||
|
||||
# 检查止损 - 仅基于买入成本判断亏损,不考虑盈利回撤
|
||||
if current_pnl < 0 and current_pnl <= self.stop_loss_pct:
|
||||
logger.info(f"风险管理策略:止损触发,当前亏损{current_pnl:.2%}")
|
||||
return -1 if position_type == 'long' else 1
|
||||
|
||||
# 检查止盈
|
||||
if current_pnl >= self.take_profit_pct:
|
||||
logger.info(f"风险管理策略:止盈触发,当前盈利{current_pnl:.2%}")
|
||||
return -1 if position_type == 'long' else 1
|
||||
|
||||
# 检查追踪止损
|
||||
if peak_profit > self.min_profit_for_trailing:
|
||||
retracement_from_peak = peak_profit - current_pnl
|
||||
if peak_profit > 0:
|
||||
retracement_pct = retracement_from_peak / peak_profit
|
||||
if retracement_pct >= self.profit_retracement_pct:
|
||||
logger.info(f"风险管理策略:追踪止损触发,最高盈利{peak_profit:.2%},回撤{retracement_pct:.2%}")
|
||||
return -1 if position_type == 'long' else 1
|
||||
|
||||
return 0
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
生成实时交易信号
|
||||
"""
|
||||
# 获取当前价格
|
||||
current_price = get_current_price(self.symbol)
|
||||
if current_price is None:
|
||||
return 0
|
||||
|
||||
# 获取历史数据
|
||||
rates = get_rates(self.symbol, self.timeframe, self.data_count)
|
||||
if rates is None or len(rates) < self.trend_period:
|
||||
return 0
|
||||
|
||||
df = pd.DataFrame(rates)
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
# 检查风险管理条件
|
||||
signal = self._check_risk_conditions(df)
|
||||
|
||||
return signal
|
||||
|
||||
def generate_signal_with_sync(self, risk_controller):
|
||||
"""
|
||||
生成实时交易信号(与主风险管理器同步)
|
||||
"""
|
||||
# 先同步状态
|
||||
self.sync_with_risk_controller(risk_controller)
|
||||
|
||||
# 然后生成信号
|
||||
return self.generate_signal()
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
回测信号生成
|
||||
"""
|
||||
df = df.copy()
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
|
||||
# 模拟持仓状态
|
||||
positions = {}
|
||||
daily_pnl = 0.0
|
||||
|
||||
for i in range(self.trend_period, len(df)):
|
||||
current_price = df['close'].iloc[i]
|
||||
current_time = df.index[i]
|
||||
|
||||
# 获取当前持仓信息
|
||||
position_info = positions.get(self.symbol)
|
||||
|
||||
if not position_info:
|
||||
# 检查开仓条件
|
||||
if self._should_open_position(df.iloc[:i+1], current_price):
|
||||
# 简化的开仓逻辑
|
||||
ma_short = df['ma_short'].iloc[i]
|
||||
ma_long = df['ma_long'].iloc[i]
|
||||
rsi = df['rsi'].iloc[i]
|
||||
|
||||
if ma_short > ma_long and rsi < 70:
|
||||
signals.iat[i] = 1
|
||||
positions[self.symbol] = {
|
||||
'entry_price': current_price,
|
||||
'position_type': 'long',
|
||||
'peak_profit': 0.0
|
||||
}
|
||||
elif ma_short < ma_long and rsi > 30:
|
||||
signals.iat[i] = -1
|
||||
positions[self.symbol] = {
|
||||
'entry_price': current_price,
|
||||
'position_type': 'short',
|
||||
'peak_profit': 0.0
|
||||
}
|
||||
else:
|
||||
# 检查平仓条件
|
||||
position_type = position_info['position_type']
|
||||
entry_price = position_info['entry_price']
|
||||
current_pnl = self._calculate_position_pnl(current_price, position_info)
|
||||
|
||||
# 更新最高利润
|
||||
position_info['peak_profit'] = max(position_info['peak_profit'], current_pnl)
|
||||
|
||||
# 检查止损止盈 - 止损仅基于买入成本判断亏损
|
||||
if (current_pnl < 0 and current_pnl <= self.stop_loss_pct) or current_pnl >= self.take_profit_pct:
|
||||
signals.iat[i] = -1 if position_type == 'long' else 1
|
||||
positions.pop(self.symbol, None)
|
||||
|
||||
# 更新日内盈亏
|
||||
daily_pnl += current_pnl
|
||||
if daily_pnl <= self.max_daily_loss:
|
||||
break # 日亏损达到限制,停止交易
|
||||
|
||||
return signals
|
||||
|
||||
def _should_open_position(self, df, current_price):
|
||||
"""
|
||||
判断是否应该开仓
|
||||
"""
|
||||
volatility = df['volatility'].iloc[-1]
|
||||
|
||||
# 低波动率且未达到日亏损限制
|
||||
return volatility < 0.02
|
||||
|
||||
def update_position_entry(self, entry_price, position_type="long"):
|
||||
"""
|
||||
更新持仓入场信息
|
||||
"""
|
||||
self.positions[self.symbol] = {
|
||||
'entry_price': entry_price,
|
||||
'position_type': position_type,
|
||||
'peak_profit': 0.0,
|
||||
'entry_time': pd.Timestamp.now()
|
||||
}
|
||||
self.last_position_time = pd.Timestamp.now()
|
||||
|
||||
logger.info(f"风险管理策略记录持仓入场:价格={entry_price:.2f}, 类型={position_type}")
|
||||
|
||||
def close_position(self, current_price):
|
||||
"""
|
||||
平仓
|
||||
"""
|
||||
position_info = self.positions.get(self.symbol)
|
||||
if position_info:
|
||||
pnl = self._calculate_position_pnl(current_price, position_info)
|
||||
self.daily_pnl += pnl
|
||||
|
||||
self.positions.pop(self.symbol, None)
|
||||
logger.info(f"风险管理策略平仓:盈亏{pnl:.2%}")
|
||||
|
||||
def reset_daily_stats(self):
|
||||
"""
|
||||
重置日内统计
|
||||
"""
|
||||
self.daily_pnl = 0.0
|
||||
logger.info("风险管理策略重置日内统计")
|
||||
|
||||
def sync_with_risk_controller(self, risk_controller):
|
||||
"""
|
||||
与主风险管理器同步状态
|
||||
"""
|
||||
# 从主风险管理器获取持仓信息
|
||||
if hasattr(risk_controller, 'position_manager'):
|
||||
position_info = risk_controller.position_manager.get_position_info()
|
||||
if position_info:
|
||||
self.positions[self.symbol] = position_info
|
||||
|
||||
# 同步日内盈亏
|
||||
if hasattr(risk_controller, 'daily_pnl'):
|
||||
self.daily_pnl = risk_controller.daily_pnl
|
||||
+19
-13
@@ -9,8 +9,6 @@ class Strategy:
|
||||
self.symbol = "XAUUSD"
|
||||
self.timeframe = mt5.TIMEFRAME_M1
|
||||
self.rsi_period = 14
|
||||
self.rsi_buy_threshold = 30
|
||||
self.rsi_sell_threshold = 70
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
@@ -25,8 +23,8 @@ class Strategy:
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
RSI策略实盘:
|
||||
RSI < 30买入,RSI > 70卖出。
|
||||
RSI策略实盘
|
||||
RSI上穿超卖线买入,下穿超买线卖出
|
||||
"""
|
||||
rates = get_rates(self.symbol, self.timeframe, self.rsi_period + 30)
|
||||
if rates is None or len(rates) < self.rsi_period:
|
||||
@@ -34,26 +32,34 @@ class Strategy:
|
||||
df = pd.DataFrame(rates)
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
if df['rsi'].iloc[-2] < self.rsi_buy_threshold:
|
||||
logger.info(f"RSI小于{self.rsi_buy_threshold},产生买入信号: {self.symbol}")
|
||||
oversold_level = 30
|
||||
overbought_level = 70
|
||||
# RSI crosses above oversold level
|
||||
if df['rsi'].iloc[-2] < oversold_level and df['rsi'].iloc[-1] > oversold_level:
|
||||
logger.info(f"RSI crosses above {oversold_level}, creating buy signal: {self.symbol}")
|
||||
return 1
|
||||
elif df['rsi'].iloc[-2] > self.rsi_sell_threshold:
|
||||
logger.info(f"RSI大于{self.rsi_sell_threshold},产生卖出信号: {self.symbol}")
|
||||
# RSI crosses below overbought level
|
||||
elif df['rsi'].iloc[-2] > overbought_level and df['rsi'].iloc[-1] < overbought_level:
|
||||
logger.info(f"RSI crosses below {overbought_level}, creating sell signal: {self.symbol}")
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
RSI回测:
|
||||
RSI < 30买入,RSI > 70卖出。
|
||||
RSI回测方法
|
||||
根据RSI穿越超买超卖线生成信号
|
||||
"""
|
||||
df = df.copy()
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
for i in range(self.rsi_period, len(df)):
|
||||
if df['rsi'].iloc[i-1] < self.rsi_buy_threshold:
|
||||
oversold_level = 30
|
||||
overbought_level = 70
|
||||
for i in range(1, len(df)):
|
||||
# RSI crosses above oversold level
|
||||
if df['rsi'].iloc[i-1] < oversold_level and df['rsi'].iloc[i] > oversold_level:
|
||||
signals.iat[i] = 1
|
||||
elif df['rsi'].iloc[i-1] > self.rsi_sell_threshold:
|
||||
# RSI crosses below overbought level
|
||||
elif df['rsi'].iloc[i-1] > overbought_level and df['rsi'].iloc[i] < overbought_level:
|
||||
signals.iat[i] = -1
|
||||
return signals
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
import MetaTrader5 as mt5
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from utils import get_rates
|
||||
from logger import logger
|
||||
from config import WAVE_THEORY_CONFIG
|
||||
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
self.timeframe = mt5.TIMEFRAME_M1 # 使用1分钟数据,与其他策略保持一致
|
||||
|
||||
# 从配置文件加载参数
|
||||
config = WAVE_THEORY_CONFIG
|
||||
self.daily_data_count = config.get("daily_data_count", 30)
|
||||
self.ema_short = config.get("ema_short", 5)
|
||||
self.ema_medium = config.get("ema_medium", 13)
|
||||
self.ema_long = config.get("ema_long", 34)
|
||||
self.wave_period = config.get("wave_period", 21)
|
||||
self.range_period = config.get("range_period", 20)
|
||||
self.adx_period = config.get("adx_period", 14)
|
||||
|
||||
# 波浪理论参数
|
||||
self.retracement_levels = [0.236, 0.382, 0.5, 0.618, 0.786]
|
||||
self.momentum_period = 14
|
||||
|
||||
# 震荡市检测参数(调整为适合1分钟数据)
|
||||
self.range_threshold = 0.005 # 0.5%的价格波动范围,适合1分钟数据
|
||||
self.adx_threshold = 25 # ADX小于25表示震荡市,适合1分钟数据
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
计算波浪理论相关指标
|
||||
"""
|
||||
# 计算EMA
|
||||
df['ema_short'] = df['close'].ewm(span=self.ema_short).mean()
|
||||
df['ema_medium'] = df['close'].ewm(span=self.ema_medium).mean()
|
||||
df['ema_long'] = df['close'].ewm(span=self.ema_long).mean()
|
||||
|
||||
# 计算动量指标
|
||||
df['momentum'] = df['close'].diff(self.momentum_period) / df['close'].shift(self.momentum_period) * 100
|
||||
|
||||
# 计算波动范围
|
||||
df['high_max'] = df['high'].rolling(self.range_period).max()
|
||||
df['low_min'] = df['low'].rolling(self.range_period).min()
|
||||
df['range_pct'] = (df['high_max'] - df['low_min']) / df['close'] * 100
|
||||
|
||||
# 计算ADX(平均趋向指数)用于判断趋势强度
|
||||
df['adx'] = self._calculate_adx(df)
|
||||
|
||||
# 识别潜在的波浪点
|
||||
df['potential_wave_points'] = self._identify_wave_points(df)
|
||||
|
||||
# 计算斐波那契回撤位
|
||||
df = self._calculate_fibonacci_levels(df)
|
||||
|
||||
return df
|
||||
|
||||
def _calculate_adx(self, df):
|
||||
"""
|
||||
计算ADX指标
|
||||
"""
|
||||
high = df['high']
|
||||
low = df['low']
|
||||
close = df['close']
|
||||
|
||||
# 计算真实波幅
|
||||
df['tr'] = pd.concat([
|
||||
high - low,
|
||||
abs(high - close.shift(1)),
|
||||
abs(low - close.shift(1))
|
||||
], axis=1).max(axis=1)
|
||||
|
||||
# 计算方向移动
|
||||
df['up_move'] = high - high.shift(1)
|
||||
df['down_move'] = low.shift(1) - low
|
||||
|
||||
df['plus_dm'] = np.where((df['up_move'] > df['down_move']) & (df['up_move'] > 0), df['up_move'], 0)
|
||||
df['minus_dm'] = np.where((df['down_move'] > df['up_move']) & (df['down_move'] > 0), df['down_move'], 0)
|
||||
|
||||
# 计算平滑值
|
||||
df['plus_di'] = 100 * (df['plus_dm'].ewm(span=self.adx_period).mean() / df['tr'].ewm(span=self.adx_period).mean())
|
||||
df['minus_di'] = 100 * (df['minus_dm'].ewm(span=self.adx_period).mean() / df['tr'].ewm(span=self.adx_period).mean())
|
||||
|
||||
# 计算DX
|
||||
df['dx'] = 100 * abs(df['plus_di'] - df['minus_di']) / (df['plus_di'] + df['minus_di'])
|
||||
|
||||
# 计算ADX
|
||||
adx = df['dx'].ewm(span=self.adx_period).mean()
|
||||
|
||||
return adx
|
||||
|
||||
def _identify_wave_points(self, df):
|
||||
"""
|
||||
识别潜在的波浪转折点
|
||||
"""
|
||||
wave_points = pd.Series(0, index=df.index)
|
||||
|
||||
for i in range(self.wave_period, len(df) - self.wave_period):
|
||||
current_high = df['high'].iloc[i]
|
||||
current_low = df['low'].iloc[i]
|
||||
|
||||
# 检查是否为波峰
|
||||
is_peak = (current_high == df['high'].iloc[i-self.wave_period:i+self.wave_period].max())
|
||||
|
||||
# 检查是否为波谷
|
||||
is_trough = (current_low == df['low'].iloc[i-self.wave_period:i+self.wave_period].min())
|
||||
|
||||
if is_peak:
|
||||
wave_points.iloc[i] = 1 # 波峰
|
||||
elif is_trough:
|
||||
wave_points.iloc[i] = -1 # 波谷
|
||||
|
||||
return wave_points
|
||||
|
||||
def _calculate_fibonacci_levels(self, df):
|
||||
"""
|
||||
计算斐波那契回撤位
|
||||
"""
|
||||
# 为每个斐波那契级别创建单独的列
|
||||
for level in self.retracement_levels:
|
||||
df[f'fib_{level}'] = None
|
||||
|
||||
for i in range(self.wave_period * 2, len(df)):
|
||||
# 寻找最近的波峰和波谷
|
||||
wave_points = df['potential_wave_points'].iloc[:i+1]
|
||||
peaks = wave_points[wave_points == 1]
|
||||
troughs = wave_points[wave_points == -1]
|
||||
|
||||
if len(peaks) > 0 and len(troughs) > 0:
|
||||
last_peak_idx = peaks.index[-1]
|
||||
last_trough_idx = troughs.index[-1]
|
||||
|
||||
if last_peak_idx > last_trough_idx:
|
||||
# 下降趋势,计算回撤位
|
||||
high_price = df['high'].iloc[last_peak_idx]
|
||||
low_price = df['low'].iloc[last_trough_idx]
|
||||
price_range = high_price - low_price
|
||||
|
||||
for level in self.retracement_levels:
|
||||
df.at[i, f'fib_{level}'] = high_price - price_range * level
|
||||
else:
|
||||
# 上升趋势,计算回撤位
|
||||
low_price = df['low'].iloc[last_trough_idx]
|
||||
high_price = df['high'].iloc[last_peak_idx]
|
||||
price_range = high_price - low_price
|
||||
|
||||
for level in self.retracement_levels:
|
||||
df.at[i, f'fib_{level}'] = low_price + price_range * level
|
||||
|
||||
return df
|
||||
|
||||
def _is_sideways_market(self, df):
|
||||
"""
|
||||
判断是否为震荡市
|
||||
"""
|
||||
if len(df) < self.range_period:
|
||||
return False
|
||||
|
||||
# 使用ADX判断趋势强度
|
||||
adx_value = df['adx'].iloc[-1]
|
||||
is_low_adx = adx_value < self.adx_threshold
|
||||
|
||||
# 使用价格范围判断
|
||||
range_pct = df['range_pct'].iloc[-1]
|
||||
is_tight_range = range_pct < self.range_threshold * 100
|
||||
|
||||
# 结合两个条件
|
||||
return is_low_adx and is_tight_range
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
波浪理论策略实盘信号生成
|
||||
"""
|
||||
rates = get_rates(self.symbol, self.timeframe, self.daily_data_count)
|
||||
if rates is None or len(rates) < self.wave_period * 3:
|
||||
return 0
|
||||
|
||||
df = pd.DataFrame(rates)
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
# 判断市场状态
|
||||
is_sideways = self._is_sideways_market(df)
|
||||
|
||||
# 获取最近的波浪点
|
||||
recent_wave_points = df['potential_wave_points'].iloc[-self.wave_period:]
|
||||
current_momentum = df['momentum'].iloc[-1]
|
||||
current_price = df['close'].iloc[-1]
|
||||
|
||||
# 震荡市中的波浪理论信号
|
||||
if is_sideways:
|
||||
# 在震荡市中,寻找区间边界的反转机会
|
||||
upper_bound = df['high_max'].iloc[-1]
|
||||
lower_bound = df['low_min'].iloc[-1]
|
||||
|
||||
# 价格接近上边界且有转弱迹象
|
||||
if current_price > upper_bound * 0.98 and current_momentum < 0:
|
||||
logger.info(f"震荡市中价格接近上边界,产生卖出信号: {self.symbol}")
|
||||
return -1
|
||||
|
||||
# 价格接近下边界且有转强迹象
|
||||
elif current_price < lower_bound * 1.02 and current_momentum > 0:
|
||||
logger.info(f"震荡市中价格接近下边界,产生买入信号: {self.symbol}")
|
||||
return 1
|
||||
|
||||
# 趋势市场中的波浪理论信号
|
||||
else:
|
||||
# 寻找波浪模式的确认信号
|
||||
ema_alignment = (df['ema_short'].iloc[-1] > df['ema_medium'].iloc[-1] > df['ema_long'].iloc[-1])
|
||||
|
||||
# 上升趋势中的回调买入
|
||||
if ema_alignment and current_momentum > 0:
|
||||
# 检查是否在斐波那契回撤位附近
|
||||
if f'fib_0.618' in df.columns and not pd.isna(df[f'fib_0.618'].iloc[-1]):
|
||||
fib_618 = df[f'fib_0.618'].iloc[-1]
|
||||
if abs(current_price - fib_618) / fib_618 < 0.01: # 1%误差范围内
|
||||
logger.info(f"上升趋势中回调至斐波那契61.8%位,产生买入信号: {self.symbol}")
|
||||
return 1
|
||||
|
||||
# 下降趋势中的反弹卖出
|
||||
elif not ema_alignment and current_momentum < 0:
|
||||
# 检查是否在斐波那契回撤位附近
|
||||
if f'fib_0.618' in df.columns and not pd.isna(df[f'fib_0.618'].iloc[-1]):
|
||||
fib_618 = df[f'fib_0.618'].iloc[-1]
|
||||
if abs(current_price - fib_618) / fib_618 < 0.01: # 1%误差范围内
|
||||
logger.info(f"下降趋势中反弹至斐波那契61.8%位,产生卖出信号: {self.symbol}")
|
||||
return -1
|
||||
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
波浪理论策略回测
|
||||
"""
|
||||
df = df.copy()
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
|
||||
for i in range(self.wave_period * 3, len(df)):
|
||||
current_df = df.iloc[:i+1]
|
||||
|
||||
# 判断市场状态
|
||||
is_sideways = self._is_sideways_market(current_df)
|
||||
|
||||
# 获取当前时刻的数据
|
||||
current_price = current_df['close'].iloc[-1]
|
||||
current_momentum = current_df['momentum'].iloc[-1]
|
||||
|
||||
if is_sideways:
|
||||
# 震荡市信号
|
||||
upper_bound = current_df['high_max'].iloc[-1]
|
||||
lower_bound = current_df['low_min'].iloc[-1]
|
||||
|
||||
if current_price > upper_bound * 0.98 and current_momentum < 0:
|
||||
signals.iat[i] = -1
|
||||
elif current_price < lower_bound * 1.02 and current_momentum > 0:
|
||||
signals.iat[i] = 1
|
||||
|
||||
else:
|
||||
# 趋势市信号
|
||||
ema_alignment = (current_df['ema_short'].iloc[-1] > current_df['ema_medium'].iloc[-1] > current_df['ema_long'].iloc[-1])
|
||||
|
||||
if ema_alignment and current_momentum > 0:
|
||||
# 检查斐波那契回撤位
|
||||
if f'fib_0.618' in current_df.columns and not pd.isna(current_df[f'fib_0.618'].iloc[-1]):
|
||||
fib_618 = current_df[f'fib_0.618'].iloc[-1]
|
||||
if abs(current_price - fib_618) / fib_618 < 0.01:
|
||||
signals.iat[i] = 1
|
||||
|
||||
elif not ema_alignment and current_momentum < 0:
|
||||
# 检查斐波那契回撤位
|
||||
if f'fib_0.618' in current_df.columns and not pd.isna(current_df[f'fib_0.618'].iloc[-1]):
|
||||
fib_618 = current_df[f'fib_0.618'].iloc[-1]
|
||||
if abs(current_price - fib_618) / fib_618 < 0.01:
|
||||
signals.iat[i] = -1
|
||||
|
||||
return signals
|
||||
|
||||
def get_market_state(self, df):
|
||||
"""
|
||||
获取当前市场状态信息
|
||||
"""
|
||||
if len(df) < self.wave_period * 3:
|
||||
return {"state": "insufficient_data", "confidence": 0}
|
||||
|
||||
is_sideways = self._is_sideways_market(df)
|
||||
adx_value = df['adx'].iloc[-1]
|
||||
range_pct = df['range_pct'].iloc[-1]
|
||||
|
||||
return {
|
||||
"state": "sideways" if is_sideways else "trending",
|
||||
"confidence": max(0, min(1, (30 - adx_value) / 30)), # ADX越低,震荡市置信度越高
|
||||
"adx": adx_value,
|
||||
"range_pct": range_pct
|
||||
}
|
||||
Reference in New Issue
Block a user