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.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
- // FALLBACK LOGIC: Try to find most recent available data
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
- chartDataPoints = validDataPoints.map(point => ({
38199
- x: this.parseTimestamp(point.time).getTime(),
38200
- o: point.open,
38201
- h: point.high,
38202
- l: point.low,
38203
- c: point.close
38204
- })).filter(point => {
38205
- // Filter out invalid timestamps
38206
- return !isNaN(point.x) && point.x > 0;
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
- chartDataPoints = validDataPoints.map(point => ({
38230
- x: this.parseTimestamp(point.time),
38231
- y: point.close
38232
- })).filter(point => {
38233
- // Filter out invalid dates
38234
- const timestamp = point.x.getTime();
38235
- return !isNaN(timestamp) && timestamp > 0;
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 to prevent Chart.js from auto-calculating incorrectly
38272
- const timestamps = chartDataPoints.map(p => this.chartType === 'candlestick' ? p.x : p.x.getTime());
38273
- const minTimestamp = Math.min(...timestamps);
38274
- const maxTimestamp = Math.max(...timestamps);
38275
- console.log('[IntradayChartWidget] X-axis bounds:', {
38276
- min: minTimestamp,
38277
- max: maxTimestamp,
38278
- minDate: new Date(minTimestamp),
38279
- maxDate: new Date(maxTimestamp)
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
- // Format timestamp for tooltip
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: this.rangeBack === 0 ? 'minute' : 'hour',
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
- minute: 'h:mm a',
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) {