> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ziplime.ru/llms.txt
> Use this file to discover all available pages before exploring further.

# Миграция с классического Zipline

> Асинхронные функции, DataFrame Polars, стили исполнения и запуск Ziplime

Ziplime сохраняет событийную модель стратегий классического Zipline, но не является его полной заменой без изменений в коде. Основные различия — асинхронные функции стратегии, DataFrame библиотеки Polars, явные стили исполнения и механизм запуска с пакетами данных и сервисами Ziplime от Финама.

Используйте эту страницу при переносе старого алгоритма Zipline в файл алгоритма Ziplime.

## Что осталось знакомым

Основная структура стратегии не изменилась:

```python theme={null}
def initialize(context):
    ...


def handle_data(context, data):
    ...
```

В Ziplime основные функции становятся асинхронными:

```python theme={null}
async def initialize(context):
    ...


async def handle_data(context, data):
    ...
```

Сохраняется и общая модель работы:

* Постоянное состояние стратегии хранится в `context`.
* Торговая логика для каждого бара размещается в `handle_data`.
* Для периодической ребалансировки используется `schedule_function`.
* Пользовательские метрики записываются через `record`.
* Для ребалансировки применяются методы целевых заявок.

## Краткая таблица преобразований

| Шаблон классического Zipline             | Шаблон Ziplime                                                          |
| ---------------------------------------- | ----------------------------------------------------------------------- |
| `from zipline.api import symbol`         | Внутри `initialize` предпочтителен `await context.symbol(...)`.         |
| `asset = symbol("SBER")`                 | `asset = await context.symbol("SBER")`                                  |
| `symbols("SBER", "GAZP")`                | `[await context.symbol(s) for s in ["SBER", "GAZP"]]`                   |
| `data.current(asset, "close")`           | `(await data.current(assets=[asset], fields=["close"]))["close"][0]`    |
| `data.history(asset, "close", 20, "1d")` | `await data.history(assets=[asset], fields=["close"], bar_count=20)`    |
| `order(asset, 100)`                      | `await context.order(asset, 100, style=MarketOrder())`                  |
| `order_target_percent(asset, 0.5)`       | `await context.order_target_percent(asset, 0.5, style=MarketOrder())`   |
| Результаты доступа к данным в pandas     | DataFrame Polars из `data.current` и `data.history`.                    |
| Синхронная `handle_data`                 | `async def handle_data(...)` и `await` перед асинхронными вызовами API. |
| `run_algorithm(...)` с данными в памяти  | `run_simulation(...)` с загруженными пакетами и сервисами Ziplime.      |

## Импорты

Алгоритмы классического Zipline часто импортируют множество функций API:

```python theme={null}
from zipline.api import (
    symbol,
    order_target_percent,
    record,
    schedule_function,
    date_rules,
    time_rules,
)
```

В Ziplime предпочтительнее напрямую вызывать методы `context` и импортировать только вспомогательные классы:

```python theme={null}
from ziplime.finance.execution import MarketOrder
from ziplime.utils.events import date_rules, time_rules
```

Пространство имён `ziplime.api` в стиле Zipline сохранено для совместимости, но прямые вызовы `context` легче читать в асинхронном коде:

```python theme={null}
asset = await context.symbol("SBER")
await context.order_target_percent(asset, 0.25, style=MarketOrder())
context.record(weight=0.25)
```

## Функции жизненного цикла

### `initialize`

Классический Zipline:

```python theme={null}
def initialize(context):
    context.asset = symbol("SBER")
```

Ziplime:

```python theme={null}
async def initialize(context):
    context.asset = await context.symbol("SBER")
```

Используйте `initialize`, чтобы найти инструменты, задать параметры стратегии, зарегистрировать функции по расписанию, подключить пайплайны и настроить ограничения.

### `handle_data`

Классический Zipline:

```python theme={null}
def handle_data(context, data):
    price = data.current(context.asset, "close")
    if price > 300:
        order_target_percent(context.asset, 1.0)
```

Ziplime:

```python theme={null}
from ziplime.finance.execution import MarketOrder


async def handle_data(context, data):
    current = await data.current(assets=[context.asset], fields=["close"])
    price = current["close"][0]

    if price > 300:
        await context.order_target_percent(
            context.asset,
            1.0,
            style=MarketOrder(),
        )
```

### `before_trading_start`

В классическом Zipline `before_trading_start` была синхронной. Сейчас Ziplime вызывает её так же.

```python theme={null}
def before_trading_start(context, data):
    context.traded_today = False
```

