- BrainTools - https://www.braintools.ru -

Всем привет! Я Александр Панов, разработчик TradeAPI в Финам. Работая с биржевыми данными и следя за развитием LLM-агентов, я задался вопросом — а что если дать языковой модели доступ к бирже и посмотреть, сможет ли она систематически зарабатывать? Так появился этот эксперимент [1].
Сегодня разберем автономную ИИ-торговлю, реализуем своего ИИ-трейдера, которого вы можете запустить торговать уже сейчас (на виртуальном счете «Финам Арена [2]» в 3 млн рублей и денежным призом) и покажем результаты наших запусков. В конце статьи — ссылка на полный код. Давайте приступать.
Все началось с громкого выхода Alpha Arena от nof1.ai [3] – исследовательской ИИ лаборатории, которая поставила себе амбициозную цель: создать ИИ нового поколения, обученный на финансовых данных. В рамках своего эксперимента они дали шести передовым моделям торговать криптоактивами в реальном времени. Вслед за ними похожие эксперименты запустили rockflow [4], ai4trade [5] и в том числе «Финам [1]».

Смогли ли они создать ИИ, который систематически обыгрывает рынок? Пока — нет. Исследователи из Гонконгского университета отдельно проверили [6] способность передовых моделей торговать на бирже и пришли к выводу: общий интеллект [7] не транслируется автоматически в торговую эффективность. Что мы, кстати, тоже скоро увидим на собственном опыте [8].
Так что будущее, в котором большие компании обирают простых трейдеров нескоро. Но это не повод расслабляться — скорее повод разобраться в этом и найти в этом новые возможности. Фондовый рынок в теории идеально подходит для ИИ-агентов: почти вся информация оцифрована, есть готовые API, данные структурированы. А теперь еще не нужно обучать нейросеть с нуля — знания о рынках, компаниях и макроэкономике уже сжаты в современных LLM и доступны каждому трейдеру, даже без глубокого понимания математики [9] и статистики. К тому же большие языковые модели умеют делать то, что раньше могли только опытные аналитики: обрабатывать большой поток разнородной информации, выстраивать логические цепочки и формулировать обоснованные выводы. Вопрос не в том, умна ли модель, а в том, как правильно её применить. И вот здесь начинается самое интересное.
Прежде чем запускать ИИ-тредера, нужно задать ему торговые рамки — что и как будет торговать. Вообще выбор активов и построение торговой стратегии — сама по себе нетривиальная задача, которую тоже можно решать с помощью ИИ. Но для первого запуска зафиксируем всё вручную.
Итак, агент торгует на российском фондовом рынке десятью бумагами: Сбербанк, Газпром, Яндекс, МТС, X5 Retail Group, Аэрофлот, АЛРОСА, Россети, Самолёт, ДВМП. Все голубые фишки — ликвидные, хорошо покрытые новостями, но при этом достаточно волатильные, чтобы было где зарабатывать и где ошибаться.
Торги будут проходить раз в день, под конец вечерней сессии Московской биржи. Именно в это время концентрируется основной объём и движение цен, что даёт агенту максимум информации для решения.
Задача агента — максимизировать доходность портфеля. Для этого ему доступны все ключевые источники: рыночные данные, новости, исторические цены и поиск в интернете. На их основе он должен рассуждать, строить гипотезы и принимать торговые решения — по сути, действовать как управляющий небольшим фондом.
Для реализации такого агента отлично подходит архитектура ReAct (Reasoning + Acting) — подход, в котором языковая модель чередует рассуждение и действие через внешние инструменты. Именно инструменты определяют реальные возможности агента: что он видит, как анализирует и какие решения может принимать.

