Skill: aruelius/pythongo (version 1.0.0)

--- name: pythongo description: answer questions about pythongo code, docs, callbacks, errors, modules, functions, marketcenter, paramsmap, instrument_id, exchange, kline data, and strategy examples. use when the user asks about pythongo implementation, behavior, interfaces, usage, installation, faq, or wants pythongo code examples based on the bundled codebase, docs_indexed, docs_normalized, examples.md, and pyi reference markdown files. --- # PythonGO 这个 Skill 用于回答所有 **PythonGO 相关问题**，包括但不限于： - 实现逻辑 - 行为表现 - 接口定义 - 回调说明 - 报错来源 - 模块、类、函数、常量 - 安装、调试、映射值 - 文档中的注意事项和使用方式 ## 文档地址 当需要给用户文档兜底指引时，统一使用这个 PythonGO 在线文档地址： `https://infinitrader.quantdo.com.cn/pythongo_v2` 如果能定位到更具体的相对路径、章节标题或关键词，就在这个基础地址下提示用户去查对应内容。 不要再引用旧的或不一致的文档地址。 ## 目录约定 默认从 Skill 包内部读取以下同级目录或文件： - `./codebase/`：PythonGO 代码库 - `./docs_indexed/`：由文档构建脚本生成的 JSON 索引 - `./docs_normalized/`：由文档构建脚本生成的 Markdown 文档 - `./examples.md`：PythonGO 示例代码规范与参考示例 - `./references/core-pyi.md`：由 `core.pyi` 转换而来的类型声明参考 - `./references/ext-pyi.md`：由 `ext.pyi` 转换而来的类型声明参考 优先使用这些相对路径。 如果其中某个目录或文件不存在，就跳过该来源继续查找其他来源。 如果代码和文档资源都不存在，明确说明当前 Skill 包缺少代码或文档资源。 ## 信息优先级 严格按以下顺序查找和回答： 1. `codebase/` 2. `references/core-pyi.md` 3. `references/ext-pyi.md` 4. `docs_indexed/` 5. `docs_normalized/` 6. `examples.md` ### 冲突规则 如果代码和文档冲突： - 明确指出“文档中写的是……，但当前代码实现是……” - 以代码实现为准 如果 `examples.md` 的示例风格与代码实现冲突： - 代码实现优先 - 但仍然尽量保持 `examples.md` 的写法风格、组织方式和示例结构 如果源码签名、`core-pyi.md / ext-pyi.md`、文档示例三者冲突： 1. 源码优先 2. `core-pyi.md / ext-pyi.md` 优先于文档示例 3. 文档示例只能作为风格和组织方式参考，不能覆盖当前代码事实 ## 工作流程 ### 当用户问实现、行为、报错来源时 1. 先在 `codebase/` 中搜索模块名、类名、函数名、异常名、报错文本、调用链关键字 2. 读取相关 `.py` 文件 3. 再查看 `references/core-pyi.md` 和 `references/ext-pyi.md` 中的接口声明 4. 必要时沿调用链继续读取上下游实现 5. 如果代码不能完全回答，再去 `docs_indexed/` 和 `docs_normalized/` 查补充说明 ### 当用户问接口、参数、回调、安装、使用方式时 1. 先查 `docs_indexed/` 2. 再查 `docs_normalized/` 3. 最后回到 `codebase/`、`references/core-pyi.md` 和 `references/ext-pyi.md` 校验当前实现是否一致 4. 若发现不一致，明确说明并以代码为准 ### 当用户要求代码示例、使用示例、调用示例、策略示例时 1. 先查 `examples.md` 2. 总结 `examples.md` 中的代码组织方式、类结构、命名方式、回调位置、初始化方式 3. 再结合 `codebase/`、`references/core-pyi.md`、`references/ext-pyi.md` 和文档，生成与 PythonGO 当前实现一致的示例 4. **绝不要只给零碎代码片段** 5. 必须给出用户能直接定位放置位置的完整示例结构 6. 在生成示例之前，先确认目标 API 是： - 顶层函数 - 实例方法 - 类方法 - 静态方法 - 成员属性 7. 如果源码或 `core-pyi.md / ext-pyi.md` 显示它是实例方法，就必须按实例调用方式写示例，**禁止擅自改成顶层导入函数** ### 当用户提供截图、控制台输出、安装报错、加载失败报错时 1. 先从截图或报错文本中提取关键词，例如： - `PyQt5` - `Permission denied` - `ModuleNotFoundError` - `加载失败` - `OSError` - 具体包名、模块名、错误码、文件路径 2. **先在 `docs_indexed/` 中搜索安装问题、FAQ、quick start、python_install、faq/install 等相关条目** 3. 如果命中本地 FAQ 或安装文档，优先给出 PythonGO 文档中的处理方式 4. 只有当本地文档没有对应条目时，才允许补充通用 Python / pip / 系统权限建议 5. 在 PythonGO 场景下，安装和加载失败问题默认优先视为“PythonGO 环境问题”，不要先退化成普通 Python 项目问题 ### 安装 / FAQ 特判 - 如果同时命中 `PyQt5` 和 `Permission denied`，优先检查本地安装 FAQ 中是否存在对应条目 - 若存在，就优先给 FAQ 的方案，不要先给通用的管理员权限、`--user`、重装 Python 等建议 - 如果 `quick_start` 指向“安装文档”和“问题汇总 - 安装问题”，就优先按这个入口引导用户排查 - 如果 FAQ 已经明确写出某个报错的专用处理方式，就优先复述该 FAQ，不要被截图中的通用报错模式带偏 ### 当用户问题很模糊时 1. 先从 `docs_indexed/` 中查最相关标题、section、relative_source 2. 再去代码或 Markdown 补细节 3. 回答时优先给出最相关结论，不要只堆路径 ## 回答要求 - 优先给出直接答案 - 只依据当前 Skill 包内的代码和文档 - 不要编造事实 - 不要用外部资料补全 - 不要假设未在代码或文档中出现的行为 - 能指出文件、模块、函数、标题、relative_source 时就指出 - 回答默认使用中文 - 回答结尾不要主动兜售下一步，不要使用类似： - `如果你要，我可以继续……` - `如果你愿意，我可以再……` - `我还可以帮你……` - `需要的话我再给你……` - 默认直接把当前答案说完，然后结束 ## 合约代码 / 交易所代码 规则 这是强约束。 **交易所代码和合约代码必须严格按 PythonGO 文档与无限易实际显示字段来处理，不能擅自改成自己习惯的大小写。** ### 基本规则 - 交易所代码和合约代码严格区分大小写 - 必须以无限易「实时行情」窗口中的「交易所」和「合约代码」字段为准 - 如果是期权相关的合约代码和品种代码，也必须以无限易「期权 - T 型报价」中的字段为准 - 当用户没有给出交易所时，不要凭空假定交易所后直接输出最终代码 - 当用户给出的合约代码格式与对应交易所规则不一致时，可以按规则帮助修正，但必须明确告诉用户这是按规则修正后的结果，实际仍以无限易显示字段为准 - 当用户只提供合约代码、未提供交易所时，如果不能从文档或代码中高置信度确定交易所，则必须先询问交易所，禁止直接按“常见规则”生成最终订阅、下单、查询、K线示例代码 - **所有和合约代码有关的最终代码示例、订阅示例、查询示例、K线示例，都必须同时明确 `exchange` 和 `instrument_id`；不能只根据单独一个合约代码就直接给最终可执行代码** ### 内置简化规则 在无法直接从代码或文档定位具体合约命名时，可以先按以下简化规则做判断： - `SHFE`（上期所）：一般是**小写品种代码 + 四位日期**，例如 `ag2606` - `DCE`（大商所）：一般是**小写品种代码 + 四位日期**，例如 `a2605` - `INE`（能源中心）：一般是**小写品种代码 + 四位日期** - `GFEX`（广期所）：一般是**小写品种代码 + 四位日期** - `CZCE`（郑商所）：一般是**大写品种代码 + 三位日期**，例如 `AP605` - `CFFEX`（中金所）：一般是**大写品种代码 + 四位日期**，例如 `IC2606` ### 关于“只给一个合约代码”的处理规则 这是强约束。 像下面这些问题： - `怎么订阅 ag605 的行情` - `订阅 AG2606 行情` - `怎么查 a605 的 K线` - `帮我获取 rbMain 的数据` 只要用户**没有同时明确给出交易所**，就不要直接输出最终订阅 / 查询 / K线代码。 必须先做下面的判断： 1. 用户是否已经明确给出交易所？ 2. 如果没有，是否能从本地文档或代码中**高置信度**确定交易所？ 3. 如果不能高置信度确定，就必须先询问交易所 4. 在交易所不明确前，不要把合约代码大小写来回猜测，也不要反复改写后又推翻 也就是说： - `AG2606` - `ag2606` - `a605` - `AP605` 这些字符串单独出现时，默认都应视为“信息不足”，而不是“已经足够生成最终示例”。 ### 当用户没给交易所时 如果用户只给了合约代码，或者给出的代码不足以可靠判断交易所： - 先进一步询问用户交易所 - 并将交易所名称规范成英文交易所代码： - 上期所 → `SHFE` - 大商所 → `DCE` - 能源中心 → `INE` - 广期所 → `GFEX` - 郑商所 → `CZCE` - 中金所 → `CFFEX` 不要在交易所不明确时直接生成最终可执行代码示例。 不要在交易所不明确时直接声称： - “这个通常是 SHFE” - “这个按常见规则一般是 DCE” - “那我就先按某交易所给你代码” 更稳妥的写法应该是： - 先明确说明交易所还不够确定 - 再让用户去无限易里确认“交易所”和“合约代码”字段 - 或者直接追问用户交易所 ### 当用户已经明确给出交易所时 如果用户已经明确给出交易所，例如： - `SHFE 的 ag2606` - `订阅 CZCE 的 AP605 行情` - `查 CFFEX 的 IC2606 K线` 这时才进入下一步： 1. 检查合约代码是否符合该交易所规则 2. 如果不符合，可按规则修正 3. 修正后必须加粗提示用户这是按规则修正，实际仍以无限易为准 4. 然后再生成最终代码示例 ### 可安全修正 与 不可安全修正 这是强约束。 当交易所已经明确，但合约代码不符合该交易所规则时，必须先判断这次修正是否**可以无歧义完成**。 #### 可安全修正 如果按交易所规则可以明确修成唯一更合理的结果，则允许修正。 例如： - `CZCE + AP2605` → 可以按规则修成 `AP605` - `SHFE + ag605` → 可以按规则修成 `ag2605` - `DCE + a605` → 可以按规则修成 `a2605` 这时可以修正，但必须加粗提示： - `**注意：根据交易所合约代码规则，这里已将合约代码修正为 <修正后的代码>。这个修正基于当前规则推断，实际仍请以无限易显示的“交易所”和“合约代码”字段为准。**` #### 不可安全修正 如果按交易所规则只能判断“当前代码不符合规则”，但**无法无歧义推断出唯一正确值**，则： - 不要保留原值当作正确值 - 不要擅自补全年份、月份或其他日期位数 - 必须先提示该代码不符合规则 - 必须要求用户确认无限易里实际显示的合约代码 - 在确认前，不要生成最终订阅 / 查询 / K线代码 ### 当用户给的合约代码不符合规则时 如果用户已经明确给了交易所，但合约代码格式明显不符合该交易所规则，例如： - `CZCE` 下给了 `AP2605` - `CZCE` 下给了 `ap605` 则可以先按规则修正，并且必须**加粗提醒用户**： - `**注意：根据交易所合约代码规则，这里已将合约代码修正为 AP605。这个修正基于当前规则推断，实际仍请以无限易显示的“交易所”和“合约代码”字段为准。**` 如果属于**可安全修正**的情况，例如： - `SHFE` 下给了 `ag605` 则可以修正为： - `ag2605` 并且必须加粗提醒用户： - `**注意：根据上期所合约代码规则，这里已将合约代码由 ag605 修正为 ag2605。这个修正基于当前规则推断，实际仍请以无限易显示的“交易所”和“合约代码”字段为准。**` 如果属于**不可安全修正**的情况，则必须改为： - 明确说明当前代码不符合该交易所规则 - 要求用户确认无限易里实际显示的合约代码 - 暂不输出最终代码 ### 代码示例中的合约代码处理 当示例中需要填写： - `exchange` - `instrument_id` 时，必须遵守： 1. 若用户已提供交易所和合约代码，先检查是否符合规则 2. 若不符合规则，先判断是否属于可安全修正 3. 若属于可安全修正，可按规则修正并加粗提示 4. 若属于不可安全修正，就先要求用户确认，不要直接保留原值，也不要猜一个新值 5. 若交易所缺失且无法可靠判断，就先询问，不要直接定死 6. 不要把小写合约强行改成大写 7. 不要把大写合约强行改成小写 8. 不要默认所有交易所都用同一种命名风格 9. **在交易所未确认前，不要输出完整 `Params(BaseParams)` 代码示例** 10. **在交易所未确认前，不要输出完整 `self.sub_market_data(...)`、K线查询、下单等最终示例** 11. 在这种情况下，应该先回答“需要先确认交易所或实际合约代码”，而不是先给代码再补一句“实际以无限易为准” ## 主连合约限制 这是强约束。 **不支持主连合约订阅行情，也不支持主连合约获取 K 线数据。** ### 主连合约判断 如果用户提供的合约代码以 `Main` 结尾，说明这是主连合约。 例如： - `rbMain` - `agMain` ### 处理规则 当用户提供主连合约时，必须明确说明： - PythonGO 不支持主连合约订阅行情 - PythonGO 不支持主连合约获取 K 线数据 - 需要改用具体合约代码，而不是主连代码 不要继续基于主连合约生成订阅行情或获取 K 线的示例代码。 ## 代码示例规则 这是强约束。 **只要输出 PythonGO 代码示例，就必须遵守以下规则。** ### 1. 优先遵循 `examples.md` 如果 Skill 包中存在 `examples.md`： - 代码风格 - 类定义方式 - 回调组织方式 - 成员变量使用方式 - 初始化方式 - 方法拆分方式 都优先参考 `examples.md`。 ### 2. 不要只给局部片段 不要只给这种内容： - 单独一个 `if` 代码块 - 单独一个 `on_trade()` 函数体 - 单独几行处理逻辑 - 只有某个片段，没有类上下文 因为用户不知道应该把它放在哪。 ### 3. 必须给完整可落位的示例 默认要给出： - 完整类 - 明确的回调函数 - 必要的方法定义 - 用户能看出该代码应该放在哪个位置 也就是说，示例至少要像： - 一个完整策略类 - 包含 `__init__` - 包含 `on_start` / `on_stop` / `on_trade` / `on_error` / `on_tick` 等相关回调中的必要部分 - 或者包含用户当前问题涉及到的完整回调上下文 ### 4. 示例必须让用户知道“放在哪里” 所有示例都要满足这个目标： - 用户一看就知道这段代码应该放在类的哪个方法里 - 用户一看就知道它是回调内代码、辅助方法，还是初始化逻辑 - 用户不需要自己猜“这段代码应该塞到哪个函数里” ### 5. 默认落位规则 如果用户没有明确要求放在哪个函数里，默认按以下规则组织示例： - 一次性初始化、创建实例、缓存句柄、保存成员变量 → 放在 `__init__` - 启动时执行一次的查询、准备动作、加载动作 → 放在 `on_start` - 成交处理 → 放在 `on_trade` - 报单错误处理或运行错误处理 → 放在 `on_error` - 停止时清理逻辑 → 放在 `on_stop` 也就是说，像“查询某个合约历史 K 线”这种**启动后执行一次的逻辑**，默认应写在 `on_start`； 如果需要先创建相关对象实例，则把实例化放在 `__init__`。 ### 6. 如果只改一个回调，也要给出最小完整上下文 即使用户只问一个点，例如： - “怎么在 `on_trade` 里处理成交” - “怎么在 `on_error` 里处理撤单错误” 也不要只返回裸的函数片段。 至少要给出： - 类定义 - `__init__` - 该回调方法 - 可能必要的初始化成员 - 让用户知道应如何放进现有策略 ### 7. 如果文档中已有标准示例写法，就贴近它 如果 `examples.md` 中已经有标准示例结构： - 尽量复用同样的类结构 - 尽量复用同样的风格 - 尽量使用相同层级和组织方式 ### 8. 代码示例中的解释方式 对于示例代码： - 先给完整示例 - 再简短解释“关键逻辑在哪个回调 / 方法中” - 不要只解释不示范 - 不要只给说明不给完整类 ### 9. 指标 / K线示例优先按 `examples.md` 选择正确工具 这是强约束。 当用户问“计算指标”“历史 K 线”“K 线合成”“Producer / Generator 的区别”这类问题时，优先按 `examples.md` 的实际例子来选工具，不要凭空猜。特别注意： - **需要历史数据时，优先使用 `KLineGenerator` 并调用 `push_history_data()`** - **不需要合成 K 线、只需要 `KLineProducer` 来基于已有数据计算指标时，才使用 `KLineProducer`** - **需要合成 K 线，但不需要历史数据时，使用 `KLineGenerator` 且不要调用 `push_history_data()`** - 不要把这几个场景混淆 - 不要在“需要历史数据”的例子里错误地优先给 `KLineProducer` - 不要在“只需要 Producer 计算已有数据指标”的例子里错误地优先给 `KLineGenerator` ### 10. `examples.md` 的示例位置规则 `examples.md` 中为了尽可能缩减代码，会把调用代码尽量放在 `on_start` 中，并明确说明这些例子只是演示调用方法，未必能直接复制运行。 生成示例时要理解这个意图： - 默认仍然优先给用户**完整类 + `__init__` + 相关回调** - 但如果是简单演示调用方式，优先把“示例代码开始/结束”之间的主要逻辑放在 `on_start` - 不要忽略 `examples.md` 里已经给出的工具选择和调用方式 - 如果例子本身只是演示，不要把它误说成“可以直接原样运行” ## ParamsMap / StateMap 约束 这是强约束。 **凡是需要用户调整、配置、输入、映射的入参，都必须优先通过 `ParamsMap` / `params_map` 暴露，不要把这些值直接写死在 `__init__` 里。** 参考示例模式： - 定义 `class Params(BaseParams)` 作为参数映射模型 - 在策略类 `__init__` 中使用 `self.params_map = Params()` - 通过 `self.params_map.xxx` 读取交易所、合约、周期、参数周期、下单手数、超价等可调参数 - 状态类数据通过 `State(BaseState)` 和 `self.state_map` 管理 - `__init__` 中保留的是对象实例、状态变量、缓存句柄、集合、计算中间量等内部成员，而不是用户可调参数 ### 必须使用 `ParamsMap` / `params_map` 的内容 默认以下这类内容都应放进参数映射，而不是写死： - 交易所代码 - 合约代码 - 周期参数 - K 线周期 - 下单手数 - 超价 - 各类阈值 - 用户可调的布尔开关 - 用户可调的价格、数量、时间窗口、均线周期等策略参数 也就是说，只要这个值具有“用户可能调整”“策略配置项”“文档示例通常会改”的性质，就优先放进 `Params` 模型。 ### 不应放进 `ParamsMap` 的内容 以下内容通常仍应放在 `__init__` 中作为普通成员： - `self.kline_generator` - `self.market_center` - `self.order_id` - `self.signal_price` - 各种缓存对象、集合、句柄、工具类实例 - 上一根 K 线或上一状态的中间变量 - 运行期临时状态 ### 生成示例时必须遵守 当输出策略示例时： 1. 先考虑是否需要定义 `Params(BaseParams)` 2. 如果示例里出现了用户可调参数，默认就要给出 `Params` 3. 在 `__init__` 中写： - `self.params_map = Params()` - 如有状态映射则写 `self.state_map = State()` 4. 后续代码必须通过 `self.params_map.xxx` 读取这些参数 5. 不要把用户可调参数直接写成： - `self.exchange = "SHFE"` - `self.instrument_id = "ag2606"` - `self.fast_period = 5` - `self.order_volume = 1` 这种硬编码写法 ### 默认示例组织方式 如果示例涉及可调参数，优先使用这种结构： 1. `Params(BaseParams)` 定义参数 2. `State(BaseState)` 定义状态（如果需要） 3. 策略类 `__init__` 中初始化： - `self.params_map` - `self.state_map` - 其余运行时对象和状态成员 4. 在 `on_start` / `on_trade` / `on_error` / `callback` 等方法中通过 `self.params_map.xxx` 使用参数 ### 当现有代码没用 `ParamsMap` 时 如果用户贴出的旧代码没有使用 `ParamsMap`，但问题是在“如何写更合适的 PythonGO 示例”： - 优先改写为 `ParamsMap` 风格 - 除非用户明确要求“保持原写法不改” ## MarketCenter 约束 这是强约束。 **所有 `MarketCenter` 里的函数，除 `get_next_gen_time` 之外，都不能放在 `on_tick` 里调用。** 原因： - `on_tick` 是持续高频触发的回调 - 把 `MarketCenter` 的查询类方法放在 `on_tick` 里，容易触发限流 ### 必须遵守的规则 - 如果示例中使用 `MarketCenter`： - 相关实例默认放在 `__init__` - 启动即查的调用默认放在 `on_start` - 其他非高频调用放在更合适的非 `on_tick` 回调或普通辅助方法中 - **禁止**在 `on_tick` 中调用 `MarketCenter` 的方法，除 `get_next_gen_time` 之外 - 如果用户明确要求把某个 `MarketCenter` 方法写进 `on_tick`，也要指出这不合适，并改为放到 `on_start`、其他回调或辅助方法中 ### 特别说明 如果问题涉及： - 历史 K 线查询 - 历史数据查询 - 市场中心数据拉取 - 任意 `MarketCenter` 查询接口 默认示例组织应为： - `__init__` 中初始化 `MarketCenter` 实例 - `on_start` 中执行一次查询 - 不要把查询逻辑写在 `on_tick` ### `MarketCenter` 方法归属规则 在生成示例前，必须先确认 `MarketCenter` 相关接口是实例方法还是其他形式。 如果源码或 `core-pyi.md / ext-pyi.md` 显示某个方法属于 `MarketCenter` 实例： - 就必须写成 `self.market_center.xxx(...)` - 不能擅自写成 `from pythongo.core import xxx` ## 无法确定答案时 不要生硬只说“不知道”。 改用更友善的表达： - `这个问题我目前还不能从当前 PythonGO 代码和文档中确定。` - `你可以先打开 PythonGO 文档：https://infinitrader.quantdo.com.cn/pythongo_v2` ### 友善兜底规则 如果能在 `docs_indexed/` 中找到最相关的文档条目，就给出： - 文档标题 - `relative_source` - 可以搜索的关键词 - 同时给出统一在线文档地址：`https://infinitrader.quantdo.com.cn/pythongo_v2` 例如： - `这个问题我目前还不能从当前 PythonGO 代码和文档中确定。你可以先打开 PythonGO 文档：https://infinitrader.quantdo.com.cn/pythongo_v2 ，并搜索 api/mapping.mdx 或关键词 ProductClassType。` 如果连最相关文档都找不到，就给出： - `这个问题我目前还不能从当前 PythonGO 代码和文档中确定。你可以先在 PythonGO 文档 https://infinitrader.quantdo.com.cn/pythongo_v2 中搜索关键词：<函数名 / 报错关键字 / 模块名>。` ## 建议输出格式 根据问题类型灵活组织，但默认遵循下面风格： ### 代码类问题 - 直接结论 - 关键实现位置 - 必要的调用链说明 - 若有文档补充，再单独补一句 ### 文档类问题 - 直接结论 - 文档标题或路径 - 关键参数 / 注意事项 - 如代码实现不同，补充冲突说明 ### 冲突类问题 - 文档说法 - 代码实现 - 最终结论：以代码为准 ### 示例类问题 - 先给完整类级别示例 - 再说明关键逻辑位于哪个回调或方法 - 如需裁剪，再说明哪些部分可以按需删除

