-- --------------------------------------------------------------- -- Tick Statistics -- --------------------------------------------------------------- -- $Header -- --------------------------------------------------------------- -- -- Program Name : TickStatistics -- Function : script executed by Geneos netprobe -- : to examine a Market Data Feed -- Author : Richard Gould -- Language : Lua (5.1) -- Creation : 01/02/2015 -- History : -- -- 01/02/2015 0.00 -> 1.00 RG Creation -- 25/06/2015 1.00 -> 2.00 RG Adaption -- -- --------------------------------------------------------------- local Program = "TickStatistics" local Version = "2" local Revision = "01" -- --------------------------------------------------------------- local sa = require 'geneos.sampler' local md = require 'geneos.marketdata' local sc = require 'geneos.sampler.commands' local he = require 'geneos.helpers' -- --------------------------------------------------------------- -- Config file is pathed by parameter from Gateway local ConfigDir = sa.params[ 'ConfigDir' ] package.path = package.path .. ";" .. ConfigDir .. "?.lua" local config = require "config" -- --------------------------------------------------------------- -- These are the default values local feedName = sa.params[ 'feedName' ] or "bloomberg" local viewName = sa.params[ 'viewName' ] or "Tick Statistics" -- Timezone offset config.offset = config.offset or 0 -- End of Defaults -- --------------------------------------------------------------- -- define columns and publish view local cols = { "instrument", "avgAsk", "avgBid", "minSpread", "maxSpread", "Ticks", "avgSpread", "maxInterval", "tradePrice" } local view = assert( sa.createView( viewName, cols ) ) -- --------------------------------------------------------------- -- Load the feed parameters and start the feeds local Feed_0 = md.addFeed( viewName, config[ feedName ] ) Feed_0:start() -- --------------------------------------------------------------- local instruments = config[ feedName ].instruments local totalTicks = 0 local SampleTime = he.formatTime( "%d/%m/%y %H:%M:%S", he.gettimeofday() ) local prevTick = {} -- A table to map instrument names to the last tick seen for each instrument -- --------------------------------------------------------------- -- Publish the headlines view.headline.totalTicks = totalTicks view.headline.Feed = feedName view.headline.Now = SampleTime view.headline.Version = Version .. "." .. Revision view:publish() -- --------------------------------------------------------------- -- utility function to round down values local function roundDown( value, dp ) if ( type( value ) ~= number ) then value = value or 0 end local dpformat = "%4." .. dp .. "f" local mult = 10^( dp or 0 ) if ( value == 0 ) then return string.format( dpformat, 0 ) end return not value or string.format( dpformat, math.ceil( value * mult - 0.5 ) / mult ) end calculateRowStats = function( instrument_name ) local tick = prevTick[instrument_name] or { field = {} } tick.next = Feed_0:getTicks(instrument_name) -- get list of ticks for this instrument if tick.next then local minSpread, maxSpread -- minimun and maximum spread values local maxInterval = 0 -- max interval b/n ticks local tickCount = 0 -- total tick count per instrument local sumBid, sumAsk, sumSpread = 0, 0, 0 -- summation of bid, ask and spread values for all ticks local validBidAskCount = 0 -- count of all ticks with valid bid and ask values local lastTime = tick.timeLast -- Extract timeLast value from the last tick of the previous sample -- get the sumSpread, sumAsk, sumBid, minSpread, maxSpread, validBidAskCount while tick.next do tick = tick.next -- compute for sumSpread, sumAsk, sumBid, minSpread, maxSpread (only if Bid & Ask values are available) if ('' ~= tick.field.Bid and '' ~= tick.field.Ask) then local spread = tick.field.Ask - tick.field.Bid sumSpread = sumSpread + spread sumAsk = sumAsk + tick.field.Ask sumBid = sumBid + tick.field.Bid -- get min and max spread if not minSpread or spread < minSpread then minSpread = spread end if not maxSpread or spread > maxSpread then maxSpread = spread end validBidAskCount = validBidAskCount + 1 end -- get maximum interval local interval = tick.timeFirst - (lastTime or tick.timeFirst) if interval > maxInterval then maxInterval = interval end lastTime = tick.timeLast -- get tick count tickCount = tickCount + 1 end prevTick[instrument_name] = tick return { minSpread = roundDown( minSpread, 4 ), maxSpread = roundDown( maxSpread, 4 ), Ticks = tickCount, maxInterval = roundDown( maxInterval, 4 ), avgBid = roundDown( sumBid / validBidAskCount, 2 ), avgAsk = roundDown( sumAsk / validBidAskCount, 2 ), avgSpread = roundDown( sumSpread / validBidAskCount, 4 ), tradePrice = tick.field.Trade -- Trade price from the last tick }, tickCount else return {}, 0 end end sa.doSample = function() SampleTime = he.formatTime( "%d/%m/%y %H:%M:%S", he.gettimeofday() ) for name,_ in pairs ( instruments ) do local rowResult, additionalTicks = calculateRowStats( name ) if ( additionalTicks >= 1 ) then view.row[ name ] = rowResult totalTicks = totalTicks + additionalTicks end end view.headline.totalTicks = totalTicks view.headline.Now = SampleTime view:publish() end -- create the command and add to the headline local Reset = sc.newDefinition() :addHeadlineTarget( view, "totalTicks" ) -- added to headline that matches the name 'totalTicks' local resetTotalTicks = function( target, args ) totalTicks = 0 -- reset counter view.headline.totalTicks = totalTicks -- update the view view:publish() -- publish out updated view end assert( sa.publishCommand( "Reset Tick Count", resetTotalTicks, Reset ) )