Каждый инструмент — это функция с подробным описанием для модели: имя, назначение, входные параметры и формат возвращаемых данных. Именно из этих описаний LLM понимает, когда и как вызывать тот или иной инструмент. Чем точнее описание — тем предсказуемее поведение [10] агента.
Разберём каждый инструмент по отдельности.
1. Рыночные данные
Агент может запрашивать исторические данные за любой промежуток времени с нужной гранулярностью — от минутных свечей до дневных. На их основе он строит картину рынка: смотрит общий тренд, оценивает волатильность, ищет уровни. Обычно он запрашивает данные за последний месяц-полтора.
Для этого используем «Финам TradeAPI [11]» — метод исторических данных Bars [12] и текущих котировок LastQuote [13]. Получаем токен API [14] и подключаем готовый SDK [15].
Реализуем инструмент get_price() для исторических данных:
@tool
async def get_price(
symbol: Symbol, start_time: datetime, end_time: datetime, timeframe: TimeFrame
) -> BarsResponse:
"""Read OHLCV data for specified stock and datetime. Get historical information for specified stock."""
return await get_finam_client().get_bars(symbol, start_time, end_time, timeframe)
Текущие котировки можно было реализовать аналогичным инструментом, но есть смысл загружать их заранее прямо в системный промпт — так агент видит актуальные цены с самого начала без лишнего вызова. Для этого напишем вспомогательную функцию get_price():
class Price(BaseModel):
bid: Decimal
ask: Decimal
async def get_price(symbol: Symbol) -> Price:
quote = await get_finam_client().get_last_quote(symbol)
return Price(
bid=Decimal(quote.quote.bid.value),
ask=Decimal(quote.quote.ask.value)
)
2. Новости
Второй источник информации [16] — новостной поток. Агент получает свежие заголовки и краткие описания статей, на основе которых может оценить настроение рынка и отреагировать на важные события по конкретным компаниям.
В более сложной версии здесь можно добавить фильтрацию по тикерам и автоматический анализ сентимента. Но для начала обойдёмся простым решением: возвращаем все последние новости одним вызовом (это порядка 8 тыс. токенов, что вполне укладывается в контекст модели).
Используем RSS-поток Финама и библиотеку feedparser для парсинга в удобной для LLM форме:
RSS_URL = "<https://www.finam.ru/analysis/conews/rsspoint/>"
@tool
def get_news() -> list[str]:
"""Fetch latest financial news headlines from Finam RSS feed."""
response = requests.get(RSS_URL, timeout=10)
response.raise_for_status()
feed = feedparser.parse(response.text)
return [(entry.title + ". " + entry.description.split('...')[0]) for entry in feed.entries]
3. Поиск в интернете
Новости дают оперативную картину, но не всегда достаточно контекста для взвешенного решения. Поэтому агент также умеет делать точечные поисковые запросы: искать финансовую отчётность компании, разбираться в её бизнес-модели или оценивать общую ситуацию в секторе.
Для этого используем Tavily [17] — поисковый API, заточенный под нужды ИИ-агентов: он возвращает структурированные результаты с релевантными выдержками, а не сырой HTML. Удобно и экономно по токенам.
class SearchResult(BaseModel):
title: str = Field(..., description="The title of the search result.")
content: str = Field(..., description="A short description of the search result.")
class SearchResponse(BaseModel):
answer: str | None = Field(None, description="A short answer")
results: list[SearchResult]
tavily_client = TavilyClient(api_key=TAVILY_API_KEY)
@tool
def search(query: str) -> SearchResponse:
"""Use search tool to scrape and return main content information related to specified query in a structured way."""
response = tavily_client.search(query, max_results=5, topic="general", search_depth="basic", country="russia")
return SearchResponse.model_validate(response)
4. Программирование
Предоставим LLM возможность писать и запускать Python-код. Это открывает широкие возможности: подсчёт технических индикаторов, построение скользящих средних, расчёт волатильности или любые другие вычисления, которые проще выразить кодом, чем описать в промпте (идея взята из подхода CodeAct [18]).
Под капотом — базовая питоновская exec(). Благодаря персистентной сессии переменные и результаты вычислений сохраняются между вызовами инструмента в рамках одного запуска агента.
@tool
def bash_python(code: str) -> str:
"""Execute Python code in bash and return result"""
output, return_value = execute_code(code, session)
# Format result: prioritize return value over output
if return_value is not None:
result = str(return_value)
if output:
result = f"{output}\n{result}"
return result
return output
5. Торговые операции
Финальный результат, который мы ожидаем от агента — конкретные торговые решения, приносящие прибыль. Вариантов много: открытие длинных и коротких позиций, предсказание движения цены или даже модные ставки на будущие события через Polymarket. В нашем случае оставим всё просто — покупка и продажа акций.
Реализуем две функции: buy и sell. Для исполнения ордеров используем API брокера [11], но прежде чем торговать реальными средствами, рекомендую начать с демосчёта [19]. А можно вообще попробовать его с виртуальными средствами на «Финам Арена [2]» (о которой будет ниже), готовый клиент для этого в коде уже есть.
@tool
async def buy(symbol: Symbol, amount: PositiveInt) -> OrderResponse:
"""Buy stock function"""
order = OrderCreateRequest(symbol=symbol, quantity=FinamDecimal(value=str(amount)), side=Side.BUY)
return await get_arena_client().place_order(order)
@tool
async def sell(symbol: Symbol, amount: PositiveInt) -> OrderResponse:
"""Sell stock function"""
order = OrderCreateRequest(symbol=symbol, quantity=FinamDecimal(value=str(amount)), side=Side.SELL)
return await get_arena_client().place_order(order)
Итак, «руки» торгового ИИ-робота готовы. Теперь напишем прошивку — системный промпт, который определяет мышление [20] и поведение [21] агента.
Хороший системный промпт должен быть максимально ясным, конкретным и структурированным. Задаём роль («ты — управляющий портфелем»), цель («максимизировать доходность»), ограничения и важные примечания. Отдельно передаём контекст: текущее состояние портфеля и свежие котировки через написанную ранее get_price(). Составим такой Jinja-шаблон:
Вы — торговый ассистент по фундаментальному анализу акций, работающий на российских биржах (Московская биржа).
Ваш торговый график:
- Вы принимаете торговые решения в конце рабочего дня (18:00) биржи
- Текущая сессия: {{ datetime }}
- Следующее решение: через день
Ваши цели:
- Анализировать и принимать решения, используя доступные инструменты.
- Вам необходимо анализировать котировки различных акций и их доходность.
- Ваша долгосрочная цель — максимизировать доходность через данный портфель.
- Перед принятием решений собирайте как можно больше информации через инструменты поиска для помощи в принятии решений.
Стандарты анализа:
- Чётко показывайте ключевые промежуточные шаги:
- Изучайте данные о текущих позициях и котировках
- Обновляйте оценку и корректируйте веса для каждого актива (если стратегия требует)
Примечания:
- Вам не нужно запрашивать разрешение пользователя во время операций, вы можете исполнять их напрямую
- Вы ДОЛЖНЫ выполнять операции через вызов инструментов, простой вывод операций не будет принят
- Вы можете торговать ТОЛЬКО акциями из списка котировок ниже
- ПОКУПКА по цене ASK (вы платите по цене продавца)
- ПРОДАЖА по цене BID (вы получаете по цене покупателя)
СТАТУС ПОРТФЕЛЯ ({{ datetime }})
Текущие позиции:
| Тикер | Кол-во | Цена | Стоимость | P&L |
|-------|--------|------|-----------|-----|
{% for pos in positions %}| {{ pos.symbol }} | {{ pos.quantity }} | {{ "%.2f"|format(pos.current_price) }} ₽ | {{ "%.2f"|format(pos.value) }} ₽ | {{ "%+.2f"|format(pos.unrealized_pnl) }} ₽ |
{% endfor %}Денежные средства: {{ "%.2f"|format(cash) }} ₽
Общий баланс (equity): {{ "%.2f"|format(equity) }} ₽
Котировки ({{ datetime }}):
| Тикер | Компания | BID (продажа) | ASK (покупка) |
|-------|----------|---------------|---------------|
{% for q in quotes %}| {{ q.symbol }} | {{ q.name }} | {{ "%.2f"|format(q.bid) }} ₽ | {{ "%.2f"|format(q.ask) }} ₽ |
{% endfor %}
Промпт написан на русском — для наглядности. В реальных запусках лучше использовать английский: банально он компактнее и потребляет меньше токенов. Вообще каких-либо убедительных исследований на эту тему я не встречал (или может быть все-таки польский? [22])
В конечном итоге в отрендеренном виде перед каждым запуском агент получает следующее:

