469 lines
18 KiB
Lua
469 lines
18 KiB
Lua
--[[
|
|
Auctioneer - Standard Deviation Statistics module
|
|
Version: 5.7.4568 (KillerKoala)
|
|
Revision: $Id: StatStdDev.lua 4840 2010-08-04 21:44:00Z Nechckn $
|
|
URL: http://auctioneeraddon.com/
|
|
|
|
This is an addon for World of Warcraft that adds statistical history to the auction data that is collected
|
|
when the auction is scanned, so that you can easily determine what price
|
|
you will be able to sell an item for at auction or at a vendor whenever you
|
|
mouse-over an item in the game
|
|
|
|
License:
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program(see GPL.txt); if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
Note:
|
|
This AddOn's source code is specifically designed to work with
|
|
World of Warcraft's interpreted AddOn system.
|
|
You have an implicit license to use this AddOn with these facilities
|
|
since that is its designated purpose as per:
|
|
http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat
|
|
--]]
|
|
if not AucAdvanced then return end
|
|
local AucAdvanced = AucAdvanced
|
|
|
|
local libType, libName = "Stat", "StdDev"
|
|
local lib,parent,private = AucAdvanced.NewModule(libType, libName)
|
|
if not lib then return end
|
|
|
|
local aucPrint,decode,_,_,replicate,empty,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals()
|
|
local tonumber,strsplit,select,pairs=tonumber,strsplit,select,pairs
|
|
local setmetatable=setmetatable
|
|
local wipe=wipe
|
|
local floor,ceil,abs=floor,ceil,abs
|
|
local concat=table.concat
|
|
local tinsert,tremove=table.insert,table.remove
|
|
-- GLOBALS: AucAdvancedStatStdDevData
|
|
|
|
local GetFaction = AucAdvanced.GetFaction
|
|
|
|
local SSDRealmData
|
|
|
|
local cache = {} -- setmetatable({}, {__mode="v"})
|
|
|
|
local ZValues = {.063, .126, .189, .253, .319, .385, .454, .525, .598, .675, .756, .842, .935, 1.037, 1.151, 1.282, 1.441, 1.646, 1.962, 20, 20000}
|
|
|
|
function lib.CommandHandler(command, ...)
|
|
local serverKey = GetFaction()
|
|
local _,_,keyText = AucAdvanced.SplitServerKey(serverKey)
|
|
if (command == "help") then
|
|
aucPrint(_TRANS('SDEV_Help_SlashHelp1') )--Help for Auctioneer - StdDev
|
|
local line = AucAdvanced.Config.GetCommandLead(libType, libName)
|
|
aucPrint(line, "help}} - ".._TRANS('SDEV_Help_SlashHelp2') ) --this StdDev help
|
|
aucPrint(line, "clear}} - ".._TRANS('SDEV_Help_SlashHelp3'):format(keyText) ) --clear current %s StdDev price database
|
|
elseif (command == "clear") then
|
|
lib.ClearData(serverKey)
|
|
end
|
|
end
|
|
|
|
function lib.Processor(callbackType, ...)
|
|
if (callbackType == "tooltip") then
|
|
lib.ProcessTooltip(...)
|
|
elseif (callbackType == "config") then
|
|
--Called when you should build your Configator tab.
|
|
private.SetupConfigGui(...)
|
|
elseif (callbackType == "scanstats") then
|
|
wipe(cache)
|
|
end
|
|
end
|
|
lib.Processors = {}
|
|
function lib.Processors.tooltip(callbackType, ...)
|
|
lib.ProcessTooltip(...)
|
|
end
|
|
function lib.Processors.config(callbackType, ...)
|
|
--Called when you should build your Configator tab.
|
|
private.SetupConfigGui(...)
|
|
end
|
|
function lib.Processors.scanstats(callbackType, ...)
|
|
wipe(cache)
|
|
end
|
|
|
|
|
|
|
|
lib.ScanProcessors = {}
|
|
function lib.ScanProcessors.create(operation, itemData, oldData)
|
|
if not AucAdvanced.Settings.GetSetting("stat.stddev.enable") then return end
|
|
|
|
-- This function is responsible for processing and storing the stats after each scan
|
|
-- Note: itemData gets reused over and over again, so do not make changes to it, or use
|
|
-- it in places where you rely on it. Make a deep copy of it if you need it after this
|
|
-- function returns.
|
|
|
|
-- We're only interested in items with buyouts.
|
|
local buyout = itemData.buyoutPrice
|
|
if not buyout or buyout == 0 then return end
|
|
if (itemData.stackSize > 1) then
|
|
buyout = buyout.."/"..itemData.stackSize
|
|
end
|
|
|
|
-- Get the signature of this item and find it's stats.
|
|
local linkType,itemId,property,factor = AucAdvanced.DecodeLink(itemData.link)
|
|
if (linkType ~= "item") then return end
|
|
if (factor and factor ~= 0) then property = property.."x"..factor end
|
|
|
|
local serverKey = GetFaction()
|
|
if not SSDRealmData[serverKey] then SSDRealmData[serverKey] = {} end
|
|
local stats = private.UnpackStats(SSDRealmData[serverKey][itemId])
|
|
if not stats[property] then stats[property] = {} end
|
|
if #stats[property] >= 100 then
|
|
tremove(stats[property], 1)
|
|
end
|
|
tinsert(stats[property], buyout)
|
|
SSDRealmData[serverKey][itemId] = private.PackStats(stats)
|
|
end
|
|
|
|
local BellCurve = AucAdvanced.API.GenerateBellCurve();
|
|
-----------------------------------------------------------------------------------
|
|
-- The PDF for standard deviation data, standard bell curve
|
|
-----------------------------------------------------------------------------------
|
|
function lib.GetItemPDF(hyperlink, serverKey)
|
|
if not AucAdvanced.Settings.GetSetting("stat.stddev.enable") then return end
|
|
-- Get the data
|
|
local average, mean, _, stddev, variance, count, confidence = lib.GetPrice(hyperlink, serverKey)
|
|
|
|
if not (average and stddev) or average == 0 or stddev == 0 then
|
|
return nil; -- No data, cannot determine pricing
|
|
end
|
|
|
|
local lower, upper = average - 3 * stddev, average + 3 * stddev;
|
|
|
|
-- Build the PDF based on standard deviation & average
|
|
BellCurve:SetParameters(average, stddev);
|
|
return BellCurve, lower, upper; -- This has a __call metamethod so it's ok
|
|
end
|
|
|
|
-----------------------------------------------------------------------------------
|
|
|
|
function private.GetCfromZ(Z)
|
|
--C = 0.05*i
|
|
if (not Z) then
|
|
return .05
|
|
end
|
|
if (Z > 10) then
|
|
return .99
|
|
end
|
|
local i = 1
|
|
while Z > ZValues[i] do
|
|
i = i + 1
|
|
end
|
|
if i == 1 then
|
|
return .05
|
|
else
|
|
i = i - 1 + ((Z - ZValues[i-1]) / (ZValues[i] - ZValues[i-1]))
|
|
return i*0.05
|
|
end
|
|
end
|
|
|
|
local datapoints_price = {} -- used temporarily in .GetPrice() to avoid unpacking strings multiple times
|
|
local datapoints_stack = {}
|
|
|
|
function lib.GetPrice(hyperlink, serverKey)
|
|
if not AucAdvanced.Settings.GetSetting("stat.stddev.enable") then return end
|
|
|
|
local linkType,itemId,property,factor = AucAdvanced.DecodeLink(hyperlink)
|
|
if (linkType ~= "item") then return end
|
|
if (factor and factor ~= 0) then property = property.."x"..factor end
|
|
|
|
if not serverKey then serverKey = GetFaction() end
|
|
|
|
if not SSDRealmData[serverKey] then return end
|
|
if not SSDRealmData[serverKey][itemId] then return end
|
|
|
|
local cacheKey = serverKey ..":"..itemId..":"..property
|
|
if cache[cacheKey] then
|
|
return unpack(cache[cacheKey])
|
|
end
|
|
|
|
local stats = private.UnpackStats(SSDRealmData[serverKey][itemId])
|
|
if not stats[property] then return end
|
|
|
|
local count = #stats[property]
|
|
if (count < 1) then return end
|
|
|
|
local total, number = 0, 0
|
|
for i = 1, count do
|
|
local price, stack = strsplit("/", stats[property][i])
|
|
price = tonumber(price) or 0
|
|
stack = tonumber(stack) or 1
|
|
if (stack < 1) then stack = 1 end
|
|
datapoints_price[i] = price -- cache these for further processing below (so they don't need to strsplit etc)
|
|
datapoints_stack[i] = stack
|
|
total = total + price
|
|
number = number + stack
|
|
end
|
|
local mean = total / number
|
|
|
|
if (count < 2) then return 0,0,0, mean, count end
|
|
|
|
local variance = 0
|
|
for i = 1, count do
|
|
variance = variance + ((mean - datapoints_price[i]/datapoints_stack[i]) ^ 2);
|
|
end
|
|
|
|
variance = variance / count;
|
|
local stdev = variance ^ 0.5
|
|
|
|
local deviation = 1.5 * stdev
|
|
|
|
total = 0 -- recompute them from entries inside the allowed deviation
|
|
number = 0
|
|
|
|
for i = 1, count do
|
|
local price,stack = datapoints_price[i], datapoints_stack[i]
|
|
if abs((price/stack) - mean) < deviation then
|
|
total = total + price
|
|
number = number + stack
|
|
end
|
|
end
|
|
|
|
local confidence = .01
|
|
local average
|
|
if (number > 0) then -- number<1 will happen if we have e.g. two big clusters: one at 1g and one at 10g
|
|
average = total / number
|
|
confidence = (.15*average)*(number^0.5)/(stdev)
|
|
confidence = private.GetCfromZ(confidence)
|
|
end
|
|
|
|
cache[cacheKey] = { average, mean, false, stdev, variance, count, confidence }
|
|
return average, mean, false, stdev, variance, count, confidence
|
|
end
|
|
|
|
function lib.GetPriceColumns()
|
|
return "Average", "Mean", false, "Std Deviation", "Variance", "Count"
|
|
end
|
|
|
|
local array = {}
|
|
function lib.GetPriceArray(hyperlink, serverKey)
|
|
if not AucAdvanced.Settings.GetSetting("stat.stddev.enable") then return end
|
|
-- Clean out the old array
|
|
wipe(array)
|
|
|
|
-- Get our statistics
|
|
local average, mean, _, stdev, variance, count, confidence = lib.GetPrice(hyperlink, serverKey)
|
|
|
|
-- These 3 are the ones that most algorithms will look for
|
|
array.price = average or mean
|
|
array.seen = count
|
|
array.confidence = confidence
|
|
-- This is additional data
|
|
array.normalized = average
|
|
array.mean = mean
|
|
array.deviation = stdev
|
|
array.variance = variance
|
|
|
|
-- Return a temporary array. Data in this array is
|
|
-- only valid until this function is called again.
|
|
return array
|
|
end
|
|
|
|
function private.SetupConfigGui(gui)
|
|
local id = gui:AddTab(lib.libName, lib.libType.." Modules")
|
|
--gui:MakeScrollable(id)
|
|
|
|
gui:AddHelp(id, "what stddev stats",
|
|
_TRANS('SDEV_Help_StdDevStats') ,--What are StdDev stats?
|
|
_TRANS('SDEV_Help_StdDevStatsAnswer') --StdDev stats are the numbers that are generated by the StdDev module consisting of a filtered Standard Deviation calculation of item cost.
|
|
)
|
|
|
|
--all options in here will be duplicated in the tooltip frame
|
|
function private.addTooltipControls(id)
|
|
gui:AddHelp(id, "filtered stddev",
|
|
_TRANS('SDEV_Help_Filtered') ,--What do you mean filtered?
|
|
_TRANS('SDEV_Help_FilteredAnswer') --Items outside a (1.5*Standard) variance are ignored and assumed to be wrongly priced when calculating the deviation.
|
|
)
|
|
|
|
gui:AddHelp(id, "what standard deviation",
|
|
_TRANS('SDEV_Help_StandardDeviationCalculation') ,--What is a Standard Deviation calculation?
|
|
_TRANS('SDEV_Help_StandardDeviationCalculationAnswer') --In short terms, it is a distance to mean average calculation.
|
|
)
|
|
|
|
gui:AddHelp(id, "what normalized",
|
|
_TRANS('SDEV_Help_Normalized') ,--What is the Normalized calculation?
|
|
_TRANS('SDEV_Help_NormalizedAnswer') --In short terms again, it is the average of those values determined within the standard deviation variance calculation.
|
|
)
|
|
|
|
gui:AddHelp(id, "what confidence",
|
|
_TRANS('SDEV_Help_Confidence') ,--What does confidence mean?
|
|
_TRANS('SDEV_Help_ConfidenceAnswer') --Confidence is a value between 0 and 1 that determines the strength of the calculations (higher the better).
|
|
)
|
|
|
|
gui:AddHelp(id, "why multiply stack size stddev",
|
|
_TRANS('SDEV_Help_WhyMultiplyStack') ,--Why have the option to multiply by stack size?
|
|
_TRANS('SDEV_Help_WhyMultiplyStackAnswer') --The original Stat-StdDev multiplied by the stack size of the item, but some like dealing on a per-item basis.
|
|
)
|
|
|
|
gui:AddControl(id, "Header", 0, _TRANS('SDEV_Interface_StdDevOptions') )--StdDev options
|
|
gui:AddControl(id, "Note", 0, 1, nil, nil, " ")
|
|
gui:AddControl(id, "Checkbox", 0, 1, "stat.stddev.enable", _TRANS('SDEV_Interface_EnableStdDevStats') )--Enable StdDev Stats
|
|
gui:AddTip(id, _TRANS('SDEV_HelpTooltip_EnableStdDevStats') )--Allow StdDev to gather and return price data
|
|
gui:AddControl(id, "Note", 0, 1, nil, nil, " ")
|
|
|
|
gui:AddControl(id, "Checkbox", 0, 4, "stat.stddev.tooltip", _TRANS('SDEV_Interface_Show') )--Show stddev stats in the tooltips?
|
|
gui:AddTip(id, _TRANS('SDEV_HelpTooltip_Show') )--Toggle display of stats from the StdDev module on or off
|
|
gui:AddControl(id, "Checkbox", 0, 6, "stat.stddev.mean", _TRANS('SDEV_Interface_DisplayMean') )--Display Mean
|
|
gui:AddTip(id, _TRANS('SDEV_HelpTooltip_DisplayMean') )--Toggle display of 'Mean' calculation in tooltips on or off
|
|
gui:AddControl(id, "Checkbox", 0, 6, "stat.stddev.normal", _TRANS('SDEV_Interface_DisplayNormalized') )--Display Normalized
|
|
gui:AddTip(id, _TRANS('SDEV_HelpTooltip_DisplayNormalized') )--Toggle display of 'Normalized' calculation in tooltips on or off'
|
|
gui:AddControl(id, "Checkbox", 0, 6, "stat.stddev.stdev", _TRANS('SDEV_Interface_DisplayStandardDeviation') )--Display Standard Deviation
|
|
gui:AddTip(id,_TRANS('SDEV_HelpTooltip_DisplayStandardDeviation') )--Toggle display of 'Standard Deviation' calculation in tooltips on or off
|
|
gui:AddControl(id, "Checkbox", 0, 6, "stat.stddev.confid", _TRANS('SDEV_Interface_DisplayConfidence') )--Display Confidence
|
|
gui:AddTip(id,_TRANS('SDEV_HelpTooltip_DisplayConfidence') )--Toggle display of 'Confidence' calculation in tooltips on or off
|
|
gui:AddControl(id, "Note", 0, 1, nil, nil, " ")
|
|
gui:AddControl(id, "Checkbox", 0, 4, "stat.stddev.quantmul", _TRANS('SDEV_Interface_MultiplyStack') )--Multiply by Stack Size
|
|
gui:AddTip(id,_TRANS('SDEV_HelpTooltip_MultiplyStack') )--Multiplies by current stack size if on
|
|
gui:AddControl(id, "Note", 0, 1, nil, nil, " ")
|
|
end
|
|
--This is the Tooltip tab provided by aucadvnced so all tooltip configuration is in one place
|
|
local tooltipID = AucAdvanced.Settings.Gui.tooltipID
|
|
|
|
--now we create a duplicate of these in the tooltip frame
|
|
private.addTooltipControls(id)
|
|
if tooltipID then private.addTooltipControls(tooltipID) end
|
|
end
|
|
|
|
function lib.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost, ...)
|
|
-- In this function, you are afforded the opportunity to add data to the tooltip should you so
|
|
-- desire. You are passed a hyperlink, and it's up to you to determine whether or what you should
|
|
-- display in the tooltip.
|
|
|
|
if not AucAdvanced.Settings.GetSetting("stat.stddev.tooltip") then return end
|
|
|
|
if not quantity or quantity < 1 then quantity = 1 end
|
|
if not AucAdvanced.Settings.GetSetting("stat.stddev.quantmul") then quantity = 1 end
|
|
local average, mean, _, stdev, var, count, confidence = lib.GetPrice(hyperlink)
|
|
|
|
if (mean and mean > 0) then
|
|
tooltip:AddLine(_TRANS('SDEV_Tooltip_PricesPoints'):format(count) )--StdDev prices %d points:
|
|
|
|
if AucAdvanced.Settings.GetSetting("stat.stddev.mean") then
|
|
tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_MeanPrice'), mean*quantity)-- Mean price
|
|
end
|
|
if (average and average > 0) then
|
|
if AucAdvanced.Settings.GetSetting("stat.stddev.normal") then
|
|
tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_Normalized'), average*quantity)-- Normalized
|
|
if (quantity > 1) then
|
|
tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_Individually'), average)-- (or individually)
|
|
end
|
|
end
|
|
if AucAdvanced.Settings.GetSetting("stat.stddev.stdev") then
|
|
tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_StdDeviation'), stdev*quantity)-- Std Deviation
|
|
if (quantity > 1) then
|
|
tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_Individually'), stdev)-- (or individually)
|
|
end
|
|
|
|
end
|
|
if AucAdvanced.Settings.GetSetting("stat.stddev.confid") then
|
|
tooltip:AddLine(" ".._TRANS('SDEV_Tooltip_Confidence')..(floor(confidence*1000))/1000)-- Confidence:
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function lib.OnLoad(addon)
|
|
if SSDRealmData then return end
|
|
|
|
AucAdvanced.Settings.SetDefault("stat.stddev.tooltip", false)
|
|
AucAdvanced.Settings.SetDefault("stat.stddev.mean", false)
|
|
AucAdvanced.Settings.SetDefault("stat.stddev.normal", false)
|
|
AucAdvanced.Settings.SetDefault("stat.stddev.stdev", true)
|
|
AucAdvanced.Settings.SetDefault("stat.stddev.confid", true)
|
|
AucAdvanced.Settings.SetDefault("stat.stddev.quantmul", true)
|
|
AucAdvanced.Settings.SetDefault("stat.stddev.enable", true)
|
|
|
|
private.InitData()
|
|
end
|
|
|
|
function lib.ClearItem(hyperlink, serverKey)
|
|
local linkType, itemID, property, factor = AucAdvanced.DecodeLink(hyperlink)
|
|
if (linkType ~= "item") then
|
|
return
|
|
end
|
|
if (factor ~= 0) then property = property.."x"..factor end
|
|
if not serverKey then serverKey = GetFaction() end
|
|
if SSDRealmData[serverKey] and SSDRealmData[serverKey][itemID] then
|
|
local stats = private.UnpackStats(SSDRealmData[serverKey][itemID])
|
|
if stats[property] then
|
|
stats[property] = nil
|
|
SSDRealmData[serverKey][itemID] = private.PackStats(stats)
|
|
wipe(cache)
|
|
local _, _, keyText = AucAdvanced.SplitServerKey(serverKey)
|
|
aucPrint(libType.._TRANS('SDEV_Interface_ClearingData'):format(hyperlink, keyText))--- StdDev: clearing data for %s for {{%s}}
|
|
end
|
|
end
|
|
end
|
|
|
|
function lib.ClearData(serverKey)
|
|
serverKey = serverKey or GetFaction()
|
|
wipe(cache)
|
|
if AucAdvanced.API.IsKeyword(serverKey, "ALL") then
|
|
wipe(SSDRealmData)
|
|
aucPrint(_TRANS('SDEV_Help_SlashHelp4').." {{".._TRANS("ADV_Interface_AllRealms").."}}") --Clearing StdDev stats for // All realms
|
|
elseif SSDRealmData[serverKey] then
|
|
SSDRealmData[serverKey] = nil
|
|
local _, _, keyText = AucAdvanced.SplitServerKey(serverKey)
|
|
aucPrint(_TRANS('SDEV_Help_SlashHelp4').." {{"..keyText.."}}") --Clearing StdDev stats for
|
|
end
|
|
end
|
|
|
|
--[[ Local functions ]]--
|
|
|
|
function private.UnpackStatIter(data, ...)
|
|
local c = select("#", ...)
|
|
local v
|
|
for i = 1, c do
|
|
v = select(i, ...)
|
|
local property, info = strsplit(":", v)
|
|
property = tonumber(property) or property
|
|
if (property and info) then
|
|
data[property] = {strsplit(";", info)}
|
|
local item
|
|
for i=1, #data[property] do
|
|
item = data[property][i]
|
|
data[property][i] = tonumber(item) or item
|
|
end
|
|
end
|
|
end
|
|
end
|
|
function private.UnpackStats(dataItem)
|
|
local data = {}
|
|
if (dataItem) then
|
|
private.UnpackStatIter(data, strsplit(",", dataItem))
|
|
end
|
|
return data
|
|
end
|
|
|
|
local tmp={}
|
|
function private.PackStats(data)
|
|
local n=0
|
|
for property, info in pairs(data) do
|
|
n=n+1
|
|
tmp[n]=property..":"..concat(info, ";")
|
|
end
|
|
return concat(tmp,",",1,n)
|
|
end
|
|
|
|
function private.InitData()
|
|
private.InitData = nil
|
|
|
|
-- Do any database upgrades here
|
|
if not AucAdvancedStatStdDevData then AucAdvancedStatStdDevData = {} end
|
|
|
|
SSDRealmData = AucAdvancedStatStdDevData
|
|
|
|
-- Do any regular database maintenance here
|
|
end
|
|
|
|
|
|
AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Stat-StdDev/StatStdDev.lua $", "$Rev: 4840 $")
|