From de31271ab3ef664c52546ee7de6d653eed6caf50 Mon Sep 17 00:00:00 2001 From: aszerW Date: Mon, 11 May 2026 23:09:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(rotation):=20=E5=AE=9E=E7=8E=B0=E8=BD=AE?= =?UTF-8?q?=E5=8A=A8=E7=AD=96=E7=95=A5=EF=BC=88=E4=BD=BF=E7=94=A8=E6=A1=86?= =?UTF-8?q?=E6=9E=B6=E6=8A=BD=E8=B1=A1+=E5=AE=9A=E5=88=B6=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RotationStrategy: 继承StrategyBase,使用MomentumFactor+TopNSelector - 实现before_entry溢价过滤、dynamic_stoploss动态止损、custom_exit自定义出场 - 策略配置从类属性读取,支持config覆盖 --- strategies/__init__.py | 9 ++++ strategies/rotation/__init__.py | 7 +++ strategies/rotation/strategy.py | 80 +++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 strategies/rotation/strategy.py diff --git a/strategies/__init__.py b/strategies/__init__.py index e69de29..7bac080 100644 --- a/strategies/__init__.py +++ b/strategies/__init__.py @@ -0,0 +1,9 @@ +""" +strategies模块入口 + +包含所有策略实现 +""" + +from strategies.rotation import RotationStrategy + +__all__ = ['RotationStrategy'] \ No newline at end of file diff --git a/strategies/rotation/__init__.py b/strategies/rotation/__init__.py index e69de29..fb303df 100644 --- a/strategies/rotation/__init__.py +++ b/strategies/rotation/__init__.py @@ -0,0 +1,7 @@ +""" +轮动策略模块入口 +""" + +from strategies.rotation.strategy import RotationStrategy + +__all__ = ['RotationStrategy'] \ No newline at end of file diff --git a/strategies/rotation/strategy.py b/strategies/rotation/strategy.py new file mode 100644 index 0000000..13f7463 --- /dev/null +++ b/strategies/rotation/strategy.py @@ -0,0 +1,80 @@ +""" +轮动策略定制实现 + +使用framework通用能力 + 定制组件 +""" + +import pandas as pd +import yaml +from datetime import datetime + +from framework.factors import FactorBase, FactorRegistry, FactorCombiner +from framework.signals import SignalGenerator +from framework.risk import CallbackHook, Position +from framework.strategy import StrategyBase +from framework.config import ConfigLoader + +# 导入定制组件 +from strategies.shared.factors.momentum import MomentumFactor +from strategies.shared.signals.selectors import TopNSelector +from strategies.shared.risk.controls import premium_filter_callback, holding_time_stoploss_callback + + +class RotationStrategy(StrategyBase): + """ + ETF轮动策略(定制实现) + + 基于动量因子 + Top N选股 + 溢价过滤 + """ + + name = "rotation" + select_num = 3 + stoploss = -0.05 + + def init_factors(self) -> FactorCombiner: + """初始化动量因子""" + # 清空注册表(避免重复注册) + FactorRegistry.clear() + + # 注册定制因子 + FactorRegistry.register(MomentumFactor) + + return FactorCombiner([ + FactorRegistry.get('momentum', n_days=25, crash_filter=True) + ]) + + def init_signal_generator(self) -> SignalGenerator: + """初始化Top N选股器(定制)""" + return TopNSelector( + select_num=self.select_num, + min_score=0.0, + group_by='market' # 定制:按大类分组 + ) + + def before_entry(self, code: str, price: float, **kwargs) -> bool: + """入场前:溢价过滤(定制)""" + premium = kwargs.get('premium', 0) + + # 定制阈值:10% + if premium > 0.10: + print(f"溢价过高,拒绝入场: {code} (溢价={premium:.2%})") + return False + + return True + + def dynamic_stoploss(self, position: Position) -> float: + """动态止损:根据持仓时间调整(定制)""" + # 定制规则:5天/10天阈值 + if position.holding_days >= 10: + return -0.03 + elif position.holding_days >= 5: + return -0.05 + return -0.10 + + def custom_exit(self, position: Position) -> bool: + """自定义出场条件(定制)""" + # 定制规则:亏损超过阈值强制出场 + if position.profit_ratio < -0.10: + print(f"亏损超阈值,强制出场: {position.code}") + return True + return False \ No newline at end of file