# 天软 pyTSL 接口使用说明

文档类型：模块 API 事实页
是否可直接用于生成代码：是，但真实账号、密码、服务地址、配置文件路径和权限范围必须来自项目上下文
遇到不确定时：[index.md](index.md)、项目自身部署文档、官方 pyTSL 接口文档

本页整理 pyTSL 官方 Python SDK 的可调用接口。它负责告诉智能体“pyTSL 有哪些类、函数、参数和返回结构”，不负责提供真实凭证、项目账号来源、服务权限或业务数据口径。

## 智能体 pyTSL 决策规则

- 用户要“用 Python 调 TSL / 执行 TSL 代码 / 调天软函数 / 查行情 / 并发或异步取数”时，优先使用本页。
- 用户只是要执行一段 TSL 并马上拿结果，默认使用 `pyTSL.Client.exec()`。
- 用户要调用服务器上已有 TSL 函数，默认使用 `pyTSL.Client.call()`。
- 用户要按证券、周期、开始时间、结束时间取标准行情字段，默认使用 `pyTSL.Client.query()`。
- 用户要一次性提交很多独立任务，先考虑 `pyTSL.Batch`；不要把大量循环直接写成频繁登录、退出。
- 用户明确要求 asyncio，才使用 `pyTSL.AsyncClient` 或 `pyTSL.async_util`。
- 不要发明真实账号、密码、服务地址、代理、配置文件路径、服务节点、字段名或业务函数名。
- 不要把登录脚本、临时验证脚本或私有凭证写进生成代码；示例只能使用占位值或从项目配置读取。

## 安装与支持范围

### 包与导入

| 项目       | 事实                                                                      |
| ---------- | ------------------------------------------------------------------------- |
| 安装包名   | `tspytsl`                                                                 |
| 导入模块名 | `pyTSL`                                                                   |
| 在线安装   | `pip install tspytsl`                                                     |
| 升级       | `pip install tspytsl -U`                                                  |
| 离线安装   | 下载对应 `.whl` 后执行 `pip install 文件名.whl`                           |
| 手动安装   | 解压对应平台的 `.pyd` 或 `.so`，放入 Python 的 `site-packages` 或项目目录 |

### Python 与平台

- pyTSL 支持 Python 3.6+。
- 官方文档截至 2025-10-13 列出 Python 3.6 到 3.14 的 Windows、Linux x86_64、Linux arm64、MacOS x86_64 支持。
- MacOS arm64 从 Python 3.8 到 3.14 支持，Python 3.6 和 3.7 不支持。
- Windows 需要确认已安装 VS2019 C++ 运行时库；其他系统不需要这一步。

## 最小同步链路

优先写成“一次登录，多次交互，最后退出”。`logout()` 后再次调用 `Client` 方法可能触发自动重新登录，频繁登录退出可能占用多个登录数。

```python
import pyTSL
from datetime import datetime

c = pyTSL.Client("user", "password", "tsl.tinysoft.com.cn", 443)
try:
    if c.login() == 0:
        raise RuntimeError(c.last_error())

    r = c.exec("return close();", stock="SZ000002", time=datetime(2024, 7, 30))
    if r.error():
        raise RuntimeError(r.message())

    value = r.value()
finally:
    c.logout()
```

## `pyTSL.Client`

同步客户端。构造一个 `Client` 会占用一个登录数。

### 构造函数：账号参数方式

签名：

```python
pyTSL.Client(user, password, ip, port, proxy_ip=None, proxy_port=None, proxy_user=None, proxy_password=None)
```

| 参数位置 | 参数名           | 必填 | 接收类型 | 说明                                               |
| -------- | ---------------- | ---- | -------- | -------------------------------------------------- |
| 1        | `user`           | 是   | `str`    | 天软账号                                           |
| 2        | `password`       | 是   | `str`    | 天软账号密码；支持用客户端工具生成的加密密码字符串 |
| 3        | `ip`             | 是   | `str`    | 天软服务器机群地址                                 |
| 4        | `port`           | 是   | `int`    | 天软服务器访问端口                                 |
| 5        | `proxy_ip`       | 否   | `str`    | 代理服务器地址                                     |
| 6        | `proxy_port`     | 否   | `int`    | 代理服务器端口                                     |
| 7        | `proxy_user`     | 否   | `str`    | 代理服务器验证账号                                 |
| 8        | `proxy_password` | 否   | `str`    | 代理服务器验证密码                                 |

常见服务器：

| 服务器           | `ip`                      | `port`         | 默认服务节点        |
| ---------------- | ------------------------- | -------------- | ------------------- |
| 深圳服务器       | `"tsl.tinysoft.com.cn"`   | `443` 或 `444` | `"正式版"`          |
| 武汉服务器       | `"wh.tinysoft.com.cn"`    | `443` 或 `444` | `"Default Service"` |
| 深圳新节点服务器 | `"sznew.tinysoft.com.cn"` | `4433`         | `"Default Service"` |

### 构造函数：ini 文件方式

签名：

```python
pyTSL.Client(ini_file)
```

| 参数位置 | 参数名     | 必填 | 接收类型 | 说明                                 |
| -------- | ---------- | ---- | -------- | ------------------------------------ |
| 1        | `ini_file` | 是   | `str`    | 连接服务器信息 `.ini` 文件的绝对路径 |

ini 文件字段：

```ini
user=
password=
ip=
port=
proxy_user=
proxy_password=
proxy_ip=
proxy_port=
```

路径含中文并报找不到文件时，可把路径按 GBK 编码后传入。

### Client 方法总表

| 方法              | 签名                                    | 返回            | 用途                                        |
| ----------------- | --------------------------------------- | --------------- | ------------------------------------------- |
| `login`           | `login()`                               | `bool`          | 登录天软服务器；成功 `1`，失败 `0`          |
| `last_error`      | `last_error()`                          | `list`          | 最近一次错误，结构为 `[错误代码, 错误信息]` |
| `logout`          | `logout()`                              | `int`           | 断开连接；成功 `0`                          |
| `exec`            | `exec(code, **kwargs)`                  | `TSResultValue` | 执行 TSL 代码串                             |
| `call`            | `call(funcname, *args, **kwargs)`       | `TSResultValue` | 调用 TSL 函数                               |
| `query`           | `query(**kwargs)`                       | `TSResultValue` | 提取行情数据                                |
| `list_bgrun`      | `list_bgrun()`                          | `list`          | 列出委托执行任务                            |
| `stop_bgrun`      | `stop_bgrun(handle)`                    | `int`           | 停止指定委托执行任务；成功 `1`              |
| `admin`           | `admin(**kwargs)`                       | `str`           | 执行天软客户端任务管理命令                  |
| `set_callback`    | `set_callback(function)`                | 未说明          | 设置 `rdo2` 回调函数                        |
| `default_service` | `default_service(service)`              | `str`           | 设置或返回默认服务节点                      |
| `server_list`     | `server_list()`                         | `list`          | 返回服务节点列表                            |
| `download_list`   | `download_list(type, timeout=0)`        | `list`          | 下载指定类型列表                            |
| `download`        | `download(type, key, ver=0, timeout=0)` | `list`          | 下载指定关键字内容                          |
| `upload`          | `upload(type, key, data, timeout=0)`    | `list`          | 上传数据至指定关键字                        |
| `remove`          | `remove(type, key, timeout=0)`          | `list`          | 删除指定关键字内容                          |
| `is_logined`      | `is_logined()`                          | `bool`          | 已登录返回 `1`，未登录返回 `0`              |
| `is_closed`       | `is_closed()`                           | `bool`          | 已断开返回 `1`，未断开返回 `0`              |

