# 概述

  使用程序化交易,使用者仅仅具备编写能力是不够的,当排查方向指向策略内部逻辑时,使用者除了凭借经验以外,还应该灵活掌握调试技巧,才能够更加顺畅的解决量化交易问题。调试涵盖以下四种方法:

图表调试

基于K线图光标定位调试图表策略

监控输出

输出策略执行时全部变量的结果

调试函数

输出策略执行时指定变量的结果

调试工具

可调试控制语句内部执行过程

- name: 图表调试
  desc: 基于K线图光标定位调试图表策略
  bgColor: '#DFEEE7'
  textColor: '#242A38'
- name: 监控输出
  desc: 输出策略执行时全部变量的结果
  bgColor: '#F0DFB1'
  textColor: '#242A38'
- name: 调试函数
  desc: 输出策略执行时指定变量的结果
  bgColor: '#CBEAFA'
  textColor: '#242A38'
- name: 调试工具
  desc: 可调试控制语句内部执行过程
  bgColor: '#F6E9E8'
  textColor: '#242A38'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

调试技巧

  1. 反向定位:根据执行结果或者日志推导相关代码位置,然后自下而上寻找触发条件关联的因子结果。
  2. 就近添加:添加中间变量输出时,变量应该写在待排查语句的前后位置,目的是避免因子在其它位置发生修改,产生误导。
  3. 由粗到细:排查因子结果由粗到细,例如从组合条件-->各个子条件-->参与条件判断的因子。

# 图表调试

  图表调试是一种简单高效的调试方法,本质上是通过光标定位到指定K线位置,根据查看到的变量返回结果,判断当前公式的执行逻辑或者状态。

示例:如下图所示中,均线多头金死叉策略,为什么只发生了一次开仓动作,其余金叉条件成立时为什么没有触发动作?

  1. 由图可以判定此现象大概率与交易条件有关,因此在开平仓语句条件处增加变量输出条件结果。相关示例代码见下方代码段;
编程技巧
  1. 多使用中间变量,避免出现复杂表达式,既能提高代码可读性,也能有助于进行调试。
  2. 若显示的变量会压缩K线图,可以添加LINETHICK0控制符;或者改为:=定义中间变量。
  3. 变量输出不支持字符串数据,但是可以通过日志函数等方法打印输出。
  4. 单行变量无法全部显示时、或者变量都是通过:=定义不显示时,可以通过键盘
    Shift
    +
    Q
    查看。
    如下图所示:

通过调试结果与代码相互验证可知:【失败原因:多头持仓为正数,所以holding<0的条件恒不成立。】

标记1:此位置死叉条件cond3成立、但是此时的holding=0(无持仓),平仓条件不成立。
标记2:此位置金叉条件cond1成立、此时的holding=0,开仓条件成立。
标记3:此位置死叉条件cond3成立、holidng=1(有持仓),但是平仓失败。

# 监控输出

  监控输出功能将程序化执行过程中使用的策略变量进行记录保存。通过记录关键点的运行结果可以完整的分析策略每次执行过程中的逻辑。此功能对于初学者较为友好,只需增加变量保存指定位置的执行结果即可。



# 技巧与注意事项

1. 不支持字符串变量输出

  • 字符串的输出,可以使用日志函数处理。(见下节内容)
//普通变量
MA1:=MA(CLOSE,5);

//字符串变量
品种:STKLABEL;
1
2
3
4
5

日志分析:

//默认输出本地计算机时间、K线时间、最新价,三个字段,之后是公式中的变量
//下标[1]处由于是字符串变量,其输出结果被固定数值代替。没有分析意义。
2024-03-28 13:44:04.689 Time:2024.03.28 13:44:00 Close:4256.2 MA1:4257.560 品种:1000000.000 [1]
2024-03-28 13:45:03.687 Time:2024.03.28 13:45:00 Close:4251.8 MA1:4254.520 品种:1000000.000