В текущей версии Ziplime не определяйте эту функцию через `async def` и не размещайте в ней заявки.

### `analyze`

Классический Zipline обычно передавал в `analyze` DataFrame с результатами. Ziplime также синхронно вызывает `analyze` с итоговой таблицей результатов.

```python theme={null}
def analyze(context, perf):
    print(perf.tail())
```

## Различия в работе с рыночными данными

Классический Zipline поддерживал вызовы, возвращающие скалярные значения:

```python theme={null}
price = data.current(asset, "close")
history = data.history(asset, "close", 20, "1d")
```

Ziplime ожидает списки и возвращает DataFrame Polars:

```python theme={null}
current = await data.current(
    assets=[asset],
    fields=["close", "volume"],
)

price = current["close"][0]
volume = current["volume"][0]
```

Исторические окна тоже запрашиваются асинхронно:

```python theme={null}
history = await data.history(
    assets=[asset],
    fields=["close"],
    bar_count=20,
)

mean_close = history["close"].mean()
```

Если старая стратегия использует операции pandas, преобразуйте результат Ziplime явно:

```python theme={null}
pandas_history = history.to_pandas()
```

Для нового кода предпочтительнее выражения Polars.

## Различия в работе с заявками

Классический Zipline часто допускал такие вызовы:

```python theme={null}
order(asset, 100)
order_target_percent(asset, 0.5)
```

Вызовы заявок в Ziplime асинхронные и требуют указать стиль исполнения:

```python theme={null}
from ziplime.finance.execution import MarketOrder, LimitOrder

await context.order(asset, 100, style=MarketOrder())
await context.order_target_percent(asset, 0.5, style=MarketOrder())
await context.order(asset, 100, style=LimitOrder(limit_price=300.0))
```

Методы целевых заявок не учитывают ещё не исполненные открытые заявки. Если старая стратегия многократно отправляет целевые заявки, добавьте проверку:

```python theme={null}
if not context.get_open_orders(asset):
    await context.order_target_percent(asset, 0.5, style=MarketOrder())
```

## Поиск символов

Классический Zipline обычно находил символы в базе инструментов с учётом даты симуляции.

Ziplime получает инструменты через сервис инструментов:

```python theme={null}
asset = await context.symbol("SBER")
asset = await context.symbol("SBER", mic="MISX")
asset = await context.symbol("SBER@MISX")
```

Если один тикер встречается на нескольких биржах, передайте `mic` или используйте форму `SYMBOL@MIC`.

Именованный набор инструментов загружается так:

```python theme={null}
context.moex_liquid = await context.symbols_universe("MOEX_LIQUID")
```

Имя набора должно быть заранее зарегистрировано в сервисе инструментов российского контура.

## Расписание

Классический Zipline:

```python theme={null}
def initialize(context):
    schedule_function(
        rebalance,
        date_rule=date_rules.month_end(),
        time_rule=time_rules.market_close(minutes=30),
    )


def rebalance(context, data):
    order_target_percent(context.asset, 1.0)
```

Ziplime:

```python theme={null}
from ziplime.finance.execution import MarketOrder
from ziplime.utils.events import date_rules, time_rules


async def initialize(context):
    context.schedule_function(
        rebalance,
        date_rule=date_rules.month_end(),
        time_rule=time_rules.market_close(minutes=30),
    )


async def rebalance(context, data):
    await context.order_target_percent(
        context.asset,
        1.0,
        style=MarketOrder(),
    )
```

В дневных симуляциях правила времени фактически игнорируются, потому что часы формируют только один бар за сессию. В минутных симуляциях правила времени учитываются.

## Портфель и позиции

В классическом Zipline часто использовался такой код:

```python theme={null}
amount = context.portfolio.positions[asset].amount
cash = context.portfolio.cash
```

Ziplime предоставляет итоговые показатели портфеля напрямую, но в текущей среде позиции хранятся во вложенной структуре по биржам и счетам. Используйте вспомогательные методы:

```python theme={null}
cash = context.portfolio.cash
portfolio_value = context.portfolio.portfolio_value
amount = await context.portfolio.get_asset_positions_amount(asset)
value = await context.portfolio.get_asset_positions_value(asset)
```

Для позиции по конкретному биржевому инструменту:

```python theme={null}
amount = await context.portfolio.get_exchange_asset_positions_amount(
    asset,
    exchange_name="MOEX",
)
```

Значение `exchange_name` должно совпадать с конфигурацией запуска.

## Запись метрик

