neuroalpha 0.0.0-stage → 0.2.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/README.md +164 -3
- package/bin/cli.js +69 -0
- package/config/strategy.json +33 -0
- package/docs//320/230/320/235/320/241/320/242/320/240/320/243/320/232/320/246/320/230/320/257.md +156 -0
- package/package.json +43 -6
- package/public/app.js +2410 -0
- package/public/index.html +337 -0
- package/public/landing.html +870 -0
- package/public/legal.html +90 -0
- package/public/styles.css +343 -0
- package/server/api/routes.js +333 -0
- package/server/auth/index.js +340 -0
- package/server/billing/yookassa.js +132 -0
- package/server/brokers/index.js +645 -0
- package/server/config.js +44 -0
- package/server/data/moex.js +165 -0
- package/server/db.js +420 -0
- package/server/hub.js +14 -0
- package/server/index.js +92 -0
- package/server/llm/client.js +187 -0
- package/server/llm/providers.js +355 -0
- package/server/model/dataset.js +352 -0
- package/server/model/forecast.js +121 -0
- package/server/model/gpu-worker.html +11 -0
- package/server/model/gpu-worker.js +60 -0
- package/server/model/linalg.js +142 -0
- package/server/model/live.js +486 -0
- package/server/model/metrics.js +134 -0
- package/server/model/models.js +459 -0
- package/server/model/profiles.js +166 -0
- package/server/model/research.js +880 -0
- package/server/model/tf-node.js +26 -0
- package/server/model/transformer-core.js +330 -0
- package/server/model/transformer-gpu.js +221 -0
- package/server/model/transformer-pool.js +200 -0
- package/server/model/transformer-worker.js +28 -0
- package/server/model/transformer.js +57 -0
- package/server/model/vocab.js +28 -0
- package/server/news/index.js +309 -0
- package/server/notify/index.js +168 -0
- package/server/params-meta.js +63 -0
- package/server/settings.js +79 -0
- package/server/strategist/analytics.js +150 -0
- package/server/strategist/index.js +484 -0
- package/server/strategist/tools.js +302 -0
- package/server/util.js +9 -0
|
@@ -0,0 +1,870 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="ru">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>НейроАльфа — ИИ-автопилот для рынка акций</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--bg: #060911;
|
|
10
|
+
--bg2: #0b101d;
|
|
11
|
+
--panel: rgba(17, 24, 39, 0.72);
|
|
12
|
+
--panel-solid: #111827;
|
|
13
|
+
--line: rgba(255, 255, 255, 0.08);
|
|
14
|
+
--line-bright: rgba(96, 165, 250, 0.38);
|
|
15
|
+
--text: #f8fafc;
|
|
16
|
+
--muted: #94a3b8;
|
|
17
|
+
--accent: #3b82f6;
|
|
18
|
+
--accent2: #6366f1;
|
|
19
|
+
--cyan: #06b6d4;
|
|
20
|
+
--green: #10b981;
|
|
21
|
+
--red: #f43f5e;
|
|
22
|
+
}
|
|
23
|
+
* { box-sizing: border-box; }
|
|
24
|
+
html { scroll-behavior: smooth; }
|
|
25
|
+
body {
|
|
26
|
+
margin: 0;
|
|
27
|
+
background: var(--bg);
|
|
28
|
+
color: var(--text);
|
|
29
|
+
font: 16px/1.6 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
30
|
+
overflow-x: hidden;
|
|
31
|
+
}
|
|
32
|
+
a { color: inherit; text-decoration: none; }
|
|
33
|
+
.wrap { max-width: 1200px; margin: 0 auto; padding: 0 24px; position: relative; z-index: 2; }
|
|
34
|
+
|
|
35
|
+
/* ---------- Фоновое свечение и сетка ---------- */
|
|
36
|
+
.ambient {
|
|
37
|
+
position: fixed; inset: 0; pointer-events: none; z-index: 0;
|
|
38
|
+
background:
|
|
39
|
+
radial-gradient(650px circle at 18% 12%, rgba(59, 130, 246, 0.16), transparent 60%),
|
|
40
|
+
radial-gradient(600px circle at 82% 22%, rgba(99, 102, 241, 0.14), transparent 60%),
|
|
41
|
+
radial-gradient(700px circle at 50% 85%, rgba(6, 182, 212, 0.09), transparent 65%);
|
|
42
|
+
}
|
|
43
|
+
.grid-bg {
|
|
44
|
+
position: fixed; inset: 0; pointer-events: none; z-index: 0;
|
|
45
|
+
background-image:
|
|
46
|
+
linear-gradient(to right, rgba(255,255,255,0.025) 1px, transparent 1px),
|
|
47
|
+
linear-gradient(to bottom, rgba(255,255,255,0.025) 1px, transparent 1px);
|
|
48
|
+
background-size: 48px 48px;
|
|
49
|
+
mask-image: radial-gradient(ellipse at 50% 30%, #000 35%, transparent 80%);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ---------- Шапка и бегущая строка ---------- */
|
|
53
|
+
header {
|
|
54
|
+
position: sticky; top: 0; z-index: 40;
|
|
55
|
+
background: rgba(6, 9, 17, 0.78);
|
|
56
|
+
backdrop-filter: blur(16px);
|
|
57
|
+
border-bottom: 1px solid var(--line);
|
|
58
|
+
}
|
|
59
|
+
.nav { height: 68px; display: flex; align-items: center; justify-content: space-between; gap: 16px; }
|
|
60
|
+
.brand { display: flex; align-items: center; gap: 10px; font-weight: 800; font-size: 20px; letter-spacing: -0.3px; }
|
|
61
|
+
.brand-icon {
|
|
62
|
+
width: 34px; height: 34px; border-radius: 9px;
|
|
63
|
+
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
|
64
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
65
|
+
color: #fff; font-size: 18px; box-shadow: 0 4px 18px rgba(59, 130, 246, 0.4);
|
|
66
|
+
}
|
|
67
|
+
.nav-links { display: flex; gap: 26px; font-size: 14.5px; color: var(--muted); font-weight: 500; }
|
|
68
|
+
.nav-links a { transition: color .15s; }
|
|
69
|
+
.nav-links a:hover { color: #fff; }
|
|
70
|
+
@media (max-width: 820px) { .nav-links { display: none; } }
|
|
71
|
+
|
|
72
|
+
.ticker-wrap {
|
|
73
|
+
border-bottom: 1px solid var(--line);
|
|
74
|
+
background: rgba(11, 16, 29, 0.65);
|
|
75
|
+
overflow: hidden; white-space: nowrap; height: 36px; display: flex; align-items: center;
|
|
76
|
+
position: relative; z-index: 2;
|
|
77
|
+
}
|
|
78
|
+
.ticker-track {
|
|
79
|
+
display: inline-flex; gap: 32px;
|
|
80
|
+
animation: tickerMove 38s linear infinite;
|
|
81
|
+
font-size: 12.5px; color: var(--muted);
|
|
82
|
+
}
|
|
83
|
+
.ticker-item { display: inline-flex; align-items: center; gap: 6px; }
|
|
84
|
+
.ticker-item b { color: var(--text); font-weight: 600; }
|
|
85
|
+
@keyframes tickerMove { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }
|
|
86
|
+
|
|
87
|
+
/* ---------- Кнопки ---------- */
|
|
88
|
+
.btn {
|
|
89
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
|
|
90
|
+
padding: 11px 22px; border-radius: 10px; border: 1px solid var(--line);
|
|
91
|
+
background: rgba(255, 255, 255, 0.04); color: var(--text);
|
|
92
|
+
font-weight: 600; font-size: 14.5px; cursor: pointer;
|
|
93
|
+
transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease, background .18s ease;
|
|
94
|
+
}
|
|
95
|
+
.btn:hover { border-color: var(--line-bright); background: rgba(255, 255, 255, 0.08); transform: translateY(-1px); }
|
|
96
|
+
.btn.primary {
|
|
97
|
+
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
|
98
|
+
border-color: transparent; color: #fff;
|
|
99
|
+
box-shadow: 0 8px 26px -4px rgba(59, 130, 246, 0.55);
|
|
100
|
+
}
|
|
101
|
+
.btn.primary:hover { box-shadow: 0 12px 32px -2px rgba(99, 102, 241, 0.7); transform: translateY(-2px); }
|
|
102
|
+
.btn.lg { padding: 14px 28px; font-size: 15.5px; border-radius: 12px; }
|
|
103
|
+
|
|
104
|
+
/* ---------- Hero ---------- */
|
|
105
|
+
.hero { position: relative; padding: 64px 0 56px; overflow: hidden; }
|
|
106
|
+
#hero-canvas {
|
|
107
|
+
position: absolute; inset: 0; width: 100%; height: 100%;
|
|
108
|
+
pointer-events: none; z-index: 1; opacity: 0.55;
|
|
109
|
+
}
|
|
110
|
+
.hero-grid {
|
|
111
|
+
display: grid; grid-template-columns: 1.12fr 0.88fr; gap: 44px; align-items: center;
|
|
112
|
+
position: relative; z-index: 2;
|
|
113
|
+
}
|
|
114
|
+
@media (max-width: 960px) { .hero-grid { grid-template-columns: 1fr; } }
|
|
115
|
+
.pill-badge {
|
|
116
|
+
display: inline-flex; align-items: center; gap: 8px;
|
|
117
|
+
padding: 6px 14px; border-radius: 999px;
|
|
118
|
+
background: rgba(59, 130, 246, 0.12); border: 1px solid rgba(59, 130, 246, 0.3);
|
|
119
|
+
color: #93c5fd; font-size: 13px; font-weight: 500; margin-bottom: 20px;
|
|
120
|
+
}
|
|
121
|
+
.pulse-dot {
|
|
122
|
+
width: 8px; height: 8px; border-radius: 50%; background: var(--green);
|
|
123
|
+
box-shadow: 0 0 10px var(--green); animation: pulseDot 2s infinite;
|
|
124
|
+
}
|
|
125
|
+
@keyframes pulseDot { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .45; transform: scale(1.25); } }
|
|
126
|
+
|
|
127
|
+
h1 {
|
|
128
|
+
font-size: clamp(36px, 4.6vw, 56px); line-height: 1.08; margin: 0 0 20px;
|
|
129
|
+
letter-spacing: -1.4px; font-weight: 800;
|
|
130
|
+
}
|
|
131
|
+
.grad-text {
|
|
132
|
+
background: linear-gradient(135deg, #ffffff 20%, #93c5fd 65%, #818cf8 100%);
|
|
133
|
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
|
134
|
+
}
|
|
135
|
+
.hero-sub { font-size: 18px; color: var(--muted); margin: 0 0 30px; max-width: 600px; line-height: 1.6; }
|
|
136
|
+
.hero-actions { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 36px; }
|
|
137
|
+
.mini-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; border-top: 1px solid var(--line); padding-top: 24px; }
|
|
138
|
+
.mini-stat .num { font-size: 26px; font-weight: 800; letter-spacing: -0.5px; }
|
|
139
|
+
.mini-stat .lbl { font-size: 12.5px; color: var(--muted); line-height: 1.35; margin-top: 2px; }
|
|
140
|
+
.pos { color: var(--green); } .neg { color: var(--red); }
|
|
141
|
+
|
|
142
|
+
/* ---------- Живой макет терминала и мессенджера в Hero ---------- */
|
|
143
|
+
.mockup-stack { position: relative; }
|
|
144
|
+
.term-card {
|
|
145
|
+
background: linear-gradient(180deg, rgba(23, 32, 52, 0.86), rgba(13, 18, 30, 0.92));
|
|
146
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
147
|
+
border-radius: 16px; padding: 18px 20px;
|
|
148
|
+
backdrop-filter: blur(20px);
|
|
149
|
+
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.65), 0 0 40px rgba(59, 130, 246, 0.12);
|
|
150
|
+
}
|
|
151
|
+
.term-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; padding-bottom: 10px; border-bottom: 1px solid var(--line); font-size: 12.5px; }
|
|
152
|
+
.dots { display: flex; gap: 6px; }
|
|
153
|
+
.dots i { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
|
|
154
|
+
.sig-row {
|
|
155
|
+
display: grid; grid-template-columns: 70px 1fr 68px 72px; align-items: center; gap: 10px;
|
|
156
|
+
padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.05); font-size: 13px;
|
|
157
|
+
}
|
|
158
|
+
.sig-bar { height: 6px; background: rgba(255,255,255,0.06); border-radius: 999px; overflow: hidden; }
|
|
159
|
+
.sig-bar div { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--accent), var(--green)); animation: barGrow 2.4s ease-out forwards; }
|
|
160
|
+
@keyframes barGrow { from { width: 0; } }
|
|
161
|
+
.tag-pill { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 600; text-align: center; }
|
|
162
|
+
.tag-pill.buy { background: rgba(16, 185, 129, 0.15); color: var(--green); border: 1px solid rgba(16, 185, 129, 0.35); }
|
|
163
|
+
.tag-pill.quar { background: rgba(244, 63, 94, 0.14); color: var(--red); border: 1px solid rgba(244, 63, 94, 0.3); }
|
|
164
|
+
|
|
165
|
+
.bot-float {
|
|
166
|
+
margin-top: 14px;
|
|
167
|
+
background: linear-gradient(135deg, rgba(30, 41, 59, 0.95), rgba(15, 23, 42, 0.98));
|
|
168
|
+
border: 1px solid var(--line-bright);
|
|
169
|
+
border-radius: 14px; padding: 14px 16px;
|
|
170
|
+
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.55);
|
|
171
|
+
animation: floatY 5s ease-in-out infinite;
|
|
172
|
+
}
|
|
173
|
+
@keyframes floatY { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
|
|
174
|
+
.bot-head { display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: #93c5fd; margin-bottom: 6px; font-weight: 600; }
|
|
175
|
+
.bot-btns { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 10px; }
|
|
176
|
+
.bot-btn { padding: 7px 10px; border-radius: 8px; font-size: 12.5px; font-weight: 600; text-align: center; border: 1px solid var(--line); }
|
|
177
|
+
.bot-btn.ok { background: rgba(16, 185, 129, 0.18); border-color: rgba(16, 185, 129, 0.45); color: #34d399; }
|
|
178
|
+
|
|
179
|
+
/* ---------- Секции и Bento-карточки ---------- */
|
|
180
|
+
section { padding: 72px 0; position: relative; z-index: 2; }
|
|
181
|
+
.sec-head { text-align: center; max-width: 720px; margin: 0 auto 44px; }
|
|
182
|
+
.sec-tag { color: #60a5fa; font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 8px; }
|
|
183
|
+
h2 { font-size: clamp(26px, 3.5vw, 38px); margin: 0 0 12px; letter-spacing: -0.8px; line-height: 1.18; }
|
|
184
|
+
.sec-sub { color: var(--muted); margin: 0; font-size: 16.5px; }
|
|
185
|
+
|
|
186
|
+
.bento { display: grid; grid-template-columns: repeat(12, 1fr); gap: 20px; }
|
|
187
|
+
.bcard {
|
|
188
|
+
grid-column: span 4;
|
|
189
|
+
background: var(--panel); border: 1px solid var(--line);
|
|
190
|
+
border-radius: 16px; padding: 26px;
|
|
191
|
+
backdrop-filter: blur(12px);
|
|
192
|
+
transition: transform .22s ease, border-color .22s ease, box-shadow .22s ease;
|
|
193
|
+
position: relative; overflow: hidden;
|
|
194
|
+
}
|
|
195
|
+
.bcard.wide { grid-column: span 6; }
|
|
196
|
+
@media (max-width: 900px) { .bcard, .bcard.wide { grid-column: span 12; } }
|
|
197
|
+
.bcard:hover {
|
|
198
|
+
transform: translateY(-4px);
|
|
199
|
+
border-color: var(--line-bright);
|
|
200
|
+
box-shadow: 0 20px 44px -15px rgba(59, 130, 246, 0.25);
|
|
201
|
+
}
|
|
202
|
+
.bicon {
|
|
203
|
+
width: 42px; height: 42px; border-radius: 10px;
|
|
204
|
+
background: rgba(59, 130, 246, 0.14); border: 1px solid rgba(59, 130, 246, 0.3);
|
|
205
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
206
|
+
font-size: 20px; margin-bottom: 14px; color: #60a5fa;
|
|
207
|
+
}
|
|
208
|
+
.bcard h3 { margin: 0 0 8px; font-size: 19px; letter-spacing: -0.3px; }
|
|
209
|
+
.bcard p { margin: 0; color: var(--muted); font-size: 14.5px; line-height: 1.55; }
|
|
210
|
+
|
|
211
|
+
/* ---------- Живая витрина стратегий ---------- */
|
|
212
|
+
.showcase-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); gap: 22px; }
|
|
213
|
+
.sc-card {
|
|
214
|
+
background: var(--panel); border: 1px solid var(--line);
|
|
215
|
+
border-radius: 18px; padding: 24px;
|
|
216
|
+
display: flex; flex-direction: column; gap: 14px;
|
|
217
|
+
transition: border-color .2s, transform .2s;
|
|
218
|
+
}
|
|
219
|
+
.sc-card:hover { border-color: var(--line-bright); transform: translateY(-3px); }
|
|
220
|
+
.sc-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
|
|
221
|
+
.sc-ret { font-size: 30px; font-weight: 800; letter-spacing: -0.8px; line-height: 1; }
|
|
222
|
+
.sc-kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; background: rgba(0,0,0,0.28); border-radius: 10px; padding: 10px 12px; font-size: 12.5px; }
|
|
223
|
+
.sc-kpis b { display: block; font-size: 14.5px; color: var(--text); }
|
|
224
|
+
|
|
225
|
+
/* ---------- Тарифы ---------- */
|
|
226
|
+
.period-wrap { display: flex; justify-content: center; margin-bottom: 32px; }
|
|
227
|
+
.period-seg { display: inline-flex; background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 4px; gap: 4px; }
|
|
228
|
+
.period-seg button {
|
|
229
|
+
border: 0; background: transparent; color: var(--muted);
|
|
230
|
+
padding: 8px 18px; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 14px; transition: .15s;
|
|
231
|
+
}
|
|
232
|
+
.period-seg button.active { background: linear-gradient(135deg, var(--accent), var(--accent2)); color: #fff; }
|
|
233
|
+
.plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(310px, 1fr)); gap: 24px; align-items: stretch; }
|
|
234
|
+
.plan {
|
|
235
|
+
background: var(--panel); border: 1px solid var(--line);
|
|
236
|
+
border-radius: 18px; padding: 30px 26px;
|
|
237
|
+
display: flex; flex-direction: column; position: relative;
|
|
238
|
+
transition: transform .2s, border-color .2s;
|
|
239
|
+
}
|
|
240
|
+
.plan:hover { transform: translateY(-4px); }
|
|
241
|
+
.plan.featured {
|
|
242
|
+
background: linear-gradient(180deg, rgba(30, 58, 138, 0.32), rgba(17, 24, 39, 0.88));
|
|
243
|
+
border-color: rgba(96, 165, 250, 0.55);
|
|
244
|
+
box-shadow: 0 20px 50px -12px rgba(37, 99, 235, 0.35);
|
|
245
|
+
}
|
|
246
|
+
.pop-ribbon {
|
|
247
|
+
position: absolute; top: 14px; right: 16px;
|
|
248
|
+
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
|
249
|
+
color: #fff; font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: 999px;
|
|
250
|
+
text-transform: uppercase; letter-spacing: 0.5px;
|
|
251
|
+
}
|
|
252
|
+
.plan h3 { margin: 0 0 4px; font-size: 21px; }
|
|
253
|
+
.plan-desc { font-size: 13.5px; color: var(--muted); margin-bottom: 14px; }
|
|
254
|
+
.price { font-size: 38px; font-weight: 800; margin: 6px 0 18px; letter-spacing: -1px; }
|
|
255
|
+
.price span { font-size: 14px; font-weight: 400; color: var(--muted); letter-spacing: 0; }
|
|
256
|
+
.plan ul { list-style: none; padding: 0; margin: 0 0 26px; font-size: 14.5px; color: #cbd5e1; flex: 1; }
|
|
257
|
+
.plan li { margin: 10px 0; display: flex; gap: 10px; align-items: flex-start; }
|
|
258
|
+
.plan li::before { content: "✓"; color: var(--green); font-weight: 800; }
|
|
259
|
+
|
|
260
|
+
/* ---------- Анимация появления при прокрутке ---------- */
|
|
261
|
+
.reveal { opacity: 0; transform: translateY(22px); transition: opacity .6s ease, transform .6s ease; }
|
|
262
|
+
.reveal.in { opacity: 1; transform: translateY(0); }
|
|
263
|
+
|
|
264
|
+
footer { padding: 40px 0; border-top: 1px solid var(--line); color: var(--muted); font-size: 13px; position: relative; z-index: 2; }
|
|
265
|
+
.modal { position: fixed; inset: 0; background: rgba(3, 6, 12, 0.78); backdrop-filter: blur(8px); display: flex; align-items: center; justify-content: center; z-index: 100; padding: 16px; }
|
|
266
|
+
.modal[hidden] { display: none; }
|
|
267
|
+
.modal-box { background: var(--panel-solid); border: 1px solid var(--line-bright); border-radius: 16px; padding: 28px; width: 100%; max-width: 430px; box-shadow: 0 24px 60px #000a; }
|
|
268
|
+
.modal-box h3 { margin: 0 0 16px; font-size: 21px; }
|
|
269
|
+
.modal-box form { display: grid; gap: 12px; }
|
|
270
|
+
.modal-box input[type=email], .modal-box input[type=password], .modal-box input[name=name] {
|
|
271
|
+
width: 100%; padding: 11px 14px; border-radius: 10px; border: 1px solid var(--line); background: var(--bg); color: var(--text); font: inherit;
|
|
272
|
+
}
|
|
273
|
+
.err { color: var(--red); font-size: 13.5px; min-height: 18px; }
|
|
274
|
+
.cli-box {
|
|
275
|
+
background: #070b14; border: 1px solid var(--line-bright); border-radius: 14px; padding: 16px 20px;
|
|
276
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 14px; color: #e2e8f0;
|
|
277
|
+
display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap;
|
|
278
|
+
box-shadow: 0 14px 36px rgba(0, 0, 0, 0.5);
|
|
279
|
+
}
|
|
280
|
+
.cli-box code { color: #38bdf8; word-break: break-all; }
|
|
281
|
+
.cab-card {
|
|
282
|
+
background: linear-gradient(135deg, rgba(30, 58, 138, 0.28), rgba(15, 23, 42, 0.92));
|
|
283
|
+
border: 1px solid var(--line-bright); border-radius: 18px; padding: 26px 28px; margin-top: 28px;
|
|
284
|
+
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.45);
|
|
285
|
+
}
|
|
286
|
+
.cab-grid { display: grid; grid-template-columns: 1.2fr 0.8fr; gap: 24px; align-items: center; }
|
|
287
|
+
@media (max-width: 860px) { .cab-grid { grid-template-columns: 1fr; } }
|
|
288
|
+
.key-pill {
|
|
289
|
+
display: inline-flex; align-items: center; gap: 10px; background: rgba(0, 0, 0, 0.45);
|
|
290
|
+
border: 1px solid var(--line); border-radius: 10px; padding: 8px 14px; font-family: monospace; font-size: 14px; color: #93c5fd;
|
|
291
|
+
}
|
|
292
|
+
</style>
|
|
293
|
+
</head>
|
|
294
|
+
<body>
|
|
295
|
+
<div class="ambient"></div>
|
|
296
|
+
<div class="grid-bg"></div>
|
|
297
|
+
|
|
298
|
+
<header>
|
|
299
|
+
<div class="wrap nav">
|
|
300
|
+
<a class="brand" href="/landing"><span class="brand-icon">◈</span><span>НейроАльфа</span></a>
|
|
301
|
+
<nav class="nav-links">
|
|
302
|
+
<a href="#showcase">Результаты</a>
|
|
303
|
+
<a href="#how">Технология</a>
|
|
304
|
+
<a href="#cli-sec">Запуск npx / API</a>
|
|
305
|
+
<a href="#brokers">Брокеры и мессенджеры</a>
|
|
306
|
+
<a href="#pricing">Тарифы</a>
|
|
307
|
+
</nav>
|
|
308
|
+
<div style="display:flex;gap:10px" id="nav-auth">
|
|
309
|
+
<button class="btn" id="btn-login-open">Войти</button>
|
|
310
|
+
<a class="btn primary" href="/?terminal=1">Открыть терминал →</a>
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
</header>
|
|
314
|
+
|
|
315
|
+
<div class="ticker-wrap">
|
|
316
|
+
<div class="ticker-track" id="ticker-track">
|
|
317
|
+
<span class="ticker-item"><b>SBER</b> <span class="pos">+0,92%</span> · импульс от финансового кластера</span>
|
|
318
|
+
<span class="ticker-item"><b>LKOH</b> <span class="pos">+0,74%</span> · переток за Brent и экспортёрами</span>
|
|
319
|
+
<span class="ticker-item"><b>YDEX</b> <span class="pos">+1,18%</span> · сильнее широкого рынка</span>
|
|
320
|
+
<span class="ticker-item"><b>PLZL</b> <span class="pos">+0,65%</span> · поддержка от макро-токена золота</span>
|
|
321
|
+
<span class="ticker-item"><b>TATN</b> <span class="pos">+0,58%</span> · влияние LKOH, ROSN</span>
|
|
322
|
+
<span class="ticker-item"><b>SGZH</b> <span class="neg">карантин</span> · отсеяна самообучением правил</span>
|
|
323
|
+
<span class="ticker-item"><b>NVTK</b> <span class="pos">+0,61%</span> · возврат к среднему по карте</span>
|
|
324
|
+
<span class="ticker-item"><b>SBER</b> <span class="pos">+0,92%</span> · импульс от финансового кластера</span>
|
|
325
|
+
<span class="ticker-item"><b>LKOH</b> <span class="pos">+0,74%</span> · переток за Brent и экспортёрами</span>
|
|
326
|
+
<span class="ticker-item"><b>YDEX</b> <span class="pos">+1,18%</span> · сильнее широкого рынка</span>
|
|
327
|
+
<span class="ticker-item"><b>PLZL</b> <span class="pos">+0,65%</span> · поддержка от макро-токена золота</span>
|
|
328
|
+
<span class="ticker-item"><b>TATN</b> <span class="pos">+0,58%</span> · влияние LKOH, ROSN</span>
|
|
329
|
+
<span class="ticker-item"><b>SGZH</b> <span class="neg">карантин</span> · отсеяна самообучением правил</span>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
|
|
333
|
+
<div class="hero">
|
|
334
|
+
<canvas id="hero-canvas"></canvas>
|
|
335
|
+
<div class="wrap hero-grid">
|
|
336
|
+
<div>
|
|
337
|
+
<div class="pill-badge"><span class="pulse-dot"></span><span>Московская биржа · ~100 ликвидных акций · Т-Банк · БКС · Финам · Алор</span></div>
|
|
338
|
+
<h1><span class="grad-text">ИИ-автопилот,</span> который читает рынок акций как единый язык</h1>
|
|
339
|
+
<p class="hero-sub">Платформа находит опережающие волны капитала между связанными бумагами, сама адаптирует пороги под комиссию вашего брокера и присылает готовые заявки в <b>Telegram и MAX</b> с подтверждением в 1 клик.</p>
|
|
340
|
+
<div class="hero-actions">
|
|
341
|
+
<button class="btn primary lg" id="btn-hero-start">Попробовать бесплатно</button>
|
|
342
|
+
<a class="btn lg" href="/?terminal=1">Открыть веб-терминал →</a>
|
|
343
|
+
</div>
|
|
344
|
+
<div class="mini-stats">
|
|
345
|
+
<div class="mini-stat"><div class="num pos">+95% … +422%</div><div class="lbl">на истории 2024–2026 против −23,6% у индекса Мосбиржи</div></div>
|
|
346
|
+
<div class="mini-stat"><div class="num pos">144 из 144</div><div class="lbl">недель подряд прогноз опережал случайный выбор</div></div>
|
|
347
|
+
<div class="mini-stat"><div class="num">1 клик</div><div class="lbl">подтверждение лимитных заявок в лотах из Telegram и MAX</div></div>
|
|
348
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
|
|
351
|
+
<div class="mockup-stack">
|
|
352
|
+
<div class="term-card">
|
|
353
|
+
<div class="term-top">
|
|
354
|
+
<div class="dots"><i style="background:#f43f5e"></i><i style="background:#f59e0b"></i><i style="background:#10b981"></i></div>
|
|
355
|
+
<span style="color:var(--muted)">НейроАльфа · Прогноз на 2 часа · Самообучение: вкл.</span>
|
|
356
|
+
<span class="pos" style="font-weight:700">● Live</span>
|
|
357
|
+
</div>
|
|
358
|
+
<div class="sig-row">
|
|
359
|
+
<b>LKOH</b>
|
|
360
|
+
<div class="sig-bar"><div style="width:88%"></div></div>
|
|
361
|
+
<span class="pos" style="text-align:right;font-weight:700">+0,84%</span>
|
|
362
|
+
<span class="tag-pill buy">купить</span>
|
|
363
|
+
</div>
|
|
364
|
+
<div class="sig-row">
|
|
365
|
+
<b>SBER</b>
|
|
366
|
+
<div class="sig-bar"><div style="width:79%"></div></div>
|
|
367
|
+
<span class="pos" style="text-align:right;font-weight:700">+0,71%</span>
|
|
368
|
+
<span class="tag-pill buy">купить</span>
|
|
369
|
+
</div>
|
|
370
|
+
<div class="sig-row">
|
|
371
|
+
<b>YDEX</b>
|
|
372
|
+
<div class="sig-bar"><div style="width:72%"></div></div>
|
|
373
|
+
<span class="pos" style="text-align:right;font-weight:700">+0,64%</span>
|
|
374
|
+
<span class="tag-pill buy">купить</span>
|
|
375
|
+
</div>
|
|
376
|
+
<div class="sig-row" style="border-bottom:0">
|
|
377
|
+
<b>SGZH</b>
|
|
378
|
+
<div class="sig-bar"><div style="width:25%;background:var(--red)"></div></div>
|
|
379
|
+
<span class="neg" style="text-align:right">−0,52%</span>
|
|
380
|
+
<span class="tag-pill quar">карантин</span>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<div class="bot-float">
|
|
385
|
+
<div class="bot-head"><span>⚡ Бот НейроАльфа · Telegram / MAX</span><span>только что</span></div>
|
|
386
|
+
<div style="font-size:13px;color:#e2e8f0">Счёт «Т-Банк Основной»: рассчитан пакет лимитных заявок (в целых лотах Мосбиржи):<br>• <b>Купить LKOH</b> — 7 лотов по 7 142,5 ₽<br>• <b>Купить SBER</b> — 16 лотов по 284,80 ₽</div>
|
|
387
|
+
<div class="bot-btns">
|
|
388
|
+
<div class="bot-btn ok">✓ Исполнить заявки (2)</div>
|
|
389
|
+
<div class="bot-btn" style="color:var(--muted)">✕ Отклонить</div>
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
</div>
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
|
|
396
|
+
<div class="wrap">
|
|
397
|
+
<div id="cabinet-box" hidden></div>
|
|
398
|
+
|
|
399
|
+
<section id="showcase" class="reveal">
|
|
400
|
+
<div class="sec-head">
|
|
401
|
+
<div class="sec-tag">Проверено пошагово без заглядывания в будущее</div>
|
|
402
|
+
<h2>Живая витрина эталонных стратегий</h2>
|
|
403
|
+
<p class="sec-sub">Каждая точка графика получена строгим скользящим обучением: модель учится на прошлом окне и торгует следующий месяц, которого ещё не видела, с учётом комиссий и дивидендов.</p>
|
|
404
|
+
</div>
|
|
405
|
+
<div class="showcase-grid" id="showcase-list">
|
|
406
|
+
<div class="bcard wide"><p>Загружаем актуальные графики с сервера…</p></div>
|
|
407
|
+
</div>
|
|
408
|
+
</section>
|
|
409
|
+
|
|
410
|
+
<section id="how" class="reveal">
|
|
411
|
+
<div class="sec-head">
|
|
412
|
+
<div class="sec-tag">Архитектура нового поколения</div>
|
|
413
|
+
<h2>Почему «НейроАльфа» обыгрывает классические индикаторы</h2>
|
|
414
|
+
<p class="sec-sub">Вместо взгляда на одну бумагу в изоляции система видит весь рынок целиком и сама подстраивает правила торговли.</p>
|
|
415
|
+
</div>
|
|
416
|
+
<div class="bento">
|
|
417
|
+
<div class="bcard wide">
|
|
418
|
+
<div class="bicon">◈</div>
|
|
419
|
+
<h3>Принцип языковых моделей (LLM) для котировок</h3>
|
|
420
|
+
<p>Акции выступают как слова в тексте: система строит многомерную карту рынка по совместным движениям цен и 20 фундаментальным свойствам бизнеса, улавливая переток капитала от лидеров к связанным бумагам.</p>
|
|
421
|
+
</div>
|
|
422
|
+
<div class="bcard wide">
|
|
423
|
+
<div class="bicon">⚡</div>
|
|
424
|
+
<h3>Самообучение правил и карантин «акций-убийц»</h3>
|
|
425
|
+
<p>Вам не нужно крутить десятки настроек. Алгоритм сам калибрует порог выхода под комиссию вашего брокера и автоматически блокирует бумаги, где сигнал деградировал или идёт новостной обвал.</p>
|
|
426
|
+
</div>
|
|
427
|
+
<div class="bcard">
|
|
428
|
+
<div class="bicon">🛡</div>
|
|
429
|
+
<h3>Стресс-тест на 5 сдвигах истории</h3>
|
|
430
|
+
<p>Любая стратегия проверяется серией сдвигов исторического окна: вы видите медиану и худший сценарий до запуска на деньгах.</p>
|
|
431
|
+
</div>
|
|
432
|
+
<div class="bcard">
|
|
433
|
+
<div class="bicon">🎙</div>
|
|
434
|
+
<h3>Голосовой ИИ-управляющий «Стратег»</h3>
|
|
435
|
+
<p>Общайтесь с платформой голосом или текстом: Стратег сам ставит эксперименты, ведёт журнал гипотез и объясняет каждую сделку простыми словами.</p>
|
|
436
|
+
</div>
|
|
437
|
+
<div class="bcard">
|
|
438
|
+
<div class="bicon">📰</div>
|
|
439
|
+
<h3>Фильтр свежих новостей и макро-факторов</h3>
|
|
440
|
+
<p>Учитывает нефть Brent, золото, юань, индекс гособлигаций RGBI и каждые 10 минут размечает новости компаний, блокируя входы против негатива.</p>
|
|
441
|
+
</div>
|
|
442
|
+
</div>
|
|
443
|
+
</section>
|
|
444
|
+
|
|
445
|
+
<section id="cli-sec" class="reveal">
|
|
446
|
+
<div class="sec-head">
|
|
447
|
+
<div class="sec-tag">Облако или локальный запуск на вашем ПК</div>
|
|
448
|
+
<h2>Запуск за 10 секунд через <code>npx</code> с персональным API-ключом</h2>
|
|
449
|
+
<p class="sec-sub">Зарегистрируйтесь на сайте, получите API-ключ и выберите удобный формат: работайте прямо в облачном веб-терминале или запустите платформу локально одной командой — тогда токены вашего брокера никогда не покинут ваш компьютер.</p>
|
|
450
|
+
</div>
|
|
451
|
+
<div class="bento">
|
|
452
|
+
<div class="bcard wide">
|
|
453
|
+
<div class="bicon">1</div>
|
|
454
|
+
<h3>Зарегистрируйтесь и получите APIKey</h3>
|
|
455
|
+
<p>Сразу после регистрации и выбора тарифа в личном кабинете создаётся ваш ключ лицензии <code>na_live_…</code>. Он открывает лимиты вашего тарифа и в браузере, и в локальном клиенте.</p>
|
|
456
|
+
</div>
|
|
457
|
+
<div class="bcard wide">
|
|
458
|
+
<div class="bicon">2</div>
|
|
459
|
+
<h3>Запустите в облаке или через <code>npx</code></h3>
|
|
460
|
+
<p style="margin-bottom:14px">Для локального запуска на Windows, macOS или Linux (нужен Node.js ≥ 22.13) выполните в терминале:</p>
|
|
461
|
+
<div class="cli-box">
|
|
462
|
+
<code id="landing-cli-cmd">npx neuroalpha --key na_live_ваш_ключ</code>
|
|
463
|
+
<button type="button" class="btn" id="btn-copy-landing-cli" style="padding:7px 14px;font-size:13px">Скопировать</button>
|
|
464
|
+
</div>
|
|
465
|
+
</div>
|
|
466
|
+
</div>
|
|
467
|
+
</section>
|
|
468
|
+
|
|
469
|
+
<section id="brokers" class="reveal">
|
|
470
|
+
<div class="sec-head">
|
|
471
|
+
<div class="sec-tag">Безопасность и полный контроль</div>
|
|
472
|
+
<h2>Подключение к вашему брокеру и управление из Telegram и MAX</h2>
|
|
473
|
+
<p class="sec-sub">Деньги всегда остаются на вашем личном счёте в Т-Банке, БКС, Финаме или Алоре. Ключи зашифрованы по стандарту AES-256-GCM.</p>
|
|
474
|
+
</div>
|
|
475
|
+
<div class="bento">
|
|
476
|
+
<div class="bcard">
|
|
477
|
+
<div class="bicon">👁</div>
|
|
478
|
+
<h3>1. Режим «Только чтение»</h3>
|
|
479
|
+
<p>Безопасное знакомство: платформа синхронизирует ваш реальный портфель и показывает расхождение с модельным прогнозом без отправки заявок.</p>
|
|
480
|
+
</div>
|
|
481
|
+
<div class="bcard">
|
|
482
|
+
<div class="bicon">📲</div>
|
|
483
|
+
<h3>2. Подтверждение в 1 клик в мессенджере</h3>
|
|
484
|
+
<p>По закрытию свечи сервер сам считает целые лоты Мосбиржи и присылает карточку в Telegram и MAX. Вы нажимаете «Исполнить» прямо с телефона.</p>
|
|
485
|
+
</div>
|
|
486
|
+
<div class="bcard">
|
|
487
|
+
<div class="bicon">🤖</div>
|
|
488
|
+
<h3>3. Полный автомат с предохранителями</h3>
|
|
489
|
+
<p>Лимитные заявки с защитным отступом цены, жёстким лимитом выделенной суммы, дневным стопом по просадке и пропуском первых/последних 10 минут сессии.</p>
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
492
|
+
</section>
|
|
493
|
+
|
|
494
|
+
<section id="pricing" class="reveal">
|
|
495
|
+
<div class="sec-head">
|
|
496
|
+
<div class="sec-tag">Прозрачные условия</div>
|
|
497
|
+
<h2>Тарифные планы</h2>
|
|
498
|
+
<p class="sec-sub">Начните с бесплатного доступа к витрине и демо-счёту или подключите полный автопилот для своих брокерских счетов.</p>
|
|
499
|
+
</div>
|
|
500
|
+
<div class="period-wrap">
|
|
501
|
+
<div class="period-seg" id="months-seg">
|
|
502
|
+
<button data-m="1" class="active">1 месяц</button>
|
|
503
|
+
<button data-m="3">3 месяца (−10%)</button>
|
|
504
|
+
<button data-m="12">12 месяцев (−20%)</button>
|
|
505
|
+
</div>
|
|
506
|
+
</div>
|
|
507
|
+
<div class="plans">
|
|
508
|
+
<div class="plan">
|
|
509
|
+
<h3>Старт (Витрина)</h3>
|
|
510
|
+
<div class="plan-desc">Для знакомства с платформой и проверки сигналов на демо-счёте</div>
|
|
511
|
+
<div class="price">0 ₽ <span>/ навсегда</span></div>
|
|
512
|
+
<ul>
|
|
513
|
+
<li>Доступ к витрине эталонных стратегий и карте рынка</li>
|
|
514
|
+
<li>Свежие новости Мосбиржи с ИИ-разметкой тональности</li>
|
|
515
|
+
<li>1 виртуальный счёт в реальном времени со сверкой коридора</li>
|
|
516
|
+
<li>До 2 собственных исторических исследований в сутки</li>
|
|
517
|
+
</ul>
|
|
518
|
+
<a class="btn" href="/?terminal=1">Начать бесплатно</a>
|
|
519
|
+
</div>
|
|
520
|
+
|
|
521
|
+
<div class="plan featured">
|
|
522
|
+
<div class="pop-ribbon">Выбор инвесторов</div>
|
|
523
|
+
<h3>Инвестор (Автопилот)</h3>
|
|
524
|
+
<div class="plan-desc">Для активного управления своим счётом с подтверждением в Telegram и MAX</div>
|
|
525
|
+
<div class="price" id="price-investor">3 900 ₽ <span>/ мес</span></div>
|
|
526
|
+
<ul>
|
|
527
|
+
<li>Подключение до 2 брокерских счетов (Т-Банк, БКС, Финам, Алор)</li>
|
|
528
|
+
<li>Авто-расписание заявок и кнопки подтверждения в <b>Telegram и MAX</b></li>
|
|
529
|
+
<li>Самообучение правил торговли и стресс-тест устойчивости</li>
|
|
530
|
+
<li>До 3 независимых виртуальных счетов и 30 экспериментов в сутки</li>
|
|
531
|
+
<li>Голосовой и текстовый ИИ-управляющий «Стратег»</li>
|
|
532
|
+
</ul>
|
|
533
|
+
<button class="btn primary" data-buy="investor">Оформить подписку</button>
|
|
534
|
+
</div>
|
|
535
|
+
|
|
536
|
+
<div class="plan">
|
|
537
|
+
<h3>Профи / Лаборатория</h3>
|
|
538
|
+
<div class="plan-desc">Максимальная мощность, нейросеть-трансформер и до 5 счетов</div>
|
|
539
|
+
<div class="price" id="price-pro">9 900 ₽ <span>/ мес</span></div>
|
|
540
|
+
<ul>
|
|
541
|
+
<li>До 5 брокерских и 5 виртуальных счетов параллельно</li>
|
|
542
|
+
<li>Обучение нейросети-трансформера и свои подключения к моделям ИИ</li>
|
|
543
|
+
<li>До 500 экспериментов в сутки, приоритет в очереди расчётов</li>
|
|
544
|
+
<li>Все функции автопилота, предохранителей и ботов Telegram / MAX</li>
|
|
545
|
+
</ul>
|
|
546
|
+
<button class="btn primary" data-buy="pro">Оформить подписку</button>
|
|
547
|
+
</div>
|
|
548
|
+
</div>
|
|
549
|
+
</section>
|
|
550
|
+
|
|
551
|
+
<footer>
|
|
552
|
+
<div style="display:flex;justify-content:space-between;flex-wrap:wrap;gap:12px">
|
|
553
|
+
<div>© 2026 НейроАльфа · Платформа алгоритмического анализа рынка акций</div>
|
|
554
|
+
<div style="display:flex;gap:18px">
|
|
555
|
+
<a href="/legal#offer" style="color:#93c5fd">Публичная оферта</a>
|
|
556
|
+
<a href="/legal#privacy" style="color:#93c5fd">Политика конфиденциальности</a>
|
|
557
|
+
<a href="/legal#risks" style="color:#93c5fd">Декларация о рисках</a>
|
|
558
|
+
</div>
|
|
559
|
+
</div>
|
|
560
|
+
<div style="margin-top:8px">Сервис является аналитическим программным обеспечением (SaaS) и не является индивидуальной инвестиционной рекомендацией или доверительным управлением. Результаты на исторических данных не гарантируют доходности в будущем.</div>
|
|
561
|
+
</footer>
|
|
562
|
+
</div>
|
|
563
|
+
|
|
564
|
+
<div class="modal" id="auth-modal" hidden>
|
|
565
|
+
<div class="modal-box">
|
|
566
|
+
<h3 id="auth-title">Вход в НейроАльфа</h3>
|
|
567
|
+
<form id="auth-form">
|
|
568
|
+
<input name="name" id="auth-name" placeholder="Ваше имя" hidden>
|
|
569
|
+
<input name="email" type="email" required placeholder="Email">
|
|
570
|
+
<input name="password" type="password" required minlength="6" placeholder="Пароль (не короче 6 символов)">
|
|
571
|
+
<label style="display:flex;gap:8px;align-items:flex-start;font-size:12.5px;color:var(--muted)"><input type="checkbox" required checked style="width:16px;height:16px;margin-top:2px"><span>Принимаю условия <a href="/legal#offer" target="_blank" style="color:#93c5fd">Публичной оферты</a>, <a href="/legal#privacy" target="_blank" style="color:#93c5fd">Политики конфиденциальности</a> и <a href="/legal#risks" target="_blank" style="color:#93c5fd">Декларации о рисках</a></span></label>
|
|
572
|
+
<div class="err" id="auth-err"></div>
|
|
573
|
+
<button class="btn primary" type="submit" id="auth-submit">Войти</button>
|
|
574
|
+
<button class="btn" type="button" id="auth-toggle">Нет аккаунта? Зарегистрироваться</button>
|
|
575
|
+
<button class="btn" type="button" id="auth-close" style="border:0;color:var(--muted)">Закрыть</button>
|
|
576
|
+
</form>
|
|
577
|
+
</div>
|
|
578
|
+
</div>
|
|
579
|
+
|
|
580
|
+
<script>
|
|
581
|
+
// ---------- 1. Анимированный граф перетоков капитала на фоне Hero ----------
|
|
582
|
+
(() => {
|
|
583
|
+
const cv = document.getElementById('hero-canvas');
|
|
584
|
+
if (!cv) return;
|
|
585
|
+
const ctx = cv.getContext('2d');
|
|
586
|
+
const tickers = ['SBER','LKOH','YDEX','ROSN','NVTK','GMKN','TATN','PLZL','CHMF','GAZP','SNGS','MOEX','T','PHOR','NLMK','ALRS'];
|
|
587
|
+
let W = 0, H = 0;
|
|
588
|
+
const resize = () => { W = cv.width = cv.offsetWidth; H = cv.height = cv.offsetHeight; };
|
|
589
|
+
window.addEventListener('resize', resize);
|
|
590
|
+
resize();
|
|
591
|
+
const nodes = tickers.map((sym, i) => ({
|
|
592
|
+
sym,
|
|
593
|
+
x: (0.1 + 0.8 * ((i * 0.618) % 1)) * (W || 1200),
|
|
594
|
+
y: (0.15 + 0.7 * (((i * 0.382) + 0.2) % 1)) * (H || 600),
|
|
595
|
+
vx: (Math.random() - 0.5) * 0.35,
|
|
596
|
+
vy: (Math.random() - 0.5) * 0.35,
|
|
597
|
+
r: i < 5 ? 5 : 3.5,
|
|
598
|
+
}));
|
|
599
|
+
const pulses = [];
|
|
600
|
+
setInterval(() => {
|
|
601
|
+
if (pulses.length < 8) {
|
|
602
|
+
const a = Math.floor(Math.random() * nodes.length);
|
|
603
|
+
const b = (a + 1 + Math.floor(Math.random() * (nodes.length - 1))) % nodes.length;
|
|
604
|
+
pulses.push({ a, b, t: 0 });
|
|
605
|
+
}
|
|
606
|
+
}, 450);
|
|
607
|
+
|
|
608
|
+
function frame() {
|
|
609
|
+
ctx.clearRect(0, 0, W, H);
|
|
610
|
+
for (const n of nodes) {
|
|
611
|
+
n.x += n.vx; n.y += n.vy;
|
|
612
|
+
if (n.x < 40 || n.x > W - 40) n.vx *= -1;
|
|
613
|
+
if (n.y < 30 || n.y > H - 30) n.vy *= -1;
|
|
614
|
+
}
|
|
615
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
616
|
+
for (let j = i + 1; j < nodes.length; j++) {
|
|
617
|
+
const dx = nodes[i].x - nodes[j].x, dy = nodes[i].y - nodes[j].y;
|
|
618
|
+
const d = Math.hypot(dx, dy);
|
|
619
|
+
if (d < 220) {
|
|
620
|
+
ctx.strokeStyle = `rgba(96, 165, 250, ${(1 - d / 220) * 0.22})`;
|
|
621
|
+
ctx.lineWidth = 1;
|
|
622
|
+
ctx.beginPath(); ctx.moveTo(nodes[i].x, nodes[i].y); ctx.lineTo(nodes[j].x, nodes[j].y); ctx.stroke();
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
for (let k = pulses.length - 1; k >= 0; k--) {
|
|
627
|
+
const p = pulses[k];
|
|
628
|
+
p.t += 0.018;
|
|
629
|
+
if (p.t >= 1) { pulses.splice(k, 1); continue; }
|
|
630
|
+
const na = nodes[p.a], nb = nodes[p.b];
|
|
631
|
+
const px = na.x + (nb.x - na.x) * p.t, py = na.y + (nb.y - na.y) * p.t;
|
|
632
|
+
ctx.fillStyle = '#38bdf8';
|
|
633
|
+
ctx.beginPath(); ctx.arc(px, py, 3, 0, Math.PI * 2); ctx.fill();
|
|
634
|
+
}
|
|
635
|
+
for (const n of nodes) {
|
|
636
|
+
ctx.fillStyle = '#60a5fa';
|
|
637
|
+
ctx.beginPath(); ctx.arc(n.x, n.y, n.r, 0, Math.PI * 2); ctx.fill();
|
|
638
|
+
ctx.fillStyle = 'rgba(203, 213, 225, 0.55)';
|
|
639
|
+
ctx.font = '600 11px system-ui';
|
|
640
|
+
ctx.fillText(n.sym, n.x + 8, n.y + 4);
|
|
641
|
+
}
|
|
642
|
+
requestAnimationFrame(frame);
|
|
643
|
+
}
|
|
644
|
+
requestAnimationFrame(frame);
|
|
645
|
+
})();
|
|
646
|
+
|
|
647
|
+
// ---------- 2. Плавное появление секций при прокрутке ----------
|
|
648
|
+
const io = new IntersectionObserver((entries) => {
|
|
649
|
+
entries.forEach((e) => { if (e.isIntersecting) e.target.classList.add('in'); });
|
|
650
|
+
}, { threshold: 0.12 });
|
|
651
|
+
document.querySelectorAll('.reveal').forEach((el) => io.observe(el));
|
|
652
|
+
|
|
653
|
+
// ---------- 3. Витрина, тарифы и авторизация ----------
|
|
654
|
+
const fmt = (v, d = 1) => (v > 0 ? '+' : '') + Number(v || 0).toFixed(d).replace('.', ',');
|
|
655
|
+
let selectedMonths = 1;
|
|
656
|
+
let isReg = false;
|
|
657
|
+
let pendingPlan = null;
|
|
658
|
+
let currentUser = null;
|
|
659
|
+
|
|
660
|
+
function sparkSvg(curve, mktCurve, id) {
|
|
661
|
+
if (!curve || curve.length < 2) return '';
|
|
662
|
+
const W = 460, H = 130;
|
|
663
|
+
const norm = (arr) => arr.map((p) => (p.equity / arr[0].equity) * 100);
|
|
664
|
+
const a = norm(curve), m = mktCurve?.length > 1 ? norm(mktCurve) : [];
|
|
665
|
+
const all = [...a, ...m];
|
|
666
|
+
const lo = Math.min(...all), hi = Math.max(...all, lo + 1);
|
|
667
|
+
const xy = (arr) => arr.map((v, i) => [((i / (arr.length - 1)) * W), (H - 10 - ((v - lo) / (hi - lo)) * (H - 20))]);
|
|
668
|
+
const pts = (arr) => xy(arr).map(([x, y]) => `${x.toFixed(1)},${y.toFixed(1)}`).join(' ');
|
|
669
|
+
const areaPts = `0,${H} ${pts(a)} ${W},${H}`;
|
|
670
|
+
return `<svg viewBox="0 0 ${W} ${H}" width="100%" height="${H}" style="overflow:visible">
|
|
671
|
+
<defs>
|
|
672
|
+
<linearGradient id="g${id}" x1="0" y1="0" x2="0" y2="1">
|
|
673
|
+
<stop offset="0%" stop-color="#3b82f6" stop-opacity="0.38"/>
|
|
674
|
+
<stop offset="100%" stop-color="#3b82f6" stop-opacity="0.0"/>
|
|
675
|
+
</linearGradient>
|
|
676
|
+
</defs>
|
|
677
|
+
<polygon points="${areaPts}" fill="url(#g${id})"/>
|
|
678
|
+
${m.length ? `<polyline points="${pts(m)}" fill="none" stroke="#64748b" stroke-width="1.6" stroke-dasharray="4 3"/>` : ''}
|
|
679
|
+
<polyline points="${pts(a)}" fill="none" stroke="#3b82f6" stroke-width="2.8"/>
|
|
680
|
+
</svg>`;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function renderCabinet() {
|
|
684
|
+
const cab = document.getElementById('cabinet-box');
|
|
685
|
+
if (!cab) return;
|
|
686
|
+
if (!currentUser) {
|
|
687
|
+
cab.hidden = true;
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
cab.hidden = false;
|
|
691
|
+
const key = currentUser.apiKey || '';
|
|
692
|
+
const cmd = key ? `npx neuroalpha --key ${key} --cloud ${location.origin}` : 'npx neuroalpha';
|
|
693
|
+
const cliEl = document.getElementById('landing-cli-cmd');
|
|
694
|
+
if (cliEl && key) cliEl.textContent = cmd;
|
|
695
|
+
const untilStr = currentUser.planUntil > Date.now()
|
|
696
|
+
? `оплачен до ${new Date(currentUser.planUntil).toLocaleDateString('ru-RU')}`
|
|
697
|
+
: 'бессрочно';
|
|
698
|
+
cab.innerHTML = `<div class="cab-card">
|
|
699
|
+
<div class="cab-grid">
|
|
700
|
+
<div>
|
|
701
|
+
<div class="sec-tag" style="margin-bottom:4px">Личный кабинет · Аккаунт активен</div>
|
|
702
|
+
<h3 style="margin:0 0 8px;font-size:22px">${currentUser.name} <span style="color:var(--muted);font-weight:400;font-size:15px">(${currentUser.email})</span></h3>
|
|
703
|
+
<div style="margin-bottom:14px;font-size:14.5px;color:#cbd5e1">
|
|
704
|
+
Тариф: <b class="pos">${currentUser.planName}</b> (${untilStr}) · Доступно: до <b>${currentUser.limits?.maxBrokerAccounts ?? 0}</b> брокерских и <b>${currentUser.limits?.maxVirtualAccounts ?? 1}</b> виртуальных счетов, до <b>${currentUser.limits?.maxRunsPerDay ?? 2}</b> экспериментов/сутки.
|
|
705
|
+
</div>
|
|
706
|
+
<div style="margin-bottom:8px;font-size:13px;color:var(--muted)">Ваш персональный API-ключ (для CLI / npx и API):</div>
|
|
707
|
+
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center">
|
|
708
|
+
<span class="key-pill" id="cab-apikey">${key}</span>
|
|
709
|
+
<button type="button" class="btn" id="cab-copy-key" style="padding:7px 14px;font-size:13px">Скопировать APIKey</button>
|
|
710
|
+
<button type="button" class="btn" id="cab-regen-key" style="padding:7px 14px;font-size:13px">Перевыпустить</button>
|
|
711
|
+
</div>
|
|
712
|
+
</div>
|
|
713
|
+
<div style="display:flex;flex-direction:column;gap:10px">
|
|
714
|
+
<div style="font-size:13px;color:var(--muted)">Быстрый запуск на вашем ПК (через npx):</div>
|
|
715
|
+
<div class="cli-box" style="padding:11px 14px;font-size:12.5px">
|
|
716
|
+
<code>${cmd}</code>
|
|
717
|
+
<button type="button" class="btn" id="cab-copy-cmd" style="padding:5px 10px;font-size:12px">Копировать</button>
|
|
718
|
+
</div>
|
|
719
|
+
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:4px">
|
|
720
|
+
<a class="btn primary" href="/?terminal=1" style="flex:1">Открыть веб-терминал →</a>
|
|
721
|
+
<a class="btn" href="#pricing">Сменить / продлить тариф</a>
|
|
722
|
+
</div>
|
|
723
|
+
</div>
|
|
724
|
+
</div>
|
|
725
|
+
</div>`;
|
|
726
|
+
|
|
727
|
+
document.getElementById('cab-copy-key')?.addEventListener('click', (e) => {
|
|
728
|
+
navigator.clipboard?.writeText(key);
|
|
729
|
+
e.target.textContent = '✓ Скопировано';
|
|
730
|
+
setTimeout(() => { e.target.textContent = 'Скопировать APIKey'; }, 2000);
|
|
731
|
+
});
|
|
732
|
+
document.getElementById('cab-copy-cmd')?.addEventListener('click', (e) => {
|
|
733
|
+
navigator.clipboard?.writeText(cmd);
|
|
734
|
+
e.target.textContent = '✓ Скопировано';
|
|
735
|
+
setTimeout(() => { e.target.textContent = 'Копировать'; }, 2000);
|
|
736
|
+
});
|
|
737
|
+
document.getElementById('cab-regen-key')?.addEventListener('click', async () => {
|
|
738
|
+
if (!confirm('Перевыпустить API-ключ? Прежний ключ перестанет работать.')) return;
|
|
739
|
+
const r = await fetch('/api/auth/api-key/regenerate', { method: 'POST' });
|
|
740
|
+
const j = await r.json();
|
|
741
|
+
if (j.user) { currentUser = j.user; renderCabinet(); }
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
document.getElementById('btn-copy-landing-cli')?.addEventListener('click', (e) => {
|
|
746
|
+
const txt = document.getElementById('landing-cli-cmd')?.textContent || 'npx neuroalpha';
|
|
747
|
+
navigator.clipboard?.writeText(txt);
|
|
748
|
+
e.target.textContent = '✓ Скопировано';
|
|
749
|
+
setTimeout(() => { e.target.textContent = 'Скопировать'; }, 2000);
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
async function loadShowcase() {
|
|
753
|
+
try {
|
|
754
|
+
const me = await (await fetch('/api/auth/me')).json();
|
|
755
|
+
currentUser = me.user;
|
|
756
|
+
if (currentUser) {
|
|
757
|
+
document.getElementById('btn-login-open').textContent = `${currentUser.name} · ${currentUser.planName}`;
|
|
758
|
+
renderCabinet();
|
|
759
|
+
}
|
|
760
|
+
const { runs } = await (await fetch('/api/public/showcase')).json();
|
|
761
|
+
const box = document.getElementById('showcase-list');
|
|
762
|
+
if (!runs?.length) return;
|
|
763
|
+
box.innerHTML = runs.map((r, idx) => `<div class="sc-card">
|
|
764
|
+
<div class="sc-head">
|
|
765
|
+
<div>
|
|
766
|
+
<div style="font-weight:700;font-size:18px">${r.title}</div>
|
|
767
|
+
<div style="font-size:13px;color:var(--muted)">Горизонт прогноза: ${r.params.modelHorizon || 2} ч · Бумаг в портфеле: ${r.params.modelTopK || 2}</div>
|
|
768
|
+
</div>
|
|
769
|
+
<div class="sc-ret pos">${fmt(r.metrics.returnPct, 1)}%</div>
|
|
770
|
+
</div>
|
|
771
|
+
${sparkSvg(r.curve, r.marketCurve, idx)}
|
|
772
|
+
<div class="sc-kpis">
|
|
773
|
+
<div><span class="muted">Индекс Мосбиржи</span><b class="neg">${fmt(r.metrics.marketPct, 1)}%</b></div>
|
|
774
|
+
<div><span class="muted">Сделок за период</span><b>${r.metrics.trades || 0}</b></div>
|
|
775
|
+
<div><span class="muted">На 1 ₽ убытка</span><b>${fmt(r.metrics.profitFactor, 2).replace('+','')} ₽</b></div>
|
|
776
|
+
</div>
|
|
777
|
+
<a class="btn" href="/?run=${r.id}">Открыть эту стратегию в терминале →</a>
|
|
778
|
+
</div>`).join('');
|
|
779
|
+
} catch {}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function updatePrices() {
|
|
783
|
+
const disc = selectedMonths >= 12 ? 0.8 : selectedMonths >= 3 ? 0.9 : 1;
|
|
784
|
+
const inv = Math.round(3900 * selectedMonths * disc);
|
|
785
|
+
const pro = Math.round(9900 * selectedMonths * disc);
|
|
786
|
+
const lbl = selectedMonths === 1 ? '/ мес' : `за ${selectedMonths} мес.`;
|
|
787
|
+
document.getElementById('price-investor').innerHTML = `${inv.toLocaleString('ru-RU')} ₽ <span>${lbl}</span>`;
|
|
788
|
+
document.getElementById('price-pro').innerHTML = `${pro.toLocaleString('ru-RU')} ₽ <span>${lbl}</span>`;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
document.getElementById('months-seg').addEventListener('click', (e) => {
|
|
792
|
+
const b = e.target.closest('button[data-m]');
|
|
793
|
+
if (!b) return;
|
|
794
|
+
selectedMonths = Number(b.dataset.m);
|
|
795
|
+
document.querySelectorAll('#months-seg button').forEach((x) => x.classList.toggle('active', x === b));
|
|
796
|
+
updatePrices();
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
function openAuth(reg = false) {
|
|
800
|
+
isReg = reg;
|
|
801
|
+
document.getElementById('auth-modal').hidden = false;
|
|
802
|
+
document.getElementById('auth-name').hidden = !reg;
|
|
803
|
+
document.getElementById('auth-title').textContent = reg ? 'Регистрация в НейроАльфа' : 'Вход в НейроАльфа';
|
|
804
|
+
document.getElementById('auth-submit').textContent = reg ? 'Создать аккаунт' : 'Войти';
|
|
805
|
+
document.getElementById('auth-toggle').textContent = reg ? 'Уже есть аккаунт? Войти' : 'Нет аккаунта? Зарегистрироваться';
|
|
806
|
+
document.getElementById('auth-err').textContent = '';
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
document.getElementById('btn-login-open').addEventListener('click', () => openAuth(false));
|
|
810
|
+
document.getElementById('btn-hero-start').addEventListener('click', () => openAuth(true));
|
|
811
|
+
document.getElementById('auth-close').addEventListener('click', () => { document.getElementById('auth-modal').hidden = true; });
|
|
812
|
+
document.getElementById('auth-toggle').addEventListener('click', () => openAuth(!isReg));
|
|
813
|
+
|
|
814
|
+
async function startCheckout(plan) {
|
|
815
|
+
try {
|
|
816
|
+
const res = await fetch('/api/billing/checkout', {
|
|
817
|
+
method: 'POST',
|
|
818
|
+
headers: { 'Content-Type': 'application/json' },
|
|
819
|
+
body: JSON.stringify({ plan, months: selectedMonths, returnOrigin: location.origin }),
|
|
820
|
+
});
|
|
821
|
+
const j = await res.json();
|
|
822
|
+
if (!res.ok) throw new Error(j.error || 'Ошибка перехода к оплате');
|
|
823
|
+
if (j.confirmationUrl) location.href = j.confirmationUrl;
|
|
824
|
+
} catch (e) {
|
|
825
|
+
alert(e.message);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
document.querySelectorAll('[data-buy]').forEach((b) => b.addEventListener('click', () => {
|
|
830
|
+
const plan = b.dataset.buy;
|
|
831
|
+
if (!currentUser) {
|
|
832
|
+
pendingPlan = plan;
|
|
833
|
+
openAuth(true);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
startCheckout(plan);
|
|
837
|
+
}));
|
|
838
|
+
|
|
839
|
+
document.getElementById('auth-form').addEventListener('submit', async (e) => {
|
|
840
|
+
e.preventDefault();
|
|
841
|
+
const f = e.target.elements;
|
|
842
|
+
const url = isReg ? '/api/auth/register' : '/api/auth/login';
|
|
843
|
+
try {
|
|
844
|
+
const res = await fetch(url, {
|
|
845
|
+
method: 'POST',
|
|
846
|
+
headers: { 'Content-Type': 'application/json' },
|
|
847
|
+
body: JSON.stringify({ name: f.name.value, email: f.email.value, password: f.password.value }),
|
|
848
|
+
});
|
|
849
|
+
const j = await res.json();
|
|
850
|
+
if (!res.ok) throw new Error(j.error || 'Ошибка авторизации');
|
|
851
|
+
currentUser = j.user;
|
|
852
|
+
document.getElementById('auth-modal').hidden = true;
|
|
853
|
+
document.getElementById('btn-login-open').textContent = `${currentUser.name} · ${currentUser.planName}`;
|
|
854
|
+
renderCabinet();
|
|
855
|
+
if (pendingPlan) {
|
|
856
|
+
const p = pendingPlan;
|
|
857
|
+
pendingPlan = null;
|
|
858
|
+
await startCheckout(p);
|
|
859
|
+
} else {
|
|
860
|
+
document.getElementById('cabinet-box')?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
861
|
+
}
|
|
862
|
+
} catch (err) {
|
|
863
|
+
document.getElementById('auth-err').textContent = err.message;
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
loadShowcase();
|
|
868
|
+
</script>
|
|
869
|
+
</body>
|
|
870
|
+
</html>
|