neuroalpha 0.6.9 → 0.6.10
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 +1 -1
- package/server/model/research.js +23 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neuroalpha",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.10",
|
|
4
4
|
"description": "НейроАльфа — ИИ-автопилот для рынка акций Мосбиржи: эмбеддинги акций, самообучение правил, коннекторы Т-Банк / БКС / Финам / Алор и боты Telegram / MAX",
|
|
5
5
|
"author": "Roman A. Perepelkin",
|
|
6
6
|
"license": "UNLICENSED",
|
package/server/model/research.js
CHANGED
|
@@ -12,6 +12,7 @@ import { strategyDefaults } from '../config.js';
|
|
|
12
12
|
import { ENTRY_EDGE_MULT, TIER1_LIQUID_SYMBOLS, symbolEdgeMult, computeTokenEdgeMults, QUARANTINE_DECAY, isStockQuarantined, canEnterIntraday, shouldExitBeforeNight, isRegimeShocked, isMarketDown } from './edge.js';
|
|
13
13
|
import { buildDividendCalendar, nextDividendAt, dividendRules, DIV_TAX } from './dividends.js';
|
|
14
14
|
import { simulateStrategy, alignSeries } from './simulator.js';
|
|
15
|
+
import { STRATEGY_POLICY, exitHoldRatio } from './policy.js';
|
|
15
16
|
import { ensureLqdtHistory } from '../data/lqdt.js';
|
|
16
17
|
|
|
17
18
|
const DAY = 86400000;
|
|
@@ -38,7 +39,8 @@ export { isMarketDown };
|
|
|
38
39
|
// 15: защита от биржевых планок (high === low) и блокировка утренних шортов 2-го эшелона в первые 20 минут сессии (07:00–07:20 МСК)
|
|
39
40
|
// 16: главная оценка — симулятор с правилами живого контура (simulator.js), аварийный стоп 7%
|
|
40
41
|
// 17: капитал в LQDT как залог, половинный размер 2-го эшелона, строки с плечом 3 и 5
|
|
41
|
-
|
|
42
|
+
// 18: глубина сопровождения позиции и вечернего окна входов holdBars=13 (130 мин) во всех контурах симуляции
|
|
43
|
+
const DATA_VERSION = 18;
|
|
42
44
|
|
|
43
45
|
const AUTO_LOCKED = { modelHorizon: 2.5, modelMinEdge: 2.5, modelExitRatio: 0.95 };
|
|
44
46
|
function reuseParamsKey(params) {
|
|
@@ -395,7 +397,9 @@ async function run(runId, p, state) {
|
|
|
395
397
|
const stockPnl = new Float64Array(N), stockCnt = new Float64Array(N);
|
|
396
398
|
const stockAllowed = (i, edgeRatio = 0) => !autoAdapt || !isStockQuarantined(stockCnt[i], stockPnl[i], edgeRatio);
|
|
397
399
|
let curExitRatio = autoAdapt ? 0.95 : exitRatio;
|
|
398
|
-
const
|
|
400
|
+
const holdBars = autoAdapt ? (STRATEGY_POLICY.holdBars ?? H) : H;
|
|
401
|
+
const horizonMin = Math.max(90, holdBars * BAR_INTERVAL);
|
|
402
|
+
const stepBars = autoAdapt ? Math.max(3, Math.floor(holdBars / 5)) : holdBars;
|
|
399
403
|
const maxSlotWeight = dir === 'long' ? Math.min(0.58, Math.max(1 / K, 1.8 / K)) : Math.min(0.75, 3.0 / (2 * K));
|
|
400
404
|
let closedThisBar = new Set();
|
|
401
405
|
|
|
@@ -458,21 +462,21 @@ async function run(runId, p, state) {
|
|
|
458
462
|
}
|
|
459
463
|
}
|
|
460
464
|
}
|
|
461
|
-
const timeOk = !autoAdapt || canEnterIntraday(barTs,
|
|
465
|
+
const timeOk = !autoAdapt || canEnterIntraday(barTs, holdBars, BAR_INTERVAL);
|
|
462
466
|
const canOpenAt = (i, edgeRatio = 0) => timeOk && !held.has(i) && !closedThisBar.has(i) && stockAllowed(i, edgeRatio) && canEnter(i, t);
|
|
463
467
|
const okLong = (i) => !divRule(i, t)?.blockLong && (!needTrade || (tokens[i].has[tIn] && tokens[i].open[tIn] > 0)) && (!autoAdapt || !isRegimeShocked(ds, i, t, 'long'));
|
|
464
|
-
const okShort = (i) => !divRule(i, t)?.blockShort && (!shortable || shortable.has(tokens[i].symbol)) && (!needTrade || (tokens[i].has[tIn] && tokens[i].open[tIn] > 0)) && (!autoAdapt || (!isRegimeShocked(ds, i, t, 'short') && canEnterIntraday(barTs,
|
|
468
|
+
const okShort = (i) => !divRule(i, t)?.blockShort && (!shortable || shortable.has(tokens[i].symbol)) && (!needTrade || (tokens[i].has[tIn] && tokens[i].open[tIn] > 0)) && (!autoAdapt || (!isRegimeShocked(ds, i, t, 'short') && canEnterIntraday(barTs, holdBars, BAR_INTERVAL, 'short', tokens[i].symbol)));
|
|
465
469
|
let order, entries;
|
|
466
470
|
if (dir !== 'long') {
|
|
467
471
|
const edge = (i) => pr[i] * sg[i] * Math.sqrt(H);
|
|
468
472
|
const need = effMinEdge * 2 * realCost * needMult;
|
|
469
473
|
const exitNeed = need * curExitRatio;
|
|
470
|
-
|
|
471
|
-
// держим позицию, пока ожидаемое движение выше порога выхода (первые два полушага — мягкий порог, чтобы не резать сделку от шума)
|
|
474
|
+
// держим позицию по единой рампе удержания exitHoldRatio (до 45 мин — 0.35, затем плавный подъём к 0.95 к концу глубины сопровождения horizonMin)
|
|
472
475
|
for (const [i, h] of [...held]) {
|
|
473
476
|
const e = edge(i);
|
|
474
477
|
const barsHeld = tIn - h.entryT;
|
|
475
|
-
const
|
|
478
|
+
const holdRatio = exitHoldRatio(barsHeld * BAR_INTERVAL, horizonMin, { baseEndRatio: curExitRatio, autoAdapt }, STRATEGY_POLICY);
|
|
479
|
+
const req = need * holdRatio * symMult[i];
|
|
476
480
|
const shocked = autoAdapt && isRegimeShocked(ds, i, t, h.side);
|
|
477
481
|
if (shocked || !(h.side === 'long' ? e > req : e < -req) || !liquid(i, t)) {
|
|
478
482
|
closePos(i, h, tIn, false, shocked ? 'Защита от экстремального сквиза/обвала' : 'Прогноз ослаб');
|
|
@@ -513,11 +517,11 @@ async function run(runId, p, state) {
|
|
|
513
517
|
const edge = (i) => pr[i] * sg[i] * Math.sqrt(H);
|
|
514
518
|
const need = effMinEdge * 2 * realCost * needMult;
|
|
515
519
|
const exitNeed = need * curExitRatio;
|
|
516
|
-
const earlyExitNeed = need * 0.35;
|
|
517
520
|
for (const [i, h] of [...held]) {
|
|
518
521
|
const e = edge(i);
|
|
519
522
|
const barsHeld = tIn - h.entryT;
|
|
520
|
-
const
|
|
523
|
+
const holdRatio = exitHoldRatio(barsHeld * BAR_INTERVAL, horizonMin, { baseEndRatio: curExitRatio, autoAdapt }, STRATEGY_POLICY);
|
|
524
|
+
const req = need * holdRatio * symMult[i];
|
|
521
525
|
const shocked = autoAdapt && isRegimeShocked(ds, i, t, 'long');
|
|
522
526
|
if (shocked || !(e > req) || !liquid(i, t)) closePos(i, h, tIn, false, shocked ? 'Защита от экстремального обвала' : '');
|
|
523
527
|
}
|
|
@@ -1120,7 +1124,9 @@ async function runStabilityLoop(targetId, runRow, st) {
|
|
|
1120
1124
|
const stockPnl = new Float64Array(N), stockCnt = new Float64Array(N);
|
|
1121
1125
|
const stockAllowed = (i, edgeRatio = 0) => !autoAdapt || !isStockQuarantined(stockCnt[i], stockPnl[i], edgeRatio);
|
|
1122
1126
|
const curExitRatio = autoAdapt ? 0.95 : exitRatio;
|
|
1123
|
-
const
|
|
1127
|
+
const holdBars = autoAdapt ? (STRATEGY_POLICY.holdBars ?? H) : H;
|
|
1128
|
+
const horizonMin = Math.max(90, holdBars * BAR_INTERVAL);
|
|
1129
|
+
const stepBars = autoAdapt ? Math.max(3, Math.floor(holdBars / 5)) : holdBars;
|
|
1124
1130
|
const maxSlotWeight = dir === 'long' ? Math.min(0.58, Math.max(1 / K, 1.8 / K)) : Math.min(0.75, 3.0 / (2 * K));
|
|
1125
1131
|
let closedThisBar = new Set();
|
|
1126
1132
|
const closePos = (i, h, t) => {
|
|
@@ -1158,17 +1164,18 @@ async function runStabilityLoop(targetId, runRow, st) {
|
|
|
1158
1164
|
if (shouldExitBeforeNight(barTs, tsOf(h.entryT), stepBars, BAR_INTERVAL)) closePos(i, h, tIn);
|
|
1159
1165
|
}
|
|
1160
1166
|
}
|
|
1161
|
-
const timeOk = !autoAdapt || canEnterIntraday(barTs,
|
|
1167
|
+
const timeOk = !autoAdapt || canEnterIntraday(barTs, holdBars, BAR_INTERVAL);
|
|
1162
1168
|
const canOpenAt = (i, edgeRatio = 0) => timeOk && !held.has(i) && !closedThisBar.has(i) && stockAllowed(i, edgeRatio) && canEnter(i, t);
|
|
1163
1169
|
const okLong = (i) => !divRule(i, t)?.blockLong && (!autoAdapt || !isRegimeShocked(ds, i, t, 'long'));
|
|
1164
|
-
const okShort = (i) => !divRule(i, t)?.blockShort && (!autoAdapt || (!isRegimeShocked(ds, i, t, 'short') && canEnterIntraday(barTs,
|
|
1170
|
+
const okShort = (i) => !divRule(i, t)?.blockShort && (!autoAdapt || (!isRegimeShocked(ds, i, t, 'short') && canEnterIntraday(barTs, holdBars, BAR_INTERVAL, 'short', tokens[i].symbol)));
|
|
1165
1171
|
const edge = (i) => pr[i] * sg[i] * Math.sqrt(H);
|
|
1166
|
-
const need = effMinEdge * 2 * realCost * ENTRY_EDGE_MULT, exitNeed = need * curExitRatio
|
|
1172
|
+
const need = effMinEdge * 2 * realCost * ENTRY_EDGE_MULT, exitNeed = need * curExitRatio;
|
|
1167
1173
|
let order, entries;
|
|
1168
1174
|
if (dir !== 'long') {
|
|
1169
1175
|
for (const [i, h] of [...held]) {
|
|
1170
1176
|
const e = edge(i), barsHeld = tIn - h.entryT;
|
|
1171
|
-
const
|
|
1177
|
+
const holdRatio = exitHoldRatio(barsHeld * BAR_INTERVAL, horizonMin, { baseEndRatio: curExitRatio, autoAdapt }, STRATEGY_POLICY);
|
|
1178
|
+
const req = need * holdRatio * symMult[i];
|
|
1172
1179
|
const shocked = autoAdapt && isRegimeShocked(ds, i, t, h.side);
|
|
1173
1180
|
if (shocked || !(h.side === 'long' ? e > req : e < -req) || !liquid(i, t)) closePos(i, h, tIn);
|
|
1174
1181
|
}
|
|
@@ -1196,7 +1203,8 @@ async function runStabilityLoop(targetId, runRow, st) {
|
|
|
1196
1203
|
} else if (effMinEdge > 0) {
|
|
1197
1204
|
for (const [i, h] of [...held]) {
|
|
1198
1205
|
const e = edge(i), barsHeld = tIn - h.entryT;
|
|
1199
|
-
const
|
|
1206
|
+
const holdRatio = exitHoldRatio(barsHeld * BAR_INTERVAL, horizonMin, { baseEndRatio: curExitRatio, autoAdapt }, STRATEGY_POLICY);
|
|
1207
|
+
const req = need * holdRatio * symMult[i];
|
|
1200
1208
|
const shocked = autoAdapt && isRegimeShocked(ds, i, t, 'long');
|
|
1201
1209
|
if (shocked || !(e > req) || !liquid(i, t)) closePos(i, h, tIn);
|
|
1202
1210
|
}
|