Инвестиционные боты (почти) с нуля. Часть 1: теория и первые шаги реализации
- вторник, 13 января 2026 г. в 00:00:05

Всем привет.
В этой части мы изучим базовую теорию и перейдем к практическому применению.
К сожалению, на Хабре я не прошел песочницу с нулевой частью, поэтому вот ссылка на часть 0, где идет описание целей более подробно.
Для лиги лени добавляю краткое описание.
Цель проста как мир: получать деньги ничего не делая. Это все упрощение, но именно этого я добиваюсь. Для этого я использую свои знания в программировании чтобы создать систему, которая будет торговать на бирже и приносить стабильный доход.
Параллельно в своем цикле статей я делюсь с вами что я уже изучил и что получилось реализовать.
Чтобы успешно торговать, нужно сначала разобраться в базовых понятиях.
Фондовая биржа — это как большой онлайн-рынок (в России основная — Московская биржа, или Мосбиржа), где встречаются покупатели и продавцы ценных бумаг. Биржа обеспечивает честные сделки, показывает актуальные цены и фиксирует все операции.
Брокер — это посредник, который даёт тебе доступ на биржу. Без брокера обычный человек не может просто так купить акцию Сбера. Брокер открывает тебе счёт, берёт комиссию и исполняет твои заявки. Я выбрал Т-Банк (не реклама, почему можете узнать в части 0).
Инвестиции vs спекуляции — Инвестиции — это долгосрочное вложение (годы), чтобы получать доход от роста активов или дивидендов/купонов. Спекуляции — короткие сделки на разнице цен.
Тут интересный момент. ИИ помогает мне составлять текст и в процессе я сам также обучаюсь. Получается название цикла не совсем верное, я планирую использовать в том числе и спекуляцию для получения прибыли.
Риски и доходность — Выше потенциальный доход — выше риск. Например, депозит сейчас даёт 14–16% годовых почти без риска, а акции могут дать 20-30% или привести к потере 50%. Личный опыт показывает, что можно заработать 10% в месяц, а можно потерять 50% в день и получить заблокированные активы. Так что нужно быть аккуратным.
Диверсификация — Не клади все яйца в одну корзину. Распределяй деньги по разным активам, чтобы падение одного не обрушило весь портфель.
Портфель — Список твоих позиций: сколько акций/облигаций/валюты, текущая стоимость, прибыль/убыток.
Стакан заявок — Таблица лучших цен покупки (bid) и продажи (ask). Показывает баланс спроса/предложения.
Обычно зелёный — bids (покупатели), красный — asks (продавцы). Спред — разница между лучшими ценами.

Чем можно торговать на Мосбирже, рассмотрим только базовые инструменты:
Акции → Доля в компании. Доход от роста цены и дивидендов.
Облигации → Долговые бумаги какой-то компании или государства.
Биржевые фонды → Доля в объединении денег многих инвесторов для покупки портфеля активов, управляемый некой организацией.
Валюта → Покупка иностранной валюты.
Теперь, вооружившись теорией, перейдем к реализации на моем стеке.
Напомню что разработка ведется на платформе MarsX 3 версии (обоснование выбора можно узнать в части 0). Большая часть кода будет написана на Node.JS для интеграции используется API от ТБанка
Для того чтобы торговать в ТБанке, естественно, нужно быть их клиентом и иметь хотя бы один инвестиционный счет. Описаний того как это сделать много, а мне не платят за новых клиентов, поэтому как-нибудь сами справитесь :)
Перейдем к получению ключа. Для этого открываем страницу настроек инвестиций https://www.tbank.ru/invest/settings/ и в самом низу находим кнопку для создания нового токена.

На данном этапе нас интересует токен для песочницы, так как своими деньгами мы рис��овать пока что не готовы.


