add files

This commit is contained in:
songkunling
2025-08-11 18:06:53 +08:00
parent 3f115f1f14
commit 21ce1831ec
23 changed files with 2429 additions and 290 deletions
+14 -12
View File
@@ -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
-79
View File
@@ -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
-113
View File
@@ -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
+352
View File
@@ -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
View File
@@ -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
+297
View File
@@ -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
}