@millistream/millistream-widgets 1.0.33 → 1.0.35

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.
Files changed (2) hide show
  1. package/millistream-widgets.js +1669 -711
  2. package/package.json +1 -1
@@ -5,220 +5,8 @@
5
5
  * Homepage: https://github.com/jondavidjohn/hidpi-canvas-polyfill
6
6
  * Issue Tracker: https://github.com/jondavidjohn/hidpi-canvas-polyfill/issues
7
7
  * License: Apache-2.0
8
+ * Modifed by: Millistream
8
9
  */
9
- (function(prototype) {
10
-
11
- var pixelRatio = (function() {
12
- var canvas = document.createElement('canvas'),
13
- context = canvas.getContext('2d');
14
- /*backingStore = context.backingStorePixelRatio ||
15
- context.webkitBackingStorePixelRatio ||
16
- context.mozBackingStorePixelRatio ||
17
- context.msBackingStorePixelRatio ||
18
- context.oBackingStorePixelRatio ||
19
- context.backingStorePixelRatio || 1;
20
- return (window.devicePixelRatio || 1) / backingStore;
21
- */
22
- })(),
23
- forEach = function(obj, func) {
24
- for (var p in obj) {
25
- if (obj.hasOwnProperty(p)) {
26
- func(obj[p], p);
27
- }
28
- }
29
- },
30
-
31
- ratioArgs = {
32
- //'fillRect': 'all',
33
- //'clearRect': 'all',
34
- 'strokeRect': 'all',
35
- 'moveTo': 'all',
36
- 'lineTo': 'all',
37
- 'arc': [0, 1, 2],
38
- 'arcTo': 'all',
39
- 'bezierCurveTo': 'all',
40
- 'isPointinPath': 'all',
41
- 'isPointinStroke': 'all',
42
- 'quadraticCurveTo': 'all',
43
- 'rect': 'all',
44
- 'translate': 'all',
45
- 'createRadialGradient': 'all',
46
- //'createLinearGradient': 'all'
47
- };
48
-
49
- if (pixelRatio === 1) return;
50
-
51
- function getPixelRatio(_this) {
52
- /* var backingStore = this.backingStorePixelRatio ||
53
- this.webkitBackingStorePixelRatio ||
54
- this.mozBackingStorePixelRatio ||
55
- this.msBackingStorePixelRatio ||
56
- this.oBackingStorePixelRatio ||
57
- this.backingStorePixelRatio || 1;*/
58
- var backingStore = _this.backingStorePixelRatio ||
59
- _this.webkitBackingStorePixelRatio ||
60
- _this.mozBackingStorePixelRatio ||
61
- _this.msBackingStorePixelRatio ||
62
- _this.oBackingStorePixelRatio ||
63
- _this.backingStorePixelRatio || 1;
64
- return (window.devicePixelRatio || 1) / backingStore;
65
-
66
- }
67
-
68
- forEach(ratioArgs, function(value, key) {
69
- prototype[key] = (function(_super) {
70
- return function() {
71
- var i, len,
72
- args = Array.prototype.slice.call(arguments);
73
- if (key == 'lineTo' || key == 'moveTo') { // PF
74
- args = args.map(function(a) {
75
- return a;
76
- });
77
- } else
78
- if (value === 'all') {
79
- args = args.map(function(a) {
80
- return a * getPixelRatio(this);
81
- });
82
- } else if (Array.isArray(value)) {
83
- for (i = 0, len = value.length; i < len; i++) {
84
- args[value[i]] *= getPixelRatio(this);
85
- }
86
- }
87
-
88
- return _super.apply(this, args);
89
- };
90
- })(prototype[key]);
91
- });
92
-
93
- // Stroke lineWidth adjustment
94
- prototype.stroke = (function(_super) {
95
- return function() {
96
- this.lineWidth *= getPixelRatio(this);
97
- _super.apply(this, arguments);
98
- this.lineWidth /= getPixelRatio(this);
99
- };
100
- })(prototype.stroke);
101
-
102
- prototype.measureText = (function(_super) {
103
- return function() {
104
- var args = Array.prototype.slice.call(arguments);
105
-
106
- var tmp = this.font;
107
- var _this = this;
108
- this.font = this.font.replace(
109
- /(\d+)(px|em|rem|pt)/g,
110
- function(w, m, u) {
111
- return Math.floor(m * getPixelRatio(_this)) + u;
112
- }
113
- );
114
- var i = _super.apply(this, args);
115
-
116
- this.font = tmp;
117
- /*this.font = this.font.replace(
118
- /(\d+)(px|em|rem|pt)/g,
119
- function(w, m, u) {
120
- return Math.floor(m / getPixelRatio()) + u;
121
- }
122
- );*/
123
- return i;
124
- };
125
- })(prototype.measureText);
126
-
127
-
128
- prototype.fillText = (function(_super) {
129
- return function() {
130
- var args = Array.prototype.slice.call(arguments);
131
- var tmp = this.font;
132
- var _this = this;
133
- this.font = this.font.replace(
134
- /(\d+)(px|em|rem|pt)/g,
135
- function(w, m, u) {
136
- return (m * getPixelRatio(_this)) + u;
137
- }
138
- );
139
- _super.apply(this, args);
140
- this.font = tmp;
141
- };
142
- })(prototype.fillText);
143
-
144
- prototype.strokeText = (function(_super) {
145
- return function() {
146
- var args = Array.prototype.slice.call(arguments);
147
-
148
- args[1] *= getPixelRatio(this); // x
149
- args[2] *= getPixelRatio(this); // y
150
- var tmp = this.font;
151
- var _this = this;
152
- this.font = this.font.replace(
153
- /(\d+)(px|em|rem|pt)/g,
154
- function(w, m, u) {
155
- return (m * getPixelRatio(_this)) + u;
156
- }
157
- );
158
-
159
- _super.apply(this, args);
160
- this.font = tmp;
161
- /*this.font = this.font.replace(
162
- /(\d+)(px|em|rem|pt)/g,
163
- function(w, m, u) {
164
- return (m / getPixelRatio()) + u;
165
- }
166
- );*/
167
- };
168
- })(prototype.strokeText);
169
-
170
- })(CanvasRenderingContext2D.prototype);
171
-
172
- (function(prototype) {
173
- var context = null;
174
-
175
- function getPixelRatio() {
176
- var backingStore = context.backingStorePixelRatio ||
177
- context.webkitBackingStorePixelRatio ||
178
- context.mozBackingStorePixelRatio ||
179
- context.msBackingStorePixelRatio ||
180
- context.oBackingStorePixelRatio ||
181
- context.backingStorePixelRatio || 1;
182
-
183
- return (window.devicePixelRatio || 1) / backingStore;
184
-
185
- }
186
- prototype.getContext = (function(_super) {
187
- return function(type) {
188
- //var backingStore, ratio;
189
- context = _super.call(this, type);
190
-
191
- if (type === '2d') {
192
-
193
- /*backingStore = context.backingStorePixelRatio ||
194
- context.webkitBackingStorePixelRatio ||
195
- context.mozBackingStorePixelRatio ||
196
- context.msBackingStorePixelRatio ||
197
- context.oBackingStorePixelRatio ||
198
- context.backingStorePixelRatio || 1;*/
199
-
200
- var ratio = getPixelRatio();
201
- if (ratio > 1) {
202
- this.style.height = this.height + 'px';
203
- this.style.width = this.width + 'px';
204
- this.width *= ratio;
205
- this.height *= ratio;
206
- }
207
- }
208
-
209
- return context;
210
- };
211
- })(prototype.getContext);
212
-
213
- prototype.setRect = (function(height, width) {
214
- if (context == null) return;
215
- this.style.height = height + 'px';
216
- this.style.width = width + 'px';
217
- this.width = width * getPixelRatio();
218
- this.height = height * getPixelRatio();
219
- });
220
-
221
- })(HTMLCanvasElement.prototype);
222
10
 
223
11
  // Millistream Chart
