neuroalpha 0.5.34 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs//320/234/320/225/320/245/320/220/320/235/320/230/320/232/320/220_/320/230_/320/237/320/220/320/240/320/220/320/234/320/225/320/242/320/240/320/253.md +26 -3
- package/package.json +1 -1
- package/public/app.js +10 -3
- package/public/index.html +4 -2
- package/server/api/routes.js +2 -1
- package/server/brokers/index.js +185 -70
- package/server/data/lqdt.js +28 -0
- package/server/model/edge.js +12 -0
- package/server/model/forecast.js +8 -2
- package/server/model/policy.js +193 -0
- package/server/model/research.js +50 -14
- package/server/model/simulator.js +366 -0
- package/server/model/walkforward.js +59 -0
- package/server/strategist/tools.js +2 -0
|
@@ -146,10 +146,33 @@ $$\text{Edge}_i = \hat{y}_i \cdot \sigma_i \cdot \sqrt{H}$$
|
|
|
146
146
|
* `Автомат` — автоматическое двухфазное выставление лимитных заявок (сначала закрывающие `sell/cover`, затем через `1,2 с` открывающие `buy/short`);
|
|
147
147
|
* `Тестовый режим (песочница брокера)` — торговля виртуальными деньгами на тестовом контуре брокера без риска.
|
|
148
148
|
2. **Лимит капитала на стратегию (`maxCapitalRub`):** Стратегия оперирует строго выделенной суммой (в пределах стартового лимита тарифа + 100% реинвестирование всей заработанной прибыли).
|
|
149
|
-
3. **Дневной стоп по просадке (`maxDailyDrawdownPct`):** Если оценка счёта за текущий день снизилась от утреннего баланса на заданный порог (по умолчанию `−3%`, с подтверждением на двух синхронизациях подряд),
|
|
149
|
+
3. **Дневной стоп по просадке (`maxDailyDrawdownPct`):** Если оценка счёта за текущий день снизилась от утреннего баланса на заданный порог (по умолчанию `−3%`, с подтверждением на двух синхронизациях подряд), до следующего торгового дня или ручного запуска запрещаются **новые входы**. Открытые позиции продолжают сопровождаться: выходы по сигналу, фиксация прибыли, аварийные стопы и закрытие перед ночью работают.
|
|
150
150
|
4. **Только лимитные заявки с точным шагом цены (`limitOffsetPct`):** Заявки выставляются от живой цены с защитным лимитным отступом (по умолчанию `0.1%`), округлённым строго по шагу цены (`MINSTEP` / `minPriceIncrement`) конкретной бумаги.
|
|
151
|
-
5. **Защита краёв сессии (`skipEdgeMinutes`):** В
|
|
152
|
-
6.
|
|
151
|
+
5. **Защита краёв сессии (`skipEdgeMinutes`):** В окна `06:50–07:25`, `09:50–10:10` и `18:35–18:55` МСК новые входы не открываются и фиксация прибыли не срабатывает на шуме стакана; аварийные стопы работают всегда.
|
|
152
|
+
6. **Аварийный стоп позиции — 7% от средней цены входа** (у брокера стоп-заявкой и во внутреннем контуре). Модель торгует возврат к среднему, поэтому стоп стоит широко: проверка на истории показала, что прежние 4,5–5% закрывали позиции на пике отклонения и снижали итог на обеих половинах периода.
|
|
153
|
+
6.1. **Плечо (`maxLeverage`, настройка счёта):** множитель размера позиций в ликвидных бумагах 1-го эшелона и предел суммарной позиции (× капитал). Бумаги 2–3-го эшелона всегда торгуются половинным размером. Работает только при включённой маржинальной торговле; тариф «Инвестор» — без плеча, «Профи» — до 3, «VIP» и «Хозяин» — до 7. Предел заявки дополнительно проверяет брокер (`GetMaxLots`, маржинальный лимит).
|
|
154
|
+
6.2. **Стоп по общей просадке (`maxDrawdownPct`, настройка счёта, 0 — выкл.):** если оценка счёта опустилась от своего максимума на заданный процент, новые входы запрещаются до ручного снятия («Снять стоп по просадке»); открытые позиции сопровождаются. После вывода денег стоп снимается вручную.
|
|
155
|
+
6.3. **Капитал в LQDT как залог** (при включённых «Свободные рубли в LQDT» и маржинальной торговле): паи LQDT не продаются под входы — внутридневные позиции открываются под обеспечение и закрываются до ночи (платы за перенос нет), а свободные рубли размещаются в LQDT, когда у стратегии нет открытых позиций.
|
|
156
|
+
6.4. **Вход против планки:** если брокер отклонил вход из-за ценового коридора Мосбиржи (код 30099 — бумага у планки), вход в эту бумагу в ту же сторону блокируется до конца торгового дня.
|
|
157
|
+
7. **Устаревшие пакеты заявок:** пакет, подтверждённый в Telegram/MAX позже чем через 10 минут, вне окна входов или в окно начала сессии, отправляется без входов (только выходы).
|
|
158
|
+
8. **Три режима обращения с начальным портфелем (`legacyMode`):**
|
|
153
159
|
* **«Изолированный карман» (`isolate`, по умолчанию):** система запоминает ваши старые позиции (`baselineQty`) и никогда их не продаёт.
|
|
154
160
|
* **«Плавный переход» (`smooth`):** старые акции с отрицательным/слабым прогнозом ИИ постепенно расчищаются, высвобождая рублёвый кэш как под новые покупки, так и под новые шорты.
|
|
155
161
|
* **«Полное управление» (`takeover`):** все нецелевые акции из словаря стратегии сразу приводятся к модельному портфелю (облигации, фонды и бумаги вне словаря не затрагиваются никогда).
|
|
162
|
+
|
|
163
|
+
### Единые правила и лаборатория правил
|
|
164
|
+
|
|
165
|
+
Все правила сопровождения позиций (окна сессий, фиксация прибыли, стоп, размер позиции) находятся в одном модуле `server/model/policy.js` и используются и живым контуром, и симулятором `server/model/simulator.js`. Симулятор повторяет живой контур по 10-минутным свечам (входы на любой свече, стопы и фиксация прибыли внутри свечи, фильтры прогноза, паузы, пределы на бумагу, издержки по эшелонам). Его результат — главная оценка в разделе «Эксперименты» (строка «Правила живого контура»).
|
|
166
|
+
|
|
167
|
+
Изменение правил проверяется в лаборатории `scripts/strategy-lab.mjs` (варианты — в `scripts/strategy-lab-variants.mjs`) и принимается, только если оно лучше текущих правил **и на первой, и на второй половине периода**. Каждая отправленная заявка записывается в журнал решений `trade_decisions` (причина, ожидание и порог, контекст дня, плечо, версия программы; `GET /api/brokers/:id/decisions`).
|
|
168
|
+
|
|
169
|
+
Проверка правил 06.11.2026 (пошаговые прогнозы 06.2024–11.2026, 788 сделок, капитал 800 тыс. ₽, LQDT за период — 18,5% годовых):
|
|
170
|
+
|
|
171
|
+
| Правила | CAGR (весь период) | 1-я / 2-я половина | PF | Макс. просадка | Худшая сделка, % капитала |
|
|
172
|
+
|---|---|---|---|---|---|
|
|
173
|
+
| До 06.11.2026 (без LQDT, 2-й эшелон полным размером) | 12,9% | 19,3% / 6,8% | 1,40 | 4,4% | −2,0% |
|
|
174
|
+
| Текущие, плечо 1 | 31,5% | 38,0% / 25,6% | 1,55 | 2,2% | −2,0% |
|
|
175
|
+
| Текущие, плечо 3 | 52,1% | 56,2% / 48,5% | 1,87 | 7,0% | −6,8% |
|
|
176
|
+
| Текущие, плечо 5 | 75,2% | 76,0% / 74,9% | 2,02 | 12,5% | −12,9% |
|
|
177
|
+
|
|
178
|
+
Отклонены (хуже хотя бы на одной половине или с катастрофическим хвостом): защита вечным фьючерсом IMOEXF (издержки больше выигрыша), пассивные лимитные входы, снижение порога входа, 6–8 позиций на сторону, выход по времени, отключение фиксации прибыли, стоп по волатильности и «стоп отмены идеи», размер позиции по риску. Результат на истории не гарантирует будущий: плечо пропорционально увеличивает и доходность, и просадки.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neuroalpha",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "НейроАльфа — ИИ-автопилот для рынка акций Мосбиржи: эмбеддинги акций, самообучение правил, коннекторы Т-Банк / БКС / Финам / Алор и боты Telegram / MAX",
|
|
5
5
|
"author": "Roman A. Perepelkin",
|
|
6
6
|
"license": "UNLICENSED",
|
package/public/app.js
CHANGED
|
@@ -563,13 +563,13 @@ function renderStress(sx) {
|
|
|
563
563
|
if (!sx?.scenarios?.length) { box.hidden = true; return; }
|
|
564
564
|
box.hidden = false;
|
|
565
565
|
const base = sx.scenarios.find((s) => s.key === 'base');
|
|
566
|
-
const real = sx.scenarios.find((s) => s.key === 'realistic');
|
|
566
|
+
const real = sx.scenarios.find((s) => s.key === 'liveRules') || sx.scenarios.find((s) => s.key === 'realistic');
|
|
567
567
|
const verdict = !real ? '' : real.full.returnPct > (sx.market?.full ?? 0) && (real.full.profitFactor ?? 0) > 1.1
|
|
568
568
|
? '<span class="tag ok">Держится при реалистичном исполнении</span>'
|
|
569
569
|
: real.full.returnPct > 0 ? '<span class="tag mid">При реалистичном исполнении преимущество слабое</span>'
|
|
570
570
|
: '<span class="tag no">При реалистичном исполнении — убыток</span>';
|
|
571
571
|
const cell = (v, d = 1, pct = true) => (v === null || v === undefined ? '<span class="muted">—</span>' : `<span class="${pct ? cls(v) : ''}">${pct ? fmtPct(v, d) : fmtNum(v, d)}</span>`);
|
|
572
|
-
const rows = sx.scenarios.map((s) => `<tr${s.key ===
|
|
572
|
+
const rows = sx.scenarios.map((s) => `<tr${s.key === real?.key ? ' style="font-weight:600"' : ''}>
|
|
573
573
|
<td title="${esc(s.note || '')}">${esc(s.label)}</td>
|
|
574
574
|
<td class="num">${cell(s.full.returnPct)}</td><td class="num">${cell(s.full.cagrPct)}</td><td class="num">${cell(s.full.afterTaxCagrPct)}</td>
|
|
575
575
|
<td class="num">${cell(s.full.profitFactor, 2, false)}</td><td class="num">${cell(s.full.maxDrawdownPct === null ? null : -s.full.maxDrawdownPct)}</td>
|
|
@@ -2925,6 +2925,7 @@ function renderBrokers() {
|
|
|
2925
2925
|
<span class="tag ${isPaused ? '' : a.mode === 'auto' ? 'mid' : 'ok'}">${isPaused ? '⏸ на паузе' : esc(BROKER_MODES[a.mode] || a.mode)}</span>
|
|
2926
2926
|
${a.sandbox ? '<span class="tag mid">песочница</span>' : ''}
|
|
2927
2927
|
${a.haltedReason ? `<span class="tag no">${esc(a.haltedReason)}</span>` : ''}
|
|
2928
|
+
${a.haltedReason && a.haltedReason.startsWith('Сработал стоп по общей просадке') ? `<button type="button" class="brk-toggle" data-btrade="resetstop" data-bid="${esc(a.id)}" title="Снять стоп по общей просадке: отсчёт просадки начнётся заново от текущей оценки счёта">Снять стоп по просадке</button>` : ''}
|
|
2928
2929
|
${a.haltedReason && a.haltedReason.startsWith('Сработал дневной стоп') ? `<button type="button" class="brk-toggle" data-btrade="resetstop" data-bid="${esc(a.id)}" title="Снять дневной стоп по просадке вручную — до конца сегодняшнего дня он больше не сработает">Снять стоп до конца дня</button>` : ''}
|
|
2929
2930
|
${collapsedMetricsHtml}
|
|
2930
2931
|
</div>
|
|
@@ -2950,7 +2951,7 @@ function renderBrokers() {
|
|
|
2950
2951
|
return `<span class="k">Доход / убыток по периодам:</span><span class="v" title="Чистый результат счёта (закрытые сделки после комиссий + дивиденды/купоны; за сегодня — с учётом дневной переоценки портфеля)">${fmtP('сегодня', pr.day)} · ${fmtP('7 дн.', pr.week)} · ${fmtP('30 дн.', pr.month)} · ${fmtP('с начала года', pr.ytd)}</span>`;
|
|
2951
2952
|
})() : ''}
|
|
2952
2953
|
<span class="k">Контроль автоследования:</span><span class="v">${ls ? (ls.autoFollowDetected ? '<b class="neg">обнаружено ✕ (торговля заблокирована)</b>' : '<b class="pos">не используется ✓ (счёт допущен)</b>') : '<span class="muted">не проверено</span>'}</span>
|
|
2953
|
-
<span class="k">Предохранители и настройки:</span><span class="v">${capSpecHtml} · дневной стоп <b>−${fmtNum(a.maxDailyDrawdownPct, 1)}%</b> · отступ лимитки <b>${fmtNum(a.limitOffsetPct, 2)}%</b>${a.broker === 'tbank' ? ` · стоп-заявки у брокера: <b>${a.brokerStops ? `<span class="pos">вкл.</span> (${Object.keys(a.brokerStopBySymbol || {}).length} активн.)` : 'выкл.'}</b> · кэш в LQDT: <b>${a.cashSweep ? '<span class="pos">вкл.</span>' : 'выкл.'}</b>` : ''}${a.baseRunLabel ? ` · эксперимент <b>${esc(a.baseRunLabel)}</b>` : ''}</span>
|
|
2954
|
+
<span class="k">Предохранители и настройки:</span><span class="v">${capSpecHtml} · дневной стоп <b>−${fmtNum(a.maxDailyDrawdownPct, 1)}%</b> · плечо <b>${fmtNum(a.maxLeverage || 1, 1)}×</b> · стоп по общей просадке <b>${a.maxDrawdownPct > 0 ? `−${fmtNum(a.maxDrawdownPct, 0)}%` : 'выкл.'}</b> · отступ лимитки <b>${fmtNum(a.limitOffsetPct, 2)}%</b>${a.broker === 'tbank' ? ` · стоп-заявки у брокера: <b>${a.brokerStops ? `<span class="pos">вкл.</span> (${Object.keys(a.brokerStopBySymbol || {}).length} активн.)` : 'выкл.'}</b> · кэш в LQDT: <b>${a.cashSweep ? '<span class="pos">вкл.</span>' : 'выкл.'}</b>` : ''}${a.baseRunLabel ? ` · эксперимент <b>${esc(a.baseRunLabel)}</b>` : ''}</span>
|
|
2954
2955
|
${a.executionAudit ? `<span class="k">Реальные издержки (сверка):</span><span class="v" title="Сверка фактических сделок счёта (после отключения автоследования) с ориентиром стресс-теста">комиссия <b>${fmtNum(a.executionAudit.commPct, 3)}%</b>${a.executionAudit.slipAllPct !== null ? ` · проскальзывание к сигналу <b>${a.executionAudit.slipAllPct > 0 ? '+' : ''}${fmtNum(a.executionAudit.slipAllPct, 3)}%</b>${a.executionAudit.slipTier1Pct !== null ? ` (1-й эш. ${a.executionAudit.slipTier1Pct > 0 ? '+' : ''}${fmtNum(a.executionAudit.slipTier1Pct, 3)}%${a.executionAudit.slipTier2Pct !== null ? `, 2–3-й эш. ${a.executionAudit.slipTier2Pct > 0 ? '+' : ''}${fmtNum(a.executionAudit.slipTier2Pct, 3)}%` : ''})` : ''}` : ''} · итого <b class="${a.executionAudit.totalCostPerSidePct <= a.executionAudit.breakEvenTargetPct ? 'pos' : 'neg'}">${fmtNum(a.executionAudit.totalCostPerSidePct, 3)}%/сторона</b> (порог безубыточности стресс-теста ≤ ${fmtNum(a.executionAudit.breakEvenTargetPct, 3)}%, закрыто ${a.executionAudit.closedCount} поз.${a.executionAudit.profitFactorGross !== null ? `, PF до ком. ${fmtNum(a.executionAudit.profitFactorGross, 2)}` : ''})</span>` : ''}
|
|
2955
2956
|
</div>
|
|
2956
2957
|
${ls?.ok ? `<div class="kpis">
|
|
@@ -3032,6 +3033,8 @@ function getBrokerFormSnap() {
|
|
|
3032
3033
|
legacyMode: f.legacyMode?.value || 'isolate',
|
|
3033
3034
|
maxCapitalRub: Number(f.maxCapitalRub.value) || 100000,
|
|
3034
3035
|
maxDailyDrawdownPct: Number(f.maxDailyDrawdownPct.value) || 3,
|
|
3036
|
+
maxLeverage: Number(f.maxLeverage?.value) || 1,
|
|
3037
|
+
maxDrawdownPct: Number(f.maxDrawdownPct?.value) || 0,
|
|
3035
3038
|
limitOffsetPct: Number(f.limitOffsetPct.value ?? 0.1),
|
|
3036
3039
|
autoSchedule: Boolean(f.autoSchedule.checked),
|
|
3037
3040
|
skipEdgeMinutes: Boolean(f.skipEdgeMinutes.checked),
|
|
@@ -3198,6 +3201,8 @@ function startBrokerEdit(acc) {
|
|
|
3198
3201
|
if (f.legacyMode) f.legacyMode.value = acc.legacyMode || 'isolate';
|
|
3199
3202
|
f.maxCapitalRub.value = acc.maxCapitalRub || 100000;
|
|
3200
3203
|
f.maxDailyDrawdownPct.value = acc.maxDailyDrawdownPct || 3;
|
|
3204
|
+
if (f.maxLeverage) f.maxLeverage.value = acc.maxLeverage || 1;
|
|
3205
|
+
if (f.maxDrawdownPct) f.maxDrawdownPct.value = acc.maxDrawdownPct || 0;
|
|
3201
3206
|
f.limitOffsetPct.value = acc.limitOffsetPct ?? 0.1;
|
|
3202
3207
|
f.autoSchedule.checked = acc.autoSchedule !== false;
|
|
3203
3208
|
f.skipEdgeMinutes.checked = acc.skipEdgeMinutes !== false;
|
|
@@ -3298,6 +3303,8 @@ brokerForm.addEventListener('submit', async (e) => {
|
|
|
3298
3303
|
baseRunId: null,
|
|
3299
3304
|
maxCapitalRub: Number(f.maxCapitalRub.value) || 100000,
|
|
3300
3305
|
maxDailyDrawdownPct: Number(f.maxDailyDrawdownPct.value) || 3,
|
|
3306
|
+
maxLeverage: Number(f.maxLeverage?.value) || 1,
|
|
3307
|
+
maxDrawdownPct: Number(f.maxDrawdownPct?.value) || 0,
|
|
3301
3308
|
limitOffsetPct: Number(f.limitOffsetPct.value ?? 0.1),
|
|
3302
3309
|
autoSchedule: f.autoSchedule.checked,
|
|
3303
3310
|
skipEdgeMinutes: f.skipEdgeMinutes.checked,
|
package/public/index.html
CHANGED
|
@@ -146,11 +146,13 @@
|
|
|
146
146
|
</label>
|
|
147
147
|
<label><span>Лимит суммы на стратегию, ₽ (предохранитель)</span><input name="maxCapitalRub" type="number" min="10000" step="10000" value="100000"></label>
|
|
148
148
|
<label><span>Дневной стоп по просадке, % (предохранитель)</span><input name="maxDailyDrawdownPct" type="number" min="0.5" max="50" step="0.5" value="3"></label>
|
|
149
|
+
<label title="Множитель размера позиций в ликвидных бумагах 1-го эшелона и предел суммарной позиции (× капитал). 1 — без плеча. Нужна включённая маржинальная торговля на счёте. Доходность и просадки растут примерно пропорционально: на истории 06.2024–11.2026 при плече 3 худшая сделка стоила ~7% капитала, при плече 5 — ~13%"><span>Плечо (1 — без плеча; «Профи» до 3, «VIP» до 7)</span><input name="maxLeverage" type="number" min="1" max="7" step="0.5" value="1"></label>
|
|
150
|
+
<label title="Если оценка счёта опустилась от своего максимума на этот процент, новые входы запрещаются до ручного снятия стопа (открытые позиции сопровождаются). 0 — выключено. После вывода денег со счёта стоп снимается вручную"><span>Стоп по общей просадке от максимума, % (0 — выкл.)</span><input name="maxDrawdownPct" type="number" min="0" max="60" step="1" value="0"></label>
|
|
149
151
|
<label><span>Защитный отступ лимитной заявки, %</span><input name="limitOffsetPct" type="number" min="0" max="2" step="0.05" value="0.1"></label>
|
|
150
152
|
<label class="check"><span>Формировать заявки автоматически по закрытию свечи (с отправкой в Telegram / MAX)</span><input name="autoSchedule" type="checkbox" class="switch" checked></label>
|
|
151
153
|
<label class="check"><span>Не торговать в первые и последние 10 минут сессии</span><input name="skipEdgeMinutes" type="checkbox" class="switch" checked></label>
|
|
152
|
-
<label class="check" id="broker-stops-row" title="Т-Банк: на каждую позицию стратегии выставляется стоп-лосс у брокера (
|
|
153
|
-
<label class="check" id="broker-cash-sweep-row" title="Т-Банк:
|
|
154
|
+
<label class="check" id="broker-stops-row" title="Т-Банк: на каждую позицию стратегии выставляется аварийный стоп-лосс у брокера (7% от средней цены). Он сработает, даже если машина выключена. Перед закрытием позиции стратегией стоп снимается автоматически. Бета: проверьте на небольшой позиции"><span>Защитные стоп-заявки у брокера <span class="tag mid">бета</span></span><input name="brokerStops" type="checkbox" class="switch"></label>
|
|
155
|
+
<label class="check" id="broker-cash-sweep-row" title="Т-Банк: свободные рубли сверх 3% резерва размещаются в фонд денежного рынка LQDT (~16–18% годовых). При включённой маржинальной торговле LQDT служит залогом: внутридневные позиции открываются под обеспечение без продажи паёв (позиции закрываются до ночи, платы за перенос нет), рубли размещаются, когда у стратегии нет открытых позиций. Без маржинальной торговли нужное число паёв продаётся перед входом в акции. Ваши начальные паи LQDT (если были на счёте до включения) не затрагиваются"><span>Свободные рубли в фонд денежного рынка LQDT <span class="tag ok">+доход на кэш</span></span><input name="cashSweep" type="checkbox" class="switch"></label>
|
|
154
156
|
<label class="check"><span>Тестовый режим (песочница брокера — виртуальные деньги без риска)</span><input name="sandbox" type="checkbox" class="switch"></label>
|
|
155
157
|
<div class="row-gap" style="justify-content:space-between;flex-wrap:wrap;margin-top:4px">
|
|
156
158
|
<div class="row-gap">
|
package/server/api/routes.js
CHANGED
|
@@ -16,7 +16,7 @@ import { forecastNow, TIER1_LIQUID_SYMBOLS, symbolEdgeMult } from '../model/fore
|
|
|
16
16
|
import { profilesInfo, startProfilesRefresh, TRAITS, loadProfiles } from '../model/profiles.js';
|
|
17
17
|
import { ensureDividends, ensureCandles } from '../data/moex.js';
|
|
18
18
|
import { live } from '../model/live.js';
|
|
19
|
-
import { brokersState, saveBrokerAccount, deleteBrokerAccount, syncBrokerAccount, discoverBrokerAccounts, freezeBrokerBaseline, fetchBrokerHistory, prepareBrokerOrders, executeBrokerOrders, rejectBrokerOrders, cancelBrokerOrder, triggerBrokerFastEntryCheck, ensureBoardSpecs, resetBrokerDailyStop, listBrokerEvents, stopAllBrokerTrading, fetchTbankCandles } from '../brokers/index.js';
|
|
19
|
+
import { brokersState, saveBrokerAccount, deleteBrokerAccount, syncBrokerAccount, discoverBrokerAccounts, freezeBrokerBaseline, fetchBrokerHistory, prepareBrokerOrders, executeBrokerOrders, rejectBrokerOrders, cancelBrokerOrder, triggerBrokerFastEntryCheck, ensureBoardSpecs, resetBrokerDailyStop, listBrokerEvents, listTradeDecisions, stopAllBrokerTrading, fetchTbankCandles } from '../brokers/index.js';
|
|
20
20
|
import { listNews } from '../news/index.js';
|
|
21
21
|
import * as strategist from '../strategist/index.js';
|
|
22
22
|
import { round, MSK, DAY } from '../util.js';
|
|
@@ -641,6 +641,7 @@ export function createRouter() {
|
|
|
641
641
|
r.post('/brokers/:id/cancel-order', wrap(async (req, res) => res.json(await cancelBrokerOrder(req.params.id, req.body?.orderId || null))));
|
|
642
642
|
r.post('/brokers/:id/reset-daily-stop', wrap((req, res) => res.json(resetBrokerDailyStop(req.params.id))));
|
|
643
643
|
r.get('/brokers/:id/events', wrap((req, res) => res.json({ events: listBrokerEvents(req.params.id, req.query.limit) })));
|
|
644
|
+
r.get('/brokers/:id/decisions', wrap((req, res) => res.json({ decisions: listTradeDecisions(req.params.id, { fromTs: req.query.from, limit: req.query.limit }) })));
|
|
644
645
|
r.post('/brokers-stop-all', wrap(async (req, res) => res.json(await stopAllBrokerTrading())));
|
|
645
646
|
|
|
646
647
|
// ---------- новости (только для прогноза «сейчас» и живой проверки) ----------
|