### `exec(code, **kwargs)`

执行 TSL 代码串。结果通过 `TSResultValue` 读取。

| 参数位置 | 参数名     | 必填 | 接收类型 | 说明                |
| -------- | ---------- | ---- | -------- | ------------------- |
| 1        | `code`     | 是   | `str`    | 要执行的 TSL 代码串 |
| 2        | `**kwargs` | 否   | 见下表   | 执行上下文属性      |

### `call(funcname, *args, **kwargs)`

调用 TSL 函数。`*args` 会自动转换常见 Python 类型到 TSL 类型。

| 参数位置 | 参数名     | 必填 | 接收类型                                                                                                                     | 说明                                           |
| -------- | ---------- | ---- | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| 1        | `funcname` | 是   | `str`                                                                                                                        | 函数名                                         |
| 2        | `*args`    | 否   | `float` / `int` / `bool` / `str` / `bytes` / `list` / `dict` / `tuple` / `pandas.DataFrame` / `numpy.ndarray` / 日期时间类型 | 传给 TSL 函数的位置参数                        |
| 3        | `**kwargs` | 否   | 见下表                                                                                                                       | 执行上下文属性，可通过 `code` 传入临时函数定义 |

Python 到 TSL 的自动转换：

| Python 类型                                                                   | TSL 类型             |
| ----------------------------------------------------------------------------- | -------------------- |
| `float` / `numpy.float32` / `numpy.float64` / `datetime` / `numpy.datetime64` | `real` / `tdatetime` |
| `bool` / `int` / `numpy.int32` / `numpy.int64`                                | `int`                |
| `str` / `bytes`                                                               | `str`                |
| `list` / `dict` / `tuple` / `pandas.DataFrame` / `numpy.ndarray`              | `array`              |

### `exec` / `call` 共享 `kwargs`

| 参数名       | 必填 | 接收类型                             | 默认值           | 说明                                                                                 |
| ------------ | ---- | ------------------------------------ | ---------------- | ------------------------------------------------------------------------------------ |
| `stock`      | 否   | `str`                                | `None`           | 当前股票代码，如 `"SZ000002"`                                                        |
| `cycle`      | 否   | `str`                                | `"日线"`         | 当前周期                                                                             |
| `time`       | 否   | `datetime` / `int` / `float` / `str` | 今天             | 当前时间，如 `datetime(2019, 10, 1)`、`43739.0`、`20191001`、`"2019-10-01 14:03:01"` |
| `rate`       | 否   | `int`                                | `0`              | 复权方式：`0` 不复权，`1` 比例复权，`2` 复杂复权                                     |
| `rateday`    | 否   | `datetime` / `int` / `float` / `str` | `0`              | 复权基准日：`0` 最后交易日，`-1` 上市日，其他值为指定基准日                          |
| `precision`  | 否   | `int`                                | `-1`             | 浮点数精度                                                                           |
| `viewpoint`  | 否   | `datetime` / `int` / `float` / `str` | `0`              | 仿真时点                                                                             |
| `code`       | 否   | `str`                                | `None`           | 临时定义函数代码段，不必先保存到服务器                                               |
| `service`    | 否   | `str`                                | 由登录服务器决定 | 指定执行服务节点                                                                     |
| `timeout`    | 否   | `int`                                | `None`           | 超时，单位毫秒；`None` 表示无超时时限                                                |
| `bgrun`      | 否   | `int`                                | `0`              | 委托执行；`1` 表示委托执行                                                           |
| `resultname` | 否   | `str`                                | `None`           | 仅 `exec` 支持；`bgrun=1` 时用于指定委托保存的结果集名称                             |
| `reportmode` | 否   | `int`                                | `-1`             | 报表数据规则                                                                         |
| `emptymode`  | 否   | `int`                                | `0`              | 空记录模式                                                                           |

### `query(**kwargs)`

提取行情数据。适合标准证券、周期、时间区间、字段查询。

| 参数位置 | 参数名     | 必填 | 接收类型 | 说明                                                          |
| -------- | ---------- | ---- | -------- | ------------------------------------------------------------- |
| 1        | `**kwargs` | 是   | 见下表   | 查询属性，必须包含 `stock`、`cycle`、`begin_time`、`end_time` |

| 参数名        | 必填 | 接收类型                             | 默认值           | 说明                                             |
| ------------- | ---- | ------------------------------------ | ---------------- | ------------------------------------------------ |
| `stock`       | 是   | `str` / 一维数组                     | `None`           | 当前股票代码；可指定多只股票                     |
| `cycle`       | 是   | `str`                                | `"日线"`         | 当前周期                                         |
| `begin_time`  | 是   | `datetime` / `int` / `float` / `str` | `None`           | 数据开始时间                                     |
| `end_time`    | 是   | `datetime` / `int` / `float` / `str` | `None`           | 数据结束时间                                     |
| `fields`      | 否   | `str` / `tuple` / `list`             | 所有字段         | 查询字段；字符串用逗号分隔                       |
| `rate`        | 否   | `int`                                | `0`              | 复权方式：`0` 不复权，`1` 比例复权，`2` 复杂复权 |
| `rateday`     | 否   | `datetime` / `int` / `float` / `str` | `0`              | 复权基准日                                       |
| `precision`   | 否   | `int`                                | `-1`             | 浮点数精度                                       |
| `viewpoint`   | 否   | `datetime` / `int` / `float` / `str` | `0`              | 仿真时点                                         |
| `service`     | 否   | `str`                                | 由登录服务器决定 | 指定执行服务节点                                 |
| `timeout`     | 否   | `int`                                | `None`           | 超时，单位毫秒                                   |
| `cyclefilter` | 否   | `int`                                | `0`              | 周期过滤器以及集合竞价分离器                     |

### 委托、任务管理和服务节点方法

#### `list_bgrun()`