224
12
  function Milli_Chart(settings) {
@@ -226,12 +14,26 @@ function Milli_Chart(settings) {
226
14
  var _this = this;
227
15
  _this.mdf_fields = [];
228
16
  _this.scaleinfoX = {};
229
- _this.scaleinfoY = {};
230
- _this.scaleinfoY2 = {};
231
17
  _this.instruments = [];
232
- _this.movingAverage = [];
233
18
  _this.unsubscriptions = {};
234
-
19
+ let scaleinfoY = {};
20
+ let scaleinfoY2 = {};
21
+ let scaleinfo = { // TODO: implement
22
+ y: {},
23
+ y2: {},
24
+ x: {},
25
+ ly: {}
26
+ };
27
+ let testscale = {
28
+ valuePerPixel: 0,
29
+ minValue: 0,
30
+ maxValue: 0,
31
+ lowValue: 0,
32
+ highValue: 0,
33
+ lineLength: 0,
34
+ decimals: 0, // behövs den?
35
+ tickSize: 0
36
+ };
235
37
  _this.settings = {
236
38
  adjusted: true,
237
39
  absoluteScaling: false,
@@ -258,6 +60,7 @@ function Milli_Chart(settings) {
258
60
  intradayDatePos: { x: 'center', y: 'bottom', orientation: 'horizontal', dateformat: 'd mmm' },
259
61
  intradaylen: null,
260
62
  messagetypes: 1030, // quote,trades and performance
63
+ newslanguage: ['sv'],
261
64
  nochartlabel: 'No data to draw on',
262
65
  onreadyCallback: null,
263
66
  previousDayClose: true,
@@ -268,12 +71,20 @@ function Milli_Chart(settings) {
268
71
  xhr: false,
269
72
  yearLabelsPos: 'bottom',
270
73
  priceIndicator: false,
74
+ type: 'ohlc ',
271
75
  tooltip: {
272
76
  display: 'block'
273
77
  },
78
+ ohlc: {
79
+ offset: "3px"
80
+ },
81
+ candlestick: {
82
+ upColor: "#00ff00",
83
+ downColor: "#ff0000"
84
+ },
274
85
  xAxisSpacing: 0,
275
86
  yAxisSpacing: 4, // undocumented
276
- xAxisModulo: 1 // undocumented
87
+ xAxisModulo: 1 // undocumented,
277
88
  };
278
89
  var m_startdate = null;
279
90
  var m_chartspaces = {
@@ -286,6 +97,7 @@ function Milli_Chart(settings) {
286
97
  height: 0
287
98
  }
288
99
  };
100
+ let chartType; // history or trades, set in drawChart
289
101
  var m_dummyDiv = null; // dummy div For chartclasses
290
102
  var m_canvas = null;
291
103
  var m_ctx = null;
@@ -318,6 +130,9 @@ function Milli_Chart(settings) {
318
130
  };
319
131
  var m_lastDrawnInstrument = -1;
320
132
  var m_priceIndicator = undefined;
133
+ const INDICATOR = 0;
134
+ const NEWSINDICATOR = 1;
135
+ const FUTUREINDICATOR = 2;
321
136
 
322
137
  MillistreamWidgetApi_AssignObject(MillistreamWidgetSettings, _this.settings);
323
138
  if (settings) {
@@ -404,7 +219,8 @@ function Milli_Chart(settings) {
404
219
  }
405
220
 
406
221
  function getScaledSetting(setting) {
407
- return parseInt(setting) * window.devicePixelRatio;
222
+ return parseInt(setting);
223
+ // return parseInt(setting) * window.devicePixelRatio;
408
224
  }
409
225
  _this.get_lang_text = function(string) {
410
226
  return string;
@@ -444,7 +260,7 @@ function Milli_Chart(settings) {
444
260
  }
445
261
 
446
262
  function getFontSize(obj) {
447
- //return parseInt(obj.fontSize) * window.devicePixelRatio;
263
+ //return parseInt(obj.fontSize) * 1;
448
264
  return parseInt(obj.fontSize);
449
265
  }
450
266
 
@@ -593,46 +409,149 @@ function Milli_Chart(settings) {
593
409
  return value * Math.pow(10, x);
594
410
  }
595
411
 
596
- function calcHighLow() {
597
- _this.scaleinfoY.lowValue = null;
598
- _this.scaleinfoY.highValue = null;
599
- _this.scaleinfoY2.lowValue = null;
600
- _this.scaleinfoY2.highValue = null;
601
- _this.scaleinfoY.lowLowerChart = null;
602
- _this.scaleinfoY.highLowerChart = null;
412
+
413
+ function drawYAxisIndicator(data, cs, legendItems) {
414
+ m_ctx.save();
415
+ m_ctx.font = m_yLegendCss.fontWeight + ' ' + m_yLegendCss.fontSize + ' ' + m_yLegendCss.fontFamily;
416
+ m_ctx.fillStyle = m_yLegendCss.color;
417
+ m_ctx.beginPath();
418
+ m_ctx.strokeStyle = m_gridVerticalCss.color;
419
+ m_ctx.moveTo(cs.left + 0.5, cs.top);
420
+ m_ctx.lineTo(cs.left + 0.5, cs.bottom);
421
+ m_ctx.stroke();
422
+ m_ctx.closePath();
423
+ var x = cs.left - 3;
424
+ var lineLength = cs.bottom - cs.top;
425
+ var numticks = lineLength / (getFontSize(m_yLegendCss) * 2);
426
+ if (numticks < 1) {
427
+ console.log('no space for legenditems', lineLength, m_chartspaces);
428
+ return null;
429
+ }
430
+ let sc = calculateIndicatorScale(data, cs);
431
+ if (sc == null) {
432
+ console.log('No scale');
433
+ return null;
434
+ }
435
+ if (typeof minvalue !== 'undefined') return sc;
436
+ if (numticks > 8) numticks = 8; // limit to 8 items on Y legend ( this is not an absolut count, since we calculate nice legend numbers
437
+
438
+ sc.valuePerPixel = (sc.maxValue - sc.minValue) / lineLength;
439
+ if (isNaN(sc.valuePerPixel) || !isFinite(sc.valuePerPixel)) {
440
+ console.log('cant draw valuePerPixel' + sc.valuePerPixel, lineLength, sc.maxValue);
441
+ return null;
442
+ }
443
+
444
+ m_ctx.strokeStyle = m_gridHorizontalCss.color;
445
+ m_ctx.fillStyle = m_yLegendCss.color;
446
+
447
+ m_ctx.textAlign = 'right';
448
+ var tickSize = getTickValue(sc.low, sc.high, numticks);
449
+
450
+ let value = sc.minValue;
451
+ if (sc.high == sc.low) { // only have one value so set values for 1 line only
452
+ sc.maxValue = sc.maxValue + tickSize;
453
+ sc.minValue = sc.minValue - tickSize;
454
+ value = Math.abs(sc.lowValue);
455
+ } else {
456
+ if (sc.minValue > 0)
457
+ value = sc.minValue - fmod(Math.abs(sc.minValue), tickSize) + tickSize;
458
+ else
459
+ value = sc.minValue + fmod(Math.abs(sc.minValue), tickSize) - tickSize;
460
+ }
461
+ for (let c = 0; c < 10; c++) { // max 10 legenditems
462
+ let y = Math.round(cs.height - cs.marginBottom - ((value - sc.minValue) * sc.valuePerPixel)) + 0.5;
463
+ y = Math.round((cs.bottom - cs.top) - ((value - sc.minValue) / sc.valuePerPixel)) + 0.5 + cs.top;
464
+ if (y <= cs.top) break;
465
+ if (y <= cs.bottom) {
466
+ if (_this.settings.gridHorizontalLines == true && legendItems) {
467
+ m_ctx.save();
468
+ if (_this.settings.gridHorizontalLinesStyle == 'dash') {
469
+ m_ctx.setLineDash([3, 3]);
470
+ }
471
+ m_ctx.beginPath();
472
+ m_ctx.moveTo(cs.left, y + 0.5);
473
+ m_ctx.lineTo(cs.right, y + 0.5);
474
+ m_ctx.stroke();
475
+ m_ctx.closePath();
476
+ m_ctx.restore();
477
+ } else
478
+ if (_this.settings.drawyaxis == true && legendItems == true) { // draw legenditem markers for price
479
+ m_ctx.beginPath();
480
+ m_ctx.moveTo(cs.left, y + 0.5);
481
+ m_ctx.lineTo(cs.left + 3, y + 0.5);
482
+ m_ctx.stroke();
483
+ m_ctx.closePath();
484
+ }
485
+ if (legendItems == true && _this.settings.drawyaxis == true) {
486
+ var label = formatLargeNumber(value, 0, _this);
487
+ var textpos = x - 5;
488
+ if (m_yLegendCss.verticalAlign == 'top') {
489
+ if (y - (getFontSize(m_yLegendCss)) > 0) // dont draw if cropped
490
+ m_ctx.fillText(label, textpos, y - ((getFontSize(m_yLegendCss) + 2)));
491
+ } else
492
+ m_ctx.fillText(label, textpos, y - (getFontSize(m_yLegendCss) / 2));
493
+ }
494
+ }
495
+ value += tickSize;
496
+ }
497
+ m_ctx.restore();
498
+ return sc;
499
+ }
500
+
501
+ function checkYLegendSpace(y, text) {
502
+ if (y - (getFontSize(m_yLegendCss)) - m_chartspaces.chart.top < 0) return false;
503
+ if (y > m_chartspaces.chart.bottom) return false;
504
+ return true;
505
+ }
506
+
507
+ function calcHighLow(scaleinfoY, scaleinfoY2) {
508
+ scaleinfoY.lowValue = null;
509
+ scaleinfoY.highValue = null;
510
+ scaleinfoY2.lowValue = null;
511
+ scaleinfoY2.highValue = null;
512
+
603
513
  var data, i;
604
514
  var useCloseprice = false;
605
515
  var today = new Date().getTime();
606
516
  today -= today % 86400000;
607
517
  var lastTradeDate = new Date().getTime();
608
518
  var todaysOpenTime = new Date(new Date().toISOString().substring(0, 10) + 'T' + _this.instruments[0].marketopen + 'Z').getTime();
519
+
609
520
  if (typeof _this.instruments[0].trades !== 'undefined' && _this.instruments[0].trades.length > 0) {
610
521
  lastTradeDate = new Date(_this.instruments[0].trades[_this.instruments[0].trades.length - 1].timestamp).getTime();
611
522
  lastTradeDate -= lastTradeDate % 86400000;
612
523
  }
613
- var quote_timestamp = _this.instruments[0].quotedate + _this.instruments[0].quotetime;
614
524
 
525
+ var quote_timestamp = _this.instruments[0].quotedate + _this.instruments[0].quotetime;
615
526
  if ((_this.instruments[0].quotedate == today && quote_timestamp > todaysOpenTime) || _this.instruments[0].quotedate == lastTradeDate) useCloseprice = true;
527
+
616
528
  for (var s = 0; s < _this.instruments.length; s++) {
617
529
  if (_this.instruments[s].insref == 0) continue;
618
530
  _this.instruments[s].startValue = null;
619
- data = _this.instruments[s][_this.scaleinfoY.type];
620
- if (_this.scaleinfoY.type != 'history' && useCloseprice) {
531
+ data = _this.instruments[s][chartType];
532
+ if (chartType != 'history' && useCloseprice) {
621
533
  if ((_this.settings.chartlen == '1d' || _this.settings.chartlen == '0d') && !m_zoom.mousedown.timestamp) {
622
534
  _this.instruments[s].startValue = parseFloat(_this.instruments[s].closeprice1d) * _this.instruments[s].factor;
623
535
  }
624
536
  }
625
- var quantity = 0;
626
-
537
+ //var quantity = 0;
538
+ let prevPrice = null;
627
539
  for (i = 0; i < data.length; i++) {
628
540
  // only calc on visible data
629
541
  var price = data[i].price * _this.instruments[s].factor;
630
- quantity = 0;
631
- if (data[i].quantity !== 'undefined') {
542
+ let highprice = data[i].highprice * _this.instruments[s].factor;
543
+ let lowprice = data[i].lowprice * _this.instruments[s].factor;
544
+
545
+ //quantity = 0;
546
+ /*if (data[i].quantity !== 'undefined') {
632
547
  quantity = data[i].quantity;
633
- }
548
+ }*/
634
549
  if (data[i].timestamp < _this.scaleinfoX.startTimeStamp) {
635
- if (_this.scaleinfoY.type == 'history') _this.instruments[s].startValue = price;
550
+ if (chartType == 'history') {
551
+ //_this.instruments[s].startValue = price; // skall inte sättas eftersom vi inte ritar den i history
552
+ scaleinfoY2.lowValue = 0;
553
+ scaleinfoY2.highValue = 0;
554
+ }
636
555
  //else if (_this.settings.chartlen != '1d' && _this.settings.chartlen != '0d' && !m_zoom.mousedown.timestamp) {
637
556
  else if (useCloseprice == false || m_zoom.mousedown.timestamp) {
638
557
  _this.instruments[s].startValue = price;
@@ -642,14 +561,15 @@ function Milli_Chart(settings) {
642
561
  if (data[i].timestamp > _this.scaleinfoX.endTimeStamp) {
643
562
  break;
644
563
  }
564
+ _this.instruments[s].endValue = price;
645
565
 
646
- if (_this.scaleinfoY.type != 'history' && (data[i].timestamp % 86400000 < _this.instruments[0].opentimestamp || data[i].timestamp % 86400000 > _this.instruments[0].closetimestamp)) {
566
+ if (chartType != 'history' && (data[i].timestamp % 86400000 < _this.instruments[0].opentimestamp || data[i].timestamp % 86400000 > _this.instruments[0].closetimestamp)) {
647
567
  // stämmer detta kan det bli överlapp vid sommartid/vintertid?
648
568
  continue;
649
569
  }
650
570
 
651
- if (_this.instruments[s].startValue == null) {
652
- if (_this.scaleinfoY.type == 'history') {
571
+ if (_this.instruments[s].startValue == null) { // no value before this date , use this date?
572
+ if (chartType == 'history') {
653
573
  _this.instruments[s].startValue = price;
654
574
  } else {
655
575
  if (isToday(new Date(data[i].timestamp)) && !m_zoom.mousedown.timestamp) {
@@ -659,51 +579,64 @@ function Milli_Chart(settings) {
659
579
  }
660
580
  }
661
581
  }
662
- if (_this.scaleinfoY.lowValue == null || _this.scaleinfoY.lowValue > price) _this.scaleinfoY.lowValue = price;
663
- if (_this.scaleinfoY.highValue == null || _this.scaleinfoY.highValue < price) _this.scaleinfoY.highValue = price;
582
+ if(_this.settings.type == 'ohlc' || _this.settings.type == 'candlestick') {
583
+ if (scaleinfoY.lowValue == null || scaleinfoY.lowValue > lowprice) scaleinfoY.lowValue = lowprice;
584
+ if (scaleinfoY.highValue == null || scaleinfoY.highValue < highprice) scaleinfoY.highValue = highprice;
585
+ }
586
+ else {
587
+ if (scaleinfoY.lowValue == null || scaleinfoY.lowValue > price) scaleinfoY.lowValue = price;
588
+ if (scaleinfoY.highValue == null || scaleinfoY.highValue < price) scaleinfoY.highValue = price;
589
+ }
664
590
  var diff = (price - _this.instruments[s].startValue);
665
591
  if (diff != 0) diff = diff / _this.instruments[s].startValue * 100;
666
592
  if (_this.instruments[s].startValue == null) diff = 0;
667
593
  data[i].diff = diff;
668
-
669
- if (_this.scaleinfoY2.lowValue == null || _this.scaleinfoY2.lowValue > diff) _this.scaleinfoY2.lowValue = diff;
670
- if (_this.scaleinfoY2.highValue == null || _this.scaleinfoY2.highValue < diff) _this.scaleinfoY2.highValue = diff;
671
- if (_this.scaleinfoY.lowLowerChart == null || _this.scaleinfoY.lowLowerChart > quantity) _this.scaleinfoY.lowLowerChart = quantity;
672
- if (_this.scaleinfoY.highLowerChart == null || _this.scaleinfoY.highLowerChart < quantity) _this.scaleinfoY.highLowerChart = quantity;
594
+ if (scaleinfoY2.lowValue == null || scaleinfoY2.lowValue > diff) scaleinfoY2.lowValue = diff;
595
+ if (scaleinfoY2.highValue == null || scaleinfoY2.highValue < diff) scaleinfoY2.highValue = diff;
596
+ //if (scaleinfoY.lowLowerChart == null || scaleinfoY.lowLowerChart > quantity) scaleinfoY.lowLowerChart = quantity;
597
+ //if (scaleinfoY.highLowerChart == null || scaleinfoY.highLowerChart < quantity) scaleinfoY.highLowerChart = quantity;
673
598
  }
674
599
  if ((_this.settings.chartlen == '1d' || _this.settings.chartlen == '0d') && !m_zoom.mousedown.timestamp) { // if closeprice is used calch high/low on it
675
600
  var cp = parseFloat(_this.instruments[s].closeprice1d) * _this.instruments[s].factor;
676
- if (_this.scaleinfoY.lowValue > cp) _this.scaleinfoY.lowValue = cp;
601
+ if (scaleinfoY.lowValue > cp) scaleinfoY.lowValue = cp;
677
602
  else
678
- if (_this.scaleinfoY.highValue < cp) _this.scaleinfoY.highValue = cp;
603
+ if (scaleinfoY.highValue < cp) scaleinfoY.highValue = cp;
679
604
  }
680
- if (_this.scaleinfoY.type != 'history') {
681
- if (_this.scaleinfoY2.lowValue > 0) {
682
- _this.scaleinfoY2.lowValue = 0;
605
+ if (chartType != 'history') {
606
+ if (scaleinfoY2.lowValue > 0) {
607
+ scaleinfoY2.lowValue = 0;
683
608
  } else {
684
- if (_this.scaleinfoY2.highValue < 0) _this.scaleinfoY2.highValue = 0;
609
+ if (scaleinfoY2.highValue < 0) scaleinfoY2.highValue = 0;
685
610
  }
686
611
  }
687
- if (_this.instruments[s].startValue) {
688
- if (_this.instruments[s].startValue > _this.scaleinfoY.highValue) _this.scaleinfoY.highValue = _this.instruments[s].startValue;
689
- if (_this.instruments[s].startValue < _this.scaleinfoY.lowValue) _this.scaleinfoY.lowValue = _this.instruments[s].startValue;
612
+ if (_this.instruments[s].startValue) { // ta bort? fråga mats
613
+ if (_this.instruments[s].startValue > scaleinfoY.highValue) scaleinfoY.highValue = _this.instruments[s].startValue;
614
+ if (_this.instruments[s].startValue < scaleinfoY.lowValue) scaleinfoY.lowValue = _this.instruments[s].startValue;
690
615
  }
691
616
  }
692
-
693
- if (_this.scaleinfoY.lowValue == null) {
694
- _this.scaleinfoY.lowValue = 0;
695
- _this.scaleinfoY.highValue = 100;
696
- _this.scaleinfoY2.lowValue = 0;
697
- _this.scaleinfoY2.highValue = 100;
617
+ if (scaleinfoY.lowValue == null) {
618
+ scaleinfoY.lowValue = 0;
619
+ scaleinfoY.highValue = 100;
620
+ scaleinfoY2.lowValue = 0;
621
+ scaleinfoY2.highValue = 100;
622
+ return;
698
623
  } else
699
- if (_this.scaleinfoY.lowValue == _this.scaleinfoY.highValue && _this.scaleinfoY.lowValue == 0) {
700
- _this.scaleinfoY.lowValue -= 1;
701
- _this.scaleinfoY.highValue += 1;
624
+ if (scaleinfoY.lowValue == scaleinfoY.highValue && scaleinfoY.lowValue == 0) {
625
+ scaleinfoY.lowValue -= 1;
626
+ scaleinfoY.highValue += 1;
627
+ scaleinfoY.lowValue -= 1;
628
+ scaleinfoY.highValue += 1;
702
629
  }
703
630
  // do we have any analyzis we need to take into account
704
631
  for (i = 0; i < _this.settings.indicators.length; i++) {
705
- if (_this.scaleinfoY.type == 'history') data = _this.settings.indicators[i].history;
706
- else data = _this.settings.indicators[i].trades;
632
+ if (_this.settings.indicators[i].method == 'rsi') continue;
633
+ if (_this.settings.indicators[i].method == 'quantity') continue;
634
+ if (_this.settings.indicators[i].method == 'news') continue;
635
+ if (_this.settings.indicators[i].target == 'lower') continue;
636
+ if (!_this.settings.indicators[i].timeseries || _this.settings.indicators[i].timeseries.length == 0) {
637
+ continue;
638
+ }
639
+ data = _this.settings.indicators[i].timeseries;
707
640
  for (s = 0; s < data.length; s++) {
708
641
  if (data[s].timestamp < _this.scaleinfoX.startTimeStamp) {
709
642
  continue;
@@ -712,132 +645,214 @@ function Milli_Chart(settings) {
712
645
  break;
713
646
  }
714
647
  if (typeof data[s].datapoints !== 'undefined') {
715
- for (var x = 0; x < data[i].datapoints.length; x++) {
716
- if (data[s].datapoints[x] < _this.scaleinfoY.lowValue) _this.scaleinfoY.lowValue = data[s].datapoints[x];
717
- else
718
- if (data[s].datapoints[x] > _this.scaleinfoY.highValue) _this.scaleinfoY.highValue = data[s].datapoints[x];
648
+ for (var x = 0; x < data[s].datapoints.length; x++) {
649
+ if (data[s].datapoints[x] < scaleinfoY.lowValue) {
650
+ scaleinfoY.lowValue = data[s].datapoints[x];
651
+ scaleinfoY2.lowValue = data[s].datapoints[x] - _this.instruments[0].startValue; // lower min diff to get full legend from bottom
652
+ } else {
653
+ if (data[s].datapoints[x] > scaleinfoY.highValue) {
654
+ scaleinfoY.highValue = data[s].datapoints[x];
655
+ scaleinfoY2.highValue = data[s].datapoints[x] - _this.instruments[0].startValue; // lower min diff to get full legend from bottom
656
+ }
657
+ }
719
658
  }
720
659
  }
721
660
  }
722
661
  }
723
- _this.scaleinfoY2.lowValue = (_this.scaleinfoY.lowValue - _this.instruments[0].startValue) / _this.instruments[0].startValue * 100;
724
- _this.scaleinfoY2.highValue = (_this.scaleinfoY.highValue - _this.instruments[0].startValue) / _this.instruments[0].startValue * 100;
662
+ scaleinfoY2.lowValue = (scaleinfoY.lowValue - _this.instruments[0].startValue) / _this.instruments[0].startValue * 100;
663
+ scaleinfoY2.highValue = (scaleinfoY.highValue - _this.instruments[0].startValue) / _this.instruments[0].startValue * 100;
725
664
  return 1;
726
665
  }
727
666
 
728
- function drawYLegendLower(x, numticks, lineLength, markers) {
729
- var i;
730
- _this.scaleinfoY.lowLowerChart = null;
731
- _this.scaleinfoY.highLowerChart = null;
667
+ function calcHighLow2(scale, cs, css, factorInfo) {
668
+ scale.lowValue = null;
669
+ scale.highValue = null;
732
670
 
733
- for (i = 0; i < m_datapoints.length; i++) {
734
- if (typeof m_datapoints[i].quantity !== 'undefined') {
735
- if (_this.scaleinfoY.lowLowerChart == null || _this.scaleinfoY.lowLowerChart > m_datapoints[i].quantity) _this.scaleinfoY.lowLowerChart = m_datapoints[i].quantity;
736
- if (_this.scaleinfoY.highLowerChart == null || _this.scaleinfoY.highLowerChart < m_datapoints[i].quantity) _this.scaleinfoY.highLowerChart = m_datapoints[i].quantity;
737
- }
738
- }
739
- if (numticks < 1) return;
740
- if (numticks > 10) numticks = 10;
741
- m_ctx.strokeStyle = m_gridHorizontalCss.color;
742
- m_ctx.fillStyle = m_yLegendCss.color;
671
+ let data, i;
672
+ let useCloseprice = false;
673
+ let today = new Date().getTime();
674
+ today -= today % 86400000;
675
+ let lastTradeDate = new Date().getTime();
676
+ let todaysOpenTime = new Date(new Date().toISOString().substring(0, 10) + 'T' + _this.instruments[0].marketopen + 'Z').getTime();
743
677
 
744
- m_ctx.textAlign = 'right';
745
- var tickSize = getTickValue(_this.scaleinfoY.lowLowerChart, _this.scaleinfoY.highLowerChart, numticks);
746
- var maxValue = _this.scaleinfoY.highLowerChart == 0 ? 100 : _this.scaleinfoY.highLowerChart + (tickSize * 0.2);
747
- var valuePerPixel = lineLength / maxValue;
748
- if (isNaN(valuePerPixel) || !isFinite(valuePerPixel)) {
749
- console.log('cant draw valuePerPixel' + valuePerPixel, lineLength, maxValue);
750
- return false;
678
+ if (typeof _this.instruments[0].trades !== 'undefined' && _this.instruments[0].trades.length > 0) {
679
+ lastTradeDate = new Date(_this.instruments[0].trades[_this.instruments[0].trades.length - 1].timestamp).getTime();
680
+ lastTradeDate -= lastTradeDate % 86400000;
751
681
  }
752
- var value = 0;
753
- for (i = 0; i < numticks; i++) {
754
- var y = Math.round(m_chartspaces.lowerChart.bottom - (value * valuePerPixel));
755
682
 
756
- if (y <= m_chartspaces.lowerChart.top) break;
757
- if (y <= m_chartspaces.lowerChart.bottom) {
758
- if (_this.settings.gridHorizontalLines == true) {
759
- m_ctx.save();
760
- if (_this.settings.gridHorizontalLinesStyle == 'dash') {
761
- m_ctx.setLineDash([3, 3]);
683
+ var quote_timestamp = _this.instruments[0].quotedate + _this.instruments[0].quotetime;
684
+ if ((_this.instruments[0].quotedate == today && quote_timestamp > todaysOpenTime) || _this.instruments[0].quotedate == lastTradeDate) useCloseprice = true;
685
+
686
+ for (var s = 0; s < _this.instruments.length; s++) {
687
+ if (_this.instruments[s].insref == 0) continue;
688
+ _this.instruments[s].startValue = null;
689
+ data = _this.instruments[s][chartType];
690
+ if (chartType != 'history' && useCloseprice) {
691
+ if ((_this.settings.chartlen == '1d' || _this.settings.chartlen == '0d') && !m_zoom.mousedown.timestamp) {
692
+ _this.instruments[s].startValue = parseFloat(_this.instruments[s].closeprice1d) * _this.instruments[s].factor;
693
+ }
694
+ }
695
+ //var quantity = 0;
696
+
697
+ for (i = 0; i < data.length; i++) {
698
+ // only calc on visible data
699
+ var price = data[i].price * _this.instruments[s].factor;
700
+
701
+ //quantity = 0;
702
+ /*if (data[i].quantity !== 'undefined') {
703
+ quantity = data[i].quantity;
704
+ }*/
705
+ if (data[i].timestamp < _this.scaleinfoX.startTimeStamp) {
706
+ if (chartType == 'history') {
707
+ _this.instruments[s].startValue = price;
762
708
  }
763
- m_ctx.beginPath();
764
- m_ctx.moveTo(m_chartspaces.lowerChart.left, y + 0.5);
765
- m_ctx.lineTo(m_chartspaces.lowerChart.right, y + 0.5);
766
- m_ctx.stroke();
767
- m_ctx.closePath();
768
- m_ctx.restore();
769
- } else
770
- if (_this.settings.drawyaxis == true && markers == true) { // draw legenditem markers for price
771
- m_ctx.beginPath();
772
- m_ctx.moveTo(m_chartspaces.lowerChart.left, y + 0.5);
773
- m_ctx.lineTo(m_chartspaces.lowerChart.left + 3, y + 0.5);
774
- m_ctx.stroke();
775
- m_ctx.closePath();
709
+ //else if (_this.settings.chartlen != '1d' && _this.settings.chartlen != '0d' && !m_zoom.mousedown.timestamp) {
710
+ else if (useCloseprice == false || m_zoom.mousedown.timestamp) {
711
+ _this.instruments[s].startValue = price;
712
+ }
713
+ continue;
714
+ }
715
+ if (data[i].timestamp > _this.scaleinfoX.endTimeStamp) {
716
+ break;
776
717
  }
777
718
 
778
- if (markers == true && _this.settings.drawyaxis == true) {
779
- var label = formatLargeNumber(value, 0, _this);
780
- var textpos = x - 5;
781
- if (m_yLegendCss.verticalAlign == 'top') {
782
- if (y - (getFontSize(m_yLegendCss)) > 0) // dont draw if cropped
783
- m_ctx.fillText(label, textpos, y - ((getFontSize(m_yLegendCss) + 2)));
784
- } else
785
- m_ctx.fillText(label, textpos, y - (getFontSize(m_yLegendCss) / 2));
719
+ if (chartType != 'history' && (data[i].timestamp % 86400000 < _this.instruments[0].opentimestamp || data[i].timestamp % 86400000 > _this.instruments[0].closetimestamp)) { // summertime?
720
+ continue;
721
+ }
722
+
723
+ if (_this.instruments[s].startValue == null) { // no value before this date , use this date?
724
+ if (chartType == 'history') {
725
+ _this.instruments[s].startValue = price;
726
+ } else {
727
+ if (isToday(new Date(data[i].timestamp)) && !m_zoom.mousedown.timestamp) {
728
+ _this.instruments[s].startValue = parseFloat(_this.instruments[s].closeprice1d) * _this.instruments[s].factor;
729
+ } else {
730
+ _this.instruments[s].startValue = price;
731
+ }
732
+ }
733
+ }
734
+ if (typeof factorInfo === 'undefined') {
735
+ if (scale.lowValue == null || scale.lowValue > price) scale.lowValue = price;
736
+ if (scale.highValue == null || scale.highValue < price) scale.highValue = price;
737
+ } else {
738
+ let diff = (price - _this.instruments[s].startValue);
739
+ if (diff != 0) diff = diff / _this.instruments[s].startValue * 100;
740
+ if (_this.instruments[s].startValue == null) diff = 0;
741
+ data[i][factorInfo.name] = diff;
742
+ if (scale.lowValue == null || scale.lowValue > diff) scale.lowValue = diff;
743
+ if (scale.highValue == null || scale.highValue < diff) scale.highValue = diff;
744
+ }
745
+ }
746
+ // hur hantera om factorInfo
747
+ if ((_this.settings.chartlen == '1d' || _this.settings.chartlen == '0d') && !m_zoom.mousedown.timestamp) { // if closeprice is used calch high/low on it
748
+ var cp = parseFloat(_this.instruments[s].closeprice1d) * _this.instruments[s].factor;
749
+ if (scale.lowValue > cp) scale.lowValue = cp;
750
+ else
751
+ if (scale.highValue < cp) scale.highValue = cp;
752
+ }
753
+ if (chartType != 'history') {
754
+ consolle
755
+ if (scale.lowValue > 0) {
756
+ scale.lowValue = 0;
757
+ } else {
758
+ if (scale.highValue < 0) scale.highValue = 0;
759
+ }
760
+ }
761
+ if (typeof factorInfo === 'undefined') {
762
+ if (_this.instruments[s].startValue) { // ta bort? fråga mats
763
+ if (_this.instruments[s].startValue > scale.highValue) scale.highValue = _this.instruments[s].startValue;
764
+ if (_this.instruments[s].startValue < scale.lowValue) scale.lowValue = _this.instruments[s].startValue;
786
765
  }
787
766
  }
788
- value += tickSize;
789
767
  }
790
- plotLower(valuePerPixel, lineLength);
791
- return true;
792
- }
768
+ if (scale.lowValue == null) {
769
+ scale.lowValue = 0;
770
+ scale.highValue = 100;
771
+ } else
772
+ if (scale.lowValue == scale.highValue && scale.lowValue == 0) {
773
+ scale.lowValue -= 1;
774
+ scale.highValue += 1;
775
+ }
776
+ // do we have any analyzis we need to take into account
777
+ if (typeof factorInfo === 'undefined') {
778
+ for (i = 0; i < _this.settings.indicators.length; i++) {
779
+ if (_this.settings.indicators[i].method == 'rsi') continue;
780
+ if (_this.settings.indicators[i].method == 'quantity') continue;
781
+ if (!_this.settings.indicators[i].timeseries || _this.settings.indicators[i].timeseries.length == 0) continue;
782
+ data = _this.settings.indicators[i].timeseries;
783
+ for (s = 0; s < data.length; s++) {
784
+ if (data[s].timestamp < _this.scaleinfoX.startTimeStamp) {
785
+ continue;
786
+ }
787
+ if (data[s].timestamp > _this.scaleinfoX.endTimeStamp) {
788
+ break;
789
+ }
790
+ if (typeof data[s].datapoints !== 'undefined') {
791
+ for (var x = 0; x < data[i].datapoints.length; x++) {
792
+ if (data[s].datapoints[x] < scale.lowValue) {
793
+ scale.lowValue = data[s].datapoints[x];
794
+ } else {
795
+ if (data[s].datapoints[x] > scaleinfoY.highValue) {
796
+ scale.highValue = data[s].datapoints[x];
797
+ }
798
+ }
799
+ }
800
+ }
801
+ }
802
+ }
803
+ }
804
+ m_ctx.font = css.fontWeight + ' ' + css.fontSize + ' ' + css.fontFamily; // set font so measure works
793
805
 
794
- function drawYAxisLower() {
795
- m_ctx.save();
796
- m_ctx.font = m_yLegendCss.fontWeight + ' ' + m_yLegendCss.fontSize + ' ' + m_yLegendCss.fontFamily;
797
- m_ctx.fillStyle = m_yLegendCss.color;
798
- var lineLen = m_chartspaces.lowerChart.bottom - m_chartspaces.lowerChart.top;
799
- var numticks = lineLen / (getFontSize(m_yLegendCss) * 2);
806
+ let v = (scale.highValue - scale.lowValue) / scale.lineLength;
807
+ scale.maxValue = scale.highValue + (v * parseInt(css.fontSize));
808
+ scale.minValue = scale.lowValue - (v * parseInt(css.fontSize));
809
+ scale.lineLength = cs.bottom - cs.top;
810
+
811
+ let numticks = scale.lineLength / (getFontSize(css) * _this.settings.yAxisSpacing);
800
812
  if (numticks > 8) numticks = 8; // limit to 8 items on Y legend ( this is not an absolut count, since we calculate nice legend numbers
813
+ scale.tickSize = getTickValue(scale.lowValue, scale.highValue, numticks);
814
+ scale.decimals = scale.tickSize.countDecimals();
801
815
 
802
- m_ctx.beginPath();
803
- m_ctx.strokeStyle = m_gridVerticalCss.color;
804
- m_ctx.moveTo(m_chartspaces.lowerChart.left + 0.5, m_chartspaces.lowerChart.top);
805
- m_ctx.lineTo(m_chartspaces.lowerChart.left + 0.5, m_chartspaces.lowerChart.bottom);
806
- m_ctx.stroke();
807
- m_ctx.closePath();
808
- var x = m_chartspaces.lowerChart.left - 3;
809
- drawYLegendLower(x, numticks, lineLen, true);
810
- m_ctx.restore();
811
- }
816
+ if (typeof factorInfo === 'undefined') {
817
+ if (scale.decimals > 4) scale.decimals = 4;
818
+ else if (scale.decimals < 2) scale.decimals = 2;
819
+ }
812
820
 
813
- function checkYLegendSpace(y, text) {
814
- //if (y - (getFontSize(m_yLegendCss) / 2) - m_chartspaces.chart.top < 0) return false;
815
- if (y - (getFontSize(m_yLegendCss)) - m_chartspaces.chart.top < 0) return false;
816
- if (y > m_chartspaces.chart.bottom) return false;
821
+ let widestValue = (scale.lowValue < 0 ? '-' : '') + Math.max(Math.abs(scale.highValue), Math.abs(scale.lowValue));
822
+ let label = formatNiceNumber(widestValue, _this.settings.thousandseparator, _this.settings.decimalseparator, scale.decimals);
823
+ if (typeof factorInfo !== 'undefined' && typeof factorInfo.suffix !== 'undefined') label += factorInfo.suffix;
824
+ if (css.float != 'right') {
825
+ cs.left = 10 + Math.round(m_ctx.measureText(label).width); // + 'px'; // räkna fram hur hur mycket plats Y värdena tar och sätt margin till det, skall vi göra så?
826
+ } else {
827
+ if (css.textAlign == 'right') {
828
+ cs.right = m_canvas.getWidth() - (10 + Math.round(m_ctx.measureText(label).width)); // + 'px'; // räkna fram hur hur mycket plats Y värdena tar och sätt margin till det, skall vi göra så?
829
+ }
830
+ }
817
831
  return true;
818
832
  }
819
833
 
820
- function drawY2Legend(x) { // percent
834
+
835
+ function drawY2Legend(scaleinfoY, scaleinfoY2, x) { // percent
821
836
  if (_this.instruments[0].pricetype == 'yield') return;
822
837
  if (_this.settings.absoluteScaling == true) {
823
838
  for (var s = 1; s < _this.instruments.length; s++) {
824
839
  if (_this.instruments[s].insref != 0) return;
825
840
  }
826
841
  }
827
-
828
- _this.scaleinfoY2.maxValue = _this.scaleinfoY2.highValue + (_this.scaleinfoY2.tickSize * 0.2);
829
- _this.scaleinfoY2.minValue = _this.scaleinfoY2.lowValue - (_this.scaleinfoY2.tickSize * 0.2);
842
+ if (_this.instruments[0].startValue == null || (scaleinfoY2.highValue == 100 && scaleinfoY2.lowValue == 0)) return false;
843
+ scaleinfoY2.maxValue = scaleinfoY2.highValue + (scaleinfoY2.tickSize * 0.2);
844
+ scaleinfoY2.minValue = scaleinfoY2.lowValue - (scaleinfoY2.tickSize * 0.2);
830
845
  m_ctx.font = m_y2LegendCss.fontWeight + ' ' + m_y2LegendCss.fontSize + ' ' + m_y2LegendCss.fontFamily;
831
846
  var value;
832
- if (_this.scaleinfoY2.highValue == _this.scaleinfoY2.lowValue) {
833
- _this.scaleinfoY2.maxValue += _this.scaleinfoY2.tickSize;
834
- _this.scaleinfoY2.minValue -= _this.scaleinfoY2.tickSize;
835
- value = Math.abs(_this.scaleinfoY2.minValue);
847
+ if (scaleinfoY2.highValue == scaleinfoY2.lowValue) {
848
+ scaleinfoY2.maxValue += scaleinfoY2.tickSize;
849
+ scaleinfoY2.minValue -= scaleinfoY2.tickSize;
850
+ value = Math.abs(scaleinfoY2.minValue);
836
851
  } else {
837
- if (_this.scaleinfoY2.minValue > 0)
838
- value = _this.scaleinfoY2.minValue - fmod(Math.abs(_this.scaleinfoY2.minValue), _this.scaleinfoY2.tickSize) + _this.scaleinfoY2.tickSize;
852
+ if (scaleinfoY2.minValue > 0)
853
+ value = scaleinfoY2.minValue - fmod(Math.abs(scaleinfoY2.minValue), scaleinfoY2.tickSize) + scaleinfoY2.tickSize;
839
854
  else {
840
- value = _this.scaleinfoY2.minValue + fmod(Math.abs(_this.scaleinfoY2.minValue), _this.scaleinfoY2.tickSize) - _this.scaleinfoY2.tickSize;
855
+ value = scaleinfoY2.minValue + fmod(Math.abs(scaleinfoY2.minValue), scaleinfoY2.tickSize) - scaleinfoY2.tickSize;
841
856
  }
842
857
  }
843
858
  var startPrice = _this.instruments[0].startValue;
@@ -854,10 +869,9 @@ function Milli_Chart(settings) {
854
869
  v = startPrice - (startPrice * (value / 100));
855
870
  else
856
871
  v = startPrice + (startPrice * (value / 100));
857
- var y = Math.round(m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) - ((v - _this.scaleinfoY.minValue) * _this.scaleinfoY.valuePerPixel));
872
+ var y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - ((v - scaleinfoY.minValue) / scaleinfoY.valuePerPixel));
858
873
 
859
874
  if (y <= m_chartspaces.chart.top) break;
860
- //if (y > m_chartspaces.chart.bottom) break;
861
875
  if (y <= m_chartspaces.chart.bottom) {
862
876
  if (_this.settings.drawy2axis == true) { // draw legenditem markers for diff
863
877
  m_ctx.beginPath();
@@ -872,7 +886,7 @@ function Milli_Chart(settings) {
872
886
  m_ctx.stroke();
873
887
  m_ctx.closePath();
874
888
  }
875
- var label = formatNiceNumber(value, _this.settings.thousandseparator, _this.settings.decimalseparator, _this.scaleinfoY2.decimals, true) + '%';
889
+ var label = formatNiceNumber(value, _this.settings.thousandseparator, _this.settings.decimalseparator, scaleinfoY2.decimals, true) + '%';
876
890
  if (m_y2LegendCss.verticalAlign == 'top') {
877
891
  if (checkYLegendSpace(y, label))
878
892
  m_ctx.fillText(label, textpos, y - ((getFontSize(m_y2LegendCss) + 2)));
@@ -882,21 +896,18 @@ function Milli_Chart(settings) {
882
896
  }
883
897
  }
884
898
  }
885
- value += _this.scaleinfoY2.tickSize;
899
+ value += scaleinfoY2.tickSize;
886
900
  }
887
901
  return true;
888
902
  }
889
903
 
890
904
  function drawYLegend(si, x, gridHorizontalLines, number, draw) {
891
905
  var value;
892
- //si.maxValue = si.highValue + (si.tickSize * 0.2);
893
- //si.minValue = si.lowValue - (si.tickSize * 0.2);
894
-
906
+ // add one fontsize to max and min values
895
907
  let v = (si.highValue - si.lowValue) / si.lineLength;
896
908
  si.maxValue = si.highValue + (v * parseInt(m_yLegendCss.fontSize));
897
909
  si.minValue = si.lowValue - (v * parseInt(m_yLegendCss.fontSize));
898
910
 
899
-
900
911
  m_ctx.font = m_yLegendCss.fontWeight + ' ' + m_yLegendCss.fontSize + ' ' + m_yLegendCss.fontFamily;
901
912
  if (si.highValue == si.lowValue) { // only have one value so set values for 1 line only
902
913
  si.maxValue = si.maxValue + si.tickSize;
@@ -908,22 +919,19 @@ function Milli_Chart(settings) {
908
919
  else
909
920
  value = si.minValue + fmod(Math.abs(si.minValue), si.tickSize) - si.tickSize;
910
921
  }
911
- si.valuePerPixel = si.lineLength / (si.maxValue - si.minValue);
922
+ si.valuePerPixel = (si.maxValue - si.minValue) / si.lineLength;
923
+
912
924
  if (isNaN(si.valuePerPixel) || !isFinite(si.valuePerPixel)) {
913
925
  console.log('cant draw valuePerPixel', si.valuePerPixel, si.lineLength, si.maxValue, si.minValue);
914
926
  return false;
915
927
  }
916
928
  var textpos;
917
- var count = 0;
918
- for (;;) {
919
- if (count++ > 10) {
920
- console.log('failsafe break');
921
- break;
922
- }
923
- var y = Math.round(m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) - ((value - si.minValue) * si.valuePerPixel));
929
+ for (let c = 0; c < 11; c++) { // maximum of 10 legenditems
930
+
931
+ var y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - ((value - si.minValue) / si.valuePerPixel));
924
932
  if (y <= m_chartspaces.chart.top) break;
925
933
  if (y <= m_chartspaces.chart.bottom) {
926
- if (gridHorizontalLines == true) {
934
+ if (gridHorizontalLines == true) { // draw horizontal lines
927
935
  m_ctx.save();
928
936
  if (_this.settings.gridHorizontalLinesStyle == 'dash') {
929
937
  m_ctx.setLineDash([3, 3]);
@@ -939,7 +947,6 @@ function Milli_Chart(settings) {
939
947
  } else {
940
948
  textpos = x + 4;
941
949
  }
942
-
943
950
  } else
944
951
  if (_this.settings.drawyaxis == true && number == 1 && draw) { // draw legenditem markers for price
945
952
  if (!draw) return;
@@ -972,48 +979,220 @@ function Milli_Chart(settings) {
972
979
  return true;
973
980
  }
974
981
 
975
- function drawYAxis() {
982
+ function calculateIndicatorScale(data, cs, minvalue) {
983
+ let sc = {};
984
+ sc.low = null;
985
+ sc.high = null;
986
+ for (let i = 0; i < data.length; i++) {
987
+ if (data[i].timestamp < _this.scaleinfoX.startTimeStamp) {
988
+ continue;
989
+ }
990
+ if (data[i].timestamp > _this.scaleinfoX.endTimeStamp) {
991
+ break; // continue?
992
+ }
993
+
994
+ if (typeof data[i].datapoints[0] !== 'undefined') {
995
+ if (sc.low == null || sc.low > data[i].datapoints[[0]]) sc.low = data[i].datapoints[0];
996
+ if (sc.high == null || sc.high < data[i].datapoints[[0]]) sc.high = data[i].datapoints[0];
997
+ }
998
+ }
999
+ if (sc.low == null || sc.high == null) return null;
1000
+ let v = (sc.high - sc.low) / (cs.bottom - cs.top);
1001
+ sc.maxValue = sc.high == 0 ? 100 : sc.high + (v * parseInt(m_yLegendCss.fontSize));
1002
+ sc.minValue = typeof minvalue == 'undefined' ? sc.lowValue - (v * parseInt(m_yLegendCss.fontSize)) : minvalue;
1003
+
1004
+ sc.maxValue = sc.high == 0 ? 100 : sc.high + (fabs(sc.high) * 0.2);
1005
+ sc.minValue = typeof minvalue === 'undefined' ? sc.low - (fabs(sc.low) * 0.2) : minvalue;
1006
+ sc.valuePerPixel = (sc.maxValue - sc.minValue) / (cs.bottom - cs.top);
1007
+ return sc;
1008
+ }
1009
+
1010
+ function drawYLegendNew(scale, cs, x, css, drawAxis, gridLines, factorInfo) {
1011
+ let value;
1012
+ // add one fontsize to max and min values
1013
+ let v = (scale.highValue - scale.lowValue) / scale.lineLength;
1014
+ scale.maxValue = scale.highValue + (v * parseInt(css.fontSize));
1015
+ scale.minValue = scale.lowValue - (v * parseInt(css.fontSize));
1016
+ if (scale.highValue == scale.lowValue) { // only have one value so set values for 1 line only
1017
+ scale.maxValue = scale.maxValue + scale.tickSize;
1018
+ scale.minValue = scale.minValue - scale.tickSize;
1019
+ value = Math.abs(scale.lowValue);
1020
+ } else {
1021
+ if (scale.minValue > 0)
1022
+ value = scale.minValue - fmod(Math.abs(scale.minValue), scale.tickSize) + scale.tickSize;
1023
+ else
1024
+ value = scale.minValue + fmod(Math.abs(scale.minValue), scale.tickSize) - scale.tickSize;
1025
+ }
1026
+ scale.valuePerPixel = (scale.maxValue - scale.minValue) / scale.lineLength;
1027
+ m_ctx.font = css.fontWeight + ' ' + css.fontSize + ' ' + css.fontFamily;
1028
+
1029
+ if (isNaN(scale.valuePerPixel) || !isFinite(scale.valuePerPixel)) {
1030
+ console.log('cant draw valuePerPixel', scale.valuePerPixel, scale.lineLength, scale.maxValue, scale.minValue);
1031
+ return false;
1032
+ }
1033
+ var textpos;
1034
+ for (let c = 0; c < 11; c++) { // maximum of 10 legenditems
1035
+ var y = Math.round(cs.height - cs.marginBottom - ((value - scale.minValue) / scale.valuePerPixel));
1036
+ if (y <= cs.top) break;
1037
+ if (y <= cs.bottom) {
1038
+ textpos = x;
1039
+ if (drawAxis == true && gridLines) { // draw horizontal lines
1040
+ m_ctx.save();
1041
+ if (_this.settings.gridHorizontalLinesStyle == 'dash') {
1042
+ m_ctx.setLineDash([3, 3]);
1043
+ }
1044
+ m_ctx.beginPath();
1045
+ m_ctx.moveTo(cs.left, y + 0.5);
1046
+ m_ctx.lineTo(cs.right, y + 0.5);
1047
+ m_ctx.stroke();
1048
+ m_ctx.closePath();
1049
+ m_ctx.restore();
1050
+ if ((css.float == 'left' && css.textAlign == 'left') || (css.float == 'right' && css.textAlign == 'left')) {
1051
+ textpos = x - 4;
1052
+ } else {
1053
+ textpos = x + 4;
1054
+ }
1055
+ } else
1056
+ if (drawAxis == true && gridLines) { // draw legenditem markers for price
1057
+ m_ctx.beginPath();
1058
+ m_ctx.moveTo(cs.left, y + 0.5);
1059
+ if ((css.float == 'left' && css.textAlign == 'left') || (css.float == 'right' && css.textAlign == 'left')) {
1060
+ m_ctx.lineTo(x - 3, y + 0.5);
1061
+ textpos = x - 4;
1062
+ } else {
1063
+ m_ctx.lineTo(x + 3, y + 0.5);
1064
+ textpos = x + 4;
1065
+ }
1066
+ m_ctx.stroke();
1067
+ m_ctx.closePath();
1068
+ }
1069
+ if (drawAxis == true) {
1070
+ var label = formatNiceNumber(value, _this.settings.thousandseparator, _this.settings.decimalseparator, scale.decimals, false);
1071
+ if (typeof factorInfo !== 'undefined' && typeof factorInfo.suffix !== 'undefined') label += factorInfo.suffix;
1072
+ if (css.verticalAlign == 'top') {
1073
+ if (checkYLegendSpace(y, label))
1074
+ m_ctx.fillText(label, textpos, y - ((getFontSize(css) + 2)));
1075
+ } else {
1076
+ if (checkYLegendSpace(y, label))
1077
+ m_ctx.fillText(label, textpos, y - (getFontSize(css) / 2));
1078
+ }
1079
+ }
1080
+ }
1081
+ value += scale.tickSize;
1082
+ }
1083
+ return true;
1084
+ }
1085
+
1086
+ function drawYAxisNew(scale, cs, css, drawAxis, gridLines, factor) {
1087
+ /*let scale = {};
1088
+ if (false == calcHighLow2(scale, factor)) {
1089
+ console.log('fail highlow');
1090
+ return;
1091
+ }*/
1092
+ m_ctx.save();
1093
+ m_ctx.strokeStyle = m_gridHorizontalCss.color;
1094
+ m_ctx.font = css.fontWeight + ' ' + css.fontSize + ' ' + css.fontFamily;
1095
+ m_ctx.fillStyle = css.color;
1096
+ scale.lineLength = cs.bottom - cs.top;
1097
+ /* let numticks = scale.lineLength / (getFontSize(css) * _this.settings.yAxisSpacing);
1098
+ if (numticks > 8) numticks = 8; // limit to 8 items on Y legend ( this is not an absolut count, since we calculate nice legend numbers
1099
+ scale.tickSize = getTickValue(scale.lowValue, scale.highValue, numticks);
1100
+ scale.decimals = scale.tickSize.countDecimals();*/
1101
+ //scale.decimals > 4 ? 4 : (scale.decimals < 2 ? 2 : scale.decimals);
1102
+
1103
+
1104
+
1105
+
1106
+ /* let widestValue = (scale.lowValue < 0 ? '-' : '') + Math.max(Math.abs(scale.highValue), Math.abs(scale.lowValue));
1107
+ let label = formatNiceNumber(widestValue, _this.settings.thousandseparator, _this.settings.decimalseparator, scale.decimals);
1108
+ console.log('testw', label, )
1109
+ if (drawAxis) {
1110
+ if (css.float != 'right') {
1111
+ cs.left = 10 + Math.round(m_ctx.measureText(label).width); // + 'px'; // räkna fram hur hur mycket plats Y värdena tar och sätt margin till det, skall vi göra så?
1112
+ } else {
1113
+ if (css.textAlign == 'right') {
1114
+ cs.right = m_canvas.getWidth() - (10 + Math.round(m_ctx.measureText(label).width)); // + 'px'; // räkna fram hur hur mycket plats Y värdena tar och sätt margin till det, skall vi göra så?
1115
+ }
1116
+ }
1117
+ }
1118
+ */
1119
+
1120
+
1121
+
1122
+
1123
+ if (css.float != 'right' && drawAxis) { // do we need to add space for half a date? ie prices are to tiny so date will be trunkated
1124
+ m_ctx.save();
1125
+ m_ctx.font = m_xLegendCss.fontWeight + ' ' + m_xLegendCss.fontSize + ' ' + m_xLegendCss.fontFamily;
1126
+ if (getStringWidth(m_ctx, _this.settings.dateformat) / 2 + 5 > cs.left) {
1127
+ cs.left = getStringWidth(m_ctx, _this.settings.dateformat) / 2 + 5;
1128
+ }
1129
+ m_ctx.restore();
1130
+ }
1131
+ // draw line
1132
+ m_ctx.strokeStyle = m_gridVerticalCss.color;
1133
+ m_ctx.beginPath();
1134
+ m_ctx.moveTo(cs.left + 0.5, cs.top);
1135
+ m_ctx.lineTo(cs.left + 0.5, cs.height - cs.marginBottom);
1136
+ m_ctx.stroke();
1137
+ m_ctx.closePath();
1138
+ m_ctx.strokeStyle = m_gridHorizontalCss.color;
1139
+ var x;
1140
+ if (css.float == 'right')
1141
+ x = cs.right;
1142
+ else
1143
+ x = cs.left;
1144
+ if (css.textAlign == 'right') {
1145
+ m_ctx.textAlign = 'left';
1146
+ drawYLegendNew(scale, cs, x, css, drawAxis, gridLines, factor);
1147
+ } else {
1148
+ m_ctx.textAlign = 'right';
1149
+ drawYLegendNew(scale, cs, x, css, drawAxis, gridLines), factor;
1150
+ }
1151
+ m_ctx.restore();
1152
+ return scale;
1153
+ }
1154
+
1155
+ function drawYAxis(scaleinfoY, scaleinfoY2) {
976
1156
  m_ctx.save();
977
1157
  m_ctx.strokeStyle = m_gridHorizontalCss.color;
978
1158
  m_ctx.font = m_yLegendCss.fontWeight + ' ' + m_yLegendCss.fontSize + ' ' + m_yLegendCss.fontFamily;
979
1159
  m_ctx.fillStyle = m_yLegendCss.color;
980
- if (0 == calcHighLow()) {
1160
+ if (0 == calcHighLow(scaleinfoY, scaleinfoY2)) {
981
1161
  m_ctx.restore();
982
1162
  console.log('fail highlow');
983
1163
  return;
984
1164
  }
985
- _this.scaleinfoY.lineLength = m_chartspaces.chart.bottom - m_chartspaces.chart.top;
986
- _this.scaleinfoY2.lineLength = _this.scaleinfoY.lineLength;
987
- //var numticks = (_this.scaleinfoY.lineLength / window.devicePixelRatio) / (getFontSize(m_yLegendCss) * _this.settings.yAxisSpacing);
988
- var numticks = _this.scaleinfoY.lineLength / (getFontSize(m_yLegendCss) * _this.settings.yAxisSpacing);
1165
+
1166
+ scaleinfoY.lineLength = m_chartspaces.chart.bottom - m_chartspaces.chart.top;
1167
+ scaleinfoY2.lineLength = scaleinfoY.lineLength;
1168
+ var numticks = scaleinfoY.lineLength / (getFontSize(m_yLegendCss) * _this.settings.yAxisSpacing);
989
1169
  if (numticks > 8) numticks = 8; // limit to 8 items on Y legend ( this is not an absolut count, since we calculate nice legend numbers
990
- _this.scaleinfoY.tickSize = getTickValue(_this.scaleinfoY.lowValue, _this.scaleinfoY.highValue, numticks);
991
- _this.scaleinfoY.decimals = _this.scaleinfoY.tickSize.countDecimals(); // räkna på diffen mellan high low kanske?
992
- _this.scaleinfoY.decimals = _this.scaleinfoY.decimals > 4 ? 4 : _this.scaleinfoY.decimals;
993
- _this.scaleinfoY.decimals = _this.scaleinfoY.decimals < 2 ? 2 : _this.scaleinfoY.decimals;
994
- var label = formatNiceNumber(_this.scaleinfoY.highValue, _this.settings.thousandseparator, _this.settings.decimalseparator, _this.scaleinfoY.decimals);
1170
+ scaleinfoY.tickSize = getTickValue(scaleinfoY.lowValue, scaleinfoY.highValue, numticks);
1171
+ scaleinfoY.decimals = scaleinfoY.tickSize.countDecimals(); // räkna på diffen mellan high low kanske?
1172
+ scaleinfoY.decimals = scaleinfoY.decimals > 4 ? 4 : scaleinfoY.decimals;
1173
+ scaleinfoY.decimals = scaleinfoY.decimals < 2 ? 2 : scaleinfoY.decimals;
1174
+ var label = formatNiceNumber(scaleinfoY.highValue, _this.settings.thousandseparator, _this.settings.decimalseparator, scaleinfoY.decimals);
995
1175
  if (_this.settings.drawyaxis) {
996
1176
  if (m_yLegendCss.float != 'right') {
997
1177
  m_chartspaces.chart.left = 10 + Math.round(m_ctx.measureText(label).width); // + 'px'; // räkna fram hur hur mycket plats Y värdena tar och sätt margin till det, skall vi göra så?
998
1178
  m_chartspaces.lowerChart.left = m_chartspaces.chart.left;
999
1179
  } else {
1000
1180
  if (m_yLegendCss.textAlign == 'right') {
1001
- m_chartspaces.chart.right = m_canvas.width - (10 + Math.round(m_ctx.measureText(label).width)); // + 'px'; // räkna fram hur hur mycket plats Y värdena tar och sätt margin till det, skall vi göra så?
1181
+ m_chartspaces.chart.right = m_canvas.getWidth() - (10 + Math.round(m_ctx.measureText(label).width)); // + 'px'; // räkna fram hur hur mycket plats Y värdena tar och sätt margin till det, skall vi göra så?
1002
1182
  m_chartspaces.lowerChart.right = m_chartspaces.chart.right;
1003
1183
  }
1004
1184
  }
1005
1185
  }
1006
1186
  if (_this.settings.drawy2axis) { // calc space for y2
1007
- //var numticks = _this.scaleinfoY2.lineLength / (getFontSize(m_yLegendCss) * _this.settings.yAxisSpacing);
1008
- _this.scaleinfoY2.tickSize = getTickValue(_this.scaleinfoY2.lowValue, _this.scaleinfoY2.highValue, numticks);
1187
+ scaleinfoY2.tickSize = getTickValue(scaleinfoY2.lowValue, scaleinfoY2.highValue, numticks);
1009
1188
 
1010
- _this.scaleinfoY2.decimals = _this.scaleinfoY2.tickSize.countDecimals(); // räkna på diffen mellan high low kanske?
1011
- _this.scaleinfoY2.decimals = _this.scaleinfoY2.decimals > 4 ? 4 : _this.scaleinfoY2.decimals;
1012
- _this.scaleinfoY2.decimals = _this.scaleinfoY2.decimals < 0 ? 0 : _this.scaleinfoY2.decimals;
1189
+ scaleinfoY2.decimals = scaleinfoY2.tickSize.countDecimals(); // räkna på diffen mellan high low kanske?
1190
+ scaleinfoY2.decimals = scaleinfoY2.decimals > 4 ? 4 : scaleinfoY2.decimals;
1191
+ scaleinfoY2.decimals = scaleinfoY2.decimals < 0 ? 0 : scaleinfoY2.decimals;
1013
1192
 
1014
- var widestDiff = '-' + Math.max(Math.abs(_this.scaleinfoY2.highValue), Math.abs(_this.scaleinfoY2.lowValue));
1015
- if (_this.settings.priceIndicator == false) label = formatNiceNumber(widestDiff, _this.settings.thousandseparator, _this.settings.decimalseparator, _this.scaleinfoY2.decimals) + ' %';
1016
- else label = formatNiceNumber(widestDiff, _this.settings.thousandseparator, _this.settings.decimalseparator, _this.scaleinfoY2.decimals < 2 ? 2 : _this.scaleinfoY2.decimals) + ' %'; // priceIndicator has 2 decimals
1193
+ var widestDiff = '-' + Math.max(Math.abs(scaleinfoY2.highValue), Math.abs(scaleinfoY2.lowValue));
1194
+ if (_this.settings.priceIndicator == false) label = formatNiceNumber(widestDiff, _this.settings.thousandseparator, _this.settings.decimalseparator, scaleinfoY2.decimals) + ' %';
1195
+ else label = formatNiceNumber(widestDiff, _this.settings.thousandseparator, _this.settings.decimalseparator, scaleinfoY2.decimals < 2 ? 2 : scaleinfoY2.decimals) + ' %'; // priceIndicator has 2 decimals
1017
1196
  if (m_y2LegendCss.float != 'right') {
1018
1197
  if (m_y2LegendCss.textAlign == 'left')
1019
1198
  m_chartspaces.chart.left = 10 + Math.round(m_ctx.measureText(label).width); // + 'px'; // räkna fram hur hur mycket plats Y värdena tar och sätt margin till det, skall vi göra så?
@@ -1021,7 +1200,8 @@ function Milli_Chart(settings) {
1021
1200
  } else {
1022
1201
  // kolla setting om den skall vara "i diagrammet"
1023
1202
  if (m_y2LegendCss.textAlign == 'right')
1024
- m_chartspaces.chart.right = m_canvas.width - (10 + Math.round(m_ctx.measureText(label).width)); // + 'px'; // räkna fram hur hur mycket plats Y värdena tar och sätt margin till det, skall vi göra så?
1203
+ m_chartspaces.chart.right = m_canvas.getWidth() - (10 + Math.round(m_ctx.measureText(label).width)); // + 'px'; // räkna fram hur hur mycket plats Y värdena tar och sätt margin till det, skall vi göra så?
1204
+ //m_chartspaces.chart.right = (m_canvas.getWidth() / window.devicePixelRatio) - (10 + Math.round(m_ctx.measureText(label).width)); // + 'px'; // räkna fram hur hur mycket plats Y värdena tar och sätt margin till det, skall vi göra så?
1025
1205
  m_chartspaces.lowerChart.right = m_chartspaces.chart.right;
1026
1206
  }
1027
1207
  }
@@ -1034,7 +1214,7 @@ function Milli_Chart(settings) {
1034
1214
  m_ctx.strokeStyle = m_gridVerticalCss.color;
1035
1215
  m_ctx.beginPath();
1036
1216
  m_ctx.moveTo(m_chartspaces.chart.left + 0.5, m_chartspaces.chart.top);
1037
- m_ctx.lineTo(m_chartspaces.chart.left + 0.5, m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom));
1217
+ m_ctx.lineTo(m_chartspaces.chart.left + 0.5, m_chartspaces.chart.height - m_chartspaces.chart.marginBottom);
1038
1218
  m_ctx.stroke();
1039
1219
  m_ctx.closePath();
1040
1220
  m_ctx.strokeStyle = m_gridHorizontalCss.color;
@@ -1045,10 +1225,10 @@ function Milli_Chart(settings) {
1045
1225
  x = m_chartspaces.chart.left;
1046
1226
  if (m_yLegendCss.textAlign == 'right') {
1047
1227
  m_ctx.textAlign = 'left';
1048
- drawYLegend(_this.scaleinfoY, x, _this.settings.gridHorizontalLines, 1, _this.settings.drawyaxis);
1228
+ drawYLegend(scaleinfoY, x, _this.settings.gridHorizontalLines, 1, _this.settings.drawyaxis);
1049
1229
  } else {
1050
1230
  m_ctx.textAlign = 'right';
1051
- drawYLegend(_this.scaleinfoY, x, _this.settings.gridHorizontalLines, 1, _this.settings.drawyaxis);
1231
+ drawYLegend(scaleinfoY, x, _this.settings.gridHorizontalLines, 1, _this.settings.drawyaxis);
1052
1232
  }
1053
1233
  if (_this.settings.drawy2axis) {
1054
1234
  if (m_y2LegendCss.float == 'right') {
@@ -1058,11 +1238,11 @@ function Milli_Chart(settings) {
1058
1238
  }
1059
1239
  if (m_y2LegendCss.textAlign == 'right') {
1060
1240
  m_ctx.textAlign = 'left';
1061
- drawY2Legend(x);
1241
+ drawY2Legend(scaleinfoY, scaleinfoY2, x);
1062
1242
 
1063
1243
  } else {
1064
1244
  m_ctx.textAlign = 'right';
1065
- drawY2Legend(x);
1245
+ drawY2Legend(scaleinfoY, scaleinfoY2, x);
1066
1246
  }
1067
1247
  }
1068
1248
  m_ctx.restore();
@@ -1163,8 +1343,8 @@ function Milli_Chart(settings) {
1163
1343
  calcXScale(starttime, endtime);
1164
1344
  if (_this.settings.drawxaxis != 0) {
1165
1345
  m_ctx.beginPath();
1166
- m_ctx.moveTo(m_chartspaces.chart.left, m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) + 0.5);
1167
- m_ctx.lineTo(m_chartspaces.chart.right, m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) + 0.5);
1346
+ m_ctx.moveTo(m_chartspaces.chart.left, m_chartspaces.chart.height - m_chartspaces.chart.marginBottom + 0.5);
1347
+ m_ctx.lineTo(m_chartspaces.chart.right, m_chartspaces.chart.height - m_chartspaces.chart.marginBottom + 0.5);
1168
1348
  m_ctx.stroke();
1169
1349
  m_ctx.closePath();
1170
1350
  }
@@ -1183,7 +1363,7 @@ function Milli_Chart(settings) {
1183
1363
  }
1184
1364
  }
1185
1365
  if (draw) {
1186
- drawXAxisGridlines({ 'x': x, y: m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) });
1366
+ drawXAxisGridlines({ 'x': x, y: m_chartspaces.chart.height - m_chartspaces.chart.marginBottom });
1187
1367
  text = year;
1188
1368
  if (checkXLegendSides(x, text)) {
1189
1369
  if (_this.settings.yearLabelsPos == 'top') {
@@ -1196,7 +1376,7 @@ function Milli_Chart(settings) {
1196
1376
  m_ctx.fillText(text, 0, 0);
1197
1377
  m_ctx.restore();
1198
1378
  } else {
1199
- m_ctx.fillText(text, x - (m_ctx.measureText(text).width / 2), m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) + getScaledSetting(m_xLegendCss.paddingTop));
1379
+ m_ctx.fillText(text, x - (m_ctx.measureText(text).width / 2), m_chartspaces.chart.height - m_chartspaces.chart.marginBottom + getScaledSetting(m_xLegendCss.paddingTop));
1200
1380
  }
1201
1381
  }
1202
1382
  numItems++;
@@ -1219,10 +1399,10 @@ function Milli_Chart(settings) {
1219
1399
  }
1220
1400
  }
1221
1401
  if (draw) {
1222
- drawXAxisGridlines({ 'x': x, y: m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) });
1402
+ drawXAxisGridlines({ 'x': x, y: m_chartspaces.chart.height - m_chartspaces.chart.marginBottom });
1223
1403
  text = formatDate(year.getFullYear() + '-07-01', _this.settings.dateformat, _this);
1224
1404
  if (checkXLegendSides(x, text)) {
1225
- m_ctx.fillText(text, x - (m_ctx.measureText(text).width / 2), m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) + getScaledSetting(m_xLegendCss.paddingTop));
1405
+ m_ctx.fillText(text, x - (m_ctx.measureText(text).width / 2), m_chartspaces.chart.height - m_chartspaces.chart.marginBottom + getScaledSetting(m_xLegendCss.paddingTop));
1226
1406
  }
1227
1407
  numItems++;
1228
1408
  legendItems.push({ 'text': text, timestamp: new Date(year + '-07-01T00:00:00Z'), 'x': x });
@@ -1248,10 +1428,10 @@ function Milli_Chart(settings) {
1248
1428
  }
1249
1429
  if (dontPrint) break;
1250
1430
  if (draw) {
1251
- drawXAxisGridlines({ 'x': x, y: m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) });
1252
- text = formatDate(year.getFullYear() + '-' + (year.getMonth() + 1) + '-01', _this.settings.dateformat, _this);
1431
+ drawXAxisGridlines({ 'x': x, y: m_chartspaces.chart.height - m_chartspaces.chart.marginBottom });
1432
+ text = formatDate(year.getFullYear() + '-' + ('0' + (year.getMonth() + 1)).slice(-2) + '-01', _this.settings.dateformat, _this);
1253
1433
  if (checkXLegendSides(x, text)) {
1254
- m_ctx.fillText(text, x - (m_ctx.measureText(text).width / 2), m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) + getScaledSetting(m_xLegendCss.paddingTop));
1434
+ m_ctx.fillText(text, x - (m_ctx.measureText(text).width / 2), m_chartspaces.chart.height - m_chartspaces.chart.marginBottom + getScaledSetting(m_xLegendCss.paddingTop));
1255
1435
  }
1256
1436
  }
1257
1437
  legendItems.push({ 'text': text, timestamp: new Date(year + '-04-01T00:00:00Z'), 'x': x });
@@ -1271,10 +1451,10 @@ function Milli_Chart(settings) {
1271
1451
  }
1272
1452
  }
1273
1453
  if (draw) {
1274
- drawXAxisGridlines({ 'x': x, y: m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) });
1275
- text = formatDate(year.getFullYear() + '-' + (year.getMonth() + 1) + '-01', _this.settings.dateformat, _this);
1454
+ drawXAxisGridlines({ 'x': x, y: m_chartspaces.chart.height - m_chartspaces.chart.marginBottom });
1455
+ text = formatDate(year.getFullYear() + '-' + ('0' + (year.getMonth() + 1)).slice(-2) + '-01', _this.settings.dateformat, _this);
1276
1456
  if (checkXLegendSides(x, text)) {
1277
- m_ctx.fillText(text, x - (m_ctx.measureText(text).width / 2), m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) + getScaledSetting(m_xLegendCss.paddingTop));
1457
+ m_ctx.fillText(text, x - (m_ctx.measureText(text).width / 2), m_chartspaces.chart.height - m_chartspaces.chart.marginBottom + getScaledSetting(m_xLegendCss.paddingTop));
1278
1458
  }
1279
1459
  }
1280
1460
  legendItems.push({ 'text': text, timestamp: new Date(year + '-10-01T00:00:00Z'), 'x': x });
@@ -1296,8 +1476,8 @@ function Milli_Chart(settings) {
1296
1476
  calcXScale(starttime, endtime);
1297
1477
  if (_this.settings.drawxaxis != 0) { // draw line
1298
1478
  m_ctx.beginPath();
1299
- m_ctx.moveTo(m_chartspaces.chart.left, m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) + 0.5);
1300
- m_ctx.lineTo(m_chartspaces.chart.right, m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) + 0.5);
1479
+ m_ctx.moveTo(m_chartspaces.chart.left, m_chartspaces.chart.height - m_chartspaces.chart.marginBottom + 0.5);
1480
+ m_ctx.lineTo(m_chartspaces.chart.right, m_chartspaces.chart.height - m_chartspaces.chart.marginBottom + 0.5);
1301
1481
  m_ctx.stroke();
1302
1482
  m_ctx.closePath();
1303
1483
  }
@@ -1351,7 +1531,7 @@ function Milli_Chart(settings) {
1351
1531
  _this.scaleinfoX.itemwidth = getStringWidth(m_ctx, '88:88') * 2; // kolla rätt format en 8a för varje tecken
1352
1532
  var maxLegendItems = Math.floor(_this.scaleinfoX.lineLength / (_this.scaleinfoX.itemwidth * 2));
1353
1533
 
1354
- if (_this.scaleinfoY.type != 'history') {
1534
+ if (chartType != 'history') {
1355
1535
  var arr = [];
1356
1536
  var tmptime = starttime;
1357
1537
  var i = 0;
@@ -1523,7 +1703,7 @@ function Milli_Chart(settings) {
1523
1703
  m_ctx.fillText(date, middleX - (m_ctx.measureText(date).width / 2), m_chartspaces.chart.bottom + getScaledSetting(m_xLegendCss.paddingTop));
1524
1704
  legendItems.push({ x: middleX, type: 0, text: date, width: m_ctx.measureText(date) });
1525
1705
  } else if (_this.settings.intradayDatePos.y == 'bottom' && _this.settings.chartlen != '0d' && _this.settings.chartlen != '1d') {
1526
- m_ctx.fillText(date, middleX - (m_ctx.measureText(date).width / 2), m_chartspaces.chart.bottom + getScaledSetting(m_xLegendCss.paddingTop) + parseInt(m_xLegendCss.fontSize) * window.devicePixelRatio);
1706
+ m_ctx.fillText(date, middleX - (m_ctx.measureText(date).width / 2), m_chartspaces.chart.bottom + getScaledSetting(m_xLegendCss.paddingTop) + parseInt(m_xLegendCss.fontSize) * 1);
1527
1707
  m_ctx.fillText(text, x - (m_ctx.measureText(text).width / 2), m_chartspaces.chart.bottom + getScaledSetting(m_xLegendCss.paddingTop));
1528
1708
  legendItems.push({ x: x, type: 0, text: text });
1529
1709
  } else {
@@ -1533,7 +1713,7 @@ function Milli_Chart(settings) {
1533
1713
  if (_this.settings.intradayDatePos.orientation == 'vertical' && _this.settings.intradayDatePos.x != 'center') {
1534
1714
  m_ctx.save();
1535
1715
  var fontMetrix = m_ctx.measureText(date);
1536
- var dx = (x + fontMetrix.actualBoundingBoxAscent + fontMetrix.actualBoundingBoxDescent + 3) / window.devicePixelRatio;
1716
+ var dx = (x + fontMetrix.actualBoundingBoxAscent + fontMetrix.actualBoundingBoxDescent + 3) / 1;
1537
1717
  var dy = m_chartCss.marginTop;
1538
1718
  m_ctx.translate(dx, dy);
1539
1719
  m_ctx.rotate(90 * Math.PI / 180);
@@ -1711,6 +1891,7 @@ function Milli_Chart(settings) {
1711
1891
  var label = formatChartTime(currentDate.toTimeString().substring(0, 8), _this.settings.timeformat);
1712
1892
  x = Math.round(x);
1713
1893
  m_ctx.fillText(label, x - (m_ctx.measureText(label).width / 2), m_chartspaces.chart.bottom + getScaledSetting(m_xLegendCss.paddingTop));
1894
+ //m_ctx.fillText(label, 125, m_chartspaces.chart.bottom + getScaledSetting(m_xLegendCss.paddingTop));
1714
1895
  legendItems.push({ x: x, type: 0, text: label });
1715
1896
  }
1716
1897
  drawXAxisGridlines({ 'x': x, y: m_chartspaces.chart.bottom }, false);
@@ -1729,9 +1910,9 @@ function Milli_Chart(settings) {
1729
1910
  }
1730
1911
 
1731
1912
  function drawClosePriceIndicator() {
1732
- if (_this.settings.closePriceIndicator == false || _this.scaleinfoY.type == 'history') return;
1913
+ if (_this.settings.closePriceIndicator == false || chartType == 'history') return;
1733
1914
  if (isNaN(_this.instruments[0].startValue) || !_this.instruments[0].startValue) return;
1734
- var y = Math.round(m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) - ((_this.instruments[0].startValue - _this.scaleinfoY.minValue) * _this.scaleinfoY.valuePerPixel)) + 0.5;
1915
+ var y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - ((_this.instruments[0].startValue - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
1735
1916
  m_ctx.save();
1736
1917
  m_ctx.strokeStyle = _this.settings.closePriceIndicator;
1737
1918
  m_ctx.setLineDash([3, 3]);
@@ -1767,11 +1948,12 @@ function Milli_Chart(settings) {
1767
1948
  oldValue = MillistreamWidgetApi_getElementNumber(_this, m_priceIndicator);
1768
1949
  } else {
1769
1950
  m_priceIndicator = document.createElement('div');
1951
+ //m_canvas.parentNode.appendChild(m_priceIndicator);
1770
1952
  m_canvas.parentNode.appendChild(m_priceIndicator);
1771
1953
  m_priceIndicator.setAttribute('class', 'millistream-chart-price-indicator');
1772
1954
  }
1773
1955
  m_priceIndicator.innerHTML = newValue;
1774
- m_priceIndicator.style.left = (m_chartspaces.chart.right / window.devicePixelRatio) - (offsetWidth == 0 ? -1 : m_priceIndicator.offsetWidth) + 'px'; // offsetWidth with devicePixelRatio???
1956
+ m_priceIndicator.style.left = (m_chartspaces.chart.right / 1) - (offsetWidth == 0 ? -1 : m_priceIndicator.offsetWidth) + 'px'; // offsetWidth with devicePixelRatio???
1775
1957
  m_priceIndicator.style.top = obj.instruments[0].y - (m_priceIndicator.offsetHeight / 2) + 'px';
1776
1958
  newValue = MillistreamWidgetApi_getElementNumber(_this, m_priceIndicator);
1777
1959
  MillistreamWidgetApi_flashElement(_this, m_priceIndicator, newValue, oldValue);
@@ -1779,7 +1961,7 @@ function Milli_Chart(settings) {
1779
1961
 
1780
1962
  function onMouseOut(evt) {
1781
1963
  if (typeof _this.settings.tooltip === 'object' && _this.settings.tooltip.preventOut) return;
1782
- for (var i = 0; i < _this.instruments.length; i++) {
1964
+ for (let i = 0; i < _this.instruments.length; i++) {
1783
1965
  if (_this.instruments[i].insref != 0 && typeof _this.instruments[i].toolTip !== 'undefined') {
1784
1966
  if (i == 0 && typeof _this.settings.tooltip == 'object' && typeof _this.settings.tooltip.mouseOut == 'function') _this.settings.tooltip.mouseOut.call();
1785
1967
  _this.instruments[i].toolTip.parentNode.removeChild(_this.instruments[i].toolTip);
@@ -1788,7 +1970,13 @@ function Milli_Chart(settings) {
1788
1970
  _this.instruments[i].toolTipPointer = undefined;
1789
1971
  }
1790
1972
  }
1791
-
1973
+ for (let i = 0; i < _this.settings.indicators.length; i++) {
1974
+ if (_this.settings.indicators[i].staticTooltip == true) continue;
1975
+ if (typeof _this.settings.indicators[i].toolTip !== 'undefined' && typeof _this.settings.indicators[i].toolTip.div !== 'undefined') {
1976
+ _this.settings.indicators[i].toolTip.div.parentNode.removeChild(_this.settings.indicators[i].toolTip.div);
1977
+ _this.settings.indicators[i].toolTip.div = undefined;
1978
+ }
1979
+ }
1792
1980
  }
1793
1981
 
1794
1982
  function clearZoom() {
@@ -1802,6 +1990,10 @@ function Milli_Chart(settings) {
1802
1990
  _this.drawChart();
1803
1991
  }
1804
1992
 
1993
+ function onMouseClick(evt) {
1994
+ if (m_zoom.mousedown && m_zoom.mousedown.pos != m_zoom.mouseup.pos) return;
1995
+ }
1996
+
1805
1997
  function onMouseMove(pos) {
1806
1998
  if (m_dataPoints.arr.length == 0) return;
1807
1999
  var rect = m_canvas.getBoundingClientRect();
@@ -1816,14 +2008,14 @@ function Milli_Chart(settings) {
1816
2008
  m_zoom.div.style.position = 'absolute';
1817
2009
  }
1818
2010
  if (x < m_chartspaces.chart.left) x = m_chartspaces.chart.left;
1819
- m_zoom.div.style.top = m_chartspaces.chart.top / window.devicePixelRatio + 'px';
1820
- m_zoom.div.style.left = (m_zoom.mousedown.pos > x / window.devicePixelRatio ? x / window.devicePixelRatio : m_zoom.mousedown.pos) + 'px';
2011
+ m_zoom.div.style.top = m_chartspaces.chart.top / 1 + 'px';
2012
+ m_zoom.div.style.left = (m_zoom.mousedown.pos > x / 1 ? x / 1 : m_zoom.mousedown.pos) + 'px';
1821
2013
  if (x > m_chartspaces.chart.right) x = m_chartspaces.chart.right;
1822
- m_zoom.div.style.height = (m_chartspaces.chart.height / window.devicePixelRatio) - (m_chartCss.marginBottom / window.devicePixelRatio) - m_chartspaces.chart.top + 'px';
1823
- m_zoom.div.style.width = (m_zoom.mousedown.pos > (x / window.devicePixelRatio) ? m_zoom.mousedown.pos - (x / window.devicePixelRatio) : (x / window.devicePixelRatio) - m_zoom.mousedown.pos) + 'px';
2014
+ m_zoom.div.style.height = (m_chartspaces.chart.height / 1) - (m_chartCss.marginBottom / 1) - m_chartspaces.chart.top + 'px';
2015
+ m_zoom.div.style.width = (m_zoom.mousedown.pos > (x / 1) ? m_zoom.mousedown.pos - (x / 1) : (x / 1) - m_zoom.mousedown.pos) + 'px';
1824
2016
  }
1825
2017
  if (typeof _this.settings.tooltip != 'object' || typeof _this.settings.tooltip.formatter != 'function') return;
1826
- if (x < m_chartspaces.chart.left || x > m_chartspaces.chart.right || y < m_chartspaces.chart.top || y > m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom)) {
2018
+ if (x < m_chartspaces.chart.left || x > m_chartspaces.chart.right || y < m_chartspaces.chart.top || y > m_chartspaces.chart.height - m_chartspaces.chart.marginBottom) {
1827
2019
  onMouseOut();
1828
2020
  return;
1829
2021
  }
@@ -1845,10 +2037,11 @@ function Milli_Chart(settings) {
1845
2037
  var highy = -1;
1846
2038
  var lowy = m_chartspaces.chart.height;
1847
2039
  var toolArray = [];
2040
+ let pointerWidth = 0;
1848
2041
  for (x = 0; x < _this.instruments.length; x++) {
1849
2042
  if (_this.instruments[x].insref != 0 && typeof obj.instruments[x] !== 'undefined') {
1850
2043
  var instr = {};
1851
- instr.chartType = _this.scaleinfoY.type;
2044
+ instr.chartType = chartType;
1852
2045
  instr.name = _this.instruments[x].name;
1853
2046
  instr.instrumenttype = _this.instruments[x].instrumenttype;
1854
2047
  instr.pricetype = _this.instruments[x].pricetype;
@@ -1878,16 +2071,9 @@ function Milli_Chart(settings) {
1878
2071
 
1879
2072
  }
1880
2073
  var pointerStyle = getComputedStyle(_this.instruments[x].toolTipPointer);
1881
- var pointerWidth = _this.instruments[x].toolTipPointer.offsetWidth + parseInt(pointerStyle.marginLeft) + parseInt(pointerStyle.marginRight);
2074
+ pointerWidth = _this.instruments[x].toolTipPointer.offsetWidth + parseInt(pointerStyle.marginLeft) + parseInt(pointerStyle.marginRight);
1882
2075
  var pointerHeight = _this.instruments[x].toolTipPointer.offsetHeight + parseInt(pointerStyle.marginTop) + parseInt(pointerStyle.marginBottom);
1883
2076
  var posy = obj.instruments[x].y - (_this.instruments[x].toolTip.offsetHeight / 2);
1884
- if (m_dataPoints.arr[i] + (_this.instruments[x].toolTip.offsetWidth * 1.5) > m_canvas.width) { // || m_dataPoints.arr[i] + (_this.instruments[x].toolTip.offsetWidth * 1.5) > m_canvas.width) { // TODO +10 should be calculated better
1885
- // draw the hover to the left
1886
- _this.instruments[x].toolTip.style.left = (obj.instruments[x].x - _this.instruments[x].toolTip.offsetWidth - (pointerWidth / 2)) + 1 + 'px';
1887
- } else {
1888
- // draw hover to the right
1889
- _this.instruments[x].toolTip.style.left = (obj.instruments[x].x + (pointerWidth / 2)) + 'px';
1890
- }
1891
2077
  _this.instruments[x].toolTip.style.top = posy + 'px';
1892
2078
  toolArray.push({ top: (obj.instruments[x].y - (pointerHeight / 2)), instrument: x });
1893
2079
  _this.instruments[x].toolTipPointer.style.left = (obj.instruments[x].x - (pointerWidth / 2)) + 1 + 'px'; // hmm plus 1??
@@ -1895,6 +2081,13 @@ function Milli_Chart(settings) {
1895
2081
  if (posy > highy) highy = posy;
1896
2082
  if (posy < lowy) lowy = posy;
1897
2083
  _this.instruments[x].toolTip.innerHTML = _this.settings.tooltip.formatter.call(instr, m_chartspaces, obj.instruments[x].x);
2084
+ if (m_dataPoints.arr[i] + (_this.instruments[x].toolTip.offsetWidth * 1.5) > m_canvas.getWidth()) { // || m_dataPoints.arr[i] + (_this.instruments[x].toolTip.offsetWidth * 1.5) > m_canvas.getWidth()) { // TODO +10 should be calculated better
2085
+ // draw the hover to the left
2086
+ _this.instruments[x].toolTip.style.left = (obj.instruments[x].x - _this.instruments[x].toolTip.offsetWidth - (pointerWidth / 2)) + 1 + 'px';
2087
+ } else {
2088
+ // draw hover to the right
2089
+ _this.instruments[x].toolTip.style.left = (obj.instruments[x].x + (pointerWidth / 2)) + 'px';
2090
+ }
1898
2091
  } else {
1899
2092
  if (_this.instruments[x].toolTip) {
1900
2093
  _this.instruments[x].toolTip.parentNode.removeChild(_this.instruments[x].toolTip);
@@ -1904,15 +2097,66 @@ function Milli_Chart(settings) {
1904
2097
  }
1905
2098
  }
1906
2099
  }
2100
+ //indicator hover
2101
+ for (x = 0; x < _this.settings.indicators.length; x++) {
2102
+ var px = getScaledSetting(pos.clientX) - getScaledSetting(rect.left) - 1;
2103
+ if (typeof _this.settings.indicators[x].toolTip === 'undefined') continue;
2104
+ if (_this.settings.indicators[x].staticTooltip == true) continue;
2105
+ var width = m_ctx.measureText(_this.settings.indicators[x].indicator).width;
2106
+ var remove = true;
2107
+ for (var xx = 0; xx < _this.settings.indicators[x].timeseries.length; xx++) {
2108
+ if (typeof _this.settings.indicators[x].timeseries[xx].pos === 'undefined') continue;
2109
+ if (typeof _this.settings.indicators[x].timeseries[xx].hl === 'undefined') continue;
2110
+ if (px >= _this.settings.indicators[x].timeseries[xx].pos.x - (width / 2) && px <= _this.settings.indicators[x].timeseries[xx].pos.x + (width / 2)) {
2111
+ if (typeof _this.settings.indicators[x].toolTip.div === 'undefined') {
2112
+ _this.settings.indicators[x].toolTip.div = document.createElement('div');
2113
+ _this.settings.indicators[x].toolTip.div.style.display = _this.settings.tooltip.display;
2114
+ _this.settings.indicators[x].toolTip.div.setAttribute('class', 'millistream-chart-tooltip'); // set class so we can measure it might change below depending on position
2115
+ _this.settings.indicators[x].toolTip.div.position = 'absolute';
2116
+ m_canvas.parentNode.appendChild(_this.settings.indicators[x].toolTip.div);
2117
+ }
2118
+ _this.settings.indicators[x].toolTip.div.style.left = _this.settings.indicators[x].timeseries[xx].pos.x / 1 + (pointerWidth / 2) + 'px'; // this i modified in the data for the instruments, but not for indicators
2119
+ _this.settings.indicators[x].toolTip.div.style.top = _this.settings.indicators[x].timeseries[xx].pos.y / 1 + 'px'; // this i modified in the data for the instruments, but not for indicators
2120
+ _this.settings.indicators[x].toolTip.div.innerHTML = _this.settings.indicators[x].toolTip.formatter.call(_this.settings.indicators[x].timeseries[xx]);
2121
+ remove = false;
2122
+ toolArray.push({ top: parseInt(_this.settings.indicators[x].toolTip.div.style.top), indicator: x });
2123
+ }
2124
+
2125
+ }
2126
+ if (remove) {
2127
+ if (_this.settings.indicators[x].toolTip.div && _this.settings.indicators[x].staticTooltip != true) {
2128
+ _this.settings.indicators[x].toolTip.div.parentNode.removeChild(_this.settings.indicators[x].toolTip.div);
2129
+ _this.settings.indicators[x].toolTip.div = undefined;
2130
+ }
2131
+
2132
+ }
2133
+ }
1907
2134
  toolArray.sort((a, b) => a.top - b.top);
1908
2135
  var lastTop = 0;
2136
+ let lastHeight = 0;
1909
2137
  for (x = 0; x < toolArray.length; x++) {
1910
- if (x != 0 && typeof _this.instruments[x - 1].toolTip !== 'undefined') {
1911
- if (lastTop + _this.instruments[x - 1].toolTip.offsetHeight > toolArray[x].top) {
1912
- toolArray[x].top = (lastTop + _this.instruments[x - 1].toolTip.offsetHeight);
2138
+ if (typeof toolArray[x].instrument !== 'undefined') {
2139
+ //if (x != 0 && typeof _this.instruments[x - 1].toolTip !== 'undefined') {
2140
+ //if (lastTop + _this.instruments[x - 1].toolTip.offsetHeight > toolArray[x].top) {
2141
+ if (lastTop + lastHeight > toolArray[x].top) {
2142
+ //toolArray[x].top = (lastTop + _this.instruments[x - 1].toolTip.offsetHeight);
2143
+ toolArray[x].top = (lastTop + lastHeight);
2144
+ }
2145
+ //}
2146
+ } else {
2147
+ //if (lastTop + _this.settings.indicators[toolArray[x].indicator].toolTip.div.offsetHeight > toolArray[x].top) {
2148
+ if (lastTop + lastHeight > toolArray[x].top) {
2149
+ //toolArray[x].top = (lastTop + _this.settings.indicators[toolArray[x].indicator].toolTip.div.offsetHeight);
2150
+ toolArray[x].top = (lastTop + lastHeight);
1913
2151
  }
1914
2152
  }
1915
- _this.instruments[toolArray[x].instrument].toolTip.style.top = toolArray[x].top + 'px';
2153
+ if (typeof toolArray[x].instrument !== 'undefined') {
2154
+ _this.instruments[toolArray[x].instrument].toolTip.style.top = toolArray[x].top + 'px';
2155
+ lastHeight = _this.instruments[toolArray[x].instrument].toolTip.offsetHeight;
2156
+ } else {
2157
+ _this.settings.indicators[toolArray[x].indicator].toolTip.div.style.top = toolArray[x].top + 'px';
2158
+ lastHeight = _this.settings.indicators[toolArray[x].indicator].toolTip.div.offsetHeight;
2159
+ }
1916
2160
  lastTop = toolArray[x].top;
1917
2161
  }
1918
2162
  return;
@@ -1948,13 +2192,13 @@ function Milli_Chart(settings) {
1948
2192
  m_zoom.mouseup.timestamp = new Date(m_zoom.mouseup.timestamp + 'T00:00:00Z');
1949
2193
  }
1950
2194
  _this.settings.chartlen = 'max';
1951
- _this.scaleinfoY.type = 'history';
2195
+ chartType = 'history';
1952
2196
  _this.drawChart();
1953
2197
  };
1954
2198
 
1955
2199
  function calcCompareFactors(type) {
1956
2200
  var i, s;
1957
- var instrumentprice;
2201
+ //var instrumentprice;
1958
2202
 
1959
2203
  for (s = 1; s < _this.instruments.length; s++) {
1960
2204
  if (_this.instruments[s].insref != 0) {
@@ -1962,11 +2206,12 @@ function Milli_Chart(settings) {
1962
2206
  if (_this.instruments[s].history[i].timestamp >= _this.scaleinfoX.startTimeStamp) {
1963
2207
  for (var x = 0; x < _this.instruments[0].history.length; x++) {
1964
2208
  if (_this.instruments[0].history[x].timestamp >= _this.instruments[s].history[i].timestamp) {
1965
- instrumentprice = _this.instruments[0].history[x].price;
2209
+ //instrumentprice = _this.instruments[0].history[x].price;
2210
+ _this.instruments[s].factor = _this.instruments[0].history[x].price / _this.instruments[s].history[i].price;
1966
2211
  break;
1967
2212
  }
1968
2213
  }
1969
- _this.instruments[s].factor = instrumentprice / _this.instruments[s].history[i].price;
2214
+ //_this.instruments[s].factor = instrumentprice / _this.instruments[s].history[i].price;
1970
2215
  break;
1971
2216
  }
1972
2217
  }
@@ -1976,14 +2221,17 @@ function Milli_Chart(settings) {
1976
2221
 
1977
2222
  function checkChartData(data) {
1978
2223
  var count = 0;
2224
+ //_this.instruments[0].timeseries = [];
1979
2225
  for (var i = 0; i < data.length; i++) {
1980
2226
  if (data[i].timestamp >= _this.scaleinfoX.startTimeStamp && data[i].timestamp <= _this.scaleinfoX.endTimeStamp) {
2227
+ // _this.instruments[0].timeseries.push(data[i]);
1981
2228
  if (count++ > 0) {
1982
2229
  return true;
1983
2230
  }
1984
2231
  }
1985
2232
  }
1986
- if (_this.scaleinfoY.type == 'trades' && count > 0 && _this.instruments[0].closeprice1d) return true; // we can draw on closeprice 1d -> first trade
2233
+ //if (_this.instruments[0].timeseries.length > 1) return true;
2234
+ if (chartType == 'trades' && count > 0 && _this.instruments[0].closeprice1d) return true; // we can draw on closeprice 1d -> first trade
1987
2235
  var y = (m_chartspaces.chart.bottom - m_chartspaces.chart.top) / 2 + m_chartspaces.chart.top;
1988
2236
  var x = (m_chartspaces.chart.right - m_chartspaces.chart.left) / 2 + m_chartspaces.chart.left;
1989
2237
  m_ctx.save();
@@ -1999,49 +2247,74 @@ function Milli_Chart(settings) {
1999
2247
  }
2000
2248
 
2001
2249
  function calcChartSpaces() {
2002
- m_chartspaces.chart.height = Math.round(m_canvas.height * (m_chartspaces.chart.percent / 100));
2003
- m_chartspaces.chart.top = getScaledSetting(m_chartCss.marginTop);
2250
+ m_chartspaces.chart.height = Math.round(m_canvas.getHeight() * (m_chartspaces.chart.percent / 100));
2251
+ m_chartspaces.chart.marginTop = getScaledSetting(m_chartCss.marginTop);
2252
+ m_chartspaces.chart.top = m_chartspaces.chart.marginTop;
2004
2253
  m_chartspaces.chart.left = getScaledSetting(m_chartCss.marginLeft);
2005
- m_chartspaces.chart.right = m_canvas.width - getScaledSetting(m_chartCss.marginRight);
2006
- m_chartspaces.chart.bottom = m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom);
2007
- m_chartspaces.chart.width = m_canvas.width;
2008
- m_chartspaces.lowerChart.height = m_canvas.height * (m_chartspaces.lowerChart.percent / 100);
2009
- m_chartspaces.lowerChart.top = Math.round(m_chartspaces.chart.bottom + getScaledSetting(m_chartCss.marginBottom) + m_chartspaces.chart.top);
2254
+ m_chartspaces.chart.right = m_canvas.getWidth()- getScaledSetting(m_chartCss.marginRight) - 50;
2255
+ m_chartspaces.chart.marginBottom = getScaledSetting(m_chartCss.marginBottom);
2256
+ m_chartspaces.chart.bottom = (m_chartspaces.chart.height - m_chartspaces.chart.marginBottom);
2257
+ m_chartspaces.chart.width = m_canvas.getWidth()
2258
+
2259
+ m_chartspaces.lowerChart.marginBottom = getScaledSetting(20) / window.devicePixelRatio;
2260
+ m_chartspaces.lowerChart.marginTop = getScaledSetting(0);
2261
+ m_chartspaces.lowerChart.height = Math.round(m_canvas.getHeight() - m_chartspaces.chart.height);
2262
+ m_chartspaces.lowerChart.top = m_chartspaces.chart.height + m_chartspaces.lowerChart.marginTop;
2010
2263
  m_chartspaces.lowerChart.left = m_chartspaces.chart.left;
2011
2264
  m_chartspaces.lowerChart.right = m_chartspaces.chart.right;
2012
- m_chartspaces.lowerChart.bottom = m_canvas.height - getScaledSetting(m_chartCss.marginBottom);
2013
- m_chartspaces.lowerChart.width = m_canvas.width;
2265
+ m_chartspaces.lowerChart.bottom = m_canvas.getHeight() - m_chartspaces.lowerChart.marginBottom;
2266
+ m_chartspaces.lowerChart.width = m_canvas.getWidth()
2014
2267
  }
2015
2268
 
2016
- _this.drawChart = function() {
2017
- if (m_zoom.isZooming) {
2018
- return; // prevent redraw due to push updates when zooming
2019
- }
2020
-
2269
+ function calculateIndicators() {
2021
2270
  for (var i = 0; i < _this.settings.indicators.length; i++) {
2022
2271
  switch (_this.settings.indicators[i].method) {
2023
2272
  case 'sma':
2024
- {
2025
- _this.settings.indicators[i].history = simpleMovingAverage(_this.instruments[0].history, _this.settings.indicators[i].method_length);
2026
- _this.settings.indicators[i].trades = simpleMovingAverage(_this.instruments[0].trades, _this.settings.indicators[i].method_length);
2027
- }
2273
+ if (chartType == 'history')
2274
+ _this.settings.indicators[i].timeseries = simpleMovingAverage(_this.instruments[0].history, _this.settings.indicators[i].method_length);
2275
+ else
2276
+ _this.settings.indicators[i].timeseries = simpleMovingAverage(_this.instruments[0].trades, _this.settings.indicators[i].method_length);
2028
2277
  break;
2029
2278
  case 'ema':
2030
- {
2031
- _this.settings.indicators[i].history = exponentialMovingAverage(_this.instruments[0].history, _this.settings.indicators[i].method_length);
2032
- _this.settings.indicators[i].trades = exponentialMovingAverage(_this.instruments[0].trades, _this.settings.indicators[i].method_length);
2033
- break;
2034
- }
2279
+ if (chartType == 'history')
2280
+ _this.settings.indicators[i].timeseries = exponentialMovingAverage(_this.instruments[0].history, _this.settings.indicators[i].method_length);
2281
+ else
2282
+ _this.settings.indicators[i].timeseries = exponentialMovingAverage(_this.instruments[0].trades, _this.settings.indicators[i].method_length);
2283
+ break;
2035
2284
  case 'bb':
2036
- {
2037
- _this.settings.indicators[i].history = bollingerBands(_this.instruments[0].history, _this.settings.indicators[i].method_length, _this.settings.indicators[i].stddev | 2);
2038
- _this.settings.indicators[i].trades = bollingerBands(_this.instruments[0].trades, _this.settings.indicators[i].method_length, _this.settings.indicators[i].stddev | 2);
2039
- break;
2040
- }
2285
+ if (chartType == 'history')
2286
+ _this.settings.indicators[i].timeseries = calculateBollingerBands(_this.instruments[0].history, _this.settings.indicators[i].method_length, _this.settings.indicators[i].stddev | 2);
2287
+ else
2288
+ _this.settings.indicators[i].timeseries = calculateBollingerBands(_this.instruments[0].trades, _this.settings.indicators[i].method_length, _this.settings.indicators[i].stddev | 2);
2289
+ break;
2290
+ case 'news':
2291
+ _this.settings.indicators[i].timeseries = calculateNews(_this.settings.indicators[i].news, _this.settings.indicators[i].method_length, _this.settings.indicators[i].stddev | 2);
2292
+ break;
2293
+ // future events osv?
2041
2294
  default:
2042
2295
  break;
2043
2296
  }
2044
2297
  }
2298
+ }
2299
+
2300
+ _this.drawChart = function() {
2301
+ if (m_zoom.isZooming) {
2302
+ return; // prevent redraw due to push updates when zooming
2303
+ }
2304
+ m_chartspaces.chart.percent = 100;
2305
+ m_chartspaces.lowerChart.percent = 0;
2306
+ for (let i = 0; i < _this.settings.indicators.length; i++) {
2307
+ if (_this.settings.indicators[i].target == 'lower') {
2308
+ m_chartspaces.chart.percent = 70;
2309
+ m_chartspaces.lowerChart.percent = 30;
2310
+ break;
2311
+ }
2312
+ }
2313
+ var period = _this.settings.chartlen.substring(_this.settings.chartlen.length - 1);
2314
+ var len = parseInt(_this.settings.chartlen.substring(0, _this.settings.chartlen.length - 1));
2315
+ if (period == 'd' && _this.settings.chartlen != 'ytd') chartType = 'trades';
2316
+ else chartType = 'history';
2317
+
2045
2318
  if (m_lastDrawnInstrument != _this.instruments[0].insref) {
2046
2319
  m_zoom.mousedown.pos = 0;
2047
2320
  m_zoom.mousedown.i = 0;
@@ -2063,10 +2336,8 @@ function Milli_Chart(settings) {
2063
2336
  }
2064
2337
  calcChartSpaces();
2065
2338
 
2066
- var period = _this.settings.chartlen.substring(_this.settings.chartlen.length - 1);
2067
- var len = parseInt(_this.settings.chartlen.substring(0, _this.settings.chartlen.length - 1));
2068
- m_ctx.clearRect(0, 0, m_canvas.width, m_canvas.height);
2069
- m_ctx.lineWidth = 1 / window.devicePixelRatio;
2339
+ m_ctx.clearRect(0, 0, m_canvas.getWidth(), m_canvas.getHeight());
2340
+ m_ctx.lineWidth = 1 / 1;
2070
2341
  m_ctx.textBaseline = 'top'; // important!
2071
2342
  var s;
2072
2343
  if (period == 'd' && _this.settings.chartlen != 'ytd') {
@@ -2104,9 +2375,14 @@ function Milli_Chart(settings) {
2104
2375
  _this.scaleinfoX.endTimeStamp -= _this.scaleinfoX.endTimeStamp - _this.scaleinfoX.endTimeStamp % 86400000;
2105
2376
  _this.scaleinfoX.endTimeStamp += _this.instruments[0].quotedate; // set enddate = last Quotedate
2106
2377
  }
2378
+ for (var i = 0; i < _this.settings.indicators.length; i++) {
2379
+ if (_this.settings.indicators[i].type == FUTUREINDICATOR) {
2380
+ let endDate = new Date(_this.settings.indicators[i].timeseries[0].timestamp).getTime();
2381
+ if (endDate > _this.scaleinfoX.endTimeStamp) _this.scaleinfoX.endTimeStamp = endDate;
2382
+ }
2383
+ }
2107
2384
  }
2108
2385
  setTimeSpanData();
2109
- _this.scaleinfoY.type = 'trades';
2110
2386
  if (_this.settings.absoluteScaling == true) {
2111
2387
  for (s = 1; s < _this.instruments.length; s++) _this.instruments[s].factor = 1;
2112
2388
  } else
@@ -2133,17 +2409,39 @@ function Milli_Chart(settings) {
2133
2409
  }
2134
2410
  _this.instruments[0].factor = 1;
2135
2411
  if (false == checkChartData(_this.instruments[0].trades)) return;
2136
- drawYAxis();
2412
+ calculateIndicators();
2413
+ drawYAxis(scaleinfoY, scaleinfoY2);
2137
2414
  drawXAxisTick(_this.scaleinfoX.startTimeStamp, _this.scaleinfoX.endTimeStamp);
2138
2415
  for (i = 0; i < _this.instruments.length; i++) {
2139
- if (_this.instruments[i].insref != 0)
2140
- plotData(_this.instruments[i].trades, i);
2416
+ if (_this.instruments[i].insref != 0) {
2417
+ plotData(_this.instruments[i].trades, i, scaleinfoY);
2418
+ }
2141
2419
  }
2142
2420
 
2143
2421
  for (i = 0; i < _this.settings.indicators.length; i++) {
2144
- if (_this.settings.indicators[i].method == 'bb') plotBollingerBand(_this.settings.indicators[i], 'trades');
2145
- else plotMovingAverage(_this.settings.indicators[i], 'trades');
2146
-
2422
+ switch (_this.settings.indicators[i].method) {
2423
+ case 'bb':
2424
+ plotBollingerBand(_this.settings.indicators[i], 'trades');
2425
+ break;
2426
+ case 'sma':
2427
+ case 'ema':
2428
+ plotIndicatorLine(_this.settings.indicators[i], scaleinfoY, m_chartspaces.chart);
2429
+ break;
2430
+ case 'rsi':
2431
+ case 'momentum':
2432
+ case 'quantity':
2433
+ break;
2434
+ case 'ohlc':
2435
+ case 'candlestick':
2436
+ break;
2437
+ default:
2438
+ if (_this.settings.indicators[i].style == 'line')
2439
+ plotIndicatorLine(_this.settings.indicators[i], scaleinfoY, m_chartspaces.chart);
2440
+ else {
2441
+ plotIndicator(_this.settings.indicators[i], scaleinfoY);
2442
+ }
2443
+ break;
2444
+ }
2147
2445
  }
2148
2446
 
2149
2447
  } else
@@ -2163,27 +2461,80 @@ function Milli_Chart(settings) {
2163
2461
 
2164
2462
  _this.scaleinfoX.endTimeStamp = new Date().getTime();
2165
2463
  _this.scaleinfoX.endTimeStamp = _this.scaleinfoX.endTimeStamp - (_this.scaleinfoX.endTimeStamp % 86400000); // TODO: detta kan vi nog ta bort eller skall det vara med closetime?
2464
+ for (var i = 0; i < _this.settings.indicators.length; i++) {
2465
+ if (_this.settings.indicators[i].type == FUTUREINDICATOR) {
2466
+ let endDate = new Date(_this.settings.indicators[i].timeseries[0].timestamp).getTime();
2467
+ if (endDate > _this.scaleinfoX.endTimeStamp) _this.scaleinfoX.endTimeStamp = endDate;
2468
+ }
2469
+
2470
+ }
2471
+ _this.scaleinfoX.endTimeStamp = _this.scaleinfoX.endTimeStamp - (_this.scaleinfoX.endTimeStamp % 86400000); // TODO: detta kan vi nog ta bort eller skall det vara med closetime?
2166
2472
  }
2167
2473
  setTimeSpanData();
2168
- _this.scaleinfoY.type = 'history';
2474
+
2169
2475
  if (_this.settings.absoluteScaling == true) {
2170
2476
  for (s = 1; s < _this.instruments.length; s++) _this.instruments[s].factor = 1;
2171
2477
  } else calcCompareFactors('history');
2172
2478
  _this.instruments[0].factor = 1;
2173
2479
 
2174
2480
  if (false == checkChartData(_this.instruments[0].history)) return;
2175
- drawYAxis();
2481
+ calculateIndicators();
2482
+
2483
+ /*let scaleY = {};
2484
+ if (_this.settings.drawyaxis) {
2485
+ if (false == calcHighLow2(scaleY, m_chartspaces.chart, m_yLegendCss)) {
2486
+ console.log('fail highlow');
2487
+ return;
2488
+ }
2489
+ }
2490
+ let scaleY2 = {};
2491
+ let factorInfo = {
2492
+ name: 'diff',
2493
+ actor: _this.instruments[0].startValue,
2494
+ suffix: '%'
2495
+ };
2496
+ if (_this.settings.drawy2axis) {
2497
+ if (false == calcHighLow2(scaleY2, m_chartspaces.chart, m_y2LegendCss, factorInfo)) {
2498
+ console.log('fail highlow');
2499
+ return;
2500
+ }
2501
+ }
2502
+
2503
+ drawYAxisNew(scaleY, m_chartspaces.chart, m_yLegendCss, _this.settings.drawyaxis, _this.settings.gridHorizontalLines);
2504
+ drawYAxisNew(scaleY2, m_chartspaces.chart, m_y2LegendCss, _this.settings.drawy2axis, false, factorInfo); *
2505
+ */
2506
+ drawYAxis(scaleinfoY, scaleinfoY2);
2176
2507
  drawXAxisMonth(_this.scaleinfoX.startTimeStamp, _this.scaleinfoX.endTimeStamp);
2177
2508
 
2178
2509
  for (i = 0; i < _this.instruments.length; i++) {
2179
2510
  if (_this.instruments[i].insref != 0)
2180
- plotData(_this.instruments[i].history, i);
2511
+ //plotData(_this.instruments[i].history, i, scaleY);
2512
+ plotData(_this.instruments[i].history, i, scaleinfoY);
2181
2513
  }
2182
2514
 
2183
2515
  for (i = 0; i < _this.settings.indicators.length; i++) {
2184
- if (_this.settings.indicators[i].method == 'bb') plotBollingerBand(_this.settings.indicators[i], 'history');
2185
- else plotMovingAverage(_this.settings.indicators[i], 'history');
2186
-
2516
+ switch (_this.settings.indicators[i].method) {
2517
+ case 'bb':
2518
+ plotBollingerBand(_this.settings.indicators[i], 'history', scaleinfoY);
2519
+ break;
2520
+ case 'sma':
2521
+ case 'ema':
2522
+ plotIndicatorLine(_this.settings.indicators[i], scaleinfoY, m_chartspaces.chart);
2523
+ break;
2524
+ case 'rsi':
2525
+ case 'momentum':
2526
+ case 'quantity':
2527
+ break;
2528
+ case 'ohlc':
2529
+ case 'candlestick':
2530
+ break;
2531
+ default:
2532
+ if (_this.settings.indicators[i].style == 'line')
2533
+ plotIndicatorLine(_this.settings.indicators[i], scaleinfoY, m_chartspaces.chart);
2534
+ else
2535
+ plotIndicator(_this.settings.indicators[i], scaleinfoY);
2536
+ break;
2537
+ }
2187
2538
  }
2188
2539
  } else
2189
2540
  if ((period == 'y' || _this.settings.chartlen == 'ytd' || _this.settings.chartlen == 'max')) {
@@ -2206,51 +2557,126 @@ function Milli_Chart(settings) {
2206
2557
 
2207
2558
  _this.scaleinfoX.endTimeStamp = new Date().getTime();
2208
2559
  _this.scaleinfoX.endTimeStamp = _this.scaleinfoX.endTimeStamp - (_this.scaleinfoX.endTimeStamp % 86400000); // TODO: detta kan vi nog ta bort eller skall det vara med closetime?
2560
+ for (var i = 0; i < _this.settings.indicators.length; i++) {
2561
+ if (_this.settings.indicators[i].type == FUTUREINDICATOR) {
2562
+ let endDate = new Date(_this.settings.indicators[i].timeseries[0].timestamp).getTime();
2563
+ if (endDate > _this.scaleinfoX.endTimeStamp) _this.scaleinfoX.endTimeStamp = endDate;
2564
+ }
2565
+ }
2209
2566
  }
2210
2567
  setTimeSpanData();
2211
- _this.scaleinfoY.type = 'history';
2568
+
2569
+
2570
+ _this.instruments[0].factor = 1;
2571
+ if (false == checkChartData(_this.instruments[0].history)) return;
2572
+ calculateIndicators();
2573
+
2212
2574
  if (_this.settings.absoluteScaling == true) {
2213
2575
  for (s = 1; s < _this.instruments.length; s++) _this.instruments[s].factor = 1;
2214
2576
  } else calcCompareFactors('history');
2215
2577
 
2216
- _this.instruments[0].factor = 1;
2217
- if (false == checkChartData(_this.instruments[0].history)) return;
2218
- drawYAxis();
2578
+ drawYAxis(scaleinfoY, scaleinfoY2);
2219
2579
  drawXAxisYears(_this.scaleinfoX.startTimeStamp, _this.scaleinfoX.endTimeStamp);
2220
2580
  for (i = 0; i < _this.instruments.length; i++)
2221
2581
  if (_this.instruments[i].insref != 0)
2222
- plotData(_this.instruments[i].history, i);
2582
+ plotData(_this.instruments[i].history, i, scaleinfoY);
2223
2583
 
2224
2584
  for (i = 0; i < _this.settings.indicators.length; i++) {
2225
- if (_this.settings.indicators[i].method == 'bb') plotBollingerBand(_this.settings.indicators[i], 'history');
2226
- else plotMovingAverage(_this.settings.indicators[i], 'history');
2227
-
2585
+ switch (_this.settings.indicators[i].method) {
2586
+ case 'bb':
2587
+ plotBollingerBand(_this.settings.indicators[i], 'history', scaleinfoY);
2588
+ break;
2589
+ case 'sma':
2590
+ case 'ema':
2591
+ plotIndicatorLine(_this.settings.indicators[i], scaleinfoY, m_chartspaces.chart);
2592
+ break;
2593
+ case 'rsi':
2594
+ case 'momentum':
2595
+ case 'quantity':
2596
+ break;
2597
+ case 'ohlc':
2598
+ case 'candlestick':
2599
+ break;
2600
+ default:
2601
+ if (_this.settings.indicators[i].style == 'line')
2602
+ plotIndicatorLine(_this.settings.indicators[i], scaleinfoY, m_chartspaces.chart);
2603
+ else
2604
+ plotIndicator(_this.settings.indicators[i], scaleinfoY);
2605
+ break;
2606
+ }
2228
2607
  }
2229
-
2230
- /*for (const [key, a] of m_analyzisMethod.entries()) {
2231
- if (a.method == 'bb') plotBollingerBand(a, 'history');
2232
- else plotMovingAverage(a, 'history');
2233
- }*/
2234
2608
  }
2235
2609
  drawBoxShadow(m_chartspaces.chart);
2236
-
2237
- if (m_chartspaces.chart.percent != 100) {
2238
- drawXAxisTickLowerChart();
2239
- drawYAxisLower();
2240
- drawBoxShadow(m_chartspaces.lowerChart);
2241
- }
2610
+ drawLowerChart();
2242
2611
  if (typeof m_chartCss.backgroundColor !== 'undefined') {
2243
2612
  m_ctx.save();
2244
2613
  //if (_this.settings.curveOnTop == false)
2245
2614
  m_ctx.globalCompositeOperation = 'destination-over';
2246
2615
  m_ctx.fillStyle = m_chartCss.backgroundColor;
2247
- //m_ctx.fillRect(0, 0, m_canvas.width, m_canvas.height);
2248
- m_ctx.fillRect(0, 0, m_canvas.width, m_canvas.height);
2616
+ //m_ctx.fillRect(0, 0, m_canvas.getWidth(), m_canvas.getHeight());
2617
+ m_ctx.fillRect(0, 0, m_canvas.getWidth(), m_canvas.getHeight());
2249
2618
  m_ctx.restore();
2250
2619
  }
2251
2620
  //onMouseOut();
2252
2621
  };
2253
2622
 
2623
+ function drawLowerChart() {
2624
+ let lowerScale = {};
2625
+ let lowerDrawn = false;
2626
+ for (let i = 0; i < _this.settings.indicators.length; i++) {
2627
+ switch (_this.settings.indicators[i].method) {
2628
+ case 'momentum':
2629
+ if (chartType == 'history') _this.settings.indicators[i].timeseries = calculateMomentum(_this.instruments[0].history, _this.settings.indicators[i].method_length);
2630
+ else _this.settings.indicators[i].timeseries = calculateMomentum(_this.instruments[0].trades, _this.settings.indicators[i].method_length);
2631
+ if (_this.settings.indicators[i].target == 'upper') {
2632
+ lowerScale = drawYAxisIndicator(_this.settings.indicators[i].timeseries, m_chartspaces.chart, false);
2633
+ plotIndicatorLine(_this.settings.indicators[i], lowerScale, m_chartspaces.chart);
2634
+ } else {
2635
+ if (lowerDrawn == false) {
2636
+ drawXAxisTickLowerChart();
2637
+ lowerScale = drawYAxisIndicator(_this.settings.indicators[i].timeseries, m_chartspaces.lowerChart, true);
2638
+ plotIndicatorLine(_this.settings.indicators[i], lowerScale, m_chartspaces.lowerChart);
2639
+ drawBoxShadow(m_chartspaces.lowerChart);
2640
+ lowerDrawn = true;
2641
+ }
2642
+ }
2643
+ break;
2644
+ case 'rsi':
2645
+ if (chartType == 'history') _this.settings.indicators[i].timeseries = calculateRSI(_this.instruments[0].history, _this.settings.indicators[i].method_length);
2646
+ else _this.settings.indicators[i].timeseries = calculateRSI(_this.instruments[0].trades, _this.settings.indicators[i].method_length);
2647
+ if (_this.settings.indicators[i].target == 'upper') {
2648
+ lowerScale = drawYAxisIndicator(_this.settings.indicators[i].timeseries, m_chartspaces.chart, false);
2649
+ plotIndicatorLine(_this.settings.indicators[i], lowerScale, m_chartspaces.chart);
2650
+ } else {
2651
+ if (lowerDrawn == false) {
2652
+ drawXAxisTickLowerChart();
2653
+ lowerScale = drawYAxisIndicator(_this.settings.indicators[i].timeseries, m_chartspaces.lowerChart, true);
2654
+ plotIndicatorLine(_this.settings.indicators[i], lowerScale, m_chartspaces.lowerChart);
2655
+ drawBoxShadow(m_chartspaces.lowerChart);
2656
+ lowerDrawn = true;
2657
+ }
2658
+ }
2659
+ break;
2660
+ case 'quantity':
2661
+ if (chartType == 'history') _this.settings.indicators[i].timeseries = calculateQuantity(_this.instruments[0].history, _this.settings.indicators[i].target);
2662
+ else _this.settings.indicators[i].timeseries = calculateQuantity(_this.instruments[0].trades, _this.settings.indicators[i].target);
2663
+ if (_this.settings.indicators[i].target == 'upper') {
2664
+ lowerScale = drawYAxisIndicator(_this.settings.indicators[i].timeseries, m_chartspaces.chart, false);
2665
+ plotBars(_this.settings.indicators[i], lowerScale, m_chartspaces.chart);
2666
+ break;
2667
+ } else {
2668
+ if (lowerDrawn == false) {
2669
+ drawXAxisTickLowerChart();
2670
+ lowerScale = drawYAxisIndicator(_this.settings.indicators[i].timeseries, m_chartspaces.lowerChart, true);
2671
+ plotBars(_this.settings.indicators[i], lowerScale, m_chartspaces.lowerChart, true);
2672
+ lowerDrawn = true;
2673
+ }
2674
+ }
2675
+ return;
2676
+ }
2677
+ }
2678
+ }
2679
+
2254
2680
  function drawBoxShadow(space) {
2255
2681
  if (typeof m_chartCss.boxShadow === 'undefined' || typeof m_chartCss.boxShadow.color === 'undefined') return;
2256
2682
  m_ctx.save();
@@ -2293,6 +2719,9 @@ function Milli_Chart(settings) {
2293
2719
  data.marketclose = '22:00:00';
2294
2720
  }
2295
2721
  }
2722
+ if (typeof data.diff1dprc !== 'undefined')
2723
+ instr.diff1dprc = data.diff1dprc;
2724
+
2296
2725
  if (typeof data.date !== 'undefined') {
2297
2726
  instr.quotedate = new Date(data.date).getTime();
2298
2727
  instr.quotedate -= instr.quotedate % 86400000;
@@ -2340,6 +2769,7 @@ function Milli_Chart(settings) {
2340
2769
  instr.trades.push(item);
2341
2770
  instr.hashmap.set(item.timestamp, item);
2342
2771
  // we might have recieved trades from push before dataapi
2772
+ // borde sorteras utanför loopen?
2343
2773
  instr.trades.sort(function(a, b) {
2344
2774
  return a.timestamp - b.timestamp;
2345
2775
  });
@@ -2357,6 +2787,8 @@ function Milli_Chart(settings) {
2357
2787
  item.timestamp = timestamp;
2358
2788
  } else continue;
2359
2789
  if (typeof data.history[i].closeprice !== 'undefined') item.price = parseFloat(data.history[i].closeprice) * factor;
2790
+ if (typeof data.history[i].closedayhighprice !== 'undefined') item.highprice = parseFloat(data.history[i].closedayhighprice) * factor;
2791
+ if (typeof data.history[i].closedaylowprice !== 'undefined') item.lowprice = parseFloat(data.history[i].closedaylowprice) * factor;
2360
2792
  if (typeof data.history[i].openprice !== 'undefined') item.openprice = data.history[i].openprice;
2361
2793
  if (typeof data.history[i].closequantity !== 'undefined') item.quantity = data.history[i].closequantity;
2362
2794
  if (typeof data.history[i].dividend !== 'undefined') item.dividend = data.history[i].dividend;
@@ -2373,19 +2805,6 @@ function Milli_Chart(settings) {
2373
2805
  }
2374
2806
  }
2375
2807
 
2376
- _this.addQuantity = function() {
2377
- if (m_chartspaces.chart.percent == 100) {
2378
- m_chartspaces.chart.percent = 70;
2379
- m_chartspaces.lowerChart.percent = 30;
2380
- } else {
2381
- m_chartspaces.chart.percent = 100;
2382
- m_chartspaces.lowerChart.percent = 0;
2383
- _this.drawChart();
2384
- return;
2385
- }
2386
- _this.drawChart();
2387
- };
2388
-
2389
2808
  function plotExternalHistoricalData(data) {
2390
2809
  // används för dividend osv
2391
2810
  m_ctx.save();
@@ -2429,14 +2848,13 @@ function Milli_Chart(settings) {
2429
2848
  // TODO: här blir det fel när det är från 00:00: 23:59 men göms av tmpx < startpoint.x
2430
2849
 
2431
2850
  }
2432
- //startpoint.y = Math.round(m_canvas.height - getScaledSetting(m_chartCss.marginBottom) - (((data[i].price * factor) - _this.scaleinfoY.minValue) * _this.scaleinfoY.valuePerPixel)) + 0.5;
2433
- startpoint.y = Math.round(m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) - (((data[i].price * factor) - _this.scaleinfoY.minValue) * _this.scaleinfoY.valuePerPixel)) + 0.5;
2851
+ startpoint.y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((data[i].price * factor) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
2434
2852
  maxy = maxy > startpoint.y ? maxy : startpoint.y;
2435
2853
 
2436
2854
  startpoint.x = Math.round(m_chartspaces.chart.left + ((data[i].timestamp - _this.scaleinfoX.startDate.getTime()) / _this.scaleinfoX.timePerPixel)) + 0.5;
2437
2855
  startpoint.x -= offset;
2438
2856
  m_ctx.beginPath();
2439
- m_ctx.arc(startpoint.x, startpoint.y - 20, 10, 0, 2 * Math.PI, false);
2857
+ m_ctx.arc(startpoint.x, startpoint.y - 20, 10, 0, 2 * Math.PI, false); // ritar en rund blob, ska nog va nåt annat
2440
2858
  m_ctx.fillStyle = 'green';
2441
2859
  m_ctx.fill();
2442
2860
  m_ctx.stroke();
@@ -2445,7 +2863,8 @@ function Milli_Chart(settings) {
2445
2863
  m_ctx.restore();
2446
2864
  }
2447
2865
 
2448
- function calcAnalyzisLine(data, pricecol) {
2866
+ function calcAnalyzisLine(data, pricecol, y, cs, sc) {
2867
+ let chartspace = typeof cs === 'undefined' ? m_chartspaces.chart : cs;
2449
2868
  var startpoint = { x: 0, y: 0 };
2450
2869
  var endpoint = { x: 0, y: 0 };
2451
2870
  var startDate = _this.scaleinfoX.startTimeStamp;
@@ -2454,35 +2873,39 @@ function Milli_Chart(settings) {
2454
2873
  var offset = 0;
2455
2874
  var maxy = 0;
2456
2875
  var ret = [];
2876
+ let num = 0;
2457
2877
  for (var i = 0; i < len; i++) {
2458
2878
  var currentDate;
2459
2879
  var tmpx = startpoint.x;
2460
2880
  var tmp;
2461
- if (data[i].timestamp < _this.scaleinfoX.startTimeStamp) {
2881
+ var timestamp = data[i].timestamp;
2882
+ if (chartType == 'history') timestamp -= (timestamp % 86400000)
2883
+ if (timestamp < _this.scaleinfoX.startTimeStamp) {
2462
2884
  continue;
2463
2885
  }
2464
- if (data[i].timestamp > _this.scaleinfoX.endTimeStamp) {
2886
+ if (timestamp > _this.scaleinfoX.endTimeStamp) {
2887
+ console.log('to big', data[i].timestamp, _this.scaleinfoX.endTimeStamp);
2465
2888
  break;
2466
2889
  }
2467
2890
 
2468
- if (_this.scaleinfoY.type != 'history' && (data[i].timestamp % 86400000 < _this.instruments[0].opentimestamp || data[i].timestamp % 86400000 > _this.instruments[0].closetimestamp)) {
2891
+ if (chartType != 'history' && (data[i].timestamp % 86400000 < _this.instruments[0].opentimestamp || data[i].timestamp % 86400000 > _this.instruments[0].closetimestamp)) {
2469
2892
  continue;
2470
2893
  }
2471
- var endtimeToday = new Date(data[i].timestamp);
2894
+ var endtimeToday = new Date(timestamp);
2472
2895
  if (endtimeToday.getDay() == 0 || endtimeToday.getDay() == 6) continue;
2473
- if (_this.scaleinfoY.type != 'history') {
2896
+ if (chartType != 'history') {
2474
2897
  endtimeToday = new Date(endtimeToday.toISOString().substring(0, 10) + 'T' + _this.instruments[0].marketclose + 'Z'); // borde räcka att göra 1 gång när det blir nytt datum
2475
2898
  }
2476
- if (data[i].timestamp > endtimeToday.getTime()) {
2899
+ if (timestamp > endtimeToday.getTime()) {
2477
2900
  continue; // dataticks efter stängning ritas inte
2478
2901
  }
2479
- currentDate = new Date(data[i].timestamp);
2902
+ currentDate = new Date(timestamp);
2480
2903
  currentDate.setHours(lastdate.getHours());
2481
2904
  currentDate.setMinutes(lastdate.getMinutes());
2482
2905
  currentDate.setSeconds(lastdate.getSeconds());
2483
2906
  if (lastdate.toISOString().substring(0, 10) != currentDate.toISOString().substring(0, 10)) { // new date
2484
2907
  tmp = new Date(lastdate.toISOString().substring(0, 10) + 'T' + _this.instruments[0].marketclose + 'Z');
2485
- var nextDate = new Date(data[i].timestamp);
2908
+ var nextDate = new Date(timestamp);
2486
2909
  tmp = tmp.getTime() + 86400000 - _this.scaleinfoX.milliPerDay; // increase to next days starttime
2487
2910
 
2488
2911
  currentDate = new Date(tmp);
@@ -2495,123 +2918,251 @@ function Milli_Chart(settings) {
2495
2918
  }
2496
2919
  offset += ((86400000 - _this.scaleinfoX.milliPerDay) / _this.scaleinfoX.timePerPixel); // * dateDiffInDays(lastdate, currentDate);
2497
2920
  lastdate = currentDate;
2498
- startpoint.x = Math.round(m_chartspaces.chart.left + ((data[i].timestamp - startDate) / _this.scaleinfoX.timePerPixel)) + 0.5 - offset;
2499
-
2500
- // TODO: här blir det fel när det är från 00:00: 23:59 men göms av tmpx < startpoint.x
2501
- if (_this.scaleinfoY.type == 'trades' && tmpx < startpoint.x) {
2502
- ret.push(startpoint.x, startpoint.y);
2503
- }
2921
+ startpoint.x = Math.round(chartspace.left + ((timestamp - startDate) / _this.scaleinfoX.timePerPixel)) + 0.5 - offset;
2922
+ }
2923
+ if (typeof data[i].datapoints === 'undefined' || typeof data[i].datapoints[pricecol] === 'undefined')
2924
+ startpoint.y = y;
2925
+ else {
2926
+ startpoint.y = Math.round((cs.bottom - cs.top) - ((data[i].datapoints[pricecol] - sc.minValue) * sc.valuePerPixel)) + 0.5 + cs.top;
2927
+ startpoint.y = Math.round((cs.bottom - cs.top) - ((data[i].datapoints[pricecol] - sc.minValue) / sc.valuePerPixel)) + 0.5 + cs.top;
2504
2928
  }
2505
- startpoint.y = Math.round(m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) - ((data[i].datapoints[pricecol] - _this.scaleinfoY.minValue) * _this.scaleinfoY.valuePerPixel)) + 0.5;
2506
2929
  maxy = maxy > startpoint.y ? maxy : startpoint.y;
2507
2930
 
2508
- startpoint.x = Math.round(m_chartspaces.chart.left + ((data[i].timestamp - _this.scaleinfoX.startDate.getTime()) / _this.scaleinfoX.timePerPixel)) + 0.5;
2931
+ startpoint.x = Math.round(chartspace.left + ((timestamp - _this.scaleinfoX.startDate.getTime()) / _this.scaleinfoX.timePerPixel)) + 0.5;
2509
2932
  startpoint.x -= offset;
2933
+ data[i].pos = { x: startpoint.x, y: startpoint.y };
2934
+ num++;
2510
2935
 
2511
- if (startpoint.x != endpoint.x || startpoint.y != endpoint.y) {
2512
- var x = Math.round(startpoint.x);
2936
+ /*if (startpoint.x != endpoint.x || startpoint.y != endpoint.y) {
2513
2937
  if (endpoint.x != 0) {
2514
2938
  if (tmpx < startpoint.x) {
2515
- if (_this.settings.hcurve) m_ctx.lineTo(startpoint.x, endpoint.y);
2516
- ret.push({ x: startpoint.x, y: startpoint.y });
2939
+ //ret.push({ x: startpoint.x, y: startpoint.y });
2940
+ data[i].pos = { x: startpoint.x, y: startpoint.y };
2941
+ num++;
2517
2942
  }
2518
2943
  } else {
2519
- ret.push({ x: startpoint.x, y: startpoint.y });
2944
+ //ret.push({ x: startpoint.x, y: startpoint.y });
2945
+ data[i].pos = { x: startpoint.x, y: startpoint.y };
2946
+ num++;
2520
2947
  }
2521
2948
  endpoint.x = startpoint.x;
2522
2949
  endpoint.y = startpoint.y;
2523
- }
2950
+ }*/
2951
+ endpoint.x = startpoint.x;
2952
+ endpoint.y = startpoint.y;
2524
2953
  }
2525
- return ret;
2954
+ return num;
2526
2955
  }
2527
2956
 
2528
- function plotMovingAverage(method, type) {
2529
- var data = method[type];
2957
+ function plotBars(method, scale, cs) {
2958
+ let data = method.timeseries;
2959
+ let num = calcAnalyzisLine(data, 0, undefined, cs, scale);
2960
+
2961
+ if (num == 0) return;
2962
+ m_ctx.save();
2530
2963
  m_ctx.strokeStyle = method.color;
2531
- m_ctx.lineWidth = method.lineWidth | 1;
2532
- var line = calcAnalyzisLine(data, 0);
2533
- if (line.length == 0) return;
2964
+ let width = Math.round(cs.width / (num + 1) / 2);
2965
+ if (_this.settings.chartlen == '1d') width = 2; // since we have do not fill the whole chart with all datapoints
2966
+ if (width < 2) width = 2;
2967
+ else if (width > 20) width = 20;
2968
+ m_ctx.lineWidth = width;
2969
+
2970
+ // let s = 0;
2971
+ for (var i = data.length - 1; i >= 0; i--) {
2972
+ if (typeof data[i].pos === 'undefined') break;
2973
+
2974
+ let x = (data[i].pos.x + 0.5);
2975
+ m_ctx.lineWidth = width;
2976
+ if (x - width <= cs.left) {
2977
+ m_ctx.lineWidth = width / 2;
2978
+ x += m_ctx.lineWidth / 2;
2979
+ } else
2980
+ if (x + width >= cs.right) {
2981
+ m_ctx.lineWidth = width / 2;
2982
+ x -= m_ctx.lineWidth / 2 + 1;
2983
+ }
2984
+
2985
+ m_ctx.beginPath();
2986
+ m_ctx.moveTo(x, cs.bottom);
2987
+ m_ctx.lineTo(x, data[i].pos.y - 0.5);
2988
+ m_ctx.closePath();
2989
+ m_ctx.stroke();
2990
+ }
2991
+ m_ctx.restore();
2992
+ return;
2993
+ }
2994
+
2995
+ function plotIndicatorLine(method, scale, cs) {
2996
+ let data = method.timeseries;
2997
+ let num = calcAnalyzisLine(data, 0, undefined, cs, scale);
2998
+ if (num == 0) return;
2534
2999
  m_ctx.save();
2535
3000
  m_ctx.beginPath();
2536
- m_ctx.closePath(); // clear path
2537
- m_ctx.moveTo(line[0].x, line[0].y);
2538
- for (var i = 1; i < line.length; i++) {
2539
- m_ctx.lineTo(line[i].x, line[i].y);
3001
+ m_ctx.closePath();
3002
+ m_ctx.lineWidth = method.lineWidth | 1;
3003
+ m_ctx.strokeStyle = method.color;
3004
+ if (typeof method.lineStyle === 'string' && method.lineStyle == 'dash') m_ctx.setLineDash([3, 3]);
3005
+
3006
+ m_ctx.moveTo(data[data.length - 1].pos.x, data[data.length - 1].pos.y);
3007
+ let lastPos = null;
3008
+ for (var i = data.length - 2; i >= 0; i--) {
3009
+ if (typeof data[i].pos === 'undefined') break;
3010
+ m_ctx.lineTo(data[i].pos.x, data[i].pos.y);
3011
+ lastPos = data[i].pos;
3012
+ }
3013
+ if (method.method == 'FutureEvent' || method.method == 'FutureEvent2') { // just monthly for now // redo?
3014
+ var pos = m_dataPoints.map.get(m_dataPoints.arr[m_dataPoints.arr.length - 1]);
3015
+ m_ctx.lineTo(pos.instruments[0].x, pos.instruments[0].y);
2540
3016
  }
3017
+ if (typeof method.callback === 'function') method.callback.call({ x: data[data.length - 1].pos.x, y: data[data.length - 1].pos.y });
2541
3018
  m_ctx.stroke();
3019
+ m_ctx.lineTo(data[data.length - 1].pos.x, cs.bottom);
3020
+ m_ctx.restore();
3021
+ }
3022
+
3023
+ function plotIndicator(method, scale) {
3024
+ var data = method.timeseries;
3025
+ var width;
3026
+ method.timeseries.forEach(o => delete o.pos);
3027
+ let num = calcAnalyzisLine(data, 0, m_chartspaces.chart.top + 1, m_chartspaces.chart, scale); // + parseInt(method.fontSize));
3028
+ m_ctx.save();
3029
+ m_ctx.font = method.fontWeight + ' ' + method.fontSize + ' ' + method.fontFamily;
3030
+ m_ctx.fillStyle = method.color;
3031
+ m_ctx.fontStyle = method.fontStyle;
3032
+ let lastPos = { x: 0, y: 0 };
3033
+ let headlines = [];
3034
+ let hlPos = data.length - 1;
3035
+ for (var i = data.length - 1; i >= 0; i--) {
3036
+ //if (typeof data[i].pos === 'undefined') break;
3037
+ if (typeof data[i].pos === 'undefined') continue;
3038
+ if (data[i].image) {
3039
+ m_ctx.drawImage(data[i].image.source, data[i].pos.x - (data[i].image.width / 2), data[i].pos.y - (data[i].image.height / 2), data[i].image.width, data[i].image.height);
3040
+ } else if (data[i].indicator) {
3041
+ m_ctx.fillStyle = data[i].color;
3042
+ width = m_ctx.measureText(data[i].indicator).width;
3043
+ m_ctx.fillText(data[i].indicator, data[i].pos.x - (width / 2), data[i].pos.y);
3044
+ } else if (method.image) {
3045
+ width = method.image.width;
3046
+ if (method.type == NEWSINDICATOR) {
3047
+ if (fabs(lastPos.x - data[i].pos.x) > width || fabs(lastPos.y > data[i].pos.y) > width) {
3048
+ m_ctx.drawImage(method.image.source, data[i].pos.x - (width / 2), data[i].pos.y - (method.image.height / 2), method.image.width, method.image.height);
3049
+ lastPos = { x: data[i].pos.x, y: data[i].y };
3050
+ method.timeseries[i].hl = [{ timestamp: data[i].timestamp, headline: data[i].headline }];
3051
+ hlPos = i;
3052
+ } else {
3053
+ method.timeseries[hlPos].hl.push({ timestamp: data[i].timestamp, headline: data[i].headline });
3054
+ }
3055
+ } else {
3056
+ m_ctx.drawImage(method.image.source, data[i].pos.x - (width / 2), data[i].pos.y - (method.image.height / 2), method.image.width, method.image.height);
3057
+ }
3058
+ } else if (method.indicator) {
3059
+ m_ctx.fillStyle = method.color;
3060
+ width = m_ctx.measureText(method.indicator).width;
3061
+ if (method.type == NEWSINDICATOR) {
3062
+ if (fabs(lastPos.x - data[i].pos.x) > width || fabs(lastPos.y > data[i].pos.y) > width) {
3063
+ m_ctx.fillText(method.indicator, data[i].pos.x - (width / 2), data[i].pos.y);
3064
+ lastPos = { x: data[i].pos.x, y: data[i].y };
3065
+ method.timeseries[i].hl = [{ timestamp: data[i].timestamp, headline: data[i].headline }];
3066
+ hlPos = i;
3067
+
3068
+ } else {
3069
+ method.timeseries[hlPos].hl.push({ timestamp: data[i].timestamp, headline: data[i].headline });
3070
+ }
3071
+ } else m_ctx.fillText(method.indicator, data[i].pos.x - (width / 2), data[i].pos.y);
3072
+
3073
+ }
3074
+ }
2542
3075
  m_ctx.restore();
2543
3076
  }
2544
3077
 
2545
3078
  function plotBollingerBand(method, type) {
2546
- var data = method[type];
2547
- var upper = calcAnalyzisLine(data, 0, null);
2548
- var lower = calcAnalyzisLine(data, 1, null);
2549
- if (upper.length == 0 || lower.length == 0) return;
3079
+ let data = method.timeseries;
2550
3080
  m_ctx.strokeStyle = method.color;
2551
3081
  m_ctx.lineWidth = method.lineWidth | 1;
2552
3082
  m_ctx.save();
2553
3083
  m_ctx.beginPath();
2554
- upper = calcAnalyzisLine(data, 0, null);
2555
- lower = calcAnalyzisLine(data, 1, null);
2556
- for (var i = 0; i < upper.length; i++) m_ctx.lineTo(upper[i].x, upper[i].y);
2557
- m_ctx.lineTo(lower[lower.length - 1].x, lower[lower.length - 1].y);
2558
- for (i = lower.length - 1; i >= 0; i--) m_ctx.lineTo(lower[i].x, lower[i].y);
2559
- if (method.border) m_ctx.stroke();
3084
+ let num = calcAnalyzisLine(data, 0, undefined, m_chartspaces.chart, scaleinfoY);
3085
+ for (let i = data.length - 1; i >= data.length - num; i--) {
3086
+ m_ctx.lineTo(data[i].pos.x, data[i].pos.y);
3087
+ }
3088
+ num = calcAnalyzisLine(data, 1, undefined, m_chartspaces.chart, scaleinfoY);
3089
+ m_ctx.lineTo(data[data.length - num].pos.x, data[data.length - num].pos.y);
3090
+ for (let i = data.length - num; i < data.length; i++) {
3091
+ m_ctx.lineTo(data[i].pos.x, data[i].pos.y);
3092
+ }
2560
3093
  m_ctx.closePath();
2561
3094
  if (method.fill) {
2562
3095
  m_ctx.fillStyle = method.fill;
2563
3096
  m_ctx.fill();
2564
3097
  }
2565
- var sma = calcAnalyzisLine(data, 2);
3098
+ num = calcAnalyzisLine(data, 2, undefined, m_chartspaces.chart, scaleinfoY);
2566
3099
  m_ctx.beginPath();
2567
3100
  m_ctx.closePath(); // clear path
2568
- m_ctx.moveTo(sma[0].x, sma[0].y);
2569
- for (i = 0; i < sma.length; i++) m_ctx.lineTo(sma[i].x, sma[i].y);
3101
+ m_ctx.moveTo(data[data.length - num].x, data[data.length - num].y);
3102
+ for (let i = data.length - num; i < data.length; i++) {
3103
+ m_ctx.lineTo(data[i].pos.x, data[i].pos.y);
3104
+ }
2570
3105
  m_ctx.stroke();
2571
3106
  m_ctx.restore();
2572
3107
  return;
2573
3108
  }
3109
+ function drawCandlestick(data,factor,scaleinfoY,x) {
3110
+ let offset = 3;
3111
+ let y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((data.openprice * factor) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
3112
+ m_ctx.moveTo(x-offset, y);
3113
+ m_ctx.lineTo(x+offset , y);
3114
+ let oldy = y;
3115
+ y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((data.price * factor) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
3116
+ m_ctx.lineTo(x+offset , y);
3117
+ m_ctx.lineTo(x-offset , y);
3118
+ m_ctx.lineTo(x-offset,oldy);
3119
+ return;
3120
+
3121
+
3122
+
3123
+ y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((data.highprice * factor) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
3124
+ m_ctx.moveTo(x, y);
3125
+ y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((data.lowprice * factor) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
3126
+ m_ctx.lineTo(x, y);
3127
+ y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((data.price * factor) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
3128
+ m_ctx.moveTo(x, y);
3129
+ m_ctx.lineTo(x + offset, y);
3130
+
2574
3131
 
2575
- function plotLower(valuePerPixel, lineLength) {
2576
- m_ctx.save();
2577
- m_ctx.strokeStyle = m_instrumentCss[0].color;
2578
- m_ctx.lineWidth = m_instrumentCss[0].width;
2579
- var width = Math.round(m_chartspaces.lowerChart.width / (m_datapoints.length + 1) / 2);
2580
- if (width < 2) width = 2;
2581
- else if (width > 20) width = 20;
2582
- m_ctx.lineWidth = width;
2583
- for (var i = 0; i < m_datapoints.length; i++) {
2584
- var x = (m_datapoints[i].x + 0.5) * window.devicePixelRatio;
2585
- m_ctx.lineWidth = width;
2586
- if (x - width <= m_chartspaces.lowerChart.left) {
2587
- m_ctx.lineWidth = width / 2;
2588
- x += m_ctx.lineWidth / 2;
2589
- } else
2590
- if (x + width >= m_chartspaces.lowerChart.right) {
2591
- m_ctx.lineWidth = width / 2;
2592
- x -= m_ctx.lineWidth / 2 + 1;
2593
- }
2594
- m_ctx.beginPath();
2595
- m_ctx.moveTo(x, m_chartspaces.lowerChart.bottom - 0.5);
2596
- var y = Math.round(m_chartspaces.lowerChart.bottom - (m_datapoints[i].quantity * valuePerPixel)) - 1;
2597
- m_ctx.closePath();
2598
- m_ctx.lineTo(x, y - 0.5);
2599
- m_ctx.stroke();
2600
- }
2601
- m_ctx.restore();
2602
3132
  }
2603
3133
 
2604
- function plotData(data, instrument) {
3134
+ function drawOHLC(data,factor,scaleinfoY,x) {
3135
+ let offset = 3;
3136
+ let y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((data.openprice * factor) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
3137
+ m_ctx.moveTo(x-offset, y);
3138
+ m_ctx.lineTo(x , y);
3139
+ y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((data.highprice * factor) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
3140
+ m_ctx.moveTo(x, y);
3141
+ y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((data.lowprice * factor) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
3142
+ m_ctx.lineTo(x, y);
3143
+ y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((data.price * factor) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
3144
+ m_ctx.moveTo(x, y);
3145
+ m_ctx.lineTo(x + offset, y);
3146
+
3147
+ }
3148
+ function plotData(data, instrument, scaleinfoY) {
2605
3149
  m_ctx.save();
2606
3150
  if (_this.settings.curveOnTop == false)
2607
3151
  m_ctx.globalCompositeOperation = 'destination-over'; // dont draw over labels inside the chart
2608
- m_ctx.strokeStyle = m_instrumentCss[instrument].color;
3152
+ let diff = _this.instruments[0].endValue - _this.instruments[0].startValue;
3153
+
3154
+ if(instrument == 0 && typeof m_instrumentCss[instrument].positiveColor !== 'undefined' && typeof m_instrumentCss[instrument].negativeColor !== 'undefined' ) {
3155
+ if(diff < 0) m_ctx.strokeStyle = m_instrumentCss[instrument].negativeColor;
3156
+ else if(diff > 0) m_ctx.strokeStyle = m_instrumentCss[instrument].positiveColor;
3157
+ else m_ctx.strokeStyle = m_instrumentCss[instrument].color;
3158
+ } else
3159
+ m_ctx.strokeStyle = m_instrumentCss[instrument].color;
2609
3160
  var factor = _this.instruments[instrument].factor;
2610
3161
  var startpoint = { x: 0, y: 0 };
2611
3162
  var endpoint = { x: 0, y: 0 };
2612
3163
  var startDate = _this.scaleinfoX.startTimeStamp;
2613
3164
  var len = data.length;
2614
- m_ctx.lineWidth = m_instrumentCss[instrument].width / window.devicePixelRatio;
3165
+ m_ctx.lineWidth = m_instrumentCss[instrument].width / 1;
2615
3166
  m_ctx.beginPath();
2616
3167
  var startx = 0;
2617
3168
  var starty = 0;
@@ -2622,9 +3173,10 @@ function Milli_Chart(settings) {
2622
3173
  var hCurveLastPoint = null;
2623
3174
  var quantity = 0;
2624
3175
  var nextDate;
3176
+ var firstDataPoint = null;
3177
+ var lastDataPoint = null;
2625
3178
  for (var i = 0; i < len; i++) {
2626
3179
  var currentDate = new Date(data[i].timestamp);
2627
- // var lastItem = data[i];
2628
3180
  var tmpx = startpoint.x;
2629
3181
  var tmp;
2630
3182
  if (data[i].timestamp < _this.scaleinfoX.startTimeStamp) {
@@ -2637,44 +3189,42 @@ function Milli_Chart(settings) {
2637
3189
  if (data[i].timestamp > new Date().getTime())
2638
3190
  break;
2639
3191
 
2640
- if (_this.scaleinfoY.type != 'history' && (data[i].timestamp % 86400000 < _this.instruments[0].opentimestamp || data[i].timestamp % 86400000 > _this.instruments[0].closetimestamp)) {
3192
+ if (chartType != 'history' && (data[i].timestamp % 86400000 < _this.instruments[0].opentimestamp || data[i].timestamp % 86400000 > _this.instruments[0].closetimestamp)) {
2641
3193
  // stämmer detta kan det bli överlapp vid sommartid/vintertid?
2642
3194
  continue;
2643
3195
  }
2644
3196
  var endtimeToday = new Date(data[i].timestamp);
2645
3197
  if (endtimeToday.getDay() == 0 || endtimeToday.getDay() == 6) continue; // do not draw weekends TODO: if main instrument has weekenddata draw it, but need to fix that in all drawfunctions
2646
- if (_this.scaleinfoY.type != 'history') {
3198
+ if (chartType != 'history') {
2647
3199
  endtimeToday = new Date(endtimeToday.toISOString().substring(0, 10) + 'T' + _this.instruments[0].marketclose + 'Z'); // borde räcka att göra 1 gång när det blir nytt datum
2648
3200
  }
2649
3201
  if (data[i].timestamp > endtimeToday.getTime()) {
2650
3202
  continue; // dataticks efter stängning ritas inte
2651
3203
  }
2652
3204
 
2653
- if (_this.scaleinfoY.type == 'history') {
2654
- //var point = { price: data[i].price, open: null, x: endpoint.x - 0.5, y: endpoint.y - 0.5, timestamp: data[i].timestamp };
2655
- //m_datapoints.push(point);
2656
- } else
2657
- if (_this.settings.previousDayClose && addedcloseprice1d == false && m_zoom.mouseup.timestamp == null) { // only draw closeprice1d on today charts
3205
+ if (chartType != 'history' && _this.settings.previousDayClose && addedcloseprice1d == false && m_zoom.mouseup.timestamp == null && instrument == 0) { // only draw closeprice1d on today charts
2658
3206
  currentDate = new Date(startDate);
2659
3207
  if (_this.settings.chartlen == '1d' || _this.settings.chartlen == '0d') { // plot the closeprice1d
2660
- endpoint.y = Math.round(m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) - (((parseFloat(_this.instruments[instrument].closeprice1d)) - _this.scaleinfoY.minValue) * _this.scaleinfoY.valuePerPixel)) + 0.5;
3208
+ endpoint.y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((parseFloat(_this.instruments[instrument].closeprice1d * _this.instruments[instrument].factor)) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
2661
3209
  endpoint.x = m_chartspaces.chart.left + 0.5 - offset;
2662
3210
  } else {
2663
- if (!isToday(new Date(data[i].timestamp))) {
2664
- nextDate = new Date(data[i].timestamp);
2665
- while (dateDiffInDays(currentDate, nextDate) > 0) {
2666
- if (currentDate.getDay() == 0 || currentDate.getDay() == 6)
2667
- offset += 86400000 / _this.scaleinfoX.timePerPixel;
2668
- else
2669
- offset += ((86400000 - _this.scaleinfoX.milliPerDay) / _this.scaleinfoX.timePerPixel); // * dateDiffInDays(lastdate, currentDate);
2670
- currentDate = new Date(currentDate.getTime() + 86400000);
2671
- }
2672
- tmp = new Date(currentDate.toISOString().substring(0, 10) + 'T' + _this.instruments[0].marketopen + 'Z');
2673
- endpoint.y = Math.round(m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) - (((parseFloat(_this.instruments[instrument].startValue)) - _this.scaleinfoY.minValue) * _this.scaleinfoY.valuePerPixel)) + 0.5;
2674
- endpoint.x = Math.round(m_chartspaces.chart.left + ((tmp.getTime() - startDate) / _this.scaleinfoX.timePerPixel)) + 0.5 - offset;
3211
+ //if (!isToday(new Date(data[i].timestamp))) { // remove, seems to mess with 2day charts if closed on friday
3212
+ nextDate = new Date(data[i].timestamp);
3213
+ while (dateDiffInDays(currentDate, nextDate) > 0) {
3214
+ if (currentDate.getDay() == 0 || currentDate.getDay() == 6)
3215
+ offset += 86400000 / _this.scaleinfoX.timePerPixel;
3216
+ else
3217
+ offset += ((86400000 - _this.scaleinfoX.milliPerDay) / _this.scaleinfoX.timePerPixel);
3218
+ currentDate = new Date(currentDate.getTime() + 86400000);
2675
3219
  }
3220
+ tmp = new Date(currentDate.toISOString().substring(0, 10) + 'T' + _this.instruments[0].marketopen + 'Z');
3221
+ endpoint.y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((parseFloat(_this.instruments[instrument].startValue * _this.instruments[instrument].factor)) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
3222
+ endpoint.x = Math.round(m_chartspaces.chart.left + ((tmp.getTime() - startDate) / _this.scaleinfoX.timePerPixel)) + 0.5 - offset;
3223
+ //}
2676
3224
  }
2677
- m_ctx.moveTo(endpoint.x, endpoint.y);
3225
+ if(_this.settings.type == 'ohlc') drawOHLC(data[i],factor,scaleinfoY,endpoint.x);
3226
+ else if(_this.settings.type == 'candlestick') drawCandlestick(data[i],factor,scaleinfoY,endpoint.x);
3227
+ else m_ctx.moveTo(endpoint.x, endpoint.y);
2678
3228
  lastdate = new Date(data[i].timestamp);
2679
3229
  addedcloseprice1d = true;
2680
3230
  startx = endpoint.x;
@@ -2702,39 +3252,59 @@ function Milli_Chart(settings) {
2702
3252
  startpoint.x = Math.round(m_chartspaces.chart.left + ((data[i].timestamp - startDate) / _this.scaleinfoX.timePerPixel)) + 0.5 - offset;
2703
3253
 
2704
3254
  // TODO: här blir det fel när det är från 00:00: 23:59 men göms av tmpx < startpoint.x
2705
- if (_this.scaleinfoY.type == 'trades' && tmpx < startpoint.x) {
3255
+ if (chartType == 'trades' && tmpx < startpoint.x) {
2706
3256
  m_ctx.lineTo(startpoint.x, startpoint.y);
2707
3257
  }
2708
3258
  }
2709
- startpoint.y = Math.round(m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) - (((data[i].price * factor) - _this.scaleinfoY.minValue) * _this.scaleinfoY.valuePerPixel)) + 0.5;
3259
+ startpoint.y = Math.round(m_chartspaces.chart.height - m_chartspaces.chart.marginBottom - (((data[i].price * factor) - scaleinfoY.minValue) / scaleinfoY.valuePerPixel)) + 0.5;
3260
+
2710
3261
  maxy = maxy > startpoint.y ? maxy : startpoint.y;
2711
3262
 
2712
3263
  startpoint.x = Math.round(m_chartspaces.chart.left + ((data[i].timestamp - _this.scaleinfoX.startDate.getTime()) / _this.scaleinfoX.timePerPixel)) + 0.5;
2713
3264
 
2714
3265
  startpoint.x -= offset;
2715
3266
  quantity += data[i].quantity;
3267
+
3268
+ // test
3269
+ var x = Math.floor(startpoint.x);
3270
+ if (!m_dataPoints.arr.includes(x)) {
3271
+ m_dataPoints.arr.push(x);
3272
+ }
3273
+ var obj = m_dataPoints.map.get(x);
3274
+ if (obj == null) {
3275
+ obj = {};
3276
+ obj.timestamp = data[i].timestamp;
3277
+ obj.instruments = [];
3278
+ m_dataPoints.map.set(x, obj);
3279
+ }
3280
+ obj.instruments[instrument] = { price: data[i].price, open: data[i].openprice, x: startpoint.x / 1 - 0.5, y: startpoint.y / 1 - 0.5, timestamp: data[i].timestamp, date: new Date(data[i].timestamp), insref: _this.instruments[instrument].insref, diff: data[i].diff, quantity: quantity };
3281
+
2716
3282
  if (startpoint.x != endpoint.x || startpoint.y != endpoint.y) {
2717
- var x = Math.round(startpoint.x);
3283
+ /*var x = Math.round (startpoint.x);
2718
3284
  if (!m_dataPoints.arr.includes(x)) {
2719
3285
  m_dataPoints.arr.push(x);
2720
- }
2721
- var obj = m_dataPoints.map.get(x);
3286
+ }*/
3287
+ /*var obj = m_dataPoints.map.get(x);
2722
3288
  if (obj == null) {
2723
3289
  obj = {};
2724
3290
  obj.timestamp = data[i].timestamp;
2725
3291
  obj.instruments = [];
2726
3292
  m_dataPoints.map.set(x, obj);
2727
3293
  }
2728
- obj.instruments[instrument] = { price: data[i].price, open: data[i].openprice, x: startpoint.x / window.devicePixelRatio - 0.5, y: startpoint.y / window.devicePixelRatio - 0.5, timestamp: data[i].timestamp, date: new Date(data[i].timestamp), insref: _this.instruments[instrument].insref, diff: data[i].diff };
3294
+ obj.instruments[instrument] = { price: data[i].price, open: data[i].openprice, x: startpoint.x / 1 - 0.5, y: startpoint.y / 1 - 0.5, timestamp: data[i].timestamp, date: new Date(data[i].timestamp), insref: _this.instruments[instrument].insref, diff: data[i].diff, quantity: quantity };*/
2729
3295
  var point;
2730
3296
  if (endpoint.x != 0) {
2731
3297
  if (tmpx < startpoint.x) {
2732
- if (_this.settings.hcurve) m_ctx.lineTo(startpoint.x, endpoint.y);
2733
- m_ctx.lineTo(startpoint.x, startpoint.y);
3298
+ if(_this.settings.type == 'ohlc') drawOHLC(data[i],factor,scaleinfoY,startpoint.x);
3299
+ else if(_this.settings.type == 'candlestick') drawCandlestick(data[i],factor,scaleinfoY,startpoint.x);
3300
+ else {
3301
+ if (_this.settings.hcurve) m_ctx.lineTo(startpoint.x, endpoint.y);
3302
+ m_ctx.lineTo(startpoint.x, startpoint.y);
3303
+ }
2734
3304
  //m_ctx.bezierCurveTo(startpoint.x, startpoint.y, endpoint.x - 1, endpoint.y - 1, endpoint.x, endpoint.y); // läs på om detta för "runda linjer"
2735
3305
 
2736
3306
  if (instrument == 0) {
2737
- point = { price: data[i].price, open: data[i].openprice, x: startpoint.x / window.devicePixelRatio - 0.5, y: startpoint.y / window.devicePixelRatio - 0.5, timestamp: data[i].timestamp, date: new Date(data[i].timestamp), quantity: quantity };
3307
+ point = { price: data[i].price, open: data[i].openprice, x: startpoint.x / 1 - 0.5, y: startpoint.y / 1 - 0.5, timestamp: data[i].timestamp, date: new Date(data[i].timestamp), quantity: quantity };
2738
3308
  if (typeof data[i].dividend !== 'undefined') point.dividend = data[i].dividend;
2739
3309
  if (typeof data[i].diff !== 'undefined') point.diff = data[i].diff;
2740
3310
  point.quantity = quantity;
@@ -2744,55 +3314,78 @@ function Milli_Chart(settings) {
2744
3314
  }
2745
3315
  } else {
2746
3316
  if (instrument == 0) {
2747
- point = { price: data[i].price, open: data[i].openprice, x: startpoint.x / window.devicePixelRatio - 0.5, y: startpoint.y / window.devicePixelRatio - 0.5, timestamp: data[i].timestamp, date: new Date(data[i].timestamp), insref: _this.instruments[instrument].insref, diff: data[i].diff, quantity: quantity };
3317
+ point = { price: data[i].price, open: data[i].openprice, x: startpoint.x / 1 - 0.5, y: startpoint.y / 1 - 0.5, timestamp: data[i].timestamp, date: new Date(data[i].timestamp), insref: _this.instruments[instrument].insref, diff: data[i].diff, quantity: quantity };
2748
3318
  m_datapoints.push(point);
2749
- if (_this.settings.hcurve && _this.scaleinfoY.type == 'history') {
3319
+ if (_this.settings.hcurve && chartType == 'history') {
2750
3320
  if (isToday(currentDate)) {
2751
3321
  // only 1 point in hcurve chart, draw line from start of chart to end date
2752
3322
  m_ctx.moveTo(m_chartspaces.chart.left, startpoint.y);
2753
- point = { price: data[i].price, open: data[i].openprice, x: m_chartspaces.chart.left / window.devicePixelRatio - 0.5, y: startpoint.y / window.devicePixelRatio - 0.5, timestamp: data[i].timestamp, date: new Date(data[i].timestamp), insref: _this.instruments[instrument].insref, diff: data[i].diff, quantity: quantity };
3323
+ point = { price: data[i].price, open: data[i].openprice, x: m_chartspaces.chart.left / 1 - 0.5, y: startpoint.y / 1 - 0.5, timestamp: data[i].timestamp, date: new Date(data[i].timestamp), insref: _this.instruments[instrument].insref, diff: data[i].diff, quantity: quantity };
2754
3324
  m_datapoints.push(point);
2755
- point = { price: data[i].price, open: data[i].openprice, x: startpoint.x / window.devicePixelRatio - 0.5, y: startpoint.y / window.devicePixelRatio - 0.5, timestamp: data[i].timestamp, date: new Date(data[i].timestamp), insref: _this.instruments[instrument].insref, diff: data[i].diff, quantity: quantity };
3325
+ point = { price: data[i].price, open: data[i].openprice, x: startpoint.x / 1 - 0.5, y: startpoint.y / 1 - 0.5, timestamp: data[i].timestamp, date: new Date(data[i].timestamp), insref: _this.instruments[instrument].insref, diff: data[i].diff, quantity: quantity };
2756
3326
  m_datapoints.push(point);
2757
3327
  startx = m_chartspaces.chart.left;
2758
3328
  starty = startpoint.y;
2759
3329
  // last point so break out and store startx and starty from fake point
2760
3330
  break;
2761
3331
  } else if (hCurveLastPoint) {
2762
- /* var y = Math.round(m_canvas.height - getScaledSetting(m_chartCss.marginBottom) - (((hCurveLastPoint.price * factor) - _this.scaleinfoY.minValue) * _this.scaleinfoY.valuePerPixel)) + 0.5;
3332
+ /* var y = Math.round(m_canvas.getHeight() - m_chartspaces.chart.marginBottom - (((hCurveLastPoint.price * factor) - scaleinfoY.minValue) * scaleinfoY.valuePerPixel)) + 0.5;
2763
3333
  m_ctx.moveTo(m_chartspaces.chart.left, y);
2764
3334
  m_ctx.lineTo(startpoint.x, y);*/
2765
3335
  }
2766
3336
  }
2767
3337
  quantity = 0;
2768
3338
  }
2769
- m_ctx.moveTo(startpoint.x, startpoint.y);
3339
+ if(_this.settings.type == 'ohlc') drawOHLC(data[i],factor,scaleinfoY,startpoint.x);
3340
+ else if(_this.settings.type == 'candlestick') drawCandlestick(data[i],factor,scaleinfoY,startpoint.x);
3341
+ else m_ctx.moveTo(startpoint.x, startpoint.y);
2770
3342
  startx = startpoint.x;
2771
3343
  starty = startpoint.y;
2772
3344
  }
2773
3345
  endpoint.x = startpoint.x;
2774
3346
  endpoint.y = startpoint.y;
2775
3347
  }
2776
- if (data[i].dividend) {
2777
- //console.log('div', data[i]);
2778
- }
3348
+ if (data[i].dividend) {}
2779
3349
  }
2780
3350
  m_dataPoints.arr.sort(function(a, b) {
2781
3351
  return a - b;
2782
3352
  });
2783
3353
  m_ctx.stroke();
2784
- if (_this.settings.fillchart == true && instrument == 0) {
2785
- m_ctx.lineTo(startpoint.x, m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) + 0.5);
2786
- m_ctx.lineTo(startx, m_chartspaces.chart.height - getScaledSetting(m_chartCss.marginBottom) + 0.5);
3354
+ if (_this.settings.fillchart == true && instrument == 0 && _this.settings.ohlc != true) {
3355
+ m_ctx.lineTo(startpoint.x, m_chartspaces.chart.height - m_chartspaces.chart.marginBottom + 0.5);
3356
+ m_ctx.lineTo(startx, m_chartspaces.chart.height - m_chartspaces.chart.marginBottom + 0.5);
2787
3357
  m_ctx.lineTo(startx, starty);
2788
3358
  m_ctx.closePath();
2789
3359
  if (typeof m_instrumentCss[0].backgroundLinearGradient !== 'undefined' && typeof m_instrumentCss[0].backgroundLinearGradient.topColor !== 'undefined' && typeof m_instrumentCss[0].backgroundLinearGradient.bottomColor !== 'undefined') {
2790
- var grd = m_ctx.createLinearGradient(0, 0, 0, m_chartspaces.chart.height - m_chartspaces.chart.top - getScaledSetting(m_chartCss.marginBottom));
2791
- grd.addColorStop(0, m_instrumentCss[0].backgroundLinearGradient.topColor);
2792
- grd.addColorStop(1, m_instrumentCss[0].backgroundLinearGradient.bottomColor);
2793
- m_ctx.fillStyle = grd;
3360
+ if(instrument == 0 && typeof m_instrumentCss[0].positiveBackgroundLinearGradient !== 'undefined' && typeof m_instrumentCss[0].negativeBackgroundLinearGradient !== 'undefined' ) {
3361
+ if(diff < 0 && typeof m_instrumentCss[0].negativeBackgroundLinearGradient.topColor !== 'undefined' && typeof m_instrumentCss[0].negativeBackgroundLinearGradient.bottomColor !== 'undefined') {
3362
+ var grd = m_ctx.createLinearGradient(0, 0, 0, m_chartspaces.chart.height - m_chartspaces.chart.top - m_chartspaces.chart.marginBottom);
3363
+ grd.addColorStop(0, m_instrumentCss[0].negativeBackgroundLinearGradient.topColor);
3364
+ grd.addColorStop(1, m_instrumentCss[0].negativeBackgroundLinearGradient.bottomColor);
3365
+ m_ctx.fillStyle = grd;
3366
+ } else if(diff > 0) {
3367
+ var grd = m_ctx.createLinearGradient(0, 0, 0, m_chartspaces.chart.height - m_chartspaces.chart.top - m_chartspaces.chart.marginBottom);
3368
+ grd.addColorStop(0, m_instrumentCss[0].positiveBackgroundLinearGradient.topColor);
3369
+ grd.addColorStop(1, m_instrumentCss[0].positiveBackgroundLinearGradient.bottomColor);
3370
+ m_ctx.fillStyle = grd;
3371
+ } else {
3372
+ var grd = m_ctx.createLinearGradient(0, 0, 0, m_chartspaces.chart.height - m_chartspaces.chart.top - m_chartspaces.chart.marginBottom);
3373
+ grd.addColorStop(0, m_instrumentCss[0].backgroundLinearGradient.topColor);
3374
+ grd.addColorStop(1, m_instrumentCss[0].backgroundLinearGradient.bottomColor);
3375
+ m_ctx.fillStyle = grd;
3376
+ }
3377
+ } else {
3378
+ var grd = m_ctx.createLinearGradient(0, 0, 0, m_chartspaces.chart.height - m_chartspaces.chart.top - m_chartspaces.chart.marginBottom);
3379
+ grd.addColorStop(0, m_instrumentCss[0].backgroundLinearGradient.topColor);
3380
+ grd.addColorStop(1, m_instrumentCss[0].backgroundLinearGradient.bottomColor);
3381
+ m_ctx.fillStyle = grd;
3382
+ }
2794
3383
  } else {
2795
- if (typeof m_instrumentCss[0].backgroundColor !== 'undefined')
3384
+ if(instrument == 0 && typeof m_instrumentCss[instrument].positiveBackgroundColor !== 'undefined' && typeof m_instrumentCss[instrument].negativeBackgroundColor !== 'undefined' ) {
3385
+ if(diff < 0) m_ctx.fillStyle = m_instrumentCss[0].negativeBackgroundColor;
3386
+ else if(diff > 0) m_ctx.fillStyle = m_instrumentCss[0].positiveBackgroundColor;
3387
+ else if (typeof m_instrumentCss[0].backgroundColor !== 'undefined') m_ctx.fillStyle = m_instrumentCss[0].backgroundColor;
3388
+ } else if (typeof m_instrumentCss[0].backgroundColor !== 'undefined')
2796
3389
  m_ctx.fillStyle = m_instrumentCss[0].backgroundColor;
2797
3390
  }
2798
3391
  m_ctx.fill();
@@ -2807,61 +3400,161 @@ function Milli_Chart(settings) {
2807
3400
  m_ctx.restore();
2808
3401
  }
2809
3402
 
2810
- /* function drawCompare(resp) {
2811
- parseData(resp[0], 1);
2812
- _this.drawChart();
2813
- requestStreaming();
2814
- return;
2815
- }
2816
- */
2817
-
2818
3403
  _this.setChartLength = function(len) {
2819
3404
  _this.settings.chartlen = len;
2820
3405
  m_zoom.mousedown.timestamp = null;
2821
3406
  m_zoom.mouseup.timestamp = null;
2822
3407
  };
2823
3408
 
2824
- function bollingerBands(prices, window, stddev) {
3409
+ function calculateMomentum(data, len) {
3410
+ // borde returnera ett object med high/low också
3411
+ var values = [];
3412
+ for (var i = len; i < data.length; i++) {
3413
+ if (data[i].timestamp >= _this.scaleinfoX.startTimeStamp) {
3414
+ values.push({ timestamp: data[i].timestamp, datapoints: [data[i].price - data[i - len].price] });
3415
+ }
3416
+ }
3417
+ return values;
3418
+
3419
+ }
3420
+
3421
+ function calculateQuantity(data) {
3422
+ // borde returnera ett object med high/low också
3423
+ let quantity = [];
3424
+ for (let i = 0; i < data.length; i++) {
3425
+ if (data[i].timestamp >= _this.scaleinfoX.startTimeStamp) {
3426
+ quantity.push({ timestamp: data[i].timestamp, datapoints: [data[i].quantity] });
3427
+ }
3428
+ }
3429
+ return quantity;
3430
+ }
3431
+
3432
+ function calculateNews(data) {
3433
+ if (data == null) return [];
3434
+
3435
+ let timeseries = [];
3436
+ for (let i = 0; i < data.length; i++) {
3437
+ let s = 0;
3438
+ if (data[i].timestamp >= _this.scaleinfoX.startTimeStamp) {
3439
+ let item = null;
3440
+ for (s; s < _this.instruments[0][chartType].length; s++) {
3441
+ if (chartType == 'history') {
3442
+ if ((data[i].timestamp - (data[i].timestamp % 86400000)) < _this.instruments[0].history[s].timestamp) break;
3443
+ if ((data[i].timestamp - (data[i].timestamp % 86400000)) >= _this.instruments[0].history[s].timestamp) {
3444
+ item = data[i];
3445
+ item.datapoints = [_this.instruments[0].history[s].price];
3446
+ }
3447
+ } else {
3448
+ if ((data[i].timestamp) < _this.instruments[0].trades[s].timestamp) break;
3449
+ if ((data[i].timestamp) >= _this.instruments[0].trades[s].timestamp) {
3450
+ item = data[i];
3451
+ item.datapoints = [_this.instruments[0].trades[s].price];
3452
+ }
3453
+ }
3454
+ }
3455
+ timeseries.push(item);
3456
+ }
3457
+ }
3458
+ return timeseries;
3459
+ }
3460
+
3461
+ function calculateRSI(p, window) {
3462
+ // borde returnera ett object med high/low också
3463
+ let rsi = [];
3464
+ let up;
3465
+ let up_average;
3466
+ let up_average_old = 0;
3467
+ let sum_up = 0;
3468
+
3469
+ let down;
3470
+ let down_average;
3471
+ let sum_down = 0;
3472
+ let down_average_old = 0;
3473
+
3474
+ for (let i = 1; i < p.length; i++) {
3475
+ let change = p[i].price - p[i - 1].price;
3476
+ if (change > 0) {
3477
+ up = change;
3478
+ down = 0;
3479
+ } else if (change < 0) {
3480
+ down = Math.abs(change);
3481
+ up = 0;
3482
+ } else {
3483
+ up = 0;
3484
+ down = 0;
3485
+ }
3486
+ if (i < window) {
3487
+ sum_up += up;
3488
+ sum_down += down
3489
+ } else if (i == window) {
3490
+ up_average = sum_up / window;
3491
+ down_average = sum_down / window;
3492
+ up_average_old = up_average;
3493
+ down_average_old = down_average
3494
+ } else {
3495
+ up_average = (up_average_old * (window - 1) + up) / window;
3496
+ down_average = (down_average_old * (window - 1) + down) / window;
3497
+ up_average_old = up_average;
3498
+ down_average_old = down_average
3499
+ }
3500
+ if (i >= window) {
3501
+ var rs = up_average / down_average;
3502
+ rsi.push({ timestamp: p[i].timestamp, datapoints: [100 - 100 / (1 + rs)] });
3503
+ }
3504
+ }
3505
+ return rsi;
3506
+ }
3507
+
3508
+ function calculateBollingerBands(prices, window, stddev) {
3509
+ // borde returnera ett object med high/low också
3510
+
2825
3511
  if (!prices || prices.length < window) {
2826
3512
  return [];
2827
3513
  }
2828
-
2829
3514
  let index = window - 1;
2830
3515
  const length = prices.length + 1;
2831
3516
  const standardDeviations = [];
3517
+ const today = new Date().getTime();
3518
+ const startDate = _this.scaleinfoX.startTimeStamp - (window * 86400000);
2832
3519
  while (++index < length) {
2833
3520
  const windowSlice = prices.slice(index - window, index);
2834
- const mean = windowSlice.reduce((prev, curr) => prev + curr.price, 0) / window;
2835
- const variance = Math.sqrt(windowSlice.reduce((a, b) => a + (b.price - mean) ** 2, 0) / window) * stddev;
2836
- const uppervariance = mean + variance;
2837
- const lowervariance = mean - variance;
2838
- standardDeviations.push({ timestamp: prices[index - 1].timestamp, datapoints: [uppervariance, lowervariance, mean] });
2839
- if (typeof prices[index] == 'undefined' || prices[index].timestamp > new Date().getTime()) break;
3521
+ if (windowSlice[window - 1].timestamp > startDate) {
3522
+ const mean = windowSlice.reduce((prev, curr) => prev + curr.price, 0) / window;
3523
+ const variance = Math.sqrt(windowSlice.reduce((a, b) => a + (b.price - mean) ** 2, 0) / window) * stddev;
3524
+ const uppervariance = mean + variance;
3525
+ const lowervariance = mean - variance;
3526
+ standardDeviations.push({ timestamp: prices[index - 1].timestamp, datapoints: [uppervariance, lowervariance, mean] });
3527
+ }
3528
+ if (typeof prices[index] == 'undefined' || prices[index].timestamp > today) break;
2840
3529
  }
2841
3530
  return standardDeviations;
2842
3531
  }
2843
3532
 
2844
3533
  function simpleMovingAverage(prices, window, n = Infinity) {
3534
+ // borde returnera ett object med high/low också
2845
3535
  if (!prices || prices.length < window) {
2846
3536
  return [];
2847
3537
  }
2848
3538
  let index = window - 1;
2849
3539
  const length = prices.length + 1;
2850
-
2851
3540
  const simpleMovingAverages = [];
2852
-
2853
3541
  let numberOfSMAsCalculated = 0;
2854
-
2855
- while (++index < length && numberOfSMAsCalculated++ < n) {
3542
+ const today = new Date().getTime();
3543
+ const startDate = _this.scaleinfoX.startTimeStamp - (window * 86400000);
3544
+ while (++index < length && numberOfSMAsCalculated < n) {
2856
3545
  const windowSlice = prices.slice(index - window, index);
2857
- const sum = windowSlice.reduce((prev, curr) => prev + curr.price, 0);
2858
- simpleMovingAverages.push({ timestamp: prices[index - 1].timestamp, datapoints: [sum / window] });
2859
- if (typeof prices[index] == 'undefined' || prices[index].timestamp > new Date().getTime()) break;
3546
+ if (windowSlice[window - 1].timestamp > startDate) {
3547
+ const sum = windowSlice.reduce((prev, curr) => prev + curr.price, 0);
3548
+ simpleMovingAverages.push({ timestamp: prices[index - 1].timestamp, datapoints: [sum / window] });
3549
+ numberOfSMAsCalculated++;
3550
+ }
3551
+ if (typeof prices[index] == 'undefined' || prices[index].timestamp > today) break;
2860
3552
  }
2861
3553
  return simpleMovingAverages;
2862
3554
  }
2863
3555
 
2864
3556
  function exponentialMovingAverage(prices, window) {
3557
+ // borde returnera ett object med high/low också
2865
3558
  if (!prices || prices.length < window) {
2866
3559
  return [];
2867
3560
  }
@@ -2873,15 +3566,16 @@ function Milli_Chart(settings) {
2873
3566
 
2874
3567
  const [sma] = simpleMovingAverage(prices, window, 1);
2875
3568
  exponentialMovingAverages.push(sma);
3569
+
2876
3570
  while (++index < length) {
2877
3571
  const value = prices[index].price;
2878
3572
  const previousEma = [exponentialMovingAverages[previousEmaIndex++].datapoints[0]];
2879
3573
  const currentEma = (value * smoothingFactor) + (previousEma * (1 - smoothingFactor));
2880
3574
  exponentialMovingAverages.push({ timestamp: prices[index].timestamp, datapoints: [currentEma] });
2881
- if (typeof prices[index] == 'undefined' || prices[index].timestamp > new Date().getTime()) break;
2882
3575
  }
2883
3576
  return exponentialMovingAverages;
2884
3577
  }
3578
+
2885
3579
  _this.removeAllIndicators = function(j) {
2886
3580
  _this.settings.indicators = [];
2887
3581
  _this.drawChart();
@@ -2893,7 +3587,16 @@ function Milli_Chart(settings) {
2893
3587
  for (var i = 0; i < _this.settings.indicators.length; i++) {
2894
3588
  if (_this.settings.indicators[i].method == obj.method && _this.settings.indicators[i].method_length == obj.method_length) {
2895
3589
  if (obj.method != 'bb' || obj.stddev == _this.settings.indicators[i].stddev) {
3590
+ if (typeof _this.settings.indicators[i].onClose === 'function') _this.settings.indicators[i].onClose.call();
2896
3591
  _this.settings.indicators.splice(i, 1);
3592
+ let lower = false;
3593
+ for (let s = 0; s < _this.settings.indicators.length; s++) {
3594
+ if (_this.settings.indicators[s].method == 'rsi' || _this.settings.indicators[s].method == 'quantity') lower = true;
3595
+ }
3596
+ if (lower == false) {
3597
+ m_chartspaces.chart.percent = 100;
3598
+ m_chartspaces.lowerChart.percent = 0;
3599
+ }
2897
3600
  _this.drawChart();
2898
3601
  return true;
2899
3602
  }
@@ -2902,39 +3605,55 @@ function Milli_Chart(settings) {
2902
3605
  return false;
2903
3606
  };
2904
3607
 
3608
+ function indicatorAlreadyExists(obj) {
3609
+ for (var i = 0; i < _this.settings.indicators.length; i++) {
3610
+ if (_this.settings.indicators[i].method == obj.method && obj.method == 'quantity') return true;
3611
+ if (_this.settings.indicators[i].method == obj.method && _this.settings.indicators[i].method_length == obj.method_length) {
3612
+ if (obj.method != 'bb' || obj.stddev == _this.settings.indicators[i].stddev) {
3613
+ return true;
3614
+ }
3615
+ }
3616
+ }
3617
+ return false;
3618
+ }
3619
+
2905
3620
  _this.addIndicator = function(method) {
2906
- if (typeof method !== 'object' || method == null || typeof method.type == undefined) return;
3621
+ if (typeof method !== 'object' || method == null || typeof method.method == undefined) return;
3622
+ if (indicatorAlreadyExists(method)) return;
2907
3623
  switch (method.method) {
2908
3624
  case 'sma':
2909
3625
  {
2910
3626
  if (typeof method.method_length !== 'number') return;
2911
- method.history = simpleMovingAverage(_this.instruments[0].history, method.method_length);
2912
- method.trades = simpleMovingAverage(_this.instruments[0].trades, method.method_length);
2913
3627
  }
2914
3628
  break;
2915
3629
  case 'ema':
2916
3630
  {
2917
3631
  if (typeof method.method_length !== 'number') return;
2918
- method.history = exponentialMovingAverage(_this.instruments[0].history, method.method_length);
2919
- method.trades = exponentialMovingAverage(_this.instruments[0].trades, method.method_length);
2920
3632
  break;
2921
3633
  }
2922
3634
  case 'bb':
2923
3635
  {
2924
3636
  if (typeof method.method_length !== 'number' || typeof method.stddev !== 'number') return;
2925
- method.history = bollingerBands(_this.instruments[0].history, method.method_length, method.stddev);
2926
- method.trades = bollingerBands(_this.instruments[0].trades, method.method_length, method.stddev);
2927
3637
  break;
2928
3638
  }
2929
- default:
2930
- if (typeof method.history !== 'undefined' && typeof method.trades !== 'undefined') {
2931
- if (!Array.isArray(method.history) || !Array.isArray(method.trades)) {
2932
- return -1;
2933
- }
3639
+ case 'rsi':
3640
+ case 'quantity':
3641
+ case 'momentum':
3642
+ if (method.target == 'lower') {
3643
+ m_chartspaces.chart.percent = 70;
3644
+ m_chartspaces.lowerChart.percent = 30;
2934
3645
  }
2935
3646
  break;
3647
+ case 'news':
3648
+ break;
3649
+ default:
3650
+ method.timeseries.sort(function(a, b) {
3651
+ return a.timestamp - b.timestamp;
3652
+ });
3653
+ break;
2936
3654
  }
2937
- _this.settings.indicators.push(method);
3655
+ if (typeof method.type === 'undefined') method.type = INDICATOR;
3656
+ _this.settings.indicators.unshift(method);
2938
3657
  _this.drawChart();
2939
3658
  };
2940
3659
 
@@ -2959,6 +3678,7 @@ function Milli_Chart(settings) {
2959
3678
  });
2960
3679
  //fetch data and redraw ( for no push charts)
2961
3680
  };
3681
+
2962
3682
  // Compare functions
2963
3683
  _this.removeAllCompares = function() {
2964
3684
  if (_this.instruments.length == 1) return;
@@ -3011,6 +3731,7 @@ function Milli_Chart(settings) {
3011
3731
  return 1;
3012
3732
  };
3013
3733
  // Size functions
3734
+
3014
3735
  function setChartSize() {
3015
3736
  var offset = 0;
3016
3737
  // set canvas size to 0 so it does not prevent the div to resize
@@ -3021,35 +3742,35 @@ function Milli_Chart(settings) {
3021
3742
  }
3022
3743
  _this.settings.target.style.height = (_this.settings.target.parentNode.offsetHeight - offset) + 'px';
3023
3744
  _this.settings.target.style.width = _this.settings.target.parentNode.offsetWidth + 'px';
3024
- //_this.settings.target.style.height = (_this.settings.target.parentNode.offsetHeight - offset) * window.devicePixelRatio + 'px';
3025
- //_this.settings.target.style.width = _this.settings.target.parentNode.offsetWidth * window.devicePixelRatio + 'px';
3745
+ //_this.settings.target.style.height = (_this.settings.target.parentNode.offsetHeight - offset) * 1 + 'px';
3746
+ //_this.settings.target.style.width = _this.settings.target.parentNode.offsetWidth * 1 + 'px';
3026
3747
  m_canvas.setRect(_this.settings.target.offsetHeight, _this.settings.target.offsetWidth);
3027
3748
  }
3028
3749
 
3029
3750
  _this.resizeStart = function(e, ui) {
3030
- m_resizing.width = m_canvas.width;
3031
- m_resizing.height = m_canvas.height;
3751
+ m_resizing.width = m_canvas.getWidth();
3752
+ m_resizing.height = m_canvas.getHeight();
3032
3753
  };
3033
3754
 
3034
3755
  _this.resizing = function(e, ui) {
3035
- var diff = (m_resizing.height - m_canvas.height) / m_canvas.height * 100;
3756
+ var diff = (m_resizing.height - m_canvas.getHeight()) / m_canvas.getHeight() * 100;
3036
3757
  if (Math.abs(diff) > 1) {
3037
3758
  setChartSize();
3038
- m_resizing.width = m_canvas.width;
3039
- m_resizing.height = m_canvas.height;
3759
+ m_resizing.width = m_canvas.getWidth();
3760
+ m_resizing.height = m_canvas.getHeight();
3040
3761
  _this.drawChart();
3041
3762
  } else {
3042
- diff = (m_resizing.width - m_canvas.width) / m_canvas.width * 100;
3763
+ diff = (m_resizing.width - m_canvas.getWidth()) / m_canvas.getWidth() * 100;
3043
3764
  if (Math.abs(diff) > 1) {
3044
3765
  setChartSize();
3045
- //m_canvas.height = Math.floor(_this.settings.target.offsetHeight * window.devicePixelRatio);
3046
- //m_canvas.width = Math.floor(_this.settings.target.offsetWidth * window.devicePixelRatio);
3047
- m_resizing.width = m_canvas.width;
3048
- m_resizing.height = m_canvas.height;
3766
+ //m_canvas.getHeight() = Math.floor(_this.settings.target.offsetHeight * 1);
3767
+ //m_canvas.getWidth() = Math.floor(_this.settings.target.offsetWidth * 1);
3768
+ m_resizing.width = m_canvas.getWidth();
3769
+ m_resizing.height = m_canvas.getHeight();
3049
3770
  _this.drawChart();
3050
3771
  }
3051
3772
  }
3052
- var cache = m_ctx.getImageData(0, 0, m_canvas.width, m_canvas.height);
3773
+ var cache = m_ctx.getImageData(0, 0, m_canvas.getWidth(), m_canvas.getHeight());
3053
3774
  setChartSize();
3054
3775
  m_ctx.putImageData(cache, 0, 0);
3055
3776
  };
@@ -3074,12 +3795,31 @@ function Milli_Chart(settings) {
3074
3795
  if (m_canvas == null) {
3075
3796
  m_canvas = MillistreamWidgetApi_addElement(_this, 'canvas', 'millistream-chart-canvas', _this.settings.target);
3076
3797
  m_ctx = m_canvas.getContext("2d");
3798
+ setHighDPICanvas(m_canvas);
3799
+ setHighDPIContext(m_ctx);
3077
3800
  setChartSize();
3078
3801
  m_canvas.addEventListener('mousemove', onMouseMove, false); // disable while loading and enable on drawReady
3079
3802
  m_canvas.addEventListener('mouseout', onMouseOut, false);
3080
3803
  m_canvas.style.cursor = "crosshair";
3081
3804
  m_canvas.onmousedown = (function(evt) {
3082
3805
  if (evt.which != 1) return; // ignore right and middle
3806
+ var rect = m_canvas.getBoundingClientRect();
3807
+ var x = getScaledSetting(evt.clientX) - getScaledSetting(rect.left) - 1;
3808
+ var y = getScaledSetting(evt.clientY) - getScaledSetting(rect.top);
3809
+
3810
+ for (let i = 0; i < _this.settings.indicators.length; i++) {
3811
+ var width = m_ctx.measureText(_this.settings.indicators[i].indicator).width;
3812
+ for (let s = 0; s < _this.settings.indicators[i].timeseries.length; s++) {
3813
+ if (x >= parseInt(_this.settings.indicators[i].timeseries[s].pos.x) - (width / 2) && x <= parseInt(_this.settings.indicators[i].timeseries[s].pos.x) + (width / 2)) {
3814
+ if (y >= _this.settings.indicators[i].timeseries[s].pos.y && y <= _this.settings.indicators[i].timeseries[s].pos.y + width) {
3815
+ if (typeof _this.settings.indicators[i].onclick === 'function') _this.settings.indicators[i].onclick.call(_this.settings.indicators[i]);
3816
+ }
3817
+ break;
3818
+ }
3819
+ }
3820
+ }
3821
+
3822
+
3083
3823
  if (_this.settings.enablezoom == false) return;
3084
3824
  if (m_datapoints.length == 0) return;
3085
3825
  var rect = m_canvas.getBoundingClientRect();
@@ -3147,15 +3887,45 @@ function Milli_Chart(settings) {
3147
3887
  function requestStreaming() {
3148
3888
  if (_this.settings.streaming != false) {
3149
3889
  var arr = [];
3890
+ var requestNews = 0;
3150
3891
  _this.instruments.forEach(function(instr) {
3151
3892
  if (instr.insref != 0) arr.push(instr.insref);
3152
3893
  });
3894
+ _this.settings.indicators.forEach(function(indicator) {
3895
+ if (indicator.method == 'news' && typeof indicator.insrefs === 'object') {
3896
+ indicator.insrefs.forEach(function(insref) {
3897
+ if (insref != 0) {
3898
+ requestNews = 1;
3899
+ arr.push(insref);
3900
+ }
3901
+ });
3902
+ }
3903
+ });
3904
+ if (requestNews == 0) _this.settings.messagetypes = _this.settings.messagetypes & ~1;
3905
+ else _this.settings.messagetypes |= 1;
3153
3906
  if (typeof _this.unsubscriptions === 'undefined' || typeof _this.unsubscriptions.insrefs === 'undefined' || _this.unsubscriptions.insrefs != arr.length) {
3154
3907
  _this.requestid = _this.settings.streaming.MillistreamWidgetStreamingApi_subscribeInstruments(_this, _this.requestid, arr);
3155
3908
  }
3156
3909
  }
3157
3910
  }
3158
3911
 
3912
+ function handleNewsStreaming(insref, json) {
3913
+ if (typeof json['167'] === 'undefined') return false;
3914
+ var found = false;
3915
+ for (var i = 0; i < _this.instruments.length; i++) {
3916
+ var s;
3917
+ for (s = 0; s < json['167'].length; s++) {
3918
+ if (json['167'][s] == _this.instruments[i].insref) {
3919
+ found = true;
3920
+ break;
3921
+ }
3922
+ }
3923
+ }
3924
+ if (found == false) {
3925
+ return false;
3926
+ }
3927
+ }
3928
+
3159
3929
  _this.streamingCallback = function(insref, mref, json) {
3160
3930
  var update = false;
3161
3931
  var calcAnalyizis = false;
@@ -3163,6 +3933,9 @@ function Milli_Chart(settings) {
3163
3933
  return item.insref == insref;
3164
3934
  });
3165
3935
  if (instr == null) {
3936
+ if (mref == 'newsheadline') {
3937
+ handleNewsStreaming(insref, json);
3938
+ }
3166
3939
  return;
3167
3940
  }
3168
3941
  if (mref == 'performance') {
@@ -3176,6 +3949,10 @@ function Milli_Chart(settings) {
3176
3949
  instr.closeprice1d = parseFloat(json['262']);
3177
3950
  update = true;
3178
3951
  }
3952
+ if (typeof json['1024'] !== 'undefined') {
3953
+ instr.diff1dprc = parseFloat(json['1024']);
3954
+ update = true;
3955
+ }
3179
3956
  }
3180
3957
  var timestamp;
3181
3958
  if (mref == 'quote') {
@@ -3215,7 +3992,7 @@ function Milli_Chart(settings) {
3215
3992
  }
3216
3993
  obj.price = typeof price === 'undefined' ? obj.price : parseFloat(price);
3217
3994
  obj.quantity = typeof quantity === 'undefined' ? obj.quantity : quantity;
3218
- if (_this.scaleinfoY.type == 'history') update = true;
3995
+ if (chartType == 'history') update = true; // TODO fix
3219
3996
  } else {
3220
3997
  if (json['3'] && json['36'] && (json['12'] || json['201'])) {
3221
3998
  timestamp = new Date(json['3'] + 'T' + json['36'].substring(0, 6) + '00' + 'Z').getTime();
@@ -3253,14 +4030,14 @@ function Milli_Chart(settings) {
3253
4030
  calcAnalyizis = true;
3254
4031
  data.price = parseFloat(json['12']); // eller 201 för tradeyield
3255
4032
  data.quantity += typeof json['13'] === 'undefined' ? 0 : parseInt(json['13']);
3256
- if (_this.scaleinfoY.type == 'trades') update = true;
4033
+ if (chartType == 'trades') update = true;
3257
4034
  } else {
3258
4035
  if (instr.pricetype == 'yield') {
3259
4036
  if (data.price != parseFloat(json['201']))
3260
4037
  calcAnalyizis = true;
3261
4038
  data.price = parseFloat(json['201']); // eller 201 för tradeyield
3262
4039
  data.quantity += typeof json['13'] === 'undefined' ? 0 : parseInt(json['13']);
3263
- if (_this.scaleinfoY.type == 'trades') update = true;
4040
+ if (chartType == 'trades') update = true;
3264
4041
  }
3265
4042
  // TODo: updatera med quantity, open , high,low osv?
3266
4043
  }
@@ -3280,7 +4057,7 @@ function Milli_Chart(settings) {
3280
4057
  break;
3281
4058
  case 'bb':
3282
4059
  _this.settings.indicators[i].trades = [];
3283
- _this.settings.indicators[i].trades = bollingerBands(_this.instruments[0].trades, _this.settings.indicators[i].method_length, _this.settings.indicators[i].stddev | 2);
4060
+ _this.settings.indicators[i].trades = calculateBollingerBands(_this.instruments[0].trades, _this.settings.indicators[i].method_length, _this.settings.indicators[i].stddev | 2);
3284
4061
  break;
3285
4062
  default:
3286
4063
  // draw custom added?
@@ -3300,7 +4077,7 @@ function Milli_Chart(settings) {
3300
4077
  let ret = [];
3301
4078
  var i;
3302
4079
  var date, datestr;
3303
- if (_this.scaleinfoY.type == 'trades') {
4080
+ if (chartType == 'trades') {
3304
4081
  if (_this.instruments.length > 0) {
3305
4082
  for (i = 0; i < _this.instruments[0].trades.length; i++) {
3306
4083
  date = new Date(_this.instruments[0].trades[i].timestamp - new Date().getTimezoneOffset() * 60000); // tz_offset in minutes
@@ -3338,6 +4115,180 @@ function Milli_Chart(settings) {
3338
4115
  return 0;
3339
4116
  };
3340
4117
 
4118
+ function fetchNews(indicator) {
4119
+ var url = milli_data_api_url + "widget=chartnews&token=" + _this.settings.token + "&target=buildwidget&fields=headline,date,time&language=" + "sv" + "&insref=" + indicator.insrefs.toString() + '&xhr=' + (_this.settings.xhr == true ? '1' : '0');
4120
+ url += '&instruments=' + _this.instruments[0].insref;
4121
+
4122
+ for (let i = 1; i < _this.instruments.length; i++) { // skall dessa vara med?
4123
+ if (_this.instruments[i].insref != 0) url += ',' + _this.instruments[i].insref;
4124
+ }
4125
+
4126
+ if (typeof _this.settings.newslanguage === 'object') url += '&newslanguage=' + _this.settings.newslanguage.toString();
4127
+
4128
+ if (_this.settings.xhr) {
4129
+ getXhrJson(url);
4130
+ } else {
4131
+ millistream_data_api.fetch(url, function(data) {
4132
+ indicator.news = data.headlines;
4133
+ indicator.news.sort(function(a, b) {
4134
+ return a.timestamp - b.timestamp;
4135
+ });
4136
+ indicator.method = 'news';
4137
+ indicator.type = NEWSINDICATOR;
4138
+ _this.addIndicator(indicator);
4139
+ requestStreaming();
4140
+ });
4141
+ }
4142
+ }
4143
+
4144
+ _this.addNews = function(indicator) {
4145
+ if (!indicator.method) return;
4146
+ var i;
4147
+ for (i = 0; i < _this.settings.indicators.length; i++) {
4148
+ if (_this.settings.indicators[i].method == indicator.method) {
4149
+ _this.removeIndicator(_this.settings.indicators[i]);
4150
+ return;
4151
+ }
4152
+ }
4153
+ fetchNews(indicator);
4154
+ };
4155
+
4156
+ function setHighDPIContext(context)
4157
+ {
4158
+ context.pixelRatio = (function() {
4159
+ var backingStore = this.backingStorePixelRatio ||
4160
+ this.webkitBackingStorePixelRatio ||
4161
+ this.mozBackingStorePixelRatio ||
4162
+ this.msBackingStorePixelRatio ||
4163
+ this.oBackingStorePixelRatio ||
4164
+ this.backingStorePixelRatio || 1;
4165
+
4166
+ return (window.devicePixelRatio || 1) / backingStore;
4167
+ });
4168
+
4169
+ let forEach = function(obj, func) {
4170
+ for (var p in obj) {
4171
+ if (obj.hasOwnProperty(p)) {
4172
+ func(obj[p], p);
4173
+ }
4174
+ }
4175
+ },
4176
+ ratioArgs = {
4177
+ 'fillRect': 'all',
4178
+ 'clearRect': 'all',
4179
+ 'strokeRect': 'all',
4180
+ 'moveTo': 'all',
4181
+ 'lineTo': 'all',
4182
+ 'arc': [0,1,2],
4183
+ 'arcTo': 'all',
4184
+ 'bezierCurveTo': 'all',
4185
+ 'isPointinPath': 'all',
4186
+ 'isPointinStroke': 'all',
4187
+ 'quadraticCurveTo': 'all',
4188
+ 'rect': 'all',
4189
+ 'translate': 'all',
4190
+ 'createRadialGradient': 'all',
4191
+ 'createLinearGradient': 'all'
4192
+ };
4193
+
4194
+ if (context.pixelRatio === 1) return;
4195
+
4196
+ forEach(ratioArgs, function(value, key) {
4197
+ context[key] = (function(_super) {
4198
+ return function() {
4199
+ var i, len,
4200
+ args = Array.prototype.slice.call(arguments);
4201
+
4202
+ if (value === 'all') {
4203
+ args = args.map(function(a) {
4204
+ return a * context.pixelRatio();
4205
+ });
4206
+ }
4207
+ else if (Array.isArray(value)) {
4208
+ for (i = 0, len = value.length; i < len; i++) {
4209
+ args[value[i]] *= context.pixelRatio();
4210
+ }
4211
+ }
4212
+
4213
+ return _super.apply(this, args);
4214
+ };
4215
+ })(context[key]);
4216
+ });
4217
+ /*context.stroke = (function(_super) {
4218
+ return function() {
4219
+ this.lineWidth *= context.pixelRatio();
4220
+ _super.apply(this, arguments);
4221
+ this.lineWidth /= context.pixelRatio();
4222
+ };
4223
+ })(context.stroke);*/
4224
+
4225
+ context.fillText = (function(_super) {
4226
+ return function() {
4227
+ var args = Array.prototype.slice.call(arguments);
4228
+ args[1] *= context.pixelRatio(); // x
4229
+ args[2] *= context.pixelRatio(); // y
4230
+ var oldfont = this.font;
4231
+ this.font = this.font.replace(
4232
+ /(\d+)(px|em|rem|pt)/g,
4233
+ function(w, m, u) {
4234
+ return (Math.round(m * context.pixelRatio())) + u;
4235
+ }
4236
+ );
4237
+ _super.apply(this, args);
4238
+ this.font = oldfont;
4239
+ };
4240
+ })(context.fillText);
4241
+
4242
+ context.strokeText = (function(_super) {
4243
+ return function() {
4244
+ var args = Array.prototype.slice.call(arguments);
4245
+ args[1] *= context.pixelRatio(); // x
4246
+ args[2] *= context.pixelRatio(); // y
4247
+ var oldfont = this.font;
4248
+ this.font = this.font.replace(
4249
+ /(\d+)(px|em|rem|pt)/g,
4250
+ function(w, m, u) {
4251
+ return (m * context.pixelRatio()) + u;
4252
+ }
4253
+ );
4254
+
4255
+ _super.apply(this, args);
4256
+ this.font = oldfont;
4257
+ };
4258
+ })(context.strokeText);
4259
+
4260
+ }
4261
+
4262
+ function setHighDPICanvas(canvas) {
4263
+ canvas.getPixelRatio = (function() {
4264
+ var context = this.getContext('2d');
4265
+ var backingStore = context.backingStorePixelRatio ||
4266
+ context.webkitBackingStorePixelRatio ||
4267
+ context.mozBackingStorePixelRatio ||
4268
+ context.msBackingStorePixelRatio ||
4269
+ context.oBackingStorePixelRatio ||
4270
+ context.backingStorePixelRatio || 1;
4271
+ return (window.devicePixelRatio || 1) / backingStore;
4272
+ });
4273
+ canvas.setRect = (function(height, width) {
4274
+ let ratio = canvas.getPixelRatio();
4275
+ this.style.height = height + 'px';
4276
+ this.style.width = width + 'px';
4277
+ this.width = width * ratio;
4278
+ this.height = height * ratio;
4279
+ });
4280
+ canvas.getWidth = (function() {
4281
+ let ratio = this.getPixelRatio();
4282
+ return this.width / ratio;
4283
+ });
4284
+ canvas.getHeight = (function() {
4285
+ let ratio = this.getPixelRatio();
4286
+ //return this.height;
4287
+ return this.height / ratio;
4288
+ });
4289
+ return ;
4290
+ }
4291
+
3341
4292
  function fetchTrades(instrument) {
3342
4293
  var oldfields = _this.settings.fields;
3343
4294
  _this.settings.fields = [...['name', 'tradecurrency', 'time', 'date', 'tradeprice', 'tradequantity', 'marketopen', 'marketclose', 'closeprice1d'], ...oldfields];
@@ -3435,14 +4386,19 @@ function Milli_Chart(settings) {
3435
4386
  if (insrefs[i] != 0)
3436
4387
  this.addCompare(insrefs[i]);
3437
4388
  }
3438
-
4389
+ for (let i = 0; i < _this.settings.indicators.length; i++) {
4390
+ if (_this.settings.indicators[i].type == NEWSINDICATOR) {
4391
+ fetchNews(_this.settings.indicators[i]);
4392
+ break;
4393
+ }
4394
+ }
3439
4395
  };
3440
4396
  if (this.settings.autodraw == true) {
3441
4397
  this.drawWidget();
3442
4398
  }
3443
4399
 
3444
4400
  (function updatePixelRatio() {
3445
- matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`).addEventListener('change', updatePixelRatio, { once: true });
4401
+ matchMedia(`(resolution: ${1}dppx)`).addEventListener('change', updatePixelRatio, { once: true });
3446
4402
  _this.drawChart();
3447
4403
  })();
3448
4404
 
@@ -3452,7 +4408,8 @@ function Milli_Chart(settings) {
3452
4408
  _this.drawChart();
3453
4409
  }
3454
4410
  });
3455
- }
4411
+ };
4412
+
3456
4413
  function Milli_OptionsList(settings) {
3457
4414
  let _this = this;
3458
4415
  let requestid = null;
@@ -6056,6 +7013,7 @@ function formatDate(date, format, widget) {
6056
7013
  }
6057
7014
  if (format == 'b dd') { // Jan 01
6058
7015
  timeStamp = new Date(date);
7016
+ console.log(date,timeStamp);
6059
7017
  mon = timeStamp.toDateString().split(' ');
6060
7018
  day = timeStamp.getDate();
6061
7019
  return mon[1] + ' ' + (day <= 9 ? '0' + day : day);