它们的关系是这样的:
行情进来 �?on_tick() 每秒触发几百�? �?on_bar() 每分钟合成一根K�? �?策略逻辑�?on_bar() 里判�? �?条件满足 �?发出买入指令
�?on_trade() 确认成交
�?持仓�?�?等止�?止损条件
�?条件满足 �?发出卖出指令
为什么策略逻辑放在 on_bar() 而不�?on_tick()�?/strong>
因为tick太碎了。螺纹钢一秒钟可能有几十笔tick,你如果在tick里做判断,一秒钟判断几十次,大部分是噪音�?/p>
K线是"压缩后的行情"。一�?分钟K线把60秒的所有tick压缩成四个数字:开盘价、最高价、最低价、收盘价。你只需要看这四个数字,就能判断"这根K线发生了什�?�?/p>
就像你看天气预报,不需要每秒看温度计,�?今天最高温32�?就够了�?/p>
创建文件 breakout_strategy.py,把下面的代码复制进去:
import time
from ctpbee import CtpBee
from ctpbee.constant import TickData, BarData, TradeData, OrderData
from ctpbee.constant import Direction, Offset, Exchange, OrderType
============ 账户配置 ============
BROKER_ID = "9999"
USER_ID = "你的SimNow账号"
PASSWORD = "你的密码"
TD_ADDRESS = "tcp://180.168.146.187:10130"
MD_ADDRESS = "tcp://180.168.146.187:10131"
============ 策略参数 ============
SYMBOL = "rb2510" # 合约代码(改成当前主力合约)
EXCHANGE = Exchange.SHFE # 上期所
LOTS = 1 # 每次�?�?PROFIT_TARGET = 50 # 止盈�?0个点
STOP_LOSS = 30 # 止损�?0个点
============ 策略状�?============
yesterday_high = None # 昨天最高价
yesterday_close = None # 昨天收盘�?yesterday_open = None # 昨天开盘价
position = 0 # 当前持仓�?=空仓�?=持多�?entry_price = 0 # 入场价格
today_traded = False # 今天是否已经交易�?
====================================
创建应用
====================================
app = CtpBee("breakout", "strategy")
config = {
"CONNECT_INFO": {
"brokerid": BROKER_ID,
"userid": USER_ID,
"password": PASSWORD,
"td_address": TD_ADDRESS,
"md_address": MD_ADDRESS,
},
"SUBSCRIBE_INFO": [SYMBOL] # 订阅行情
}
app.config.from_mapping(config)
====================================
策略逻辑
====================================
@app.route(handler="on_bar")
def on_bar(bar):
"""
每分钟收到一根K线时触发�? 策略的核心判断在这里�? """
global yesterday_high, yesterday_close, yesterday_open
global position, entry_price, today_traded
# ---- 第一步:记录昨天的高�?----
# 每根K线都有日期。如果日期变了,说明新的一天开始了�? # 我们用新一天的第一根K线来"锁定"昨天的数据�? # (简化处理:实际项目中应该用日线数据�?
# ---- 第二步:判断买入条件 ----
# 条件1:今天还没交易过(一天只做一次)
# 条件2:昨天是阳线(收�?> 开盘),说明昨天上涨了
# 条件3:当前价格突破了昨天最高价
if not today_traded and yesterday_high is not None:
# 昨天是阳线?
is_yang_line = yesterday_close > yesterday_open
# 当前K线的最高价突破了昨天高点?
if is_yang_line and bar.high_price > yesterday_high:
print(f"🚀 突破信号!当前最高价 {bar.high_price} > 昨天高点 {yesterday_high}")
print(f" 昨天是阳线:开�?{yesterday_open},收�?{yesterday_close}")
# 发出买入指令
app.buy(
symbol=SYMBOL,
exchange=EXCHANGE,
price=bar.close_price, # 用当前收盘价下单
volume=LOTS,
order_type=OrderType.LIMIT # 限价�? )
today_traded = True
print(f" 📤 已发出买入指令:{SYMBOL} {LOTS}�?@ {bar.close_price}")
# ---- 第三步:判断止盈/止损 ----
if position > 0: # 如果持有多头仓位
profit = bar.close_price - entry_price # 盈亏点数
if profit >= PROFIT_TARGET:
print(f"💰 止盈触发!盈�?{profit} �?>= 目标 {PROFIT_TARGET} �?)
app.sell(
symbol=SYMBOL,
exchange=EXCHANGE,
price=bar.close_price,
volume=LOTS,
order_type=OrderType.LIMIT
)
print(f" 📤 已发出卖出指令(止盈�?)
elif profit <= -STOP_LOSS:
print(f"🛑 止损触发!亏�?{profit} �?<= 容忍 {-STOP_LOSS} �?)
app.sell(
symbol=SYMBOL,
exchange=EXCHANGE,
price=bar.close_price,
volume=LOTS,
order_type=OrderType.LIMIT
)
print(f" 📤 已发出卖出指令(止损�?)
@app.route(handler="on_tick")
def on_tick(tick):
"""
收到行情时触发。这里我们只用来更新昨天的高点数据�? (简化版:用tick的最高价来模拟昨天的最高价�? """
global yesterday_high, yesterday_close, yesterday_open
# 在实际项目中,你应该用日线数据来获取昨天的高点�? # 这里简化处理:用最近看到的最高价作为参考�? # 后面回测章节会教你用正确的方式获取历史数据�? if yesterday_high is None or tick.last_price > yesterday_high:
yesterday_high = tick.last_price
if yesterday_close is None:
yesterday_close = tick.last_price
if yesterday_open is None:
yesterday_open = tick.last_price
@app.route(handler="on_trade")
def on_trade(trade):
"""
成交回报。当你的订单真正成交时触发�? """
global position, entry_price
if trade.direction == Direction.LONG:
# 买入成交
position += trade.volume
entry_price = trade.price
print(f"�?买入成交!价�?{trade.price},数�?{trade.volume}�?)
print(f" 当前持仓:{position}手,入场价:{entry_price}")
elif trade.direction == Direction.SHORT:
# 卖出成交
position -= trade.volume
print(f"�?卖出成交!价�?{trade.price},数�?{trade.volume}�?)
print(f" 当前持仓:{position}�?)
@app.route(handler="on_order")
def on_order(order):
"""
委托状态回报。你的订单被交易所受理、排队、成交、或拒绝时触发�? """
print(f"📋 委托状态更新:{order.status} | {order.symbol} | {order.direction}")
====================================
启动
====================================
if __name__ == "__main__":
print("=" * 60)
print("🚀 突破策略启动")
print(f" 品种:{SYMBOL}")
print(f" 止盈:{PROFIT_TARGET} �?)
print(f" 止损:{STOP_LOSS} �?)
print(f" 手数:{LOTS}")
print("=" * 60)
app.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n🛑 程序停止")
app.release()
SYMBOL = "rb2510"
EXCHANGE = Exchange.SHFE
LOTS = 1
PROFIT_TARGET = 50
STOP_LOSS = 30
这五行定义了你的策略"性格"�?/p>
为什么止盈比止损大? 因为50:30的比例意味着你赚一次的钱比亏一次的钱多。只要胜率超�?7.5%�?0÷80),长期就是赚钱的。这就是"盈亏�?的概念�?/p>
yesterday_high = None
position = 0
entry_price = 0
today_traded = False
这些变量记录策略�?记忆"。程序不像人,它没有脑子,所以你得用变量帮它记住�?/p>
today_traded = False 这个特别重要。没有它,你的程序可能一分钟触发一次买入信号,一天买几十次——那不是策略,那是乱买�?/p>
if not today_traded and yesterday_high is not None:
is_yang_line = yesterday_close > yesterday_open
if is_yang_line and bar.high_price > yesterday_high:
app.buy(...)
三个条件同时满足才买�?/p>
1. 今天没交易过 �?防止重复下单
2. 昨天是阳�?/strong> �?昨天上涨了,今天突破才有意义。如果昨天暴跌,今天突破昨天高点可能只是反弹,不是趋势�?/p>
3. 当前价格突破昨天高点 �?这就�?突破"的核心定�?/p>
三个条件缺一个都不买。这就是策略�?纪律"�?/p>
止盈止损逻辑
if position > 0:
profit = bar.close_price - entry_price
if profit >= PROFIT_TARGET:
app.sell(...) # 止盈
elif profit <= -STOP_LOSS:
app.sell(...) # 止损
持仓之后,每根K线都检查盈亏:
为什么用 elif 而不是两�?if�?/strong> 因为止盈和止损不可能同时触发。要么赚够了,要么亏够了,不会既赚够又亏够�?/p>
下单函数
app.buy(symbol, exchange, price, volume, order_type)
app.sell(symbol, exchange, price, volume, order_type)
buy 是买入开仓,sell 是卖出平仓。参数一目了然:
限价�?vs 市价单:
新手建议用限价单。你至少知道自己以什么价格下单了�?/p>
保存代码,运行:
python breakout_strategy.py
你会看到�?/p>
============================================================
🚀 突破策略启动
品种:rb2510
止盈�?0 �? 止损�?0 �? 手数�?
============================================================
📊 行情数据持续接收�?..
🚀 突破信号!当前最高价 3812.00 > 昨天高点 3805.00
昨天是阳线:开�?3798.00,收�?3803.00
📤 已发出买入指令:rb2510 1�?@ 3812.00
�?买入成交!价�?3812.00,数�?1�? 当前持仓�?手,入场价:3812.00
💰 止盈触发!盈�?52 �?>= 目标 50 �? 📤 已发出卖出指令(止盈�?�?卖出成交!价�?3864.00,数�?1�? 当前持仓�?�?
从信号到成交到止盈,全程自动。你不需要盯着屏幕,不需要犹豫,不需要手动点按钮�?/p>
你的程序替你做了所有事�?/strong>
这就是程序化交易。不是AI替你思考,不是机器替你决策。是你定好规则,机器替你执行�?/p>
说实话:不一定�?/strong>
突破策略在趋势行情里表现不错,但在震荡行情里会被反复打脸——突破昨天高点,买入,然后价格回落,止损。再突破,再买入,再止损。一天亏三次�?/p>
这就是为什么下一集要�?strong>回测——用历史数据验证你的策略到底行不行,而不是靠感觉�?/p>
但不管能不能赚钱,这一集你学会了一件更重要的事�?strong>怎么把一个交易思路变成代码�?/strong>
思路 �?五个问题 �?四个函数 �?完整程序。这个流程,以后不管你写什么策略,都是一样的�?/p>
CTP: 合约 rb2510 不存�?
合约有生命周期。rb2510�?025�?0月交割的合约,到�?025�?0月就没了。你需要改成当前的主力合约。去SimNow交易端看一下,或者搜"螺纹钢主力合约代�?�?/p>
CTP: 可用资金不足
SimNow模拟账户默认�?00万。如果你之前做了很多交易没平仓,可用资金可能不够。先平掉所有持仓再试�?/p>
限价单需要排队。如果你的买入价格比当前市价低,可能一直排不到。改成市价单试试,或者把买入价格设成当前卖一价�?/p>
检�?today_traded 变量。如果忘了加这个限制,程序会在每根K线都判断一次突破条件,可能反复下单�?/p>
1. 策略就是五个问题的答�?/strong>:品种、买入条件、数量、卖出条件、不做条�?/p>
2. 四个核心函数:on_tick(看行情)、on_bar(看K线做判断)、on_trade(确认成交)、on_order(委托状态) 3. 突破策略的完整代�?/strong>:从参数定义到止盈止损,每一步都有解�?/p>
4. 止盈止损的盈亏比�?0:30意味着胜率超过37.5%就长期盈�?/p>
5. 限价�?vs 市价�?/strong>:新手用限价单更安全 �?集你学会�?写策�?。但这个策略到底能不能赚钱?你不知道�?/p>
下一集:回测——用过去三年的历史数据跑一遍你的策略,看它到底赚不赚、亏不亏、什么时候表现好、什么时候会崩�?/p>
回测是程序化交易最核心的能力。没有回测,你的策略就是赌博。有了回测,你的策略就是科学实验�?/p>
明天见�?/p>
*本系列面向零基础读者。有疑问加微�?futures886 或在评论区留言�? 果子量化 - AI驱动的期货程序化交易平台 官网�?a href="https://gocxh.com">gocxh.com | 微信:futures886
下一集预�?/h2>