UI与扩展

策略光有逻辑不够,还得能「看见」。PythonGo 提供了内置 K 线图组件和定时器工具,让策略既能画指标线、又能做定时任务。本章讲 UI 组件怎么接、指标怎么定义、定时器怎么用。

一、带 UI 的策略基类

需要 K 线图的策略,要从 pythongo.ui(而非 pythongo.base)导入 BaseStrategy

from pythongo.ui import BaseStrategy   # 注意是 ui 模块


class DemoKLine(BaseStrategy):
    """带 K 线图的策略"""
    ...

pythongo.ui.BaseStrategy 继承自 pythongo.base.BaseStrategy,额外内置了 K 线组件 self.widget,并自动处理策略启动时显示窗口、暂停时隐藏窗口的逻辑——你不用手动管 UI 生命周期。

只有不需要 K 线图的策略才用 pythongo.base.BaseStrategy需要画图就用 pythongo.ui。回测时 UI 相关代码会被自动跳过。

二、定义主图与副图指标

指标通过 @property 装饰器定义成属性,框架每次取值都拿最新数据,无需手动赋值:

class DemoKLine(BaseStrategy):
    def __init__(self) -> None:
        super().__init__()
        self.params_map = Params()
        self.fast_ma = 0.0
        self.slow_ma = 0.0

    @property
    def main_indicator_data(self) -> dict[str, float]:
        """主图指标——画在 K 线主图上"""
        return {
            f"MA{self.params_map.fast_period}": self.fast_ma,
            f"MA{self.params_map.slow_period}": self.slow_ma,
        }

    @property
    def sub_indicator_data(self) -> dict[str, float]:
        """副图指标——画在 K 线副图上(如 KDJ)"""
        return {"K": self.k, "D": self.d, "J": self.j}

要点:

  • 属性名固定:主图用 main_indicator_data,副图用 sub_indicator_data
  • 返回 dict[str, float],键是指标名(显示在图例上),值是数值。
  • f-string 配合参数,能让指标名随周期变化(如 MA5 / MA20),改周期时图例自动更新。
  • 不能直接给这两个属性赋值,必须自己定义同名 @property

三、把数据推给 K 线图

算完指标后,用 self.widget.recv_kline() 把 K 线和指标一起更新到图上:

from pythongo.classdef import KLineData


def callback(self, kline: KLineData) -> None:
    """K 线成型回调"""
    self.calc_indicator()
    self.widget.recv_kline({
        "kline": kline,              # K 线数据(必有)
        "signal_price": 0.0,         # 可选:买卖信号标记价格
        **self.main_indicator_data   # 解包主图指标
    })

def real_time_callback(self, kline: KLineData) -> None:
    """实时回调(每个 tick)——实时更新指标和图"""
    self.calc_indicator()
    self.widget.recv_kline({"kline": kline, **self.main_indicator_data})

recv_kline 接收一个字典,键包括:kline(K 线对象)、signal_price(信号价格,会在图上标箭头)、以及你解包进去的指标名。

四、定时器 Scheduler

很多策略需要定时任务(开盘下单、收盘平仓、每隔 N 秒检查)。PythonGo 封装了 apschedulerScheduler

from datetime import datetime
from pythongo.utils import Scheduler


class MyStrategy(BaseStrategy):
    def __init__(self) -> None:
        super().__init__()
        self.scheduler = Scheduler()    # 传入名称即为单例模式
        self.scheduler.start()

    def on_start(self) -> None:
        super().on_start()
        # 每隔 30 秒执行一次
        self.scheduler.add_job(
            func=self.do_something,
            trigger="interval",
            id="my_job",
            seconds=30,
            next_run_time=datetime.now()   # 立即执行第一次
        )
        # 每天 14:55 执行
        self.scheduler.add_job(
            func=self.close_position,
            trigger="cron",
            id="close_job",
            hour=14, minute=55
        )

    def on_stop(self) -> None:
        super().on_stop()
        self.scheduler.remove_job("my_job")

    def do_something(self) -> None:
        self.output("定时任务触发")

    def close_position(self) -> None:
        self.output("收盘平仓")

三种触发器:

触发器含义关键参数
date某个确定时间点运行一次run_date
interval固定间隔周期运行seconds / minutes / hours
cron按日历周期运行(类似 cron)hour / minute / day_of_week

单例模式Scheduler("my_name") 传入名称后,同名的实例返回同一内存地址,可在多个策略间共享同一个定时器。单例下 stop() 只移除所有任务,不停止定时器本身。定时器停止后无法重启,需重新实例化。

五、日志与调试

  • self.output(*msg):输出到 PythonGO 控制台,支持任意类型和参数。
  • CustomLogHandler:第三方库的日志(如它自带 logging)默认看不到,用这个 Handler 桥接到 PythonGO 日志系统。

调试时善用 self.output 打印关键变量,配合 state_map 在界面状态栏实时观察数值变化。

小结

pythongo.ui.BaseStrategy 获得内置 K 线组件;main_indicator_data / sub_indicator_data 定义主副图指标; widget.recv_kline() 更新图表;Scheduler 做定时任务。到此,PythonGo 从环境、骨架、行情交易到 UI 扩展形成 完整闭环——左侧导航可随时回查任一章节。