Files
sanguo_quant_live/strategies/pure-breakout-20260327/main_strategy_single_file.py
T
2026-04-02 08:55:07 +08:00

162 lines
5.7 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
纯突破量化策略
N日新高放量突破买入,严格止损止盈
"""
import pandas as pd
import numpy as np
from vnpy.app.cta_strategy import CtaTemplate
from vnpy.trader.object import BarData
class PureBreakoutStrategy(CtaTemplate):
"""
纯突破策略
- N日新高放量突破买入
- 止损:跌破突破日最低价的-5%
- 跟踪止盈:从买入后高点回落10%止盈
- 均线止盈:跌破均线卖出
- 最长持有到期自动卖出
- 单票最大仓位5%
"""
author = "翼德"
parameters = [
"breakout_days",
"volume_multiple",
"stop_loss_pct",
"trailing_stop_pct",
"ma_period",
"max_holding_days",
"max_position_pct",
]
variables = [
]
def __init__(self, cta_engine, strategy_name, setting_dict):
super().__init__(cta_engine, strategy_name, setting_dict)
# 默认参数
self.breakout_days = getattr(self, 'breakout_days', 60)
self.volume_multiple = getattr(self, 'volume_multiple', 1.5)
self.stop_loss_pct = getattr(self, 'stop_loss_pct', 0.05)
self.trailing_stop_pct = getattr(self, 'trailing_stop_pct', 0.10)
self.ma_period = getattr(self, 'ma_period', 20)
self.max_holding_days = getattr(self, 'max_holding_days', 60)
self.max_position_pct = getattr(self, 'max_position_pct', 0.05)
# 持仓信息
self.in_market = False
self.entry_price = 0
self.breakout_low = 0
self.highest_price = 0
self.entry_date = None
self.holding_days = 0
def on_init(self):
self.write_log("策略初始化完成")
self.load_bar(self.breakout_days + self.max_holding_days)
def on_start(self):
self.write_log("策略启动")
def on_stop(self):
self.write_log("策略停止")
def on_bar(self, bar: BarData):
if self.in_market:
self.holding_days += 1
# 更新最高价
if bar.close > self.highest_price:
self.highest_price = bar.close
# 检查卖出条件
exit_signal = False
# 1. 止损检查 - 跌破突破低价*(1-stop_loss_pct)
stop_price = self.breakout_low * (1 - self.stop_loss_pct)
if bar.low <= stop_price:
exit_signal = True
self.write_log(f"触发止损,价格{bar.low:.2f} <= 止损价{stop_price:.2f}")
# 2. 跟踪止盈 - 从最高点回落超过trailing_stop_pct
if not exit_signal:
trailing_price = self.highest_price * (1 - self.trailing_stop_pct)
if bar.close <= trailing_price:
exit_signal = True
self.write_log(f"触发跟踪止盈,价格{bar.close:.2f} <= 止盈价{trailing_price:.2f}")
# 3. 均线止盈 - 收盘价跌破均线
if not exit_signal:
closes = self.get_bars(self.ma_period)
if len(closes) >= self.ma_period:
ma = np.mean([b.close for b in closes])
if bar.close < ma:
exit_signal = True
self.write_log(f"触发均线止盈,价格{bar.close:.2f} < MA{self.ma_period}={ma:.2f}")
# 4. 持有到期
if not exit_signal and self.holding_days >= self.max_holding_days:
exit_signal = True
self.write_log(f"持有到期{self.holding_days}天,自动卖出")
if exit_signal:
# 全部卖出
position = self.get_position(self.vt_symbol)
if position and position.volume > 0:
self.sell(self.vt_symbol, bar.close, position.volume)
self.in_market = False
return
# 如果没持仓,检查突破信号
else:
# 获取最近N日数据
bars = self.get_bars(self.breakout_days + 1)
if len(bars) < self.breakout_days + 1:
return
# 计算N日最高价
highest = max(b.close for b in bars[:-1])
current_close = bar.close
current_volume = bar.volume
# 计算N日平均成交量
avg_volume = np.mean(b.volume for b in bars[:-1])
# 突破条件:收盘价创新高 + 成交量放量
if current_close > highest and current_volume >= avg_volume * self.volume_multiple:
# 突破买入
# 计算目标仓位
target_value = self.balance * self.max_position_pct
target_volume = int(target_value / bar.open / 100) * 100
if target_volume > 0:
self.buy(self.vt_symbol, bar.open, target_volume)
self.in_market = True
self.entry_price = bar.open
self.breakout_low = bar.low
self.highest_price = bar.close
self.entry_date = bar.datetime
self.holding_days = 0
self.write_log(f"突破买入,价格{bar.open:.2f},数量{target_volume}")
return
def _need_rebalance(self, current_date):
if self.last_rebalance_date is None:
return True
if self.rebalance_freq == 'M':
if current_date.month != self.last_rebalance_date.month:
return True
elif self.rebalance_freq == 'W':
current_week = current_date.isocalendar()[1]
last_week = self.last_rebalance_date.isocalendar()[1]
if current_week != last_week:
return True
return False