原创内容第1050篇,专注AGI+,AI量化投资、个人成长与财富自由立足长期去规划一件事,去做一件事,打磨一个产品,“小而美”,“小而精”。我把ETF策略总结为四大模板:资产配置、多因子轮动,择时以及策略组合涵盖所有维度:
横截面(轮动):在多个ETF中选择
时间序列(择时):对单个ETF择时
权重分配(配置):确定持仓比例
组合构建(组合):策略层面组合
可组合性:任何复杂策略都可以拆解为这四种基本操作的组合。
从这个意义上讲,ETF策略的玩法比股票还多。
股票基本没有组合的概念,单支择时意义有限,更不需要多个策略组合,股票的主流策略是“多因子轮动”。
从策略模板设计上股票更为简单——不需要区分买入、卖出规则,直接一个筛选规则就好了。
当然工程上,股票数量大,还需要财务报表这样的多因子,因此,在数据计算、性能以及因子存储上,更复杂,要求更高,在多因子上的玩法也更多。
另外还有一个很大的差别,也就是股票一般是选股机制,比如通过截面因子,基本面因子来选股,而etf多是主观确定etf池——这一块可以交给大模型智能体去优化。
如下代码就包含了这四种类型,以及各种止盈、止损的方式,我们可以各种因子,组合出各种策略:
from dataclasses import dataclass, fieldfrom datetime import datetimefrom typing import List, Dict, Optional, Unionfrom enum import Enum
class StrategyType(Enum): ASSET_ALLOCATION = "asset_allocation" ROTATION = "rotation" TIMING = "timing" PORTFOLIO = "portfolio"
class WeightMethod(Enum): EQUALLY = "WeightEqually" FIXED = "WeightFixed" RISK_PARITY = "WeightRiskParity" VOLATILITY = "WeightVolatility"
class PeriodType(Enum): DAILY = "RunDaily" WEEKLY = "RunWeekly" MONTHLY = "RunMonthly" QUARTERLY = "RunQuarterly" YEARLY = "RunYearly" Once = 'RunOnce' CUSTOM_DAYS = "custom_days"
class StopLossType(Enum):
FIXED = "fixed" TRAILING = "trailing" TIME_BASED = "time_based" TECHNICAL = "technical" VOLATILITY = "volatility" COMBINED = "combined"
@dataclassclass FixedStopLossConfig: """固定止损配置""" loss_threshold: float sell_ratio: float enabled: bool = True
def __post_init__(self): if not 0 self.loss_threshold <= 1: raise ValueError("loss_threshold 必须在 (0, 1] 范围内") if not 0 self.sell_ratio <= 1: raise ValueError("sell_ratio 必须在 (0, 1] 范围内")
@dataclassclass TrailingStopLossConfig: """移动止损配置""" profit_threshold: float pullback_threshold: float sell_ratio: float enabled: bool = True
def __post_init__(self): if not 0 self.profit_threshold <= 1: raise ValueError("profit_threshold 必须在 (0, 1] 范围内") if not 0 self.pullback_threshold <= 1: raise ValueError("pullback_threshold 必须在 (0, 1] 范围内") if not 0 self.sell_ratio <= 1: raise ValueError("sell_ratio 必须在 (0, 1] 范围内")
@dataclassclass TimeBasedStopLossConfig: """时间止损配置""" max_hold_days: int sell_ratio: float = 1.0 enabled: bool = True
def __post_init__(self): if self.max_hold_days <= 0: raise ValueError("max_hold_days 必须大于0") if not 0 self.sell_ratio <= 1: raise ValueError("sell_ratio 必须在 (0, 1] 范围内")
@dataclassclass TechnicalStopLossConfig: """技术指标止损配置""" indicator: str condition: str sell_ratio: float = 1.0 enabled: bool = True
def __post_init__(self): if not 0 self.sell_ratio <= 1: raise ValueError("sell_ratio 必须在 (0, 1] 范围内")
@dataclassclass
VolatilityStopLossConfig: """波动率止损配置""" atr_multiplier: float = 2.0 lookback_period: int = 14 sell_ratio: float = 1.0 enabled: bool = True
def __post_init__(self): if self.atr_multiplier <= 0: raise ValueError("atr_multiplier 必须大于0") if self.lookback_period <= 0: raise ValueError("lookback_period 必须大于0") if not 0 self.sell_ratio <= 1: raise ValueError("sell_ratio 必须在 (0, 1] 范围内")
@dataclassclass StopLossConfig: """止损配置(主要用于择时策略)""" stop_loss_type: StopLossType = StopLossType.COMBINED
fixed_stop_losses: List[FixedStopLossConfig] = field(default_factory=list) trailing_stop_losses: List[TrailingStopLossConfig] = field(default_factory=list) time_based_stop_losses: List[TimeBasedStopLossConfig] = field(default_factory=list) technical_stop_losses: List[TechnicalStopLossConfig] = field(default_factory=list) volatility_stop_losses: List[VolatilityStopLossConfig] = field(default_factory=list)
execution_priority: List[StopLossType] = field(default_factory=lambda: [ StopLossType.FIXED, StopLossType.TRAILING, StopLossType.TECHNICAL, StopLossType.VOLATILITY, StopLossType.TIME_BASED ])
@dataclassclass BaseTask: """基础策略配置""" strategy_type: StrategyType symbols: List[str] = field(default_factory=list)
period: PeriodType = PeriodType.DAILY period_days: Optional[int] = None
max_position_ratio: float = 1.0
@dataclassclass AssetAllocationTask(BaseTask): """资产配置策略""" strategy_type: StrategyType = StrategyType.ASSET_ALLOCATION
weight_method: WeightMethod = WeightMethod.EQUALLY weight_fixed: Dict[str, float] = field(default_factory=dict) risk_parity_target: Optional[float] = None
rebalance_threshold: float = 0.05 dynamic_rebalance: bool = True
@dataclassclass RotationTask(BaseTask): """轮动策略""" strategy_type: StrategyType = StrategyType.ROTATION
select_buy_rules: List[str] = field(default_factory=list) select_sell_rules: List[str
] = field(default_factory=list)
min_hold_count: int = 1 max_hold_count: int = 10 buy_at_least_count: int = 0 sell_at_least_count: int = 1
order_by_signal: str = '' order_by_topK: int = 5 order_by_dropN: int = 0 order_by_desc: bool = True
rotation_threshold: float = 0.02 hold_period: Optional[int] = None
@dataclassclass TimingTask(BaseTask): """择时策略""" strategy_type: StrategyType = StrategyType.TIMING
buy_rules: List[str] = field(default_factory=list) sell_rules: List[str] = field(default_factory=list)
position_size: float = 1.0 pyramiding: int = 1
stop_loss_config: StopLossConfig = field(default_factory=StopLossConfig)
use_technical: bool = False technical_params: Dict[str, float] = field(default_factory=dict)
@dataclassclass PortfolioTask(BaseTask): """策略组合""" strategy_type: StrategyType = StrategyType.PORTFOLIO
sub_strategies: List[Union[AssetAllocationTask, RotationTask, TimingTask]] = field(default_factory=list)
weight_method: WeightMethod = WeightMethod.EQUALLY strategy_weights: Dict[str, float] = field(default_factory=dict)
correlation_threshold: float = 0.8 max_drawdown_control: bool = True
if __name__ == "__main__": asset_task = AssetAllocationTask( symbols=['510300.SH', '510500.SH', '159915.SZ'], weight_method=WeightMethod.FIXED, weight_fixed={'510300.SH': 0.5, '510500.SH': 0.3, '159915.SZ': 0.2}, period=PeriodType.MONTHLY, rebalance_threshold=0.1 )
rotation_task = RotationTask( symbols=['512100.SH', '512000.SH', '512700.SH', '515000.SH'], select_buy_rules=['pe_ratio < 20', 'pb_ratio < 2'], order_by_signal='momentum_score', order_by_topK=2, period=PeriodType.WEEKLY )
timing_task = TimingTask( symbols=['510300.SH'], buy_rules=['close > ma20', 'rsi < 30'], sell_rules=['close < ma20', 'rsi > 70'],
position_size=0.8, stop_loss_config=StopLossConfig( fixed_stop_losses=[ FixedStopLossConfig(loss_threshold=0.05, sell_ratio=0.3), FixedStopLossConfig(loss_threshold=0.08, sell_ratio=0.5), FixedStopLossConfig(loss_threshold=0.12, sell_ratio=1.0) ], trailing_stop_losses=[ TrailingStopLossConfig(profit_threshold=0.15, pullback_threshold=0.03, sell_ratio=0.2), TrailingStopLossConfig(profit_threshold=0.25, pullback_threshold=0.05, sell_ratio=0.5), TrailingStopLossConfig(profit_threshold=0.4, pullback_threshold=0.08, sell_ratio=1.0) ], time_based_stop_losses=[ TimeBasedStopLossConfig(max_hold_days=30, sell_ratio=0.5), TimeBasedStopLossConfig(max_hold_days=60, sell_ratio=1.0) ], technical_stop_losses=[ TechnicalStopLossConfig( indicator='ma', condition='close < ma20', sell_ratio=0.5 ), TechnicalStopLossConfig( indicator='boll', condition='close < boll_lower', sell_ratio=0.3 ) ], volatility_stop_losses=[ VolatilityStopLossConfig( atr_multiplier=2.0, lookback_period=14, sell_ratio=0.5 ) ] ) )
portfolio_task = PortfolioTask( sub_strategies=[asset_task, rotation_task, timing_task], weight_method=WeightMethod.FIXED, strategy_weights={'asset_allocation': 0.4, 'rotation': 0.4, 'timing': 0.2} )
代码和数据下载:AI量化实验室——2025量化投资的星辰大海AI量化实验室 星球,已经运行三年多,2000+会员。
aitrader代码,因子表达式引擎、遗传算法(Deap)因子挖掘引擎等,支持vnpy,qlib,backtrader和bt引擎,内置多个年化30%+的策略,每周五迭代一次,代码和数据在星球全部开源。
点击 “查看原文”,直接访问策略集合。