Skip to content

Instantly share code, notes, and snippets.

@ajinasokan
Last active June 20, 2024 16:27
Show Gist options
  • Save ajinasokan/b36d17da1a58af62782978c16bca309a to your computer and use it in GitHub Desktop.
Save ajinasokan/b36d17da1a58af62782978c16bca309a to your computer and use it in GitHub Desktop.
Kite Ticker Pure JS Example
<script src="ticker.js"></script>
<script>
var ticker = new KiteTicker({api_key: "api_key", access_token: "access_token"});
ticker.connect();
ticker.on("ticks", onTicks);
ticker.on("connect", subscribe);
function onTicks(ticks) {
console.log("Ticks", ticks);
}
function subscribe() {
var items = [738561];
ticker.subscribe(items);
ticker.setMode(ticker.modeFull, items);
}
</script>
/**
* The WebSocket client for connecting to Kite connect streaming quotes service.
*
* Getting started:
* ---------------
*
* var KiteTicker = require("kiteconnect").KiteTicker;
* var ticker = new KiteTicker({
* api_key: "api_key",
* access_token: "access_token"
* });
*
* ticker.connect();
* ticker.on("ticks", onTicks);
* ticker.on("connect", subscribe);
*
* function onTicks(ticks) {
* console.log("Ticks", ticks);
* }
*
* function subscribe() {
* var items = [738561];
* ticker.subscribe(items);
* ticker.setMode(ticker.modeFull, items);
* }
*
* Tick structure (passed to the tick callback you assign):
* ---------------------------
* [{ tradable: true,
* mode: 'full',
* instrument_token: 208947,
* last_price: 3939,
* last_quantity: 1,
* average_price: 3944.77,
* volume: 28940,
* buy_quantity: 4492,
* sell_quantity: 4704,
* ohlc: { open: 3927, high: 3955, low: 3927, close: 3906 },
* change: 0.8448540706605223,
* last_trade_time: 1515491369,
* timestamp: 1515491373,
* oi: 24355,
* oi_day_high: 0,
* oi_day_low: 0,
* depth:
* buy: [{
* quantity: 59,
* price: 3223,
* orders: 5
* },
* {
* quantity: 164,
* price: 3222,
* orders: 15
* },
* {
* quantity: 123,
* price: 3221,
* orders: 7
* },
* {
* quantity: 48,
* price: 3220,
* orders: 7
* },
* {
* quantity: 33,
* price: 3219,
* orders: 5
* }],
* sell: [{
* quantity: 115,
* price: 3224,
* orders: 15
* },
* {
* quantity: 50,
* price: 3225,
* orders: 5
* },
* {
* quantity: 175,
* price: 3226,
* orders: 14
* },
* {
* quantity: 49,
* price: 3227,
* orders: 10
* },
* {
* quantity: 106,
* price: 3228,
* orders: 13
* }]
* }
* }, ...]
*
* Auto reconnection
* -----------------
* Auto reonnection is enabled by default and it can be disabled by passing `reconnect` param while initialising `KiteTicker`.
*
* Auto reonnection mechanism is based on [Exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) algorithm in which
* next retry interval will be increased exponentially. `max_delay` and `max_tries` params can be used to tweak
* the alogrithm where `max_delay` is the maximum delay after which subsequent reconnection interval will become constant and
* `max_tries` is maximum number of retries before it quits reconnection.
* For example if `max_delay` is 60 seconds and `max_tries` is 50 then the first reconnection interval starts from
* minimum interval which is 2 seconds and keep increasing up to 60 seconds after which it becomes constant and when reconnection attempt
* is reached upto 50 then it stops reconnecting.
* Callback `reconnect` will be called with current reconnect attempt and next reconnect interval and
* `on_noreconnect` is called when reconnection attempts reaches max retries.
*
* Here is an example demonstrating auto reconnection.
*
* var KiteTicker = require("kiteconnect").KiteTicker;
* var ticker = new KiteTicker({
* api_key: "api_key",
* access_token: "access_token"
* });
*
* // set autoreconnect with 10 maximum reconnections and 5 second interval
* ticker.autoReconnect(true, 10, 5)
* ticker.connect();
* ticker.on("ticks", onTicks);
* ticker.on("connect", subscribe);
*
* ticker.on("noreconnect", function() {
* console.log("noreconnect");
* });
*
* ticker.on("reconnect", function(reconnect_count, reconnect_interval) {
* console.log("Reconnecting: attempt - ", reconnect_count, " interval - ", reconnect_interval);
* });
*
* function onTicks(ticks) {
* console.log("Ticks", ticks);
* }
*
* function subscribe() {
* var items = [738561];
* ticker.subscribe(items);
* ticker.setMode(ticker.modeFull, items);
* }
*
*
* @constructor
* @name KiteTicker
* @param {Object} params
* @param {string} params.api_key API key issued you.
* @param {string} params.access_token Access token obtained after successful login flow.
* @param {bool} [params.reconnect] Enable/Disable auto reconnect. Enabled by default.
* @param {number} [params.max_retry=50] is maximum number re-connection attempts. Defaults to 50 attempts and maximum up to 300 attempts.
* @param {number} [params.max_delay=60] in seconds is the maximum delay after which subsequent re-connection interval will become constant. Defaults to 60s and minimum acceptable value is 5s.
* #param {string} [params.root="wss://websocket.kite.trade/"] Kite websocket root.
*/
var KiteTicker = function (params) {
var root = params.root || 'wss://ws.kite.trade/'
var read_timeout = 5, // seconds
reconnect_max_delay = 0,
reconnect_max_tries = 0,
// message flags (outgoing)
mSubscribe = 'subscribe',
mUnSubscribe = 'unsubscribe',
mSetMode = 'mode',
// incoming
mAlert = 10,
mMessage = 11,
mLogout = 12,
mReload = 13,
mClearCache = 14,
// public constants
modeFull = 'full', // Full quote including market depth. 164 bytes.
modeQuote = 'quote', // Quote excluding market depth. 52 bytes.
modeLTP = 'ltp'
// public constants
/**
* @memberOf KiteTicker
* @desc Set mode full
*/
this.modeFull = modeFull
/**
* @memberOf KiteTicker
* @desc Set mode quote
*/
this.modeQuote = modeQuote
/**
* @memberOf KiteTicker
* @desc Set mode LTP
*/
this.modeLTP = modeLTP
var ws = null,
triggers = {'connect': [],
'ticks': [],
'disconnect': [],
'error': [],
'close': [],
'reconnect': [],
'noreconnect': [],
'message': [],
'order_update': []},
read_timer = null,
last_read = 0,
reconnect_timer = null,
auto_reconnect = false,
current_reconnection_count = 0,
last_reconnect_interval = 0
current_ws_url = null,
token_modes = {},
defaultReconnectMaxDelay = 60,
defaultReconnectMaxRetries = 50,
maximumReconnectMaxRetries = 300,
minimumReconnectMaxDelay = 5
// segment constants
var NseCM = 1,
NseFO = 2,
NseCD = 3,
BseCM = 4,
BseFO = 5,
BseCD = 6,
McxFO = 7,
McxSX = 8,
Indices = 9
// Enable auto reconnect by default
if (!params.reconnect) params.reconnect = true
autoReconnect(params.reconnect, params.max_retry, params.max_delay)
/**
* Auto reconnect settings
* @param {bool} Enable or disable auto disconnect, defaults to false
* @param {number} [max_retry=50] is maximum number re-connection attempts. Defaults to 50 attempts and maximum up to 300 attempts.
* @param {number} [max_delay=60] in seconds is the maximum delay after which subsequent re-connection interval will become constant. Defaults to 60s and minimum acceptable value is 5s.
* @memberOf KiteTicker
* @method autoReconnect
*/
this.autoReconnect = function (t, max_retry, max_delay) {
autoReconnect(t, max_retry, max_delay)
}
/**
* Initiate a websocket connection
* @memberOf KiteTicker
* @method connect
* @instance
*/
this.connect = function () {
// Skip if its already connected
if (ws && (ws.readyState == ws.CONNECTING || ws.readyState == ws.OPEN)) return
var url = root + '?api_key=' + params.api_key +
'&access_token=' + params.access_token + '&uid=' + (new Date().getTime().toString())
ws = new WebSocket(url)
ws.binaryType = 'arraybuffer'
ws.onopen = function () {
// Reset last reconnect interval
last_reconnect_interval = null
// Reset current_reconnection_count attempt
current_reconnection_count = 0
// Store current open connection url to check for auto re-connection.
if (!current_ws_url) current_ws_url = this.url
// Trigger on connect event
trigger('connect')
// If there isn't an incoming message in n seconds, assume disconnection.
clearInterval(read_timer)
last_read = new Date()
read_timer = setInterval(function () {
if ((new Date() - last_read) / 1000 >= read_timeout) {
// reset current_ws_url incase current connection times out
// This is determined when last heart beat received time interval
// exceeds read_timeout value
current_ws_url = null
if (ws) ws.close()
clearInterval(read_timer)
triggerDisconnect()
}
}, read_timeout * 1000)
}
ws.onmessage = function (e) {
// Binary tick data.
if (e.data instanceof ArrayBuffer) {
if (e.data.byteLength > 2) {
var d = parseBinary(e.data)
if (d) trigger('ticks', [d])
}
} else {
parseTextMessage(e.data)
}
// Set last read time to check for connection timeout
last_read = new Date()
}
ws.onerror = function (e) {
trigger('error', [e])
// Force close to avoid ghost connections
if (this && this.readyState == this.OPEN) this.close()
}
ws.onclose = function (e) {
trigger('close', [e])
// the ws id doesn't match the current global id,
// meaning it's a ghost close event. just ignore.
if (current_ws_url && (this.url != current_ws_url)) return
triggerDisconnect(e)
}
}
/**
* @memberOf KiteTicker
* @method disconnect
* @instance
*/
this.disconnect = function () {
if (ws && ws.readyState != ws.CLOSING && ws.readyState != ws.CLOSED) {
ws.close()
}
}
/**
* Check if the ticker is connected
* @memberOf KiteTicker
* @method connected
* @instance
* @returns {bool}
*/
this.connected = function () {
if (ws && ws.readyState == ws.OPEN) {
return true
} else {
return false
}
}
/**
* Register websocket event callbacks
* Available events
* ~~~~
* connect - when connection is successfully established.
* ticks - when ticks are available (Arrays of `ticks` object as the first argument).
* disconnect - when socket connection is disconnected. Error is received as a first param.
* error - when socket connection is closed with error. Error is received as a first param.
* close - when socket connection is closed cleanly.
* reconnect - When reconnecting (current re-connection count and reconnect interval as arguments respectively).
* noreconnect - When re-connection fails after n number times.
* order_update - When order update (postback) is received for the connected user (Data object is received as first argument).
* ~~~~
*
* @memberOf KiteTicker
* @method on
* @instance
*
* @example
* ticker.on("ticks", callback);
* ticker.on("connect", callback);
* ticker.on("disconnect", callback);
*/
this.on = function (e, callback) {
if (triggers.hasOwnProperty(e)) {
triggers[e].push(callback)
}
}
/**
* Subscribe to array of tokens
* @memberOf KiteTicker
* @method subscribe
* @instance
* @param {array} tokens Array of tokens to be subscribed
*
* @example
* ticker.subscribe([738561]);
*/
this.subscribe = function (tokens) {
if (tokens.length > 0) {
send({'a': mSubscribe, 'v': tokens})
}
return tokens
}
/**
* Unsubscribe to array of tokens
* @memberOf KiteTicker
* @method unsubscribe
* @instance
* @param {array} tokens Array of tokens to be subscribed
*
* @example
* ticker.unsubscribe([738561]);
*/
this.unsubscribe = function (tokens) {
if (tokens.length > 0) {
send({'a': mUnSubscribe, 'v': tokens})
}
return tokens
}
/**
* Set modes to array of tokens
* @memberOf KiteTicker
* @method setMode
* @instance
* @param {string} mode - mode to set
* @param {array} tokens Array of tokens to be subscribed
*
* @example
* ticker.setMode(ticker.modeFull, [738561]);
*/
this.setMode = function (mode, tokens) {
if (tokens.length > 0) {
send({'a': mSetMode, 'v': [mode, tokens]})
}
return tokens
}
function autoReconnect (t, max_retry, max_delay) {
auto_reconnect = (t == true)
// Set default values
max_retry = max_retry || defaultReconnectMaxRetries
max_delay = max_delay || defaultReconnectMaxDelay
// Set reconnect constraints
reconnect_max_tries = max_retry >= maximumReconnectMaxRetries ? maximumReconnectMaxRetries : max_retry
reconnect_max_delay = max_delay <= minimumReconnectMaxDelay ? minimumReconnectMaxDelay : max_delay
}
function triggerDisconnect (e) {
ws = null
trigger('disconnect', [e])
if (auto_reconnect) attemptReconnection()
}
// send a message via the socket
// automatically encodes json if possible
function send (message) {
if (!ws || ws.readyState != ws.OPEN) return
try {
if (typeof (message) === 'object') {
message = JSON.stringify(message)
}
ws.send(message)
} catch (e) { ws.close() };
}
// trigger event callbacks
function trigger (e, args) {
if (!triggers[e]) return
for (var n = 0; n < triggers[e].length; n++) {
triggers[e][n].apply(triggers[e][n], args || [])
}
}
function parseTextMessage (data) {
try {
data = JSON.parse(data)
} catch (e) {
return
}
if (data.type === 'order') {
trigger('order_update', [data.data])
}
}
// parse received binary message. each message is a combination of multiple tick packets
// [2-bytes num packets][size1][tick1][size2][tick2] ...
function parseBinary (binpacks) {
var packets = splitPackets(binpacks),
ticks = []
for (var n = 0; n < packets.length; n++) {
var bin = packets[n],
instrument_token = buf2long(bin.slice(0, 4)),
segment = instrument_token & 0xff
var tradable = true
if (segment === Indices) tradable = false
var divisor = 100.0
if (segment === NseCD) divisor = 10000000.0
// Parse LTP
if (bin.byteLength === 8) {
ticks.push({
tradable: tradable,
mode: modeLTP,
instrument_token: instrument_token,
last_price: buf2long(bin.slice(4, 8)) / divisor
})
// Parse indices quote and full mode
} else if (bin.byteLength === 28 || bin.byteLength === 32) {
var mode = modeQuote
if (bin.byteLength === 32) mode = modeFull
var tick = {
tradable: tradable,
mode: mode,
instrument_token: instrument_token,
last_price: buf2long(bin.slice(4, 8)) / divisor,
ohlc: {
high: buf2long(bin.slice(8, 12)) / divisor,
low: buf2long(bin.slice(12, 16)) / divisor,
open: buf2long(bin.slice(16, 20)) / divisor,
close: buf2long(bin.slice(20, 24)) / divisor
},
change: buf2long(bin.slice(24, 28))
}
// Compute the change price using close price and last price
if (tick.ohlc.close != 0) {
tick.change = (tick.last_price - tick.ohlc.close) * 100 / tick.ohlc.close
}
// Full mode with timestamp in seconds
if (bin.byteLength === 32) {
tick.timestamp = null
var timestamp = buf2long(bin.slice(28, 32))
if (timestamp) tick.timestamp = new Date(timestamp)
}
ticks.push(tick)
} else if (bin.byteLength === 44 || bin.byteLength === 184) {
var mode = modeQuote
if (bin.byteLength === 184) mode = modeFull
var tick = {
tradable: tradable,
mode: mode,
instrument_token: instrument_token,
last_price: buf2long(bin.slice(4, 8)) / divisor,
last_quantity: buf2long(bin.slice(8, 12)),
average_price: buf2long(bin.slice(12, 16)) / divisor,
volume: buf2long(bin.slice(16, 20)),
buy_quantity: buf2long(bin.slice(20, 24)),
sell_quantity: buf2long(bin.slice(24, 28)),
ohlc: {
open: buf2long(bin.slice(28, 32)) / divisor,
high: buf2long(bin.slice(32, 36)) / divisor,
low: buf2long(bin.slice(36, 40)) / divisor,
close: buf2long(bin.slice(40, 44)) / divisor
}
}
// Compute the change price using close price and last price
if (tick.ohlc.close != 0) {
tick.change = (tick.last_price - tick.ohlc.close) * 100 / tick.ohlc.close
}
// Parse full mode
if (bin.byteLength === 184) {
// Parse last trade time
tick.last_trade_time = null
var last_trade_time = buf2long(bin.slice(44, 48))
if (last_trade_time) tick.last_trade_time = new Date(last_trade_time * 1000)
// Parse timestamp
tick.timestamp = null
var timestamp = buf2long(bin.slice(60, 64))
if (timestamp) tick.timestamp = new Date(timestamp * 1000)
// Parse OI
tick.oi = buf2long(bin.slice(48, 52))
tick.oi_day_high = buf2long(bin.slice(52, 56))
tick.oi_day_low = buf2long(bin.slice(56, 60))
tick.depth = {
buy: [],
sell: []
}
var s = 0, depth = bin.slice(64, 184)
for (var i = 0; i < 10; i++) {
s = i * 12
tick.depth[i < 5 ? 'buy' : 'sell'].push({
quantity: buf2long(depth.slice(s, s + 4)),
price: buf2long(depth.slice(s + 4, s + 8)) / divisor,
orders: buf2long(depth.slice(s + 8, s + 10))
})
}
}
ticks.push(tick)
}
}
return ticks
}
// split one long binary message into individual tick packets
function splitPackets (bin) {
// number of packets
var num = buf2long(bin.slice(0, 2)),
j = 2,
packets = []
for (var i = 0; i < num; i++) {
// first two bytes is the packet length
var size = buf2long(bin.slice(j, j + 2)),
packet = bin.slice(j + 2, j + 2 + size)
packets.push(packet)
j += 2 + size
}
return packets
}
function attemptReconnection () {
// Try reconnecting only so many times.
if (current_reconnection_count > reconnect_max_tries) {
trigger('noreconnect')
process.exit(1)
}
if (current_reconnection_count > 0) {
last_reconnect_interval = Math.pow(2, current_reconnection_count)
} else if (!last_reconnect_interval) {
last_reconnect_interval = 1
}
if (last_reconnect_interval > reconnect_max_delay) {
last_reconnect_interval = reconnect_max_delay
}
current_reconnection_count++
trigger('reconnect', [current_reconnection_count, last_reconnect_interval])
reconnect_timer = setTimeout(function () {
self.connect()
}, last_reconnect_interval * 1000)
}
// Big endian byte array to long.
function buf2long (buf) {
var b = new Uint8Array(buf),
val = 0,
len = b.length
for (var i = 0, j = len - 1; i < len; i++, j--) {
val += b[j] << (i * 8)
}
return val
}
// de-duplicate an array
function arrayUnique () {
var u = {}, a = []
for (var i = 0, l = this.length; i < l; ++i) {
if (u.hasOwnProperty(this[i])) {
continue
}
a.push(this[i])
u[this[i]] = 1
}
return a
}
var self = this
}
@rkolagatla
Copy link

rkolagatla commented Oct 14, 2023

There is a place where Microsoft and Google meet and it is Browser Wars as well as they conspired through JavaScript and Adobe Flash.

Zerodha has made a case up for taking me to UNITED NATIONS SECURITY COUNCIL as this user is/was an Employee of ZERODHA.
He is a Malayali. Auction.com and Ooty Schools. Shanti........

This just won't work - Don't waste your TIME and Microsoft buys GitHub.....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment