private, public = {}, {} Aux.stat_average = public private.PUSH_INTERVAL = 57600 function private.load_data() local dataset = Aux.persistence.load_dataset() dataset.stat_average_data = dataset.stat_average_data or {} return dataset.stat_average_data end function private.get_item_key(auction_info) return auction_info.item_id..':'..auction_info.suffix_id end function private.process_auction(auction_info) if auction_info.buyout_price == 0 then return end local item_record = private.load_data()[get_item_key(auction_info)] or { auction_count = 0, unit_count = 0, accumulated_buyout_price = 0, } item_record.auction_count = item_record.auction_count + 1 item_record.unit_count = item_record.unit_count + auction_info.count item_record.accumulated_buyout_price = item_record.accumulated_buyout_price + auction_info.buyout_price --[[ data = { [1] = total buyout, [2] = seen count, [3] = today's minimum buyout, [4] = auctions count, (only recorded if different from seen count) }--]] if data0 then local aucs = data0[4] if aucs then data0[4] = aucs + 1 elseif stack > 1 then -- no recorded auctions count, so auctions count must have been equal to seen count up to this point data0[4] = data0[2] + 1 end local mbo = data0[3] if mbo == 0 or buyoutper < mbo then data0[3] = buyoutper end data0[2] = data0[2] + stack data0[1] = data0[1] + buyout end if dataP then local aucs = dataP[4] if aucs then dataP[4] = aucs + 1 elseif stack > 1 then -- no recorded auctions count, so auctions count must have been equal to seen count up to this point dataP[4] = dataP[2] + 1 end local mbo = dataP[3] if mbo == 0 or buyoutper < mbo then dataP[3] = buyoutper end dataP[2] = dataP[2] + stack dataP[1] = dataP[1] + buyout end private.WriteItemData(serverKey, storeID, storeProperty) end function private.push_data() local realmdata = SSRealmData[serverKey] if not realmdata then return end local daily, means = realmdata.daily, realmdata.means local lookupmeansdata, lookupmeansindex = {}, {} for storeID, itemstring in pairs(daily) do -- find the means data for this storeID, and build lookup tables to help cross-index -- we leave the last (DATA_DIVIDER) level of the data as strings for now; later we will fully unpack only the ones we need local itemstoremeans if means[storeID] then itemstoremeans = {strsplit(ITEM_DIVIDER, means[storeID])} else itemstoremeans = {} end for index, propertystring in ipairs(itemstoremeans) do local prop, datastringmeans = strsplit(PROPERTY_DIVIDER, propertystring) lookupmeansdata[prop] = datastringmeans lookupmeansindex[prop] = index -- remember where we got datastringmeans from, so we can put the revised datastring back in the same place end local itemsdaily = {strsplit(ITEM_DIVIDER, itemstring)} for index, propertystring in ipairs(itemsdaily) do -- extract daily data entries for this itemID & property local prop, datastringdaily = strsplit(PROPERTY_DIVIDER, propertystring) local dailybuy, dailyseen, dailymbo, dailyauctions = strsplit(DATA_DIVIDER, datastringdaily) dailybuy, dailyseen, dailymbo, dailyauctions = tonumber(dailybuy), tonumber(dailyseen), tonumber(dailymbo), tonumber(dailyauctions) local dailyavg = dailybuy -- dailyseen may be 0 for certain unusual items which do not modify property "0" -- our database format requires that there must always be a property "0" (which must always be at index 1) -- if no items of that base type with property "0" were seen today, the entry will be empty (all 0) if dailyseen > 0 then dailyavg = dailyavg / dailyseen end -- look for existing means data for this property local datastringmeans = lookupmeansdata[prop] if datastringmeans then if dailyseen > 0 then datameans = {strsplit(DATA_DIVIDER, datastringmeans)} -- seendays, seencount, EMA3, EMA7, EMA14, avgminbuy [, seenauctions] for k, v in ipairs(datameans) do datameans[k] = tonumber(v) end -- update means data for this entry local seendays = datameans[1] local newseendays = seendays + 1 datameans[1] = newseendays datameans[2] = datameans[2] + dailyseen local seenauctions = datameans[7] if seenauctions then datameans[7] = seenauctions + (dailyauctions or dailyseen) else datameans[7] = dailyauctions -- may be nil end if seendays < 3 then -- for first 3 days perform plain average insead of EMAs datameans[3] = numberformat((datameans[3] * seendays + dailyavg) / newseendays) -- EMA3 datameans[6] = numberformat((datameans[6] * seendays + dailymbo) / newseendays) -- average minimum buyout if newseendays == 3 then -- this is the third day, prep other EMAs for next time datameans[4] = datameans[3] datameans[5] = datameans[3] end else -- do normal EMA calculations datameans[3] = numberformat((datameans[3] * 2 + dailyavg) / 3) -- EMA3 datameans[4] = numberformat((datameans[4] * 6 + dailyavg) / 7) -- EMA7 datameans[5] = numberformat((datameans[5] * 13 + dailyavg) / 14) -- EMA14 local avgmbo = datameans[6] -- average minimum buyout if avgmbo < 1 then datameans[6] = dailymbo else if dailymbo >= 1 then if avgmbo < dailymbo then if (avgmbo*10/dailymbo) < 9 then datameans[6] = numberformat((avgmbo+dailymbo)/2) else datameans[6] = numberformat((avgmbo*7+dailymbo)/8) end else if (dailymbo*10/avgmbo) < 9 then datameans[6] = numberformat((avgmbo+dailymbo)/2) else datameans[6] = numberformat((avgmbo*7+dailymbo)/8) end end end end end itemstoremeans[lookupmeansindex[prop]] = prop..PROPERTY_DIVIDER..tconcat(datameans, DATA_DIVIDER) end else -- this property has not been seen before, create a new entry for it -- we don't need to use an intermediate table, we can build the datastring directly -- there is no need to update lookupmeansdata[prop] or lookupmeansindex[prop], as each prop should only occur once for each storeID if dailyauctions then tinsert(itemstoremeans, newstringtemplate7:format(prop, dailyseen, dailyavg, dailymbo, dailyauctions)) -- represents data table with 7 entries else tinsert(itemstoremeans, newstringtemplate6:format(prop, dailyseen, dailyavg, dailymbo)) -- represents data table with 6 entries (missing seenauctions) end end end means[storeID] = tconcat(itemstoremeans, ITEM_DIVIDER) wipe(lookupmeansdata) wipe(lookupmeansindex) end realmdata.dailypush = time() + PUSHTIME wipe(daily) end