Metadata-Version: 2.4
Name: agushuju
Version: 0.3.1
Summary: 爱股数据（agushuju.com）Python SDK — tushare 亲和的 A 股数据接口
Author: agushuju
License-Expression: MIT
Keywords: agushuju,tushare,stock,futures,finance,quant
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.20
Provides-Extra: dataframe
Requires-Dist: pandas>=1.0; extra == "dataframe"
Provides-Extra: async
Requires-Dist: httpx>=0.24; extra == "async"

# agushuju — 爱股数据 Python SDK

tushare 亲和的 A 股数据接口客户端：方法名与 [爱股数据](https://www.agushuju.com) 端点一一对应，
`stock_` 前缀端点可省略前缀调用（`stock_daily` → `pro.daily()`），从 tushare 迁移只改一行 import。

v0.3.0 起模块化重构，新增：复权行情（`pro.bar`）、自动翻页（`fetch_all`/`iterate`）、
交易日历工具、错误分派体系、参数本地预检、本地缓存、异步客户端与 `agu` 命令行。

## 安装

```bash
# PyPI 安装（推荐）
pip install agushuju

# 可选增强（按需叠加）
pip install "agushuju[dataframe]"   # pandas 输出（未装则返回 list[dict]）
pip install "agushuju[async]"       # httpx 异步客户端

# 源码安装（本仓库）
cd sdk/python && pip install .
```

在线运行（`/coderunner`）的环境已在服务端预装本 SDK，可直接 `import agushuju`。

## 发版

```bash
python sdk/publish.py          # 预检+构建+校验+上传+验证（内置流程见脚本头部说明）
```

## 快速开始

```python
import agushuju as agu

pro = agu.pro_api('你的APIKey')   # https://www.agushuju.com/userapikey
# 也可自动发现凭证：环境变量 AGUSHUJU_TOKEN → ~/.agushuju/token（agu token set 写入）

# 股票日线（端点 stock_daily，tushare 亲和省略 stock_ 前缀）
# tushare 习惯参数开箱即用（SDK 自动翻译 ts_code / start_date / YYYYMMDD）：
df = pro.daily(ts_code='000001.SZ', start_date='20260101', end_date='20260901')
# 平台原生参数完全等价：
df = pro.daily(stock_code='000001.SZ', trade_date_start='2026-01-01', trade_date_end='2026-09-01')

# 指数 / 基金 / 期货（非 stock_ 前缀端点用全名）
df = pro.index_daily(ts_code='000001.SH')          # ts_code 自动按端点路由
df = pro.fund_daily(fund_code='510300.SH')
df = pro.futures_daily(futures_code='RB2601.SHF')

# 交易日历、财务指标
cal = pro.trade_cal(exchange='SSE', cal_date_start='2026-01-01', cal_date_end='2026-12-31')
fina = pro.fina_indicator(stock_code='000001.SZ', end_date_start='2026-06-30', end_date_end='2026-06-30')

# 分页
df = pro.daily(stock_code='000001.SZ', limit=100, offset=200)

# 通用原始调用（任意端点 path 直达，新端点无需 SDK 发版）
rows = pro.query('stock_moneyflow', stock_code='000001.SZ')
```

## 方法名规则

| 端点 path | SDK 方法 |
|---|---|
| `stock_daily` | `pro.daily()`（`stock_` 前缀省略） |
| `stock_trade_cal` | `pro.trade_cal()` |
| `index_daily` / `fund_daily` / `futures_daily` | `pro.index_daily()` 等全名 |
| 任意新端点 | `pro.query('<path>', **params)` |

未识别的方法名会按原样请求，服务端返回错误时抛 `ApiError` 并带上下文。

### tushare 参数兼容（v0.2.0 起；v0.2.1 扩充路由族）

方法名与参数均亲和：tushare 风格参数在请求前自动翻译为平台参数，**原生参数也完全支持**（两者可混用，显式原生参数优先）：

| tushare 写法 | SDK 自动翻译为 |
|---|---|
| `ts_code='000001.SZ'` | 按端点路由为对应代码参数：股票 `stock_code`、指数 `index_code`、基金 `fund_code`、期货 `futures_code`、可转债 `cb_code`、银行间 `bc_code`、期权 `opt_code`、黄金 `sge_code`、外汇 `fx_code`、tick/持仓类 `symbol` 等（v0.2.1 起全族覆盖） |
| `start_date='20260101'` / `end_date='20260901'` | 端点主日期列的「列名_start / 列名_end」（如 `trade_date_start`；财务指标端点映射到报告期 `end_date_start`），日期值 `YYYYMMDD` 自动转 `YYYY-MM-DD`；schema 含字面 `start_date`/`end_date` 的端点（美股 `us_stock_daily` 等）直通不改写（v0.2.1 起） |
| `period='20260630'` | 不翻译（无对应语义），请用 `end_date_start='2026-06-30', end_date_end='2026-06-30'` |

各端点可用参数见[接口文档](https://www.agushuju.com/doc)；schema 之外的参数会被服务端静默忽略（不会报错），翻译映射基于端点 schema 快照（`_PRIMARY_DATE`），新端点默认回退 `trade_date`。

## 客户端配置

```python
pro = agu.pro_api(
    'token',
    base_url='http://127.0.0.1:8181',  # 默认 https://www.agushuju.com，或环境变量 AGUSHUJU_API_BASE
    timeout=60,   # 单次请求超时（秒）
    retry=3,      # 总尝试次数上限（含首次；指数退避，网络错误/5xx 才重试）
    validate='off',  # 参数预检 'off'（默认）/'warn'/'strict'，拼写错的参数请求前暴露
    cache=False,     # True 启用本地缓存（~/.agushuju/cache.db，重复查询不扣费）
    cache_ttl=3600,  # 缓存默认过期秒数
)
```

返回值为 `pandas.DataFrame`（未安装 pandas 时为 `list[dict]`）；`df.attrs['count']` 为满足条件的总记录数（分页统计）。

## 错误处理（v0.3.0 错误分派）

按平台错误契约分派异常子类，调用方按类型精准处置；所有子类都是 `ApiError` 的实例，
`except ApiError` 的存量代码不受影响：

| 异常类 | 触发 | 处置建议 |
|---|---|---|
| `AuthError` | 401 API Key 无效/过期 | 检查 token |
| `BalanceError` | 402 余额不足 | 充值 |
| `RateLimitError` | 429 频率超限/免费额度用尽 | 读 `e.retry_after`（服务端建议等待秒数）后重试 |
| `NotFoundError` | 404 端点不存在 | 检查 path 拼写 / 端点是否下线 |
| `ParamError` | 400 参数错误 | 对照 `agu schema <端点>` 修正 |
| `ServerError` / `NetworkError` | 5xx / 网络失败 | SDK 已自动重试（指数退避），仍失败再捕获 |

```python
from agushuju import ApiError, RateLimitError, BalanceError
import time

try:
    df = pro.daily(ts_code='000001.SZ')
except RateLimitError as e:
    time.sleep(e.retry_after or 60)       # 429 不自动重试（可能是当日额度用尽），调用方决策
except BalanceError:
    raise SystemExit('余额不足，请充值')
except ApiError as e:
    print(e.code, e)
```

## 复权行情与均线（v0.3.0，tushare pro_bar 同款）

```python
# 前复权 + 均线（内部编排 stock_daily + stock_adj_factor 两端点）
df = pro.bar(ts_code='000001.SZ', start_date='20260101', adj='qfq', ma=[5, 10, 20, 60])
df = pro.bar(ts_code='000001.SZ', adj='hfq')          # 后复权
df = pro.bar(ts_code='000001.SZ')                     # 不复权（等价 pro.daily）

# 底层纯函数亦可单独使用（喂任意同构 DataFrame）
from agushuju.adj import apply_adjust, add_ma
out = apply_adjust(daily_df, factor_df, adj='qfq')
out = add_ma(daily_df, periods=(5, 20))
```

复权公式与 tushare 一致：后复权 `price × factor`；前复权 `price × factor ÷ 最新 factor`。
平台列名（`close_price` 等）与 tushare 列名（`close` 等）自动探测兼容；
输出行序与服务端一致（最新在前）。平台日线自带 `change_amount`/`change_percent`
涨跌幅列，无需客户端重算。

## 自动翻页（v0.3.0）

```python
# 全量拉取：服务端 count 驱动翻页，合并为一个 DataFrame
df = pro.fetch_all('stock_daily', stock_code='000001.SZ',
                   trade_date_start='2024-01-01', trade_date_end='2024-12-31')

# 逐页惰性处理（大区间不全量驻留内存）
for row in pro.iterate('news_announcement', stock_code='000001.SZ',
                       trade_date_start='2026-01-01', page_size=500):
    handle(row)
```

`limit/offset` 由翻页器接管（显式传入会报错）；`max_rows` 默认 10 万行保护——
忘给区间参数时防止全表翻页烧额度。

## 交易日历工具（v0.3.0）

```python
cal = pro.calendar()                       # exchange='SSE'（默认）或 'SZSE'
cal.load('2025-01-01', '2026-12-31')       # 一次拉取常驻内存
cal.is_trading_day('2026-10-01')           # False（国庆休市）
cal.prev('2026-10-09')                     # 2026-09-30（沿服务端 pretrade_date 链）
cal.next('2026-09-30')                     # 2026-10-08
cal.range('2026-01-01', '2026-03-31')      # 区间内全部交易日（升序）
```

区间外查询明确报错（不做周末近似——节假日会答错，宁可不答）。

## 异步客户端（v0.3.0，需 `pip install "agushuju[async]"`）

```python
import asyncio
from agushuju.async_client import AsyncDataApi

async def main():
    async with AsyncDataApi('你的APIKey') as pro:
        dfs = await asyncio.gather(*[
            pro.query('stock_daily', stock_code=c, trade_date_start='2026-09-01')
            for c in ('000001.SZ', '600000.SH', '300750.SZ')])
    return dfs

asyncio.run(main())
```

方法与同步版同名同参；错误分派/方言翻译/重试策略/本地缓存完全共用。

## 命令行（v0.3.0）

```bash
agu token set <APIKey>          # 保存到 ~/.agushuju/token（后续命令自动发现）
agu query stock_daily ts_code=000001.SZ start_date=20260101   # 单页预览（表格）
agu query news_announcement --limit 5 --format json
agu fetch stock_trade_cal exchange=SSE start_date=20260101 end_date=20261231 --out cal.csv
agu endpoints --pattern limit_  # 按名筛端点 + 各自参数
agu schema stock_daily          # 某端点的参数清单
agu calendar --start 2026-01-01 --end 2026-03-31
agu health
```

`key=value` 参数与 SDK 同源走方言翻译；`fetch` 自动翻页全量导出（csv/json）。

## 参数本地预检（v0.3.0）

schema 外参数会被服务端**静默丢弃**——`pro.daily(code='000001.SZ')`（应为
`stock_code`）不报错而是拉全表。开启预检后请求前暴露：

```python
pro = agu.pro_api(validate='warn')    # 警告到 stderr，请求照发（快照滞后的安全网）
pro = agu.pro_api(validate='strict')  # 抛 ParamError，请求不发（长任务前置校验）
```

预检数据源为烧入的 `_SCHEMA_SNAPSHOT`（CI 守卫与服务端逐参数双向一致）；
快照外端点整体放行（新端点无需 SDK 发版）。

## 模块结构（v0.3.0）

| 模块 | 职责 |
|---|---|
| `agushuju.client` | DataApi 同步客户端（请求编排/重试/缓存集成） |
| `agushuju.async_client` | AsyncDataApi 异步客户端（httpx 可选） |
| `agushuju.dialect` | tushare 参数方言翻译 |
| `agushuju.endpoints` | 端点烧入快照（五张表，CI 守卫数据源） |
| `agushuju.errors` | 错误分派体系（7 类异常 + 工厂） |
| `agushuju.retry` | 重试策略（指数退避 + Retry-After 尊重） |
| `agushuju.pagination` | 自动翻页（iterate/fetch_all） |
| `agushuju.adj` | 复权/均线/pro_bar 组合层 |
| `agushuju.calendar_utils` | 交易日历工具 |
| `agushuju.schema_check` | 参数本地预检（off/warn/strict） |
| `agushuju.cache` | sqlite 本地请求缓存 |
| `agushuju.upass` | token 管理（env → 文件） |
| `agushuju.cli` | `agu` 命令行 |

## 测试

```bash
python -m unittest discover -s sdk/python/tests -v   # 仓库根目录；纯函数层，不发网络请求
```

## 相关链接

- 接口文档：https://www.agushuju.com/doc
- 定价与限流：https://www.agushuju.com/pricing
- 数据源状态：https://www.agushuju.com/status
- API Key 管理：https://www.agushuju.com/userapikey
