-
-
Save ajinasokan/b36d17da1a58af62782978c16bca309a to your computer and use it in GitHub Desktop.
<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 | |
} |
@rahulmr I don't know what you meant by dynamic. It already is. You can provide any token to subscribe from anywhere in the script. You can build UI in that HTML file and connect it to JS.
@ajinasokan Got it, thanks. I was looking for an example where I can type symbol and then subscribe. I understood you just wanted to have a simple example in here.
@ajinasokan Could you please share a similar js file for kiteconnect as well. I tried creating one using browserify. But facing few issues.
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.....
Great work @ajinasokan . Is it possible to implement a dynamic way to subscribe to token? Mean to say how can we process ticks further using some UI button etc..