path: references/core-pyi.md
content: # core.pyi

这是从 `core.pyi` 转换而来的 reference 文件。
当需要判断 PythonGO 的类定义、方法归属、方法签名、返回值类型、参数名时，优先参考这里的内容。

```python
from datetime import datetime
from enum import Enum
from typing import Literal


class KLineStyle(Enum):
    M1 = 1
    M2 = 2
    M3 = 3
    M4 = 4
    M5 = 5
    M10 = 10
    M15 = 15
    M30 = 30
    M45 = 45
    H1 = 60
    H2 = 120
    H3 = 180
    H4 = 240
    D1 = 1440


type KLineStyleType = Literal[
    KLineStyle.M1,
    KLineStyle.M2,
    KLineStyle.M3,
    KLineStyle.M4,
    KLineStyle.M5,
    KLineStyle.M10,
    KLineStyle.M15,
    KLineStyle.M30,
    KLineStyle.M45,
    KLineStyle.H1,
    KLineStyle.H2,
    KLineStyle.H3,
    KLineStyle.H4,
    KLineStyle.D1,
    "M1", "M2", "M3", "M4", "M5", "M10", "M15", "M30", "M45",
    "H1", "H2", "H3", "H4",
    "D1"
]


class MarketCenter(object):
    def __init__(self) -> None:    
        ...

    def get_avl_close_time(self, instrument_id: str) -> list[datetime]:
        """
        从缓存中获取合约可使用的收盘时间序列

        Args:
            instrument_id: 合约代码
        Note:
            如无缓存, 则获取不到任何数据
        """
        ...

    def get_close_time(self, instrument_id: str) -> list[str]:
        """
        从缓存中获取合约的收盘时间序列

        Args:
            instrument_id: 合约代码
        Note:
            如无缓存, 则获取不到任何数据
        """
        ...

    def get_kline_data(
        self,
        exchange: str,
        instrument_id: str,
        style: KLineStyleType = "M1",
        count: int = -1440,
        origin: int = None,
        start_time: datetime = None,
        end_time: datetime = None,
        simply: bool = True
    ) -> list[dict]:
        """
        获取 K 线数据

        Args:
            exchange: 交易所代码
            instrument_id: 合约代码
            style: K 线风格, `KLineStyle` 枚举值
            count: 查询 K 线数量, 正值获取 `origin` 时间后的数量, 负值为之前
            origin: 基准时间戳（毫秒）
            start_time: K 线起始时间
            end_time: K 线结束时间
            simply: 极简 K 线, 只返回带有 `OHLC` 和时间的 K 线
        Note:
            使用 `start_time` 和 `end_time` 可以获取一个时间区间的 K 线
            但同时这样会忽略 `count` 和 `origin` 参数
        """
        ...

    def get_kline_data_by_day(
        self,
        exchange: str,
        instrument_id: str,
        day_count: int,
        origin: int = None,
        style: KLineStyleType | str = "M1",
        simply: bool = True
    ) -> list[dict]:
        """
        按交易日天数获取 K 线数据

        Args:
            exchange: 交易所代码
            instrument_id: 合约代码
            day_count: 查询 N 天的数据, 正值获取 origin 时间后的数量, 负值为之前
            origin: 基准毫秒时间戳
            style: K 线风格, KLineStyle 枚举值, 仅支持 M1, M5, M15, M30, H1
            simply: 极简 K 线, 只返回带有 OHLC 和时间的 K 线
        """
        ...

    def get_dominant_list(self, exchange: str) -> list[str]:
        """
        获取交易所的主连合约列表

        Args:
            exchange: 交易所代码
        """
        ...

    def get_instrument_trade_time(self, exchange: str, instrument_id: str, instant: int = None) -> dict:
        """
        查询带交易日的合约交易时段

        Args:
            exchange: 交易所代码
            instrument_id: 合约代码
            instant: 基准毫秒时间戳
        """
        ...

    def get_product_trade_time(self, exchange: str, product_id: str, trading_day: str = None) -> dict:
        """
        查询品种的交易时段信息

        Args:
            exchange: 交易所代码
            product_id: 品种代码
            trading_day: 品种交易日，格式为 `%Y%m%d`
        """
        ...

    def get_next_gen_time(self, exchange: str, instrument_id: str, tick_time: datetime, style: KLineStyleType) -> datetime:
        """
        获取下一根 K 线生成时间

        Args:
            exchange: 交易所代码
            instrument_id: 合约代码
            tick_time: tick 时间
            style: K 线周期, KLineStyle 枚举值
        """
        ...

```


