Metadata-Version: 2.5
Name: vikron-nt
Version: 0.1.0
Summary: Vikron Catalog integration compatible with NautilusTrader. Vikron independently develops and maintains this integration. Nautech Systems Pty Ltd and the NautilusTrader project have no affiliation with it and do not endorse or support it.
Project-URL: Repository, https://gitee.com/vikron-data/vikron-nt
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: numpy<3,>=1.26
Requires-Dist: packaging>=24
Requires-Dist: pyarrow<27,>=21
Provides-Extra: v1
Requires-Dist: nautilus-trader<2.0.0,>=1.226.0; extra == 'v1'
Provides-Extra: v2
Requires-Dist: nautilus-trader>=2.0.0rc6; extra == 'v2'
Description-Content-Type: text/markdown

# Vikron NT

将 Vikron Downloader 生成的 Catalog 数据接入 NautilusTrader，支持量化投研与策略回测。

项目仓库：[vikron-data/vikron-nt](https://gitee.com/vikron-data/vikron-nt)。

支持平台：Windows amd64、Linux amd64/arm64、macOS arm64。

## 独立性声明

Vikron NT 由 Vikron 独立开发和维护。本项目与 Nautech Systems Pty Ltd 及 NautilusTrader 项目均无隶属关系，也未获得其背书或支持。

Vikron independently develops and maintains this integration. Nautech Systems Pty Ltd and the NautilusTrader project have no affiliation with it and do not endorse or support it.

## Catalog 格式

| Downloader 输出格式 | 数据目录 | Python：`nautilus-trader` 依赖范围 | Rust：Nautilus crate 依赖范围 | Rust 宏参数 |
| --- | --- | --- | --- | --- |
| Catalog v1 | `vikron/catalog-v1` | `>=1.226.0,<2.0.0` | `>=0.55.0,<=0.64.0` | `v1` |
| Catalog v2 | `vikron/catalog-v2` | `>=2.0.0rc6` | `>=0.65.0` | `v2` |

根据数据目录选择对应版本。

## 支持的数据

| 数据 | 类型归属 | 类型名称（Python / Rust） | 数据来源 | 主要源字段 |
| --- | --- | --- | --- | --- |
| 报价 | Nautilus 原生 | `QuoteTick` | `book_ticker` | `bid_price`、`ask_price`、`bid_amount`、`ask_amount` |
| 成交 | Nautilus 原生 | `TradeTick` | `trades` | `id`、`side`、`price`、`amount` |
| 订单簿增量 | Nautilus 原生 | `OrderBookDelta` | `incremental_book_L2` | `is_snapshot`、`side`、`price`、`amount` |
| 标记价格 | Nautilus 原生 | `MarkPriceUpdate` | `derivative_ticker` | `mark_price` |
| 指数价格 | Nautilus 原生 | `IndexPriceUpdate` | `derivative_ticker` | `index_price` |
| 资金费率 | Nautilus 原生 | `FundingRateUpdate` | `derivative_ticker` | `funding_rate`、`funding_timestamp` |
| 强平 | Vikron 自定义 | `Liquidation` | `liquidations` | `id`、`side`、`price`、`amount` |
| 持仓量 | Vikron 自定义 | `OpenInterestUpdate` | `derivative_ticker` | `open_interest` |
| 最新价格 | Vikron 自定义 | `LastPriceUpdate` | `derivative_ticker` | `last_price` |
| 预测资金费率 | Vikron 自定义 | `PredictedFundingRateUpdate` | `derivative_ticker` | `predicted_funding_rate`、`funding_timestamp` |

## 自定义数据接入

| 官方接口 | Python v1 | Python v2 | Rust v1 | Rust v2 |
| --- | --- | --- | --- | --- |
| ParquetDataCatalog 查询 | 支持 | 支持 | 支持 | 支持 |
| BacktestNode 自动加载 | 支持 | 当前不支持 | 当前不支持 | 当前不支持 |
| BacktestEngine 手动加入数据 | 支持 | 支持 | 支持 | 支持 |

## 安装

### Python

需要 Python 3.12+。v1、v2 分别安装在独立虚拟环境中。

```sh
python -m pip install --upgrade pip

# Catalog v1
python -m pip install "vikron-nt[v1]==0.1.0"

# Catalog v2（另一个虚拟环境）
python -m pip install "vikron-nt[v2]==0.1.0"
```

### Rust

需要 Rust 1.98.1+；使用 Nautilus `0.66.0` 时需要 Rust 1.99.0+。

#### Arrow 版本配置

| Nautilus Rust 版本 | `arrow-array` / `arrow-schema` 配置 | Catalog 宏参数 |
| --- | --- | --- |
| `0.55.0–0.62.0` | `"58"` | `v1` |
| `0.63.0–0.64.0` | `"59"` | `v1` |
| `0.65.0–0.66.0` | `"59"` | `v2` |

根据上表选择 Nautilus 和 Arrow 版本，再将对应配置加入项目的 `Cargo.toml`。两个 Arrow 依赖应与 Nautilus 解析到同一版本。

#### Catalog v2

```toml
[dependencies]
vikron-nt = "0.1.0"
arrow-array = "59"
arrow-schema = "59"

nautilus-core = "=0.65.0"
nautilus-model = { version = "=0.65.0", features = ["high-precision"] }
nautilus-serialization = { version = "=0.65.0", features = ["arrow"] }
nautilus-persistence = { version = "=0.65.0", features = ["high-precision"] }
```

#### Catalog v1

使用以下依赖配置：

```toml
[dependencies]
vikron-nt = "0.1.0"
arrow-array = "59"
arrow-schema = "59"

nautilus-core = "=0.64.0"
nautilus-model = { version = "=0.64.0", features = ["high-precision"] }
nautilus-serialization = { version = "=0.64.0", features = ["arrow"] }
nautilus-persistence = { version = "=0.64.0", features = ["high-precision"] }
```

使用 Nautilus `0.55.0–0.62.0` 时，将上例中的 Nautilus 版本统一替换为所需版本，并将两个 Arrow 依赖改为 `"58"`。

#### 生成扩展类型

在自己项目的一个公共模块中调用一次，其他模块通过导入复用：

```rust
vikron_nt::define_catalog_types! {
    version: v2,
    arrow_array: ::arrow_array,
    arrow_schema: ::arrow_schema,
    pub mod vikron_data;
}
```

使用 Catalog v1 时，将 `version: v2` 改为 `version: v1`。

生成模块提供四种自定义数据类型，以及 `prepare_catalog` 和 `register_custom_types`。

请保留 `nautilus-core`、`nautilus-model` 和 `nautilus-serialization` 的依赖名，并让项目中的 Nautilus crates 使用同一版本。Arrow 依赖由用户配置，其他宏辅助依赖由 vikron 提供。

多个模块或 crate 应导入同一份生成类型。Arrow 依赖重命名后，在宏中使用对应的新路径。

## Catalog

每个进程使用前调用 `prepare_catalog(path)`，传入本地 Catalog 根目录，完成类型注册并返回绝对路径。将示例中的路径和品种替换为自己的数据。

Rust 从生成模块导入该函数，并处理返回的错误。可重复调用。

下载新增数据类型后，再次初始化目录。

### Windows

- **目录与网络盘**：支持本地目录和 UNC 路径（如 `\\server\share\vikron\catalog-v1`）。目录别名和网络盘映射由 `prepare_catalog` 自动处理；共享需要凭据时，先手动连接网络盘。
- **Python v1 精度**：Windows 上的 NautilusTrader 1.x 使用 8 字节定点数，读取 Downloader 的 16 字节数据前需转换。转换会将小数精度从 16 位舍入到 9 位，超出范围时返回错误：

  ```sh
  # 一次性全量转换（默认写到 <src>-int64，原 catalog 不动）
  python -m vikron_nt.downcast ./data/vikron/catalog-v1 ./data/vikron/catalog-v1-int64

  # 每天下载后只转新增文件（已存在的跳过）
  python -m vikron_nt.downcast ./data/vikron/catalog-v1 ./data/vikron/catalog-v1-int64 --incremental
  ```

  之后按 `price_precision=9`、`size_precision=9` 构造合约（tick 仍是 `0.1` / `0.001`）。

  也可以让 `prepare_catalog` 自动回退（第一次调用时转换，之后复用；只转新增文件）：

  ```python
  path = prepare_catalog("./data/vikron/catalog-v1", fallback="downcast")
  # 或全局生效：set VIKRON_WIDE_PRECISION_FALLBACK=downcast
  ```

  默认 `fallback="error"`。自动转换时会发出警告，副本写在原目录旁；原目录只读时写入 `%LOCALAPPDATA%\vikron\catalog-int64`。

- **Rust**：`high-precision` 在 Windows 上可正常编译，不受上面的精度限制；UNC 路径同样会自动映射盘符。

### Python · Catalog v1

```python
from vikron_nt import prepare_catalog, Liquidation
from nautilus_trader.persistence.catalog import ParquetDataCatalog

path = prepare_catalog("./data/vikron/catalog-v1")
catalog = ParquetDataCatalog(path)
symbol = "ETHUSDT-PERP.BINANCE"
events = catalog.query(Liquidation, identifiers=[symbol])
for event in events:
    print(event.data.instrument_id, event.data.price, event.data.size)
```

### Python · Catalog v2

```python
from vikron_nt import prepare_catalog, Liquidation
from nautilus_trader.model import NautilusDataType
from nautilus_trader.persistence import ParquetDataCatalog

path = prepare_catalog("./data/vikron/catalog-v2")
catalog = ParquetDataCatalog(path)
symbol = "ETHUSDT-PERP.BINANCE"
events = catalog.query_custom_data(
    NautilusDataType.Custom(Liquidation.type_name_static()),
    identifiers=[symbol],
)
for event in events:
    value = event.data
    print(value.instrument_id, value.price, value.size, value.ts_init)
```

Python v1 / v2 均可通过 `identifiers` 筛选品种（完整的 Nautilus `InstrumentId`），通过 `start`、`end` 指定时间范围（Unix 纳秒，按 `ts_init` 筛选，包含起止边界）。

### Rust · Catalog v1 / v2

以下是 v2 查询示例。示例在二进制入口生成模块；如果已经在库入口生成，请改为导入公共模块，删除重复的宏调用。

```rust
use nautilus_model::data::Data;
use nautilus_persistence::backend::parquet::catalog::ParquetDataCatalog;
vikron_nt::define_catalog_types! {
    version: v2,
    arrow_array: ::arrow_array,
    arrow_schema: ::arrow_schema,
    pub mod vikron_data;
}

use vikron_data::{Liquidation, prepare_catalog};

fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let path = prepare_catalog("./data/vikron/catalog-v2")?;
    let mut catalog = ParquetDataCatalog::new(&path, None, None, None, None);
    let identifiers = vec!["ETHUSDT-PERP.BINANCE".to_string()];
    let events = catalog.query_custom_data_dynamic(
        "VikronLiquidation", Some(&identifiers), None, None, None, None, false,
    )?;

    for event in events {
        if let Data::Custom(custom) = event {
            if let Some(value) = custom.data.as_any().downcast_ref::<Liquidation>() {
                println!("{} {} {}", value.instrument_id, value.price, value.size);
            }
        }
    }
    Ok(())
}
```

使用 v1 时，将宏参数改为 `version: v1`、目录改为 `catalog-v1`，并将 `ParquetDataCatalog` 的导入改为：

```rust
use nautilus_persistence::backend::catalog::ParquetDataCatalog;
```

自定义数据的查询名称为类型名前加 `Vikron`，例如 `VikronOpenInterestUpdate`。

## BacktestNode

### Python · v1

沿用 Python Catalog v1 示例中的 `catalog` 和 `path`，通过 Binance 官方适配器在线获取 Instrument，再写入同一 Catalog：

```python
import asyncio
from decimal import Decimal

from nautilus_trader.adapters.binance.futures.providers import BinanceFuturesInstrumentProvider
from nautilus_trader.adapters.binance.http.client import BinanceHttpClient
from nautilus_trader.common.component import LiveClock
from nautilus_trader.model.identifiers import InstrumentId
from nautilus_trader.model.objects import FIXED_PRECISION


async def fetch_instrument(symbol):
    clock = LiveClock()
    client = BinanceHttpClient(
        clock=clock, api_key=None, api_secret=None,
        base_url="https://fapi.binance.com",
    )
    provider = BinanceFuturesInstrumentProvider(client=client, clock=clock)
    instrument_id = InstrumentId.from_str(symbol)
    await provider.load_ids_async([instrument_id])
    instrument = provider.find(instrument_id)
    if instrument is None:
        raise ValueError(f"交易所未返回品种信息：{symbol}")

    # 匹配运行时精度：通常为 16 位，Windows v1 转换后为 9 位。
    # 保留交易所的价格与数量步长。
    values = instrument.to_dict(instrument)
    values.update(price_precision=FIXED_PRECISION, size_precision=FIXED_PRECISION)
    for field in ("price_increment", "size_increment"):
        values[field] = format(Decimal(values[field]), f".{FIXED_PRECISION}f")
    return type(instrument).from_dict(values)


symbol = "ETHUSDT-PERP.BINANCE"
instrument = asyncio.run(fetch_instrument(symbol))
catalog.write_data([instrument])
```

在线接口返回当前品种参数；历史回测需核对当时的步长和交易规则，已下架品种需另行提供 Instrument。未配置 API 凭证时，适配器使用默认手续费档位。

接着配置回测：

```python
from nautilus_trader.backtest.config import (
    BacktestDataConfig, BacktestEngineConfig, BacktestRunConfig, BacktestVenueConfig,
)
from nautilus_trader.backtest.node import BacktestNode

config = BacktestRunConfig(
    venues=[BacktestVenueConfig(
        name="BINANCE", oms_type="NETTING", account_type="MARGIN",
        starting_balances=["100000 USDT"],
    )],
    data=[
        BacktestDataConfig(
            catalog_path=path, data_cls="nautilus_trader.model.data:QuoteTick",
            instrument_ids=[symbol],
        ),
        BacktestDataConfig(
            catalog_path=path, data_cls="vikron_nt:Liquidation",
            instrument_ids=[symbol], client_id="VIKRON",
            metadata={"instrument_id": symbol},
        ),
    ],
    engine=BacktestEngineConfig(),  # 在此配置自己的策略。
    chunk_size=10000,              # 设置为 None 时一次性加载。
    raise_exception=True,
)
node = BacktestNode(configs=[config])
try:
    results = node.run()
finally:
    node.dispose()
```

策略订阅方式见下方 BacktestEngine 的 Python 示例；metadata 和 client ID 需与上述配置一致。

### Python · v2、Rust · v1 / v2

自定义数据请使用下方 BacktestEngine 方式。

## BacktestEngine

以下为数据接入片段：`engine` 已配置交易场所、手续费和初始资金，`strategy` 是自己的策略，`instrument` 是在线获取的品种信息，价格和数量精度需匹配 Catalog。分批加载和时间顺序由使用者管理。

### Python · v1

沿用 Catalog v1 示例中的 `events` 和 `symbol`，补上与订阅一致的 metadata，再加入 Engine：

```python
from nautilus_trader.model.data import CustomData, DataType
from nautilus_trader.model.identifiers import ClientId

kind = DataType(Liquidation, {"instrument_id": symbol})
events = [CustomData(kind, event.data) for event in events]
engine.add_instrument(instrument)
engine.add_strategy(strategy)
engine.add_data(events, client_id=ClientId("VIKRON"), sort=True)
try:
    engine.run()
finally:
    engine.dispose()
```

在策略的 `on_start` 中订阅，metadata 应与数据一致；在 `on_data` 中处理事件：

```python
from nautilus_trader.model.data import DataType
from nautilus_trader.model.identifiers import ClientId

# 放在策略的 on_start 中；symbol 为 Catalog 示例中的品种。
kind = DataType(Liquidation, {"instrument_id": symbol})
self.subscribe_data(kind, client_id=ClientId("VIKRON"))
```

### Python · v2

沿用 Catalog v2 示例中的 `events` 和 `symbol`，将数据加入 Engine：

```python
from nautilus_trader.model import ClientId

engine.add_instrument(instrument)
engine.add_strategy(strategy)
engine.add_data(events, client_id=ClientId("VIKRON"), sort=True)
try:
    engine.run()
finally:
    engine.dispose()
```

在策略的 `on_start` 中订阅，在 `on_data` 中处理事件：

```python
from nautilus_trader.model import ClientId, DataType

# 放在策略的 on_start 中；symbol 为 Catalog 示例中的品种。
kind = DataType(Liquidation.type_name_static(), {"instrument_id": symbol}, symbol)
self.subscribe_data(kind, client_id=ClientId("VIKRON"))
```

### Rust · v1 / v2

添加 `nautilus-backtest`、`nautilus-common` 和 `nautilus-trading` 依赖。使用对应的 Catalog 初始化方式后：

```rust
use nautilus_model::identifiers::ClientId;

let events = catalog.query_custom_data_dynamic(
    "VikronLiquidation", Some(&identifiers), None, None, None, None, false,
)?;
engine.add_instrument(&instrument)?;
engine.add_strategy(strategy)?;
engine.add_data(events, Some(ClientId::from("VIKRON")), true, true)?;
engine.run(None, None, None, false)?;
engine.dispose();
```

从查询结果中的 `Data::Custom(custom)` 取出 `custom.data_type.clone()`，传给自己的策略；在 `on_start` 中订阅：

```rust
use nautilus_common::actor::DataActor;

self.subscribe_data(kind, Some(ClientId::from("VIKRON")), None);
```

在策略的 `on_data` 回调中处理 `CustomData`。

## Rust 自定义类型

四种类型均包含以下公共字段：

| 字段 | Rust 类型 | 含义 |
| --- | --- | --- |
| `instrument_id` | `InstrumentId` | 完整 Nautilus 品种标识 |
| `ts_event` | `UnixNanos` | 事件时间 |
| `ts_init` | `UnixNanos` | 初始化时间 |

| Rust 类型 | 注册名称 | 额外字段 |
| --- | --- | --- |
| `Liquidation` | `VikronLiquidation` | `source_id: Option<String>`、`side: OrderSide`、`price: Price`、`size: Quantity` |
| `OpenInterestUpdate` | `VikronOpenInterestUpdate` | `open_interest: Decimal` |
| `LastPriceUpdate` | `VikronLastPriceUpdate` | `price: Price` |
| `PredictedFundingRateUpdate` | `VikronPredictedFundingRateUpdate` | `rate: Decimal`、`funding_timestamp: Option<UnixNanos>` |

`Decimal` 字段可通过字符串的 `.parse()` 构造。类型提供 `data_type(instrument_id)`，用于创建订阅的数据类型。

这些是观测数据，不会自动触发订单撮合或资金费结算。持仓量单位沿用数据源；预测资金费率与实际结算资金费率分别保留。

## 常见问题

### 查询提示 custom data type is not registered？

确认已在查询前成功调用生成模块的 `prepare_catalog` 或 `register_custom_types`，并处理返回的错误。检查查询名称是否使用 `Vikron` 前缀，以及数据目录和宏参数是否对应。每个进程都需要独立注册。

### 为什么查询成功，但 downcast_ref 返回 None？

确认注册时使用的类型与向下转型的类型来自同一个生成模块。重复展开宏或多个共享适配 crate 的副本，都可能产生不同的类型身份。

## 许可证

本库采用 [MIT 许可证](./LICENSE)。Vikron Downloader、NautilusTrader 和行情数据分别适用各自的许可或授权条款。
