рдмрд┐рд▓ рдЧреЗрдЯреНрд╕ рдХрд╛ рдЧреЛрд▓реНрдл рдХреНрд▓рдм рдкрдбрд╝реЛрд╕реА рдмрдирдиреЗ рдХреЗ рд▓рд┐рдП js рдФрд░ Google рд╢реАрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреИрд╕реЗ рдХрд░реЗрдВ


рд╣рд╛рд▓ рд╣реА рдореЗрдВ, рд╣рдм рдкрд░ рд▓реЗрдЦ рджрд┐рдЦрд╛рдИ рджреЗрдиреЗ рд▓рдЧреЗ рд╣реИрдВ рдЬреЛ рдЖрддреНрдо-рдЕрд▓рдЧрд╛рд╡ рдкрд░ рдЦрд╛рд▓реА рд╕рдордп рдХреА рдХрд╣рд╛рдирд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рд╢реБрд░реВ рд╣реЛрддреЗ рд╣реИрдВ рдФрд░, рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк, рдПрдХ рдкрд╛рд╡ рд░реЛрдЯреА рд╕реЗ рдЯреНрд░реЙрд▓реАрдмрд╕ рджрд┐рдЦрд╛рдИ рджреЗрддреЗ рд╣реИрдВред рд╢рд╛рдпрдж рдкреНрд░рд╢рд╛рд╕рди рдХреЛ рдПрдХ рдирдпрд╛ рд╣рдм рдЬреЛрдбрд╝рдиреЗ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП - рд╕реНрд╡ - рдЕрд▓рдЧрд╛рд╡ ред


рдЗрд╕рд▓рд┐рдП рдореБрдЭреЗ рдЦрд╛рд▓реА рд╕рдордп рдорд┐рд▓рд╛, рдЬрд┐рд╕реЗ рдореИрдВрдиреЗ рдЯрд┐рдВрдХреЙрдл рдЗрдиреНрд╡реЗрд╕реНрдЯрдореЗрдВрдЯреНрд╕ рдореЗрдВ рдЕрдкрдиреЗ рд▓реЗрдирджреЗрди рдХрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдорд░реНрдкрд┐рдд рдХрд┐рдпрд╛ред 2 рдкреНрд░рдХрд╛рд░ рдХреЗ рд▓реЛрдЧ рд╣реИрдВ: рдХреБрдЫ рдЕрдкрдиреЗ рд╕рд┐рд░ рдореЗрдВ рдЦреВрдмрд╕реВрд░рддреА рд╕реЗ рдмрд╣реБрдЖрдпрд╛рдореА рд╕рд░рдгрд┐рдпреЛрдВ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░рддреЗ рд╣реИрдВ, рдЙрди рдкрд░ рдЖрдИрдкреАрдереЙрди рдиреЛрдЯрдмреБрдХ рдореЗрдВ рдлреЙрд░-рд▓реВрдк рдореЗрдВ рдЬрд╛рддреЗ рд╣реИрдВ, рдЬрдмрдХрд┐ рдЕрдиреНрдп рд▓реЛрдЧ рд╕рдВрдЦреНрдпрд╛рдУрдВ рдХреЛ "рдорд╣рд╕реВрд╕" рдХрд░рдирд╛ рдкрд╕рдВрдж рдХрд░рддреЗ рд╣реИрдВ, рдЙрдиреНрд╣реЗрдВ рдПрдХреНрд╕реЗрд▓ рдореЗрдВ рдЕрд▓рдорд╛рд░рд┐рдпреЛрдВ рдкрд░ рд░рдЦрдирд╛ред рдореИрдВ рдЕрдкрдиреЗ рдЖрдк рдХреЛ рджреВрд╕рд░реА рд╢реНрд░реЗрдгреА рдореЗрдВ рд░рдЦрддрд╛ рд╣реВрдВ, рдЗрд╕рд▓рд┐рдП рдореИрдВрдиреЗ рд╕рд╛рд╡рдзрд╛рдиреАрдкреВрд░реНрд╡рдХ Google рд╢реАрдЯ рдореЗрдВ рдЕрдкрдиреЗ рд╕рднреА рд╕реМрджреЗ рджрд░реНрдЬ рдХрд┐рдПред


рдХрдЯ рдХреЗ рддрд╣рдд, рдореИрдВ рдЖрдкрдХреЛ рдмрддрд╛рддрд╛ рд╣реВрдВ рдХрд┐ рдореИрдВрдиреЗ Google Apps рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдФрд░ рдЯрд┐рдВрдХрдлреЙрдл рдЗрдВрд╡реЗрд╕реНрдЯрдореЗрдВрдЯ рдПрдкреАрдЖрдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдиреА рджрд┐рдирдЪрд░реНрдпрд╛ рдХреЛ рдХреИрд╕реЗ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдХрд┐рдпрд╛ред


рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ рдХрд┐ рд╣рдо рдЗрд╕ рдмрд┐рдВрджреБ рдкрд░ рдкрд╣реБрдБрдЪреЗрдВ, рд╢рдмреНрджреЛрдВ рдХреА рдПрдХ рдЫреЛрдЯреА рд╢рдмреНрджрд╛рд╡рд▓реА рдЬреЛ рдореИрдВ рд▓реЗрдЦ рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реВрдБ:


  • рдЯреАрдЖрдИ - рдЯрд┐рдВрдХреЙрдл рдирд┐рд╡реЗрд╢
  • рдПрдХ рд╕рд╛рдзрди рдХрд┐рд╕реА рднреА рд╕реБрд░рдХреНрд╖рд╛, рдЬреИрд╕реЗ рдХрд┐ рд╕реНрдЯреЙрдХ, рдмреЙрдиреНрдб рдпрд╛ рдИрдЯреАрдПрдл рд╣реИред
  • Ticker тАФ ID . , , .
  • Figi тАФ Financial Instrument Global Identifier ( ). API- figi.
  • тАФ .


. - , тАФ . : , . /, .


- , . . OvkHabr. , API, .


Google Apps Script


, , Google Sheets, Tools -> Script editor, JavaScript.



OpenAPI


OpenAPI, swagger-ui


http- , . .


http .
?


  1. , figi
  2. ,
  3. ,

, :


class TinkoffClient
class TinkoffClient {
  constructor(token) {
    this.token = token
    this.baseUrl = 'https://api-invest.tinkoff.ru/openapi/'
  }

  _makeApiCall(methodUrl) {
    const url = this.baseUrl + methodUrl
    Logger.log(`[API Call] ${url}`)
    const params = {'escaping': false, 'headers': {'accept': 'application/json', "Authorization": `Bearer ${this.token}`}}
    const response = UrlFetchApp.fetch(url, params)
    if (response.getResponseCode() == 200)
      return JSON.parse(response.getContentText())
  }

  getInstrumentByTicker(ticker) {
    const url = `market/search/by-ticker?ticker=${ticker}`
    const data = this._makeApiCall(url)
    return data.payload.instruments[0]
  }

  getOrderbookByFigi(figi) {
    const url = `market/orderbook?depth=1&figi=${figi}`
    const data = this._makeApiCall(url)
    return data.payload
  }

  getOperations(from, to, figi) {
    // Arguments `from` && `to` should be in ISO 8601 format
    const url = `operations?from=${from}&to=${to}&figi=${figi}`
    const data = this._makeApiCall(url)
    return data.payload.operations
  }
}

const tinkoffClient = new TinkoffClient(OPENAPI_TOKEN)


- , . , yandex YNDX.


Custom Functions


Google Sheets , AVERAGE, SUM, VLOOKUP. , , . , , тАФ .gs . , , , . , , , , .


getPriceByTicker, . (=getPriceByTicker("YNDX")).


figi , , :


function _getFigiByTicker(ticker) {
  const {figi} = tinkoffClient.getInstrumentByTicker(ticker)
  return figi
}

function getPriceByTicker(ticker) {
  const figi = _getFigiByTicker(ticker)
  const {lastPrice} = tinkoffClient.getOrderbookByFigi(figi)
  return lastPrice
}



! , , .



, , . , GAS :


  • ,
    function onEdit(e) {
      const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
      sheet.getRange('Z1').setValue(Math.random())
    }
  • , , getPriceByTicker
    =getPriceByTicker("YNDX", Z1)

Cache Service


, 2 API-. , , figi . , Apps Script Cache Service. key-value , :


const CACHE = CacheService.getScriptCache()

function _getFigiByTicker(ticker) {
  const cached = CACHE.get(ticker)
  if (cached != null) 
    return cached
  const {figi} = tinkoffClient.getInstrumentByTicker(ticker)
  CACHE.put(ticker, figi)
  return figi
}


. getTrades, API, .


, , . , ISO 8601, API.
Figi _getFigiByTicker, .


function getTrades(ticker, from, to) {
  const figi = _getFigiByTicker(ticker)
  if (!from) {
    from = TRADING_START_AT.toISOString()
  }
  if (!to) {
    const now = new Date()
    to = new Date(now + MILLIS_PER_DAY)
    to = to.toISOString()
  }
  const operations = tinkoffClient.getOperations(from, to, figi)
  ...
}


, Operation :


Operation
"operation": {
    "id": "string",
    "status": "Done",
    "trades": [{
        "tradeId": "string",
        "date": "2019-08-19T18:38:33.131642+03:00",
        "price": 0,
        "quantity": 0
     }],
    "commission": {
      "currency": "RUB",
      "value": 0
    },
    "currency": "RUB",
    "payment": 0,
    "price": 0,
    "quantity": 0,
    "figi": "string",
    "instrumentType": "Stock",
    "isMarginCall": true,
    "date": "2019-08-19T18:38:33.131642+03:00",
    "operationType": "Buy"
  }

- . , : 100 YNDX 2500тВ╜, 40 2500тВ╜, 60 2499тВ╜. , OvkHabr, тАФ trades.


, , , .


,


Pricew=2500тИЧ40+2499тИЧ60100=2499,4



function _calculateTrades(trades) {
  let totalSum = 0
  let totalQuantity = 0
  for (let j in trades) {
    const {quantity, price} = trades[j]
    totalQuantity += quantity
    totalSum += quantity * price
  }
  const weigthedPrice = totalSum / totalQuantity
  return [totalQuantity, totalSum, weigthedPrice]
}


