Module:BassaridMarketActivity: Difference between revisions

From MicrasWiki
Jump to navigationJump to search
NewZimiaGov (talk | contribs)
No edit summary
NewZimiaGov (talk | contribs)
No edit summary
Line 1: Line 1:
---------------------------------------------------------
---------------------------------------------------------
-- Module: BassaridiaDailyInvestorTable
-- Module: BassaridiaStockMarketTable
-- Generates a daily table of investor stock activity
--
-- with half increasing, half decreasing. No investor
-- Similar to your original "BassaridiaDailyInvestorTable",
-- can gain or lose more than 30% from their start.
-- but with columns typical of a stock market table:
-- Reasons are unique per investor per day.
--  • Date (still "DD/MM/YYYY PSSC")
--  • Symbol
--  • Company
--  • Open (Value at Start, in Polis)
--  • Close (Value at End, in Polis)
--  • Volume (simply "Stocks Bought + Stocks Sold")
--   • Change (Close - Open, in Polis)
--   • Change % ((Change / Open) * 100)
--  • Reason (same unique reason logic)
--
-- All daily logic, randomization, and 30%-cap constraints
-- are unchanged, so you'll see the SAME daily values
-- you'd get from the original script.
---------------------------------------------------------
---------------------------------------------------------


Line 10: Line 22:


---------------------------------------------------------
---------------------------------------------------------
-- 1. getCurrentDate: returns "DD/MM/YYYY PSSC" + dayOfYear
-- 1) getDailyDate: same logic, same "DD/MM/YYYY PSSC"
---------------------------------------------------------
---------------------------------------------------------
local function getCurrentDate()
local function getDailyDate()
     local startDate = os.time({year = 1999, month = 8, day = 6})
     local startDate = os.time({year = 1999, month = 8, day = 6})
     local secondsInDay = 86400
     local secondsInDay = 86400
Line 24: Line 36:
     local dayOfYear = math.floor((yearFraction - psscYear) * daysPerYear) + 1
     local dayOfYear = math.floor((yearFraction - psscYear) * daysPerYear) + 1


     -- simplistic approach: 3 "months" in the 183-day year
     -- 3 "months" in the 183-day year
     local month, dayOfMonth
     local month, dayOfMonth
     if dayOfYear <= 61 then
     if dayOfYear <= 61 then
Line 42: Line 54:


---------------------------------------------------------
---------------------------------------------------------
-- 2. Investor Data
-- 2) "Investors" => "Companies" with symbols
--    Same stock counts, but we add short ticker-like symbols.
---------------------------------------------------------
---------------------------------------------------------
local investors = {
local companies = {
     { name = "Vaeringheim Regional Investor",     stocks = 17424 },
     {
     { name = "Alpazkigz Regional Investor",       stocks = 16214 },
        symbol = "VREG",
     { name = "Odiferia Regional Investor",       stocks = 14718 },
        name   = "Vaeringheim Regional Investor",
     { name = "Hafaan Regional Investor",         stocks = 3630 },
        stocks = 17424
     { name = "Jeseri Regional Investor",         stocks = 1870 },
    },
     { name = "Hatch Ministry Regional Investor", stocks = 7810 },
     {
     { name = "New South Jangsong Division",       stocks = 11085 },
        symbol = "ALPK",
     { name = "Haifan Bassaridia Division",       stocks = 12500 },
        name   = "Alpazkigz Regional Investor",
        stocks = 16214
    },
     {
        symbol = "ODFR",
        name   = "Odiferia Regional Investor",
        stocks = 14718
    },
     {
        symbol = "HAFA",
        name   = "Hafaan Regional Investor",
        stocks = 3630
    },
     {
        symbol = "JESR",
        name   = "Jeseri Regional Investor",
        stocks = 1870
    },
     {
        symbol = "HMIN",
        name   = "Hatch Ministry Regional Investor",
        stocks = 7810
    },
     {
        symbol = "JANG",
        name   = "New South Jangsong Division",
        stocks = 11085
    },
     {
        symbol = "HBAS",
        name   = "Haifan Bassaridia Division",
        stocks = 12500
    },
}
}


---------------------------------------------------------
---------------------------------------------------------
-- 3. Reasons for Increase / Decrease
-- 3) Reasons for Increase / Decrease (same texts)
---------------------------------------------------------
---------------------------------------------------------
local increaseReasons = {
local increaseReasons = {
Line 115: Line 160:


---------------------------------------------------------
---------------------------------------------------------
-- 4. Utility for Unique Reasons
-- 4) Utility function to pop a unique reason
---------------------------------------------------------
---------------------------------------------------------
local function popUniqueReason(list)
local function popUniqueReason(pool)
     if #list == 0 then
     if #pool == 0 then
         return "No reason available (out of reasons)."
         return "(No reason: out of reasons!)"
     end
     end
     local rnd = math.random(#list)
     local i = math.random(#pool)
     local chosen = list[rnd]
     local result = pool[i]
     table.remove(list, rnd)
     table.remove(pool, i)
     return chosen
     return result
end
end


---------------------------------------------------------
---------------------------------------------------------
-- 5. Force half Inc, half Dec
-- 5) Force half up, half down (unchanged)
---------------------------------------------------------
---------------------------------------------------------
local function assignUpDown(count)
local function assignUpDown(count)
Line 134: Line 179:
     local arr = {}
     local arr = {}
     for i = 1, half do
     for i = 1, half do
      arr[i] = "Inc"
        arr[#arr + 1] = "Inc"
     end
     end
     for i = half+1, count do
     for i = half+1, count do
      arr[i] = "Dec"
        arr[#arr + 1] = "Dec"
     end
     end
     -- shuffle
     -- Shuffle
     for i = #arr, 2, -1 do
     for i = #arr, 2, -1 do
      local j = math.random(i)
        local j = math.random(i)
      arr[i], arr[j] = arr[j], arr[i]
        arr[i], arr[j] = arr[j], arr[i]
     end
     end
     return arr
     return arr
Line 148: Line 193:


---------------------------------------------------------
---------------------------------------------------------
-- 6. Main generator
-- 6) Main function: produce a "stock table" with the
--    same daily numbers as the original script
---------------------------------------------------------
---------------------------------------------------------
function p.generateTable(frame)
function p.generateTable(frame)
     -- Date-based seed
     -- (a) Date-based seed: same formula
     local dateString, dayOfYear = getCurrentDate()
     local dateString, dayOfYear = getDailyDate()
     local localTime = os.date("*t")
     local localTime = os.date("*t")
     local dailySeed = localTime.year * 1000 + localTime.yday
     local dailySeed = localTime.year * 1000 + localTime.yday
     math.randomseed(dailySeed)
     math.randomseed(dailySeed)


     local stockPrice = 30
     -- (b) Copy reason arrays
    local n = #investors
    local updown = assignUpDown(n)
 
    -- Copy reason arrays so we can remove used ones
     local incPool = {}
     local incPool = {}
     for _, r in ipairs(increaseReasons) do incPool[#incPool+1] = r end
     for _, r in ipairs(increaseReasons) do incPool[#incPool + 1] = r end
     local decPool = {}
     local decPool = {}
     for _, r in ipairs(decreaseReasons) do decPool[#decPool+1] = r end
     for _, r in ipairs(decreaseReasons) do decPool[#decPool + 1] = r end


     -- Table header
     -- (c) Assign half up, half down
    local n = #companies
    local directions = assignUpDown(n)
 
    -- The original script used a per-share "Polis" price of 30.
    local sharePrice = 30
 
    -- (d) Build the "wikitable" header
     local out = {}
     local out = {}
     table.insert(out, '{| class="wikitable" style="text-align:center; width:100%;"')
     table.insert(out, '{| class="wikitable" style="text-align:center; width:100%;"')
     table.insert(out, "|-")
     table.insert(out, "|-")
     table.insert(out, "! Date")
     table.insert(out, "! Date")
     table.insert(out, "! Investor")
     table.insert(out, "! Symbol")
     table.insert(out, "! Stocks at Start")
     table.insert(out, "! Company")
     table.insert(out, "! Stocks Bought")
     table.insert(out, "! Open (Polis)")
    table.insert(out, "! Stocks Sold")
     table.insert(out, "! Close (Polis)")
    table.insert(out, "! Stocks at End")
     table.insert(out, "! Volume")
     table.insert(out, "! Value at Start (Polis)")
     table.insert(out, "! Value at End (Polis)")
     table.insert(out, "! Change")
     table.insert(out, "! Change")
    table.insert(out, "! Change %")
     table.insert(out, "! Reason")
     table.insert(out, "! Reason")


     local totalStartValue = 0
     -- (e) For each "company" (previously "investor")
    local totalEndValue  = 0
    for i, co in ipairs(companies) do
        local stocksStart = co.stocks
        local upOrDown = directions[i]


    -- For each investor
         -- same 30% constraint
    for i, inv in ipairs(investors) do
        local stocksStart = inv.stocks
        local label = updown[i] -- "Inc" or "Dec"
 
         -- ensure final net won't exceed 30% from start
         local maxDelta = math.floor(stocksStart * 0.3)
         local maxDelta = math.floor(stocksStart * 0.3)
         if maxDelta < 1 then maxDelta = 1 end
         if maxDelta < 1 then maxDelta = 1 end


         local buy=0
         -- same random buy/sell logic
         local sell=0
         local buy, sell = 0, 0
         if label == "Inc" then
         if upOrDown == "Inc" then
             buy  = math.random(1, maxDelta)
             buy  = math.random(1, maxDelta)
             sell = math.random(0, buy-1)
             sell = math.random(0, buy - 1)
         else
         else
             sell = math.random(1, maxDelta)
             sell = math.random(1, maxDelta)
             buy  = math.random(0, sell-1)
             buy  = math.random(0, sell - 1)
         end
         end
        local stocksEnd = stocksStart + buy - sell
        -- Convert "stocks" to Polis value
        local openVal  = stocksStart * sharePrice
        local closeVal = stocksEnd  * sharePrice


         local stocksEnd = stocksStart + buy - sell
         -- Volume is the total shares that changed hands
         local valueStart = stocksStart * stockPrice
         local volume = buy + sell 
        local valueEnd  = stocksEnd  * stockPrice
        totalStartValue = totalStartValue + valueStart
        totalEndValue  = totalEndValue  + valueEnd


         local changeWord
        -- "Change" is close - open (in Polis)
         if stocksEnd > stocksStart then
         local changeVal = closeVal - openVal
            changeWord = "Increased"
         -- "Change%"  
         elseif stocksEnd < stocksStart then
         local pctChange = 0
            changeWord = "Decreased"
         if openVal ~= 0 then
         else
             pctChange = (changeVal / openVal) * 100
             changeWord = "No Change"
         end
         end


        -- Reason
         local reason
         local reason
         if changeWord == "Increased" then
         if changeVal > 0 then
             reason = popUniqueReason(incPool)
             reason = popUniqueReason(incPool)
         elseif changeWord == "Decreased" then
         elseif changeVal < 0 then
             reason = popUniqueReason(decPool)
             reason = popUniqueReason(decPool)
         else
         else
             -- If No Change, we can draw from either pool
             -- If no net change, pick from incPool per original code
            -- to avoid duplication, let's pick from incPool
             reason = popUniqueReason(incPool)
             reason = popUniqueReason(incPool)
         end
         end


        -- (f) Insert row
         table.insert(out, "|-")
         table.insert(out, "|-")
         table.insert(out, string.format("| %s", dateString))
         table.insert(out, dateString) -- e.g. "22/01/2025 PSSC"
        table.insert(out, string.format("| %s", inv.name))
         table.insert(out, co.symbol)
         table.insert(out, string.format("| %d", stocksStart))
         table.insert(out, co.name)
         table.insert(out, string.format("| %d", buy))
         table.insert(out, tostring(openVal))
         table.insert(out, string.format("| %d", sell))
         table.insert(out, tostring(closeVal))
         table.insert(out, string.format("| %d", stocksEnd))
         table.insert(out, tostring(volume))
         table.insert(out, string.format("| %d", valueStart))
         table.insert(out, string.format("%+d", changeVal))
         table.insert(out, string.format("| %d", valueEnd))
         table.insert(out, string.format("%.2f%%", pctChange))
         table.insert(out, string.format("| %s", changeWord))
         table.insert(out, reason)
         table.insert(out, string.format("| %s", reason))
     end
     end
    -- Totals row
    table.insert(out, "|-")
    table.insert(out, string.format("| colspan=\"6\" | Totals"))
    table.insert(out, string.format("| %d", totalStartValue))
    table.insert(out, string.format("| %d", totalEndValue))
    table.insert(out, "| ")
    table.insert(out, "| ")


     table.insert(out, "|}")
     table.insert(out, "|}")

Revision as of 22:31, 22 January 2025

Documentation for this module may be created at Module:BassaridMarketActivity/doc

---------------------------------------------------------
-- Module: BassaridiaStockMarketTable
-- 
-- Similar to your original "BassaridiaDailyInvestorTable",
-- but with columns typical of a stock market table:
--   • Date (still "DD/MM/YYYY PSSC")
--   • Symbol
--   • Company
--   • Open (Value at Start, in Polis)
--   • Close (Value at End, in Polis)
--   • Volume (simply "Stocks Bought + Stocks Sold")
--   • Change (Close - Open, in Polis)
--   • Change % ((Change / Open) * 100)
--   • Reason (same unique reason logic)
-- 
-- All daily logic, randomization, and 30%-cap constraints
-- are unchanged, so you'll see the SAME daily values
-- you'd get from the original script.
---------------------------------------------------------

local p = {}

---------------------------------------------------------
-- 1) getDailyDate: same logic, same "DD/MM/YYYY PSSC"
---------------------------------------------------------
local function getDailyDate()
    local startDate = os.time({year = 1999, month = 8, day = 6})
    local secondsInDay = 86400
    local daysPerYear = 183

    local now = os.time()
    local totalDaysElapsed = math.floor((now - startDate) / secondsInDay)

    local yearFraction = totalDaysElapsed / daysPerYear
    local psscYear = math.floor(yearFraction)
    local dayOfYear = math.floor((yearFraction - psscYear) * daysPerYear) + 1

    -- 3 "months" in the 183-day year
    local month, dayOfMonth
    if dayOfYear <= 61 then
        month = 1
        dayOfMonth = dayOfYear
    elseif dayOfYear <= 122 then
        month = 2
        dayOfMonth = dayOfYear - 61
    else
        month = 3
        dayOfMonth = dayOfYear - 122
    end

    local dateString = string.format("%d/%d/%d PSSC", dayOfMonth, month, psscYear)
    return dateString, dayOfYear
end

---------------------------------------------------------
-- 2) "Investors" => "Companies" with symbols
--    Same stock counts, but we add short ticker-like symbols.
---------------------------------------------------------
local companies = {
    {
        symbol = "VREG",
        name   = "Vaeringheim Regional Investor",
        stocks = 17424
    },
    {
        symbol = "ALPK",
        name   = "Alpazkigz Regional Investor",
        stocks = 16214
    },
    {
        symbol = "ODFR",
        name   = "Odiferia Regional Investor",
        stocks = 14718
    },
    {
        symbol = "HAFA",
        name   = "Hafaan Regional Investor",
        stocks = 3630
    },
    {
        symbol = "JESR",
        name   = "Jeseri Regional Investor",
        stocks = 1870
    },
    {
        symbol = "HMIN",
        name   = "Hatch Ministry Regional Investor",
        stocks = 7810
    },
    {
        symbol = "JANG",
        name   = "New South Jangsong Division",
        stocks = 11085
    },
    {
        symbol = "HBAS",
        name   = "Haifan Bassaridia Division",
        stocks = 12500
    },
}

---------------------------------------------------------
-- 3) Reasons for Increase / Decrease (same texts)
---------------------------------------------------------
local increaseReasons = {
    "Improved logistics at the General Port attracted new investors.",
    "An influx of goods from Delphica boosted trade volume at the port.",
    "High demand for Noctic-Rabrev exports raised investor confidence.",
    "Completion of a new shipping lane connecting Nexa increased activity.",
    "Successful negotiations with Erythros merchants expanded trade.",
    "Festival of Light in Vaeringheim drove significant port activity.",
    "High attendance at the Bassaridia Trade Symposium enhanced reputation.",
    "Discovery of new mineral resources near Acheron increased export potential.",
    "A surge in ceremonial wine exports from the Blood Vineyards bolstered ops.",
    "Efficient cargo handling improvements boosted investor sentiment.",
    "Seasonal agricultural exports from Jogi saw a sharp increase.",
    "High sales of pearl and abalone products boosted port traffic.",
    "Luxury goods from Tel-Amin attracted new high-end investors.",
    "Port capacity expansion in Suncliff improved competitiveness.",
    "Improved relations with Maskmakers Guild stabilized trade routes.",
    "Increased fishing yields around Lake Morovia led to more port activity.",
    "Enhanced security measures at the Port reduced piracy concerns.",
    "Cultural appreciation for Horehound exports boosted confidence.",
    "Trade deals with Saluria expanded port usage significantly.",
    "Demand for silk from the Seamstresses of Rouge improved port revenue.",
    "Moonlit Tide celebrations increased ceremonial goods traffic.",
    "Launch of new Garganid fertilizer shipments attracted agriculture investors.",
    "Global interest in sacred resin exports enhanced shipments.",
    "Marketing campaign highlighted the Port's strategic location.",
    "Completion of high-speed rail connections boosted trade efficiency."
}

local decreaseReasons = {
    "Pirate raids in the Strait of Haifa disrupted shipments to the Port.",
    "Unfavorable weather delayed agriculture exports from Plains of Jogi.",
    "Labor shortages in the Port caused cargo-handling delays.",
    "A major fire in Salurian sacred groves reduced resin exports.",
    "Rising costs of Noctic-Rabrev cultivation hurt confidence.",
    "Unrest in Jangsong Province disrupted camel meat shipments.",
    "Currency fluctuations in Vaeringheim affected stability.",
    "Economic sanctions on neighbors reduced export activity.",
    "Decline in ceremonial artifact production lowered traffic.",
    "Cultural disputes over Horehound caused export delays.",
    "Counterfeit relics in Nexa harmed the port's reputation.",
    "Fishing yields were outpaced by demand, reducing shipments.",
    "Decline in pearl quality from Anterran sources lowered interest.",
    "Shortage of ceremonial wine from Blood Vineyards cut exports.",
    "Unrest among Maskmaker merchants disrupted trade agreements.",
    "Less demand for silk from Seamstresses of Rouge impacted flows.",
    "Collapse of trade agreement with Tel-Amin lowered volume.",
    "Seasonal labor shortages shrank port shipments significantly.",
    "Disruptions in Suncliff Fisheries slowed aquatic exports.",
    "Alternative shipping routes bypassed the Port altogether.",
    "Economic downturn in Lunalis Sancta reduced investor interest.",
    "Lingering piracy concerns in Haifan waters harmed confidence.",
    "Failure at Bassaridia Trade Symposium lowered traffic.",
    "Natural disasters near Acheron destroyed key routes.",
    "Sabotage of port infrastructure caused shipping delays."
}

---------------------------------------------------------
-- 4) Utility function to pop a unique reason
---------------------------------------------------------
local function popUniqueReason(pool)
    if #pool == 0 then
        return "(No reason: out of reasons!)"
    end
    local i = math.random(#pool)
    local result = pool[i]
    table.remove(pool, i)
    return result
end

---------------------------------------------------------
-- 5) Force half up, half down (unchanged)
---------------------------------------------------------
local function assignUpDown(count)
    local half = math.floor(count / 2)
    local arr = {}
    for i = 1, half do
        arr[#arr + 1] = "Inc"
    end
    for i = half+1, count do
        arr[#arr + 1] = "Dec"
    end
    -- Shuffle
    for i = #arr, 2, -1 do
        local j = math.random(i)
        arr[i], arr[j] = arr[j], arr[i]
    end
    return arr
end

---------------------------------------------------------
-- 6) Main function: produce a "stock table" with the
--    same daily numbers as the original script
---------------------------------------------------------
function p.generateTable(frame)
    -- (a) Date-based seed: same formula
    local dateString, dayOfYear = getDailyDate()
    local localTime = os.date("*t")
    local dailySeed = localTime.year * 1000 + localTime.yday
    math.randomseed(dailySeed)

    -- (b) Copy reason arrays
    local incPool = {}
    for _, r in ipairs(increaseReasons) do incPool[#incPool + 1] = r end
    local decPool = {}
    for _, r in ipairs(decreaseReasons) do decPool[#decPool + 1] = r end

    -- (c) Assign half up, half down
    local n = #companies
    local directions = assignUpDown(n)

    -- The original script used a per-share "Polis" price of 30.
    local sharePrice = 30

    -- (d) Build the "wikitable" header
    local out = {}
    table.insert(out, '{| class="wikitable" style="text-align:center; width:100%;"')
    table.insert(out, "|-")
    table.insert(out, "! Date")
    table.insert(out, "! Symbol")
    table.insert(out, "! Company")
    table.insert(out, "! Open (Polis)")
    table.insert(out, "! Close (Polis)")
    table.insert(out, "! Volume")
    table.insert(out, "! Change")
    table.insert(out, "! Change %")
    table.insert(out, "! Reason")

    -- (e) For each "company" (previously "investor")
    for i, co in ipairs(companies) do
        local stocksStart = co.stocks
        local upOrDown = directions[i]

        -- same 30% constraint
        local maxDelta = math.floor(stocksStart * 0.3)
        if maxDelta < 1 then maxDelta = 1 end

        -- same random buy/sell logic
        local buy, sell = 0, 0
        if upOrDown == "Inc" then
            buy  = math.random(1, maxDelta)
            sell = math.random(0, buy - 1)
        else
            sell = math.random(1, maxDelta)
            buy  = math.random(0, sell - 1)
        end
        local stocksEnd = stocksStart + buy - sell

        -- Convert "stocks" to Polis value
        local openVal  = stocksStart * sharePrice
        local closeVal = stocksEnd   * sharePrice

        -- Volume is the total shares that changed hands
        local volume = buy + sell  

        -- "Change" is close - open (in Polis)
        local changeVal = closeVal - openVal
        -- "Change%" 
        local pctChange = 0
        if openVal ~= 0 then
            pctChange = (changeVal / openVal) * 100
        end

        -- Reason
        local reason
        if changeVal > 0 then
            reason = popUniqueReason(incPool)
        elseif changeVal < 0 then
            reason = popUniqueReason(decPool)
        else
            -- If no net change, pick from incPool per original code
            reason = popUniqueReason(incPool)
        end

        -- (f) Insert row
        table.insert(out, "|-")
        table.insert(out, dateString)  -- e.g. "22/01/2025 PSSC"
        table.insert(out, co.symbol)
        table.insert(out, co.name)
        table.insert(out, tostring(openVal))
        table.insert(out, tostring(closeVal))
        table.insert(out, tostring(volume))
        table.insert(out, string.format("%+d", changeVal))
        table.insert(out, string.format("%.2f%%", pctChange))
        table.insert(out, reason)
    end

    table.insert(out, "|}")
    return table.concat(out, "\n")
end

return p