Компонуем агента используя фреймворк LangChain. В качестве языковой модели используем ChatOpenAI класс, который поддерживает все OpenAI-совместимые API провайдеров или локальных моделей. В моем случае это OpenRouter – единый агрегатор всех LLM через единый API-ключ.
async def build_agent_graph():
system_prompt = await render_jinja_prompt()
llm = ChatOpenAI(base_url=BASE_URL, model=f"{PROVIDER}/{MODEL}", api_key=OPENROUTER_API_KEY, temperature=0.1, max_retries=5, timeout=10)
prompt = ChatPromptTemplate.from_messages([
SystemMessage(system_prompt),
MessagesPlaceholder(variable_name="messages"),
])
tools = [bash_python, get_news, get_price, search, buy, sell]
agent = prompt | llm.bind_tools(tools)
async def call_model(state: AgentGraphState):
try:
response = await agent.ainvoke(state)
except Exception as e:
logger.error(f"Error in call_model: {e}")
response = AIMessage(content=f"Произошла ошибка при обработке запроса: {str(e)}")
return {"messages": [response]}
# Построение графа
builder = StateGraph(AgentGraphState)
builder.add_node("model", call_model)
builder.add_node("tools", ToolNode(tools, handle_tool_errors=True))
# Маршрутизация
builder.add_edge(START, "model")
builder.add_conditional_edges("model", tools_condition)
builder.add_edge("tools", "model")
return builder.compile()
Здесь в графе реализован классический ReAct цикл: рассуждение, вызов инструмента, получение результата — и снова по кругу, пока модель сама не решит остановиться. LangGraph берет на себя всю маршрутизацию, параллельный запуск инструментов, сбор результатов и обработку ошибок.
Создаем задачу cron на запуск агента каждый будний день в 18:00, под конец вечерней сессии:
0 18 * * 1-5 python src/main.py
Кстати, от того же LangChain есть классная платформа трейсинга LangSmith [23]. Подключается через env-переменные и позволяет в реальном времени видеть весь мыслительный процесс агента: о чем думал, какие инструменты вызывал, с какими параметрами, что получил в ответ и где возникли проблемы.