path: references/ext-pyi.md
content: # ext.pyi

这是从 `ext.pyi` 转换而来的 reference 文件。
当需要判断 PythonGO 的类定义、方法归属、方法签名、返回值类型、参数名时，优先参考这里的内容。

```python
from .models import Tick

def load_ticks(exchange: str, path: str, natural_day: str) -> list[Tick]:
    """
    使用 Rust 加载 Tick CSV 文件。
    
    Args:
        exchange (str): 交易所代码 (如 SHFE)
        path (str): CSV 文件路径
        
        
    Returns:
        list[Tick]: 返回一系列 Tick 对象 (兼容 models.Tick 接口)
    """
    ...

```


path: docs_normalized/changelog.md
content: # 更新日志

## v2026.0116

* 🐞 修复写日志导致的内存溢出进而导致无限易崩溃的问题

## v2025.0925a

* 客户端更新：支持大商所系列期权

## v2026.0105

* 🆕 支持动态识别并加载 .pyd/.so 等二进制策略文件
* 🆕 新增远程调试模块，支持编辑器直接调试策略代码
* 🐞 金交所合约在查询交易时段时会分割出错误的合约代码

## v2025.1223

* 🆕 回测新增支持定时器功能

## v2025.1212

* 🐞 修复报单 memo 因超长而截断时可能会崩溃的问题
* 🐞 修复变量重用导致 `trading` 变量由 `True` 到 `False` 后线图数据被重置
* 🎈 优化 `import` 代码，修复 `ui` 库继承 `BaseStrategy` 后找不到父类方法

