neuroalpha 0.5.4 → 0.5.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neuroalpha",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "НейроАльфа — ИИ-автопилот для рынка акций Мосбиржи: эмбеддинги акций, самообучение правил, коннекторы Т-Банк / БКС / Финам / Алор и боты Telegram / MAX",
5
5
  "author": "Roman A. Perepelkin",
6
6
  "license": "UNLICENSED",
@@ -354,7 +354,7 @@ function computeExecutionAudit(a) {
354
354
  slipTier1Pct,
355
355
  slipTier2Pct,
356
356
  totalCostPerSidePct,
357
- breakEvenTargetPct: 0.229,
357
+ breakEvenTargetPct: 0.237,
358
358
  grossRub,
359
359
  feesRub: round(feesRub, 0),
360
360
  netRub,
@@ -1498,20 +1498,10 @@ export async function prepareBrokerOrders(id, { onlyEntries = false } = {}) {
1498
1498
  const rawTopK = Number(params.modelTopK ?? 2);
1499
1499
  const autoK = rawTopK === 0 || autoAdapt;
1500
1500
  const K = rawTopK > 0 ? rawTopK : 4;
1501
- const filterBrokerDynamicTopK = (cands, needVal, maxCount) => {
1501
+ // Отбор до K кандидатов, прошедших абсолютный порог выгоды (единый с бэктестом в research.js)
1502
+ const filterBrokerDynamicTopK = (cands, _needVal, maxCount) => {
1502
1503
  if (!cands.length || maxCount <= 0) return [];
1503
- const lim = Math.min(cands.length, maxCount);
1504
- if (!autoK || lim <= 1) return cands.slice(0, lim);
1505
- const bestE = Math.abs(cands[0].expectedPct || 0);
1506
- const out = [cands[0]];
1507
- for (let m = 1; m < lim; m++) {
1508
- const curE = Math.abs(cands[m].expectedPct || 0);
1509
- const relMin = m === 1 ? 0.62 : 0.78;
1510
- const absMin = m === 1 ? needVal * 1.15 : needVal * 1.45;
1511
- if (curE >= bestE * relMin && curE >= absMin) out.push(cands[m]);
1512
- else break;
1513
- }
1514
- return out;
1504
+ return cands.slice(0, Math.min(cands.length, maxCount));
1515
1505
  };
1516
1506
  // Автоматическая подстройка направления и мощности торговли под тариф и реальные данные счёта:
1517
1507
  const marginAllowed = acc.lastSync?.marginEnabled !== false;
@@ -480,11 +480,13 @@ function predictAttention(m, f, u) {
480
480
  s += ML[mi].b[i] * zz;
481
481
  }
482
482
  // Равный ансамбль 50/50 с базовой моделью своей инерции (при наличии весов inertiaCoef):
483
- // без подгонки весов объединяет переток по карте соседей и собственный импульс/возврат акции
483
+ // если внимание к соседям и собственная инерция смотрят в разные стороны (s * sIn < 0),
484
+ // ослабляем сигнал вдвое, чтобы не проходить порог входа на конфликте моделей, но не искажать ранговый порядок IC!
484
485
  if (m.inertiaCoef) {
485
486
  let sIn = 0;
486
487
  for (let q = 0; q < 3; q++) sIn += m.inertiaCoef[q] * f.X[0][q][k];
487
- s = 0.50 * s + 0.50 * sIn;
488
+ const mix = 0.50 * s + 0.50 * sIn;
489
+ s = s * sIn >= 0 ? mix : mix * 0.5;
488
490
  }
489
491
  out[i] = s;
490
492
  }
@@ -279,24 +279,10 @@ async function run(runId, p, state) {
279
279
  const rawTopK = Number(p.modelTopK ?? 2);
280
280
  const autoK = rawTopK === 0 || autoAdapt;
281
281
  const K = rawTopK > 0 ? rawTopK : 4;
282
- // Адаптивный отбор числа акций (Dynamic Top-K):
283
- // лидер №1 берётся всегда при пробое порога; следующие бумаги (2…K) берутся только если их сигнал
284
- // достаточно силён относительно лидера (>= 68% от силы №1) и уверенно перекрывает порог (>= 1.25 * need),
285
- // чтобы не размывать капитал на слабые окраинные сигналы
286
- const filterDynamicTopK = (cands, edgeFn, needVal, maxCount) => {
282
+ // Отбор до K кандидатов, прошедших абсолютный порог выгоды (без отсева 2..K из-за сравнения с лидером №1 — проверено на IS и OOS)
283
+ const filterDynamicTopK = (cands, _edgeFn, _needVal, maxCount) => {
287
284
  if (!cands.length || maxCount <= 0) return [];
288
- const lim = Math.min(cands.length, maxCount);
289
- if (!autoK || lim <= 1) return cands.slice(0, lim);
290
- const bestE = Math.abs(edgeFn(cands[0]));
291
- const out = [cands[0]];
292
- for (let m = 1; m < lim; m++) {
293
- const curE = Math.abs(edgeFn(cands[m]));
294
- const relMin = m === 1 ? 0.62 : 0.78;
295
- const absMin = m === 1 ? needVal * 1.15 : needVal * 1.45;
296
- if (curE >= bestE * relMin && curE >= absMin) out.push(cands[m]);
297
- else break;
298
- }
299
- return out;
285
+ return cands.slice(0, Math.min(cands.length, maxCount));
300
286
  };
301
287
  const dir = p.modelDirection;
302
288
  const exitRatio = Number(p.modelExitRatio ?? 0.5);