Агент запущен. Что по доходности?
Мы в Финам дали шести ведущим моделям торговать на российском и американском рынках — каждой по 100 000 ₽ и $10 000, с 1 февраля по 1 апреля (39 торговых дней). Подробный разбор в отдельной статье [24], покажу графики доходности.
Результаты выглядят пока не серьезно, мало статистической значимости, период небольшой. Но положительные сигналы есть: агент умеет читать новостной фон, формировать портфель и в определённые моменты обыгрывать индекс.
Следующая задача — снять ограничения: расширить набор инструментов (короткие позиции, опционы, фьючерсы, деривативы), добавить риск-менеджмент и дать агенту возможность самостоятельно искать возможности на всём рынке.
Мы на личном примере убедились, что задача не в том, чтобы взять умную модель — а в том, чтобы построить систему с этой умной моделью. Вот несколько идей, что делать дальше:
Коллективный разум [25]. Что если решение принимает не одна модель, а несколько — с разными специализациями, разными промптами, разными взглядами на рынок?
Гибридный подход. Сейчас ИИ-агент — медленная вдумчивая система: анализирует, рассуждает, принимает решения раз в день. Классические алгоритмы быстрее и точнее в узких задачах. Что если создать гибрид таких систем?
Продвинутые инструменты: Анализ сентиментов новостей, технические индикаторы, стоп-лосс и тейк-профит ордера и другие инструменты трейдера применимы и здесь.
Список можно продолжать. ИИ-трейдинг — это только зарождающееся направление, где пока больше вопросов чем ответов. Но именно это делает его интересным.
Сейчас проходит конкурс по алготрейдингу — «Финам Арена [2]». Регистрируйтесь, получайте 3 млн рублей в управление через API и запускайте сегодняшнего агента! Лучшие стратегии смогут разделить призовой фонд в 300 тысяч и привлечь реальное инвестирование.
Регистрация открыта до 1 июля 2026 года, торги — с 1 июня по 1 августа.
Конкурс для клиентов брокера Финам. Открыть счёт можно здесь [26].
Код агента оставляю здесь [27]. Действуйте!
Автор: Alex_panov
Источник [28]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/30478
URLs in this post:
[1] этот эксперимент: https://habr.com/ru/companies/finam_broker/articles/1005638/
[2] Финам Арена: https://www.comon.ru/landing/finam-arena/?utm_source=habr&utm_medium=social&utm_campaign=anatomy_ai_trading
[3] nof1.ai: http://nof1.ai/
[4] rockflow: https://rockalpha.rockflow.ai/arena/r1
[5] ai4trade: https://ai4trade.ai/
[6] отдельно проверили: https://arxiv.org/abs/2512.10971
[7] интеллект: http://www.braintools.ru/article/7605
[8] опыте: http://www.braintools.ru/article/6952
[9] математики: http://www.braintools.ru/article/7620
[10] поведение: http://www.braintools.ru/article/9372
[11] Финам TradeAPI: https://api.finam.ru/?utm_source=habr&utm_medium=social&utm_campaign=anatomy_ai_trading
[12] Bars: https://api.finam.ru/docs/rest/#marketdataservice_bars?utm_source=habr&utm_medium=social&utm_campaign=anatomy_ai_trading
[13] LastQuote: https://api.finam.ru/docs/rest/#marketdataservice_lastquote?utm_source=habr&utm_medium=social&utm_campaign=anatomy_ai_trading
[14] токен API: https://tradeapi.finam.ru/docs/tokens/?utm_source=habr&utm_medium=social&utm_campaign=anatomy_ai_trading
[15] готовый SDK: https://tradeapi.finam.ru/getting-started/?utm_source=habr&utm_medium=social&utm_campaign=anatomy_ai_trading
[16] источник информации: http://www.braintools.ru/article/8616
[17] Tavily: https://tavily.com/
[18] CodeAct: https://habr.com/ru/articles/980542/
[19] демосчёта: https://www.finam.ru/landings/demoaccount/?utm_source=habr&utm_medium=social&utm_campaign=anatomy_ai_trading
[20] мышление: http://www.braintools.ru/thinking
[21] поведение: http://www.braintools.ru/article/5593
[22] или может быть все-таки польский?: https://sg.news.yahoo.com/polish-most-effective-language-prompting-193819331.html
[23] LangSmith: https://smith.langchain.com/
[24] отдельной статье: https://habr.com/ru/companies/finam_broker/news/1019984/
[25] Коллективный разум: http://www.braintools.ru/article/4995
[26] здесь: https://www.finam.ru/landings/open-order-new/?utm_source=habr&utm_medium=social&utm_campaign=product
[27] здесь: https://github.com/Alexander-Panov/ai-trader
[28] Источник: https://habr.com/ru/companies/finam_broker/articles/1036574/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1036574
Нажмите здесь для печати.