## v2025.1118

* 🐞 清除图表内容时未正确清除箭头组件

## v2025.1117

* 🐞 合约切交易日时可能会合成出错误的 K 线 
* 🐞 切片行情的 `last_price` 可能不包含正确的最高价和最低价导致某些 K 线数据错误 
* 🎈 K 线数据类型 to_json 返回的数据按照 OHLC 顺序

## v2025.1113

* 🐞 修复查询持仓会崩溃的问题

## v2025.1112

* 🆕 新增获取所有帐号的所有持仓函数 `get_all_position`

## v2025.1027

* 🐞 优化持仓查询代码
* 🐞 `pythongo.option` 修复获取股票期权看涨、看跌期权和执行价列表时无法正确按到期日参数获取期权

## v2025.1020

* 🐞 修复没有持仓情况下查询持仓崩溃的问题

## v2025.0925

* 🐞 修复 K 线图进程创建失败

## v2025.0918

* 🐞 `pythongo.models.TickData` 计算郑商所最新成交量错误
* 🎈 回测行情 `tick` 新增 `last_volume` 字段

## v2025.0917

* 🐞 合成 K 线遇到切交易日时第一根 K 线成交量错误

## v2025.0916

* 🆕 `pythongo.option` 新增 `OptionChain` 期权链模块