| 参数位置 | 参数名 | 必填 | 接收类型 | 说明       |
| -------- | ------ | ---- | -------- | ---------- |
| 无       | 无     | 否   | 无       | 不接收参数 |

返回 `["+OK", [委托任务1], ...]`。委托任务结构为 `[handle, 执行的函数名, 开始执行时间, 已运行时间]`。

#### `stop_bgrun(handle)`

| 参数位置 | 参数名   | 必填 | 接收类型 | 说明         |
| -------- | -------- | ---- | -------- | ------------ |
| 1        | `handle` | 是   | `float`  | 委托任务编号 |

返回整数，成功为 `1`。

#### `admin(**kwargs)`

| 参数位置 | 参数名     | 必填 | 接收类型 | 说明                                      |
| -------- | ---------- | ---- | -------- | ----------------------------------------- |
| 1        | `**kwargs` | 是   | `str`    | 天软客户端任务管理命令，如 `"oa"`、`"ou"` |

返回对应指令的查询信息。

#### `set_callback(function)`

| 参数位置 | 参数名     | 必填 | 接收类型          | 说明                 |
| -------- | ---------- | ---- | ----------------- | -------------------- |
| 1        | `function` | 是   | Python 可调用对象 | 设置 `rdo2` 回调函数 |

`rdo2 callback(...)` 调用时会回到 Python 函数；不要把已经通过 `register_proc` 注册过的函数再当作该回调。

#### `default_service(service)`

| 参数位置 | 参数名    | 必填 | 接收类型 | 说明                                           |
| -------- | --------- | ---- | -------- | ---------------------------------------------- |
| 1        | `service` | 否   | `str`    | 为空时返回当前服务节点；非空时设置当前服务节点 |

返回字符串，内容为服务节点。

#### `server_list()`

| 参数位置 | 参数名 | 必填 | 接收类型 | 说明       |
| -------- | ------ | ---- | -------- | ---------- |
| 无       | 无     | 否   | 无       | 不接收参数 |

返回服务节点列表。

### 下载、上传和删除

#### 类型取值

| 类型值   | 可用于                                             | 说明             |
| -------- | -------------------------------------------------- | ---------------- |
| `0x2001` | `download_list` / `download`                       | 系统板块         |
| `0x2002` | `download_list` / `download` / `upload` / `remove` | 用户板块         |
| `0x5001` | `download_list` / `download`                       | 系统基本面表描述 |
| `0x6001` | `download_list` / `download` / `upload` / `remove` | 用户函数         |
| `0x6002` | `download_list` / `download`                       | 共享函数         |
| `0x6003` | `download_list` / `download`                       | 系统公用函数     |
| `0x6004` | `download_list` / `download`                       | 系统函数         |
| `0x8001` | `download_list` / `download` / `upload` / `remove` | 用户数据表       |

#### `download_list(type, timeout=0)`

| 参数位置 | 参数名    | 必填 | 接收类型 | 说明                                    |
| -------- | --------- | ---- | -------- | --------------------------------------- |
| 1        | `type`    | 是   | `int`    | 指定类型                                |
| 2        | `timeout` | 否   | `int`    | 超时，单位毫秒；默认 `0` 表示无超时限制 |

返回长度为 2 的列表。无错误返回 `[0, 结果列表]`，有错误返回 `[错误代码, 错误信息]`。结果列表中的单项结构为 `[关键字, 版本, 修改时间]`。

#### `download(type, key, ver=0, timeout=0)`

| 参数位置 | 参数名    | 必填 | 接收类型 | 说明                                    |
| -------- | --------- | ---- | -------- | --------------------------------------- |
| 1        | `type`    | 是   | `int`    | 指定类型                                |
| 2        | `key`     | 是   | `str`    | 关键字，如函数名、板块名                |
| 3        | `ver`     | 否   | `int`    | 关键字版本；默认 `0` 表示最新版本       |
| 4        | `timeout` | 否   | `int`    | 超时，单位毫秒；默认 `0` 表示无超时限制 |

无错误返回 `[0, 结果]`，有错误返回 `[错误代码, 错误信息]`。结果结构包含：

| 键               | 说明                                                     |
| ---------------- | -------------------------------------------------------- |
| `"data"`         | 关键字内容，TSL 流格式，可用 `pyTSL.DecodeStream()` 转换 |
| `"LastModified"` | 版本                                                     |
| `"LastTime"`     | 修改时间                                                 |

#### `upload(type, key, data, timeout=0)`

| 参数位置 | 参数名    | 必填 | 接收类型 | 说明                                    |
| -------- | --------- | ---- | -------- | --------------------------------------- |
| 1        | `type`    | 是   | `int`    | 只支持 `0x2002`、`0x6001`、`0x8001`     |
| 2        | `key`     | 是   | `str`    | 关键字，如函数名、板块名、用户数据名    |
| 3        | `data`    | 是   | `Any`    | 上传至关键字的数据                      |
| 4        | `timeout` | 否   | `int`    | 超时，单位毫秒；默认 `0` 表示无超时限制 |

无错误返回 `[0, 结果]`，有错误返回 `[错误代码, 错误信息]`。

#### `remove(type, key, timeout=0)`

| 参数位置 | 参数名    | 必填 | 接收类型 | 说明                                    |
| -------- | --------- | ---- | -------- | --------------------------------------- |
| 1        | `type`    | 是   | `int`    | 只支持 `0x2002`、`0x6001`、`0x8001`     |
| 2        | `key`     | 是   | `str`    | 关键字，如函数名、用户数据名            |
| 3        | `timeout` | 否   | `int`    | 超时，单位毫秒；默认 `0` 表示无超时限制 |

无错误返回 `[0, 结果]`，有错误返回 `[错误代码, 错误信息]`。

## `pyTSL.AsyncClient`

异步客户端，适用于 Python `asyncio`。与 `Client` 一样会占用登录数；异步任务数量受账号并发数限制，普通账号常见并发数为 5，应保留资源。

构造方式同 `Client`：

```python
pyTSL.AsyncClient("user", "password", "tsl.tinysoft.com.cn", 443)
pyTSL.AsyncClient("E:\\my.ini")
```

也支持空构造后复用已有 `Client`：

```python
c = pyTSL.Client("user", "password", "tsl.tinysoft.com.cn", 443)
ac = pyTSL.AsyncClient()
ac.c = c
```

