问题描述
解决方案
这是一个基于PTrade API实现的马丁格尔(Martingale)策略。
策略逻辑说明
马丁格尔策略的核心思想是“亏损加仓”,通过在价格下跌时不断加倍买入,从而摊低持仓成本。只要价格出现反弹,就能快速回本并获利。
具体规则如下:
- 初始建仓:当空仓时,买入基础数量(如100股)。
- 加倍补仓:如果当前价格相比上一次买入价格下跌了指定幅度(如5%),则以上一次买入数量的2倍进行补仓。
- 止盈出场:当当前价格高于持仓平均成本的一定幅度(如10%)时,卖出全部持仓,重置策略。
- 风控限制:为了防止资金耗尽,设置最大加仓次数限制。
风险提示
马丁格尔策略属于高风险策略。在单边下跌行情中,所需的资金呈指数级增长(1, 2, 4, 8, 16...),极易导致资金链断裂或深度套牢。请务必在模拟盘充分测试,并设置好最大加仓次数。
PTrade 策略代码
def initialize(context):
"""
策略初始化函数
"""
# 1. 设置要操作的股票代码 (示例:贵州茅台)
g.security = '600519.SS'
set_universe(g.security)
# 2. 策略参数设置
g.base_amount = 100 # 初始建仓数量(股)
g.drop_pct = 0.05 # 下跌补仓阈值 (5%)
g.profit_pct = 0.10 # 止盈阈值 (10%)
g.martingale_factor = 2 # 加仓倍数 (通常为2)
g.max_add_times = 4 # 最大加仓次数 (防止资金耗尽)
# 3. 全局变量用于记录状态
g.add_times = 0 # 当前已加仓次数
g.last_buy_price = 0 # 上一次买入的价格
g.last_buy_amount = 0 # 上一次买入的数量
def handle_data(context, data):
"""
按频率运行的策略函数 (日线或分钟线)
"""
security = g.security
# 获取当前最新价格
current_price = data[security]['close']
# 获取当前持仓信息
position = get_position(security)
# --- 场景1:当前无持仓,进行初始建仓 ---
if position.amount == 0:
# 资金充足性检查 (简单估算)
cost = current_price * g.base_amount
if context.portfolio.cash >= cost:
order(security, g.base_amount)
# 更新状态记录
g.last_buy_price = current_price
g.last_buy_amount = g.base_amount
g.add_times = 0
log.info("初始建仓: %s, 价格: %.2f, 数量: %d" % (security, current_price, g.base_amount))
else:
log.warning("资金不足,无法进行初始建仓")
# --- 场景2:持有仓位,判断是否止盈或补仓 ---
else:
avg_cost = position.cost_basis # 持仓平均成本
# A. 判断止盈:当前价格 > 平均成本 * (1 + 止盈率)
if current_price >= avg_cost * (1 + g.profit_pct):
# 卖出所有持仓
order_target(security, 0)
# 重置状态
g.add_times = 0
g.last_buy_price = 0
g.last_buy_amount = 0
log.info("触发止盈: %s, 卖出价格: %.2f, 盈利幅度: %.2f%%" % (
security, current_price, (current_price - avg_cost)/avg_cost * 100))
return
# B. 判断补仓:当前价格 < 上次买入价 * (1 - 下跌阈值)
# 且加仓次数未达到上限
if current_price <= g.last_buy_price * (1 - g.drop_pct):
if g.add_times < g.max_add_times:
# 计算本次需要买入的数量 = 上次数量 * 倍数
buy_amount = g.last_buy_amount * g.martingale_factor
# 资金检查
cost = current_price * buy_amount
if context.portfolio.cash >= cost:
order(security, buy_amount)
# 更新状态
g.last_buy_price = current_price
g.last_buy_amount = buy_amount
g.add_times += 1
log.info("触发第 %d 次补仓: %s, 价格: %.2f, 数量: %d" % (
g.add_times, security, current_price, buy_amount))
else:
log.warning("触发补仓信号,但资金不足!当前需资金: %.2f, 可用资金: %.2f" % (
cost, context.portfolio.cash))
else:
# 达到最大加仓次数,不再操作,只能躺平等待反弹或手动干预
pass
def before_trading_start(context, data):
"""
盘前处理 (可选)
"""
pass
def after_trading_end(context, data):
"""
盘后处理 (可选)
"""
pass
代码关键点解析
-
状态记录 (
g.last_buy_price,g.last_buy_amount):- 马丁格尔策略的关键在于相对于“上一次操作”进行判断,而不是相对于平均成本。因此我们需要在全局变量
g中手动记录上一次买入的价格和数量。 position.cost_basis仅用于计算止盈条件。
- 马丁格尔策略的关键在于相对于“上一次操作”进行判断,而不是相对于平均成本。因此我们需要在全局变量
-
加仓逻辑:
buy_amount = g.last_buy_amount * g.martingale_factor:这里实现了指数级加仓(例如:100 -> 200 -> 400 -> 800)。g.max_add_times:这是一个非常重要的风控参数。如果没有这个限制,在股票连续下跌时,账户资金会极快耗尽。
-
止盈逻辑:
- 止盈是基于平均成本 (
avg_cost) 计算的。因为补仓拉低了均价,只要价格反弹超过均价的一定比例,即可整体获利离场。
- 止盈是基于平均成本 (
-
Python 版本兼容性:
- 代码使用了
%格式化字符串(如log.info("..." % (...))),这是为了兼容 PTrade 可能使用的 Python 3.5 环境(不支持 f-string)。
- 代码使用了
常见问题 (Q&A)
Q: 如何修改为按固定金额加仓而不是按股数翻倍?
A: 可以将 g.base_amount 理解为金额,在下单时使用 order_value,并在计算下一次下单金额时乘以倍数。
Q: 这个策略适合什么行情?
A: 适合震荡行情。在单边下跌行情中会造成巨额亏损,在单边上涨行情中资金利用率较低(因为只持有了底仓)。
Q: 回测时为什么会买入失败?
A: 马丁格尔策略对资金要求极高。例如第5次补仓时,单笔买入量是初始量的16倍。如果初始买入1万元,第5次就需要16万元,总投入需要31万元。请确保回测资金充足。