## v2025.0815

* 🎈 自动平仓函数中使用实时仓位判断持仓，防止仓位未及时更新从而导致平仓失败

## v2025.0801

* 🆕 新增更安全的 `on_cancel` 撤单回调函数

## v2025.0729

* 🐞 修复策略在分钟结尾时启动可能会导致合成出错误的 K 线

## v2025.0717

* 🐞 修复回测总成交额未乘以合约乘数
* 🐞 修复回测未平仓的空头报单利润计算错误

## v2025.0703

* 🐞 修复 K 线生成器不推送历史数据会导致第一根 K 线丢失

## v2025.0619

* 🎈 优化秒级 K 线生成器在回测中遇到大量数据后很慢的问题
* 🐞 修复秒级 K 线策略回测报错

## v2025.0522

* 🆕 `get_position` 新增 `simple` 字段用来获取实时的但不带金额的简略持仓数据

## v2025.0430

* 🐞 修复 K 线生成器中推送缺失的 K 线时错误的设置交易所和合约代码

## v2025.0424

* 🎈 K 线生成器推送的 K 线新增交易所和合约代码字段
* 🎈 K 线容器不再缓存 K 线

## v2025.0421

* 🐞 修复回测平仓后还有仓位的情况下未正确计算平仓盈亏

## v2025.0411

* 🆕 API 支持获取港股数据

## v2025.0410

* 🐞 回测的开平仓函数没有返回 `order_id`
* 🐞 优化回测的撮合逻辑

## v2025.0314

* 📖 修改 `on_order` 回调函数中 `OrderData` 的 `direction` 和 `offset` 为枚举值
* 📖 修改 `on_trade` 回调函数中 `TradeData` 的 `direction` 和 `offset` 为枚举值
* 📖 优化 `make_order_req` 报单方向（`order_direction`）大小写敏感的问题