| 方法            | 签名                                    | 返回            | 参数来源                         |
| --------------- | --------------------------------------- | --------------- | -------------------------------- |
| `login`         | `login()`                               | `bool`          | 无参数；成功 `1`，失败 `0`       |
| `logout`        | `logout()`                              | `int`           | 无参数；成功 `0`                 |
| `exec`          | `exec(code, **kwargs)`                  | `TSResultValue` | 同 `Client.exec`                 |
| `call`          | `call(funcname, *args, **kwargs)`       | `TSResultValue` | 同 `Client.call`                 |
| `query`         | `query(**kwargs)`                       | `TSResultValue` | 同 `Client.query`                |
| `admin`         | `admin(**kwargs)`                       | `str`           | 同 `Client.admin`                |
| `download_list` | `download_list(type, timeout=0)`        | `list`          | 同 `Client.download_list`        |
| `download`      | `download(type, key, ver=0, timeout=0)` | `list`          | 同 `Client.download`             |
| `upload`        | `upload(type, key, data, timeout=0)`    | `list`          | 同 `Client.upload`               |
| `remove`        | `remove(type, key, timeout=0)`          | `list`          | 同 `Client.remove`               |
| `task_list`     | `task_list()`                           | `list`          | 返回异步任务列表                 |
| `task_cancel`   | `task_cancel(task)`                     | `int`           | 取消异步任务；成功 `1`，失败 `0` |

最小异步示例：

```python
import asyncio
import pyTSL
from datetime import datetime

async def main():
    c = pyTSL.AsyncClient("user", "password", "tsl.tinysoft.com.cn", 443)
    try:
        if await c.login() == 0:
            raise RuntimeError("login failed")
        r = await c.exec("return close();", stock="SZ000002", time=datetime(2024, 7, 30))
        if r.error():
            raise RuntimeError(r.message())
        return r.value()
    finally:
        await c.logout()

value = asyncio.run(main())
```

## `pyTSL.async_util`

`async_util` 把普通 `Client` 的操作包装为可 `await` 的函数。所有函数第一个参数都是 `pyTSL.Client` 实例。

| 函数                  | 签名                                             | 返回            | 对应同步方法           |
| --------------------- | ------------------------------------------------ | --------------- | ---------------------- |
| `async_login`         | `async_login(c)`                                 | `bool`          | `c.login()`            |
| `async_logout`        | `async_logout(c)`                                | `int`           | `c.logout()`           |
| `async_exec`          | `async_exec(c, code, **kwargs)`                  | `TSResultValue` | `c.exec(...)`          |
| `async_call`          | `async_call(c, funcname, *args, **kwargs)`       | `TSResultValue` | `c.call(...)`          |
| `async_query`         | `async_query(c, **kwargs)`                       | `TSResultValue` | `c.query(...)`         |
| `async_admin`         | `async_admin(c, **kwargs)`                       | `str`           | `c.admin(...)`         |
| `async_download_list` | `async_download_list(c, type, timeout=0)`        | `list`          | `c.download_list(...)` |
| `async_download`      | `async_download(c, type, key, ver=0, timeout=0)` | `list`          | `c.download(...)`      |
| `async_upload`        | `async_upload(c, type, key, data, timeout=0)`    | `list`          | `c.upload(...)`        |
| `async_remove`        | `async_remove(c, type, key, timeout=0)`          | `list`          | `c.remove(...)`        |

## pyTSL 全局方法

### `DatetimeToDouble(datetime, timezone=8)`

| 参数位置 | 参数名     | 必填 | 接收类型                              | 说明                       |
| -------- | ---------- | ---- | ------------------------------------- | -------------------------- |
| 1        | `datetime` | 是   | `datetime.date` / `datetime.datetime` | Python 日期或日期时间      |
| 2        | `timezone` | 否   | `int`                                 | 时区，默认 `8`，即北京时间 |

返回 TSL 的时间类型 `TDateTime`，精度到毫秒。

### `DoubleToDatetime(t)`

| 参数位置 | 参数名 | 必填 | 接收类型              | 说明         |
| -------- | ------ | ---- | --------------------- | ------------ |
| 1        | `t`    | 是   | `TDateTime` / `float` | TSL 时间类型 |

返回 Python `datetime`，精度到毫秒。

### `DataFrameDiff(df1, df2)`

| 参数位置 | 参数名 | 必填 | 接收类型           | 说明                                  |
| -------- | ------ | ---- | ------------------ | ------------------------------------- |
| 1        | `df1`  | 是   | `pandas.DataFrame` | 第一个 DataFrame，结构需与 `df2` 一致 |
| 2        | `df2`  | 是   | `pandas.DataFrame` | 第二个 DataFrame，结构需与 `df1` 一致 |

返回两个 DataFrame 的不同数据以及行列位置。

### `EncodeStream(obj)`

| 参数位置 | 参数名 | 必填 | 接收类型    | 说明                 |
| -------- | ------ | ---- | ----------- | -------------------- |
| 1        | `obj`  | 是   | Python 变量 | 要转换的 Python 对象 |

返回 TSL 流格式。

### `DecodeStream(bytes, parse_date=False)`

| 参数位置 | 参数名       | 必填 | 接收类型                  | 说明                                 |
| -------- | ------------ | ---- | ------------------------- | ------------------------------------ |
| 1        | `bytes`      | 是   | TSL 流格式                | 要解码的 TSL 流                      |
| 2        | `parse_date` | 否   | `bool` / `list` / `tuple` | 是否把日期字段转成 Python `datetime` |

`parse_date` 取值：

| 取值                     | 说明                                                    |
| ------------------------ | ------------------------------------------------------- |
| `False`                  | 默认值，不转换，TSL `TDateTime` 保持实数                |
| `True`                   | 将 TSL `TDateTime` 或日期字符串转换为 Python `datetime` |
| `["date"]` / `("date",)` | 只转换指定字段                                          |

返回 Python 变量。

### `register_proc(funcname1, funcname2)`

| 参数位置 | 参数名      | 必填 | 接收类型          | 说明                     |
| -------- | ----------- | ---- | ----------------- | ------------------------ |
| 1        | `funcname1` | 是   | `str`             | TSL 本地调用时使用的名称 |
| 2        | `funcname2` | 是   | Python 可调用对象 | Python 中定义的函数      |

将 Python 函数注册为天软函数，可在交互语句中用 `rdo2` 调用。`importfile` 和 `exportfile` 已有默认实现，不要注册同名函数。

### `DataFrameToTSArray(DataFrame)`

| 参数位置 | 参数名      | 必填 | 接收类型           | 说明               |
| -------- | ----------- | ---- | ------------------ | ------------------ |
| 1        | `DataFrame` | 是   | `pandas.DataFrame` | 要转换的 DataFrame |

返回 TSL 的 `array` 数据结构。`Client.call()` 传入 DataFrame 时也会自动转换。

### `start_network_threads(n)`

| 参数位置 | 参数名 | 必填 | 接收类型 | 说明                         |
| -------- | ------ | ---- | -------- | ---------------------------- |
| 1        | `n`    | 是   | `int`    | 额外启动的网络 IO 处理线程数 |

返回 `None`。pyTSL 默认有 1 个线程处理网络 IO；多连接场景可以增加线程。

