Skip to content

Commit

Permalink
fix(demand): update trailing purchase revenues and counts when updati… (
Browse files Browse the repository at this point in the history
#53)

…ng demand factor

This is causing demand factor to only fall, as the proper values are not
being set for the trailing purchases and revenus
  • Loading branch information
dtfiedler authored Sep 3, 2024
2 parents d5bb717 + c8099e8 commit 2a88505
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 50 deletions.
89 changes: 58 additions & 31 deletions spec/demand_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -35,69 +35,87 @@ describe("demand", function()

describe("revenue based criteria", function()
it("mvgAvgTrailingPurchaseCounts() should calculate moving average of trailing purchase counts", function()
DemandFactor.trailingPeriodPurchases = { 1, 2, 3, 4, 5, 6, 7 }
_G.DemandFactor.trailingPeriodPurchases = { 1, 2, 3, 4, 5, 6, 7 }
assert.are.equal(4, demand.mvgAvgTrailingPurchaseCounts())
end)

it("mvgAvgTrailingRevenues() should calculate moving average of trailing revenues", function()
DemandFactor.trailingPeriodRevenues = { 1, 2, 3, 4, 5, 6 }
_G.DemandFactor.trailingPeriodRevenues = { 1, 2, 3, 4, 5, 6 }
assert.are.equal(3.5, demand.mvgAvgTrailingRevenues())
end)

it("isDemandIncreasing() should return false when demand is is not increasing based on revenue", function()
DemandFactor.revenueThisPeriod = 0
DemandFactor.trailingPeriodRevenues = { 0, 10, 10, 10, 10, 10 }
_G.DemandFactor.revenueThisPeriod = 0
_G.DemandFactor.trailingPeriodRevenues = { 0, 10, 10, 10, 10, 10 }
assert.is_false(demand.isDemandIncreasing())
end)

it("isDemandIncreasing() should return true when demand is increasing based on revenue", function()
DemandFactor.revenueThisPeriod = 10
DemandFactor.trailingPeriodRevenues = { 10, 0, 0, 0, 0, 0, 0 }
_G.DemandFactor.revenueThisPeriod = 10
_G.DemandFactor.trailingPeriodRevenues = { 10, 0, 0, 0, 0, 0, 0 }
assert.is_true(demand.isDemandIncreasing())
end)

it(
"updateDemandFactor() should update demand factor if demand is increasing and a new period has started",
function()
DemandFactor.revenueThisPeriod = 10
DemandFactor.trailingPeriodRevenues = { 10, 0, 0, 0, 0, 0, 0 }
local startNextPeriodTimestamp = demand.getSettings().periodLengthMs + 1
local currentPeriod = 3
_G.DemandFactor.currentPeriod = currentPeriod
_G.DemandFactor.revenueThisPeriod = 10
_G.DemandFactor.trailingPeriodRevenues = { 5, 0, 0, 5, 0, 0, 0 }
local startNextPeriodTimestamp = demand.getSettings().periodLengthMs * currentPeriod + 1
demand.updateDemandFactor(startNextPeriodTimestamp)
assert.are.equal(1.05, demand.getDemandFactor())
assert.are.same({ 5, 0, 0, 10, 0, 0, 0 }, demand.getTrailingPeriodRevenues())
assert.are.equal(currentPeriod + 1, demand.getCurrentPeriod())
end
)

it(
"updateDemandFactor() should update demand factor if demand is decreasing and a new period has started",
function()
DemandFactor.revenueThisPeriod = 0
DemandFactor.trailingPeriodRevenues = { 0, 10, 0, 0, 0, 0, 0 }
local startNextPeriodTimestamp = demand.getSettings().periodLengthMs + 1
local currentPeriod = 5
_G.DemandFactor.currentPeriod = currentPeriod
_G.DemandFactor.revenueThisPeriod = 0
_G.DemandFactor.trailingPeriodRevenues = { 0, 10, 0, 0, 5, 10, 0 }
local startNextPeriodTimestamp = demand.getSettings().periodLengthMs * currentPeriod + 1
demand.updateDemandFactor(startNextPeriodTimestamp)
assert.are.equal(0.9749999999999999778, demand.getDemandFactor())
-- update the 6th spot in the pervious period revenues for the 5th period
assert.are.same({ 0, 10, 0, 0, 5, 0, 0 }, demand.getTrailingPeriodRevenues())
assert.are.equal(currentPeriod + 1, demand.getCurrentPeriod())
end
)

it(
"updateDemandFactor() should increment consecutive periods at minimum and not lower demand factor if demand factor is already at minimum",
function()
DemandFactor.currentDemandFactor = 0.5
DemandFactor.revenueThisPeriod = 0
DemandFactor.trailingPeriodRevenues = { 0, 10, 10, 10, 10, 10 }
demand.updateDemandFactor(demand.getSettings().periodLengthMs + 1)
local currentPeriod = 12
_G.DemandFactor.currentPeriod = currentPeriod
_G.DemandFactor.currentDemandFactor = 0.5
_G.DemandFactor.revenueThisPeriod = 0
_G.DemandFactor.trailingPeriodRevenues = { 0, 10, 10, 10, 10, 10, 0 }
local currentTimestamp = demand.getSettings().periodLengthMs * currentPeriod + 1
demand.updateDemandFactor(currentTimestamp)
assert.are.equal(0.5, demand.getDemandFactor())
-- update the 12 % 7 = 5th spot in the pervious period revenues
assert.are.same({ 0, 10, 10, 10, 10, 0, 0 }, demand.getTrailingPeriodRevenues())
-- increments the period by one
assert.are.equal(currentPeriod + 1, demand.getCurrentPeriod())
end
)

it(
"updateDemandFactor() adjust fees and reset demend factor parameters when consecutive periods at minimum threshold is hit",
function()
DemandFactor.currentDemandFactor = demand.getSettings().demandFactorMin - 0.1
DemandFactor.consecutivePeriodsWithMinDemandFactor = demand.getSettings().stepDownThreshold
DemandFactor.revenueThisPeriod = 0
DemandFactor.trailingPeriodRevenues = { 0, 10, 10, 10, 10, 10 }
local currentPeriod = 15
_G.DemandFactor.currentPeriod = currentPeriod -- 15 % 7 = 1 = 2 index of the trailing period array
_G.DemandFactor.currentDemandFactor = demand.getSettings().demandFactorMin - 0.1
_G.DemandFactor.consecutivePeriodsWithMinDemandFactor = demand.getSettings().stepDownThreshold
_G.DemandFactor.revenueThisPeriod = 0
_G.DemandFactor.trailingPeriodRevenues = { 0, 10, 10, 10, 10, 10, 0 }
local expectedFees = {}
local startNextPeriodTimestamp = demand.getSettings().periodLengthMs + 1
local startNextPeriodTimestamp = demand.getSettings().periodLengthMs * currentPeriod + 1
-- use pairs as fees is a map
for nameLength, fee in pairs(constants.genesisFees) do
expectedFees[nameLength] = fee * demand.getSettings().demandFactorMin
Expand All @@ -106,6 +124,7 @@ describe("demand", function()
assert.are.equal(1, demand.getDemandFactor())
assert.are.equal(0, demand.getConsecutivePeriodsWithMinDemandFactor())
assert.are.same(expectedFees, demand.getFees())
assert.are.same({ 0, 0, 10, 10, 10, 10, 0 }, demand.getTrailingPeriodRevenues())
end
)
end)
Expand All @@ -126,39 +145,47 @@ describe("demand", function()
end)

it("isDemandIncreasing() should return true when demand is increasing for purchases based criteria", function()
DemandFactor.purchasesThisPeriod = 10
DemandFactor.trailingPeriodPurchases = { 10, 0, 0, 0, 0, 0, 0 }
_G.DemandFactor.purchasesThisPeriod = 10
_G.DemandFactor.trailingPeriodPurchases = { 10, 0, 0, 0, 0, 0, 0 }
assert.is_true(demand.isDemandIncreasing())
end)

it(
"isDemandIncreasing() should return false when demand is not increasing for purchases based criteria",
function()
DemandFactor.purchasesThisPeriod = 0
DemandFactor.trailingPeriodPurchases = { 0, 10, 10, 10, 10, 10, 10 }
_G.DemandFactor.purchasesThisPeriod = 0
_G.DemandFactor.trailingPeriodPurchases = { 0, 10, 10, 10, 10, 10, 10 }
assert.is_false(demand.isDemandIncreasing())
end
)

it(
"updateDemandFactor() should update demand factor if demand is increasing and a new period has started",
function()
DemandFactor.purchasesThisPeriod = 10
DemandFactor.trailingPeriodPurchases = { 10, 0, 0, 0, 0, 0, 0 }
local startNextPeriodTimestamp = demand.getSettings().periodLengthMs + 1
local currentPeriod = 3
_G.DemandFactor.currentPeriod = currentPeriod
_G.DemandFactor.purchasesThisPeriod = 10
_G.DemandFactor.trailingPeriodPurchases = { 10, 0, 0, 0, 0, 0, 0 }
local startNextPeriodTimestamp = demand.getSettings().periodLengthMs * currentPeriod + 1
demand.updateDemandFactor(startNextPeriodTimestamp)
assert.are.equal(1.05, demand.getDemandFactor())
assert.are.same({ 10, 0, 0, 10, 0, 0, 0 }, demand.getTrailingPeriodPurchases())
assert.are.equal(currentPeriod + 1, demand.getCurrentPeriod())
end
)

it(
"updateDemandFactor() should update demand factor if demand is decreasing and a new period has started",
function()
DemandFactor.purchasesThisPeriod = 0
DemandFactor.trailingPeriodPurchases = { 0, 10, 0, 0, 0, 0, 0 }
local startNextPeriodTimestamp = demand.getSettings().periodLengthMs + 1
local currentPeriod = 5
_G.DemandFactor.currentPeriod = currentPeriod
_G.DemandFactor.purchasesThisPeriod = 0
_G.DemandFactor.trailingPeriodPurchases = { 0, 10, 0, 0, 0, 0, 0 }
local startNextPeriodTimestamp = demand.getSettings().periodLengthMs * currentPeriod + 1
demand.updateDemandFactor(startNextPeriodTimestamp)
assert.are.equal(0.9749999999999999778, demand.getDemandFactor())
assert.are.same({ 0, 10, 0, 0, 0, 0, 0 }, demand.getTrailingPeriodPurchases())
assert.are.equal(currentPeriod + 1, demand.getCurrentPeriod())
end
)
end)
Expand Down
47 changes: 28 additions & 19 deletions src/demand.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ local demand = {}

DemandFactor = DemandFactor
or {
currentPeriod = 0,
currentPeriod = 1, -- one based index of the current period
trailingPeriodPurchases = { 0, 0, 0, 0, 0, 0, 0 }, -- Acts as a ring buffer of trailing period purchase counts
trailingPeriodRevenues = { 0, 0, 0, 0, 0, 0 }, -- Acts as a ring buffer of trailing period revenues
purchasesThisPeriod = 0,
Expand Down Expand Up @@ -51,7 +51,6 @@ function demand.mvgAvgTrailingRevenues()
end

function demand.isDemandIncreasing()
local currentPeriod = demand.getCurrentPeriod()
local settings = demand.getSettings()

-- check that we have settings
Expand All @@ -60,15 +59,15 @@ function demand.isDemandIncreasing()
return false
end

local purchasesLastPeriod = demand.getTrailingPeriodPurchases()[currentPeriod] or 0
local revenueInLastPeriod = demand.getTrailingPeriodRevenues()[currentPeriod] or 0
local purchasesInCurrentPeriod = demand.getCurrentPeriodPurchases()
local revunueInCurrentPeriod = demand.getCurrentPeriodRevenue()
local mvgAvgOfTrailingNamePurchases = demand.mvgAvgTrailingPurchaseCounts()
local mvgAvgOfTrailingRevenue = demand.mvgAvgTrailingRevenues()

if settings.criteria == "revenue" then
return revenueInLastPeriod > 0 and revenueInLastPeriod > mvgAvgOfTrailingRevenue
return revunueInCurrentPeriod > 0 and (revunueInCurrentPeriod > mvgAvgOfTrailingRevenue)
else
return purchasesLastPeriod > 0 and purchasesLastPeriod > mvgAvgOfTrailingNamePurchases
return purchasesInCurrentPeriod > 0 and (purchasesInCurrentPeriod > mvgAvgOfTrailingNamePurchases)
end
end

Expand Down Expand Up @@ -122,12 +121,18 @@ function demand.updateDemandFactor(timestamp)
demand.resetConsecutivePeriodsWithMinimumDemandFactor()
demand.updateFees(settings.demandFactorMin)
demand.setDemandFactor(settings.demandFactorBaseValue)
demand.resetConsecutivePeriodsWithMinimumDemandFactor()
else
demand.incrementConsecutivePeriodsWithMinDemandFactor(1)
end
end

demand.incrementPeriodAndResetValues()
-- update the current period values in the ring buffer for previous periods
demand.updateTrailingPeriodPurchases()
demand.updateTrailingPeriodRevenues()
demand.resetPurchasesThisPeriod()
demand.resetRevenueThisPeriod()
demand.incrementCurrentPeriod(1)
end

function demand.updateFees(multiplier)
Expand Down Expand Up @@ -180,7 +185,7 @@ end

function demand.getCurrentPeriod()
local demandFactor = utils.deepCopy(DemandFactor)
return demandFactor and demandFactor.currentPeriod or 0
return demandFactor and demandFactor.currentPeriod or 1
end

function demand.updateSettings(settings)
Expand All @@ -201,14 +206,25 @@ end
function demand.setDemandFactor(demandFactor)
DemandFactor.currentDemandFactor = demandFactor
end
function demand.updateTrailingPeriodPurchases()

function demand.getPeriodIndex()
local currentPeriod = demand.getCurrentPeriod()
DemandFactor.trailingPeriodPurchases[currentPeriod] = demand.getCurrentPeriodPurchases()
local settings = demand.getSettings()
if not settings then
return 0
end
-- current period is one based index of the current period
return (currentPeriod % settings.movingAvgPeriodCount) + 1 -- has to be + 1 to avoid zero index
end

function demand.updateTrailingPeriodPurchases()
local periodIndex = demand.getPeriodIndex()
DemandFactor.trailingPeriodPurchases[periodIndex] = demand.getCurrentPeriodPurchases()
end

function demand.updateTrailingPeriodRevenues()
local currentPeriod = demand.getCurrentPeriod()
DemandFactor.trailingPeriodRevenues[currentPeriod] = demand.getCurrentPeriodRevenue()
local periodIndex = demand.getPeriodIndex()
DemandFactor.trailingPeriodRevenues[periodIndex] = demand.getCurrentPeriodRevenue()
end

function demand.resetPurchasesThisPeriod()
Expand Down Expand Up @@ -243,11 +259,4 @@ function demand.incrementConsecutivePeriodsWithMinDemandFactor(count)
DemandFactor.consecutivePeriodsWithMinDemandFactor = DemandFactor.consecutivePeriodsWithMinDemandFactor + count
end

function demand.incrementPeriodAndResetValues()
demand.resetConsecutivePeriodsWithMinimumDemandFactor()
demand.resetPurchasesThisPeriod()
demand.resetRevenueThisPeriod()
demand.incrementCurrentPeriod(1)
end

return demand

0 comments on commit 2a88505

Please sign in to comment.