4.策略基础 - 交易进出
交易进出规则是量化策略的骨架,决定了何时开仓、何时平仓。Jesse 把这套逻辑拆分成几个清晰的方法,让策略编写变得像搭积木一样直观。这一章我们把这些核心方法掰开揉碎,看看它们到底怎么工作。
定义做多规则
策略要不要做多,本质上就是一个是非判断。Jesse 用 should_long() 方法来回答这个问题。这个方法必须返回一个布尔值——True 或者 False,没有中间地带。
should_long() 的基本用法
should_long() 的调用时机很讲究:只有在当前没有持仓、也没有挂单的情况下,Jesse 才会在每个新 K 线到来时调用它。这意味着它专门负责"是否开新仓"这个决策,不会干扰已有的仓位管理。
来看一个最简单的例子:
def should_long(self):
# 如果当前 K 线是阳线(收盘价高于开盘价),就返回 True
if self.close > self.open:
return True
return False
这段代码的逻辑直白得不能再直白:看到阳线就想做多。实际策略中当然不会这么简单,但框架就是这样。我们可以在里面写任意复杂的判断条件,比如检查均线排列、RSI 数值、成交量变化等等。只要最后返回 True,Jesse 就会认为"现在应该做多"。
关键约束
这里有个硬性规定:should_long() 和 should_short() 不能同时返回 True。从逻辑上讲,我们不可能在同一个时刻既做多又做空。Jesse 会强制检查这一点,如果两个方法都返回 True,策略会直接报错终止。
另外要注意,should_long() 只负责入场判断。如果已经持有仓位,想动态调整出场点,那不是它的职责范围。这类需求要交给 update_position() 方法处理,这个我们会在后面的章节详细展开。
定义做空规则
做空规则与做多规则完全对称。should_short() 方法同样返回布尔值,决定是否在空仓状态下开空单。
should_short() 的实现方式
def should_short(self):
# 如果当前 K 线是阴线(收盘价低于开盘价),就返回 True
if self.close < self.open:
return True
return False
这段代码和做多例子正好相反,看到阴线就准备做空。实际应用中,这里可以写各种判断市场弱势的信号,比如价格跌破关键支撑、均线空头排列、MACD 死叉等等。
现货模式的限制
需要特别说明的是,做空功能只在期货模式下有效。如果正在开发现货策略,可以简单地让 should_short() 永远返回 False,或者直接留空:
def should_short(self):
return False
def go_short(self):
pass
这样 Jesse 就不会尝试执行任何做空操作,策略可以安全地运行在现货环境中。
执行做多订单
决定做多之后,下一步是精确设定入场价格、仓位大小和出场点位。go_long() 方法就是干这个的。
go_long() 的核心结构
在这个方法里,我们要设置三个关键属性:
def go_long(self):
self.buy = 数量, 入场价格
self.stop_loss = 数量, 止损价格
self.take_profit = 数量, 止盈价格
这三个属性名是 Jesse 规定的,不能更改。它们分别对应买入订单、止损订单和止盈订单。每个属性都需要一个元组,包含两个元素:交易数量和价格。
完整示例
来看一个能直接运行的例子:
def go_long(self):
qty = 1
# 用当前价格市价买入 1 个单位
self.buy = qty, self.price
# 止损设在当前 K 线最低价再低 10 美元的位置
self.stop_loss = qty, self.low - 10
# 止盈设在当前 K 线最高价再高 10 美元的位置
self.take_profit = qty, self.high + 10
这段代码展示了基本用法。qty 变量存储交易数量,self.price 是当前价格,self.low 和 self.high 分别是当前 K 线的最低价和最高价。止损和止盈都设置了 10 美元的缓冲空间。
智能订单系统
Jesse 的订单系统很聪明,能自动判断订单类型,不用我们手动指定。对于做多订单:
- 如果入场价格等于当前价格,自动使用市价单(MARKET)
- 如果入场价格低于当前价格,自动使用限价单(LIMIT)
- 如果入场价格高于当前价格,自动使用止损单(STOP)
这个设计省了不少事。比如上面的例子中,self.buy = qty, self.price 因为价格相等,会自动以市价单执行。如果想在回调时买入,可以写成 self.buy = qty, self.price - 5,这时 Jesse 会生成一个限价单,等待价格跌到指定位置再成交。
执行做空订单
做空订单的设置与做多几乎一样,只是入口属性从 self.buy 变成了 self.sell。
go_short() 的实现
def go_short(self):
self.sell = 数量, 入场价格
self.stop_loss = 数量, 止损价格
self.take_profit = 数量, 止盈价格
结构完全一致,只是 self.sell 表示卖出开仓。
做空示例
def go_short(self):
qty = 1
# 用当前价格市价卖出 1 个单位(开空)
self.sell = qty, self.price
# 止损设在当前 K 线最高价再高 10 美元的位置
self.stop_loss = qty, self.high + 10
# 止盈设在当前 K 线最低价再低 10 美元的位置
self.take_profit = qty, self.low - 10
注意做空的止损止盈逻辑与做多相反。因为是先高价卖出,希望低价买回,所以止损要设在更高位置防止价格上涨,止盈要设在更低位置等待价格下跌。
智能订单系统对做空同样适用:价格等于当前价用市价单,高于当前价用止损单,低于当前价用限价单。
取消入场订单
有时候我们提交了入场订单,但市场走势发生变化,之前的入场条件不再成立。这时就需要 should_cancel_entry() 方法来取消未成交的挂单。
适用场景
这个方法只在特定情况下触发:已经提交了开仓订单,但订单还没成交。常见于使用限价单或止损单入场时。市价单会立即执行,所以不需要取消。
想象一个场景:策略想在价格突破前高时追多,于是提交了一个止损买单,入场价设在前高上方 2 美元。但下一个 K 线出来后,价格没涨反而跌了,前高位置也下移了。这时之前的挂单价格就显得过高,应该取消重挂。
具体实现
def should_long(self):
return True
def go_long(self):
qty = 1
entry = self.high + 2 # 在前高上方 2 美元挂单
self.buy = qty, entry # 这会生成一个止损买单
def should_cancel_entry(self):
return True
这个例子中,should_cancel_entry() 直接返回 True,表示"只要订单没成交,就取消"。实际策略中通常需要加判断条件,比如检查当前价格与挂单价格的距离、市场趋势是否改变等。
重要限制
should_cancel_entry() 只影响入场订单,不会影响已经设置的止损或止盈订单。那些订单的修改需要通过 update_position() 方法来完成。
多点进出管理
前面的例子都是一次性入场、一次性出场。实际交易中,我们可能想分批建仓、分批止盈,甚至动态调整出场点。
分批止盈示例
def go_long(self):
qty = 1
self.buy = qty, 100
self.stop_loss = qty, 80
# 在 120 和 140 两个价位分批止盈
self.take_profit = [
(qty/2, 120), # 平掉一半仓位
(qty/2, 140) # 平掉剩下的一半
]
这里 self.take_profit 接收一个列表,每个元素是一个元组,包含(数量, 价格)。Jesse 会自动为每个价位生成对应的止盈订单。同理,self.stop_loss 也可以这样设置多个出场点。
分批入场示例
def go_long(self):
qty = 1
# 先在 120 买入一半,价格继续上涨到 140 时再买入另一半
self.buy = [
(qty/2, 120),
(qty/2, 140)
]
self.stop_loss = qty, 100
self.take_profit = qty, 160
这种写法让策略能在不同价位分批建仓,降低单次入场的风险。Jesse 会按顺序处理这些订单,成交一个再处理下一个。
动态出场需求
有时候入场时并不知道确切的止盈价位,比如趋势跟踪策略,可能要等趋势反转信号出现才出场。这种情况下,需要在 update_position() 方法中动态更新出场点。这个高级用法会在第 6 章详细讲解。
策略生命周期钩子
除了核心的交易进出方法,Jesse 还提供了几个生命周期钩子,让我们能在特定时机执行自定义逻辑。
before() 方法
这是每个 K 线周期最先调用的方法,适合更新自定义变量或做一些预处理:
def before(self):
# 在策略逻辑执行前更新自定义变量
self.vars.counter = self.vars.counter + 1
after() 方法
这是每个 K 线周期最后调用的方法,适合收尾工作或记录状态:
def after(self):
# 在策略逻辑执行后记录当前仓位信息
if self.position.is_open:
self.log(f"当前盈亏:{self.position.pnl}")
这两个方法在复杂策略中很有用,可以把准备工作和清理工作与核心交易逻辑分开,代码更清晰。
初始化与终止
策略类作为 Python 类,也支持构造函数和资源清理。
init() 初始化
def __init__(self):
super().__init__()
print('策略类初始化完成')
# 这里可以加载模型、初始化变量等
注意:必须首先调用 super().__init__(),否则 Jesse 会报错。这个方法在整个回测或实盘过程中只执行一次,适合放一些只需要初始化一次的操作,比如加载机器学习模型、计算长期指标等。
terminate() 终止处理
当策略即将结束时,Jesse 会调用 terminate() 方法。这适合保存数据、记录日志等清理工作:
def terminate(self):
self.log('执行即将终止...')
# 保存训练好的模型
# 导出统计结果
还有一个 before_terminate() 方法,在 terminate() 之前调用,区别是这个方法里还可以提交订单,比如想在策略停止前平掉部分仓位,可以在这里操作。
实战建议
把这些方法组合起来,就能构建完整的交易策略。建议从简单开始:
- 先在
should_long()里写一两个清晰的入场条件 - 在
go_long()里设置固定的止损止盈 - 跑回测看看效果
- 逐步加入
should_cancel_entry()处理挂单 - 需要动态调整时,再研究
update_position()
记住,策略不是越复杂越好。把核心逻辑写清楚,比堆砌几十个指标更有价值。Jesse 的 API 设计得很克制,每个方法职责单一,这迫使我们把策略结构梳理得更清晰。
下一章我们会把这些交易规则放到回测引擎里,看看它们在历史数据上的表现如何。回测不仅能验证策略逻辑是否正确,还能暴露很多意想不到的问题。