mdas-jsview-sdk 1.0.10-uat.0 → 1.0.12-uat.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/dist/mdas-sdk.esm.js +328 -82
- package/dist/mdas-sdk.esm.js.map +1 -1
- package/dist/mdas-sdk.js +328 -82
- package/dist/mdas-sdk.js.map +1 -1
- package/dist/mdas-sdk.min.js +9 -9
- package/dist/mdas-sdk.min.js.map +1 -1
- package/package.json +1 -1
package/dist/mdas-sdk.js
CHANGED
|
@@ -1205,13 +1205,12 @@
|
|
|
1205
1205
|
<div class="company-market-info">
|
|
1206
1206
|
<span class="night-company-name"></span>
|
|
1207
1207
|
<span class="market-name"></span>
|
|
1208
|
-
<span class="market-mic"></span>
|
|
1209
1208
|
</div>
|
|
1210
1209
|
<h1 class="symbol editable-symbol"
|
|
1211
1210
|
title="Double-click to edit symbol"
|
|
1212
1211
|
data-original-symbol="">
|
|
1213
1212
|
</h1>
|
|
1214
|
-
|
|
1213
|
+
|
|
1215
1214
|
</div>
|
|
1216
1215
|
<div class="price-section">
|
|
1217
1216
|
<div class="current-price"></div>
|
|
@@ -4421,13 +4420,6 @@
|
|
|
4421
4420
|
marketNameElement.textContent = this.exchangeName || data.exchangeName || '';
|
|
4422
4421
|
}
|
|
4423
4422
|
|
|
4424
|
-
// Update MIC (Market Identifier Code)
|
|
4425
|
-
const marketMicElement = this.container.querySelector('.market-mic');
|
|
4426
|
-
if (marketMicElement) {
|
|
4427
|
-
const micValue = this.mic || data.mic || '';
|
|
4428
|
-
marketMicElement.textContent = micValue;
|
|
4429
|
-
}
|
|
4430
|
-
|
|
4431
4423
|
// Update price
|
|
4432
4424
|
const currentPriceElement = this.container.querySelector('.current-price');
|
|
4433
4425
|
if (currentPriceElement) {
|
|
@@ -37506,6 +37498,10 @@ ${SharedStyles}
|
|
|
37506
37498
|
|
|
37507
37499
|
// Symbol editor
|
|
37508
37500
|
this.symbolEditor = null;
|
|
37501
|
+
|
|
37502
|
+
// Lazy loading cache for 5D data
|
|
37503
|
+
this.cached5DData = null;
|
|
37504
|
+
this.is5DDataLoading = false;
|
|
37509
37505
|
this.createWidgetStructure();
|
|
37510
37506
|
this.setupSymbolEditor();
|
|
37511
37507
|
this.initialize();
|
|
@@ -37607,6 +37603,10 @@ ${SharedStyles}
|
|
|
37607
37603
|
this.chartData = null;
|
|
37608
37604
|
this.livePrice = null;
|
|
37609
37605
|
|
|
37606
|
+
// Clear cached 5D data for old symbol
|
|
37607
|
+
this.cached5DData = null;
|
|
37608
|
+
this.is5DDataLoading = false;
|
|
37609
|
+
|
|
37610
37610
|
// Update internal symbol
|
|
37611
37611
|
this.symbol = upperSymbol;
|
|
37612
37612
|
|
|
@@ -37757,7 +37757,13 @@ ${SharedStyles}
|
|
|
37757
37757
|
throw new Error('API service not available');
|
|
37758
37758
|
}
|
|
37759
37759
|
|
|
37760
|
-
//
|
|
37760
|
+
// For 5D chart: Load data from multiple days (rangeback 0-5)
|
|
37761
|
+
if (this.rangeBack === 5) {
|
|
37762
|
+
await this.load5DayChartData(apiService);
|
|
37763
|
+
return;
|
|
37764
|
+
}
|
|
37765
|
+
|
|
37766
|
+
// FALLBACK LOGIC: Try to find most recent available data (for 1D)
|
|
37761
37767
|
const MAX_LOOKBACK_DAYS = 7;
|
|
37762
37768
|
let attemptRangeBack = this.rangeBack;
|
|
37763
37769
|
let dataFound = false;
|
|
@@ -37846,6 +37852,9 @@ ${SharedStyles}
|
|
|
37846
37852
|
await this.startAutoRefresh();
|
|
37847
37853
|
// Subscribe to live price updates
|
|
37848
37854
|
this.subscribeToLivePrice();
|
|
37855
|
+
|
|
37856
|
+
// LAZY LOADING: Preload 5D data in the background after 1D loads
|
|
37857
|
+
this.preload5DDataInBackground();
|
|
37849
37858
|
} else {
|
|
37850
37859
|
// Historical data or fallback - don't refresh
|
|
37851
37860
|
this.stopAutoRefresh();
|
|
@@ -37873,6 +37882,159 @@ ${SharedStyles}
|
|
|
37873
37882
|
this.showError(errorMessage);
|
|
37874
37883
|
}
|
|
37875
37884
|
}
|
|
37885
|
+
async load5DayChartData(apiService) {
|
|
37886
|
+
try {
|
|
37887
|
+
// Check if we have cached 5D data
|
|
37888
|
+
if (this.cached5DData) {
|
|
37889
|
+
if (this.debug) {
|
|
37890
|
+
console.log('[IntradayChartWidget] Using cached 5D data');
|
|
37891
|
+
}
|
|
37892
|
+
|
|
37893
|
+
// Use cached data
|
|
37894
|
+
this.chartData = this.cached5DData;
|
|
37895
|
+
this.renderChart();
|
|
37896
|
+
this.updateStats();
|
|
37897
|
+
this.hideLoading();
|
|
37898
|
+
|
|
37899
|
+
// Don't auto-refresh for 5D chart
|
|
37900
|
+
this.stopAutoRefresh();
|
|
37901
|
+
return;
|
|
37902
|
+
}
|
|
37903
|
+
|
|
37904
|
+
// No cached data, load it now
|
|
37905
|
+
const combinedData = await this.fetch5DayData(apiService);
|
|
37906
|
+
|
|
37907
|
+
// Process the combined data
|
|
37908
|
+
this.chartData = new IntradayChartModel(combinedData);
|
|
37909
|
+
if (this.chartData.dataPoints.length === 0) {
|
|
37910
|
+
throw new Error(`No valid data points for ${this.symbol}`);
|
|
37911
|
+
}
|
|
37912
|
+
|
|
37913
|
+
// Cache the processed data
|
|
37914
|
+
this.cached5DData = this.chartData;
|
|
37915
|
+
this.renderChart();
|
|
37916
|
+
this.updateStats();
|
|
37917
|
+
this.hideLoading();
|
|
37918
|
+
if (this.debug) {
|
|
37919
|
+
console.log(`[IntradayChartWidget] 5D chart loaded with ${this.chartData.dataPoints.length} data points`);
|
|
37920
|
+
}
|
|
37921
|
+
|
|
37922
|
+
// Don't auto-refresh for 5D chart
|
|
37923
|
+
this.stopAutoRefresh();
|
|
37924
|
+
} catch (error) {
|
|
37925
|
+
console.error('[IntradayChartWidget] Error loading 5D chart data:', error);
|
|
37926
|
+
throw error; // Re-throw to be caught by parent loadChartData
|
|
37927
|
+
}
|
|
37928
|
+
}
|
|
37929
|
+
async fetch5DayData(apiService) {
|
|
37930
|
+
const DAYS_TO_COLLECT = 5;
|
|
37931
|
+
const MAX_RANGEBACK_ATTEMPTS = 7; // API limit: maximum 7 rangeback
|
|
37932
|
+
|
|
37933
|
+
if (this.debug) {
|
|
37934
|
+
console.log('[IntradayChartWidget] Fetching 5D chart data with parallel requests (API limit: rangeback 0-6)');
|
|
37935
|
+
}
|
|
37936
|
+
|
|
37937
|
+
// OPTIMIZATION: Query all rangeback values in parallel (0-6)
|
|
37938
|
+
const rangebackPromises = [];
|
|
37939
|
+
for (let rb = 0; rb < MAX_RANGEBACK_ATTEMPTS; rb++) {
|
|
37940
|
+
rangebackPromises.push(apiService.getIntradayChart(this.source, this.symbol, rb).then(response => {
|
|
37941
|
+
// Extract data array from response
|
|
37942
|
+
let dataArray = response;
|
|
37943
|
+
if (response && response.data && Array.isArray(response.data)) {
|
|
37944
|
+
dataArray = response.data;
|
|
37945
|
+
}
|
|
37946
|
+
return {
|
|
37947
|
+
rangeBack: rb,
|
|
37948
|
+
data: dataArray
|
|
37949
|
+
};
|
|
37950
|
+
}).catch(error => {
|
|
37951
|
+
// If a request fails, return null so we can filter it out
|
|
37952
|
+
if (this.debug) {
|
|
37953
|
+
console.warn(`[IntradayChartWidget] Request failed for rangeback ${rb}:`, error);
|
|
37954
|
+
}
|
|
37955
|
+
return {
|
|
37956
|
+
rangeBack: rb,
|
|
37957
|
+
data: null
|
|
37958
|
+
};
|
|
37959
|
+
}));
|
|
37960
|
+
}
|
|
37961
|
+
|
|
37962
|
+
// Wait for all parallel requests to complete
|
|
37963
|
+
const results = await Promise.all(rangebackPromises);
|
|
37964
|
+
if (this.debug) {
|
|
37965
|
+
console.log('[IntradayChartWidget] All parallel requests completed');
|
|
37966
|
+
}
|
|
37967
|
+
|
|
37968
|
+
// Filter out empty/null responses and collect valid days
|
|
37969
|
+
const collectedDays = results.filter(result => {
|
|
37970
|
+
const isValid = result.data && Array.isArray(result.data) && result.data.length > 0;
|
|
37971
|
+
if (this.debug) {
|
|
37972
|
+
if (isValid) {
|
|
37973
|
+
console.log(`[IntradayChartWidget] Rangeback ${result.rangeBack}: ${result.data.length} data points`);
|
|
37974
|
+
} else {
|
|
37975
|
+
console.log(`[IntradayChartWidget] Rangeback ${result.rangeBack}: No data (skipped)`);
|
|
37976
|
+
}
|
|
37977
|
+
}
|
|
37978
|
+
return isValid;
|
|
37979
|
+
}).slice(0, DAYS_TO_COLLECT); // Take only the first 5 days with data
|
|
37980
|
+
|
|
37981
|
+
if (collectedDays.length === 0) {
|
|
37982
|
+
throw new Error(`No intraday data available for ${this.symbol} in the past ${MAX_RANGEBACK_ATTEMPTS} days`);
|
|
37983
|
+
}
|
|
37984
|
+
if (this.debug) {
|
|
37985
|
+
console.log(`[IntradayChartWidget] Collected ${collectedDays.length}/${DAYS_TO_COLLECT} days of data`);
|
|
37986
|
+
if (collectedDays.length < DAYS_TO_COLLECT) {
|
|
37987
|
+
console.log(`[IntradayChartWidget] Note: Only ${collectedDays.length} days available within API limit (rangeback 0-6)`);
|
|
37988
|
+
}
|
|
37989
|
+
}
|
|
37990
|
+
|
|
37991
|
+
// Combine all collected days into a single array (oldest to newest)
|
|
37992
|
+
const combinedData = [];
|
|
37993
|
+
for (let i = collectedDays.length - 1; i >= 0; i--) {
|
|
37994
|
+
combinedData.push(...collectedDays[i].data);
|
|
37995
|
+
}
|
|
37996
|
+
if (this.debug) {
|
|
37997
|
+
console.log(`[IntradayChartWidget] Combined ${combinedData.length} total data points from ${collectedDays.length} days`);
|
|
37998
|
+
}
|
|
37999
|
+
return combinedData;
|
|
38000
|
+
}
|
|
38001
|
+
preload5DDataInBackground() {
|
|
38002
|
+
// Prevent multiple simultaneous preloads
|
|
38003
|
+
if (this.is5DDataLoading || this.cached5DData) {
|
|
38004
|
+
return;
|
|
38005
|
+
}
|
|
38006
|
+
this.is5DDataLoading = true;
|
|
38007
|
+
if (this.debug) {
|
|
38008
|
+
console.log('[IntradayChartWidget] Starting background preload of 5D data...');
|
|
38009
|
+
}
|
|
38010
|
+
|
|
38011
|
+
// Run in background without blocking
|
|
38012
|
+
setTimeout(async () => {
|
|
38013
|
+
try {
|
|
38014
|
+
const apiService = this.wsManager.getApiService();
|
|
38015
|
+
if (!apiService) {
|
|
38016
|
+
if (this.debug) {
|
|
38017
|
+
console.log('[IntradayChartWidget] API service not available for preload');
|
|
38018
|
+
}
|
|
38019
|
+
return;
|
|
38020
|
+
}
|
|
38021
|
+
const combinedData = await this.fetch5DayData(apiService);
|
|
38022
|
+
|
|
38023
|
+
// Create and cache the model
|
|
38024
|
+
const chartData = new IntradayChartModel(combinedData);
|
|
38025
|
+
this.cached5DData = chartData;
|
|
38026
|
+
if (this.debug) {
|
|
38027
|
+
console.log(`[IntradayChartWidget] ✓ Background preload complete: ${chartData.dataPoints.length} data points cached`);
|
|
38028
|
+
}
|
|
38029
|
+
} catch (error) {
|
|
38030
|
+
if (this.debug) {
|
|
38031
|
+
console.warn('[IntradayChartWidget] Background preload failed:', error);
|
|
38032
|
+
}
|
|
38033
|
+
} finally {
|
|
38034
|
+
this.is5DDataLoading = false;
|
|
38035
|
+
}
|
|
38036
|
+
}, 1000); // Wait 1 second after 1D loads before starting preload
|
|
38037
|
+
}
|
|
37876
38038
|
subscribeToLivePrice() {
|
|
37877
38039
|
// Unsubscribe from previous subscription if any
|
|
37878
38040
|
if (this.unsubscribe) {
|
|
@@ -38195,16 +38357,28 @@ ${SharedStyles}
|
|
|
38195
38357
|
|
|
38196
38358
|
if (this.chartType === 'candlestick') {
|
|
38197
38359
|
// Prepare OHLC data for candlestick chart
|
|
38198
|
-
|
|
38199
|
-
x
|
|
38200
|
-
|
|
38201
|
-
|
|
38202
|
-
|
|
38203
|
-
|
|
38204
|
-
|
|
38205
|
-
|
|
38206
|
-
|
|
38207
|
-
}
|
|
38360
|
+
if (this.rangeBack > 0) {
|
|
38361
|
+
// For multi-day charts, use index as x-value (linear scale)
|
|
38362
|
+
chartDataPoints = validDataPoints.map((point, index) => ({
|
|
38363
|
+
x: index,
|
|
38364
|
+
o: point.open,
|
|
38365
|
+
h: point.high,
|
|
38366
|
+
l: point.low,
|
|
38367
|
+
c: point.close
|
|
38368
|
+
}));
|
|
38369
|
+
} else {
|
|
38370
|
+
// For single-day charts, use timestamps (time scale)
|
|
38371
|
+
chartDataPoints = validDataPoints.map(point => ({
|
|
38372
|
+
x: this.parseTimestamp(point.time).getTime(),
|
|
38373
|
+
o: point.open,
|
|
38374
|
+
h: point.high,
|
|
38375
|
+
l: point.low,
|
|
38376
|
+
c: point.close
|
|
38377
|
+
})).filter(point => {
|
|
38378
|
+
// Filter out invalid timestamps
|
|
38379
|
+
return !isNaN(point.x) && point.x > 0;
|
|
38380
|
+
});
|
|
38381
|
+
}
|
|
38208
38382
|
if (chartDataPoints.length === 0) {
|
|
38209
38383
|
console.error('[IntradayChartWidget] No valid candlestick data points after date parsing');
|
|
38210
38384
|
this.showError('Unable to parse chart data timestamps');
|
|
@@ -38226,14 +38400,23 @@ ${SharedStyles}
|
|
|
38226
38400
|
};
|
|
38227
38401
|
} else {
|
|
38228
38402
|
// Prepare data with timestamps for line/area chart
|
|
38229
|
-
|
|
38230
|
-
x
|
|
38231
|
-
|
|
38232
|
-
|
|
38233
|
-
|
|
38234
|
-
|
|
38235
|
-
|
|
38236
|
-
|
|
38403
|
+
if (this.rangeBack > 0) {
|
|
38404
|
+
// For multi-day charts, use index as x-value (linear scale)
|
|
38405
|
+
chartDataPoints = validDataPoints.map((point, index) => ({
|
|
38406
|
+
x: index,
|
|
38407
|
+
y: point.close
|
|
38408
|
+
}));
|
|
38409
|
+
} else {
|
|
38410
|
+
// For single-day charts, use timestamps (time scale)
|
|
38411
|
+
chartDataPoints = validDataPoints.map(point => ({
|
|
38412
|
+
x: this.parseTimestamp(point.time),
|
|
38413
|
+
y: point.close
|
|
38414
|
+
})).filter(point => {
|
|
38415
|
+
// Filter out invalid dates
|
|
38416
|
+
const timestamp = point.x.getTime();
|
|
38417
|
+
return !isNaN(timestamp) && timestamp > 0;
|
|
38418
|
+
});
|
|
38419
|
+
}
|
|
38237
38420
|
if (chartDataPoints.length === 0) {
|
|
38238
38421
|
console.error('[IntradayChartWidget] No valid chart data points after date parsing');
|
|
38239
38422
|
this.showError('Unable to parse chart data timestamps');
|
|
@@ -38268,16 +38451,23 @@ ${SharedStyles}
|
|
|
38268
38451
|
console.log('[IntradayChartWidget] Last data point - Source time:', validDataPoints[validDataPoints.length - 1].time);
|
|
38269
38452
|
console.log('[IntradayChartWidget] Last chart point:', chartDataPoints[chartDataPoints.length - 1]);
|
|
38270
38453
|
|
|
38271
|
-
// Calculate explicit min/max for x-axis
|
|
38272
|
-
|
|
38273
|
-
|
|
38274
|
-
|
|
38275
|
-
|
|
38276
|
-
min
|
|
38277
|
-
max
|
|
38278
|
-
|
|
38279
|
-
|
|
38280
|
-
|
|
38454
|
+
// Calculate explicit min/max for x-axis (only for time scale, not linear)
|
|
38455
|
+
let minTimestamp, maxTimestamp;
|
|
38456
|
+
if (this.rangeBack === 0) {
|
|
38457
|
+
// For time scale, calculate timestamp bounds
|
|
38458
|
+
const timestamps = chartDataPoints.map(p => this.chartType === 'candlestick' ? p.x : p.x.getTime());
|
|
38459
|
+
minTimestamp = Math.min(...timestamps);
|
|
38460
|
+
maxTimestamp = Math.max(...timestamps);
|
|
38461
|
+
console.log('[IntradayChartWidget] X-axis bounds:', {
|
|
38462
|
+
min: minTimestamp,
|
|
38463
|
+
max: maxTimestamp,
|
|
38464
|
+
minDate: new Date(minTimestamp),
|
|
38465
|
+
maxDate: new Date(maxTimestamp)
|
|
38466
|
+
});
|
|
38467
|
+
} else {
|
|
38468
|
+
// For linear scale, bounds are just indices
|
|
38469
|
+
console.log('[IntradayChartWidget] Using linear scale with', chartDataPoints.length, 'data points');
|
|
38470
|
+
}
|
|
38281
38471
|
const config = {
|
|
38282
38472
|
type: this.chartType === 'candlestick' ? 'candlestick' : 'line',
|
|
38283
38473
|
data: {
|
|
@@ -38315,10 +38505,9 @@ ${SharedStyles}
|
|
|
38315
38505
|
const index = context[0].dataIndex;
|
|
38316
38506
|
const point = validDataPoints[index];
|
|
38317
38507
|
if (point) {
|
|
38318
|
-
//
|
|
38508
|
+
// Display timestamp as-is from the data, without timezone conversion
|
|
38319
38509
|
const date = new Date(point.time);
|
|
38320
38510
|
return date.toLocaleString('en-US', {
|
|
38321
|
-
timeZone: 'America/New_York',
|
|
38322
38511
|
month: '2-digit',
|
|
38323
38512
|
day: '2-digit',
|
|
38324
38513
|
year: 'numeric',
|
|
@@ -38369,62 +38558,62 @@ ${SharedStyles}
|
|
|
38369
38558
|
}
|
|
38370
38559
|
},
|
|
38371
38560
|
annotation: {
|
|
38372
|
-
annotations:
|
|
38373
|
-
livePriceLine: {
|
|
38374
|
-
type: 'line',
|
|
38375
|
-
scaleID: 'y',
|
|
38376
|
-
value: this.livePrice || stats.close,
|
|
38377
|
-
borderColor: '#667eea',
|
|
38378
|
-
borderWidth: 2,
|
|
38379
|
-
borderDash: [5, 5],
|
|
38380
|
-
label: {
|
|
38381
|
-
display: true,
|
|
38382
|
-
content: this.livePrice ? `$${this.livePrice.toFixed(2)}` : `$${stats.close.toFixed(2)}`,
|
|
38383
|
-
enabled: true,
|
|
38384
|
-
position: 'end',
|
|
38385
|
-
backgroundColor: 'rgb(102, 126, 234)',
|
|
38386
|
-
color: '#ffffff',
|
|
38387
|
-
font: {
|
|
38388
|
-
size: 12,
|
|
38389
|
-
weight: 'bold',
|
|
38390
|
-
family: 'system-ui, -apple-system, sans-serif'
|
|
38391
|
-
},
|
|
38392
|
-
padding: {
|
|
38393
|
-
top: 4,
|
|
38394
|
-
bottom: 4,
|
|
38395
|
-
left: 8,
|
|
38396
|
-
right: 8
|
|
38397
|
-
},
|
|
38398
|
-
borderRadius: 4,
|
|
38399
|
-
xAdjust: -10,
|
|
38400
|
-
yAdjust: 0
|
|
38401
|
-
}
|
|
38402
|
-
}
|
|
38403
|
-
}
|
|
38561
|
+
annotations: this.createAnnotations(validDataPoints, stats)
|
|
38404
38562
|
}
|
|
38405
38563
|
},
|
|
38406
38564
|
scales: {
|
|
38407
38565
|
x: {
|
|
38408
|
-
type: 'time',
|
|
38409
|
-
min: minTimestamp,
|
|
38410
|
-
max: maxTimestamp,
|
|
38411
|
-
time: {
|
|
38412
|
-
unit:
|
|
38566
|
+
type: this.rangeBack === 0 ? 'time' : 'linear',
|
|
38567
|
+
min: this.rangeBack === 0 ? minTimestamp : undefined,
|
|
38568
|
+
max: this.rangeBack === 0 ? maxTimestamp : undefined,
|
|
38569
|
+
time: this.rangeBack === 0 ? {
|
|
38570
|
+
unit: 'hour',
|
|
38571
|
+
stepSize: 1,
|
|
38413
38572
|
displayFormats: {
|
|
38414
|
-
|
|
38415
|
-
hour: 'MMM d, ha'
|
|
38573
|
+
hour: 'h:mm a'
|
|
38416
38574
|
},
|
|
38417
38575
|
tooltipFormat: 'MMM d, h:mm a'
|
|
38418
|
-
},
|
|
38576
|
+
} : undefined,
|
|
38419
38577
|
grid: {
|
|
38420
38578
|
display: false
|
|
38421
38579
|
},
|
|
38422
38580
|
ticks: {
|
|
38423
|
-
maxTicksLimit: 10,
|
|
38581
|
+
maxTicksLimit: this.rangeBack === 0 ? 10 : 8,
|
|
38424
38582
|
color: '#6b7280',
|
|
38425
38583
|
autoSkip: true,
|
|
38426
38584
|
maxRotation: 0,
|
|
38427
|
-
minRotation: 0
|
|
38585
|
+
minRotation: 0,
|
|
38586
|
+
callback: (value, index, ticks) => {
|
|
38587
|
+
if (this.rangeBack > 0) {
|
|
38588
|
+
// For multi-day charts with linear scale
|
|
38589
|
+
// value is the x-value (index in data array), not the tick index
|
|
38590
|
+
const dataIndex = Math.round(value);
|
|
38591
|
+
const point = validDataPoints[dataIndex];
|
|
38592
|
+
if (point) {
|
|
38593
|
+
const date = new Date(point.time);
|
|
38594
|
+
return date.toLocaleString('en-US', {
|
|
38595
|
+
month: 'short',
|
|
38596
|
+
day: 'numeric',
|
|
38597
|
+
hour: 'numeric',
|
|
38598
|
+
minute: '2-digit',
|
|
38599
|
+
hour12: true
|
|
38600
|
+
});
|
|
38601
|
+
}
|
|
38602
|
+
return '';
|
|
38603
|
+
}
|
|
38604
|
+
// For single-day time scale, provide fallback formatting
|
|
38605
|
+
// If value is a number (timestamp), format it
|
|
38606
|
+
if (typeof value === 'number') {
|
|
38607
|
+
const date = new Date(value);
|
|
38608
|
+
return date.toLocaleString('en-US', {
|
|
38609
|
+
hour: 'numeric',
|
|
38610
|
+
minute: '2-digit',
|
|
38611
|
+
hour12: true
|
|
38612
|
+
});
|
|
38613
|
+
}
|
|
38614
|
+
// Otherwise let Chart.js handle it (if date adapter is working)
|
|
38615
|
+
return value;
|
|
38616
|
+
}
|
|
38428
38617
|
}
|
|
38429
38618
|
},
|
|
38430
38619
|
y: {
|
|
@@ -38470,6 +38659,63 @@ ${SharedStyles}
|
|
|
38470
38659
|
// Add reset zoom button listener
|
|
38471
38660
|
this.setupZoomReset();
|
|
38472
38661
|
}
|
|
38662
|
+
createAnnotations(dataPoints, stats) {
|
|
38663
|
+
const annotations = {
|
|
38664
|
+
livePriceLine: {
|
|
38665
|
+
type: 'line',
|
|
38666
|
+
scaleID: 'y',
|
|
38667
|
+
value: this.livePrice || stats.close,
|
|
38668
|
+
borderColor: '#667eea',
|
|
38669
|
+
borderWidth: 2,
|
|
38670
|
+
borderDash: [5, 5],
|
|
38671
|
+
label: {
|
|
38672
|
+
display: true,
|
|
38673
|
+
content: this.livePrice ? `$${this.livePrice.toFixed(2)}` : `$${stats.close.toFixed(2)}`,
|
|
38674
|
+
enabled: true,
|
|
38675
|
+
position: 'end',
|
|
38676
|
+
backgroundColor: 'rgb(102, 126, 234)',
|
|
38677
|
+
color: '#ffffff',
|
|
38678
|
+
font: {
|
|
38679
|
+
size: 12,
|
|
38680
|
+
weight: 'bold',
|
|
38681
|
+
family: 'system-ui, -apple-system, sans-serif'
|
|
38682
|
+
},
|
|
38683
|
+
padding: {
|
|
38684
|
+
top: 4,
|
|
38685
|
+
bottom: 4,
|
|
38686
|
+
left: 8,
|
|
38687
|
+
right: 8
|
|
38688
|
+
},
|
|
38689
|
+
borderRadius: 4,
|
|
38690
|
+
xAdjust: -10,
|
|
38691
|
+
yAdjust: 0
|
|
38692
|
+
}
|
|
38693
|
+
}
|
|
38694
|
+
};
|
|
38695
|
+
|
|
38696
|
+
// Add day separators for multi-day charts (5D, etc.)
|
|
38697
|
+
if (this.rangeBack > 0 && dataPoints.length > 0) {
|
|
38698
|
+
let currentDay = null;
|
|
38699
|
+
dataPoints.forEach((point, index) => {
|
|
38700
|
+
const pointDate = new Date(point.time);
|
|
38701
|
+
const day = pointDate.toDateString();
|
|
38702
|
+
|
|
38703
|
+
// Add vertical line at the start of each new day
|
|
38704
|
+
if (currentDay !== day && currentDay !== null) {
|
|
38705
|
+
annotations[`daySeparator${index}`] = {
|
|
38706
|
+
type: 'line',
|
|
38707
|
+
scaleID: 'x',
|
|
38708
|
+
value: index,
|
|
38709
|
+
borderColor: 'rgba(0, 0, 0, 0.1)',
|
|
38710
|
+
borderWidth: 1,
|
|
38711
|
+
borderDash: [3, 3]
|
|
38712
|
+
};
|
|
38713
|
+
}
|
|
38714
|
+
currentDay = day;
|
|
38715
|
+
});
|
|
38716
|
+
}
|
|
38717
|
+
return annotations;
|
|
38718
|
+
}
|
|
38473
38719
|
setupZoomReset() {
|
|
38474
38720
|
const resetBtn = this.container.querySelector('.zoom-reset-btn');
|
|
38475
38721
|
if (resetBtn) {
|