187 lines
6.4 KiB
Python
187 lines
6.4 KiB
Python
"""
|
|
行情策略管理模块 - 支持多种策略
|
|
"""
|
|
import logging
|
|
import pandas as pd
|
|
import numpy as np
|
|
import pandas as pd
|
|
from quote_manager import QuoteManager, DataSource
|
|
from logger_utils_new import log_info, log_warning, log_error, log_trigger, log_debug, setup_logger, LOG_STYLES
|
|
from abc import ABC, abstractmethod
|
|
from enum import Enum, auto
|
|
|
|
|
|
class StrategyType(Enum):
|
|
TU_STRATEGY = auto() # 土策略
|
|
BREAKOUT_STRATEGY = auto() # 突破策略
|
|
MEAN_REVERSION_STRATEGY = auto() # 均值回归策略
|
|
|
|
class TradingStrategy(ABC):
|
|
def __init__(self, quote_manager):
|
|
self.quote_manager = quote_manager
|
|
self.history_data = {}
|
|
self.STOP_LOSS = 0.03
|
|
self.COMMISSION = 0.0003
|
|
self.MIN_TRADE_AMOUNT = 1e7
|
|
@abstractmethod
|
|
def _analyze_signal(self, quote_data):
|
|
"""分析交易信号"""
|
|
pass
|
|
|
|
class TuStrategy(TradingStrategy):
|
|
"""土策略实现"""
|
|
def analyze_signal(self, quote_data):
|
|
# 现有的土策略分析逻辑
|
|
# ... 保留原有代码 ...
|
|
signal = {
|
|
'code': quote_data['TS_CODE'],
|
|
'signal': 'buy' if quote_data['PRICE'] < quote_data['OPEN'] else 'sell',
|
|
'price': quote_data['PRICE'],
|
|
'volume': quote_data['VOLUME'],
|
|
'timestamp': pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
}
|
|
return signal
|
|
|
|
class BreakoutStrategy(TradingStrategy):
|
|
"""突破策略实现"""
|
|
def analyze_signal(self, quote_data):
|
|
# 实现突破策略逻辑
|
|
# ... 新增代码 ...
|
|
signal = {
|
|
'code': quote_data['TS_CODE'],
|
|
'signal': 'buy' if quote_data['PRICE'] > quote_data['OPEN'] else'sell',
|
|
'price': quote_data['PRICE'],
|
|
'volume': quote_data['VOLUME'],
|
|
'timestamp': pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
}
|
|
return signal
|
|
|
|
class StrategyManager:
|
|
def __init__(self, quote_manager):
|
|
self.quote_manager = quote_manager
|
|
self.strategies = {
|
|
StrategyType.TU_STRATEGY: TuStrategy(quote_manager),
|
|
StrategyType.BREAKOUT_STRATEGY: BreakoutStrategy(quote_manager)
|
|
}
|
|
self.current_strategy = StrategyType.TU_STRATEGY # 默认策略
|
|
|
|
def set_strategy(self, strategy_type):
|
|
"""设置当前使用的策略"""
|
|
if strategy_type in self.strategies:
|
|
self.current_strategy = strategy_type
|
|
return True
|
|
return False
|
|
|
|
def monitor_stocks(self, stock_codes):
|
|
"""使用当前策略监控股票"""
|
|
strategy = self.strategies[self.current_strategy]
|
|
return strategy.monitor_stocks(stock_codes)
|
|
|
|
|
|
class TradingStrategy(ABC):
|
|
def __init__(self, quote_manager):
|
|
self.quote_manager = QuoteManager()
|
|
|
|
def monitor_stocks(self, stock_codes):
|
|
"""监控股票行情"""
|
|
try:
|
|
quotes = self.quote_manager.get_realtime_quotes(stock_codes)
|
|
log_info(f"行情更新: {pd.Timestamp.now()}")
|
|
|
|
signals = []
|
|
for code, data in quotes.items():
|
|
signal = self._analyze_signal(data)
|
|
log_info(f"{code}: 最新价 {data['PRICE']} 成交量 {data['VOLUME']}")
|
|
signals.append({
|
|
'code': code,
|
|
'signal': signal,
|
|
'price': data['PRICE'],
|
|
'volume': data['VOLUME'],
|
|
'timestamp': pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
})
|
|
|
|
|
|
return signals
|
|
|
|
except Exception as e:
|
|
log_error(f"获取行情失败: {str(e)}")
|
|
return []
|
|
|
|
def __init__(self, quote_manager):
|
|
self.quote_manager = quote_manager
|
|
self.history_data = {}
|
|
self.STOP_LOSS = 0.03 # 止损比例
|
|
self.COMMISSION = 0.0003 # 单边佣金
|
|
self.MIN_TRADE_AMOUNT = 1e7 # 最小成交金额
|
|
|
|
def _calculate_slope(self, x):
|
|
"""计算斜率"""
|
|
if len(x) < 2:
|
|
return np.nan
|
|
|
|
n = len(x)
|
|
sum_x = (n - 1) * n / 2
|
|
sum_xx = (n - 1) * n * (2 * n - 1) / 6
|
|
|
|
sum_y = np.sum(x)
|
|
sum_xy = np.sum(np.arange(n) * x)
|
|
|
|
denom = n * sum_xx - sum_x * sum_x
|
|
if denom == 0:
|
|
return np.nan
|
|
|
|
return (n * sum_xy - sum_x * sum_y) / denom
|
|
|
|
def _update_history_data(self, code, data):
|
|
"""更新历史数据"""
|
|
if code not in self.history_data:
|
|
self.history_data[code] = []
|
|
self.history_data[code].append(data)
|
|
# 只保留最近30天数据
|
|
if len(self.history_data[code]) > 30:
|
|
self.history_data[code] = self.history_data[code][-30:]
|
|
return pd.DataFrame(self.history_data[code])
|
|
|
|
def _analyze_signal(self, quote_data):
|
|
"""基于土策略分析交易信号"""
|
|
code = quote_data['TS_CODE']
|
|
df = self._update_history_data(code, quote_data)
|
|
|
|
if len(df) < 25: # 确保有足够数据计算指标
|
|
return 'HOLD'
|
|
|
|
# 计算技术指标
|
|
df = df.sort_values('TRADE_DATE')
|
|
df['X_1'] = df['LOW'].rolling(10, min_periods=5).min()
|
|
df['X_2'] = df['HIGH'].rolling(25, min_periods=10).max()
|
|
df['X_6'] = ((df['CLOSE'] - df['X_1']) / (df['X_2'].replace(0, np.nan) - df['X_1']) * 4)
|
|
df['X_6'] = df['X_6'].ewm(span=4, adjust=False).mean().shift(1)
|
|
|
|
# 信号过滤条件
|
|
df['X_7'] = df['X_6'].rolling(5).apply(
|
|
lambda x: 0 if (np.diff(x > 3.5) == 1).any() else 1, raw=True
|
|
).fillna(1)
|
|
|
|
# 动量计算
|
|
df['X_10'] = df['CLOSE'].pct_change(2).shift(1) * 100
|
|
df['X_43'] = df['X_10'].rolling(2).sum() * df['X_7']
|
|
|
|
# 复合指标
|
|
ema_open_12 = df['OPEN'].ewm(span=12, adjust=False).mean()
|
|
df['X_15'] = df['X_7'] * (df['OPEN'] - ema_open_12) / ema_open_12 * 200
|
|
|
|
# 斜率计算
|
|
df['X_41'] = df['X_15'].rolling(2).apply(self._calculate_slope)
|
|
df['X_42'] = df['X_6'].rolling(2).apply(self._calculate_slope)
|
|
|
|
# 最终土值计算
|
|
df['tu_value'] = (df['X_41'] * 0.02 - df['X_42'] * df['X_7']) * df['X_7']
|
|
|
|
# 过滤条件
|
|
current = df.iloc[-1]
|
|
if (current['X_43'] > 8 and
|
|
not current['CODE'].startswith(('313', '314', '315', '688')) and
|
|
current['AMOUNT'] > self.MIN_TRADE_AMOUNT and
|
|
not np.isnan(current['tu_value'])):
|
|
return 'BUY' if current['tu_value'] > 0 else 'SELL'
|
|
return 'HOLD' |