, custom , . , .


, . , , . , " " ( ) . , ( )


const values = [
  ["ID", "Date", "Operation", "Ticker", "Quantity", "Price", "Currency", "SUM", "Commission"], 
]
for (let i=operations.length-1; i>=0; i--) {
  const {operationType, status, trades, id, date, currency, commission} = operations[i]
  if (operationType == "BrokerCommission" || status == "Decline") 
    continue
  let [totalQuantity, totalSum, weigthedPrice] = _calculateTrades(trades) // calculate weighted values
  if (operationType == "Buy") {  // inverse values in a way, that it will be easier to work with
    totalQuantity = -totalQuantity
    totalSum = -totalSum
  }
  values.push([
    id, isoToDate(date), operationType, ticker, totalQuantity, weigthedPrice, currency, totalSum, commission.value
  ])
}

values. :


getTrades
function isoToDate(dateStr){
  // How to format date string so that google scripts recognizes it?
  // https://stackoverflow.com/a/17253060
  const str = dateStr.replace(/-/,'/').replace(/-/,'/').replace(/T/,' ').replace(/\+/,' \+').replace(/Z/,' +00')
  return new Date(str)
}

function _calculateTrades(trades) {
  let totalSum = 0
  let totalQuantity = 0
  for (let j in trades) {
    const {quantity, price} = trades[j]
    totalQuantity += quantity
    totalSum += quantity * price
  }
  const weigthedPrice = totalSum / totalQuantity
  return [totalQuantity, totalSum, weigthedPrice]
}

function getTrades(ticker, from, to) {
  const figi = _getFigiByTicker(ticker)
  if (!from) {
    from = TRADING_START_AT.toISOString()
  }
  if (!to) {
    const now = new Date()
    to = new Date(now + MILLIS_PER_DAY)
    to = to.toISOString()
  }
  const operations = tinkoffClient.getOperations(from, to, figi)

  const values = [
    ["ID", "Date", "Operation", "Ticker", "Quantity", "Price", "Currency", "SUM", "Commission"], 
  ]
  for (let i=operations.length-1; i>=0; i--) {
    const {operationType, status, trades, id, date, currency, commission} = operations[i]
    if (operationType == "BrokerCommission" || status == "Decline") 
      continue
    let [totalQuantity, totalSum, weigthedPrice] = _calculateTrades(trades) // calculate weighted values
    if (operationType == "Buy") {  // inverse values in a way, that it will be easier to work with
      totalQuantity = -totalQuantity
      totalSum = -totalSum
    }
    values.push([
      id, isoToDate(date), operationType, ticker, totalQuantity, weigthedPrice, currency, totalSum, commission.value
    ])
  }
  return values
}

:




API , , Google Apps Script, Google Sheets . , )


рд╕рднреА рдХреЛрдб рдФрд░ рдПрдХ рдЫреЛрдЯрд╛ рдХреИрд╕реЗ-рдХреЛ рдЬреАрдереВрдм рдкрд░ рдЕрдкрд▓реЛрдб рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ


рдЙрди рдкрд╛рдардХреЛрдВ рдХреЗ рд▓рд┐рдП рдЬреЛ рдирд┐рд╡реЗрд╢ рдХреА рд░рд╛рд╣ рдкрд░ рдЪрд▓рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдпрд╣ рдирд╣реАрдВ рдЬрд╛рдирддреЗ рдХрд┐ рдХрд╣рд╛рдВ рд╕реЗ рд╢реБрд░реВ рдХрд░реЗрдВ - рдореИрдВ рдЯрд┐рдВрдХреЙрдл рдкрддреНрд░рд┐рдХрд╛ https://journal.tinkoff.ru/pro/invest/ рд╕реЗ рдореБрдлреНрдд рдХреЛрд░реНрд╕ рдХреА рд╕рд▓рд╛рд╣ рджреЗ рд╕рдХрддрд╛ рд╣реВрдВ - рдпрд╣ рд╕рдВрдХреНрд╖рд┐рдкреНрдд, рдЬрд╛рдирдХрд╛рд░реАрдкреВрд░реНрдг рдФрд░ рд╕рдордЭрдиреЗ рдореЗрдВ рдЖрд╕рд╛рди рд╣реИред


рдФрд░ рдЬрдм рдЖрдк TI рдореЗрдВ рдмреНрд░реЛрдХрд░реЗрдЬ рдЦрд╛рддрд╛ рдЦреЛрд▓рддреЗ рд╣реИрдВ, рддреЛ рдореЗрд░реЗ рд▓рд┐рдВрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЖрдкрдХреЛ рдЙрдкрд╣рд╛рд░ рдХреЗ рд░реВрдк рдореЗрдВ 20,000 рд░реВрдмрд▓ рддрдХ рдХрд╛ рд╣рд┐рд╕реНрд╕рд╛ рдкреНрд░рд╛рдкреНрдд рд╣реЛрдЧрд╛ред


рдзреНрдпрд╛рди рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рджред


All Articles