neuroalpha 0.2.0 → 0.3.12

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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -3
  3. package/bin/cli.js +69 -69
  4. package/config/seed-run.json +1 -0
  5. package/config/strategy.json +4 -10
  6. package/deploy/install.sh +128 -0
  7. package/docs//320/224/320/225/320/237/320/233/320/236/320/231.md +103 -0
  8. package/docs//320/230/320/235/320/241/320/242/320/240/320/243/320/232/320/246/320/230/320/257.md +1 -1
  9. 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 +171 -0
  10. package/package.json +15 -5
  11. package/public/admin.html +426 -0
  12. package/public/app.js +741 -251
  13. package/public/index.html +45 -20
  14. package/public/landing.html +1004 -870
  15. package/public/legal.html +22 -0
  16. package/public/styles.css +49 -15
  17. package/server/api/routes.js +176 -23
  18. package/server/auth/index.js +544 -340
  19. package/server/billing/yookassa.js +1 -0
  20. package/server/brokers/index.js +1224 -645
  21. package/server/config.js +6 -4
  22. package/server/data/moex.js +1 -0
  23. package/server/db.js +53 -2
  24. package/server/hub.js +1 -0
  25. package/server/index.js +10 -2
  26. package/server/llm/client.js +1 -0
  27. package/server/llm/providers.js +50 -3
  28. package/server/model/dataset.js +362 -352
  29. package/server/model/forecast.js +15 -21
  30. package/server/model/linalg.js +1 -0
  31. package/server/model/live.js +489 -486
  32. package/server/model/metrics.js +1 -0
  33. package/server/model/models.js +31 -21
  34. package/server/model/profiles.js +1 -0
  35. package/server/model/research.js +119 -172
  36. package/server/model/vocab.js +1 -0
  37. package/server/news/index.js +1 -0
  38. package/server/notify/index.js +304 -168
  39. package/server/params-meta.js +20 -25
  40. package/server/settings.js +1 -0
  41. package/server/strategist/analytics.js +1 -0
  42. package/server/strategist/index.js +6 -6
  43. package/server/strategist/tools.js +2 -1
  44. package/server/util.js +1 -0
  45. package/server/model/gpu-worker.html +0 -11
  46. package/server/model/gpu-worker.js +0 -60
  47. package/server/model/tf-node.js +0 -26
  48. package/server/model/transformer-core.js +0 -330
  49. package/server/model/transformer-gpu.js +0 -221
  50. package/server/model/transformer-pool.js +0 -200
  51. package/server/model/transformer-worker.js +0 -28
  52. package/server/model/transformer.js +0 -57
@@ -1,3 +1,4 @@
1
+ // Copyright (c) 2026 Roman A. Perepelkin. Licensed under the MIT License.
1
2
  import { normalizeParams } from '../settings.js';
2
3
  import { round, DAY, fmtBarMsk } from '../util.js';
3
4
  import { loadDataset, barsFromHours, BAR_MS, BAR_INTERVAL } from './dataset.js';
@@ -18,37 +19,28 @@ export const lastClosedIndex = (ds, now = Date.now()) => {
18
19
  // Обучение на окне, заканчивающемся свечой idx, и прогноз по этой свече.
19
20
  // Возвращает строки по всем акциям: ожидаемое движение (относительно рынка), сигнал покупки, кто влияет.
20
21
  export async function predictAt(ds, p, idx, { explain = true } = {}) {
21
- const H = barsFromHours(p.modelHorizon);
22
+ const autoAdapt = p.modelAutoAdapt !== false;
23
+ const effHorizon = autoAdapt ? 2.5 : p.modelHorizon;
24
+ const effMinEdge = autoAdapt ? 2.5 : p.modelMinEdge;
25
+ const H = barsFromHours(effHorizon);
26
+ const maxH = autoAdapt ? Math.round(H * 1.4) : H;
22
27
  const trainLen = Math.round(p.modelTrainDays * ds.barsPerDay);
23
- const trainStart = Math.max(FEATURE_WARMUP + H + 2, idx - trainLen);
28
+ const trainStart = Math.max(FEATURE_WARMUP + maxH + 2, idx - trainLen);
24
29
  // «текстовая» часть карты: устойчивые описания бизнеса (профили готовятся отдельно, здесь только читаем)
25
30
  const profiles = p.modelTextWeight > 0 ? loadProfiles() : null;
26
- const f = buildFold(ds, { trainStart, testStart: idx, testEnd: idx + 1, H, embedDim: p.modelEmbedDim, textWeight: p.modelTextWeight, profiles });
31
+ const f = buildFold(ds, { trainStart, testStart: idx, testEnd: idx + 1, H, embedDim: p.modelEmbedDim, textWeight: p.modelTextWeight, profiles, multiHorizon: autoAdapt });
27
32
  const u = idx - f.lo;
28
33
  const chosen = p.modelType in MODELS ? p.modelType : 'attention';
29
- const need = ['attention', chosen];
30
- // трансформер обучается здесь, только если торгует он (для сравнения — только в исследовании)
31
- const list = [...new Set(explain ? [...Object.keys(MODELS).filter((n) => !MODELS[n].heavy), chosen] : need)];
34
+ const list = [...new Set(explain ? Object.keys(MODELS) : ['attention', chosen])];
32
35
  const fitted = {};
33
36
  const preds = {};
34
37
  for (const n of list) {
35
- if (n === 'transformer') {
36
- // ~минута обучения — в отдельном потоке (или на видеокарте), чтобы сервер не замирал
37
- const tm = await import('./transformer.js');
38
- const { fitPredictIsolated } = await import('./transformer-pool.js');
39
- const r = await fitPredictIsolated(tm.transformerInput(f), [u], tm.transformerCfg(p), { gpu: p.transformerGpu });
40
- fitted[n] = { name: n, info: r.info, where: r.where };
41
- preds[n] = r.preds.get(u);
42
- continue;
43
- }
44
38
  fitted[n] = await MODELS[n].fit(f);
45
39
  preds[n] = MODELS[n].predict(fitted[n], f, u);
46
- if (fitted[n].dispose) fitted[n].dispose();
47
40
  }
48
41
  const cost = (p.commissionPct + p.slippagePct) / 100;
49
- const threshold = p.modelMinEdge * 2 * cost;
42
+ const threshold = effMinEdge * 2 * cost;
50
43
  const bear = isMarketDown(ds, idx);
51
- const autoAdapt = p.modelAutoAdapt !== false;
52
44
  const dir = p.modelDirection || 'long';
53
45
  const needL = autoAdapt && bear && dir !== 'neutral' ? threshold * 1.25 : threshold;
54
46
  const needS = autoAdapt && dir === 'longshort' ? (bear ? threshold * 0.90 : threshold * 1.25) : threshold;
@@ -94,7 +86,10 @@ function withNews(result, p) {
94
86
  // Прогноз на ближайшие H часов по последней закрытой свече
95
87
  export async function forecastNow(input, { force = false, onProgress } = {}) {
96
88
  const p = normalizeParams(input);
97
- const key = JSON.stringify([p.modelType, p.modelHorizon, p.modelTrainDays, p.modelEmbedDim, p.modelMinEdge, p.modelDirection, p.modelUseMacro, p.modelUseDividends, p.modelTextWeight, p.commissionPct, p.slippagePct, ...(p.modelType === 'transformer' ? [p.transformerEpochs, p.transformerStride, p.transformerGpu] : [])]);
89
+ const autoAdapt = p.modelAutoAdapt !== false;
90
+ const effHorizon = autoAdapt ? 2.5 : p.modelHorizon;
91
+ const effMinEdge = autoAdapt ? 2.5 : p.modelMinEdge;
92
+ const key = JSON.stringify([autoAdapt, p.modelType, effHorizon, p.modelTrainDays, p.modelEmbedDim, effMinEdge, p.modelDirection, p.modelUseMacro, p.modelUseDividends, p.modelTextWeight, p.commissionPct, p.slippagePct]);
98
93
  if (!force && cache && cache.key === key && Date.now() - cache.at < 10 * 60000) return withNews(cache.result, p);
99
94
  if (!force && inflight.has(key)) return withNews(await inflight.get(key), p);
100
95
  const task = (async () => {
@@ -107,11 +102,10 @@ export async function forecastNow(input, { force = false, onProgress } = {}) {
107
102
  at: now,
108
103
  barTs: ds.timeline[last],
109
104
  bar: fmtBarMsk(ds.timeline[last]),
110
- horizon: p.modelHorizon, direction: p.modelDirection, model: r.chosen, thresholdPct: round(r.threshold * 100, 3), marketDown: r.marketDown,
105
+ horizon: effHorizon, direction: p.modelDirection, model: r.chosen, thresholdPct: round(r.threshold * 100, 3), marketDown: r.marketDown,
111
106
  rows: r.rows.map(({ i, expected, ...x }) => ({ ...x, cluster: snap.clusters[i] })).sort((a, b) => b.expectedPct - a.expectedPct),
112
107
  map: { symbols: ds.tokens.map((t) => t.symbol), names: ds.tokens.map((t) => t.name), pts: snap.pts.map(([x, y]) => [round(x, 3), round(y, 3)]), cl: snap.clusters },
113
108
  heads: HEADS.map((h, i) => ({ label: h.label, coef: round(r.fitted.attention.coef[i], 4) })),
114
- ...(r.fitted.transformer ? { transformer: { device: r.fitted.transformer.where, bestEpoch: r.fitted.transformer.info.bestEpoch, epochs: r.fitted.transformer.info.epochs } } : {}),
115
109
  };
116
110
  cache = { key, at: now, result };
117
111
  return result;
@@ -1,3 +1,4 @@
1
+ // Copyright (c) 2026 Roman A. Perepelkin. Licensed under the MIT License.
1
2
  // Небольшая линейная алгебра без зависимостей: матрицы — массивы Float64Array (строки)
2
3
 
3
4
  export const matrix = (n, m) => Array.from({ length: n }, () => new Float64Array(m));