Oanda broker API

I’m a software developer working on a personal project trading with Oanda using their API. First off, I love their API since it is RESTful and easy to work with. That is the programmer side of the brain speaking. The trader side of the brain is still and infant and I have questions. I’m looking to make some friends that are also into trading/programming and can answer some of my basic questions.

I have silly questions like when do I need to get a Price versus a Candle? Is the current price the “close” of the latest candle?

Thanks
Scott

My understanding is… Candles are historical data. If you want the current price, then you should subscribe to a real-time price stream.

https://developer.oanda.com/rest-live-v20/pricing-ep/#CurrentPrices

The “latest candle” is in fact a partial data set being populated and visualized in real time by the data coming from the stream (your guess was actually pretty good – the “Close” price of the candle is indeed what moves up and down as price data comes in). The data set is finalized (the candle “closes”) when the time interval for the candle elapses. A new data set now begins populating from the stream (the next candle “opens”).

Probably more precisely – the candles are slices of a much larger data set that continues to populate from a real-time data stream…

1 Like

Very good explanation. Thank you.

I think what you need is just no more than an example.
I happened to get one example for you.

registerIndicator(“fintechee_oanda_loader”, “A plugin to load Oanda’s streaming quotes and transactions(v1.10)”, function (context) {
// Disclaimer: we are not affiliated with the data providers or the API providers.
window.oandaDemo = getIndiParameter(context, “oandaDemo”)
window.oandaAccountId = getIndiParameter(context, “oandaAccountId”)
window.oandaTradeKey = getIndiParameter(context, “oandaTradeKey”)

var changeCallback = function (ctx) {
var symbolName = getExtraSymbolName(ctx)

if (typeof window.oandaApiLoader.cryptocurrenciesList[symbolName] != "undefined") {
  var chartId = getChartHandleByContext(ctx)
  var symbol = window.oandaApiLoader.cryptocurrenciesList[symbolName].symbolName.split("/")
  var baseCurrency = symbol[0]
  var termCurrency = symbol[1]

  setTakeoverMode(chartId)
  changeChartMenuItemName(ctx)
  var timeFrame = getTimeFrame(ctx)

  window.oandaApiLoader.ticksList.add(baseCurrency)
  window.oandaApiLoader.ticksList.add(termCurrency)
  var chart = window.oandaApiLoader.charts[chartId + ""]
  chart.timeFrame = timeFrame
  chart.baseCurrency = baseCurrency
  chart.termCurrency = termCurrency

  // Solution A:
  // Worked as well
  // You can compare the two solutions.
  // $.ajax({
  //   type: "GET",
  //   url: (window.oandaDemo ? "https://api-fxpractice.oanda.com/v3/instruments/" : "https://api-fxtrade.oanda.com/v3/instruments/") + chart.baseCurrency + "_" + chart.termCurrency + "/candles?granularity=" + timeFrame.toUpperCase(),
  //   contentType: "application/json; charset=utf-8",
  //   dataType: "json",
  //   headers: {
  //     "Authorization": "bearer " + window.oandaTradeKey,
  //     "Accept-Datetime-Format": "UNIX"
  //   },
  //   success: function (res) {
  //     var data = []
  //
  //     if (Array.isArray(res.candles)) {
  //       for (var i in res.candles) {
  //         var ohlc = res.candles[i].mid
  //
  //         data.push({
  //           time: Math.floor(res.candles[i].time / 1000),
  //           volume: res.candles[i].volume,
  //           open: parseFloat(ohlc.o),
  //           high: parseFloat(ohlc.h),
  //           low: parseFloat(ohlc.l),
  //           close: parseFloat(ohlc.c)
  //         })
  //       }
  //
  //       takeoverLoad(chartId, data)
  //     }
  //   }
  // })

  // Solution B:
  oandaDataAPI.instruments.candles(window.oandaAccountId, chart.baseCurrency + "_" + chart.termCurrency, {
    granularity: timeFrame.toUpperCase()
  })
  .then(function (res) {
    var data = []

    if (Array.isArray(res.candles)) {
      for (var i in res.candles) {
        var ohlc = res.candles[i].mid

        data.push({
          time: Math.floor(new Date(res.candles[i].time).getTime() / 1000),
          volume: res.candles[i].volume,
          open: parseFloat(ohlc.o),
          high: parseFloat(ohlc.h),
          low: parseFloat(ohlc.l),
          close: parseFloat(ohlc.c)
        })
      }

      takeoverLoad(chartId, data)
    }
  })

} else {
  var chartId = getChartHandleByContext(ctx)
  unsetTakeoverMode(chartId)
  takeoverLoad(chartId, [])
  delete window.oandaApiLoader.charts[chartId + ""]
}

}

var removeCallback = function (ctx) {
var chartId = getChartHandleByContext(ctx)
unsetTakeoverMode(chartId)
takeoverLoad(chartId, [])
delete window.oandaApiLoader.charts[chartId + “”]
}

var chartId = getChartHandleByContext(context)

if (typeof window.oandaApiLoader == “undefined”) {
window.oandaApiLoader = {
cryptocurrenciesList: [],
charts: [],
ticksList: new Set(),
socket: null,
sendOrder: function (symbolName, volume) {

  },
  onTick: function (msg) {
    var ticks = msg.data

    for (var i in window.oandaApiLoader.charts) {
      var chart = window.oandaApiLoader.charts[i]
      var bUpdatable1 = false
      var bUpdatable2 = false

      for (var j in ticks) {
        if (j == chart.baseCurrency) {
          chart.baseCurrencyPrice = parseFloat(ticks[j])
          bUpdatable1 = true
        }
        if (j == chart.termCurrency) {
          chart.termCurrencyPrice = parseFloat(ticks[j])
          bUpdatable2 = true
        }
      }

      if (bUpdatable1 && bUpdatable2) {
        if (chart.baseCurrencyPrice != null && chart.termCurrencyPrice != null && chart.baseCurrencyPrice > 0 && chart.termCurrencyPrice > 0) {
          var tick = {
            time: Math.floor(new Date().getTime() / 1000),
            volume: (typeof ticks.volume != "undefined" ? ticks.volume : 0),
            price: Math.round(chart.baseCurrencyPrice / chart.termCurrencyPrice * 100000) / 100000
          }

          if (chart.timeFrame == "M1") {
            takeoverUpdate(chart.chartId, 60, tick)
          } else if (chart.timeFrame == "M5") {
            takeoverUpdate(chart.chartId, 300, tick)
          } else if (chart.timeFrame == "M15") {
            takeoverUpdate(chart.chartId, 900, tick)
          } else if (chart.timeFrame == "M30") {
            takeoverUpdate(chart.chartId, 1800, tick)
          } else if (chart.timeFrame == "H1") {
            takeoverUpdate(chart.chartId, 3600, tick)
          } else if (chart.timeFrame == "H4") {
            takeoverUpdate(chart.chartId, 14400, tick)
          } else if (chart.timeFrame == "D") {
            takeoverUpdate(chart.chartId, 86400, tick)
          } else {
            takeoverUpdate(chart.chartId, 86400, tick)
          }
        }
      }
    }
  },
  onTransaction: function (data) {

  },
  setupSocket: function () {
    var symbolsList = []
    for (var i in window.oandaApiLoader.cryptocurrenciesList) {
      symbolsList.push(window.oandaApiLoader.cryptocurrenciesList[i].symbolName)
    }

    if (symbolsList.length > 0) {
      var script = document.createElement("script")
      document.body.appendChild(script)
      script.onload = function () {
        window.latestTickTime = 0
        window.oandaDataAPI.addToken(window.oandaDemo, window.oandaAccountId, window.oandaTradeKey)
        window.oandaDataCallback = function (res) {
          window.latestOandaTickTime = new Date().getTime()
          window.oandaLoaded = true

          if (typeof res.instrument != "undefined") {
            var data = {
              data: []
            }

            var ask = null
            var bid = null
            var price = null
            if (Array.isArray(res.asks)) {
              ask = parseFloat(res.asks[0].price)
              price = ask
            }
            if (Array.isArray(res.bids)) {
              bid = parseFloat(res.bids[0].price)
              if (price != null) {
                price = (price + bid) / 2
              } else {
                price = bid
              }
            }

            var instrument = res.instrument.split("_")
            data.data[instrument[0]] = price
            data.data[instrument[1]] = 1

            window.oandaApiLoader.oandaQuotes[instrument[0] + "/" + instrument[1]] = {
              ask: ask,
              bid: bid
            }

            window.oandaApiLoader.onTick(data)
          }
        }
        window.oandaDataAPI.pricing.stream(window.oandaAccountId, {instruments: symbolsList.join(","), snapshot: false}, window.oandaDataCallback)
        window.oandaOrderAPI.addToken(window.oandaDemo, window.oandaAccountId, window.oandaTradeKey)
        window.oandaOrderCallback = function (res) {
          if (typeof res.type != "undefined") {
            var data = {

            }
            if (res.type == "ORDER_FILL") {

            } else if (res.type == "HEARTBEAT") {
            }

            // {"accountBalance":"6505973.49885","accountID":"<ACCOUNT>","batchID":"777","financing":"0.00000","id":"778","instrument":"EUR_USD","orderID":"777","pl":"0.00000","price":"1.11625","reason":"MARKET_ORDER","time":"2016-09-20T18:18:22.126490230Z","tradeOpened":{"tradeID":"778","units":"100"},"type":"ORDER_FILL","units":"100","userID":1179508}
            window.oandaApiLoader.onTransaction(data)
          }
        }
        window.oandaOrderAPI.transactions.stream(window.oandaAccountId, window.oandaOrderCallback)
      }
      script.onerror = function () {
        alert("Failed to load required libs. Please refresh this page again.")
      }
      script.async = true
      script.src = "https://www.fintechee.com/js/oanda/oanda_wrapper.js"
    }
  },
  resetupSocket: function () {
    var symbolsList = []
    for (var i in window.oandaApiLoader.cryptocurrenciesList) {
      symbolsList.push(window.oandaApiLoader.cryptocurrenciesList[i].symbolName)
    }

    if (symbolsList.length > 0) {
      window.oandaDataAPI.pricing.stream(window.oandaAccountId, {instruments: symbolsList.join(","), snapshot: false}, window.oandaDataCallback)
      window.oandaOrderAPI.transactions.stream(window.oandaAccountId, window.oandaOrderCallback)
    }
  }
}

window.oandaApiLoader.charts[chartId + ""] = {
  chartId: chartId,
  timeFrame: null,
  baseCurrency: null,
  termCurrency: null,
  baseCurrencyPrice: null,
  termCurrencyPrice: null
}
createTakeover(chartId, changeCallback, removeCallback)

var cryptocurrenciesList = [{
  symbolName: "AUD/CAD",
  displayName: "AUD/CAD (Oanda)"
}, {
  symbolName: "AUD/CHF",
  displayName: "AUD/CHF (Oanda)"
}, {
  symbolName: "AUD/JPY",
  displayName: "AUD/JPY (Oanda)"
}, {
  symbolName: "AUD/USD",
  displayName: "AUD/USD (Oanda)"
}, {
  symbolName: "EUR/GBP",
  displayName: "EUR/GBP (Oanda)"
}, {
  symbolName: "EUR/JPY",
  displayName: "EUR/JPY (Oanda)"
}, {
  symbolName: "EUR/USD",
  displayName: "EUR/USD (Oanda)"
}, {
  symbolName: "GBP/AUD",
  displayName: "GBP/AUD (Oanda)"
}, {
  symbolName: "GBP/CHF",
  displayName: "GBP/CHF (Oanda)"
}, {
  symbolName: "GBP/JPY",
  displayName: "GBP/JPY (Oanda)"
}, {
  symbolName: "GBP/USD",
  displayName: "GBP/USD (Oanda)"
}, {
  symbolName: "USD/CAD",
  displayName: "USD/CAD (Oanda)"
}, {
  symbolName: "USD/CHF",
  displayName: "USD/CHF (Oanda)"
}, {
  symbolName: "USD/JPY",
  displayName: "USD/JPY (Oanda)"
}]

window.oandaApiLoader.oandaQuotes = []

for (var i in cryptocurrenciesList) {
  window.oandaApiLoader.cryptocurrenciesList[cryptocurrenciesList[i].displayName] = cryptocurrenciesList[i]
  window.oandaApiLoader.oandaQuotes[cryptocurrenciesList[i].symbolName] = null
}

addExtraSymbols(cryptocurrenciesList)

window.oandaApiLoader.setupSocket()

} else if (typeof window.oandaApiLoader.charts[chartId + “”] == “undefined”) {
window.oandaApiLoader.charts[chartId + “”] = {
chartId: chartId,
baseCurrency: null,
termCurrency: null,
baseCurrencyPrice: null,
termCurrencyPrice: null
}
createTakeover(chartId, changeCallback, removeCallback)
}
},
[{
name: “oandaDemo”,
value: true,
required: true,
type: PARAMETER_TYPE.BOOLEAN,
range: null
}, {
name: “oandaAccountId”,
value: “XXX-XXX-XXXXXXXX-XXX”,
required: true,
type: PARAMETER_TYPE.STRING,
range: null
}, {
name: “oandaTradeKey”,
value: “XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”,
required: true,
type: PARAMETER_TYPE.STRING,
range: null
}],
[{
name: DATA_NAME.TIME,
index: 0
}],
[{
name: “oanda”,
visible: false
}],
WHERE_TO_RENDER.CHART_WINDOW)

As far as the timeframe of the candles is concerned, most of the patterns vary from 1-3 days, making them short-term patterns that are available for at least 1-2 weeks. Now, there are engulfing patterns, piercing patterns, and dark cloud cover patterns that require 2 days.

What I’ve understood till now is that candles can be of great use for backtesting. I doubt if you could get any idea about the current price from anywhere near a candle. Rest, I’ll get back to you in case I find any better solution.