## `pyTSL.Batch`

用于并发执行多个天软任务，只占用一个登录数。并发数受账号资源限制，不是越大越好；普通账号常见并发数为 5，建议保守设置。

### 构造函数

签名：

```python
pyTSL.Batch(parallel=5, reconnect=0)
```

| 参数位置 | 参数名      | 必填 | 接收类型 | 说明                                                 |
| -------- | ----------- | ---- | -------- | ---------------------------------------------------- |
| 1        | `parallel`  | 否   | `int`    | 并行任务数，默认 `5`                                 |
| 2        | `reconnect` | 否   | `int`    | 是否重新连接；`0` 不重新连接，`1` 重新连接，默认 `0` |

### Batch 方法

| 方法      | 签名                                          | 返回   | 说明                                             |
| --------- | --------------------------------------------- | ------ | ------------------------------------------------ |
| `exec`    | `exec(client, code, **kwargs)`                | `list` | 并发执行 TSL 代码串                              |
| `call`    | `call(client, functionname, *args, **kwargs)` | `list` | 并发调用 TSL 函数                                |
| `query`   | `query(client, **kwargs)`                     | `list` | 并发查询行情数据                                 |
| `__len__` | `__len__()`                                   | `int`  | 通过 `len(iter(batch))` 返回迭代器中剩余元素个数 |

`Batch.exec()` 参数：

| 参数位置 | 参数名     | 必填 | 接收类型         | 说明                           |
| -------- | ---------- | ---- | ---------------- | ------------------------------ |
| 1        | `client`   | 是   | `pyTSL.Client`   | 已构造的客户端实例             |
| 2        | `code`     | 是   | `str`            | TSL 代码串                     |
| 3        | `**kwargs` | 否   | 见 `Client.exec` | 不支持委托执行；额外支持 `key` |

`Batch.call()` 参数：

| 参数位置 | 参数名         | 必填 | 接收类型         | 说明                           |
| -------- | -------------- | ---- | ---------------- | ------------------------------ |
| 1        | `client`       | 是   | `pyTSL.Client`   | 已构造的客户端实例             |
| 2        | `functionname` | 是   | `str`            | TSL 函数名                     |
| 3        | `*args`        | 否   | 见 `Client.call` | 传给 TSL 函数的位置参数        |
| 4        | `**kwargs`     | 否   | 见 `Client.call` | 不支持委托执行；额外支持 `key` |

`Batch.query()` 参数：

| 参数位置 | 参数名     | 必填 | 接收类型          | 说明                           |
| -------- | ---------- | ---- | ----------------- | ------------------------------ |
| 1        | `client`   | 是   | `pyTSL.Client`    | 已构造的客户端实例             |
| 2        | `**kwargs` | 是   | 见 `Client.query` | 不支持委托执行；额外支持 `key` |

`key` 参数：

| 参数名 | 必填 | 接收类型 | 默认值 | 说明                                              |
| ------ | ---- | -------- | ------ | ------------------------------------------------- |
| `key`  | 否   | `str`    | `""`   | 标识每个任务；也可通过返回的 `Task.key(str)` 设置 |

返回结构：

| 情况     | 返回                  |
| -------- | --------------------- |
| 提交成功 | `[1, Task对象]`       |
| 提交失败 | `[0, 错误信息字符串]` |

结果读取：

```python
for r in iter(batch):
    print(r.key(), r.value())
```

遍历时每个 `r` 是 `TSResultValue`。

## `pyTSL.Task`

`Batch` 执行任务的标识。

| 方法  | 签名            | 返回  | 说明                                       |
| ----- | --------------- | ----- | ------------------------------------------ |
| `id`  | `id()`          | `int` | 返回任务 ID                                |
| `key` | `key(str=None)` | `str` | 有参数时设置关键字；无参数时返回当前关键字 |

## `TSResultValue`

`Client.exec()`、`Client.call()`、`Client.query()`、异步方法和 `Batch` 迭代结果都会返回或产出 `TSResultValue`。

| 方法        | 签名                      | 返回                 | 说明                                    |
| ----------- | ------------------------- | -------------------- | --------------------------------------- |
| `error`     | `error()`                 | `int`                | `0` 表示无错误，非 `0` 表示出错         |
| `message`   | `message()`               | `str`                | 有错误时返回错误信息，无错误时为空      |
| `value`     | `value(parse_date=False)` | `list` / 标量 / 对象 | 返回结果值                              |
| `dataframe` | `dataframe()`             | `pandas.DataFrame`   | 输出 DataFrame；相当于把 `value()` 转表 |
| `stream`    | `stream()`                | TSL 流格式           | 返回 TSL 流格式结果                     |
| `stn`       | `stn()`                   | `str`                | 返回 TSL STN 格式结果                   |
| `key`       | `key()`                   | `str`                | 返回任务关键字，配合 `Batch` 使用       |
| `cancelled` | `cancelled()`             | `bool`               | 返回任务是否被取消                      |

`value(parse_date=...)`：

| 取值                                        | 说明                                                                                                    |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `False`                                     | 默认值，不转换日期，TSL `TDateTime` 保持实数                                                            |
| `True`                                      | 将一维数组或单个值中的 TSL 日期时间、日期字符串转换为 Python `datetime`；`query()` 的默认行为是转换日期 |
| `["date1", "date2"]` / `("date1", "date2")` | 对二维数组或表格中的指定日期字段做转换                                                                  |

推荐错误处理：

```python
r = c.exec("return 1 + 1;")
if r.error():
    raise RuntimeError(r.message())
value = r.value()
```

## `pyTSL.Const`

常量模块包含周期、行情字段、文件导入导出类型和文件读写类型。导入方式：

```python
from pyTSL.Const import *
```

### 周期常量

| 常量          | 对应值           |
| ------------- | ---------------- |
| `cy_month`    | `"月线"`         |
| `cy_day`      | `"日线"`         |
| `cy_week`     | `"周线"`         |
| `cy_quarter`  | `"季线"`         |
| `cy_halfyear` | `"半年线"`       |
| `cy_1m`       | `"1分钟线"`      |
| `cy_2m`       | `"2分钟线"`      |
| `cy_3m`       | `"3分钟线"`      |
| `cy_5m`       | `"5分钟线"`      |
| `cy_10m`      | `"10分钟线"`     |
| `cy_15m`      | `"15分钟线"`     |
| `cy_20m`      | `"20分钟线"`     |
| `cy_30m`      | `"30分钟线"`     |
| `cy_40m`      | `"40分钟线"`     |
| `cy_60m`      | `"60分钟线"`     |
| `cy_120m`     | `"120分钟线"`    |
| `cy_detail`   | `"成交明细"`     |
| `cy_halfs`    | `"半秒线"`       |
| `cy_1s`       | `"1秒线"`        |
| `cy_2s`       | `"2秒线"`        |
| `cy_3s`       | `"3秒线"`        |
| `cy_4s`       | `"4秒线"`        |
| `cy_5s`       | `"5秒线"`        |
| `cy_6s`       | `"6秒线"`        |
| `cy_10s`      | `"10秒线"`       |
| `cy_12s`      | `"12秒线"`       |
| `cy_15s`      | `"15秒线"`       |
| `cy_20s`      | `"20秒线"`       |
| `cy_30s`      | `"30秒线"`       |
| `cy_f30m`     | `"期货30分钟线"` |
| `cy_f60m`     | `"期货60分钟线"` |

### 行情字段常量

| 常量                    | 字段值                    | 说明                 |
| ----------------------- | ------------------------- | -------------------- |
| `STOCKID`               | `"StockID"`               | 股票代码             |
| `STOCKNAME`             | `"StockName"`             | 股票名称             |
| `DATE`                  | `"date"`                  | 当前时间             |
| `PRICE`                 | `"price"`                 | 价格                 |
| `CLOSE`                 | `"close"`                 | 收盘价               |
| `OPEN`                  | `"open"`                  | 开盘价               |
| `HIGH`                  | `"high"`                  | 最高价               |
| `LOW`                   | `"low"`                   | 最低价               |
| `VOL`                   | `"vol"`                   | 成交量               |
| `AMOUNT`                | `"amount"`                | 成交金额             |
| `CJBS`                  | `"cjbs"`                  | 成交笔数             |
| `YCLOSE`                | `"yclose"`                | 上次价               |
| `SYL1`                  | `"syl1"`                  | 市盈率 1             |
| `SYL2`                  | `"syl2"`                  | 市盈率 2             |
| `BUY1`                  | `"buy1"`                  | 买一价               |
| `BUY2`                  | `"buy2"`                  | 买二价               |
| `BUY3`                  | `"buy3"`                  | 买三价               |
| `BUY4`                  | `"buy4"`                  | 买四价               |
| `BUY5`                  | `"buy5"`                  | 买五价               |
| `SALE1`                 | `"sale1"`                 | 卖一价               |
| `SALE2`                 | `"sale2"`                 | 卖二价               |
| `SALE3`                 | `"sale3"`                 | 卖三价               |
| `SALE4`                 | `"sale4"`                 | 卖四价               |
| `SALE5`                 | `"sale5"`                 | 卖五价               |
| `BC1`                   | `"bc1"`                   | 买一量               |
| `BC2`                   | `"bc2"`                   | 买二量               |
| `BC3`                   | `"bc3"`                   | 买三量               |
| `BC4`                   | `"bc4"`                   | 买四量               |
| `BC5`                   | `"bc5"`                   | 买五量               |
| `SC1`                   | `"sc1"`                   | 卖一量               |
| `SC2`                   | `"sc2"`                   | 卖二量               |
| `SC3`                   | `"sc3"`                   | 卖三量               |
| `SC4`                   | `"sc4"`                   | 卖四量               |
| `SC5`                   | `"sc5"`                   | 卖五量               |
| `WB`                    | `"wb"`                    | 委比                 |
| `LB`                    | `"lb"`                    | 量比                 |
| `ZMM`                   | `"zmm"`                   | 买卖标识             |
| `BUY_VOL`               | `"buy_vol"`               | 主买量               |
| `BUY_AMOUNT`            | `"buy_amount"`            | 主买金额             |
| `SALE_VOL`              | `"sale_vol"`              | 主卖量               |
| `SALE_AMOUNT`           | `"sale_amount"`           | 主卖金额             |
| `W_BUY`                 | `"w_buy"`                 | 委买                 |
| `W_SALE`                | `"w_sale"`                | 委卖                 |
| `SECTIONAL_BUY_VOL`     | `"sectional_buy_vol"`     | 时点当日累计主买量   |
| `SECTIONAL_BUY_AMOUNT`  | `"sectional_buy_amount"`  | 时点当日累计主买金额 |
| `SECTIONAL_SALE_VOL`    | `"sectional_sale_vol"`    | 时点当日累计主卖量   |
| `SECTIONAL_SALE_AMOUNT` | `"sectional_sale_amount"` | 时点当日累计主卖金额 |
| `SECTIONAL_W_BUY`       | `"sectional_w_buy"`       | 时点当日累计委买     |
| `SECTIONAL_W_SALE`      | `"sectional_w_sale"`      | 时点当日累计委卖     |
| `SECTIONAL_YCLOSE`      | `"sectional_yclose"`      | 前日收盘             |
| `SECTIONAL_OPEN`        | `"sectional_open"`        | 时点当日开盘         |
| `SECTIONAL_HIGH`        | `"sectional_high"`        | 时点当日最高         |
| `SECTIONAL_LOW`         | `"sectional_low"`         | 时点当日最低         |
| `SECTIONAL_VOL`         | `"sectional_vol"`         | 时点当日累计成交量   |
| `SECTIONAL_AMOUNT`      | `"sectional_amount"`      | 时点当日累计成交金额 |
| `SECTIONAL_CJBS`        | `"sectional_cjbs"`        | 时点当日累计成交笔数 |
| `SECTIONAL_WB`          | `"sectional_wb"`          | 时点当日累计委比     |

### 文件导入导出类型常量

| 常量       | 值  | 说明                        |
| ---------- | --- | --------------------------- |
| `ftCSV`    | `0` | 逗号分割文件类型            |
| `ftXLS`    | `1` | Excel 文件类型              |
| `ftStream` | `2` | 天软对象流文件类型          |
| `ftString` | `3` | 天软对象字符串文件类型      |
| `ftADO`    | `4` | ADO 文件类型                |
| `ftXML`    | `5` | XML 文件类型                |
| `ftXLS2`   | `6` | 支持多表头的 Excel 文件类型 |
| `ftXLS3`   | `7` | Excel 文件类型              |
| `ftDBF`    | `8` | DBF 文件类型                |

### 文件读写类型常量

| 常量       | 值  | 说明             |
| ---------- | --- | ---------------- |
| `rwByte`   | `0` | 字节流读写类型   |
| `rwInt`    | `1` | 整数流读写类型   |
| `rwReal`   | `2` | 实数流读写类型   |
| `rwStr`    | `3` | 字符串流读写类型 |
| `rwObj`    | `4` | 对象流读写类型   |
| `rwRaw`    | `5` | 原始读写类型     |
| `rwBinary` | `6` | 二进制读写类型   |

## `query` 周期与字段

### 周期字符串