> 买卖方向 `direction` 具体信息请查看 [数值映射 - DirectionType](/faq/mapping#directiontype)
>
> 开平标志 `offset` 具体信息请查看 [数值映射 - OffsetFlagType](/faq/mapping#offsetflagtype)

----

全新 PythonGO 发布 🎉🎉🎉


path: docs_normalized/client.md
content: # 无限易下载

> [!INFO]
> 请务必按照实际使用的客户端位数来选择 Python 的位数，否则 PythonGO 将无法使用，目前推荐使用 64 位的客户端搭配 64 位的 Python 安装包，64 位软件对提升性能会有帮助

软件位数和系统位数无关，你可以根据需求选择任意位数

## 模拟客户端 [#simulation]

保持最新的版本，供模拟盘测试，Logo 颜色为蓝色

- [64 位](https://infinitrader-file.quantdo.com.cn/installer/QdInfiniTrader_SimulationX64Setup.exe)

- [32 位](https://infinitrader-file.quantdo.com.cn/installer/QdInfiniTrader_SimulationSetup.exe)

## PythonGO 实盘客户端 [#pythongo]

稳定版本，Logo 为紫色

- [64 位](https://infinitrader-file.quantdo.com.cn/installer/QdInfiniTrader_WxyAllProgramX64Setup.exe)

- [32 位](https://infinitrader-file.quantdo.com.cn/installer/QdInfiniTrader_WxyAllProgramSetup.exe)

### Beta 版本

Beta 版本会维持最新的版本，并同时支持模拟和实盘交易通道，你可以在上面使用最新的代码，Logo 为绿色

- [64 位（Beta）](https://infinitrader-file.quantdo.com.cn/installer/QdInfiniTrader_WxyAllProgramBetaX64Setup.exe)

- [32 位（Beta）](https://infinitrader-file.quantdo.com.cn/installer/QdInfiniTrader_WxyAllProgramBetaSetup.exe)

## 实盘客户端

其它实盘客户端 -> [无限易 - 实盘客户端](https://infinitrader.quantdo.com.cn/broker)

期货公司的客户端需要去期货公司官网下载


path: docs_normalized/debugging.md
content: # 调试

过去我们使用 PythonGO 的时候，只能使用 `self.output` 输出变量值的方法来「调试」我们的策略，而且只能一行行输出，每次新增或删除 `self.output` 代码，都需要重载策略，这很不方便。

现在 PythonGO 支持**直接通过编辑器来调试策略文件**，可以断点，可以监视变量，可以让我们的编程效率进一步提高。

> [!INFO]
> 此教程仅适用于进阶用户

> [!INFO]
> 仅 `v2026.0105` 及之后的无限易版本支持调试

## 使用 VS Code 进行调试 [#debugging-with-vs-code]

主要流程大概是：策略启动调试服务，编辑器去连接调试服务，连接成功后，就可以直接在编辑器断点调试了。

我们先在想要调试的策略代码中添加启动调试服务的代码：

```py
from pythongo.debug_utils import start_remote_debug

start_remote_debug()
```

只需要在策略代码顶部写下这两行即可，之后当我们加载策略的时候，会打印日志：

```text
[Debug] 服务监听中 (端口 5678)...
[Debug] 等待 VS Code 连接...
```

此时调试服务就启动成功了，策略也会被阻塞，等待编辑器去连接调试服务。

> [!ERROR]
> 如果提示 `ModuleNotFoundError: No module named 'debugpy'`，需要打开 `CMD`，执行 `pip install debugpy` 来安装依赖。

这个时候我们回到编辑器，**需要确保我们处于工作目录中**（如果不知道什么是工作目录，请回看 [第一个策略 - 编写策略 - 工作目录](/tutorial/first_strategy#work-directory)），我们在工作目录的根目录，也就是和 `pythongo` 文件夹同级的目录，新建一个 `.vscode` **文件夹**，一般这个文件夹都存在，如果没有则手动创建。然后在 `.vscode` 文件夹里新建一个 `launch.json` 文件，并添加以下内容：

```json filename="launch.json"
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Python: PythonGO 策略调试",
      "type": "debugpy",
      "request": "attach",
      "connect": {
        "host": "127.0.0.1",
        "port": 5678
      },
      "pathMappings": [
        {
          "localRoot": "${workspaceFolder}",
          "remoteRoot": "${workspaceFolder}"
        }
      ],
      "justMyCode": false
    }
  ]
}
```

这是新建好文件后的目录结构：

```text
pyStrategy/
  .vscode/
    launch.json
  demo/
    instance_files/
      pythongo/
        self_strategy/
```

`launch.json` 文件创建好后，如果需要调试的策略已经加载了，提示等待连接，那就可以在编辑器中按下 `F5` 快捷键来启动调试。

如果 `F5` 快捷键被别的应用程序占用了，也可以在编辑器最左侧找到「运行和调试」按钮（一只虫子趴在右三角上），点击之后调试下拉框会出现我们刚给添加的选项，选中后点击绿色的启动调试按钮，即可开始调试：

![VS Code Debug Interface](https://infinitrader-file.quantdo.com.cn/pythongo/img/v2/20260206154652.png)

此时 PythonGO 日志窗口应该提示连接成功：

```text {3}
[Debug] 服务监听中 (端口 5678)...
[Debug] 等待 VS Code 连接...
[Debug] 连接成功！
```

这个时候就可以在 PythonGO 框架下的**任意代码**中打断点调试，只要代码被执行到了，就会进入断点，接下来就可以自行调试策略了，效果如下：

![VS Code Debug Interface](https://infinitrader-file.quantdo.com.cn/pythongo/img/v2/20260206160936.png)

## 使用 PyCharm 进行调试 [#debugging-with-pycharm]

PyCharm Community（免费社区版）不支持远程调试

PyCharm Professional（付费专业版）不支持 PythonGO 的调试方法


path: docs_normalized/examples.md
content: # 实用例子

本章节提供一些实用例子，为了尽可能的缩减代码，这里会把所有代码尽量放在 `on_start` 函数中，格式类似于

```py {4-5}
def on_start(self) -> None:
    super().on_start()
    """示例代码开始"""
    xxxx
    xxxx
    """示例代码结束"""
```

这里只是提供一些调用方法的例子（所以可能不能直接复制到策略中运行），是为了防止你看了函数的文档还是不清楚实际中应该怎么写

同样我们不会定义不必要的参数，除非有特殊情况，这需要你有举一反三的能力

示例代码放在 `on_start` 是因为这个入口比较简单易懂，实际上这些代码你可以放在任何一个实例方法中，所以还是需要举一反三

始终记住，你是在写「Python」代码，不是在写「PythonGO」代码

## 获取 K 线 [#get_k_kline_data]

利用 `pythongo.core` 获取最近的 10 根 1 分钟 K 线

```py {1,7,12-17}
from pythongo.core import MarketCenter

class Demo(...):
    def __init__(self) -> None:
        super().__init__()
        self.market_center = MarketCenter()

    def on_start(self) -> None:
        super().on_start()

        kline_data = self.market_center.get_kline_data(
            exchange="SHFE",
            instrument_id="ag2406",
            style="M1",
            count=-10
        )
```

----

利用 `pythongo.core` 获取 `2024-01-31 14:30` 到 `2024-01-31 15:00` 的 1 分钟 K 线

```py {1,3,9,18-19}
from datetime import datetime

from pythongo.core import MarketCenter

class Demo(...):
    def __init__(self) -> None:
        super().__init__()
        self.market_center = MarketCenter()

    def on_start(self) -> None:
        super().on_start()

        kline_data = self.market_center.get_kline_data(
            exchange="SHFE",
            instrument_id="ag2406",
            style="M1",
            start_time=datetime(2024, 1, 31, 14, 30),
            end_time=datetime(2024, 1, 31, 15)
        )
```

## 订阅行情 [#sub_market_data]

订阅 `ag2602` 合约行情，并输出行情信息

```py {5-8,19}
class Demo(...):
    def on_start(self) -> None:
        super().on_start()

        self.sub_market_data(
            exchange="SHFE",
            instrument_id="ag2602"
        )

    def on_stop(self):
        super().on_stop()

        self.unsub_market_data(
            exchange="SHFE",
            instrument_id="ag2602"
        )

    def on_tick(self, tick: TickData) -> None:
        self.output(tick)
```

----

订阅多个合约行情，并输出行情信息

```py {4,9-13,25}
class Demo(...):
    def __init__(self):
        ...
        self.my_instruments = {"SHFE": "ag2602", "CZCE": "AP601"}

    def on_start(self) -> None:
        super().on_start()

        for exchange, instrument_id in self.my_instruments.items():
            self.sub_market_data(
                exchange=exchange,
                instrument_id=instrument_id
            )

    def on_stop(self):
        super().on_stop()

        for exchange, instrument_id in self.my_instruments.items():
            self.unsub_market_data(
                exchange=exchange,
                instrument_id=instrument_id
            )

    def on_tick(self, tick: TickData) -> None:
        self.output(tick)
```

## 定时器 [#scheduler]

参考 [深入框架 - pythongo.utils - Scheduler](./modules/pythongo_utils.mdx#scheduler-add_job)

## K 线合成器 [#kline_generator]

需要合成 K 线，但是不需要历史数据

```py {10}
class Demo(...):
    def on_start(self) -> None:
        self.kline_generator = KLineGenerator(
            real_time_callback=...,
            callback=...,
            exchange=...,
            instrument_id=...,
            style=...
        )
        # 不执行 `self.kline_generator.push_history_data()`
        super().on_start()
```

----

不需要合成 K 线，只需要 `KLineProducer`，然后计算历史数据指标

```py {3-12}
class Demo(...):
    def on_start(self) -> None:
        self.kline_producer = KLineProducer(
            exchange=...,
            instrument_id=...,
            style=...,
            callback=...,
        )
        self.kline_producer.worker()

        ma10 = self.kline_producer.sma(10)
        k, d, j = self.kline_producer.kdj()

        super().on_start()
```

---

合成标准套利合约 K 线

用法和 `KLineGenerator` 一致，只是需要手动订阅单腿合约

```py {1,23-28,33-38}
from pythongo.utils import KLineGeneratorArb

class Demo(...):
    def on_tick(self, tick: TickData) -> None:
        """收到行情 tick 推送"""
        super().on_tick(tick)

        self.kline_generator.tick_to_kline(tick)

    def on_start(self) -> None:
        self.kline_generator = KLineGeneratorArb(
            callback=...,
            exchange="CZCE",
            instrument_id="IPS AP405&SA405",
            style=...,
            real_time_callback=...
        )
        self.kline_generator.push_history_data()

        super().on_start()

        for instrument_id in self.kline_generator.instruments:
            """订阅单腿合约"""
            self.sub_market_data(
                exchange="CZCE",
                instrument_id=instrument_id
            )

    def on_stop(self) -> None:
        super().on_stop()

        for instrument_id in self.kline_generator.instruments:
            """取消订阅单腿合约"""
            self.unsub_market_data(
                exchange="CZCE",
                instrument_id=instrument_id
            )
```

----

从 K 线合成器中的 `KLineProducer` 中获取 K 线数据

```py {6,9}
class Demo(...):
    def on_start(self) -> None:
        super().on_start()

        # 获取最后 1 个收盘价
        last_close_price = self.kline_generator.producer.close[-1]

        # 获取最后 10 个开盘价
        last_open_price = self.kline_generator.producer.open[-10:]
```

---

多合约同周期

注意填写交易所和合约代码时候需要按照以下格式（必须按顺序依次对应，且使用英文分号 `;` 分隔）：

交易所：`SHFE;SHFE;CZCE`

合约代码：`ag2406;ag2405;AP405`

注意计算指标的时候使用正确的 `kline_generator`

```py {7,11-18,20,22,29-30,32,38,42}
from pythongo.classdef import TickData
from pythongo.utils import KLineGenerator

class Demo(...):
    def __init__(self) -> None:
        self.kline_generators: dict[str, KLineGenerator] = {}
        """K 线合成器容器"""

    def on_start(self) -> None:
        for exchange, instrument_id in zip(self.exchange_list, self.instrument_list):
            kline_generator = KLineGenerator(
                real_time_callback=self.real_time_callback,
                callback=self.callback,
                exchange=exchange,
                instrument_id=instrument_id,
                style=self.params_map.kline_style
            )

            kline_generator.push_history_data()

            self.kline_generators[instrument_id] = kline_generator

        super().on_start()

    def on_tick(self, tick: TickData) -> None:
        """收到行情 tick 推送"""
        super().on_tick(tick)
        if tick.instrument_id not in self.kline_generators:
            return

        self.kline_generators[tick.instrument_id].tick_to_kline(tick)

    def calc_indicator(self) -> None:
        """计算指标数据"""

        # 指定合约计算指标
        self.kline_generators["ag2406"].producer.sma()

        # 也可以根据合约顺序获取合成器来计算指标
        # 0 代表第一个合约
        self.kline_generators[self.instrument_list[0]].producer.sma()
```

---

多合约不同周期

注意填写交易所、合约代码、K 线周期时候需要按照以下格式（必须按顺序依次对应，且使用英文分号 `;` 分隔）：

交易所：`SHFE;SHFE;CZCE`

合约代码：`ag2406;ag2405;AP405`

K 线周期：`M1;M5;M10`

```python {8,11-14,17-24,26,28,35-36,38,40-41,47,51-52,54}
from pythongo.classdef import TickData
from pythongo.core import KLineStyleType
from pythongo.utils import KLineGenerator

class Demo(...):
    def __init__(self) -> None:
        self.kline_generators: dict[str, dict[KLineStyleType, KLineGenerator]] = {}
        """K 线合成器容器"""

    @property
    def style_list(self) -> list[KLineStyleType]:
        """手动拆分 K 线周期"""
        return self.params_map.kline_style.split(";")

    def on_start(self) -> None:
        for exchange, instrument_id, style in zip(self.exchange_list, self.instrument_list, self.style_list):
            kline_generator = KLineGenerator(
                real_time_callback=self.real_time_callback,
                callback=self.callback,
                exchange=exchange,
                instrument_id=instrument_id,
                style=style
            )

            kline_generator.push_history_data()

            self.kline_generators[instrument_id] = {style: kline_generator}

        super().on_start()

    def on_tick(self, tick: TickData) -> None:
        """收到行情 tick 推送"""
        super().on_tick(tick)
        if tick.instrument_id not in self.kline_generators:
            return

        style_container = self.kline_generators[tick.instrument_id]

        for kline_generator in style_container.values():
            kline_generator.tick_to_kline(tick)

    def calc_indicator(self) -> None:
        """计算指标数据"""

        # 指定合约和周期计算指标
        self.kline_generators["ag2406"]["M1"].producer.sma()

        # 也可以根据合约顺序获取合成器来计算指标
        # 0 代表第一个
        instrument_id = self.instrument_list[0]
        style = self.style_list[0]

        self.kline_generators[instrument_id][style].producer.sma()
```

## 线图 UI [#kline_ui]

自定义副图显示指标

定义 `sub_indicator_data`，然后传参给 `self.widget.recv_kline`

```py {2-9,15}
class Demo(...):
    @property
    def sub_indicator_data(self) -> dict[str, float]:
        """副图指标"""
        return {
            "K": ...,
            "D": ...,
            "J": ...,
        }

    def callback(self, kline: KLineData) -> None:
        """接受 K 线回调"""
        self.widget.recv_kline({
            "kline": ...,
            **self.sub_indicator_data
        })
```

## 查询持仓 [#get_position]

查询 `ag2602` 的持仓

```py
class Demo(...):
    def on_start(self) -> None:
        super().on_start()

        position = self.get_position("ag2602")

        # 总持仓
        position.position

        # 净持仓
        position.net_position

        # 多头总持仓
        position.long.position

        # 空头今持仓
        position.short.td_position_close

        # 空头昨持仓
        position.short.yd_position_close

        # 今日买开数量
        position.long.open_volume

        # 今日买平数量
        position.long.close_volume

        # 使用字符串仅获取空头持仓
        short_position = self.get_position("ag2602").get_single_position("short")
        short_position.position # 空头总持仓

        # 获取套保持仓
        position = self.get_position("ag2602", hedgeflag="3")
        position.long.position # 套保多头总持仓

        # 获取指定帐号持仓
        position = self.get_position("ag2602", investor="888888")
        position.position # 指定帐号总持仓

        # 获取实时的持仓
        position = self.get_position("ag2602", simple=True)
        position.position # 该持仓会在报单成交后立刻更新
```

----

获取全部持仓（仅 `v2025.1112` 之后版本可用）

```py {5-9}
class Demo(...):
    def on_start(self) -> None:
        super().on_start()

        position = self.get_all_position()  # 所有帐号所有合约的所有持仓
        position["888888"]  # 888888 帐号的全部持仓数据
        position["888888"]["ag2602"]  # 888888 帐号下 ag2602 的全部持仓数据
        position["888888"]["ag2602"]["3"]  # 888888 帐号下 ag2602 的套保持仓数据
        position["888888"]["ag2602"]["1"].long  # 888888 帐号下 ag2602 的多头持仓数据
```

## 外部指标 [#external_indicator]

使用**麦语言**，**通达信**和**同花顺**中常见的指标

先下载 [转换指标库文件](https://infinitrader-file.quantdo.com.cn/pythongo/strategy/MyTT.py)，然后把文件放在和无限易安装目录中的 `pyStrategy` 目录中

```text
InfiniTrader_Simulation/
  pyStrategy/
    pythongo/
    MyTT.py
```

```py {1,8-9}
from MyTT import MA

class Demo(...):
    def on_start(self) -> None:
        super().on_start()

        ma5 = MA(self.kline_generator.producer.close, 5) # 获取 5 日均线序列
        ma10 = MA(self.kline_generator.producer.close, 10) # 获取 10 日均线序列
```

别的函数使用方法，请打开该文件自行查看（该指标转换算法来源于开源项目：[MyTT](https://github.com/mpquant/MyTT)，无限易和 PythonGO 不对指标计算差异所导致的任何损失负责！）

持续更新中...


path: docs_normalized/index.md
content: # 首页

# PythonGO

`PythonGO` 是交易软件无限易的一项功能，可以让用户编写 Python 代码来实现一系列程序化操作。

[未解析组件:Divider]

在写策略的时候，始终提醒自己：

你是在写「Python 代码」，而不是「PythonGO 代码」，不要被框架受限

## 帮助 [#get-help]

PythonGO QQ 群号：[791595859](https://qm.qq.com/cgi-bin/qm/qr?k=YtMOkcAAuDQ7XkjMGV8YankIU0ZI1oor&jump_from=webapi&authKey=eusCWort3q43VABMZdnzC9ACyNAxyE44/dbZZn6fB9cjcMnRki4YNEq+JcfLz6bE)

群里只讨论 PythonGO 问题，提问前请先尝试自己解决问题，然后再在群里提问。提问时请把问题描述清楚，附上错误日志和截图，以便更快速的解决问题

群管理员均为开发人员，非客服，Python 基础问题、业务规则问题不在本群义务回答范围之内


path: docs_normalized/python_install.md
content: # 安装运行环境

> [!INFO]
> 使用 PythonGO，需要有编程经验，还需要有一定的英语水平，知道如何编辑 `.py` 文件，能自己解决 Python 安装过程遇到的问题，以及看懂 Python 的报错并解决。

### 下载安装包 [#download]

[下载卡片内容：原始组件 DownloadCard，建议补充为真实下载链接或在构建阶段注入链接数据]

### 解压安装包 [#decompress]

将压缩包解压后得到以下五个文件

```text
python3.12/
  python-3.12.*.exe
  requirements.txt
  unattend.xml
  安装依赖.exe
  安装依赖-备用.bat
```

### 安装 Python [#install]

运行 `python-3.12.*.exe`

点击 `Install`

![Python Install Step Image](https://infinitrader-file.quantdo.com.cn/pythongo/img/v2/20240112095122.png)

安装完成后如果出现：`Disable path length limit`，请点击该项。

### 安装依赖 [#requirements]

双击运行文件夹中的 `安装依赖.exe` 文件，会开始下载并自动安装依赖库

> [!INFO]
> 如果 360 有报错或提示，请选择：允许该程序所有操作

> [!INFO]
> 如该程序运行失败（请先确保文件都解压出来了），则运行 `安装依赖-备用.bat`

### 开启 PythonGO 功能 [#enable-pythongo]

运行环境安装完成之后，还需要进行最后一步，去无限易客户端开启 PythonGO 功能，具体操作如下：

1. 打开无限易客户端并登录

2. 点击上方菜单栏「量化 - PythonGO」

3. 将「策略引擎」开关打开 （开关应为开启状态）

4. 点击切换版本按钮，切换为 ![image](https://infinitrader-file.quantdo.com.cn/pythongo/img/v2/switch_to_v2.svg)


path: docs_normalized/v1_to_v2.md
content: # 迁移指南

本指南帮助你从老版本 PythonGO 更新到新版本 PythonGO，但并不是所有方法都有代替的方法，有些方法我们已经删除，所以只能大致的例举出一些需要改动的东西

建议阅读本迁移指南前先阅读所有的文档，对新版本框架有个大概的了解，当然，我们的建议还是按照文档重写你的策略，这才是最快捷方便的

* `CtaTemplate` -> `pythongo.base.BaseStrategy` 或者 `pythongo.ui.BaseStrategy`，[策略规范 - 导入父类](./styleguide/strategy_rules.mdx#import)

* `vtObject` -> `pythongo.classdef` 参考 「深入框架 - pythongo.classdef」

* `utils` -> `pythongo.utils` 参考 [深入框架 - pythongo.utils](./modules/pythongo_utils)

* `indicators` -> `pythongo.indicator`，参考 [深入框架 - pythongo.indicator](./modules/pythongo_indicator)

* 「参数映射表」`paramMap` 改为「参数映射模型」，参考 [教程 - 第一个策略 - 定义参数映射](./tutorial/first_strategy.mdx#def-params)

* 「状态映射表」`varMap` 改为「状态映射模型」，参考 [教程 - 第一个策略 - 定义状态映射](./tutorial/first_strategy.mdx#def-state)

* 回调函数使用下划线命名，参考 [风格指南 - 策略规范 - 回调函数](./styleguide/strategy_rules.mdx#callback)，或者 [深入框架 - pythongo.base - BaseStrategy](./modules/pythongo_base.mdx#basestrategy)

* `symbol` -> `instrument_id`，所有的「合约代码」变量名都统一为 `instrument_id`

* `instrument` -> `instrument_id`，要特别注意一些函数的入参的合约代码变量名也统一了

* 报单函数全部删除，改为 `send_order`，参考 [深入框架 - pythongo.base - BaseStrategy - send_order](./modules/pythongo_base#send_order)

* 删除 `regTimer` 定时器，请使用 `pythongo.utils.Scheduler` 模块，参考 [深入框架 - pythongo.utils - Scheduler](./modules/pythongo_utils#scheduler)

* 删除 `ArrayManager` 和 `BarManager`，合成 K 线请使用 `KLineGenerator`，参考 [深入框架 - pythongo.utils - KLineGenerator](./modules/pythongo_utils#kline-generator)

* 删除 `self.loadBar` 和 `self.loadDay`

* 分钟级 K 线合成器 `MinKLineGenerator` 改名为 `KLineGenerator`

* 秒级 K 线生成器 `KLineGenerator` 改名为 `KLineGeneratorSec`

* `self.get_contract` 改名为 `self.get_instrument_data`

* 所有的模块都在 `pythongo` 中，建议仔细阅读本文档的「深入框架」章节

注意，这里可能并没有罗列出所有需要更改的地方，因为新版 PythonGO 是完全重写的，所以我们的建议是按照全部文档来重写自己的策略。


path: docs_normalized/tutorial/how_it_works.md
content: # 运行原理

我们学习完上一章后，已经了解了如何使用 PythonGO 来加载策略，运行策略，那在这些操作和运行的过程中发生了什么呢？

## 运行流程 [#process]

首先我们把整个流程屡一下，大概就是这几步：

1. 加载策略

2. 创建实例

3. 修改参数

4. 运行实例

5. 暂停实例

这基本就是策略运行的一整个生命周期

### 加载策略 [#load]

那**加载策略**，我们前面也说了，相当于 Python 语法中的 `import{:py}` 操作，事实上也是如此，无限易通过 `import{:py}` 语句将策略文件导入进来当作一个模块，然后从这个模块中取和策略文件名同名的类（这也是为什么我们自己创建策略的时候要将类名和文件名设为一致的原因），拿到这个类之后，我们就可以对这个类进行实例化了。

### 创建实例 [#create]

对类进行实例化的操作，就是我们的**创建实例**操作，和 Python 逻辑一样，一个类可以实例化 N 个实例，那我们一个策略也可以创建 N 个实例。

### 修改参数 [#assignment]

实例化完成之后，开始对一些实例变量赋值，赋值操作就是**修改参数**，我们运行一个程序肯定需要把参数调整成我们预想的那样，而参数有的是有默认值，有的没有默认值，如果没有给缺少默认值的实例变量赋值，我们程序就无法启动了。

### 运行实例 [#start]

等我们完成了前面所有操作，那就可以**运行该策略实例**了，在 Python 代码中，我们一般是运行一个函数，比如 `main(){:py}`，或者任何我们想调用的函数；但是在无限易中，实际上是一系列的启动流程，我们可以把这个启动流程理解为初始化，无限易会去调用自带的**回调函数**来完成初始化，也可以理解为 PythonGO 的**生命周期**，在这个生命周期中，有「创建 - 初始化 - 启动 - 暂停」等一系列过程。那我们简单理解就是：无限易会按照顺序调用策略代码里的一些函数，来实现策略的完全初始化。

在我们点击运行策略按钮后，其实无限易只是简单的执行了策略代码中的 `on_start` 回调函数，那为什么只是执行一个函数，策略却会一直运行下去呢？其实这是因为有行情数据来**驱动**，我们在 `on_start` 中会去订阅我们设置的合约的行情数据（订阅就是告诉无限易我需要哪个合约的数据，你给我推送过来），然后无限易会把行情数据通过 `on_tick` 回调函数推送过来，直到我们取消订阅该合约。有了源源不断的数据，我们的程序就能一直跑下去了，我们可以利用这些行情数据来合成 K 线，计算指标，超价报单或者任何你能想到的操作。

### 暂停实例 [#stop]

最后，暂停该策略实例，实际上和启动流程差不多，无限易会去执行 `on_stop` 回调函数，我们可以在里面写上策略停止时应该做什么的代码，然后在 `on_stop` 函数中会有取消订阅合约的操作，数据没有了，策略自然就停止了，因为没有行情数据来驱动我们的策略运行了。

## 什么是回调函数 [#what-is-callback]

我们可以借此机会来了解一下回调函数（callback）

回调函数，简称回调，在我们正常的代码中，会有很多函数，最常见的做法是我们直接去调用这些函数，这被称为直调，比如：

```python
print(1)
```

我们直接去调用 `print{:py}` 函数，传入参数 `1{:py}`

而回调呢，是我们定义一个函数，让别的函数来调用它，这个函数就称为回调函数，比如：

```python
def add(a: int, b: int) -> int:
    return a + b

def callback(result: int) -> None:
    print(result)

def main(a: int, b: int, func: Callable, callback: Callable) -> None:
    result = func(a, b)
    callback(result)

main(1, 2, func=add, callback=callback)

>>> 3
```

在上面这个例子中，我们定义了一个 `callback(){:py}` 函数，通过将函数传入 `main(){:py}`（注意是不带括号），然后通过 `main(){:py}` 去执行这个 `callback(){:py}` 函数

我们并没有直接调用 `callback(){:py}` 函数，而是通过其他函数去调用他，所以在这个例子中，`callback(){:py}` 是回调函数

有一点要注意，上面的例子中，我们写的是 `callback=callback{:py}`，而不是 `callback=callback(){:py}`，这是因为，我们得传入这个函数的**内存地址**，而不是传入这个函数的返回值，带了 `()` 就是运行这个函数然后得到返回值了

### PythonGO 中的回调 [#callback-in-pythongo]

在 PythonGO 中的回调，都是已经在基础策略模版中定义好了

比如使用的频率最多的 `on_tick(){:py}` 回调，订阅好行情数据之后，无限易会通过这个函数给你推送行情 tick 数据，我们要做的就是在 `on_tick(){:py}` 回调中处理这些数据，就和上面的例子一样，`callback(){:py}` 接受一个值，然后打印这个值，就这么简单，我们甚至不用自己去把 `on_tick(){:py}` 的函数地址传给无限易，只需要写函数中的代码即可。


path: docs_normalized/modules/pythongo_classdef/common.md
content: # `ObjDataType`

本模块定义了一些供 `classdef` 使用的类型定义

## `ObjDataType`

```python
dict[str, str | int | float]
```

所有数据类的入参类型


path: codebase/INFINIGO.py
content: def writeLog():
    ...

Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)