@barchart/portfolio-api-common 1.3.23 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,25 +8,26 @@ A *private* library of shared JavaScript code pertaining to the paper-trading po
8
8
 
9
9
  Simply put, this project contains code that runs on both the servers (i.e. Serverless applications) and clients (e.g. browser, mobile, etc).
10
10
 
11
- #### [lib/serialization](https://github.com/barchart/portfolio-api-common/tree/master/lib/serialization)
11
+ #### [/lib/serialization](https://github.com/barchart/portfolio-api-common/tree/master/lib/serialization)
12
12
 
13
- Data is passed between client and server in JSON format. However, the code works with more complex types. For example, [Decimal](https://github.com/barchart/barchart-common-js/blob/master/lang/Decimal.js) instances are used in place of [native JavaScript floats](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number). [Day](https://github.com/barchart/barchart-common-js/blob/master/lang/Day.js) instances are used instead of [native JavaScript Dates](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date).
13
+ Data is passed between client and server in JSON format. However, the code works with more complex types. For example, [```Decimal```](https://github.com/barchart/barchart-common-js/blob/master/lang/Decimal.js) instances are used in place of [native JavaScript floats](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number) and [```Day```](https://github.com/barchart/barchart-common-js/blob/master/lang/Day.js) instances are used instead of [native JavaScript Dates](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date).
14
14
 
15
- So, before data is exchanged, it must be converted to pure JSON. Conversely, when data is received, as pure JSON, its translated into more complex types before use. This is facilitated by the [Schema](https://github.com/barchart/barchart-common-js/blob/master/serialization/json/Schema.js) definitions which build custom "reviver" functions for [JSON parsing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse).
15
+ So, before data is exchanged, it must be converted to pure JSON. Conversely, when data is received, as pure JSON, its translated into more complex types before use. This is facilitated by the [```Schema```](https://github.com/barchart/barchart-common-js/blob/master/serialization/json/Schema.js) definitions which build custom "reviver" functions for [JSON parsing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse).
16
16
 
17
- #### [lib/processing](https://github.com/barchart/portfolio-api-common/tree/master/lib/processing)
17
+ #### [/lib/processing](https://github.com/barchart/portfolio-api-common/tree/master/lib/processing)
18
18
 
19
19
  For reporting and display purposes, positions can be grouped together (by asset class, by portfolio, by user, etc). This code supports aggregation and gain/loss calculation. It is used by server-generated reports and dynamic user interfaces.
20
20
 
21
21
  ### Notable Consumers
22
22
 
23
- * [aws-lambda-portfolio](https://github.com/barchart/aws-lambda-portfolio) - Serverless applications (i.e. the servers)
24
- * [portfolio-client-js](https://github.com/barchart/portfolio-client-js) - JavaScript client SDK for communicating with server API's.
25
- * [tgam-portfolio-ui-js](https://github.com/barchart/tgam-portfolio-ui-js) - A dynamic, single-page HTML UI.
23
+ * [@barchart/aws-lambda-portfolio](https://github.com/barchart/aws-lambda-portfolio) - Serverless applications (i.e. the backend)
24
+ * [@barchart/portfolio-client-js](https://github.com/barchart/portfolio-client-js) - JavaScript SDK for communicating with the backend
26
25
 
27
26
  ### Package Managers
28
27
 
29
28
  This library has been published as a *private* module to NPM as [@barchart/portfolio-api-common](https://www.npmjs.com/package/@barchart/portfolio-api-common).
30
29
 
31
- > npm login
32
- > npm install @barchart/portfolio-api-common -S
30
+ ```shell
31
+ npm login
32
+ npm install @barchart/portfolio-api-common -S
33
+ ```
package/gulpfile.js CHANGED
@@ -88,7 +88,7 @@ gulp.task('release', gulp.series(
88
88
  ));
89
89
 
90
90
  gulp.task('lint', () => {
91
- return gulp.src([ './**/*.js', './test/specs/**/*.js', '!./node_modules/**', '!./docs/**', '!./test/SpecRunner.js' ])
91
+ return gulp.src([ './**/*.js', './test/specs/**/*.js', '!./node_modules/**', '!./test/SpecRunner.js' ])
92
92
  .pipe(jshint({'esversion': 6}))
93
93
  .pipe(jshint.reporter('default'))
94
94
  .pipe(jshint.reporter('fail'));
@@ -356,13 +356,13 @@ module.exports = (() => {
356
356
  const transactionCreateFailedInstrumentCorrupt = new FailureType('TRANSACTION_CREATE_FAILED_INSTRUMENT_CORRUPT', 'Unable to create transaction, corporate action history for {U|symbol} cannot be located.');
357
357
 
358
358
  const transactionDeleteFailedOutOfSequence = new FailureType('TRANSACTION_DELETE_FAILED_OUT_OF_SEQUENCE', 'Deleting any transaction, except for the most recent, will cause transaction history to be re-written. Please confirm your intent to re-write transaction history (which could take some time and alter the historical results for this position).');
359
- const transactionDeleteFailedNoTransaction = new FailureType('TRANSACTION_DELETE_FAILED_NO_TRANSACTION', 'Unable to delete transaction. The referenced transaction does not exist.');
359
+ const transactionDeleteFailedNoTransaction = new FailureType('TRANSACTION_DELETE_FAILED_NO_TRANSACTION', 'Unable to delete transaction. The referenced transaction does not exist.', false);
360
360
  const transactionDeleteFailedDirectionSwitchOnRewrite = new FailureType('TRANSACTION_DELETE_FAILED_DIRECTION_SWITCH_ON_REWRITE', 'Deleting this transaction would cause your history to be re-written and the position to switch from long to short (i.e. positive to negative) or vice versa.', false);
361
361
  const transactionDeleteFailedPositionLocked = new FailureType('TRANSACTION_DELETE_FAILED_POSITION_LOCKED', 'Unable to delete transaction, your {L|description} history is being recalculated. Please wait a minute or two and retry.');
362
362
  const transactionDeleteFailedTypeReserved = new FailureType('TRANSACTION_DELETE_FAILED_TYPE_RESERVED', 'Unable to delete {U|type.description} transaction, this type of transaction is managed by the system.');
363
363
 
364
364
  const transactionEditFailedInvalidDate = new FailureType('TRANSACTION_EDIT_FAILED_INVALID_DATE', 'Unable to edit transaction with given date.');
365
- const transactionEditFailedNoTransaction = new FailureType('TRANSACTION_EDIT_FAILED_NO_TRANSACTION', 'Unable to edit transaction. The referenced transaction does not exist.');
365
+ const transactionEditFailedNoTransaction = new FailureType('TRANSACTION_EDIT_FAILED_NO_TRANSACTION', 'Unable to edit transaction. The referenced transaction does not exist.', false);
366
366
  const transactionEditFailedTypeReserved = new FailureType('TRANSACTION_EDIT_FAILED_TYPE_RESERVED', 'Unable to edit {U|type.description} transaction, this type of transaction is managed by the system.');
367
367
 
368
368
  return PortfolioFailureType;
@@ -104,6 +104,10 @@ module.exports = (() => {
104
104
  static get SPINOFF() {
105
105
  return spinoff;
106
106
  }
107
+
108
+ toString() {
109
+ return `[CorporateActionType (code=${this.code})]`;
110
+ }
107
111
  }
108
112
 
109
113
  const split = new CorporateActionType('SPLIT', 'Split', false);
@@ -252,7 +252,7 @@ module.exports = (() => {
252
252
  }
253
253
 
254
254
  toString() {
255
- return '[InstrumentType]';
255
+ return `[InstrumentType (code=${this.code})]`;
256
256
  }
257
257
  }
258
258
 
@@ -118,6 +118,10 @@ module.exports = (() => {
118
118
  return even;
119
119
  }
120
120
  }
121
+
122
+ toString() {
123
+ return `[PositionDirection (code=${this.code})]`;
124
+ }
121
125
  }
122
126
 
123
127
  const long = new PositionDirection('LONG', 'Long', 'positive');
@@ -175,7 +175,7 @@ module.exports = (() => {
175
175
  }
176
176
 
177
177
  toString() {
178
- return '[PositionSummaryFrame]';
178
+ return `[PositionSummaryFrame (code=${this.code})]`;
179
179
  }
180
180
  }
181
181
 
@@ -494,7 +494,7 @@ module.exports = (() => {
494
494
  }
495
495
 
496
496
  toString() {
497
- return '[TransactionType]';
497
+ return `[TransactionType (code=${this.code})]`;
498
498
  }
499
499
  }
500
500
 
@@ -48,7 +48,7 @@ module.exports = (() => {
48
48
  }
49
49
 
50
50
  toString() {
51
- return '[ValuationType]';
51
+ return `[ValuationType (code=${this.code})]`;
52
52
  }
53
53
  }
54
54
 
@@ -161,7 +161,7 @@ module.exports = (() => {
161
161
  const tree = new Tree();
162
162
 
163
163
  createGroups.call(this, tree, this._items, treeDefinition, treeDefinition.definitions);
164
-
164
+
165
165
  map[treeDefinition.name] = tree;
166
166
 
167
167
  return map;
@@ -493,6 +493,41 @@ module.exports = (() => {
493
493
  return is.object(item) && item.data.locked;
494
494
  }
495
495
 
496
+ /**
497
+ * Causes a position to be flagged as calculating.
498
+ *
499
+ * @public
500
+ * @param {Object} position
501
+ */
502
+ setPositionCalculating(position) {
503
+ if (position) {
504
+ assert.argumentIsRequired(position, 'position', Object);
505
+ assert.argumentIsRequired(position.position, 'position.position', String);
506
+
507
+ const item = this._items.find(i => i.position.position === position.position);
508
+
509
+ if (item) {
510
+ item.setPositionCalculating(position);
511
+ }
512
+ }
513
+ }
514
+
515
+ /**
516
+ * Returns a position's calculating status.
517
+ *
518
+ * @public
519
+ * @param {Object} position
520
+ * @return {Boolean}
521
+ */
522
+ getPositionCalculating(position) {
523
+ assert.argumentIsRequired(position, 'position', Object);
524
+ assert.argumentIsRequired(position.position, 'position.position', String);
525
+
526
+ const item = this._items.find(i => i.position.position === position.position);
527
+
528
+ return is.object(item) && item.data.calculating;
529
+ }
530
+
496
531
  /**
497
532
  * Performs a batch update of both position quotes and forex quotes,
498
533
  * triggering updates to position(s) and data aggregation(s).
@@ -545,6 +580,26 @@ module.exports = (() => {
545
580
  }
546
581
  }
547
582
 
583
+ /**
584
+ * Returns current price for symbol provided.
585
+ *
586
+ * @param {String} symbol
587
+ * @return {null|Number}
588
+ */
589
+ getCurrentPrice(symbol) {
590
+ assert.argumentIsRequired(symbol, 'symbol', String);
591
+
592
+ let price;
593
+
594
+ if (this._symbols.hasOwnProperty(symbol) && this._symbols[symbol].length > 0) {
595
+ price = this._symbols[symbol][0].currentPrice;
596
+ } else {
597
+ price = null;
598
+ }
599
+
600
+ return price;
601
+ }
602
+
548
603
  /**
549
604
  * Sets a historical forex quote.
550
605
  *
@@ -73,6 +73,7 @@ module.exports = (() => {
73
73
  this._dataFormat.hide = false;
74
74
  this._dataFormat.invalid = false;
75
75
  this._dataFormat.locked = false;
76
+ this._dataFormat.calculating = false;
76
77
  this._dataFormat.newsExists = false;
77
78
  this._dataFormat.quantity = null;
78
79
  this._dataFormat.quantityPrevious = null;
@@ -580,6 +581,7 @@ module.exports = (() => {
580
581
 
581
582
  let newsBinding = Disposable.getEmpty();
582
583
  let lockedBinding = Disposable.getEmpty();
584
+ let calculatingBinding = Disposable.getEmpty();
583
585
 
584
586
  if (this._single) {
585
587
  newsBinding = item.registerNewsExistsChangeHandler((exists) => {
@@ -590,6 +592,10 @@ module.exports = (() => {
590
592
  lockedBinding = item.registerLockChangeHandler((locked) => {
591
593
  this._dataFormat.locked = locked;
592
594
  });
595
+
596
+ calculatingBinding = item.registerCalculatingChangeHandler((calculating) => {
597
+ this._dataFormat.calculating = calculating;
598
+ });
593
599
  }
594
600
 
595
601
  this._disposeStack.push(item.registerPortfolioChangeHandler((portfolio) => {
@@ -604,6 +610,7 @@ module.exports = (() => {
604
610
  this._disposeStack.push(fundamentalBinding);
605
611
  this._disposeStack.push(quoteBinding);
606
612
  this._disposeStack.push(lockedBinding);
613
+ this._disposeStack.push(calculatingBinding);
607
614
  this._disposeStack.push(newsBinding);
608
615
 
609
616
  this._disposeStack.push(item.registerPositionItemDisposeHandler(() => {
@@ -611,6 +618,7 @@ module.exports = (() => {
611
618
  quoteBinding.dispose();
612
619
  newsBinding.dispose();
613
620
  lockedBinding.dispose();
621
+ calculatingBinding.dispose();
614
622
 
615
623
  array.remove(this._items, i => i === item);
616
624
  array.remove(this._excludedItems, i => i === item);
@@ -663,7 +671,7 @@ module.exports = (() => {
663
671
  const format = group._dataFormat;
664
672
 
665
673
  const currency = group.currency;
666
-
674
+
667
675
  const items = group._consideredItems;
668
676
 
669
677
  group._bypassCurrencyTranslation = items.every(item => item.currency === currency);
@@ -794,6 +802,7 @@ module.exports = (() => {
794
802
 
795
803
  format.invalid = definition.type === PositionLevelType.POSITION && item.invalid;
796
804
  format.locked = definition.type === PositionLevelType.POSITION && item.data.locked;
805
+ format.calculating = definition.type === PositionLevelType.POSITION && item.data.calculating;
797
806
  }
798
807
 
799
808
  let portfolioType = null;
@@ -899,7 +908,7 @@ module.exports = (() => {
899
908
  actual.marketChangePercent = marketChangePercent;
900
909
 
901
910
  format.market = formatCurrency(actual.market, currency);
902
-
911
+
903
912
  if (updates.marketDirection.up || updates.marketDirection.down) {
904
913
  format.marketDirection = unchanged;
905
914
  setTimeout(() => format.marketDirection = updates.marketDirection, 0);
@@ -68,7 +68,7 @@ module.exports = (() => {
68
68
 
69
69
  this._data.quantity = null;
70
70
  this._data.quantityPrevious = null;
71
-
71
+
72
72
  this._data.realized = null;
73
73
  this._data.income = null;
74
74
  this._data.basisPrice = null;
@@ -100,12 +100,14 @@ module.exports = (() => {
100
100
 
101
101
  this._data.newsExists = false;
102
102
  this._data.fundamental = { };
103
+ this._data.calculating = getIsCalculating(position);
103
104
  this._data.locked = getIsLocked(position);
104
105
 
105
106
  this._quoteChangedEvent = new Event(this);
106
107
  this._newsExistsChangedEvent = new Event(this);
107
108
  this._fundamentalDataChangedEvent = new Event(this);
108
109
  this._lockChangedEvent = new Event(this);
110
+ this._calculatingChangedEvent = new Event(this);
109
111
  this._portfolioChangedEvent = new Event(this);
110
112
  this._positionItemDisposeEvent = new Event(this);
111
113
 
@@ -203,6 +205,15 @@ module.exports = (() => {
203
205
  return this._previousQuote;
204
206
  }
205
207
 
208
+ /**
209
+ * The current price.
210
+ *
211
+ * @return {null|Number}
212
+ */
213
+ get currentPrice() {
214
+ return this._currentPrice;
215
+ }
216
+
206
217
  updatePortfolio(portfolio) {
207
218
  assert.argumentIsRequired(portfolio, 'portfolio', Object);
208
219
  assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
@@ -302,6 +313,26 @@ module.exports = (() => {
302
313
  }
303
314
  }
304
315
 
316
+ /**
317
+ * Sets a position's calculating status.
318
+ *
319
+ * @public
320
+ * @param {Object} position
321
+ */
322
+ setPositionCalculating(position) {
323
+ assert.argumentIsRequired(position, 'position', Object);
324
+
325
+ if (this.getIsDisposed()) {
326
+ return;
327
+ }
328
+
329
+ const value = getIsCalculating(position);
330
+
331
+ if (this._data.calculating !== value) {
332
+ this._calculatingChangedEvent.fire(this._data.calculating = value);
333
+ }
334
+ }
335
+
305
336
  /**
306
337
  * Registers an observer for quote changes, which is fired after internal recalculations
307
338
  * of position data are complete.
@@ -347,6 +378,17 @@ module.exports = (() => {
347
378
  return this._lockChangedEvent.register(handler);
348
379
  }
349
380
 
381
+ /**
382
+ * Registers an observer for position calculating changes.
383
+ *
384
+ * @public
385
+ * @param {Function} handler
386
+ * @return {Disposable}
387
+ */
388
+ registerCalculatingChangeHandler(handler) {
389
+ return this._calculatingChangedEvent.register(handler);
390
+ }
391
+
350
392
  /**
351
393
  * Registers an observer changes to portfolio metadata.
352
394
  *
@@ -709,6 +751,12 @@ module.exports = (() => {
709
751
  return is.object(position.system) && is.boolean(position.system.locked) && position.system.locked;
710
752
  }
711
753
 
754
+ function getIsCalculating(position) {
755
+ assert.argumentIsRequired(position, 'position', Object);
756
+
757
+ return is.object(position.system) && is.object(position.system.calculate) && is.number(position.system.calculate.processors) && position.system.calculate.processors > 0;
758
+ }
759
+
712
760
  function getSnapshot(position, currentSummary, reporting) {
713
761
  let snapshot;
714
762
 
@@ -99,6 +99,7 @@ module.exports = (() => {
99
99
  .withField('name', DataType.STRING)
100
100
  .withField('timezone', DataType.forEnum(Timezones, 'Timezone'))
101
101
  .withField('dates.create', DataType.DAY)
102
+ .withField('dates.access', DataType.TIMESTAMP, true)
102
103
  .withField('defaults.cash', DataType.BOOLEAN, true)
103
104
  .withField('defaults.currency', DataType.forEnum(Currency, 'Currency'))
104
105
  .withField('defaults.reinvest', DataType.BOOLEAN, true)
@@ -109,6 +110,7 @@ module.exports = (() => {
109
110
  .withField('legacy.warnings', DataType.NUMBER, true)
110
111
  .withField('legacy.drops', DataType.NUMBER, true)
111
112
  .withField('miscellany', DataType.AD_HOC, true)
113
+ .withField('system.calculate.processors', DataType.NUMBER, true)
112
114
  .withField('system.sequence', DataType.NUMBER)
113
115
  .withField('system.version', DataType.STRING)
114
116
  .withField('system.timestamp', DataType.TIMESTAMP)
@@ -121,6 +123,7 @@ module.exports = (() => {
121
123
  .withField('name', DataType.STRING)
122
124
  .withField('timezone', DataType.forEnum(Timezones, 'Timezone'))
123
125
  .withField('dates.create', DataType.DAY)
126
+ .withField('dates.access', DataType.TIMESTAMP, true)
124
127
  .withField('defaults.cash', DataType.BOOLEAN, true)
125
128
  .withField('defaults.currency', DataType.forEnum(Currency, 'Currency'))
126
129
  .withField('defaults.reinvest', DataType.BOOLEAN, true)
@@ -131,6 +134,7 @@ module.exports = (() => {
131
134
  .withField('legacy.warnings', DataType.NUMBER, true)
132
135
  .withField('legacy.drops', DataType.NUMBER, true)
133
136
  .withField('miscellany', DataType.AD_HOC, true)
137
+ .withField('system.calculate.processors', DataType.NUMBER, true)
134
138
  .schema
135
139
  );
136
140
 
@@ -103,6 +103,7 @@ module.exports = (() => {
103
103
  .withField('legacy.position', DataType.STRING, true)
104
104
  .withField('system.version', DataType.NUMBER, true)
105
105
  .withField('system.locked', DataType.BOOLEAN, true)
106
+ .withField('system.calculate.processors', DataType.NUMBER, true)
106
107
  .withField('root', DataType.STRING, true)
107
108
  .schema
108
109
  );
@@ -133,6 +134,7 @@ module.exports = (() => {
133
134
  .withField('snapshot.income', DataType.DECIMAL)
134
135
  .withField('snapshot.value', DataType.DECIMAL)
135
136
  .withField('system.locked', DataType.BOOLEAN, true)
137
+ .withField('system.calculate.processors', DataType.NUMBER, true)
136
138
  .withField('previous', DataType.NUMBER, true)
137
139
  .schema
138
140
  );
@@ -130,6 +130,7 @@ module.exports = (() => {
130
130
  const complete = new TransactionSchema(SchemaBuilder.withName('complete')
131
131
  .withField('portfolio', DataType.STRING)
132
132
  .withField('position', DataType.STRING)
133
+ .withField('transaction', DataType.STRING)
133
134
  .withField('sequence', DataType.NUMBER)
134
135
  .withField('type', DataType.forEnum(TransactionType, 'TransactionType'))
135
136
  .withField('date', DataType.DAY)
@@ -179,6 +180,7 @@ module.exports = (() => {
179
180
  const client = new TransactionSchema(SchemaBuilder.withName('client')
180
181
  .withField('portfolio', DataType.STRING)
181
182
  .withField('position', DataType.STRING)
183
+ .withField('transaction', DataType.STRING)
182
184
  .withField('sequence', DataType.NUMBER)
183
185
  .withField('type', DataType.forEnum(TransactionType, 'TransactionType'))
184
186
  .withField('date', DataType.DAY)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.3.23",
3
+ "version": "1.4.2",
4
4
  "description": "Common code used by the Portfolio system",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",
@@ -9,8 +9,8 @@
9
9
  },
10
10
  "scripts": {},
11
11
  "dependencies": {
12
- "@barchart/common-js": "~3.3.0",
13
- "uuid": "3.1.0"
12
+ "@barchart/common-js": "^3.5.1",
13
+ "uuid": "^3.4.0"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@babel/core": "^7.6.2",
@@ -23,10 +23,9 @@
23
23
  "gulp-git": "^2.9.0",
24
24
  "gulp-jasmine": "^2.2.1",
25
25
  "gulp-jshint": "~2.1.0",
26
- "jsdoc": "^3.5.5",
27
26
  "jshint": "2.9.5",
28
27
  "vinyl-buffer": "^1.0.1",
29
28
  "vinyl-source-stream": "^2.0.0"
30
29
  },
31
- "license": "GPL-3.0"
30
+ "license": "MIT"
32
31
  }