2. 只输出各个变量执行过程中最后的结果

  • 若希望跟踪变量在策略中的变化过程,可以在跟踪变量附件增加中间变量。(示例见标签页【变量细节跟踪】
  • 策略中关键条件使用中间变量保存,能够提高监控输出日志的精细度。

3. 避免在条件控制体内声明变量

  • 在条件控制语句内声明的序列变量,在条件不成立时,会沿用最近一次成立时的有效值。
  • 在代码的条件控制之外声明并初始化变量,有助于提升代码逻辑的健壮性。(见标签页[优化后代码])





# 调试函数

当策略逻辑复杂或者变量众多时,使用监控输出功能得到日志会过于臃肿,反而不利于分析定位问题。因此我们可以通过在策略代码的特定关联位置中增加调试函数,已达到快速灵活处理问题。调试函数DEBUGFILE用法如下:

debugfile(文件保存路径,写入的文本内容,变量)

使用调试函数遵循以下技巧:

  1. 反向定位:根据执行结果或者日志推导相关代码位置,然后自下而上寻找触发条件关联的因子结果。
  2. 就近添加:添加中间变量输出时,变量应该写在待排查语句的前后位置,目的是避免因子在其它位置发生修改,产生误导。
  3. 由粗到细:排查因子结果由粗到细,例如从组合条件-->各个子条件-->参与条件判断的因子。

示例:记录交易策略运行时的条件结果

使用dubugfile函数遵循就近添加、由粗到细的处理原则。完整的调试示例如下:

//中间变量
ma5:ma(close,5);
ma20:ma(close,20);
 
//开仓条件
con1:=cross(ma5,ma20);  //金叉
con2:=cross(ma20,ma5);  //死叉

//就近原则添加调试函数(即在con1 and tholding=0判断条件前后均可,可视情况灵活选择)
//增加ISLASTBAR条件判断可以减少debugfile函数的内部判断,从而增加运行效率。
if ISLASTBAR=1 then begin
    debugfile('D:\T_' & stklabel & '.txt',stklabel &'   [开仓条件='& numtostr(con1 and tholding=0,0)  &']'
        & '   各项条件:[开多条件1=' & numtostr(con1,0)&'   持仓手数='& numtostr(tholding,0)&']',0);
end
if con1 and tholding=0 then
    begin
         tbuy(1, 1, mkt);
   end


if ISLASTBAR=1 then begin
    debugfile('D:\T_' & stklabel & '.txt',stklabel &'   [平仓条件='& numtostr(con1 and tholding=0,0)  &']'
        & '   各项条件:[平仓条件1='& numtostr(con2,0)&'   持仓手数=' & numtostr(tholding,0) & ']',0);
end
if con2 and tholding=0 then
    begin
         tsell(1, 1, mkt);
    end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

第9行解析

  • 参数1:'D:\T_' & 'stklabel' & '.txt'
    • 日志写入的路径,此处的结构:'D:\T_' + 品种代码 + 文件名后缀。此处的品种代码是字符串变量,多品种时可以实现分别记录,避免混淆。
  • 参数2:
    'stklabel' & ' [开仓条件=' & numtostr(con1 and tholding=0,0) & ']' & ' 各项条件:[开多条件1=' & numtostr(con1,0) & ' 持仓手数=' & numtostr(tholding,0) & ']'
    • 使用numtostr将开平仓条件、持仓转换成字符串后。与其他字符串拼接。以增加整个日志文件的可读性。
  • 参数3:0 。
    • 任意数字,在此处没有实质性作用,仅是占位使用。

debugfile日志输出如下:

日志文件名:T_P00.txt

       1                    2         3                       4
----------------------------------------------------------------------------------
2022-04-08 10:06:00.284    P00   [开仓条件=0]   各项条件:[开多条件1=0   持仓手数=0]
2022-04-08 10:06:00.286    P00   [平仓条件=0]   各项条件:[平仓条件1=0   持仓手数=0]
2022-04-08 10:06:01.092    P00   [开仓条件=0]   各项条件:[开多条件1=0   持仓手数=0]
2022-04-08 10:06:01.092    P00   [平仓条件=0]   各项条件:[平仓条件1=0   持仓手数=0]
2022-04-08 10:06:01.096    P00   [开仓条件=0]   各项条件:[开多条件1=0   持仓手数=0]
2022-04-08 10:06:01.097    P00   [平仓条件=0]   各项条件:[平仓条件1=0   持仓手数=0]
......
1
2
3
4
5
6
7
8
9





# 调试工具

  在众多量化软件中,金字塔是少有提供可视化调试器的软件,公式调试器可以精确跟踪IF条件、FOR循环等控制语句的内部执行流程。也可以帮助资深用户深入分析全局变量、运行模式的等机制上的区别。

# 断点调试

断点调试可以在代码行的任意位置设定一个断点,当程序执行到该断点行后会自动停下,并告知当前最新的变量执行结果。设置方法:

  1. 方法1:在指定代码行号位置处双击添加断点
  2. 方法2:光标定位到指定代码行按快捷键
    F9

   设置断点的公式会在被执行的情况下遇到断点后自动打开公式编辑器并启动调试状态,包括在图表和后台的程序化交易过程中。

# 单步跟踪

   单步跟踪的作用是执行处于暂停等待状态下的公式,但每次只执行一行,这样我们就可以精细的看到公式行具体的执行过程与直接结果的变化情况。适用方法如下图:

  1. 数据窗口显示当前所调试品种的主数据内容,介于序列模式和逐K线模式的不同,在逐K线模式下,会将当前的执行K线周期序号行置红色显示。
  2. 变量窗口显示当前断点行或单步跟踪行的变量执行变化情况,介于序列与单值变量的不同,
    • 序列变量在点击“+”号会展开该变量的数列数组,而单值变量展开时只有一行。
    • 逐K线模式下只有单值数据这一种,返回当前执行的结果。如上图所示。

# 对比总结

功能 功能特点 功能局限性
图表调试      1. 可视化操作,符合技术分析使用习惯
2. 支持基于历史数据运算调试
1. 此方法反馈的都是历史数据最后一次的执行结果,无法反馈出K线当时执行时的结果。
监控输出 1. 设置简便,对调试要求不高
2. 以日志形式,输出全部变量本次执行的最后结果
3. 适用于简单的图表、后台程序化策略。
1.只支持实时输出,不支持回溯历史数据
2.日志冗余度受策略执行影响
3.日志量过大会增加日志分析难度
调试函数 1. 必须具备代码分析和调试定位能力。
2. 以日志形式,输出指定的变量、指定代码位置的实时执行结果
3. 适合精确跟踪控制语句内部执行流程
4. 适用于任意图表、后台程序化策略。
1.只支持实时输出,不支持回溯历史数据
调试工具 1. 适合精确跟踪控制语句内部执行流程 1. 调试效率偏低。
2. 支持回溯历史数据,不能连续反馈实时的执行结果