Created
February 21, 2020 09:25
-
-
Save DaniilVysotskiy/147de2326f6bd8cfee230606714b5f23 to your computer and use it in GitHub Desktop.
Search shares and bonds on Moex by tickers Google App Script for Google Spreadsheet
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Link to security's history example: https://iss.moex.com/iss/history/engines/stock/markets/bonds/securities/SU29012RMFS0?till=2020-02-14&from=2020-02-07&sort_column=TRADEDATE&sort_order=desc&limit=1 | |
// Manual: http://iss.moex.com/iss/reference/63 | |
// Moex market types: | |
// - bonds | |
// - index | |
// - shares | |
const moexMarketTypesMap = { | |
"облигации": "bonds", | |
"акции": "shares", | |
"фонды": "shares", | |
"индексы": "index", | |
"bonds": "bonds", | |
"etf": "bonds", | |
"shares": "shares", | |
"fonds": "shares", | |
"indexes": "index", | |
undefined: "shares", | |
}; | |
// Get normalized ticker type according to moex tickers types | |
const getNormalizedTickerType = (type, map) => { | |
return map[type.toLowerCase()]; | |
}; | |
// Get link to moex item, using market and security (ticker) | |
const getMoexItemLink = (market, security, from, till) => { | |
return `https://iss.moex.com/iss/history/engines/stock/markets/${market}/securities/${security}.xml?till=${till}&from=${from}&sort_column=TRADEDATE&sort_order=desc&limit=1&numtrades=1`; | |
}; | |
const collectMyTickers = (spreadsheet, range, sheetPortfolioName) => { | |
const portfolioSheet = spreadsheet.getSheetByName(sheetPortfolioName); | |
const tickersList = portfolioSheet.getRange(range); | |
return tickersList.getValues().reduce((result, ticker) => { | |
const item = { ticker: ticker[0], type: ticker[1] }; | |
if (item.ticker && item.type) { | |
result.push(item); | |
} | |
return result; | |
}, []); | |
} | |
function onOpen() { | |
SpreadsheetApp.getUi() // Or DocumentApp or FormApp. | |
.createMenu('Scripts!') | |
.addItem('Fetch Market Data', 'init') | |
.addToUi(); | |
init(); | |
} | |
function collectDataFromXML(url, itemType) { | |
const response = UrlFetchApp.fetch(url); | |
const xml = response.getContentText() || null; | |
if (xml === null) return null; | |
const document = XmlService.parse(xml); | |
const root = document.getRootElement(); | |
const data = root.getChild("data"); | |
const rows = data.getChild("rows"); | |
const item = rows.getChild("row"); | |
const itemData = []; | |
if (item) { | |
const ticker = item.getAttribute('SECID').getValue().trim(); | |
const name = item.getAttribute('SHORTNAME').getValue().trim(); | |
const close = item.getAttribute('CLOSE').getValue().trim().replace(".", ","); | |
const open = item.getAttribute('OPEN').getValue().trim().replace(".", ","); | |
const curPrice = close != "" && close != "0" && close != 0 ? close : open; | |
const marketPrice = item.getAttribute('MARKETPRICE2').getValue().trim().replace(".",","); | |
const numTrades = item.getAttribute('NUMTRADES').getValue().trim(); | |
const low = item.getAttribute('LOW').getValue().trim().replace(".", ","); | |
const high = item.getAttribute('HIGH').getValue().trim().replace(".", ","); | |
const couponValue = itemType === 'bonds' ? item.getAttribute('COUPONVALUE').getValue().replace(".", ",") : null; | |
const couponPrecent = itemType === 'bonds' ? item.getAttribute('COUPONPERCENT').getValue().replace(".", ",") : null; | |
// const couponPeriod = itemType === 'bonds' ? item.getAttribute('COUPONPERIOD').getValue() : null; | |
const expirationDate = itemType === 'bonds' ? item.getAttribute('MATDATE').getValue() : null; | |
const faceValue = itemType === 'bonds' ? item.getAttribute('FACEVALUE').getValue().replace(".", ",") : null; | |
const NKD = itemType === 'bonds' ? item.getAttribute('ACCINT').getValue().replace(".", ",") : null; | |
const tradeDate = item.getAttribute('TRADEDATE').getValue().trim(); | |
itemData.push(ticker); | |
itemData.push(name); | |
itemData.push(curPrice); | |
itemData.push(marketPrice); | |
itemData.push(open); | |
itemData.push(close); | |
itemData.push(low); | |
itemData.push(high); | |
itemData.push(numTrades); | |
itemData.push(couponValue); | |
itemData.push(couponPrecent); | |
// itemData.push(couponPeriod); | |
itemData.push(expirationDate); | |
itemData.push(faceValue); | |
itemData.push(NKD); | |
itemData.push(tradeDate); | |
} | |
return itemData; | |
} | |
const fetchMarketsData = (tickersList, googleSpreadSheet, sheetMarketDataName) => { | |
if (!tickersList.length) return; | |
const headerTitles = [ | |
"Ticker", | |
"Name", | |
"Current Price", | |
"Market Price", | |
"Open", | |
"Close", | |
"Low", | |
"High", | |
"Number of Trades", | |
"Coupon Value", | |
"Coupon Precent", | |
"Expiration Date", | |
"Face Value", | |
"NKD", | |
"Trade Date" | |
]; | |
const marketData = googleSpreadSheet.getSheetByName(sheetMarketDataName); | |
let dataRange = marketData.getRange("A1:R300"); | |
const currentDate = new Date(); | |
// Get current date in "YYYY-MM-DD" format | |
const currentDateFormatted = currentDate.toISOString().split('T')[0]; | |
const weekAgoDate = new Date(currentDate.setDate(currentDate.getDate() - 7)); | |
// Get date week ago in "YYYY-MM-DD" format | |
const weekAgoDateFormatted = weekAgoDate.toISOString().split('T')[0]; | |
// Market data container | |
const allMarketData = [headerTitles]; | |
tickersList.forEach((item, index) => { | |
item.type = getNormalizedTickerType(item.type, moexMarketTypesMap); | |
const url = getMoexItemLink(item.type, item.ticker, weekAgoDateFormatted, currentDateFormatted); | |
const data = collectDataFromXML(url, item.type); | |
if (data !== null) { | |
allMarketData.push(data); | |
} | |
}); | |
dataRange.clear({ contentsOnly: true }); | |
dataRange = marketData.getRange(1, 1, allMarketData.length, allMarketData[0].length); | |
dataRange.setValues(allMarketData); | |
Browser.msgBox(`Обновлено ${allMarketData.length} записей! Все изменения на листе ${sheetMarketDataName}.`); | |
} | |
function init() { | |
const sheet_id = "1sVSFSdjKxAe7t-DJDBLdpy_vxskaYW4G9qhlxHNxAVg"; // Spreadsheet id of portfolio | |
const googleSpreadSheet = SpreadsheetApp.openById(sheet_id); | |
const rangeMyTickers = "A2:B100"; // Column consisted of tickers and its types in portfolio | |
const myTickersList = collectMyTickers(googleSpreadSheet, rangeMyTickers, "Портфель"); | |
fetchMarketsData(myTickersList, googleSpreadSheet, "MarketData"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment