@barchart/portfolio-api-common 1.2.122 → 1.2.126

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.
@@ -64,7 +64,7 @@ module.exports = (() => {
64
64
  *
65
65
  * @public
66
66
  * @param {Number} periods
67
- * @returns {Array.<PositionSummaryRange>}
67
+ * @returns {PositionSummaryRange[]}
68
68
  */
69
69
  getRecentRanges(periods) {
70
70
  const startDate = this.getStartDate(periods);
@@ -77,8 +77,8 @@ module.exports = (() => {
77
77
  * Returns the ranges for the set of {@link Transaction} objects.
78
78
  *
79
79
  * @public
80
- * @param {Array.<Transaction>} transactions
81
- * @returns {Array.<PositionSummaryRange>}
80
+ * @param {Transaction[]} transactions
81
+ * @returns {PositionSummaryRange[]}
82
82
  */
83
83
  getRanges(transactions) {
84
84
  assert.argumentIsArray(transactions, 'transactions');
@@ -91,7 +91,7 @@ module.exports = (() => {
91
91
  *
92
92
  * @public
93
93
  * @param {Day} date
94
- * @return {Array.<PositionSummaryRange>}
94
+ * @return {PositionSummaryRange[]}
95
95
  */
96
96
  getRangesFromDate(date) {
97
97
  assert.argumentIsRequired(date, 'date', Day, 'Day');
@@ -180,8 +180,8 @@ module.exports = (() => {
180
180
  }
181
181
 
182
182
  const yearly = new PositionSummaryFrame('YEARLY', 'year', false, getYearlyRanges, getYearlyStartDate, getYearlyRangeDescription);
183
- const quarterly = new PositionSummaryFrame('QUARTER', 'quarter', false, getQuarterlyRanges, getQuarterlyStartDate, getQuarterlyRangeDescription);
184
- const monthly = new PositionSummaryFrame('MONTH', 'month', false, getMonthlyRanges, getMonthlyStartDate, getMonthlyRangeDescription);
183
+ const quarterly = new PositionSummaryFrame('QUARTERLY', 'quarter', false, getQuarterlyRanges, getQuarterlyStartDate, getQuarterlyRangeDescription);
184
+ const monthly = new PositionSummaryFrame('MONTHLY', 'month', false, getMonthlyRanges, getMonthlyStartDate, getMonthlyRangeDescription);
185
185
  const ytd = new PositionSummaryFrame('YTD', 'year-to-date', true, getYearToDateRanges, getYearToDateStartDate, getYearToDateRangeDescription);
186
186
 
187
187
  /**
@@ -241,32 +241,30 @@ module.exports = (() => {
241
241
  }
242
242
 
243
243
  function getMonthlyRanges(transactions) {
244
- const ranges = [ ];
245
-
246
- if (!transactions.length) {
247
- return ranges;
248
- }
244
+ const ranges = [ ];
249
245
 
250
- const first = array.first(transactions);
251
- const last = array.last(transactions);
246
+ if (transactions.length !== 0) {
247
+ const first = array.first(transactions);
248
+ const last = array.last(transactions);
252
249
 
253
- const firstDate = first.date;
254
- let lastDate;
250
+ const firstDate = first.date;
255
251
 
256
- lastDate = last.snapshot.open.getIsZero()
257
- ? new Day(last.date.year, last.date.month, last.date.day).addMonths(1)
258
- : Day.getToday();
259
- lastDate = lastDate.getEndOfMonth();
252
+ let lastDate;
260
253
 
261
- for (
262
- let end = firstDate.getEndOfMonth();
263
- end.format() <= lastDate.format();
264
- end = end.addMonths(1).getEndOfMonth()
265
- ) {
266
- ranges.push(getRange(end.subtractMonths(1).getEndOfMonth(), end));
254
+ if (last.snapshot.open.getIsZero()) {
255
+ lastDate = last.date;
256
+ } else {
257
+ lastDate = Day.getToday();
258
+ }
259
+
260
+ lastDate = lastDate.getEndOfMonth();
261
+
262
+ for (let end = firstDate.getEndOfMonth(); !end.getIsAfter(lastDate); end = end.addMonths(1).getEndOfMonth()) {
263
+ ranges.push(getRange(end.subtractMonths(1).getEndOfMonth(), end));
264
+ }
267
265
  }
268
-
269
- return ranges;
266
+
267
+ return ranges;
270
268
  }
271
269
 
272
270
  function getYearToDateRanges(transactions) {
@@ -303,8 +301,8 @@ module.exports = (() => {
303
301
  }
304
302
 
305
303
  function getMonthlyStartDate(periods, date) {
306
- const today = date || Day.getToday();
307
-
304
+ const today = date || Day.getToday();
305
+
308
306
  return today
309
307
  .subtractMonths(periods)
310
308
  .subtractDays(today.day);
@@ -323,7 +321,7 @@ module.exports = (() => {
323
321
  }
324
322
 
325
323
  function getMonthlyRangeDescription(start, end) {
326
- return '';
324
+ return `Month ended ${end.format()}`;
327
325
  }
328
326
 
329
327
  function getYearToDateRangeDescription(start, end) {
@@ -136,7 +136,7 @@ module.exports = (() => {
136
136
  let average;
137
137
 
138
138
  if (basis && open && !open.getIsZero()) {
139
- average = basis.divide(open);
139
+ average = basis.divide(open).absolute();
140
140
  } else {
141
141
  average = '';
142
142
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.2.122",
3
+ "version": "1.2.126",
4
4
  "description": "Common classes used by the Portfolio system",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",
@@ -23,9 +23,10 @@
23
23
  "gulp-bump": "~1.0.0",
24
24
  "gulp-git": "~1.6.0",
25
25
  "gulp-jasmine": "^2.2.1",
26
- "gulp-jshint": "~1.11.2",
26
+ "gulp-jshint": "~2.1.0",
27
27
  "gulp-util": "^3.0.7",
28
28
  "jsdoc": "^3.5.5",
29
+ "jshint": "2.9.5",
29
30
  "run-sequence": "~1.1.4",
30
31
  "vinyl-buffer": "^1.0.0",
31
32
  "vinyl-source-stream": "^1.1.0"
@@ -455,7 +455,7 @@ module.exports = (() => {
455
455
  *
456
456
  * @public
457
457
  * @param {Number} periods
458
- * @returns {Array.<PositionSummaryRange>}
458
+ * @returns {PositionSummaryRange[]}
459
459
  */
460
460
  getRecentRanges(periods) {
461
461
  const startDate = this.getStartDate(periods);
@@ -468,8 +468,8 @@ module.exports = (() => {
468
468
  * Returns the ranges for the set of {@link Transaction} objects.
469
469
  *
470
470
  * @public
471
- * @param {Array.<Transaction>} transactions
472
- * @returns {Array.<PositionSummaryRange>}
471
+ * @param {Transaction[]} transactions
472
+ * @returns {PositionSummaryRange[]}
473
473
  */
474
474
  getRanges(transactions) {
475
475
  assert.argumentIsArray(transactions, 'transactions');
@@ -482,7 +482,7 @@ module.exports = (() => {
482
482
  *
483
483
  * @public
484
484
  * @param {Day} date
485
- * @return {Array.<PositionSummaryRange>}
485
+ * @return {PositionSummaryRange[]}
486
486
  */
487
487
  getRangesFromDate(date) {
488
488
  assert.argumentIsRequired(date, 'date', Day, 'Day');
@@ -571,8 +571,8 @@ module.exports = (() => {
571
571
  }
572
572
 
573
573
  const yearly = new PositionSummaryFrame('YEARLY', 'year', false, getYearlyRanges, getYearlyStartDate, getYearlyRangeDescription);
574
- const quarterly = new PositionSummaryFrame('QUARTER', 'quarter', false, getQuarterlyRanges, getQuarterlyStartDate, getQuarterlyRangeDescription);
575
- const monthly = new PositionSummaryFrame('MONTH', 'month', false, getMonthlyRanges, getMonthlyStartDate, getMonthlyRangeDescription);
574
+ const quarterly = new PositionSummaryFrame('QUARTERLY', 'quarter', false, getQuarterlyRanges, getQuarterlyStartDate, getQuarterlyRangeDescription);
575
+ const monthly = new PositionSummaryFrame('MONTHLY', 'month', false, getMonthlyRanges, getMonthlyStartDate, getMonthlyRangeDescription);
576
576
  const ytd = new PositionSummaryFrame('YTD', 'year-to-date', true, getYearToDateRanges, getYearToDateStartDate, getYearToDateRangeDescription);
577
577
 
578
578
  /**
@@ -632,32 +632,30 @@ module.exports = (() => {
632
632
  }
633
633
 
634
634
  function getMonthlyRanges(transactions) {
635
- const ranges = [ ];
636
-
637
- if (!transactions.length) {
638
- return ranges;
639
- }
635
+ const ranges = [ ];
636
+
637
+ if (transactions.length !== 0) {
638
+ const first = array.first(transactions);
639
+ const last = array.last(transactions);
640
+
641
+ const firstDate = first.date;
640
642
 
641
- const first = array.first(transactions);
642
- const last = array.last(transactions);
643
+ let lastDate;
643
644
 
644
- const firstDate = first.date;
645
- let lastDate;
645
+ if (last.snapshot.open.getIsZero()) {
646
+ lastDate = last.date;
647
+ } else {
648
+ lastDate = Day.getToday();
649
+ }
646
650
 
647
- lastDate = last.snapshot.open.getIsZero()
648
- ? new Day(last.date.year, last.date.month, last.date.day).addMonths(1)
649
- : Day.getToday();
650
- lastDate = lastDate.getEndOfMonth();
651
+ lastDate = lastDate.getEndOfMonth();
651
652
 
652
- for (
653
- let end = firstDate.getEndOfMonth();
654
- end.format() <= lastDate.format();
655
- end = end.addMonths(1).getEndOfMonth()
656
- ) {
657
- ranges.push(getRange(end.subtractMonths(1).getEndOfMonth(), end));
653
+ for (let end = firstDate.getEndOfMonth(); !end.getIsAfter(lastDate); end = end.addMonths(1).getEndOfMonth()) {
654
+ ranges.push(getRange(end.subtractMonths(1).getEndOfMonth(), end));
655
+ }
658
656
  }
659
-
660
- return ranges;
657
+
658
+ return ranges;
661
659
  }
662
660
 
663
661
  function getYearToDateRanges(transactions) {
@@ -694,8 +692,8 @@ module.exports = (() => {
694
692
  }
695
693
 
696
694
  function getMonthlyStartDate(periods, date) {
697
- const today = date || Day.getToday();
698
-
695
+ const today = date || Day.getToday();
696
+
699
697
  return today
700
698
  .subtractMonths(periods)
701
699
  .subtractDays(today.day);
@@ -714,7 +712,7 @@ module.exports = (() => {
714
712
  }
715
713
 
716
714
  function getMonthlyRangeDescription(start, end) {
717
- return '';
715
+ return `Month ended ${end.format()}`;
718
716
  }
719
717
 
720
718
  function getYearToDateRangeDescription(start, end) {
@@ -14020,22 +14018,36 @@ moment.tz.load(require('./data/packed/latest.json'));
14020
14018
  function createDate (y, m, d, h, M, s, ms) {
14021
14019
  // can't just apply() to create a date:
14022
14020
  // https://stackoverflow.com/q/181348
14023
- var date = new Date(y, m, d, h, M, s, ms);
14024
-
14021
+ var date;
14025
14022
  // the date constructor remaps years 0-99 to 1900-1999
14026
- if (y < 100 && y >= 0 && isFinite(date.getFullYear())) {
14027
- date.setFullYear(y);
14023
+ if (y < 100 && y >= 0) {
14024
+ // preserve leap years using a full 400 year cycle, then reset
14025
+ date = new Date(y + 400, m, d, h, M, s, ms);
14026
+ if (isFinite(date.getFullYear())) {
14027
+ date.setFullYear(y);
14028
+ }
14029
+ } else {
14030
+ date = new Date(y, m, d, h, M, s, ms);
14028
14031
  }
14032
+
14029
14033
  return date;
14030
14034
  }
14031
14035
 
14032
14036
  function createUTCDate (y) {
14033
- var date = new Date(Date.UTC.apply(null, arguments));
14034
-
14037
+ var date;
14035
14038
  // the Date.UTC function remaps years 0-99 to 1900-1999
14036
- if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) {
14037
- date.setUTCFullYear(y);
14039
+ if (y < 100 && y >= 0) {
14040
+ var args = Array.prototype.slice.call(arguments);
14041
+ // preserve leap years using a full 400 year cycle, then reset
14042
+ args[0] = y + 400;
14043
+ date = new Date(Date.UTC.apply(null, args));
14044
+ if (isFinite(date.getUTCFullYear())) {
14045
+ date.setUTCFullYear(y);
14046
+ }
14047
+ } else {
14048
+ date = new Date(Date.UTC.apply(null, arguments));
14038
14049
  }
14050
+
14039
14051
  return date;
14040
14052
  }
14041
14053
 
@@ -14137,7 +14149,7 @@ moment.tz.load(require('./data/packed/latest.json'));
14137
14149
 
14138
14150
  var defaultLocaleWeek = {
14139
14151
  dow : 0, // Sunday is the first day of the week.
14140
- doy : 6 // The week that contains Jan 1st is the first week of the year.
14152
+ doy : 6 // The week that contains Jan 6th is the first week of the year.
14141
14153
  };
14142
14154
 
14143
14155
  function localeFirstDayOfWeek () {
@@ -14246,25 +14258,28 @@ moment.tz.load(require('./data/packed/latest.json'));
14246
14258
  }
14247
14259
 
14248
14260
  // LOCALES
14261
+ function shiftWeekdays (ws, n) {
14262
+ return ws.slice(n, 7).concat(ws.slice(0, n));
14263
+ }
14249
14264
 
14250
14265
  var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
14251
14266
  function localeWeekdays (m, format) {
14252
- if (!m) {
14253
- return isArray(this._weekdays) ? this._weekdays :
14254
- this._weekdays['standalone'];
14255
- }
14256
- return isArray(this._weekdays) ? this._weekdays[m.day()] :
14257
- this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()];
14267
+ var weekdays = isArray(this._weekdays) ? this._weekdays :
14268
+ this._weekdays[(m && m !== true && this._weekdays.isFormat.test(format)) ? 'format' : 'standalone'];
14269
+ return (m === true) ? shiftWeekdays(weekdays, this._week.dow)
14270
+ : (m) ? weekdays[m.day()] : weekdays;
14258
14271
  }
14259
14272
 
14260
14273
  var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
14261
14274
  function localeWeekdaysShort (m) {
14262
- return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort;
14275
+ return (m === true) ? shiftWeekdays(this._weekdaysShort, this._week.dow)
14276
+ : (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort;
14263
14277
  }
14264
14278
 
14265
14279
  var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
14266
14280
  function localeWeekdaysMin (m) {
14267
- return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin;
14281
+ return (m === true) ? shiftWeekdays(this._weekdaysMin, this._week.dow)
14282
+ : (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin;
14268
14283
  }
14269
14284
 
14270
14285
  function handleStrictParse$1(weekdayName, format, strict) {
@@ -15013,13 +15028,13 @@ moment.tz.load(require('./data/packed/latest.json'));
15013
15028
  weekdayOverflow = true;
15014
15029
  }
15015
15030
  } else if (w.e != null) {
15016
- // local weekday -- counting starts from begining of week
15031
+ // local weekday -- counting starts from beginning of week
15017
15032
  weekday = w.e + dow;
15018
15033
  if (w.e < 0 || w.e > 6) {
15019
15034
  weekdayOverflow = true;
15020
15035
  }
15021
15036
  } else {
15022
- // default to begining of week
15037
+ // default to beginning of week
15023
15038
  weekday = dow;
15024
15039
  }
15025
15040
  }
@@ -15613,7 +15628,7 @@ moment.tz.load(require('./data/packed/latest.json'));
15613
15628
  years = normalizedInput.year || 0,
15614
15629
  quarters = normalizedInput.quarter || 0,
15615
15630
  months = normalizedInput.month || 0,
15616
- weeks = normalizedInput.week || 0,
15631
+ weeks = normalizedInput.week || normalizedInput.isoWeek || 0,
15617
15632
  days = normalizedInput.day || 0,
15618
15633
  hours = normalizedInput.hour || 0,
15619
15634
  minutes = normalizedInput.minute || 0,
@@ -15917,7 +15932,7 @@ moment.tz.load(require('./data/packed/latest.json'));
15917
15932
  ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match
15918
15933
  };
15919
15934
  } else if (!!(match = isoRegex.exec(input))) {
15920
- sign = (match[1] === '-') ? -1 : (match[1] === '+') ? 1 : 1;
15935
+ sign = (match[1] === '-') ? -1 : 1;
15921
15936
  duration = {
15922
15937
  y : parseIso(match[2], sign),
15923
15938
  M : parseIso(match[3], sign),
@@ -15959,7 +15974,7 @@ moment.tz.load(require('./data/packed/latest.json'));
15959
15974
  }
15960
15975
 
15961
15976
  function positiveMomentsDifference(base, other) {
15962
- var res = {milliseconds: 0, months: 0};
15977
+ var res = {};
15963
15978
 
15964
15979
  res.months = other.month() - base.month() +
15965
15980
  (other.year() - base.year()) * 12;
@@ -16068,7 +16083,7 @@ moment.tz.load(require('./data/packed/latest.json'));
16068
16083
  if (!(this.isValid() && localInput.isValid())) {
16069
16084
  return false;
16070
16085
  }
16071
- units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
16086
+ units = normalizeUnits(units) || 'millisecond';
16072
16087
  if (units === 'millisecond') {
16073
16088
  return this.valueOf() > localInput.valueOf();
16074
16089
  } else {
@@ -16081,7 +16096,7 @@ moment.tz.load(require('./data/packed/latest.json'));
16081
16096
  if (!(this.isValid() && localInput.isValid())) {
16082
16097
  return false;
16083
16098
  }
16084
- units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
16099
+ units = normalizeUnits(units) || 'millisecond';
16085
16100
  if (units === 'millisecond') {
16086
16101
  return this.valueOf() < localInput.valueOf();
16087
16102
  } else {
@@ -16090,9 +16105,14 @@ moment.tz.load(require('./data/packed/latest.json'));
16090
16105
  }
16091
16106
 
16092
16107
  function isBetween (from, to, units, inclusivity) {
16108
+ var localFrom = isMoment(from) ? from : createLocal(from),
16109
+ localTo = isMoment(to) ? to : createLocal(to);
16110
+ if (!(this.isValid() && localFrom.isValid() && localTo.isValid())) {
16111
+ return false;
16112
+ }
16093
16113
  inclusivity = inclusivity || '()';
16094
- return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) &&
16095
- (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units));
16114
+ return (inclusivity[0] === '(' ? this.isAfter(localFrom, units) : !this.isBefore(localFrom, units)) &&
16115
+ (inclusivity[1] === ')' ? this.isBefore(localTo, units) : !this.isAfter(localTo, units));
16096
16116
  }
16097
16117
 
16098
16118
  function isSame (input, units) {
@@ -16101,7 +16121,7 @@ moment.tz.load(require('./data/packed/latest.json'));
16101
16121
  if (!(this.isValid() && localInput.isValid())) {
16102
16122
  return false;
16103
16123
  }
16104
- units = normalizeUnits(units || 'millisecond');
16124
+ units = normalizeUnits(units) || 'millisecond';
16105
16125
  if (units === 'millisecond') {
16106
16126
  return this.valueOf() === localInput.valueOf();
16107
16127
  } else {
@@ -16111,11 +16131,11 @@ moment.tz.load(require('./data/packed/latest.json'));
16111
16131
  }
16112
16132
 
16113
16133
  function isSameOrAfter (input, units) {
16114
- return this.isSame(input, units) || this.isAfter(input,units);
16134
+ return this.isSame(input, units) || this.isAfter(input, units);
16115
16135
  }
16116
16136
 
16117
16137
  function isSameOrBefore (input, units) {
16118
- return this.isSame(input, units) || this.isBefore(input,units);
16138
+ return this.isSame(input, units) || this.isBefore(input, units);
16119
16139
  }
16120
16140
 
16121
16141
  function diff (input, units, asFloat) {
@@ -16292,62 +16312,130 @@ moment.tz.load(require('./data/packed/latest.json'));
16292
16312
  return this._locale;
16293
16313
  }
16294
16314
 
16315
+ var MS_PER_SECOND = 1000;
16316
+ var MS_PER_MINUTE = 60 * MS_PER_SECOND;
16317
+ var MS_PER_HOUR = 60 * MS_PER_MINUTE;
16318
+ var MS_PER_400_YEARS = (365 * 400 + 97) * 24 * MS_PER_HOUR;
16319
+
16320
+ // actual modulo - handles negative numbers (for dates before 1970):
16321
+ function mod$1(dividend, divisor) {
16322
+ return (dividend % divisor + divisor) % divisor;
16323
+ }
16324
+
16325
+ function localStartOfDate(y, m, d) {
16326
+ // the date constructor remaps years 0-99 to 1900-1999
16327
+ if (y < 100 && y >= 0) {
16328
+ // preserve leap years using a full 400 year cycle, then reset
16329
+ return new Date(y + 400, m, d) - MS_PER_400_YEARS;
16330
+ } else {
16331
+ return new Date(y, m, d).valueOf();
16332
+ }
16333
+ }
16334
+
16335
+ function utcStartOfDate(y, m, d) {
16336
+ // Date.UTC remaps years 0-99 to 1900-1999
16337
+ if (y < 100 && y >= 0) {
16338
+ // preserve leap years using a full 400 year cycle, then reset
16339
+ return Date.UTC(y + 400, m, d) - MS_PER_400_YEARS;
16340
+ } else {
16341
+ return Date.UTC(y, m, d);
16342
+ }
16343
+ }
16344
+
16295
16345
  function startOf (units) {
16346
+ var time;
16296
16347
  units = normalizeUnits(units);
16297
- // the following switch intentionally omits break keywords
16298
- // to utilize falling through the cases.
16348
+ if (units === undefined || units === 'millisecond' || !this.isValid()) {
16349
+ return this;
16350
+ }
16351
+
16352
+ var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate;
16353
+
16299
16354
  switch (units) {
16300
16355
  case 'year':
16301
- this.month(0);
16302
- /* falls through */
16356
+ time = startOfDate(this.year(), 0, 1);
16357
+ break;
16303
16358
  case 'quarter':
16359
+ time = startOfDate(this.year(), this.month() - this.month() % 3, 1);
16360
+ break;
16304
16361
  case 'month':
16305
- this.date(1);
16306
- /* falls through */
16362
+ time = startOfDate(this.year(), this.month(), 1);
16363
+ break;
16307
16364
  case 'week':
16365
+ time = startOfDate(this.year(), this.month(), this.date() - this.weekday());
16366
+ break;
16308
16367
  case 'isoWeek':
16368
+ time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1));
16369
+ break;
16309
16370
  case 'day':
16310
16371
  case 'date':
16311
- this.hours(0);
16312
- /* falls through */
16372
+ time = startOfDate(this.year(), this.month(), this.date());
16373
+ break;
16313
16374
  case 'hour':
16314
- this.minutes(0);
16315
- /* falls through */
16375
+ time = this._d.valueOf();
16376
+ time -= mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR);
16377
+ break;
16316
16378
  case 'minute':
16317
- this.seconds(0);
16318
- /* falls through */
16379
+ time = this._d.valueOf();
16380
+ time -= mod$1(time, MS_PER_MINUTE);
16381
+ break;
16319
16382
  case 'second':
16320
- this.milliseconds(0);
16321
- }
16322
-
16323
- // weeks are a special case
16324
- if (units === 'week') {
16325
- this.weekday(0);
16326
- }
16327
- if (units === 'isoWeek') {
16328
- this.isoWeekday(1);
16329
- }
16330
-
16331
- // quarters are also special
16332
- if (units === 'quarter') {
16333
- this.month(Math.floor(this.month() / 3) * 3);
16383
+ time = this._d.valueOf();
16384
+ time -= mod$1(time, MS_PER_SECOND);
16385
+ break;
16334
16386
  }
16335
16387
 
16388
+ this._d.setTime(time);
16389
+ hooks.updateOffset(this, true);
16336
16390
  return this;
16337
16391
  }
16338
16392
 
16339
16393
  function endOf (units) {
16394
+ var time;
16340
16395
  units = normalizeUnits(units);
16341
- if (units === undefined || units === 'millisecond') {
16396
+ if (units === undefined || units === 'millisecond' || !this.isValid()) {
16342
16397
  return this;
16343
16398
  }
16344
16399
 
16345
- // 'date' is an alias for 'day', so it should be considered as such.
16346
- if (units === 'date') {
16347
- units = 'day';
16400
+ var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate;
16401
+
16402
+ switch (units) {
16403
+ case 'year':
16404
+ time = startOfDate(this.year() + 1, 0, 1) - 1;
16405
+ break;
16406
+ case 'quarter':
16407
+ time = startOfDate(this.year(), this.month() - this.month() % 3 + 3, 1) - 1;
16408
+ break;
16409
+ case 'month':
16410
+ time = startOfDate(this.year(), this.month() + 1, 1) - 1;
16411
+ break;
16412
+ case 'week':
16413
+ time = startOfDate(this.year(), this.month(), this.date() - this.weekday() + 7) - 1;
16414
+ break;
16415
+ case 'isoWeek':
16416
+ time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1) + 7) - 1;
16417
+ break;
16418
+ case 'day':
16419
+ case 'date':
16420
+ time = startOfDate(this.year(), this.month(), this.date() + 1) - 1;
16421
+ break;
16422
+ case 'hour':
16423
+ time = this._d.valueOf();
16424
+ time += MS_PER_HOUR - mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR) - 1;
16425
+ break;
16426
+ case 'minute':
16427
+ time = this._d.valueOf();
16428
+ time += MS_PER_MINUTE - mod$1(time, MS_PER_MINUTE) - 1;
16429
+ break;
16430
+ case 'second':
16431
+ time = this._d.valueOf();
16432
+ time += MS_PER_SECOND - mod$1(time, MS_PER_SECOND) - 1;
16433
+ break;
16348
16434
  }
16349
16435
 
16350
- return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
16436
+ this._d.setTime(time);
16437
+ hooks.updateOffset(this, true);
16438
+ return this;
16351
16439
  }
16352
16440
 
16353
16441
  function valueOf () {
@@ -17053,10 +17141,14 @@ moment.tz.load(require('./data/packed/latest.json'));
17053
17141
 
17054
17142
  units = normalizeUnits(units);
17055
17143
 
17056
- if (units === 'month' || units === 'year') {
17057
- days = this._days + milliseconds / 864e5;
17144
+ if (units === 'month' || units === 'quarter' || units === 'year') {
17145
+ days = this._days + milliseconds / 864e5;
17058
17146
  months = this._months + daysToMonths(days);
17059
- return units === 'month' ? months : months / 12;
17147
+ switch (units) {
17148
+ case 'month': return months;
17149
+ case 'quarter': return months / 3;
17150
+ case 'year': return months / 12;
17151
+ }
17060
17152
  } else {
17061
17153
  // handle milliseconds separately because of floating point math errors (issue #1867)
17062
17154
  days = this._days + Math.round(monthsToDays(this._months));
@@ -17099,6 +17191,7 @@ moment.tz.load(require('./data/packed/latest.json'));
17099
17191
  var asDays = makeAs('d');
17100
17192
  var asWeeks = makeAs('w');
17101
17193
  var asMonths = makeAs('M');
17194
+ var asQuarters = makeAs('Q');
17102
17195
  var asYears = makeAs('y');
17103
17196
 
17104
17197
  function clone$1 () {
@@ -17290,6 +17383,7 @@ moment.tz.load(require('./data/packed/latest.json'));
17290
17383
  proto$2.asDays = asDays;
17291
17384
  proto$2.asWeeks = asWeeks;
17292
17385
  proto$2.asMonths = asMonths;
17386
+ proto$2.asQuarters = asQuarters;
17293
17387
  proto$2.asYears = asYears;
17294
17388
  proto$2.valueOf = valueOf$1;
17295
17389
  proto$2._bubble = bubble;
@@ -17334,7 +17428,7 @@ moment.tz.load(require('./data/packed/latest.json'));
17334
17428
  // Side effect imports
17335
17429
 
17336
17430
 
17337
- hooks.version = '2.22.2';
17431
+ hooks.version = '2.24.0';
17338
17432
 
17339
17433
  setHookCallback(createLocal);
17340
17434
 
@@ -17375,7 +17469,7 @@ moment.tz.load(require('./data/packed/latest.json'));
17375
17469
  TIME: 'HH:mm', // <input type="time" />
17376
17470
  TIME_SECONDS: 'HH:mm:ss', // <input type="time" step="1" />
17377
17471
  TIME_MS: 'HH:mm:ss.SSS', // <input type="time" step="0.001" />
17378
- WEEK: 'YYYY-[W]WW', // <input type="week" />
17472
+ WEEK: 'GGGG-[W]WW', // <input type="week" />
17379
17473
  MONTH: 'YYYY-MM' // <input type="month" />
17380
17474
  };
17381
17475
 
@@ -17764,7 +17858,7 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
17764
17858
  });
17765
17859
  });
17766
17860
 
17767
- describe('and yearly position summary ranges are processed for a transaction set that opens in 2019 and closes in 2016, but has after-the-fact superfluous valuations in 2017 and 2018', () => {
17861
+ describe('and yearly position summary ranges are processed for a transaction set that opens in 2015 and closes in 2016, but has after-the-fact superfluous valuations in 2017 and 2018', () => {
17768
17862
  let ranges;
17769
17863
 
17770
17864
  beforeEach(() => {
@@ -17817,6 +17911,120 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
17817
17911
  });
17818
17912
  });
17819
17913
 
17914
+ describe('and monthly position summary ranges are processed for a transaction set that does not close', () => {
17915
+ let ranges;
17916
+
17917
+ beforeEach(() => {
17918
+ const transactions = [
17919
+ {
17920
+ date: new Day(2019, 1, 20),
17921
+ snapshot: {
17922
+ open: new Decimal(1)
17923
+ },
17924
+ type: TransactionType.BUY
17925
+ },
17926
+ {
17927
+ date: new Day(2019, 2, 21),
17928
+ snapshot: {
17929
+ open: new Decimal(1)
17930
+ },
17931
+ type: TransactionType.BUY
17932
+ }
17933
+ ];
17934
+
17935
+ ranges = PositionSummaryFrame.MONTHLY.getRanges(transactions);
17936
+ });
17937
+
17938
+ it('should have at least two ranges', () => {
17939
+ expect(ranges.length > 1).toEqual(true);
17940
+ });
17941
+
17942
+ it('the first range should be from 12-31-2018 to 01-31-2019', () => {
17943
+ expect(ranges[0].start.format()).toEqual('2018-12-31');
17944
+ expect(ranges[0].end.format()).toEqual('2019-01-31');
17945
+ });
17946
+
17947
+ it('the last range should be for the current month', () => {
17948
+ const today = Day.getToday();
17949
+
17950
+ expect(ranges[ranges.length - 1].start.format()).toEqual(today.getEndOfMonth().subtractMonths(1).getEndOfMonth().format());
17951
+ expect(ranges[ranges.length - 1].end.format()).toEqual(today.getEndOfMonth().format());
17952
+ });
17953
+ });
17954
+
17955
+ describe('and monthly position summary ranges are processed for a transaction set closes the same month', () => {
17956
+ let ranges;
17957
+
17958
+ beforeEach(() => {
17959
+ const transactions = [
17960
+ {
17961
+ date: new Day(2018, 12, 1),
17962
+ snapshot: {
17963
+ open: new Decimal(1)
17964
+ },
17965
+ type: TransactionType.BUY
17966
+ },
17967
+ {
17968
+ date: new Day(2018, 12, 31),
17969
+ snapshot: {
17970
+ open: new Decimal(0)
17971
+ },
17972
+ type: TransactionType.SELL
17973
+ }
17974
+ ];
17975
+
17976
+ ranges = PositionSummaryFrame.MONTHLY.getRanges(transactions);
17977
+ });
17978
+
17979
+ it('should have one range', () => {
17980
+ expect(ranges.length).toEqual(1);
17981
+ });
17982
+
17983
+ it('the first range should be from 11-30-2018 to 12-31-2018', () => {
17984
+ expect(ranges[0].start.format()).toEqual('2018-11-30');
17985
+ expect(ranges[0].end.format()).toEqual('2018-12-31');
17986
+ });
17987
+ });
17988
+
17989
+ describe('and monthly position summary ranges are processed for a transaction set closes the next month', () => {
17990
+ let ranges;
17991
+
17992
+ beforeEach(() => {
17993
+ const transactions = [
17994
+ {
17995
+ date: new Day(2015, 10, 20),
17996
+ snapshot: {
17997
+ open: new Decimal(1)
17998
+ },
17999
+ type: TransactionType.BUY
18000
+ },
18001
+ {
18002
+ date: new Day(2015, 11, 20),
18003
+ snapshot: {
18004
+ open: new Decimal(0)
18005
+ },
18006
+ type: TransactionType.SELL
18007
+ }
18008
+ ];
18009
+
18010
+ ranges = PositionSummaryFrame.MONTHLY.getRanges(transactions);
18011
+ });
18012
+
18013
+ it('should have two ranges', () => {
18014
+ expect(ranges.length).toEqual(2);
18015
+ });
18016
+
18017
+ it('the first range should be from 09-30-2015 to 10-31-2015', () => {
18018
+ expect(ranges[0].start.format()).toEqual('2015-09-30');
18019
+ expect(ranges[0].end.format()).toEqual('2015-10-31');
18020
+ });
18021
+
18022
+ it('the second range should be from 10-31-2015 to 11-30-2015', () => {
18023
+ expect(ranges[1].start.format()).toEqual('2015-10-31');
18024
+ expect(ranges[1].end.format()).toEqual('2015-11-30');
18025
+ });
18026
+ });
18027
+
17820
18028
  describe('and a year-to-date position summary ranges are processed for a transaction set that closed in 2017', () => {
17821
18029
  let ranges;
17822
18030
 
@@ -173,7 +173,7 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
173
173
  });
174
174
  });
175
175
 
176
- describe('and yearly position summary ranges are processed for a transaction set that opens in 2019 and closes in 2016, but has after-the-fact superfluous valuations in 2017 and 2018', () => {
176
+ describe('and yearly position summary ranges are processed for a transaction set that opens in 2015 and closes in 2016, but has after-the-fact superfluous valuations in 2017 and 2018', () => {
177
177
  let ranges;
178
178
 
179
179
  beforeEach(() => {
@@ -226,6 +226,120 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
226
226
  });
227
227
  });
228
228
 
229
+ describe('and monthly position summary ranges are processed for a transaction set that does not close', () => {
230
+ let ranges;
231
+
232
+ beforeEach(() => {
233
+ const transactions = [
234
+ {
235
+ date: new Day(2019, 1, 20),
236
+ snapshot: {
237
+ open: new Decimal(1)
238
+ },
239
+ type: TransactionType.BUY
240
+ },
241
+ {
242
+ date: new Day(2019, 2, 21),
243
+ snapshot: {
244
+ open: new Decimal(1)
245
+ },
246
+ type: TransactionType.BUY
247
+ }
248
+ ];
249
+
250
+ ranges = PositionSummaryFrame.MONTHLY.getRanges(transactions);
251
+ });
252
+
253
+ it('should have at least two ranges', () => {
254
+ expect(ranges.length > 1).toEqual(true);
255
+ });
256
+
257
+ it('the first range should be from 12-31-2018 to 01-31-2019', () => {
258
+ expect(ranges[0].start.format()).toEqual('2018-12-31');
259
+ expect(ranges[0].end.format()).toEqual('2019-01-31');
260
+ });
261
+
262
+ it('the last range should be for the current month', () => {
263
+ const today = Day.getToday();
264
+
265
+ expect(ranges[ranges.length - 1].start.format()).toEqual(today.getEndOfMonth().subtractMonths(1).getEndOfMonth().format());
266
+ expect(ranges[ranges.length - 1].end.format()).toEqual(today.getEndOfMonth().format());
267
+ });
268
+ });
269
+
270
+ describe('and monthly position summary ranges are processed for a transaction set closes the same month', () => {
271
+ let ranges;
272
+
273
+ beforeEach(() => {
274
+ const transactions = [
275
+ {
276
+ date: new Day(2018, 12, 1),
277
+ snapshot: {
278
+ open: new Decimal(1)
279
+ },
280
+ type: TransactionType.BUY
281
+ },
282
+ {
283
+ date: new Day(2018, 12, 31),
284
+ snapshot: {
285
+ open: new Decimal(0)
286
+ },
287
+ type: TransactionType.SELL
288
+ }
289
+ ];
290
+
291
+ ranges = PositionSummaryFrame.MONTHLY.getRanges(transactions);
292
+ });
293
+
294
+ it('should have one range', () => {
295
+ expect(ranges.length).toEqual(1);
296
+ });
297
+
298
+ it('the first range should be from 11-30-2018 to 12-31-2018', () => {
299
+ expect(ranges[0].start.format()).toEqual('2018-11-30');
300
+ expect(ranges[0].end.format()).toEqual('2018-12-31');
301
+ });
302
+ });
303
+
304
+ describe('and monthly position summary ranges are processed for a transaction set closes the next month', () => {
305
+ let ranges;
306
+
307
+ beforeEach(() => {
308
+ const transactions = [
309
+ {
310
+ date: new Day(2015, 10, 20),
311
+ snapshot: {
312
+ open: new Decimal(1)
313
+ },
314
+ type: TransactionType.BUY
315
+ },
316
+ {
317
+ date: new Day(2015, 11, 20),
318
+ snapshot: {
319
+ open: new Decimal(0)
320
+ },
321
+ type: TransactionType.SELL
322
+ }
323
+ ];
324
+
325
+ ranges = PositionSummaryFrame.MONTHLY.getRanges(transactions);
326
+ });
327
+
328
+ it('should have two ranges', () => {
329
+ expect(ranges.length).toEqual(2);
330
+ });
331
+
332
+ it('the first range should be from 09-30-2015 to 10-31-2015', () => {
333
+ expect(ranges[0].start.format()).toEqual('2015-09-30');
334
+ expect(ranges[0].end.format()).toEqual('2015-10-31');
335
+ });
336
+
337
+ it('the second range should be from 10-31-2015 to 11-30-2015', () => {
338
+ expect(ranges[1].start.format()).toEqual('2015-10-31');
339
+ expect(ranges[1].end.format()).toEqual('2015-11-30');
340
+ });
341
+ });
342
+
229
343
  describe('and a year-to-date position summary ranges are processed for a transaction set that closed in 2017', () => {
230
344
  let ranges;
231
345