| 分类   | 可用值                                                                                                                                                 |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 低频   | `"日线"`、`"周线"`、`"月线"`、`"季线"`、`"半年线"`、`"年线"`                                                                                           |
| 高频   | `"1分钟线"`、`"2分钟线"`、`"3分钟线"`、`"5分钟线"`、`"10分钟线"`、`"15分钟线"`、`"20分钟线"`、`"30分钟线"`、`"40分钟线"`、`"60分钟线"`、`"120分钟线"`  |
| 超高频 | `"成交明细"`、`"半秒线"`、`"1秒线"`、`"2秒线"`、`"3秒线"`、`"4秒线"`、`"5秒线"`、`"6秒线"`、`"10秒线"`、`"12秒线"`、`"15秒线"`、`"20秒线"`、`"30秒线"` |
| 其他   | `"任意周期"`、`"期货30分钟线"`、`"期货60分钟线"`                                                                                                       |

### 低频字段

| 字段        | 数据类型   | 名称     | 说明                                                               |
| ----------- | ---------- | -------- | ------------------------------------------------------------------ |
| `StockID`   | `string`   | 代码     | 股票 ID                                                            |
| `StockName` | `string`   | 名称     | 股票名称                                                           |
| `date`      | `datetime` | 日期     | 当前时间                                                           |
| `price`     | `real`     | 最新价   | 该周期内最后一笔成交价                                             |
| `open`      | `real`     | 开盘价   | 交易所公布的开盘价                                                 |
| `close`     | `real`     | 收盘价   | 交易所公布的收盘价                                                 |
| `high`      | `real`     | 最高价   | 周期内最高价                                                       |
| `low`       | `real`     | 最低价   | 周期内最低价                                                       |
| `vol`       | `real`     | 成交量   | 周期内成交量；股票单位为股，基金单位为份，期货和期权单位为合约数量 |
| `amount`    | `real`     | 成交金额 | 周期内成交金额                                                     |
| `cjbs`      | `real`     | 成交笔数 | 股票为成交笔数；期货/期权可表示持仓变动量，日线表示当日持仓量      |
| `yclose`    | `real`     | 系统昨收 | 交易所发布的考虑分红送股后的价格                                   |
| `syl1`      | `real`     | 市盈率 1 | 期货/期权日线表示当日结算价；其他通常为 `0`                        |
| `syl2`      | `real`     | 市盈率 2 | 期货/期权表示昨日结算价；ETF/LOF 表示时点净值；其他通常为 `0`      |

### 高频与 Level1 字段

| 字段                    | 数据类型   | 名称                 | 说明                                                                                                |
| ----------------------- | ---------- | -------------------- | --------------------------------------------------------------------------------------------------- |
| `StockID`               | `string`   | 代码                 | 股票 ID                                                                                             |
| `StockName`             | `string`   | 名称                 | 股票名称                                                                                            |
| `date`                  | `datetime` | 时间                 | 当前时间                                                                                            |
| `price`                 | `real`     | 价格                 | 交易明细为该时间点最后一笔成交价；其他为该周期最后一笔成交价                                        |
| `vol`                   | `real`     | 成交量               | 周期内成交量；分钟周期表示该分钟成交量，不是开盘累计                                                |
| `amount`                | `real`     | 成交金额             | 周期内成交金额；分钟周期表示该分钟成交金额                                                          |
| `cjbs`                  | `real`     | 成交笔数             | 股票为成交笔数；期货/期权为周期内持仓变动量                                                         |
| `yclose`                | `real`     | 上次价               | 上一周期的收盘价                                                                                    |
| `syl1`                  | `real`     | 市盈率 1             | 期货/期权分钟线或秒线中，当日结算价存储在当日最后成交记录中；其他通常为 `0`                         |
| `syl2`                  | `real`     | 市盈率 2             | 期货/期权为昨日结算价；ETF/LOF 为时点净值；其他通常为 `0`                                           |
| `buy1` 至 `buy5`        | `real`     | 买一价至买五价       | 期货没有 `buy2` 至 `buy5`                                                                           |
| `sale1` 至 `sale5`      | `real`     | 卖一价至卖五价       | 期货没有 `sale2` 至 `sale5`                                                                         |
| `bc1` 至 `bc5`          | `int`      | 买一量至买五量       | 对应价位的委买量                                                                                    |
| `sc1` 至 `sc5`          | `int`      | 卖一量至卖五量       | 对应价位的委卖量                                                                                    |
| `wb`                    | `real`     | 委比                 | 公式为委买/委卖                                                                                     |
| `lb`                    | `int`      | 量比                 | 秒线取交易明细最后一笔；1 分钟线按过去 5 日均量和当日累计开市时间计算；其他分钟线取周期内最后一分钟 |
| `zmm`                   | `int`      | 买卖标识             | 交易明细中 `1` 主买，`2` 主卖，`3` 集合竞价/涨停/跌停等；非交易明细无意义                           |
| `buy_vol`               | `real`     | 主买量               | 主买成交量加买卖标识为 `3` 的成交量一半                                                             |
| `buy_amount`            | `real`     | 主买金额             | 主买成交金额加买卖标识为 `3` 的成交金额一半                                                         |
| `sale_vol`              | `real`     | 主卖量               | 主卖成交量加买卖标识为 `3` 的成交量一半                                                             |
| `sale_amount`           | `real`     | 主卖金额             | 主卖成交金额加买卖标识为 `3` 的成交金额一半                                                         |
| `w_buy`                 | `real`     | 委买                 | 一个周期内的委买量                                                                                  |
| `w_sale`                | `real`     | 委卖                 | 一个周期内的委卖量                                                                                  |
| `sectional_buy_vol`     | `real`     | 时点当日累计主买量   | 从开盘到当前时间的主买量之和                                                                        |
| `sectional_buy_amount`  | `real`     | 时点当日累计主买金额 | 从开盘到当前时间的主买金额之和                                                                      |
| `sectional_sale_vol`    | `real`     | 时点当日累计主卖量   | 从开盘到当前时间的主卖量之和                                                                        |
| `sectional_sale_amount` | `real`     | 时点当日累计主卖金额 | 从开盘到当前时间的主卖金额之和                                                                      |
| `sectional_w_buy`       | `real`     | 时点当日累计委买     | 从开盘到当前时间的委买量之和                                                                        |
| `sectional_w_sale`      | `real`     | 时点当日累计委卖     | 从开盘到当前时间的委卖量之和                                                                        |
| `sectional_yclose`      | `real`     | 前日收盘             | 上一交易日收盘价，与 `yclose` 的“上一周期收盘价”不同                                                |
| `sectional_open`        | `real`     | 时点当日开盘         | 今天开盘价                                                                                          |
| `sectional_high`        | `real`     | 时点当日最高         | 从开盘到当前时间的最高价                                                                            |
| `sectional_low`         | `real`     | 时点当日最低         | 从开盘到当前时间的最低价                                                                            |
| `sectional_vol`         | `real`     | 时点当日累计成交量   | 从开盘到当前时间成交量之和                                                                          |
| `sectional_amount`      | `real`     | 时点当日累计成交金额 | 从开盘到当前时间成交金额之和                                                                        |
| `sectional_cjbs`        | `int`      | 时点当日累计成交笔数 | 股票为成交笔数；股指期货/期权可表示市场现有持仓量                                                   |
| `sectional_wb`          | `real`     | 时点当日累计委比     | 当日累计委买/当日累计委卖                                                                           |

