准备工作
工欲善其事,必先利其器,我们前面铺垫了这么多,终于来到编写策略代码的环节了,但是别急,我们还需要一个趁手的代码编辑器,编辑器可以帮我们检查在代码编写阶段出现的 BUG,可以帮我们统一代码的风格,还可以帮我们快速补全代码,让写代码效率更上一层楼。
我们推荐的编辑器是:Visual Studio Code 点击下载 (opens in a new tab)
安装完成之后,我们还需要下载以下扩展:
-
简体中文 点击下载 (opens in a new tab) (汉化编辑器)
-
Python 点击下载 (opens in a new tab)
-
Pylance 点击下载 (opens in a new tab)
依次点击下载链接后,会打开对应的下载页面,我们看到「右侧侧边栏」,找到 「Resources」,点击 「Download Extension」,会开始下载扩展,扩展的文件后缀名是 .vsix
当我们把扩展下载好之后,会得到四个扩展文件,现在打开编辑器,点击左侧侧边栏「扩展」按钮,再点击「三个点」按钮,在菜单中选择「Install From VISX...」,中文则是「从 VISX 中安装...」,选择我们刚下载的四个扩展文件后点击「Install」,等待安装完成即可
注意:如果你打不开下载页面,或者觉得操作困难,可以跳过安装扩展的步骤,这些扩展只是让编辑器更好的支持 Python 语言,但不是必须的。
编写策略
我们知道如何加载策略后,也大体了解了一下运行原理,编辑器也准备好了,现在终于开始编写我们第一个策略了。
工作目录
我们首先需要用编辑器打开工作目录:
点击「策略管理」按钮后点击「文件夹」按钮打开策略路径,此时是位于 self_strategy
目录(策略路径)下,我们需要往上两级,按住 Alt + ↑上方向键 两次,进入以 InfiniTrader
开头的目录,比如:InfiniTrader_Simulation
,此时进入了无限易的主目录,我们可以在这个目录下看到 pyStrategy
文件夹,这是 PythonGO 整体的工作目录,右键该目录:
-
如果是 Windows 10:在弹出的菜单中点击「通过 Code 打开」
-
如果是 Windows 11:在弹出的菜单中点击「显示更多选项」,再点击「通过 Code 打开」
-
如果没有「通过 Code 打开」,则自行打开编辑器后,把
pyStrategy
文件夹拖入到编辑器中
之后的开发会一直在该工作目录中进行。
创建策略
在左侧文件栏中选中 self_strategy
文件夹,再点击「新建文件」按钮,在出现的输入框中输入策略文件名,这里我们取名 DemoTest.py
(注意不要和自带的策略重名)
当然你也可以直接在 self_strategy
文件夹直接右键新建 .py
策略文件
编写代码
现在我们来写一个最简单的一旦运行策略就按照无限易 PythonGO 窗口的参数下单的策略
定义参数映射
参数映射,是指可以通过无限易 PythonGO 窗口参数栏对策略的参数进行修改的映射操作
现阶段,我们的参数映射都是通过 pydantic
这个强大的数据验证库,它可以根据我们定义的类型注释严格控制数据类型,在我们写代码的过程中就可以感受到该库的强大之处。
我们通过一个「参数映射模型」即可完成映射
from typing import Literal
# 从 base 库中导入定义参数和状态映射模型必须的三个方法
from pythongo.base import BaseParams, BaseState, Field
class Params(BaseParams):
"""参数映射模型"""
exchange: str = Field(default="", title="交易所代码")
instrument_id: str = Field(default="", title="合约代码")
order_price: int | float = Field(default=0, title="报单价格")
order_volume: int = Field(default=1, title="报单手数")
order_direction: Literal["buy", "sell"] = Field(default="buy", title="报单方向")
上面的代码表示:
-
我们自己的参数映射模型叫做
Params
,继承自BaseParams
-
我们想从无限易 PythonGO 窗口参数栏传入定义的这 5 个值(或者说修改 5 个值)
-
类型注释在参数映射模型中是一定要写的,所有的值类型都会按照定义的类型注释被解析和自动转换,其中
Literal
的作用是表示这个变量只允许使用定义好的字面值,当前代码表示order_direction
只允许使用buy
或者sell
这两个字符串 -
Field
用来自定义元数据并将其添加到参数映射模型的字段中 -
default
(必填)定义这个参数默认值,相当于self.exchange = ""
-
title
(必填)定义这个参数的中文名,会在无限易 PythonGO 窗口显示
效果如图所示:
定义状态映射
状态映射,是指可以通过无限易 PythonGO 窗口状态栏对策略的参数或变量进行实时查看的映射操作(如没该需求可以不定义)
定义方法和参数映射是一样的,通过一个「状态映射模型」即可完成映射
# 从 base 库中导入定义参数和状态映射模型必须的三个方法
from pythongo.base import BaseParams, BaseState, Field
class State(BaseState):
"""状态映射模型"""
order_id: int | None = Field(default=None, title="报单编号")
上面的代码表示:
-
我们自己的状态映射模型叫做
State
,继承自BaseState
-
我们想从无限易 PythonGO 窗口状态栏查看报单编号(
order_id
)的值(注意:这个报单编号需要我们自己赋值,和报单成功后的返回的报单编号没有主动映射关系,仅仅是同名,你也可以定义为任意名称) -
其他和参数映射说明一样
效果如图所示:
定义策略类
当我们把映射模型定义好之后,就要开始写策略类了
根据规范 策略规范 - 导入父类 导入我们需要的父类基础策略模版 BaseStrategy
,然后基于我们之前学过的知识 运行原理 - 加载策略,我们知道了「策略类名」要和「策略文件名」保持一致:
# 从 base 库中导入基础策略模版 BaseStrategy
from pythongo.base import BaseParams, BaseState, BaseStrategy, Field
class DemoTest(BaseStrategy):
"""我的第一个策略"""
...
根据 PythonGO 代码规范,我们还需要给策略写一个函数文档字符串,来告诉无限易这个策略的描述
效果如图所示:
实例化映射模型
上面定义好了两个映射模型,也定义好了策略类,现在我们把映射模型实例化,作为策略的实例属性,这样才可以在策略中获取到定义的映射数据
from typing import Literal
from pythongo.base import BaseParams, BaseState, BaseStrategy, Field
class Params(BaseParams):
"""参数映射模型"""
exchange: str = Field(default="", title="交易所代码")
instrument_id: str = Field(default="", title="合约代码")
order_price: int | float = Field(default=0, title="报单价格")
order_volume: int = Field(default=1, title="报单手数")
order_direction: Literal["buy", "sell"] = Field(default="buy", title="报单方向")
class State(BaseState):
"""状态映射模型"""
order_id: int | None = Field(default=None, title="报单编号")
class DemoTest(BaseStrategy):
"""我的第一个策略"""
def __init__(self) -> None:
super().__init__()
self.params_map = Params()
self.state_map = State()
定义回调
在 PythonGO 的父类基础策略模版中,以 on
开头的函数都是回调函数
在 运行原理 中,我们大致的了解了回调函数,那在当前的示例中我们只需要用到三个回调函数,根据 策略规范 - 回调函数 按顺序定义如下:
on_start()
我们的策略的逻辑是一旦运行就按照无限易 PythonGO 窗口的参数下单,那下单函数就应该写在本回调中
我们使用 报单函数 - send_order 来进行报单操作,对应的报单数据我们从参数 self.params_map
中获取,得到的报单编号,我们赋值给状态映射模型中的 order_id
,然后需要调用 update_status_bar
函数才可以刷新状态栏的值:
def on_start(self) -> None:
super().on_start()
self.state_map.order_id = self.send_order(
exchange=self.params_map.exchange,
instrument_id=self.params_map.instrument_id,
volume=self.params_map.order_volume,
price=self.params_map.order_price,
order_direction=self.params_map.order_direction
)
self.update_status_bar()
on_stop()
在暂停回调中,我们输出一句自定义信息
def on_stop(self) -> None:
super().on_stop()
self.output("我的第一个策略暂停了")
on_order()
在报单变化回调中,我们只需要输出看看报单后回调收到的报单数据(OrderData
)信息。要注意的是,参数 order
是一个实例对象,不是字典对象,所以他的取值是用 .
取值的,比如 order.exchange
,但是我们直接输出 order
,显示的内容确是字典,这是因为我们对这个数据对象做了处理,如果没做处理的话,看到的是内存地址 <order.OrderData at 0x96b06c0>
from pythongo.classdef import OrderData
def on_order(self, order: OrderData) -> None:
super().on_order(order)
self.output("报单信息:", order)
完整代码
是的,一个简单的策略就完成了,代码不多,希望你可以仔细阅读这些代码
现在这个策略会根据你在无限易 PythonGO 窗口填写的参数信息(交易所代码,合约代码,报单价格,报单手数,报单方向)来进行报单,最终的代码是这样:
from typing import Literal
from pythongo.base import BaseParams, BaseState, BaseStrategy, Field
from pythongo.classdef import OrderData
class Params(BaseParams):
"""参数映射模型"""
exchange: str = Field(default="", title="交易所代码")
instrument_id: str = Field(default="", title="合约代码")
order_price: int | float = Field(default=0, title="报单价格")
order_volume: int = Field(default=1, title="报单手数")
order_direction: Literal["buy", "sell"] = Field(default="buy", title="报单方向")
class State(BaseState):
"""状态映射模型"""
order_id: int | None = Field(default=None, title="报单编号")
class DemoTest(BaseStrategy):
"""我的第一个策略"""
def __init__(self) -> None:
super().__init__()
self.params_map = Params()
self.state_map = State()
def on_order(self, order: OrderData) -> None:
super().on_order(order)
self.output("报单信息:", order)
def on_start(self) -> None:
super().on_start()
self.state_map.order_id = self.send_order(
exchange=self.params_map.exchange,
instrument_id=self.params_map.instrument_id,
volume=self.params_map.order_volume,
price=self.params_map.order_price,
order_direction=self.params_map.order_direction
)
self.update_status_bar()
def on_stop(self) -> None:
super().on_stop()
self.output("我的第一个策略暂停了")
然后按照 操作入门 来加载我们刚写的第一个策略即可,快去试一试!