dexbot 1.5.2 → 1.5.3
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/CHANGELOG.md +17 -0
- package/analysis/ama_fitting/package.json +1 -1
- package/analysis/resolve_source.ts +3 -0
- package/analysis/results/ama_sweep_results_lp_pool_133_1h.json +2455 -0
- package/analysis/results/bot_fitting_results_lp_pool_133_1h.json +218 -0
- package/analysis/tradingview/README.md +5 -1
- package/analysis/tradingview/analyze_tradingview.ts +27 -1
- package/analysis/tradingview/h-bts_tradingview.html +1570 -0
- package/analysis/tradingview/t-bts_tradingview.html +1570 -0
- package/analysis/tradingview/tradingview_uplot_chart_generator.ts +351 -22
- package/analysis/trend_detection/package.json +1 -1
- package/claw/package.json +1 -1
- package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
- package/claw/runtimes/openclaw-plugin/package.json +1 -1
- package/claw/tests/test_claw_mcp_transport.ts +2 -2
- package/dist/analysis/resolve_source.d.ts.map +1 -1
- package/dist/analysis/resolve_source.js +3 -0
- package/dist/analysis/resolve_source.js.map +1 -1
- package/dist/analysis/tradingview/analyze_tradingview.d.ts +3 -0
- package/dist/analysis/tradingview/analyze_tradingview.d.ts.map +1 -1
- package/dist/analysis/tradingview/analyze_tradingview.js +27 -2
- package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -1
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts.map +1 -1
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js +348 -22
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js.map +1 -1
- package/dist/market_adapter/core/asymmetric_bounds.d.ts +33 -1
- package/dist/market_adapter/core/asymmetric_bounds.d.ts.map +1 -1
- package/dist/market_adapter/core/asymmetric_bounds.js +38 -1
- package/dist/market_adapter/core/asymmetric_bounds.js.map +1 -1
- package/dist/modules/dexbot_class.d.ts +2 -18
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.d.ts +37 -37
- package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.js +655 -556
- package/dist/modules/dexbot_cow_runtime.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +31 -51
- package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
- package/dist/modules/order/grid.d.ts.map +1 -1
- package/dist/modules/order/grid.js +28 -37
- package/dist/modules/order/grid.js.map +1 -1
- package/dist/modules/order/manager.d.ts +2 -1
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +10 -1
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/sync_engine.d.ts.map +1 -1
- package/dist/modules/order/sync_engine.js +74 -0
- package/dist/modules/order/sync_engine.js.map +1 -1
- package/dist/modules/order/utils/math.d.ts +9 -11
- package/dist/modules/order/utils/math.d.ts.map +1 -1
- package/dist/modules/order/utils/math.js +9 -11
- package/dist/modules/order/utils/math.js.map +1 -1
- package/dist/modules/order/utils/order.d.ts +51 -16
- package/dist/modules/order/utils/order.d.ts.map +1 -1
- package/dist/modules/order/utils/order.js +203 -29
- package/dist/modules/order/utils/order.js.map +1 -1
- package/dist/modules/order/utils/system.d.ts +1 -30
- package/dist/modules/order/utils/system.d.ts.map +1 -1
- package/dist/modules/order/utils/system.js +31 -98
- package/dist/modules/order/utils/system.js.map +1 -1
- package/dist/modules/order/utils/validate.d.ts +3 -2
- package/dist/modules/order/utils/validate.d.ts.map +1 -1
- package/dist/modules/order/utils/validate.js +5 -1
- package/dist/modules/order/utils/validate.js.map +1 -1
- package/docs/DEXBOT_COMPARISON.md +3 -3
- package/docs/EVOLUTION.md +6 -5
- package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
- package/docs/README.md +2 -2
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import { MARKET_ADAPTER } from '../../modules/constants.js';
|
|
5
|
-
import { escapeHtml, serializeJsonForScript, UPLOT_SHARED_SCRIPT } from '../chart_utils.js';
|
|
5
|
+
import { escapeHtml, serializeJsonForScript, UPLOT_SHARED_SCRIPT, embedFunctionSources } from '../chart_utils.js';
|
|
6
6
|
import { cursorCSS, uplotBgCSS } from '../chart_css.js';
|
|
7
7
|
import { zoomResetScript } from '../chart_ui.js';
|
|
8
8
|
import { normalizeCandle } from '../math_utils.js';
|
|
@@ -10,6 +10,14 @@ import { PATHS } from '../../modules/paths.js';
|
|
|
10
10
|
import { getStorage } from '../../modules/storage/index.js';
|
|
11
11
|
const { readJSON } = getStorage();
|
|
12
12
|
import { getErrorMessage } from '../../modules/utils/errors.js';
|
|
13
|
+
import { parseRelativeMultiplier } from '../../modules/order/utils/math.js';
|
|
14
|
+
import { computeAverageAmaSlopePct } from '../../market_adapter/core/strategies/dynamic_weight_series.js';
|
|
15
|
+
import {
|
|
16
|
+
resolveBaseBounds,
|
|
17
|
+
computeAsymmetricBoundsMetrics,
|
|
18
|
+
applyAsymmetricBounds,
|
|
19
|
+
applyNarrowingSideGuard,
|
|
20
|
+
} from '../../market_adapter/core/asymmetric_bounds.js';
|
|
13
21
|
|
|
14
22
|
|
|
15
23
|
function inferBaseIntervalSeconds(candles: any[], fallback: any = 3600) {
|
|
@@ -103,6 +111,28 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
103
111
|
|
|
104
112
|
const marketProfiles = data.marketProfiles || loadMarketProfiles();
|
|
105
113
|
const defaultAmaConfig = resolveAmaDefaults({ meta, data, marketProfiles });
|
|
114
|
+
const rangeSlope = {
|
|
115
|
+
lookbackBars: MARKET_ADAPTER.DYNAMIC_WEIGHT_AMA_LOOKBACK_BARS,
|
|
116
|
+
maxSlopePct: MARKET_ADAPTER.DYNAMIC_WEIGHT_AMA_MAX_SLOPE_PCT,
|
|
117
|
+
neutralZonePct: MARKET_ADAPTER.DYNAMIC_WEIGHT_AMA_NEUTRAL_ZONE_PCT,
|
|
118
|
+
maxSlopeOffset: MARKET_ADAPTER.DYNAMIC_WEIGHT_ASYMMETRIC_OFFSET_CLAMP,
|
|
119
|
+
maxAsymmetryFactor: MARKET_ADAPTER.ASYMMETRIC_BOUNDS_MAX_ASYMMETRY_FACTOR,
|
|
120
|
+
minScaleSlots: MARKET_ADAPTER.ASYMMETRIC_BOUNDS_MIN_SCALE_SLOTS,
|
|
121
|
+
};
|
|
122
|
+
function defaultRangeSpan(input: { rangeSpan?: unknown; grid?: { minPrice?: unknown; maxPrice?: unknown } | null }): number {
|
|
123
|
+
const cli = Number(input.rangeSpan);
|
|
124
|
+
if (Number.isFinite(cli)) return Math.min(2, Math.max(1.25, cli));
|
|
125
|
+
const down = parseRelativeMultiplier(input.grid?.minPrice);
|
|
126
|
+
const up = parseRelativeMultiplier(input.grid?.maxPrice);
|
|
127
|
+
if (down != null && down > 1 && up != null && up > 1) return Math.min(2, Math.max(1.25, (down + up) / 2));
|
|
128
|
+
const vd = Number(input.grid?.minPrice);
|
|
129
|
+
const vu = Number(input.grid?.maxPrice);
|
|
130
|
+
if (Number.isFinite(vd) && Number.isFinite(vu) && vd > 0 && vu > vd) {
|
|
131
|
+
const ref = Math.sqrt(vd * vu);
|
|
132
|
+
return Math.min(2, Math.max(1.25, (ref / vd + vu / ref) / 2));
|
|
133
|
+
}
|
|
134
|
+
return 1.55;
|
|
135
|
+
}
|
|
106
136
|
const defaults = {
|
|
107
137
|
smaPeriod: Math.max(1, Math.round(data.smaPeriod ?? 500)),
|
|
108
138
|
amaDefaults: defaultAmaConfig,
|
|
@@ -111,6 +141,21 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
111
141
|
vwapEnabled: data.vwapEnabled === true,
|
|
112
142
|
vwapBars: Math.max(5, Math.round(data.vwapBars ?? 500)),
|
|
113
143
|
priceScale: data.priceScale === 'linear' ? 'linear' : 'log',
|
|
144
|
+
rangeEnabled: data.rangeEnabled !== false,
|
|
145
|
+
rangeScaleEnabled: data.rangeScaleEnabled === true,
|
|
146
|
+
rangeWidthPct: Number.isFinite(Number(data.rangeWidthPct)) && Number(data.rangeWidthPct) > 0
|
|
147
|
+
? Number(data.rangeWidthPct)
|
|
148
|
+
: 2,
|
|
149
|
+
rangeSpan: defaultRangeSpan(data),
|
|
150
|
+
grid: data.grid && data.grid.minPrice != null && data.grid.maxPrice != null ? {
|
|
151
|
+
minPrice: data.grid.minPrice,
|
|
152
|
+
maxPrice: data.grid.maxPrice,
|
|
153
|
+
incrementPercent: Number(data.grid.incrementPercent) > 0 ? Number(data.grid.incrementPercent) : null,
|
|
154
|
+
maxAsymmetryFactor: Number.isFinite(Number(data.grid.maxAsymmetryFactor)) && Number(data.grid.maxAsymmetryFactor) > 0
|
|
155
|
+
? Number(data.grid.maxAsymmetryFactor)
|
|
156
|
+
: rangeSlope.maxAsymmetryFactor,
|
|
157
|
+
} : null,
|
|
158
|
+
rangeSlope,
|
|
114
159
|
};
|
|
115
160
|
|
|
116
161
|
const assetLabelA = meta.assetA?.symbol || meta.assetA?.id || 'Asset A';
|
|
@@ -134,6 +179,12 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
134
179
|
vwapEnabled: defaults.vwapEnabled,
|
|
135
180
|
vwapBars: defaults.vwapBars,
|
|
136
181
|
priceScale: defaults.priceScale,
|
|
182
|
+
rangeEnabled: defaults.rangeEnabled,
|
|
183
|
+
rangeScaleEnabled: defaults.rangeScaleEnabled,
|
|
184
|
+
rangeWidthPct: defaults.rangeWidthPct,
|
|
185
|
+
rangeSpan: defaults.rangeSpan,
|
|
186
|
+
grid: defaults.grid,
|
|
187
|
+
rangeSlope,
|
|
137
188
|
defaultPairMode,
|
|
138
189
|
assetLabelA,
|
|
139
190
|
assetLabelB,
|
|
@@ -177,7 +228,8 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
177
228
|
#brand { display: flex; flex-direction: column; gap: 4px; min-width: 240px; }
|
|
178
229
|
#title { font-size: 15px; font-weight: 700; color: #f5f8fb; }
|
|
179
230
|
#subtitle { font-size: 11px; color: #b9c6d4; text-transform: uppercase; letter-spacing: 0.6px; }
|
|
180
|
-
#toolbar { display: flex; flex-
|
|
231
|
+
#toolbar { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
|
|
232
|
+
.toolbar-row { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px 12px; align-items: center; }
|
|
181
233
|
.group { display: inline-flex; align-items: center; gap: 8px; padding-left: 12px; border-left: 1px solid #263241; }
|
|
182
234
|
.group:first-child { padding-left: 0; border-left: 0; }
|
|
183
235
|
:root {
|
|
@@ -399,7 +451,8 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
399
451
|
${cursorCSS()}
|
|
400
452
|
@media (max-width: 980px) {
|
|
401
453
|
#topbar { flex-direction: column; align-items: flex-start; }
|
|
402
|
-
#toolbar {
|
|
454
|
+
#toolbar { align-items: flex-start; }
|
|
455
|
+
.toolbar-row { justify-content: flex-start; }
|
|
403
456
|
.group { padding-left: 0; border-left: 0; flex-wrap: wrap; }
|
|
404
457
|
#legend { min-width: 0; max-width: calc(100vw - 24px); }
|
|
405
458
|
}
|
|
@@ -413,9 +466,7 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
413
466
|
<div id="subtitle">${escapeHtml(poolLabel)} · ${escapeHtml(pairLabelNormal)} · ${escapeHtml(intervalLabel)} base · indicators from 1h · volume · uPlot</div>
|
|
414
467
|
</div>
|
|
415
468
|
<div id="toolbar">
|
|
416
|
-
<div class="
|
|
417
|
-
${timeframes.map((item: any) => `<button class="time-btn${item.label === defaultTimeframe.label ? ' active' : ''}" data-timeframe="${escapeHtml(item.label)}"${item.enabled ? '' : ' disabled'}>${escapeHtml(item.label)}</button>`).join('')}
|
|
418
|
-
</div>
|
|
469
|
+
<div class="toolbar-row">
|
|
419
470
|
<div class="group">
|
|
420
471
|
<div class="indicator">
|
|
421
472
|
<span class="tag">pair</span>
|
|
@@ -429,6 +480,11 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
429
480
|
<span class="tag">scale</span>
|
|
430
481
|
<button type="button" class="scale-toggle${defaults.priceScale === 'linear' ? ' is-linear' : ''}" id="scale-toggle" data-scale="${escapeHtml(defaults.priceScale)}" aria-label="Toggle price scale" title="Toggle price scale">${defaults.priceScale === 'linear' ? 'Linear' : 'Log'}</button>
|
|
431
482
|
</div>
|
|
483
|
+
</div>
|
|
484
|
+
<div class="group" id="tf-group">
|
|
485
|
+
${timeframes.map((item: any) => `<button class="time-btn${item.label === defaultTimeframe.label ? ' active' : ''}" data-timeframe="${escapeHtml(item.label)}"${item.enabled ? '' : ' disabled'}>${escapeHtml(item.label)}</button>`).join('')}
|
|
486
|
+
</div>
|
|
487
|
+
<div class="group">
|
|
432
488
|
<div class="indicator">
|
|
433
489
|
<label><input type="checkbox" id="sma-toggle"${defaults.smaEnabled ? ' checked' : ''}> SMA</label>
|
|
434
490
|
<span class="tag">period</span>
|
|
@@ -447,6 +503,10 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
447
503
|
<button type="button" class="step-btn" id="vwap-bars-dec">▼</button>
|
|
448
504
|
</span>
|
|
449
505
|
</div>
|
|
506
|
+
</div>
|
|
507
|
+
</div>
|
|
508
|
+
<div class="toolbar-row">
|
|
509
|
+
<div class="group">
|
|
450
510
|
<div class="indicator">
|
|
451
511
|
<label><input type="checkbox" id="ama-toggle"${defaults.amaEnabled ? ' checked' : ''}> AMA</label>
|
|
452
512
|
<span class="tag">er</span>
|
|
@@ -469,13 +529,22 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
469
529
|
</span>
|
|
470
530
|
<button type="button" class="reset-btn" id="ama-reset">Reset</button>
|
|
471
531
|
</div>
|
|
472
|
-
<div class="indicator"
|
|
532
|
+
<div class="indicator" title="Range min/max built only from the live AMA price (green above, red below); Scale sizes it by AMA slope like the grid build">
|
|
533
|
+
<label><input type="checkbox" id="range-toggle"${defaults.rangeEnabled ? ' checked' : ''}> Range</label>
|
|
534
|
+
<label title="Range Scaling: size the band by AMA slope like the grid build (trend side widens, opposite tightens) and fit the price axis to it"><input type="checkbox" id="range-scale-toggle"${defaults.rangeScaleEnabled ? ' checked' : ''}> Scale</label>
|
|
535
|
+
<span id="range-grid-wrap" style="display:${defaults.grid ? 'inline' : 'none'}" title="x-range around AMA (default = bot grid setting)">
|
|
536
|
+
<span class="tag">grid</span>
|
|
537
|
+
<input type="range" id="range-span" min="1.25" max="2" step="0.05" value="${defaults.rangeSpan.toFixed(2)}" style="width:90px;vertical-align:middle">
|
|
538
|
+
<span id="range-span-val" style="font-size:11px;color:#8b949e;width:40px;display:inline-block;text-align:right">${defaults.rangeSpan.toFixed(2)}x</span>
|
|
539
|
+
</span>
|
|
540
|
+
</div>
|
|
541
|
+
<div class="indicator">
|
|
473
542
|
<label><input type="checkbox" id="ama-init-offset-toggle"> Init Offset</label>
|
|
474
543
|
<input type="range" id="ama-init-offset" min="-50" max="50" value="0" step="1" style="width:120px;vertical-align:middle" disabled>
|
|
475
544
|
<span id="ama-init-offset-val" style="font-size:11px;color:#8b949e;width:32px;display:inline-block;text-align:right">0%</span>
|
|
476
545
|
</div>
|
|
477
546
|
</div>
|
|
478
|
-
|
|
547
|
+
</div>
|
|
479
548
|
</div>
|
|
480
549
|
</div>
|
|
481
550
|
<div id="chart-shell">
|
|
@@ -495,6 +564,7 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
495
564
|
<span class="legend-item"><span class="legend-dot" style="background:#a855f7"></span><span class="legend-label">AMA Off</span> <span class="legend-value" id="legend-ama-off">-</span></span>
|
|
496
565
|
|
|
497
566
|
<span class="legend-item"><span class="legend-dot" style="background:#93c5fd"></span><span class="legend-label">VWMA</span> <span class="legend-value" id="legend-vwap">-</span></span>
|
|
567
|
+
<span class="legend-item"><span class="legend-dot" id="legend-range-dot" style="background:#8b949e"></span><span class="legend-label">Range</span> <span class="legend-value" id="legend-range">-</span></span>
|
|
498
568
|
</div>
|
|
499
569
|
</div>
|
|
500
570
|
<div id="price-chart" title="Wheel: zoom time (ulos = tyhjaa tilaa datan ymparilla) · Drag: siirra aika- ja hintanakymaa · Wheel/drag hinta-akselilla: zoomaa hintaa · Double-click hinta-akselia: autofit"></div>
|
|
@@ -530,6 +600,14 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
530
600
|
: Number(payload.amaDefaults?.slowPeriod || ${MARKET_ADAPTER.AMAS.AMA3.slowPeriod});
|
|
531
601
|
let currentVwapEnabled = state.vwapEnabled ?? !!payload.vwapEnabled;
|
|
532
602
|
let currentVwapBars = Number.isFinite(state.vwapBars) ? state.vwapBars : Number(payload.vwapBars || 500);
|
|
603
|
+
let currentRangeEnabled = state.rangeEnabled ?? !!payload.rangeEnabled;
|
|
604
|
+
let currentRangeScaleEnabled = state.rangeScaleEnabled ?? !!payload.rangeScaleEnabled;
|
|
605
|
+
let currentRangeWidthPct = Number.isFinite(state.rangeWidthPct) && Number(state.rangeWidthPct) > 0
|
|
606
|
+
? Number(state.rangeWidthPct)
|
|
607
|
+
: (Number.isFinite(Number(payload.rangeWidthPct)) && Number(payload.rangeWidthPct) > 0 ? Number(payload.rangeWidthPct) : 2);
|
|
608
|
+
let currentRangeSpan = Number.isFinite(state.rangeSpan) && Number(state.rangeSpan) > 0
|
|
609
|
+
? Math.min(2, Math.max(1.25, Number(state.rangeSpan)))
|
|
610
|
+
: (Number.isFinite(Number(payload.rangeSpan)) && Number(payload.rangeSpan) > 0 ? Math.min(2, Math.max(1.25, Number(payload.rangeSpan))) : 1.55);
|
|
533
611
|
let currentAmaInitOffsetEnabled = false;
|
|
534
612
|
let currentAmaInitOffset = Number.isFinite(state.amaInitOffset) ? state.amaInitOffset : 0;
|
|
535
613
|
let currentPriceScale = state.priceScale || payload.priceScale || 'log';
|
|
@@ -551,6 +629,9 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
551
629
|
let currentSmaInit = [];
|
|
552
630
|
let currentSmaInitOff = [];
|
|
553
631
|
let currentAmaOff = [];
|
|
632
|
+
let currentRangeUpper = [];
|
|
633
|
+
let currentRangeLower = [];
|
|
634
|
+
let currentRangeTrend = [];
|
|
554
635
|
let priceChart = null;
|
|
555
636
|
let volumeChart = null;
|
|
556
637
|
let lastRenderedPriceScale = null;
|
|
@@ -749,6 +830,10 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
749
830
|
amaSlowPeriod: currentAmaSlowPeriod,
|
|
750
831
|
vwapEnabled: currentVwapEnabled,
|
|
751
832
|
vwapBars: currentVwapBars,
|
|
833
|
+
rangeEnabled: currentRangeEnabled,
|
|
834
|
+
rangeScaleEnabled: currentRangeScaleEnabled,
|
|
835
|
+
rangeWidthPct: currentRangeWidthPct,
|
|
836
|
+
rangeSpan: currentRangeSpan,
|
|
752
837
|
amaInitOffset: currentAmaInitOffset,
|
|
753
838
|
priceScale: currentPriceScale,
|
|
754
839
|
pairMode: currentPairMode,
|
|
@@ -1078,6 +1163,106 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1078
1163
|
vwapCache.set(cacheKey, out);
|
|
1079
1164
|
return out;
|
|
1080
1165
|
}
|
|
1166
|
+
// Bot-grid range band: min/max derive ONLY from the live AMA price.
|
|
1167
|
+
// All math below runs the canonical market_adapter/grid sources,
|
|
1168
|
+
// embedded verbatim (not hand-copied): computeAverageAmaSlopePct,
|
|
1169
|
+
// applyAsymmetricBounds (+ metrics/base), applyNarrowingSideGuard,
|
|
1170
|
+
// parseRelativeMultiplier. With Range Scaling on, the AMA slope sizes
|
|
1171
|
+
// the real range: trend side widens, opposite side tightens. Painted
|
|
1172
|
+
// symmetrically always: green [AMA, upper], red [lower, AMA].
|
|
1173
|
+
// Without usable grid shape falls back to a width% envelope around AMA.
|
|
1174
|
+
// Never reads candles, pair-display mapping, or axis/zoom state —
|
|
1175
|
+
// inversion and timeframe sampling apply to the AMA first.
|
|
1176
|
+
${embedFunctionSources([computeAverageAmaSlopePct, resolveBaseBounds, computeAsymmetricBoundsMetrics, applyAsymmetricBounds, applyNarrowingSideGuard, parseRelativeMultiplier])}
|
|
1177
|
+
function computeRangeBand(baseAma) {
|
|
1178
|
+
const n = Array.isArray(baseAma) ? baseAma.length : 0;
|
|
1179
|
+
const upper = new Array(n).fill(null);
|
|
1180
|
+
const lower = new Array(n).fill(null);
|
|
1181
|
+
const trend = new Array(n).fill(0);
|
|
1182
|
+
const slopeCfg = payload.rangeSlope || {};
|
|
1183
|
+
const gridCfg = payload.grid || null;
|
|
1184
|
+
const lookback = Math.max(1, Math.round(Number(slopeCfg.lookbackBars) || 9));
|
|
1185
|
+
const maxSlope = Number(slopeCfg.maxSlopePct) > 0 ? Number(slopeCfg.maxSlopePct) : 0.09;
|
|
1186
|
+
const neutral = Number(slopeCfg.neutralZonePct) >= 0 ? Number(slopeCfg.neutralZonePct) : 0;
|
|
1187
|
+
const maxSlopeOffset = Number(slopeCfg.maxSlopeOffset) > 0 ? Number(slopeCfg.maxSlopeOffset) : 0.5;
|
|
1188
|
+
const maxAsym = gridCfg && Number(gridCfg.maxAsymmetryFactor) > 0
|
|
1189
|
+
? Number(gridCfg.maxAsymmetryFactor)
|
|
1190
|
+
: (Number(slopeCfg.maxAsymmetryFactor) > 0 ? Number(slopeCfg.maxAsymmetryFactor) : 0.35);
|
|
1191
|
+
const widthPct = Number(currentRangeWidthPct) > 0 ? Number(currentRangeWidthPct) : 2;
|
|
1192
|
+
const inc = gridCfg && Number(gridCfg.incrementPercent) > 0 ? Number(gridCfg.incrementPercent) : null;
|
|
1193
|
+
const minSlots = Math.floor(Number(gridCfg && gridCfg.minScaleSlots) > 0 ? Number(gridCfg.minScaleSlots) : (Number(slopeCfg.minScaleSlots) || 0));
|
|
1194
|
+
// User x-range span (slider, default = bot grid ratio): the only
|
|
1195
|
+
// width input — min = AMA/span, max = AMA*span. Grid config only
|
|
1196
|
+
// selects the grid path vs the width% fallback, never a price.
|
|
1197
|
+
const span = Math.min(2, Math.max(1.25, Number(currentRangeSpan) > 0 ? Number(currentRangeSpan) : 1.55));
|
|
1198
|
+
const gMinN = Number(gridCfg && gridCfg.minPrice);
|
|
1199
|
+
const gMaxN = Number(gridCfg && gridCfg.maxPrice);
|
|
1200
|
+
const multOk = (v) => { const m = parseRelativeMultiplier(v); return m != null && m > 1; };
|
|
1201
|
+
const hasGridShape = gridCfg != null && (
|
|
1202
|
+
(multOk(gridCfg.minPrice) && multOk(gridCfg.maxPrice)) ||
|
|
1203
|
+
(Number.isFinite(gMinN) && Number.isFinite(gMaxN) && gMinN > 0 && gMaxN > gMinN));
|
|
1204
|
+
const slopeScaling = !!currentRangeScaleEnabled;
|
|
1205
|
+
if (!currentAmaEnabled || n === 0) return { upper, lower, trend };
|
|
1206
|
+
for (let i = 0; i < n; i++) {
|
|
1207
|
+
const ama = baseAma[i];
|
|
1208
|
+
const past = i - lookback >= 0 ? baseAma[i - lookback] : null;
|
|
1209
|
+
if (!Number.isFinite(ama) || ama <= 0 || !Number.isFinite(past) || past <= 0) continue;
|
|
1210
|
+
const slopePct = computeAverageAmaSlopePct(ama, past, lookback);
|
|
1211
|
+
if (slopePct == null || !Number.isFinite(slopePct)) continue;
|
|
1212
|
+
const dir = Math.abs(slopePct) <= neutral ? 0 : (slopePct > 0 ? 1 : -1);
|
|
1213
|
+
const strength = Math.min(Math.abs(slopePct) / maxSlope, 1);
|
|
1214
|
+
if (!hasGridShape) {
|
|
1215
|
+
// Width% envelope: symmetric around AMA; scaling sizes it
|
|
1216
|
+
// by slope strength ("real range"), otherwise full width.
|
|
1217
|
+
const w = slopeScaling ? strength * widthPct : widthPct;
|
|
1218
|
+
if (!(w > 0)) continue;
|
|
1219
|
+
const half = ama * w / 100;
|
|
1220
|
+
if (!Number.isFinite(half) || half <= 0) continue;
|
|
1221
|
+
upper[i] = ama + half;
|
|
1222
|
+
lower[i] = Math.max(ama - half, ama * 1e-6);
|
|
1223
|
+
trend[i] = dir;
|
|
1224
|
+
continue;
|
|
1225
|
+
}
|
|
1226
|
+
// Symmetric base from this bar's AMA and the user span —
|
|
1227
|
+
// no chart or config price level.
|
|
1228
|
+
let rMin = ama / span;
|
|
1229
|
+
let rMax = ama * span;
|
|
1230
|
+
if (slopeScaling && dir !== 0) {
|
|
1231
|
+
// Canonical tilt: ama_slope_model slope offset applied
|
|
1232
|
+
// through asymmetric_bounds, then the narrowing-side guard.
|
|
1233
|
+
const trendName = dir > 0 ? 'UP' : 'DOWN';
|
|
1234
|
+
const slopeOffset = clamp(slopePct / maxSlope, -1, 1) * maxSlopeOffset;
|
|
1235
|
+
const tilt = applyAsymmetricBounds({
|
|
1236
|
+
centerPrice: ama,
|
|
1237
|
+
minPrice: ama / span,
|
|
1238
|
+
maxPrice: ama * span,
|
|
1239
|
+
trend: trendName,
|
|
1240
|
+
slopeOffset,
|
|
1241
|
+
maxSlopeOffset,
|
|
1242
|
+
maxAsymmetryFactor: maxAsym,
|
|
1243
|
+
});
|
|
1244
|
+
const guard = applyNarrowingSideGuard({
|
|
1245
|
+
centerPrice: ama,
|
|
1246
|
+
minPrice: tilt.resolvedMinPrice,
|
|
1247
|
+
maxPrice: tilt.resolvedMaxPrice,
|
|
1248
|
+
trend: trendName,
|
|
1249
|
+
incrementPercent: inc,
|
|
1250
|
+
minScaleSlots: minSlots,
|
|
1251
|
+
});
|
|
1252
|
+
rMin = guard.minPrice;
|
|
1253
|
+
rMax = guard.maxPrice;
|
|
1254
|
+
}
|
|
1255
|
+
if (!Number.isFinite(rMin) || !Number.isFinite(rMax) || rMin <= 0 || rMax <= rMin) continue;
|
|
1256
|
+
upper[i] = rMax;
|
|
1257
|
+
lower[i] = rMin;
|
|
1258
|
+
trend[i] = dir;
|
|
1259
|
+
}
|
|
1260
|
+
return { upper, lower, trend };
|
|
1261
|
+
}
|
|
1262
|
+
function sampleTrendByIndex(series, idxs) {
|
|
1263
|
+
if (!Array.isArray(series) || !Array.isArray(idxs)) return [];
|
|
1264
|
+
return idxs.map((idx) => (Number.isFinite(idx) && series[idx] != null ? series[idx] : 0));
|
|
1265
|
+
}
|
|
1081
1266
|
function lowerBound(arr, value) {
|
|
1082
1267
|
let lo = 0;
|
|
1083
1268
|
let hi = arr.length;
|
|
@@ -1098,6 +1283,25 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1098
1283
|
let end = Math.min(xs.length, lowerBound(xs, maxX) + 2);
|
|
1099
1284
|
let min = Infinity;
|
|
1100
1285
|
let max = -Infinity;
|
|
1286
|
+
// Range scaling active: fit the price axis to the full envelope
|
|
1287
|
+
// (lower ↔ upper through AMA) instead of the candles, so the
|
|
1288
|
+
// switch visibly adjusts the range. Falls through to the candle
|
|
1289
|
+
// fit when the band has no finite values in view.
|
|
1290
|
+
if (currentRangeEnabled && currentRangeScaleEnabled && currentAmaEnabled) {
|
|
1291
|
+
let bmin = Infinity;
|
|
1292
|
+
let bmax = -Infinity;
|
|
1293
|
+
for (let i = start; i < end; i++) {
|
|
1294
|
+
const a = currentAma[i];
|
|
1295
|
+
const up = currentRangeUpper[i];
|
|
1296
|
+
const lo = currentRangeLower[i];
|
|
1297
|
+
if (Number.isFinite(a) && a > 0) { if (a < bmin) bmin = a; if (a > bmax) bmax = a; }
|
|
1298
|
+
if (Number.isFinite(up) && up > 0) { if (up < bmin) bmin = up; if (up > bmax) bmax = up; }
|
|
1299
|
+
if (Number.isFinite(lo) && lo > 0) { if (lo < bmin) bmin = lo; if (lo > bmax) bmax = lo; }
|
|
1300
|
+
}
|
|
1301
|
+
if (Number.isFinite(bmin) && Number.isFinite(bmax) && bmin > 0 && bmax > bmin) {
|
|
1302
|
+
return [bmin * 0.97, bmax * 1.03];
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1101
1305
|
for (let i = start; i < end; i++) {
|
|
1102
1306
|
const lo = currentLow[i];
|
|
1103
1307
|
const hi = currentHigh[i];
|
|
@@ -1174,11 +1378,24 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1174
1378
|
refreshLegend();
|
|
1175
1379
|
saveState();
|
|
1176
1380
|
}
|
|
1381
|
+
function hideRangeBandImmediate() {
|
|
1382
|
+
currentRangeUpper = new Array(currentCandles.length).fill(null);
|
|
1383
|
+
currentRangeLower = new Array(currentCandles.length).fill(null);
|
|
1384
|
+
currentRangeTrend = new Array(currentCandles.length).fill(0);
|
|
1385
|
+
// The band is a draw hook with no uPlot series: nothing else
|
|
1386
|
+
// repaints on hide, so redraw explicitly (stale pixels otherwise linger).
|
|
1387
|
+
if (priceChart && typeof priceChart.redraw === 'function') priceChart.redraw();
|
|
1388
|
+
refreshLegend();
|
|
1389
|
+
saveState();
|
|
1390
|
+
}
|
|
1177
1391
|
function hideAmaSeriesImmediate() {
|
|
1178
1392
|
currentAma = new Array(currentCandles.length).fill(null);
|
|
1179
1393
|
currentAmaOff = new Array(currentCandles.length).fill(null);
|
|
1180
1394
|
currentSmaInit = new Array(currentCandles.length).fill(null);
|
|
1181
1395
|
currentSmaInitOff = new Array(currentCandles.length).fill(null);
|
|
1396
|
+
currentRangeUpper = new Array(currentCandles.length).fill(null);
|
|
1397
|
+
currentRangeLower = new Array(currentCandles.length).fill(null);
|
|
1398
|
+
currentRangeTrend = new Array(currentCandles.length).fill(0);
|
|
1182
1399
|
if (Array.isArray(currentPriceData) && currentPriceData.length >= 11) {
|
|
1183
1400
|
currentPriceData[6] = currentAma;
|
|
1184
1401
|
currentPriceData[8] = currentSmaInit;
|
|
@@ -1219,6 +1436,18 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1219
1436
|
document.getElementById('legend-sma-init').textContent = Number.isFinite(currentSmaInit[idx]) ? fmtPrice(currentSmaInit[idx]) : '-';
|
|
1220
1437
|
document.getElementById('legend-off-init').textContent = Number.isFinite(currentSmaInitOff[idx]) ? fmtPrice(currentSmaInitOff[idx]) : '-';
|
|
1221
1438
|
document.getElementById('legend-ama-off').textContent = Number.isFinite(currentAmaOff[idx]) ? fmtPrice(currentAmaOff[idx]) : '-';
|
|
1439
|
+
const rangeDir = currentRangeTrend[idx];
|
|
1440
|
+
const rangeDot = document.getElementById('legend-range-dot');
|
|
1441
|
+
const rangeVal = document.getElementById('legend-range');
|
|
1442
|
+
if (rangeDot) rangeDot.style.background = rangeDir === 1 ? '#26a69a' : (rangeDir === -1 ? '#ef5350' : '#8b949e');
|
|
1443
|
+
if (rangeVal) {
|
|
1444
|
+
const ru = currentRangeUpper[idx];
|
|
1445
|
+
const rl = currentRangeLower[idx];
|
|
1446
|
+
const ama = currentAma[idx];
|
|
1447
|
+
if (currentRangeEnabled && currentAmaEnabled && Number.isFinite(ru) && ru > ama && Number.isFinite(rl) && rl > 0 && rl < ama && ama > 0) {
|
|
1448
|
+
rangeVal.textContent = '+' + ((ru - ama) / ama * 100).toFixed(1) + '% / -' + ((ama - rl) / ama * 100).toFixed(1) + '%';
|
|
1449
|
+
} else rangeVal.textContent = '-';
|
|
1450
|
+
}
|
|
1222
1451
|
document.getElementById('legend-vwap').textContent = Number.isFinite(currentVwap[idx]) ? fmtPrice(currentVwap[idx]) : '-';
|
|
1223
1452
|
}
|
|
1224
1453
|
function candlePlugin() {
|
|
@@ -1362,6 +1591,80 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1362
1591
|
hooks: { drawAxes: [drawVolumeBars] },
|
|
1363
1592
|
};
|
|
1364
1593
|
}
|
|
1594
|
+
function rangeBandPlugin() {
|
|
1595
|
+
function drawRangeBand(u) {
|
|
1596
|
+
if (!currentRangeEnabled || !currentAmaEnabled || !currentCandles.length) return;
|
|
1597
|
+
const xs = u.data[0];
|
|
1598
|
+
if (!Array.isArray(xs) || xs.length === 0) return;
|
|
1599
|
+
u.ctx.save();
|
|
1600
|
+
u.ctx.beginPath();
|
|
1601
|
+
u.ctx.rect(u.bbox.left, u.bbox.top, u.bbox.width, u.bbox.height);
|
|
1602
|
+
u.ctx.clip();
|
|
1603
|
+
// Behind candles/AMA lines (same trick as the dynamic-weight signal background).
|
|
1604
|
+
u.ctx.globalCompositeOperation = 'destination-over';
|
|
1605
|
+
const UP_FILL = 'rgba(38,166,154,0.16)';
|
|
1606
|
+
const DOWN_FILL = 'rgba(239,83,80,0.16)';
|
|
1607
|
+
// Symmetric envelope, always both sides: green [AMA, upper]
|
|
1608
|
+
// plus red [lower, AMA] along the whole AMA, tilted by the
|
|
1609
|
+
// grid's range scaling. Paint per contiguous valid segment.
|
|
1610
|
+
let segTop = [];
|
|
1611
|
+
let segBot = [];
|
|
1612
|
+
const flush = () => {
|
|
1613
|
+
if (segTop.length > 1) {
|
|
1614
|
+
u.ctx.fillStyle = UP_FILL;
|
|
1615
|
+
u.ctx.beginPath();
|
|
1616
|
+
u.ctx.moveTo(segTop[0][0], segTop[0][1]);
|
|
1617
|
+
for (let k = 1; k < segTop.length; k++) u.ctx.lineTo(segTop[k][0], segTop[k][1]);
|
|
1618
|
+
for (let k = segTop.length - 1; k >= 0; k--) u.ctx.lineTo(segTop[k][0], segTop[k][2]);
|
|
1619
|
+
u.ctx.closePath();
|
|
1620
|
+
u.ctx.fill();
|
|
1621
|
+
}
|
|
1622
|
+
if (segBot.length > 1) {
|
|
1623
|
+
u.ctx.fillStyle = DOWN_FILL;
|
|
1624
|
+
u.ctx.beginPath();
|
|
1625
|
+
u.ctx.moveTo(segBot[0][0], segBot[0][1]);
|
|
1626
|
+
for (let k = 1; k < segBot.length; k++) u.ctx.lineTo(segBot[k][0], segBot[k][1]);
|
|
1627
|
+
for (let k = segBot.length - 1; k >= 0; k--) u.ctx.lineTo(segBot[k][0], segBot[k][2]);
|
|
1628
|
+
u.ctx.closePath();
|
|
1629
|
+
u.ctx.fill();
|
|
1630
|
+
}
|
|
1631
|
+
u.ctx.strokeStyle = 'rgba(38,166,154,0.55)';
|
|
1632
|
+
u.ctx.lineWidth = 1;
|
|
1633
|
+
u.ctx.beginPath();
|
|
1634
|
+
segTop.forEach((p, k) => { if (k === 0) u.ctx.moveTo(p[0], p[1]); else u.ctx.lineTo(p[0], p[1]); });
|
|
1635
|
+
u.ctx.stroke();
|
|
1636
|
+
u.ctx.strokeStyle = 'rgba(239,83,80,0.55)';
|
|
1637
|
+
u.ctx.beginPath();
|
|
1638
|
+
segBot.forEach((p, k) => { if (k === 0) u.ctx.moveTo(p[0], p[1]); else u.ctx.lineTo(p[0], p[1]); });
|
|
1639
|
+
u.ctx.stroke();
|
|
1640
|
+
segTop = [];
|
|
1641
|
+
segBot = [];
|
|
1642
|
+
};
|
|
1643
|
+
for (let i = 0; i < xs.length; i++) {
|
|
1644
|
+
const a = currentAma[i];
|
|
1645
|
+
const up = currentRangeUpper[i];
|
|
1646
|
+
const lo = currentRangeLower[i];
|
|
1647
|
+
if (!Number.isFinite(a) || a <= 0 || !Number.isFinite(up) || up <= a
|
|
1648
|
+
|| !Number.isFinite(lo) || lo <= 0 || lo >= a) {
|
|
1649
|
+
flush();
|
|
1650
|
+
continue;
|
|
1651
|
+
}
|
|
1652
|
+
const x = u.valToPos(xs[i], 'x', true);
|
|
1653
|
+
const yA = u.valToPos(a, 'y', true);
|
|
1654
|
+
const yU = u.valToPos(up, 'y', true);
|
|
1655
|
+
const yL = u.valToPos(lo, 'y', true);
|
|
1656
|
+
if (!Number.isFinite(x) || !Number.isFinite(yA) || !Number.isFinite(yU) || !Number.isFinite(yL)) {
|
|
1657
|
+
flush();
|
|
1658
|
+
continue;
|
|
1659
|
+
}
|
|
1660
|
+
segTop.push([x, yU, yA]);
|
|
1661
|
+
segBot.push([x, yL, yA]);
|
|
1662
|
+
}
|
|
1663
|
+
flush();
|
|
1664
|
+
u.ctx.restore();
|
|
1665
|
+
}
|
|
1666
|
+
return { hooks: { draw: [drawRangeBand] } };
|
|
1667
|
+
}
|
|
1365
1668
|
function buildData() {
|
|
1366
1669
|
const tf = timeframeMap.get(currentTimeframe) || timeframeMap.get(defaultTimeframe.label) || timeframes[0];
|
|
1367
1670
|
currentSeriesState = getSeriesState(currentPairMode);
|
|
@@ -1396,6 +1699,10 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1396
1699
|
currentSma = new Array(currentCandles.length).fill(null);
|
|
1397
1700
|
}
|
|
1398
1701
|
currentAma = currentAmaEnabled ? sampleSeriesByIndex(baseAma, aggregated.idxs) : new Array(currentCandles.length).fill(null);
|
|
1702
|
+
const baseRange = computeRangeBand(baseAma);
|
|
1703
|
+
currentRangeUpper = sampleSeriesByIndex(baseRange.upper, aggregated.idxs);
|
|
1704
|
+
currentRangeLower = sampleSeriesByIndex(baseRange.lower, aggregated.idxs);
|
|
1705
|
+
currentRangeTrend = sampleTrendByIndex(baseRange.trend, aggregated.idxs);
|
|
1399
1706
|
currentAmaOff = currentAmaEnabled && currentAmaInitOffset !== 0 ? sampleSeriesByIndex(baseAmaOff, aggregated.idxs) : new Array(currentCandles.length).fill(null);
|
|
1400
1707
|
currentVwap = currentVwapEnabled ? sampleSeriesByIndex(baseVwap, aggregated.idxs) : new Array(currentCandles.length).fill(null);
|
|
1401
1708
|
const amaCfg = currentAmaConfig();
|
|
@@ -1500,7 +1807,7 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1500
1807
|
|
|
1501
1808
|
const plugin = candlePlugin();
|
|
1502
1809
|
plugin.opts(null, priceOpts);
|
|
1503
|
-
priceOpts.plugins = [plugin];
|
|
1810
|
+
priceOpts.plugins = [rangeBandPlugin(), plugin];
|
|
1504
1811
|
if (!priceChart) priceChart = new uPlot(priceOpts, data.priceData, priceEl);
|
|
1505
1812
|
else priceChart.setData(data.priceData, false);
|
|
1506
1813
|
|
|
@@ -1657,12 +1964,11 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1657
1964
|
priceBounds = null;
|
|
1658
1965
|
return;
|
|
1659
1966
|
}
|
|
1660
|
-
const range = max - min;
|
|
1661
1967
|
// Uloszoomauksen rajat: alin naytettava hinta voi painua viimeistaan
|
|
1662
|
-
// ~35 % datan minimin alapuolelle, ylin ~1.6x datan maksimin
|
|
1663
|
-
// kokonaisvali saa olla enintaan ~1.6x datan hintahaarukasta —
|
|
1968
|
+
// ~35 % datan minimin alapuolelle, ylin ~1.6x datan maksimin —
|
|
1664
1969
|
// ei siis paase "lipsahtamaan" candleita nakyvasta kadottaen.
|
|
1665
|
-
|
|
1970
|
+
// Kokonaisvalille ei ole ylarajaa: pystyakselia saa zoomata ulos vapaasti.
|
|
1971
|
+
priceBounds = { min: min, max: max, floor: min * 0.35, ceil: max * 1.6 };
|
|
1666
1972
|
}
|
|
1667
1973
|
function applyYRange(chart, min, max) {
|
|
1668
1974
|
if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) return false;
|
|
@@ -1675,13 +1981,6 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1675
1981
|
if (priceBounds) {
|
|
1676
1982
|
let nMin = Math.max(min, priceBounds.floor);
|
|
1677
1983
|
let nMax = Math.min(max, priceBounds.ceil);
|
|
1678
|
-
if (nMax - nMin > priceBounds.maxSpan) {
|
|
1679
|
-
const c = (nMin + nMax) / 2;
|
|
1680
|
-
nMin = c - priceBounds.maxSpan / 2;
|
|
1681
|
-
nMax = c + priceBounds.maxSpan / 2;
|
|
1682
|
-
nMin = Math.max(nMin, priceBounds.floor);
|
|
1683
|
-
nMax = Math.min(nMax, priceBounds.ceil);
|
|
1684
|
-
}
|
|
1685
1984
|
if (nMax <= nMin) return false;
|
|
1686
1985
|
min = nMin;
|
|
1687
1986
|
max = nMax;
|
|
@@ -1765,6 +2064,11 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1765
2064
|
document.getElementById('ama-slow').value = currentAmaSlowPeriod.toFixed(1);
|
|
1766
2065
|
document.getElementById('vwap-toggle').checked = currentVwapEnabled;
|
|
1767
2066
|
document.getElementById('vwap-bars').value = String(currentVwapBars);
|
|
2067
|
+
document.getElementById('range-toggle').checked = currentRangeEnabled;
|
|
2068
|
+
document.getElementById('range-scale-toggle').checked = currentRangeScaleEnabled;
|
|
2069
|
+
document.getElementById('range-span').value = Number(currentRangeSpan).toFixed(2);
|
|
2070
|
+
document.getElementById('range-span-val').textContent = Number(currentRangeSpan).toFixed(2) + 'x';
|
|
2071
|
+
document.getElementById('range-grid-wrap').style.display = payload.grid ? 'inline' : 'none';
|
|
1768
2072
|
document.getElementById('ama-init-offset-toggle').checked = currentAmaInitOffsetEnabled;
|
|
1769
2073
|
document.getElementById('ama-init-offset').value = String(currentAmaInitOffset);
|
|
1770
2074
|
document.getElementById('ama-init-offset').disabled = !currentAmaInitOffsetEnabled;
|
|
@@ -1835,7 +2139,6 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1835
2139
|
|
|
1836
2140
|
setControls();
|
|
1837
2141
|
rerender(false);
|
|
1838
|
-
|
|
1839
2142
|
document.querySelectorAll('.time-btn').forEach((btn) => {
|
|
1840
2143
|
btn.addEventListener('click', () => {
|
|
1841
2144
|
if (btn.disabled) return;
|
|
@@ -1846,7 +2149,6 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1846
2149
|
rerender(true);
|
|
1847
2150
|
});
|
|
1848
2151
|
});
|
|
1849
|
-
|
|
1850
2152
|
const scaleToggle = document.getElementById('scale-toggle');
|
|
1851
2153
|
if (scaleToggle) {
|
|
1852
2154
|
scaleToggle.addEventListener('click', () => {
|
|
@@ -1879,6 +2181,9 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1879
2181
|
document.getElementById('ama-init-offset-val').textContent = currentAmaInitOffset + '%';
|
|
1880
2182
|
currentVwapEnabled = document.getElementById('vwap-toggle').checked;
|
|
1881
2183
|
currentVwapBars = clamp(Math.round(Number(document.getElementById('vwap-bars').value) || 500), 24, 2000);
|
|
2184
|
+
currentRangeEnabled = document.getElementById('range-toggle').checked;
|
|
2185
|
+
currentRangeScaleEnabled = document.getElementById('range-scale-toggle').checked;
|
|
2186
|
+
currentRangeSpan = Math.min(2, Math.max(1.25, Math.round((Number(document.getElementById('range-span').value) || 1.55) * 20) / 20));
|
|
1882
2187
|
setControls();
|
|
1883
2188
|
rerender(false);
|
|
1884
2189
|
};
|
|
@@ -1917,6 +2222,25 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1917
2222
|
rerender(false);
|
|
1918
2223
|
});
|
|
1919
2224
|
document.getElementById('ama-init-offset-toggle').addEventListener('change', syncInputs);
|
|
2225
|
+
document.getElementById('range-toggle').addEventListener('change', () => {
|
|
2226
|
+
const nextEnabled = document.getElementById('range-toggle').checked;
|
|
2227
|
+
if (currentRangeEnabled === nextEnabled) return;
|
|
2228
|
+
currentRangeEnabled = nextEnabled;
|
|
2229
|
+
setControls();
|
|
2230
|
+
if (!currentRangeEnabled) {
|
|
2231
|
+
hideRangeBandImmediate();
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
rerender(false);
|
|
2235
|
+
});
|
|
2236
|
+
document.getElementById('range-scale-toggle').addEventListener('change', () => {
|
|
2237
|
+
const nextEnabled = document.getElementById('range-scale-toggle').checked;
|
|
2238
|
+
if (currentRangeScaleEnabled === nextEnabled) return;
|
|
2239
|
+
currentRangeScaleEnabled = nextEnabled;
|
|
2240
|
+
manualYRange = null;
|
|
2241
|
+
setControls();
|
|
2242
|
+
rerender(false);
|
|
2243
|
+
});
|
|
1920
2244
|
['sma-period', 'ama-er', 'ama-fast', 'ama-slow', 'vwap-bars'].forEach((id) => {
|
|
1921
2245
|
document.getElementById(id).addEventListener('change', syncInputs);
|
|
1922
2246
|
document.getElementById(id).addEventListener('blur', syncInputs);
|
|
@@ -1968,6 +2292,11 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
|
|
|
1968
2292
|
document.getElementById('ama-init-offset-val').textContent = snapped + '%';
|
|
1969
2293
|
syncInputs();
|
|
1970
2294
|
});
|
|
2295
|
+
document.getElementById('range-span').addEventListener('input', () => {
|
|
2296
|
+
const input = document.getElementById('range-span');
|
|
2297
|
+
document.getElementById('range-span-val').textContent = Number(input.value).toFixed(2) + 'x';
|
|
2298
|
+
syncInputs();
|
|
2299
|
+
});
|
|
1971
2300
|
|
|
1972
2301
|
window.addEventListener('resize', () => {
|
|
1973
2302
|
if (!charts.length) return;
|
package/claw/package.json
CHANGED
|
@@ -30,7 +30,7 @@ async function testParserAcceptsJsonlAcrossChunksAndSingleBuffer() {
|
|
|
30
30
|
capabilities: {},
|
|
31
31
|
clientInfo: {
|
|
32
32
|
name: 'claw-mcp-transport-test',
|
|
33
|
-
version: '1.5.
|
|
33
|
+
version: '1.5.3'
|
|
34
34
|
},
|
|
35
35
|
protocolVersion: '2024-11-05'
|
|
36
36
|
}
|
|
@@ -187,7 +187,7 @@ async function testMainEntrypointHandlesRealProcessInitialize() {
|
|
|
187
187
|
capabilities: {},
|
|
188
188
|
clientInfo: {
|
|
189
189
|
name: 'claw-mcp-transport-test',
|
|
190
|
-
version: '1.5.
|
|
190
|
+
version: '1.5.3'
|
|
191
191
|
},
|
|
192
192
|
protocolVersion: '2024-11-05'
|
|
193
193
|
}
|