Поздравляю! Ключ создан, теперь его нужно сохранить в укромное место и потом использовать для доступа к API.
Для реального счета нужно создавать отдельный токен, на этом пока что не будем акцентироваться.
Документацию можно найти тут https://developer.tbank.ru/invest/api
В целом, ничего сложного в API нет, обычные REST запросы к внешнему сервису с токеном в заголовках запроса для авторизации.
Из интересного только способ отображение чисел, таких как цены, суммы и т.п. Для отображения числа ТБанк использует объекты с полями units и nano. units - целая часть числа, nano - дробная часть.
Причины этого понять не сложно - во многих языках программирования есть проблемы округления при работе с числами с плавающей запятой, а при работе с деньгами округлять нельзя, поэтому проще разделить их на две сущности и работать с целыми числами.
Так же стоит уточнить, для работы с большинством запросов нужно слать accountId - уникальные идентификатор счета, с которым проводятся манипуляции. Получить список этих счетов и создать новые можно с помощью API.
Для доступа к API я написал простой сервис, который оборачивает запросы к API в удобную обертку, нужно только прокинуть в него необходимые данные для конкретного запроса. Ключ доступа, идентификатор аккаунта, домен API, работа с заголовками запроса, все это делает сам сервис.
Код предназначен для MarsX 3 версии, но с небольшими исправлениями может заработать на чистом Node.JS.
В Req лежат investAccountId и investAccountIsSandbox, они парсятся из cookie запроса при каждом запросе к моему серверу.
function server() {
const currentAccountId = Req.investAccountId;
return {
// UsersService
getAccounts: async (isSandbox) => callApi('UsersService', 'GetAccounts', { status: "ACCOUNT_STATUS_UNSPECIFIED" }, isSandbox),
// OperationsService
getPortfolio: async () => callApi('OperationsService', 'GetPortfolio', { accountId: currentAccountId }),
getOperations: async () => callApi('OperationsService', 'GetOperationsByCursor', { accountId: currentAccountId }),
// OrdersService
postOrder: async (payload) => callApi('OrdersService', 'PostOrder', payload),
// SandboxService
createSandboxAccount: async (name) => callApi('SandboxService', 'OpenSandboxAccount', { name: name }, true),
closeSandboxAccount: async (accountId) => callApi('SandboxService', 'CloseSandboxAccount', { accountId }, true),
addSandboxMoney: async (props) => callApi('SandboxService', 'SandboxPayIn', props, true),
// InstrumentsService
getBonds: async () => callApi('InstrumentsService', 'Bonds', {}),
getEtfs: async () => callApi('InstrumentsService', 'Etfs', {}),
getCurrencies: async () => callApi('InstrumentsService', 'Currencies', {}),
getShares: async () => callApi('InstrumentsService', 'Shares', {}),
// MarketDataService
getOrderBook: async (figi) => callApi('MarketDataService', 'GetOrderBook', {
"depth": 50,
"instrumentId": figi
}),
}
}
async function callApi(service, method, payload, isSandboxProp = undefined) {
const isSandbox = isSandboxProp ?? Req.investAccountIsSandbox;
const domain = isSandbox ? sett.sandboxUrl : sett.url;
const token = isSandbox ? sett.sandboxToken : sett.token;
const url = `${domain}/tinkoff.public.invest.api.contract.v1.${service}/${method}`;
try {
const response = await axios.post(url, payload, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
return response.data;
} catch (error) {
throw new Error(`API error: ${error.response?.status} ${error.response?.statusText}`);
}
}Точность при работе с деньгами - это очень важно, но часто ее можно игнорировать, особенно когда идет о суммах меньше копейки, а тебе нужно отобразить баланс твоего портфеля. В данном случае можно безболезненно округлять до копеек и их показывать.
Для этого я сделал небольшой хелпер-конвертер значений во float тип данных.
function server(params) {
const units = params.units;
const nano = params.nano;
const isExactAmount = params.isExactAmount || false;
const decimal = parseInt(units) + parseInt(nano) / 1000000000;
return isExactAmount ? decimal : Number(decimal.toFixed(2));
}Сервис по работе с API песочницы и с реальными деньгами, в зависимости от выбранного аккаунта
возможность переключать аккаунты и создавать новые в песочнице
возможность добавлять деньги в аккаунт песочницы. Эх, была бы возможность так сделать на реальный счет - простым запросом пополнять баланс, без источника денег :) . На реальный счет только переводом с личного счета, смысла добавлять такую логику в систему пока что не вижу.
выбранный аккаунт и его тип сохраняются в куках браузера
просмотр портфеля по выбранному аккаунту
просмотр всех инструментов для покупки, сгруппированных по типам, с возможностью поиска
просмотр стакана для выбранного инструмента
возможность выставления заявки на покупку/продажу с выбором типа заявки (лимитная, рыночная, лучшая цена)
просмотр совершенных операций
Прошу не сильно критиковать, я не сильно люблю фронтенд, да и структура пока что не устоявшаяся чтобы тратить много времени на дизайн. Потом выделю время для приведение в порядок внешнего вида.
Логотип не существует еще, так как и названия у проекта еще нет.





Как показала практика, изначальный план по 1-2 статьи в месяц провалился. Теперь цель хотя бы 1 статья в месяц.
В следующей статье начнем рассматривать сигналы которые можно использовать для определения точек покупки и продажи. На первых шагах рассмотрим: скользящие средние, возврат к среднему, RSI.
Если у вас есть идеи что стоит рассмотреть подробнее — пишите комментарии.