## 数据类型转换规则

| TSL          | Python             | TSL 转 Python               | Python 转 TSL                |
| ------------ | ------------------ | --------------------------- | ---------------------------- |
| 数值型       | `float` / `int`    | 自动转换                    | 自动转换                     |
| 字符串       | `str`              | 自动转换                    | 自动转换                     |
| 布尔型       | `bool` / `int`     | 自动转换                    | 自动转换为整数语义           |
| 日期时间实数 | `datetime`         | `pyTSL.DoubleToDatetime()`  | `pyTSL.DatetimeToDouble()`   |
| `array`      | `pandas.DataFrame` | `TSResultValue.dataframe()` | `pyTSL.DataFrameToTSArray()` |
| TSL 流       | Python 变量        | `pyTSL.DecodeStream()`      | `pyTSL.EncodeStream()`       |

TSL 日期时间是一个实数，例如 `20240730.145533T` 对应日期和时间；`Client.call()` 中的日期时间参数可直接传 Python `datetime`，pyTSL 会自动转换。

## `pyTSLPy` 兼容模块

`pyTSLPy` 是用 `pyTSL` 实现旧版 `TSLPy3.pyd` 功能的兼容模块。安装命令：

```bash
pip install pyTSLPy
```

迁移入口：

```python
import pyTSLPy as ts
```

支持情况：

| 旧接口                                          | 支持情况   | 说明                                          |
| ----------------------------------------------- | ---------- | --------------------------------------------- |
| `DefaultConnectAndLogin`                        | 支持       | 通过配置文件登录                              |
| `ConnectServer`                                 | 支持       | 连接服务器                                    |
| `LoginServer`                                   | 支持       | 登录用户                                      |
| `Disconnect`                                    | 支持       | 断开连接                                      |
| `Logined`                                       | 支持       | 判断是否连接                                  |
| `SetService`                                    | 支持       | 设置服务器类型                                |
| `GetService`                                    | 支持       | 获得服务器类型                                |
| `RemoteExecute`                                 | 不完全支持 | 不支持本地交互                                |
| `RemoteCallFunc`                                | 不完全支持 | 不支持本地交互                                |
| `EncodeDate` / `EncodeTime` / `EncodeDateTime`  | 支持       | 在 Python 中构造 TSL 日期、时间、日期时间     |
| `DecodeDate` / `DecodeTime` / `DecodeDateTime`  | 支持       | 将 TSL 日期、时间、日期时间转换到 Python 数组 |
| `SetComputeBitsOption` / `GetComputeBitsOption` | 暂不支持   | 计算服务器位数相关                            |
| `SetSysParam` / `GetSysParam`                   | 暂不支持   | 系统参数相关                                  |

如果旧代码使用本地交互函数，`pyTSLPy` 只直接支持 `ExportFile` 和 `ImportFile`；其他本地交互函数应通过 `pyTSL.register_proc()` 注册。

## 常见风险

- `import pyTSL` 失败并提示找不到模块时，Windows 先确认 VC 运行库。
- 旧版本出现字符串对象分配错误时，先升级 pyTSL。
- MacOS 导入报签名或权限问题时，需要按系统权限策略放开对应模块。
- `Client(ini_file)` 路径含中文时，可传入 GBK 编码后的路径。
- `logout()` 后再次执行 `Client` 方法可能自动重新登录；生成长期任务时遵循一次登录、多次交互。
- pyTSL 返回值编码为 UTF-8，不需要再手动从 GBK 转码。
- 使用代理时，代理需要支持 HTTP CONNECT；只支持应用层代理的环境需要使用官方协议网关方案。
- Linux 旧版本在子进程场景可能出现网络线程初始化问题；应使用较新 pyTSL。
- 非中国时区下日期时间转换有偏差时，使用 `DatetimeToDouble(datetime, timezone)` 显式指定时区。

## 常用示例

### 执行 TSL 代码串

```python
import pyTSL

c = pyTSL.Client("user", "password", "tsl.tinysoft.com.cn", 443)
try:
    if c.login() == 0:
        raise RuntimeError(c.last_error())

    r = c.exec("return 1 + 1;")
    if r.error():
        raise RuntimeError(r.message())
    print(r.value())
finally:
    c.logout()
```

### 查询行情并转 DataFrame

```python
import pyTSL
from datetime import datetime

c = pyTSL.Client("user", "password", "tsl.tinysoft.com.cn", 443)
try:
    if c.login() == 0:
        raise RuntimeError(c.last_error())

    r = c.query(
        stock=["SZ000001", "SZ000002"],
        cycle="日线",
        begin_time=datetime(2020, 1, 1),
        end_time=datetime(2020, 3, 20),
        fields="StockID,StockName,date,close",
    )
    if r.error():
        raise RuntimeError(r.message())
    df = r.dataframe()
finally:
    c.logout()
```

### 通过 `code` 临时定义并调用 TSL 函数

```python
import pyTSL
from datetime import date

tsl_code = """
function getValue(begt, endt);
begin
    n := tradeDays(begt, endt);
    return nday(n, "date", dateToStr(spTime()), "close", close(), "vol", vol(), "amount", amount());
end;
"""

c = pyTSL.Client("user", "password", "tsl.tinysoft.com.cn", 443)
try:
    if c.login() == 0:
        raise RuntimeError(c.last_error())

    r = c.call("getValue", date(2019, 4, 10), date(2019, 4, 18), code=tsl_code, stock="SH000001", cycle="日线")
    if r.error():
        raise RuntimeError(r.message())
    df = r.dataframe()
finally:
    c.logout()
```

### 并发任务

```python
import pyTSL

c = pyTSL.Client("user", "password", "tsl.tinysoft.com.cn", 443)
try:
    if c.login() == 0:
        raise RuntimeError(c.last_error())

    batch = pyTSL.Batch(3, 0)
    for stock in ["SH600028", "SH600030", "SH600036"]:
        ok, task = batch.exec(c, "return stockMarketValue(20240410T);", stock=stock, key=stock)
        if ok == 0:
            print(task)

    for r in iter(batch):
        if r.error():
            print(r.key(), r.message())
        else:
            print(r.key(), r.value())
finally:
    c.logout()
```
