量化研究--开放ptrade西蒙斯全天候ETF策略动量策略
文章声明:本内容为个人的业余研究,和任何单位,机构没有关系,文章出现的股票代码,全部只是测试例子,不做投资参考,投资有风险,代码学习使用,不做商业用途
ptrade运行在证券公司的服务器,稳定性好,不需要怎么样维护就可以24小时运行,群友需要一个ptrade的实盘交易模板,我把聚宽的动量策略改到了ptrade给大家参考实盘写,今天的行情可以
我挂的ptrade策略测试还可以的,我在迁移小市值,指数增强策略到ptrade
实盘交易的挂的策略数据
pt非常的稳定
策略的原理
该“西蒙斯全天候ETF动量策略”是一个设计良好、逻辑清晰、具备基础风控的中期动量轮动策略。它非常适合作为Ptrade平台的入门到进阶的实盘策略进行学习和实践。
其最大的亮点在于通过一个分散的ETF池和简单的动量规则,试图捕捉不同类别资产的中期上涨趋势,并在趋势不明时自动转入防御状态。
策略核心逻辑分析 (Core Strategy Logic)
该策略本质上是一个动量因子驱动的单一资产轮动策略。其核心思想可以概括为:
-
选择一篮子不同资产类别的ETF(如黄金、美股、港股、债券、商品、A股不同风格),构建一个“全天候”的备选池。
-
定期计算备选池中每个ETF在特定回顾期(20天)内的动量(涨幅)。
-
买入并持有过去20天内动量最强(涨幅最大) 的那一只ETF。
-
风险控制:如果所有ETF的动量都为负(即过去20天都在下跌),则策略默认买入国债ETF(
511090.SS
)以规避风险,寻求避险资产的稳定收益。 -
定期轮动:每5个交易日检查并执行一次上述调仓操作。
代码模块逐行解析 (Code Walkthrough)
1. 初始化函数 initialize(context)
这是策略的设置环节,在所有回测或实盘交易开始前只执行一次。
-
g.trader_type
和g.user_cash
: 提供了两种模式。“默认”模式使用账户全部资金,“自定义”模式使用固定金额。这增加了策略的灵活性。 -
context.ETF_POOL
: 这是策略的核心之一。作者精心挑选了9只ETF,覆盖了:-
大宗商品: 黄金(
518880
)、豆粕(159985
) -
海外资产: 纳斯达克(
513100
)、恒生科技(513130
) -
固定收益/避险资产: 国债(
511010
)、30年国债(511090
) -
A股不同风格: 红利(
515100
)、创业板(成长159915
)、中证1000(小盘成长512100
) -
评价:这个组合确实体现了“全天候”的思想,试图在各种宏观环境下(增长、通胀、衰退)都能有表现不错的资产被选中。但组合中缺乏最核心的宽基指数(如沪深300ETF),这是一个值得注意的特点或遗漏。
-
-
context.rebalance_days = 5
: 调仓频率。5天一次降低了交易频率,契合动量因子中期有效的特性,同时也有效控制了交易成本。 -
context.days = 20
: 动量计算周期。20个交易日(约1个月)是动量策略中非常常用和经典的回顾期。 -
run_daily(rebalance, time='10:00')
: 每天上午10点运行rebalance
函数,但函数内部有判断,并非每天都会调仓。
2. 动量计算函数 calculate_momentum(etf, context)
-
计算方法:
(最新价 - 20日前价) / 20日前价
。这是最简单、最直接的动量(或称“速率”)计算方式。 -
异常处理: 使用
try-except
捕获可能的数据获取错误(如ETF尚未上市),并返回一个极低的动量值(-100
),确保有问题的ETF不会被选中。这是一个很好的编程实践。 -
潜在问题: 函数内使用
get_price
并指定开始日期为'20200101'
,在回测中没问题,但在 long-running 的实盘中,每次调用都获取从2020年至今的全部数据再截取最后20天,效率极低,会给服务器造成不必要的压力。应优化为直接获取最近context.days+1
天的数据。
3. 选择最佳ETF函数 get_top_momentum_etf(etf_pool, context)
-
使用字典推导式计算池中所有ETF的动量。
-
使用
max(scores, key=scores.get)
找到动量值最大的ETF代码。逻辑清晰简洁。
4. 调仓函数 rebalance(context, data=None)
这是策略的执行中枢。
-
调仓周期判断:
context.days_since_rebalance
计数器用于确保每5个交易日才执行一次调仓逻辑。 -
动量阈值风控: 这是策略另一个核心点。在选中动量最强的ETF后,会检查其动量值是否大于0。如果连最强的动量都为负,则说明所有资产处于普跌的弱势环境,此时策略 Override 选择,买入30年期国债ETF(
511090.SS
)进行防御。这是一个非常关键且有效的风险控制机制。 -
交易执行:
-
只有当目标ETF与当前持仓ETF不同时,才执行调仓(先卖后买),避免了不必要的交易。
-
根据
g.trader_type
决定下单金额(全仓或固定金额)。
-
-
日志输出: 大量的
print
语句非常适合实盘模拟盘调试,可以清晰跟踪策略的每一步决策。
策略评价与潜在问题 (Evaluation & Potential Issues)
优点 (Strengths):
-
逻辑清晰简洁:策略很容易理解和实现,因子的经济学逻辑(动量效应)也广为人知。
-
资产配置分散:ETF池覆盖多个低相关性的资产类别,理论上能平滑曲线,降低单一市场风险。
-
内置风控:动量负值转向国债的规则提供了下行保护,这是策略稳健性的重要保证。
-
交易频率适中:5天调仓一次兼顾了策略敏感度和交易成本。
潜在问题与改进点 (Weaknesses & Improvements):
-
动量指标单一:仅使用20期收益率代表动量,可能噪音较大。可考虑加入动量趋势的稳定性作为过滤条件(例如,要求价格在20日均线之上),或使用综合动量指标(如
ROC(20) * 0.5 + ROC(50) * 0.3 + ROC(120) * 0.2
)。 -
“龟兔赛跑”问题:策略永远只持有第一名。如果某个ETF短期暴涨后动能衰竭,策略会立即切换到新的第一名,可能导致频繁追高并在反转点买入。可以考虑设置动量阈值(例如,新动量的领先优势必须超过原持仓动量一定比例才切换),以减少无效交易。
-
数据获取效率:如前所述,
calculate_momentum
函数内的数据获取方式在实盘中效率低下,需要优化。 -
极端行情下的流动性问题:策略没有考虑ETF本身的流动性(成交量)。如果选中一只成交量很小的ETF,大资金实盘交易会产生巨大的冲击成本。
-
参数优化:
20
天动量窗口和5
天调仓周期是默认参数,其有效性需要针对历史数据进行回测优化,并警惕过拟合风险。 -
无止损机制:策略依赖于动量值为负时切换至国债来进行风控,但对于已持仓的品种,没有设置个股级别的止损线。
回测的结果,详细的分析内容
量化研究--开放ptrade西蒙斯全天候ETF策略动量策略https://mp.weixin.qq.com/s/uAPzg2SsjgcvN_3xSN1YpA
更多pt实盘在上线
pt动量模板直接加我获取就可以,学习使用,多模拟盘测试,参考写实盘
'''
西蒙斯全天候ETF策略动量策略
ptrade实盘交易回测模板,先模拟盘测试熟悉
作者:西蒙斯量化
时间:20250825
作者微信:xg_quant
'''
def initialize(context):
#交易类型=默认/自定义
g.trader_type='默认'
#自定义金额
g.user_cash=50000
g.holdetf = ''
context.ETF_POOL = [
'518880.SS', # 黄金ETF(大宗商品)
'513100.SS', # 纳指100(海外资产)
'513130.SS', # 恒生科技(海外资产)
'515100.SS', # 红利ETF(红利产品)
'159985.SZ', #大宗豆粕ETF(大宗商品)
'511010.SS', # 国债ETF(国债产品)
'511090.SS', # 30年国债ETF(国债产品)
'159915.SZ', # 创业板100(成长股)
'512100.SS', # 中证1000ETF(成长股)
]
# 调仓频率,5天轮动一次
context.rebalance_days = 5
#运行天数记录
context.days_since_rebalance = 0
#动量周期
context.days=20
# 定时运行函数
run_daily(context,rebalance, time='10:00')
# 动量因子计算
def calculate_momentum(etf, context): # 新增context参数
days=context.days
try:
# 获取过去n+1天的收盘价
current_dt=context.blotter.current_dt
current_dt=current_dt.strftime('%Y-%m-%d')
prices=get_price(etf, start_date='20200101', end_date=current_dt, frequency='1d')['close'].tolist()[-days:]
# 计算动量
return (prices[-1] - prices[0]) / prices[0]
except:
print(etf,'可能没有上市有问题')
return -100
# 获取动量最高的ETF
def get_top_momentum_etf(etf_pool, context): # 添加context参数
scores = {etf: calculate_momentum(etf, context) for etf in etf_pool}
return max(scores, key=scores.get)
# 调仓函数
def rebalance(context, data=None):
current_dt=context.blotter.current_dt
current_dt=current_dt.strftime('%Y-%m-%d')
# 每隔指定天数调仓一次
if context.days_since_rebalance < context.rebalance_days:
context.days_since_rebalance += 1
print("{} 目前不是轮动时间 目前已经轮动时间{}".format(current_dt,context.days_since_rebalance))
return
context.days_since_rebalance = 0
target_etf = get_top_momentum_etf(context.ETF_POOL, context) # 添加context参数
print("{}今天动量最大的标的{}".format(current_dt,target_etf))
# 新增动量值判断逻辑:当最高动量小于0时选择国债ETF
momentum_score = calculate_momentum(target_etf, context) # 添加context参数
if momentum_score < 0:
target_etf = '511090.XSHG' # 30年国债ETF代码
if target_etf != g.holdetf:
if g.holdetf != '':
order_target_value(g.holdetf, 0)
g.holdetf = target_etf