-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpercentilesPortfolio.js
397 lines (336 loc) · 12.6 KB
/
percentilesPortfolio.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
function onOpen() {
setExchangeRates();
setMetalPrices();
}
// var TRENDING_ENDPOINT = 'Your own endpoint';
// var ENDPOINT_RATES = 'Your own endpoint';
// var GOLD_ENDPOINT = 'Your own endpoint';
// var EXCHANGE_RATE_CHART_ENDPOINT = 'Your own endpoint';
// Currencies to track in portfolio
var CURRENCIES = ['MXN','JPY','CNY','EUR'];
var UTILITIES_SHEET = 'Utilities';
/**
* Global variables to track percentiles and exchange rates
*/
var portfolioTicks = [];
var exchangeRates = {};
var bitcoinRate;
var metalsRate;
// Column constants
var SOLD_COLUMN_MANAGED = 'AG';
var CHANGE_COLUMN_MANAGED = 'W';
var SOLD_COLUMN_PASSIVE = 'AE';
var CHANGE_COLUMN_PASSIVE = 'V';
var PCT_LOW = -3.0;
var PCT_HIGH = 3.0;
var EXCHANGE_RATE_CELL = 'F64';
var BITCOIN_RATE_CELL = 'F73';
// CHANGE PCT
/**
* Constants to track currencies in portfolio
* See Utilities sheet
*/
var CURRENCIES_HISTORICAL_NAME = 'E';
var CURRENCIES_HISTORICAL_PREVIOUS = 'F';
var CURRENCIES_HISTORICAL_CURRENT = 'G';
var CURRENCIES_HISTORICAL_VARIATION = 'H';
var CURRENCIES_START_ROW = 2;
/**
* Gets the latest exchange rate for a given currency
* @param {String} currency
* @returns {String}
*/
function getLatestExchangeRates(currency) {
var currencies = currency ? currency : CURRENCIES.join(',');
var url = ENDPOINT_RATES.replace('{currency}', currencies);
var response = UrlFetchApp.fetch(url);
var data = JSON.parse(response.getContentText());
var rate = 0.0;
if (data && data.quotes && data.quotes) {
currencies = currencies.split(',');
for (var i = 0, c; (c = currencies[i]); i++) {
exchangeRates[c] = data.quotes['USD' + c] ? data.quotes['USD' + c] : 0.0;
}
}
return exchangeRates;
}
/**
* Gets the current bitcoin price
* @returns {String}
*/
function getBitcoinPrice() {
var response = UrlFetchApp.fetch(BITCOIN_ENDPOINT);
var r = JSON.parse(response.getContentText());
var rate = 0.0;
if (r && r.data && r.data.amount) {
var rate = r.data.amount;
}
return rate;
}
/**
* Gets the spot prices for Gold and Silver
* You can overwrite this function with your custom endpoint
* and logic to parse values.
* @returns {String}
*/
function getMetalPrices() {
var options = {
contentType: 'text/plain;charset=UTF-8',
headers: {
origin: GOLD_ORIGIN
}
};
var r = UrlFetchApp.fetch(GOLD_ENDPOINT, options);
return r.getContentText();
}
function processMetalPrices(data) {
data = data || getMetalPrices();
var metals = data.split('\n');
var goldData = metals[1].split(',');
var silverData = metals[0].split(',');
return {
gold: getMetalData(goldData),
silver: getMetalData(silverData)
};
}
// exports.processMetalPrices = processMetalPrices;
/**
* Process plain text data string and retrives
* a metal value price (min/max) and variation (usd/pct)
* @param {*} data
*/
function getMetalData(data) {
return {
'min': data[data.length - 2],
'max': data[data.length - 1],
'var_usd': data[data.length - 4],
'var_pct': data[data.length - 3]
};
}
function setExchangeRates() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var master = spreadsheet.getSheetByName('Master');
// Get and set exchange rates
exchangeRates = getLatestExchangeRates();
master.getRange(EXCHANGE_RATE_CELL).setValue(exchangeRates.MXN);
bitcoinRate = getBitcoinPrice();
master.getRange(BITCOIN_RATE_CELL).setValue(bitcoinRate);
// Set exchange rates in utilities
var utilitiesSheet = spreadsheet.getSheetByName(UTILITIES_SHEET);
var i = 0;
for (var c; (c = CURRENCIES[i]); i++) {
utilitiesSheet.getRange(CURRENCIES_HISTORICAL_NAME + (CURRENCIES_START_ROW + i)).setValue(c);
var currentRange = utilitiesSheet.getRange(CURRENCIES_HISTORICAL_CURRENT + (CURRENCIES_START_ROW + i));
utilitiesSheet.getRange(CURRENCIES_HISTORICAL_PREVIOUS + (CURRENCIES_START_ROW + i)).setValue(currentRange.getValue());
currentRange.setValue(exchangeRates[c]);
}
utilitiesSheet.getRange(CURRENCIES_HISTORICAL_NAME + (CURRENCIES_START_ROW + i)).setValue('BTC');
currentRange = utilitiesSheet.getRange(CURRENCIES_HISTORICAL_CURRENT + (CURRENCIES_START_ROW + i));
utilitiesSheet.getRange(CURRENCIES_HISTORICAL_PREVIOUS + (CURRENCIES_START_ROW + i)).setValue(currentRange.getValue());
utilitiesSheet.getRange(CURRENCIES_HISTORICAL_CURRENT + (CURRENCIES_START_ROW + i)).setValue(bitcoinRate);
}
/**
* Sets the current metal data into spreadsheet
*/
function setMetalPrices() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var metals = spreadsheet.getSheetByName('Metals');
metalsRate = processMetalPrices();
metals.getRange('T2').setValue((parseFloat(metalsRate.gold.max) + parseFloat(metalsRate.gold.min))/2);
metals.getRange('T3').setValue((parseFloat(metalsRate.silver.max) + parseFloat(metalsRate.silver.min))/2);
}
/**
* Determines worst and best performers of day
*/
function getPortfolioPercentiles() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var percentilesSheet = spreadsheet.getSheetByName(UTILITIES_SHEET);
var managedPortfolioSheet = spreadsheet.getSheetByName('Managed Portfolio');
var passivePortfolioSheet = spreadsheet.getSheetByName('Passive Portfolio');
var managedPortfolioMovers = getMovers(managedPortfolioSheet, SOLD_COLUMN_MANAGED, CHANGE_COLUMN_MANAGED);
var passivePortfolioMovers = getMovers(passivePortfolioSheet, SOLD_COLUMN_PASSIVE, CHANGE_COLUMN_PASSIVE);
var message = constructPortfolioMessage('Managed Portfolio - Movers ', managedPortfolioMovers);
message = message + constructPortfolioMessage('Passive Portfolio - Movers ', passivePortfolioMovers);
return message;
}
function getPortfolioTicks() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var portfolios = [
'Managed Portfolio',
'Passive Portfolio',
'Kuspit'
];
portfolios.forEach(function(portfolio) {
var list = [];
var s = spreadsheet.getSheetByName(portfolio);
var last = s.getLastRow();
var range = s.getRange('A2:A' + last);
var ticks = range.getValues();
if (ticks) {
list.append(ticks);
}
});
}
function sendBriefing() {
var d = new Date();
// Send briefing only on market days
if (d.getDay() === 6 || d.getDay() === 0) {
//return;
}
setExchangeRates();
var message = '<div>' + constructCurrenciesMessage();
//message = message + contructMortgageRatesMessage();
message = message + constructMetalsMessage() + '</div>';
// var message = getEarningsCalendarMessage(2);
message = message + '<div style="display: inline-block;">' + getPortfolioPercentiles() + '</div>';
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var url = spreadsheet.getUrl();
var owner = spreadsheet.getOwner();
var type;
type = d.getHours() < 12 ? 'Opening' : 'Closing';
var microdata = '<div itemscope itemtype="http://schema.org/EmailMessage">' +
'<div itemprop="potentialAction" itemscope itemtype="http://schema.org/ViewAction">' +
'<link itemprop="target" href="' + url + '"/>' +
'<meta itemprop="name" content="Track Protfolio & Market"/>' +
'</div>' +
'<meta itemprop="description" content="Track Protfolio & Market"/>' +
'</div>';
MailApp.sendEmail(owner.getEmail(), 'Market Intelligence - ' + type + ' Briefing ' + getToday(), message, {
name: 'Market Intelligence Bot',
htmlBody: message + microdata
});
}
function constructCurrenciesMessage() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var utilitiesSheet = spreadsheet.getSheetByName(UTILITIES_SHEET);
var numberOfCurrencies = CURRENCIES.length + 1;
var range = utilitiesSheet.getRange(CURRENCIES_HISTORICAL_NAME + CURRENCIES_START_ROW + ':' + CURRENCIES_HISTORICAL_VARIATION + (CURRENCIES_START_ROW + numberOfCurrencies));
var values = range.getValues();
// var bitcoinPrice = getBitcoinPrice();
var currencies = {};
for (var i = 0, c; i < numberOfCurrencies; i++) {
currencies[values[i][0]] = {
previous: parseFloat(values[i][1]).toFixed(2),
current: parseFloat(values[i][2]).toFixed(2),
change: (parseFloat(values[i][3])*100).toFixed(2),
url: EXCHANGE_RATE_CHART_ENDPOINT.replace('{currency}', c)
};
}
var message = '<div style="display: inline; float: left; margin: 0 35px 0 0;"><h3>Currencies</h3>';
message = message +
'<table style="float: left; margin: 0 25px 0 0;">' +
'<tr>' +
'<td><b>Currency</b></td>' +
'<td><b>Previous</b></td>' +
'<td><b>Current</b></td>' +
'<td><b>Change %</b></td>' +
'</tr>';
for (var currency in currencies) {
var data = currencies[currency];
var color = data.change < 0 ? 'red' : 'green';
message = message +
'<tr>' +
'<td>' +
'<a href="' + data.url + '">' + currency + '</a>' +
'</td>' +
'<td>' + data.previous + '</td>' +
'<td>' + data.current + '</td>' +
'<td style="color: ' + color + ';">' + data.change + '%</td>' +
'</tr>';
}
message = message + '</table></div>';
return message;
}
function constructMetalsMessage() {
var data = processMetalPrices();
var message = '<div style="display: inline; float: left; margin: 0 35px 0 0;"><h3>Metals</h3>';
message = message +
'<table style="float: left; margin: 0 25px 0 0;">' +
'<tr>' +
'<td><b>Metal</b></td>' +
'<td><b>Min</b></td>' +
'<td><b>Max</b></td>' +
'<td><b>Var USD</b></td>' +
'<td><b>Var %</b></td>' +
'</tr>';
for (var metal in data) {
message = message + contructMetalTableRow(metal, data[metal]);
}
message = message + '</table></div>';
return message;
}
function contructMetalTableRow(metal, data) {
var color = parseFloat(data.var_pct) < 0 ? 'red' : 'green';
return '<tr>' +
'<td>' + metal + '</td>' +
'<td>' + data.min + '</td>' +
'<td>' + data.max + '</td>' +
'<td style="color: ' + color + ';">' + data.var_usd + '</td>' +
'<td style="color: ' + color + ';">' + data.var_pct + '</td>' +
'</tr>';
}
function constructPortfolioMessage(portfolioName, movers) {
var message = '<div style="display: inline; float: left; margin: 0 35px 0 0;"><h3>' + portfolioName + getToday() + '</h3>';
message = renderMovers(movers.best, 'green', message);
message = renderMovers(movers.worst, 'red', message);
message = message + '</div>';
return message;
}
function getMovers(sheet, soldColumn, changeColumn) {
var movers = {
worst: {},
best: {}
};
for (var i = 2, tick; (tick = sheet.getRange('A' + i).getValue()); i++) {
var sold = sheet.getRange(soldColumn + i).getValue();
if (sold !== '') {
continue;
}
if (portfolioTicks.indexOf(tick) === -1) {
portfolioTicks.push(tick);
}
var change = sheet.getRange(changeColumn + i).getValue();
if (change <= PCT_LOW && !movers.worst.hasOwnProperty(tick)) {
movers.worst[tick] = change;
} else if (change >= PCT_HIGH && !movers.best.hasOwnProperty(tick)) {
movers.best[tick] = change;
}
}
return movers;
}
function renderMovers(group, color, message) {
message = message +
'<table style="float: left; margin: 0 25px 0 0;">' +
'<tr>' +
'<td><b>Tick</b></td>' +
'<td><b>Change %</b></td>' +
'</tr>';
for (var prop in group) {
message = message +
'<tr>' +
'<td>' +
'<b>' +
'<a style="color: ' + color + '; "' +
' href="https://www.tradingview.com/symbols/' + prop + '">' + prop +
'</a>' +
'</b>' +
'</td>' +
'<td><b>' + group[prop] + '</b></td>' +
'</tr>';
};
message = message + '</table>';
return message;
}
function getToday() {
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
if (dd < 10) {
dd = '0' + dd;
}
if (mm < 10) {
mm = '0' + mm;
}
return mm + '/' + dd + '/' + yyyy;
}