Эта часть почти не отличается от Zipline:

```python theme={null}
context.record(signal=signal, cash=context.portfolio.cash)
```

Записанные значения становятся столбцами итоговой таблицы результатов.

## Перенос пайплайнов

Ziplime включает API Pipeline в стиле Zipline:

```python theme={null}
from ziplime.pipeline import Pipeline
from ziplime.pipeline.data import EquityPricing
from ziplime.pipeline.terms.factors import SimpleMovingAverage


def make_pipeline():
    sma_20 = SimpleMovingAverage(
        inputs=[EquityPricing.close],
        window_length=20,
    )
    return Pipeline(columns={"sma_20": sma_20})
```

Подключите пайплайн в `initialize`:

```python theme={null}
async def initialize(context):
    context.attach_pipeline(make_pipeline(), "signals")
```

Получите результат после инициализации:

```python theme={null}
def before_trading_start(context, data):
    context.signals = context.pipeline_output("signals")
```

Поддержка пайплайнов зависит от загрузчиков. Ценовые данные через `EquityPricing` доступны по стандартному пути; для пользовательских наборов данных нужны собственные загрузчики.

## Запуск перенесённых алгоритмов

Примеры классического Zipline часто напрямую вызывают `run_algorithm(...)` с данными pandas или именами пакетов.

Стандартный процесс в Ziplime:

1. Загрузить данные в пакет Ziplime или использовать уже загруженный пакет.
2. Загрузить пакет через `bundle_service.load_bundle(...)`.
3. Передать загруженный источник данных в `run_simulation(...)`.
4. В параметре `algorithm_file` указать перенесённый `.py`-файл стратегии.

```python theme={null}
result = await run_simulation(
    start_date=start_date,
    end_date=end_date,
    trading_calendar="XMOS",
    algorithm_file="my_migrated_algo.py",
    total_cash=10_000_000.0,
    market_data_source=market_data,
    custom_data_sources=[],
    emission_rate=datetime.timedelta(days=1),
    benchmark_asset_symbol="SBER",
    stop_on_error=True,
    asset_service=asset_service,
)
```

В примере стартовый капитал — 10 000 000 рублей. Идентификатор `"XMOS"` должен поддерживаться установленным календарём; если российский контур зарегистрировал календарь под другим именем, используйте имя из его конфигурации.

## Полный пример до и после

Классический Zipline:

```python theme={null}
from zipline.api import order_target_percent, record, symbol


def initialize(context):
    context.asset = symbol("SBER")
    context.window = 20


def handle_data(context, data):
    history = data.history(context.asset, "close", context.window, "1d")
    price = data.current(context.asset, "close")
    mean_price = history.mean()

    if price > mean_price:
        order_target_percent(context.asset, 1.0)
    else:
        order_target_percent(context.asset, 0.0)

    record(price=price, mean_price=mean_price)
```

Ziplime:

```python theme={null}
from ziplime.finance.execution import MarketOrder


async def initialize(context):
    context.asset = await context.symbol("SBER")
    context.window = 20


async def handle_data(context, data):
    history = await data.history(
        assets=[context.asset],
        fields=["close"],
        bar_count=context.window,
    )
    current = await data.current(
        assets=[context.asset],
        fields=["close"],
    )

    price = current["close"][0]
    mean_price = history["close"].mean()

    target = 1.0 if price > mean_price else 0.0

    if not context.get_open_orders(context.asset):
        await context.order_target_percent(
            context.asset,
            target,
            style=MarketOrder(),
        )

    context.record(price=price, mean_price=mean_price, target=target)
```

## Контрольный список миграции

* Замените импорты `zipline.api` прямыми вызовами `context` и импортами вспомогательных объектов Ziplime.
* Сделайте `initialize`, `handle_data` и запланированные функции асинхронными.
* Добавьте `await` при поиске инструментов, доступе к данным, размещении и отмене заявок.
* Передавайте списки в `data.current` и `data.history`.
* Перепишите операции pandas на Polars или явно вызывайте `.to_pandas()`.
* Передавайте в методы заявок стиль исполнения, например `MarketOrder()`.
* Используйте `def before_trading_start`, а не `async def before_trading_start`.
* Используйте `def analyze`, а не `async def analyze`.
* Замените прямой доступ к `portfolio.positions[asset]` вспомогательными методами портфеля.
* Для неоднозначных символов передавайте MIC-код, например `MISX`.
* Перенесите настройку запуска в `run_simulation(...)` с пакетами данных Ziplime российского контура.
