From f8a3ce94fe173426448abab2d463a36e6dbe45d2 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Fri, 6 Mar 2026 10:12:43 -0800 Subject: [PATCH 01/10] draft time series simulation --- .../tests/flash_crash_moderate_helpers.cdc | 2980 +++++++++++++++++ .../forked_flash_crash_moderate_test.cdc | 463 +++ 2 files changed, 3443 insertions(+) create mode 100644 cadence/tests/flash_crash_moderate_helpers.cdc create mode 100644 cadence/tests/forked_flash_crash_moderate_test.cdc diff --git a/cadence/tests/flash_crash_moderate_helpers.cdc b/cadence/tests/flash_crash_moderate_helpers.cdc new file mode 100644 index 00000000..99f5391c --- /dev/null +++ b/cadence/tests/flash_crash_moderate_helpers.cdc @@ -0,0 +1,2980 @@ +import Test + +// AUTO-GENERATED from flash_crash_moderate.json — do not edit manually +// Run: python3 scripts/generate_fixture.py + +access(all) struct SimAgent { + access(all) let count: Int + access(all) let initialHF: UFix64 + access(all) let rebalancingHF: UFix64 + access(all) let targetHF: UFix64 + access(all) let debtPerAgent: UFix64 + access(all) let totalSystemDebt: UFix64 + + init( + count: Int, + initialHF: UFix64, + rebalancingHF: UFix64, + targetHF: UFix64, + debtPerAgent: UFix64, + totalSystemDebt: UFix64 + ) { + self.count = count + self.initialHF = initialHF + self.rebalancingHF = rebalancingHF + self.targetHF = targetHF + self.debtPerAgent = debtPerAgent + self.totalSystemDebt = totalSystemDebt + } +} + +access(all) struct SimPool { + access(all) let size: UFix64 + access(all) let concentration: UFix64 + access(all) let feeTier: UFix64 + + init(size: UFix64, concentration: UFix64, feeTier: UFix64) { + self.size = size + self.concentration = concentration + self.feeTier = feeTier + } +} + +access(all) struct SimConstants { + access(all) let btcCollateralFactor: UFix64 + access(all) let btcLiquidationThreshold: UFix64 + access(all) let yieldAPR: UFix64 + access(all) let directMintYT: Bool + + init( + btcCollateralFactor: UFix64, + btcLiquidationThreshold: UFix64, + yieldAPR: UFix64, + directMintYT: Bool + ) { + self.btcCollateralFactor = btcCollateralFactor + self.btcLiquidationThreshold = btcLiquidationThreshold + self.yieldAPR = yieldAPR + self.directMintYT = directMintYT + } +} + +access(all) let flash_crash_moderate_prices: [UFix64] = [ + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 100000.00000000, + 96000.00000000, + 92000.00000000, + 88000.00000000, + 84000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80000.00000000, + 80015.34000000, + 80030.68000000, + 80046.02000000, + 80061.35000000, + 80076.68000000, + 80092.00000000, + 80107.32000000, + 80122.64000000, + 80137.95000000, + 80153.26000000, + 80168.56000000, + 80183.86000000, + 80199.16000000, + 80214.45000000, + 80229.74000000, + 80245.02000000, + 80260.30000000, + 80275.58000000, + 80290.85000000, + 80306.12000000, + 80321.38000000, + 80336.64000000, + 80351.90000000, + 80367.15000000, + 80382.40000000, + 80397.65000000, + 80412.89000000, + 80428.13000000, + 80443.36000000, + 80458.59000000, + 80473.81000000, + 80489.03000000, + 80504.25000000, + 80519.46000000, + 80534.67000000, + 80549.88000000, + 80565.08000000, + 80580.28000000, + 80595.47000000, + 80610.66000000, + 80625.85000000, + 80641.03000000, + 80656.20000000, + 80671.38000000, + 80686.55000000, + 80701.71000000, + 80716.88000000, + 80732.03000000, + 80747.19000000, + 80762.34000000, + 80777.48000000, + 80792.62000000, + 80807.76000000, + 80822.90000000, + 80838.03000000, + 80853.15000000, + 80868.27000000, + 80883.39000000, + 80898.51000000, + 80913.62000000, + 80928.72000000, + 80943.82000000, + 80958.92000000, + 80974.02000000, + 80989.11000000, + 81004.19000000, + 81019.27000000, + 81034.35000000, + 81049.43000000, + 81064.50000000, + 81079.56000000, + 81094.62000000, + 81109.68000000, + 81124.74000000, + 81139.79000000, + 81154.83000000, + 81169.87000000, + 81184.91000000, + 81199.95000000, + 81214.98000000, + 81230.00000000, + 81245.02000000, + 81260.04000000, + 81275.06000000, + 81290.07000000, + 81305.07000000, + 81320.07000000, + 81335.07000000, + 81350.07000000, + 81365.06000000, + 81380.04000000, + 81395.02000000, + 81410.00000000, + 81424.97000000, + 81439.94000000, + 81454.91000000, + 81469.87000000, + 81484.83000000, + 81499.78000000, + 81514.73000000, + 81529.68000000, + 81544.62000000, + 81559.56000000, + 81574.49000000, + 81589.42000000, + 81604.35000000, + 81619.27000000, + 81634.19000000, + 81649.10000000, + 81664.01000000, + 81678.91000000, + 81693.81000000, + 81708.71000000, + 81723.60000000, + 81738.49000000, + 81753.38000000, + 81768.26000000, + 81783.14000000, + 81798.01000000, + 81812.88000000, + 81827.74000000, + 81842.60000000, + 81857.46000000, + 81872.31000000, + 81887.16000000, + 81902.01000000, + 81916.85000000, + 81931.68000000, + 81946.52000000, + 81961.34000000, + 81976.17000000, + 81990.99000000, + 82005.81000000, + 82020.62000000, + 82035.43000000, + 82050.23000000, + 82065.03000000, + 82079.83000000, + 82094.62000000, + 82109.40000000, + 82124.19000000, + 82138.97000000, + 82153.74000000, + 82168.51000000, + 82183.28000000, + 82198.04000000, + 82212.80000000, + 82227.56000000, + 82242.31000000, + 82257.06000000, + 82271.80000000, + 82286.54000000, + 82301.27000000, + 82316.00000000, + 82330.73000000, + 82345.45000000, + 82360.17000000, + 82374.88000000, + 82389.59000000, + 82404.30000000, + 82419.00000000, + 82433.70000000, + 82448.39000000, + 82463.08000000, + 82477.77000000, + 82492.45000000, + 82507.13000000, + 82521.80000000, + 82536.47000000, + 82551.13000000, + 82565.80000000, + 82580.45000000, + 82595.11000000, + 82609.75000000, + 82624.40000000, + 82639.04000000, + 82653.67000000, + 82668.31000000, + 82682.93000000, + 82697.56000000, + 82712.18000000, + 82726.79000000, + 82741.41000000, + 82756.01000000, + 82770.62000000, + 82785.22000000, + 82799.81000000, + 82814.40000000, + 82828.99000000, + 82843.57000000, + 82858.15000000, + 82872.72000000, + 82887.29000000, + 82901.86000000, + 82916.42000000, + 82930.98000000, + 82945.53000000, + 82960.08000000, + 82974.63000000, + 82989.17000000, + 83003.71000000, + 83018.24000000, + 83032.77000000, + 83047.29000000, + 83061.81000000, + 83076.33000000, + 83090.84000000, + 83105.35000000, + 83119.85000000, + 83134.35000000, + 83148.85000000, + 83163.34000000, + 83177.83000000, + 83192.31000000, + 83206.79000000, + 83221.26000000, + 83235.74000000, + 83250.20000000, + 83264.66000000, + 83279.12000000, + 83293.58000000, + 83308.03000000, + 83322.47000000, + 83336.91000000, + 83351.35000000, + 83365.78000000, + 83380.21000000, + 83394.64000000, + 83409.06000000, + 83423.47000000, + 83437.89000000, + 83452.30000000, + 83466.70000000, + 83481.10000000, + 83495.49000000, + 83509.89000000, + 83524.27000000, + 83538.66000000, + 83553.03000000, + 83567.41000000, + 83581.78000000, + 83596.15000000, + 83610.51000000, + 83624.87000000, + 83639.22000000, + 83653.57000000, + 83667.91000000, + 83682.26000000, + 83696.59000000, + 83710.92000000, + 83725.25000000, + 83739.58000000, + 83753.90000000, + 83768.21000000, + 83782.53000000, + 83796.83000000, + 83811.14000000, + 83825.43000000, + 83839.73000000, + 83854.02000000, + 83868.31000000, + 83882.59000000, + 83896.87000000, + 83911.14000000, + 83925.41000000, + 83939.68000000, + 83953.94000000, + 83968.19000000, + 83982.45000000, + 83996.69000000, + 84010.94000000, + 84025.18000000, + 84039.41000000, + 84053.65000000, + 84067.87000000, + 84082.10000000, + 84096.31000000, + 84110.53000000, + 84124.74000000, + 84138.95000000, + 84153.15000000, + 84167.34000000, + 84181.54000000, + 84195.73000000, + 84209.91000000, + 84224.09000000, + 84238.27000000, + 84252.44000000, + 84266.61000000, + 84280.77000000, + 84294.93000000, + 84309.09000000, + 84323.24000000, + 84337.38000000, + 84351.53000000, + 84365.66000000, + 84379.80000000, + 84393.93000000, + 84408.05000000, + 84422.17000000, + 84436.29000000, + 84450.40000000, + 84464.51000000, + 84478.62000000, + 84492.71000000, + 84506.81000000, + 84520.90000000, + 84534.99000000, + 84549.07000000, + 84563.15000000, + 84577.22000000, + 84591.29000000, + 84605.36000000, + 84619.42000000, + 84633.48000000, + 84647.53000000, + 84661.58000000, + 84675.62000000, + 84689.66000000, + 84703.70000000, + 84717.73000000, + 84731.75000000, + 84745.78000000, + 84759.80000000, + 84773.81000000, + 84787.82000000, + 84801.82000000, + 84815.83000000, + 84829.82000000, + 84843.81000000, + 84857.80000000, + 84871.79000000, + 84885.77000000, + 84899.74000000, + 84913.71000000, + 84927.68000000, + 84941.64000000, + 84955.60000000, + 84969.55000000, + 84983.50000000, + 84997.45000000, + 85011.39000000, + 85025.32000000, + 85039.26000000, + 85053.18000000, + 85067.11000000, + 85081.03000000, + 85094.94000000, + 85108.85000000, + 85122.76000000, + 85136.66000000, + 85150.56000000, + 85164.45000000, + 85178.34000000, + 85192.22000000, + 85206.10000000, + 85219.98000000, + 85233.85000000, + 85247.72000000, + 85261.58000000, + 85275.44000000, + 85289.29000000, + 85303.14000000, + 85316.99000000, + 85330.83000000, + 85344.67000000, + 85358.50000000, + 85372.33000000, + 85386.15000000, + 85399.97000000, + 85413.78000000, + 85427.60000000, + 85441.40000000, + 85455.20000000, + 85469.00000000, + 85482.79000000, + 85496.58000000, + 85510.37000000, + 85524.15000000, + 85537.92000000, + 85551.69000000, + 85565.46000000, + 85579.22000000, + 85592.98000000, + 85606.73000000, + 85620.48000000, + 85634.23000000, + 85647.97000000, + 85661.71000000, + 85675.44000000, + 85689.16000000, + 85702.89000000, + 85716.61000000, + 85730.32000000, + 85744.03000000, + 85757.74000000, + 85771.44000000, + 85785.13000000, + 85798.83000000, + 85812.51000000, + 85826.20000000, + 85839.88000000, + 85853.55000000, + 85867.22000000, + 85880.89000000, + 85894.55000000, + 85908.21000000, + 85921.86000000, + 85935.51000000, + 85949.15000000, + 85962.79000000, + 85976.42000000, + 85990.06000000, + 86003.68000000, + 86017.30000000, + 86030.92000000, + 86044.53000000, + 86058.14000000, + 86071.75000000, + 86085.35000000, + 86098.94000000, + 86112.53000000, + 86126.12000000, + 86139.70000000, + 86153.28000000, + 86166.85000000, + 86180.42000000, + 86193.98000000, + 86207.54000000, + 86221.10000000, + 86234.65000000, + 86248.19000000, + 86261.74000000, + 86275.27000000, + 86288.81000000, + 86302.34000000, + 86315.86000000, + 86329.38000000, + 86342.89000000, + 86356.41000000, + 86369.91000000, + 86383.41000000, + 86396.91000000, + 86410.40000000, + 86423.89000000, + 86437.38000000, + 86450.86000000, + 86464.33000000, + 86477.80000000, + 86491.27000000, + 86504.73000000, + 86518.19000000, + 86531.64000000, + 86545.09000000, + 86558.53000000, + 86571.97000000, + 86585.40000000, + 86598.84000000, + 86612.26000000, + 86625.68000000, + 86639.10000000, + 86652.51000000, + 86665.92000000, + 86679.32000000, + 86692.72000000, + 86706.12000000, + 86719.51000000, + 86732.89000000, + 86746.27000000, + 86759.65000000, + 86773.02000000, + 86786.39000000, + 86799.75000000, + 86813.11000000, + 86826.46000000, + 86839.81000000, + 86853.16000000, + 86866.50000000, + 86879.83000000, + 86893.16000000, + 86906.49000000, + 86919.81000000, + 86933.13000000, + 86946.44000000, + 86959.75000000, + 86973.06000000, + 86986.36000000, + 86999.65000000, + 87012.94000000, + 87026.23000000, + 87039.51000000, + 87052.79000000, + 87066.06000000, + 87079.33000000, + 87092.59000000, + 87105.85000000, + 87119.10000000, + 87132.35000000, + 87145.60000000, + 87158.84000000, + 87172.07000000, + 87185.30000000, + 87198.53000000, + 87211.75000000, + 87224.97000000, + 87238.19000000, + 87251.39000000, + 87264.60000000, + 87277.80000000, + 87290.99000000, + 87304.18000000, + 87317.37000000, + 87330.55000000, + 87343.73000000, + 87356.90000000, + 87370.07000000, + 87383.23000000, + 87396.39000000, + 87409.54000000, + 87422.69000000, + 87435.84000000, + 87448.98000000, + 87462.11000000, + 87475.24000000, + 87488.37000000, + 87501.49000000, + 87514.61000000, + 87527.72000000, + 87540.83000000, + 87553.93000000, + 87567.03000000, + 87580.13000000, + 87593.22000000, + 87606.30000000, + 87619.38000000, + 87632.46000000, + 87645.53000000, + 87658.59000000, + 87671.66000000, + 87684.71000000, + 87697.77000000, + 87710.81000000, + 87723.86000000, + 87736.90000000, + 87749.93000000, + 87762.96000000, + 87775.99000000, + 87789.01000000, + 87802.02000000, + 87815.03000000, + 87828.04000000, + 87841.04000000, + 87854.04000000, + 87867.03000000, + 87880.02000000, + 87893.00000000, + 87905.98000000, + 87918.96000000, + 87931.93000000, + 87944.89000000, + 87957.85000000, + 87970.81000000, + 87983.76000000, + 87996.70000000, + 88009.64000000, + 88022.58000000, + 88035.51000000, + 88048.44000000, + 88061.36000000, + 88074.28000000, + 88087.20000000, + 88100.11000000, + 88113.01000000, + 88125.91000000, + 88138.80000000, + 88151.69000000, + 88164.58000000, + 88177.46000000, + 88190.34000000, + 88203.21000000, + 88216.08000000, + 88228.94000000, + 88241.80000000, + 88254.65000000, + 88267.50000000, + 88280.34000000, + 88293.18000000, + 88306.01000000, + 88318.84000000, + 88331.67000000, + 88344.49000000, + 88357.30000000, + 88370.11000000, + 88382.92000000, + 88395.72000000, + 88408.52000000, + 88421.31000000, + 88434.09000000, + 88446.88000000, + 88459.65000000, + 88472.43000000, + 88485.20000000, + 88497.96000000, + 88510.72000000, + 88523.47000000, + 88536.22000000, + 88548.97000000, + 88561.71000000, + 88574.44000000, + 88587.17000000, + 88599.90000000, + 88612.62000000, + 88625.33000000, + 88638.05000000, + 88650.75000000, + 88663.45000000, + 88676.15000000, + 88688.84000000, + 88701.53000000, + 88714.22000000, + 88726.89000000, + 88739.57000000, + 88752.24000000, + 88764.90000000, + 88777.56000000, + 88790.21000000, + 88802.86000000, + 88815.51000000, + 88828.15000000, + 88840.78000000, + 88853.42000000, + 88866.04000000, + 88878.66000000, + 88891.28000000, + 88903.89000000, + 88916.50000000, + 88929.10000000, + 88941.70000000, + 88954.29000000, + 88966.88000000, + 88979.46000000, + 88992.04000000, + 89004.61000000, + 89017.18000000, + 89029.74000000, + 89042.30000000, + 89054.86000000, + 89067.41000000, + 89079.95000000, + 89092.49000000, + 89105.02000000, + 89117.56000000, + 89130.08000000, + 89142.60000000, + 89155.12000000, + 89167.63000000, + 89180.13000000, + 89192.64000000, + 89205.13000000, + 89217.62000000, + 89230.11000000, + 89242.59000000, + 89255.07000000, + 89267.54000000, + 89280.01000000, + 89292.47000000, + 89304.93000000, + 89317.38000000, + 89329.83000000, + 89342.27000000, + 89354.71000000, + 89367.15000000, + 89379.58000000, + 89392.00000000, + 89404.42000000, + 89416.83000000, + 89429.24000000, + 89441.65000000, + 89454.05000000, + 89466.44000000, + 89478.83000000, + 89491.22000000, + 89503.60000000, + 89515.97000000, + 89528.34000000, + 89540.71000000, + 89553.07000000, + 89565.42000000, + 89577.78000000, + 89590.12000000, + 89602.46000000, + 89614.80000000, + 89627.13000000, + 89639.46000000, + 89651.78000000, + 89664.10000000, + 89676.41000000, + 89688.72000000, + 89701.02000000, + 89713.31000000, + 89725.61000000, + 89737.89000000, + 89750.18000000, + 89762.46000000, + 89774.73000000, + 89787.00000000, + 89799.26000000, + 89811.52000000, + 89823.77000000, + 89836.02000000, + 89848.26000000, + 89860.50000000, + 89872.73000000, + 89884.96000000, + 89897.19000000, + 89909.40000000, + 89921.62000000, + 89933.83000000, + 89946.03000000, + 89958.23000000, + 89970.42000000, + 89982.61000000, + 89994.80000000, + 90006.98000000, + 90019.15000000, + 90031.32000000, + 90043.48000000, + 90055.64000000, + 90067.80000000, + 90079.95000000, + 90092.09000000, + 90104.23000000, + 90116.37000000, + 90128.50000000, + 90140.62000000, + 90152.74000000, + 90164.85000000, + 90176.96000000, + 90189.07000000, + 90201.17000000, + 90213.26000000, + 90225.35000000, + 90237.44000000, + 90249.52000000, + 90261.59000000, + 90273.66000000, + 90285.73000000, + 90297.79000000, + 90309.84000000, + 90321.89000000, + 90333.94000000, + 90345.98000000, + 90358.01000000, + 90370.04000000, + 90382.07000000, + 90394.09000000, + 90406.10000000, + 90418.11000000, + 90430.12000000, + 90442.12000000, + 90454.11000000, + 90466.10000000, + 90478.09000000, + 90490.07000000, + 90502.04000000, + 90514.01000000, + 90525.98000000, + 90537.93000000, + 90549.89000000, + 90561.84000000, + 90573.78000000, + 90585.72000000, + 90597.66000000, + 90609.59000000, + 90621.51000000, + 90633.43000000, + 90645.34000000, + 90657.25000000, + 90669.16000000, + 90681.06000000, + 90692.95000000, + 90704.84000000, + 90716.72000000, + 90728.60000000, + 90740.48000000, + 90752.34000000, + 90764.21000000, + 90776.07000000, + 90787.92000000, + 90799.77000000, + 90811.61000000, + 90823.45000000, + 90835.28000000, + 90847.11000000, + 90858.93000000, + 90870.75000000, + 90882.56000000, + 90894.37000000, + 90906.17000000, + 90917.97000000, + 90929.76000000, + 90941.55000000, + 90953.33000000, + 90965.11000000, + 90976.88000000, + 90988.65000000, + 91000.41000000, + 91012.17000000, + 91023.92000000, + 91035.66000000, + 91047.41000000, + 91059.14000000, + 91070.87000000, + 91082.60000000, + 91094.32000000, + 91106.03000000, + 91117.74000000, + 91129.45000000, + 91141.15000000, + 91152.84000000, + 91164.53000000, + 91176.22000000, + 91187.90000000, + 91199.57000000, + 91211.24000000, + 91222.91000000, + 91234.56000000, + 91246.22000000, + 91257.87000000, + 91269.51000000, + 91281.15000000, + 91292.78000000, + 91304.41000000, + 91316.03000000, + 91327.65000000, + 91339.26000000, + 91350.87000000, + 91362.47000000, + 91374.07000000, + 91385.66000000, + 91397.24000000, + 91408.82000000, + 91420.40000000, + 91431.97000000, + 91443.54000000, + 91455.10000000, + 91466.65000000, + 91478.20000000, + 91489.75000000, + 91501.28000000, + 91512.82000000, + 91524.35000000, + 91535.87000000, + 91547.39000000, + 91558.90000000, + 91570.41000000, + 91581.91000000, + 91593.41000000, + 91604.90000000, + 91616.39000000, + 91627.87000000, + 91639.35000000, + 91650.82000000, + 91662.29000000, + 91673.75000000, + 91685.20000000, + 91696.65000000, + 91708.10000000, + 91719.54000000, + 91730.97000000, + 91742.40000000, + 91753.83000000, + 91765.24000000, + 91776.66000000, + 91788.07000000, + 91799.47000000, + 91810.87000000, + 91822.26000000, + 91833.64000000, + 91845.03000000, + 91856.40000000, + 91867.77000000, + 91879.14000000, + 91890.50000000, + 91901.86000000, + 91913.21000000, + 91924.55000000, + 91935.89000000, + 91947.22000000, + 91958.55000000, + 91969.88000000, + 91981.19000000, + 91992.51000000, + 92003.81000000, + 92015.12000000, + 92026.41000000, + 92037.70000000, + 92048.99000000, + 92060.27000000, + 92071.55000000, + 92082.82000000, + 92094.08000000, + 92105.34000000, + 92116.59000000, + 92127.84000000, + 92139.09000000, + 92150.32000000, + 92161.56000000, + 92172.78000000, + 92184.00000000, + 92195.22000000, + 92206.43000000, + 92217.64000000, + 92228.84000000, + 92240.03000000, + 92251.22000000, + 92262.41000000, + 92273.59000000, + 92284.76000000, + 92295.93000000, + 92307.09000000, + 92318.25000000, + 92329.40000000, + 92340.55000000, + 92351.69000000, + 92362.82000000, + 92373.95000000, + 92385.08000000, + 92396.20000000, + 92407.31000000, + 92418.42000000, + 92429.52000000, + 92440.62000000, + 92451.71000000, + 92462.80000000, + 92473.88000000, + 92484.96000000, + 92496.03000000, + 92507.09000000, + 92518.15000000, + 92529.21000000, + 92540.25000000, + 92551.30000000, + 92562.34000000, + 92573.37000000, + 92584.39000000, + 92595.42000000, + 92606.43000000, + 92617.44000000, + 92628.45000000, + 92639.45000000, + 92650.44000000, + 92661.43000000, + 92672.41000000, + 92683.39000000, + 92694.36000000, + 92705.33000000, + 92716.29000000, + 92727.25000000, + 92738.20000000, + 92749.14000000, + 92760.08000000, + 92771.01000000, + 92781.94000000, + 92792.87000000, + 92803.78000000, + 92814.69000000, + 92825.60000000, + 92836.50000000, + 92847.40000000, + 92858.29000000, + 92869.17000000, + 92880.05000000, + 92890.92000000, + 92901.79000000, + 92912.65000000, + 92923.51000000, + 92934.36000000, + 92945.20000000, + 92956.04000000, + 92966.88000000, + 92977.70000000, + 92988.53000000, + 92999.34000000, + 93010.16000000, + 93020.96000000, + 93031.76000000, + 93042.56000000, + 93053.35000000, + 93064.13000000, + 93074.91000000, + 93085.68000000, + 93096.45000000, + 93107.21000000, + 93117.97000000, + 93128.72000000, + 93139.46000000, + 93150.20000000, + 93160.94000000, + 93171.66000000, + 93182.39000000, + 93193.10000000, + 93203.81000000, + 93214.52000000, + 93225.22000000, + 93235.91000000, + 93246.60000000, + 93257.28000000, + 93267.96000000, + 93278.63000000, + 93289.30000000, + 93299.96000000, + 93310.61000000, + 93321.26000000, + 93331.91000000, + 93342.54000000, + 93353.18000000, + 93363.80000000, + 93374.42000000, + 93385.04000000, + 93395.65000000, + 93406.25000000, + 93416.85000000, + 93427.44000000, + 93438.03000000, + 93448.61000000, + 93459.19000000, + 93469.76000000, + 93480.32000000, + 93490.88000000, + 93501.43000000, + 93511.98000000, + 93522.52000000, + 93533.05000000, + 93543.58000000, + 93554.11000000, + 93564.62000000, + 93575.14000000, + 93585.64000000, + 93596.14000000, + 93606.64000000, + 93617.13000000, + 93627.61000000, + 93638.09000000, + 93648.56000000, + 93659.03000000, + 93669.49000000, + 93679.95000000, + 93690.40000000, + 93700.84000000, + 93711.28000000, + 93721.71000000, + 93732.13000000, + 93742.56000000, + 93752.97000000, + 93763.38000000, + 93773.78000000, + 93784.18000000, + 93794.57000000, + 93804.96000000, + 93815.34000000, + 93825.71000000, + 93836.08000000, + 93846.44000000, + 93856.80000000, + 93867.15000000, + 93877.49000000, + 93887.83000000, + 93898.17000000, + 93908.49000000, + 93918.81000000, + 93929.13000000, + 93939.44000000, + 93949.74000000, + 93960.04000000, + 93970.34000000, + 93980.62000000, + 93990.90000000, + 94001.18000000, + 94011.45000000, + 94021.71000000, + 94031.97000000, + 94042.22000000, + 94052.46000000, + 94062.70000000, + 94072.94000000, + 94083.17000000, + 94093.39000000, + 94103.60000000, + 94113.81000000, + 94124.02000000, + 94134.22000000, + 94144.41000000, + 94154.60000000, + 94164.78000000, + 94174.95000000, + 94185.12000000, + 94195.28000000, + 94205.44000000, + 94215.59000000, + 94225.74000000, + 94235.87000000, + 94246.01000000, + 94256.14000000, + 94266.26000000, + 94276.37000000, + 94286.48000000, + 94296.59000000, + 94306.68000000, + 94316.77000000, + 94326.86000000, + 94336.94000000, + 94347.01000000, + 94357.08000000, + 94367.14000000, + 94377.20000000, + 94387.25000000, + 94397.29000000, + 94407.33000000, + 94417.36000000, + 94427.39000000, + 94437.41000000, + 94447.42000000, + 94457.43000000, + 94467.43000000, + 94477.42000000, + 94487.41000000, + 94497.40000000, + 94507.38000000, + 94517.35000000, + 94527.31000000, + 94537.27000000, + 94547.22000000, + 94557.17000000, + 94567.11000000, + 94577.05000000, + 94586.98000000, + 94596.90000000, + 94606.82000000, + 94616.73000000, + 94626.63000000, + 94636.53000000, + 94646.42000000, + 94656.31000000, + 94666.19000000, + 94676.07000000, + 94685.93000000, + 94695.80000000, + 94705.65000000, + 94715.50000000, + 94725.35000000, + 94735.18000000, + 94745.02000000, + 94754.84000000, + 94764.66000000, + 94774.47000000, + 94784.28000000, + 94794.08000000, + 94803.88000000, + 94813.67000000, + 94823.45000000, + 94833.22000000, + 94842.99000000, + 94852.76000000, + 94862.52000000, + 94872.27000000, + 94882.01000000, + 94891.75000000, + 94901.49000000, + 94911.21000000, + 94920.93000000, + 94930.65000000, + 94940.36000000, + 94950.06000000, + 94959.75000000, + 94969.44000000, + 94979.13000000, + 94988.80000000, + 94998.48000000, + 95008.14000000, + 95017.80000000, + 95027.45000000, + 95037.10000000, + 95046.74000000, + 95056.37000000, + 95066.00000000, + 95075.62000000, + 95085.23000000, + 95094.84000000, + 95104.44000000, + 95114.04000000, + 95123.63000000, + 95133.21000000, + 95142.79000000, + 95152.36000000, + 95161.93000000, + 95171.48000000, + 95181.04000000, + 95190.58000000, + 95200.12000000, + 95209.65000000, + 95219.18000000, + 95228.70000000, + 95238.22000000, + 95247.72000000, + 95257.22000000, + 95266.72000000, + 95276.21000000, + 95285.69000000, + 95295.17000000, + 95304.64000000, + 95314.10000000, + 95323.56000000, + 95333.01000000, + 95342.45000000, + 95351.89000000, + 95361.32000000, + 95370.75000000, + 95380.16000000, + 95389.58000000, + 95398.98000000, + 95408.38000000, + 95417.77000000, + 95427.16000000, + 95436.54000000, + 95445.91000000, + 95455.28000000, + 95464.64000000, + 95474.00000000, + 95483.35000000, + 95492.69000000, + 95502.02000000, + 95511.35000000, + 95520.67000000, + 95529.99000000, + 95539.30000000, + 95548.60000000, + 95557.90000000, + 95567.19000000, + 95576.47000000, + 95585.75000000, + 95595.02000000, + 95604.28000000, + 95613.54000000, + 95622.79000000, + 95632.03000000, + 95641.27000000, + 95650.50000000, + 95659.73000000, + 95668.95000000, + 95678.16000000, + 95687.36000000, + 95696.56000000, + 95705.75000000, + 95714.94000000, + 95724.12000000, + 95733.29000000, + 95742.46000000, + 95751.62000000, + 95760.77000000, + 95769.92000000, + 95779.05000000, + 95788.19000000, + 95797.31000000, + 95806.43000000, + 95815.55000000, + 95824.65000000, + 95833.75000000, + 95842.85000000, + 95851.93000000, + 95861.01000000, + 95870.09000000, + 95879.15000000, + 95888.21000000, + 95897.27000000, + 95906.31000000, + 95915.35000000, + 95924.39000000, + 95933.41000000, + 95942.43000000, + 95951.45000000, + 95960.46000000, + 95969.46000000, + 95978.45000000, + 95987.44000000, + 95996.42000000, + 96005.39000000, + 96014.36000000, + 96023.31000000, + 96032.27000000, + 96041.21000000, + 96050.15000000, + 96059.09000000, + 96068.01000000, + 96076.93000000, + 96085.85000000, + 96094.75000000, + 96103.65000000, + 96112.54000000, + 96121.43000000, + 96130.31000000, + 96139.18000000, + 96148.04000000, + 96156.90000000, + 96165.75000000, + 96174.60000000, + 96183.44000000, + 96192.27000000, + 96201.09000000, + 96209.91000000, + 96218.72000000, + 96227.53000000, + 96236.32000000, + 96245.11000000, + 96253.90000000, + 96262.67000000, + 96271.44000000, + 96280.21000000, + 96288.96000000, + 96297.71000000, + 96306.45000000, + 96315.19000000, + 96323.92000000, + 96332.64000000, + 96341.35000000, + 96350.06000000, + 96358.76000000, + 96367.45000000, + 96376.14000000, + 96384.82000000, + 96393.49000000, + 96402.16000000, + 96410.82000000, + 96419.47000000, + 96428.12000000, + 96436.75000000, + 96445.39000000, + 96454.01000000, + 96462.63000000, + 96471.24000000, + 96479.84000000, + 96488.44000000, + 96497.03000000, + 96505.61000000, + 96514.18000000, + 96522.75000000, + 96531.31000000, + 96539.87000000, + 96548.41000000, + 96556.95000000, + 96565.49000000, + 96574.01000000, + 96582.53000000, + 96591.04000000, + 96599.55000000, + 96608.04000000, + 96616.54000000, + 96625.02000000, + 96633.50000000, + 96641.96000000, + 96650.43000000, + 96658.88000000, + 96667.33000000, + 96675.77000000, + 96684.20000000, + 96692.63000000, + 96701.05000000, + 96709.46000000, + 96717.87000000, + 96726.26000000, + 96734.65000000, + 96743.04000000, + 96751.41000000, + 96759.78000000, + 96768.15000000, + 96776.50000000, + 96784.85000000, + 96793.19000000, + 96801.52000000, + 96809.85000000, + 96818.16000000, + 96826.48000000, + 96834.78000000, + 96843.08000000, + 96851.37000000, + 96859.65000000, + 96867.92000000, + 96876.19000000, + 96884.45000000, + 96892.70000000, + 96900.95000000, + 96909.19000000, + 96917.42000000, + 96925.64000000, + 96933.86000000, + 96942.07000000, + 96950.27000000, + 96958.47000000, + 96966.65000000, + 96974.83000000, + 96983.01000000, + 96991.17000000, + 96999.33000000, + 97007.48000000, + 97015.62000000, + 97023.76000000, + 97031.89000000, + 97040.01000000, + 97048.12000000, + 97056.23000000, + 97064.32000000, + 97072.41000000, + 97080.50000000, + 97088.57000000, + 97096.64000000, + 97104.70000000, + 97112.76000000, + 97120.80000000, + 97128.84000000, + 97136.87000000, + 97144.90000000, + 97152.91000000, + 97160.92000000, + 97168.92000000, + 97176.92000000, + 97184.90000000, + 97192.88000000, + 97200.85000000, + 97208.82000000, + 97216.77000000, + 97224.72000000, + 97232.66000000, + 97240.59000000, + 97248.52000000, + 97256.44000000, + 97264.35000000, + 97272.25000000, + 97280.15000000, + 97288.03000000, + 97295.91000000, + 97303.78000000, + 97311.65000000, + 97319.51000000, + 97327.36000000, + 97335.20000000, + 97343.03000000, + 97350.86000000, + 97358.68000000, + 97366.49000000, + 97374.29000000, + 97382.08000000, + 97389.87000000, + 97397.65000000, + 97405.42000000, + 97413.19000000, + 97420.94000000, + 97428.69000000, + 97436.43000000, + 97444.17000000, + 97451.89000000, + 97459.61000000, + 97467.32000000, + 97475.02000000, + 97482.72000000, + 97490.40000000, + 97498.08000000, + 97505.75000000, + 97513.42000000, + 97521.07000000, + 97528.72000000, + 97536.36000000, + 97543.99000000, + 97551.61000000, + 97559.23000000, + 97566.83000000, + 97574.43000000, + 97582.03000000, + 97589.61000000, + 97597.19000000, + 97604.75000000, + 97612.31000000, + 97619.87000000, + 97627.41000000, + 97634.95000000, + 97642.47000000, + 97649.99000000, + 97657.51000000, + 97665.01000000, + 97672.51000000, + 97679.99000000, + 97687.47000000, + 97694.94000000, + 97702.41000000, + 97709.86000000, + 97717.31000000, + 97724.75000000, + 97732.18000000, + 97739.61000000, + 97747.02000000, + 97754.43000000, + 97761.83000000, + 97769.22000000, + 97776.60000000, + 97783.97000000, + 97791.34000000, + 97798.70000000, + 97806.05000000, + 97813.39000000, + 97820.72000000, + 97828.05000000, + 97835.37000000, + 97842.68000000, + 97849.98000000, + 97857.27000000, + 97864.55000000, + 97871.83000000, + 97879.10000000, + 97886.36000000, + 97893.61000000, + 97900.85000000, + 97908.08000000, + 97915.31000000, + 97922.53000000, + 97929.74000000, + 97936.94000000, + 97944.13000000, + 97951.31000000, + 97958.49000000, + 97965.66000000, + 97972.82000000, + 97979.97000000, + 97987.11000000, + 97994.24000000, + 98001.37000000, + 98008.48000000, + 98015.59000000, + 98022.69000000, + 98029.78000000, + 98036.87000000, + 98043.94000000, + 98051.01000000, + 98058.06000000, + 98065.11000000, + 98072.15000000, + 98079.18000000, + 98086.21000000, + 98093.22000000, + 98100.23000000, + 98107.23000000, + 98114.21000000, + 98121.19000000, + 98128.17000000, + 98135.13000000, + 98142.08000000, + 98149.03000000, + 98155.96000000, + 98162.89000000, + 98169.81000000, + 98176.72000000, + 98183.63000000, + 98190.52000000, + 98197.40000000, + 98204.28000000, + 98211.15000000, + 98218.00000000, + 98224.85000000, + 98231.69000000, + 98238.53000000, + 98245.35000000, + 98252.16000000, + 98258.97000000, + 98265.76000000, + 98272.55000000, + 98279.33000000, + 98286.10000000, + 98292.86000000, + 98299.61000000, + 98306.36000000, + 98313.09000000, + 98319.82000000, + 98326.53000000, + 98333.24000000, + 98339.94000000, + 98346.63000000, + 98353.31000000, + 98359.98000000, + 98366.64000000, + 98373.29000000, + 98379.94000000, + 98386.57000000, + 98393.20000000, + 98399.82000000, + 98406.42000000, + 98413.02000000, + 98419.61000000, + 98426.19000000, + 98432.76000000, + 98439.33000000, + 98445.88000000, + 98452.42000000, + 98458.96000000, + 98465.48000000, + 98472.00000000, + 98478.51000000, + 98485.00000000, + 98491.49000000, + 98497.97000000, + 98504.44000000, + 98510.90000000, + 98517.35000000, + 98523.79000000, + 98530.22000000, + 98536.65000000, + 98543.06000000, + 98549.46000000, + 98555.86000000, + 98562.24000000, + 98568.62000000, + 98574.99000000, + 98581.34000000, + 98587.69000000, + 98594.03000000, + 98600.36000000, + 98606.68000000, + 98612.99000000, + 98619.29000000, + 98625.58000000, + 98631.86000000, + 98638.13000000, + 98644.39000000, + 98650.64000000, + 98656.88000000, + 98663.12000000, + 98669.34000000, + 98675.55000000, + 98681.76000000, + 98687.95000000, + 98694.13000000, + 98700.31000000, + 98706.47000000, + 98712.63000000, + 98718.77000000, + 98724.91000000, + 98731.03000000, + 98737.15000000, + 98743.25000000, + 98749.35000000, + 98755.44000000, + 98761.51000000, + 98767.58000000, + 98773.63000000, + 98779.68000000, + 98785.72000000, + 98791.74000000, + 98797.76000000, + 98803.76000000, + 98809.76000000, + 98815.75000000, + 98821.72000000, + 98827.69000000, + 98833.64000000, + 98839.59000000, + 98845.53000000, + 98851.45000000, + 98857.37000000, + 98863.27000000, + 98869.17000000, + 98875.05000000, + 98880.93000000, + 98886.79000000, + 98892.64000000, + 98898.49000000, + 98904.32000000, + 98910.14000000, + 98915.96000000, + 98921.76000000, + 98927.55000000, + 98933.33000000, + 98939.10000000, + 98944.86000000, + 98950.61000000, + 98956.35000000, + 98962.08000000, + 98967.80000000, + 98973.51000000, + 98979.21000000, + 98984.89000000, + 98990.57000000, + 98996.23000000, + 99001.89000000, + 99007.53000000, + 99013.17000000, + 99018.79000000, + 99024.40000000, + 99030.00000000, + 99035.59000000, + 99041.17000000, + 99046.74000000, + 99052.30000000, + 99057.85000000, + 99063.39000000, + 99068.91000000, + 99074.43000000, + 99079.93000000, + 99085.42000000, + 99090.91000000, + 99096.38000000, + 99101.84000000, + 99107.29000000, + 99112.72000000, + 99118.15000000, + 99123.57000000, + 99128.97000000, + 99134.36000000, + 99139.75000000, + 99145.12000000, + 99150.48000000, + 99155.83000000, + 99161.16000000, + 99166.49000000, + 99171.80000000, + 99177.11000000, + 99182.40000000, + 99187.68000000, + 99192.95000000, + 99198.21000000, + 99203.45000000, + 99208.69000000, + 99213.91000000, + 99219.12000000, + 99224.32000000, + 99229.51000000, + 99234.69000000, + 99239.85000000, + 99245.01000000, + 99250.15000000, + 99255.28000000, + 99260.40000000, + 99265.50000000, + 99270.60000000, + 99275.68000000, + 99280.75000000, + 99285.81000000, + 99290.86000000, + 99295.89000000, + 99300.92000000, + 99305.93000000, + 99310.93000000, + 99315.91000000, + 99320.89000000, + 99325.85000000, + 99330.80000000, + 99335.74000000, + 99340.67000000, + 99345.58000000, + 99350.48000000, + 99355.37000000, + 99360.25000000, + 99365.12000000, + 99369.97000000, + 99374.81000000, + 99379.64000000, + 99384.45000000, + 99389.26000000, + 99394.05000000, + 99398.82000000, + 99403.59000000, + 99408.34000000, + 99413.08000000, + 99417.81000000, + 99422.52000000, + 99427.22000000, + 99431.91000000, + 99436.59000000, + 99441.25000000, + 99445.90000000, + 99450.54000000, + 99455.16000000, + 99459.77000000, + 99464.37000000, + 99468.95000000, + 99473.52000000, + 99478.08000000, + 99482.63000000, + 99487.16000000, + 99491.68000000, + 99496.18000000, + 99500.67000000, + 99505.15000000, + 99509.62000000, + 99514.07000000, + 99518.51000000, + 99522.93000000, + 99527.34000000, + 99531.74000000, + 99536.12000000, + 99540.49000000, + 99544.84000000, + 99549.19000000, + 99553.51000000, + 99557.83000000, + 99562.13000000, + 99566.41000000, + 99570.69000000, + 99574.94000000, + 99579.19000000, + 99583.42000000, + 99587.63000000, + 99591.83000000, + 99596.02000000, + 99600.19000000, + 99604.35000000, + 99608.49000000, + 99612.62000000, + 99616.73000000, + 99620.83000000, + 99624.92000000, + 99628.99000000, + 99633.04000000, + 99637.08000000, + 99641.11000000, + 99645.12000000, + 99649.11000000, + 99653.09000000, + 99657.05000000, + 99661.00000000, + 99664.94000000, + 99668.86000000, + 99672.76000000, + 99676.65000000, + 99680.52000000, + 99684.38000000, + 99688.22000000, + 99692.04000000, + 99695.85000000, + 99699.65000000, + 99703.43000000, + 99707.19000000, + 99710.93000000, + 99714.66000000, + 99718.38000000, + 99722.08000000, + 99725.76000000, + 99729.42000000, + 99733.07000000, + 99736.70000000, + 99740.32000000, + 99743.91000000, + 99747.50000000, + 99751.06000000, + 99754.61000000, + 99758.14000000, + 99761.65000000, + 99765.15000000, + 99768.63000000, + 99772.09000000, + 99775.53000000, + 99778.96000000, + 99782.37000000, + 99785.76000000, + 99789.14000000, + 99792.49000000, + 99795.83000000, + 99799.15000000, + 99802.45000000, + 99805.73000000, + 99809.00000000, + 99812.25000000, + 99815.47000000, + 99818.68000000, + 99821.87000000, + 99825.04000000, + 99828.20000000, + 99831.33000000, + 99834.44000000, + 99837.54000000, + 99840.61000000, + 99843.67000000, + 99846.70000000, + 99849.72000000, + 99852.72000000, + 99855.69000000, + 99858.65000000, + 99861.58000000, + 99864.49000000, + 99867.39000000, + 99870.26000000, + 99873.11000000, + 99875.94000000, + 99878.75000000, + 99881.54000000, + 99884.30000000, + 99887.05000000, + 99889.77000000, + 99892.47000000, + 99895.15000000, + 99897.80000000, + 99900.43000000, + 99903.04000000, + 99905.63000000, + 99908.19000000, + 99910.73000000, + 99913.24000000, + 99915.73000000, + 99918.20000000, + 99920.64000000, + 99923.06000000, + 99925.45000000, + 99927.82000000, + 99930.16000000, + 99932.47000000, + 99934.76000000, + 99937.02000000, + 99939.26000000, + 99941.47000000, + 99943.65000000, + 99945.80000000, + 99947.93000000, + 99950.02000000, + 99952.09000000, + 99954.13000000, + 99956.14000000, + 99958.12000000, + 99960.07000000, + 99961.98000000, + 99963.87000000, + 99965.72000000, + 99967.54000000, + 99969.33000000, + 99971.08000000, + 99972.80000000, + 99974.48000000, + 99976.12000000, + 99977.73000000, + 99979.31000000, + 99980.84000000, + 99982.33000000, + 99983.78000000, + 99985.19000000, + 99986.56000000, + 99987.88000000, + 99989.16000000, + 99990.38000000, + 99991.56000000, + 99992.68000000, + 99993.75000000, + 99994.76000000, + 99995.71000000, + 99996.60000000, + 99997.41000000, + 99998.15000000, + 99998.80000000, + 99999.35000000, + 99999.77000000 +] + +access(all) let flash_crash_moderate_agents: [SimAgent] = [ + SimAgent( + count: 150, + initialHF: 1.15000000, + rebalancingHF: 1.05000000, + targetHF: 1.08000000, + debtPerAgent: 133333.00000000, + totalSystemDebt: 20000000.00000000 + ) +] + +access(all) let flash_crash_moderate_pools: {String: SimPool} = { + "moet_yt": SimPool( + size: 500000.00000000, + concentration: 0.95000000, + feeTier: 0.00050000 + ), + "moet_btc": SimPool( + size: 5000000.00000000, + concentration: 0.80000000, + feeTier: 0.00300000 + ) +} + +access(all) let flash_crash_moderate_constants: SimConstants = SimConstants( + btcCollateralFactor: 0.80000000, + btcLiquidationThreshold: 0.85000000, + yieldAPR: 0.10000000, + directMintYT: true +) + +access(all) let flash_crash_moderate_expectedLiquidationCount: Int = 0 +access(all) let flash_crash_moderate_expectedAllAgentsSurvive: Bool = true + +access(all) let flash_crash_moderate_durationMinutes: Int = 2880 +access(all) let flash_crash_moderate_notes: String = "20% BTC crash over 5 min, floor for 20 min, exponential recovery. No oracle manipulation, no liquidity evaporation, no random noise." diff --git a/cadence/tests/forked_flash_crash_moderate_test.cdc b/cadence/tests/forked_flash_crash_moderate_test.cdc new file mode 100644 index 00000000..6281bb4d --- /dev/null +++ b/cadence/tests/forked_flash_crash_moderate_test.cdc @@ -0,0 +1,463 @@ +#test_fork(network: "mainnet-fork", height: 143292255) + +import Test +import BlockchainHelpers + +import "test_helpers.cdc" +import "evm_state_helpers.cdc" +import "flash_crash_moderate_helpers.cdc" + +import "FlowYieldVaults" +import "FlowToken" +import "MOET" +import "FlowYieldVaultsStrategiesV2" +import "FlowALPv0" + + +// ============================================================================ +// CADENCE ACCOUNTS +// ============================================================================ + +access(all) let flowYieldVaultsAccount = Test.getAccount(0xb1d63873c3cc9f79) +access(all) let flowALPAccount = Test.getAccount(0x6b00ff876c299c61) +access(all) let bandOracleAccount = Test.getAccount(0x6801a6222ebf784a) +access(all) let whaleFlowAccount = Test.getAccount(0x92674150c9213fc9) +access(all) let coaOwnerAccount = Test.getAccount(0xe467b9dd11fa00df) + +access(all) var strategyIdentifier = Type<@FlowYieldVaultsStrategiesV2.FUSDEVStrategy>().identifier +access(all) var flowTokenIdentifier = Type<@FlowToken.Vault>().identifier +access(all) var moetTokenIdentifier = Type<@MOET.Vault>().identifier + +// ============================================================================ +// PROTOCOL ADDRESSES +// ============================================================================ + +access(all) let factoryAddress = "0xca6d7Bb03334bBf135902e1d919a5feccb461632" + +// ============================================================================ +// VAULT & TOKEN ADDRESSES +// ============================================================================ + +access(all) let morphoVaultAddress = "0xd069d989e2F44B70c65347d1853C0c67e10a9F8D" +access(all) let pyusd0Address = "0x99aF3EeA856556646C98c8B9b2548Fe815240750" +access(all) let moetAddress = "0x213979bB8A9A86966999b3AA797C1fcf3B967ae2" +access(all) let wflowAddress = "0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e" + +// ============================================================================ +// STORAGE SLOT CONSTANTS +// ============================================================================ + +access(all) let moetBalanceSlot = 0 as UInt256 +access(all) let pyusd0BalanceSlot = 1 as UInt256 +access(all) let fusdevBalanceSlot = 12 as UInt256 +access(all) let wflowBalanceSlot = 3 as UInt256 + +access(all) let morphoVaultTotalSupplySlot = 11 as UInt256 +access(all) let morphoVaultTotalAssetsSlot = 15 as UInt256 + +// ============================================================================ +// SIMULATION CONFIG +// ============================================================================ + +// Number of agents to simulate (adjust to control test duration) +access(all) let numAgents = 1 + +// Funding per agent (FLOW) +access(all) let fundingPerAgent: UFix64 = 1000.0 + +// Initial FLOW price from fixture (used to normalize prices to a ratio) +access(all) let initialPrice: UFix64 = flash_crash_moderate_prices[0] + +// YT pricing: ERC4626 vault share price with deterministic 10% APR +access(all) let yieldAPR: UFix64 = flash_crash_moderate_constants.yieldAPR +access(all) let minutesPerYear: UFix64 = 525600.0 + +// ============================================================================ +// SETUP +// ============================================================================ + +access(all) +fun setup() { + deployContractsForFork() + + // Initialize all Uniswap V3 pools at 1:1 price + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: pyusd0Address, + tokenBAddress: morphoVaultAddress, + fee: 100, + priceTokenBPerTokenA: feeAdjustedPrice(1.0, fee: 100, reverse: false), + tokenABalanceSlot: pyusd0BalanceSlot, + tokenBBalanceSlot: fusdevBalanceSlot, + signer: coaOwnerAccount + ) + + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: pyusd0Address, + tokenBAddress: wflowAddress, + fee: 3000, + priceTokenBPerTokenA: feeAdjustedPrice(1.0, fee: 3000, reverse: false), + tokenABalanceSlot: pyusd0BalanceSlot, + tokenBBalanceSlot: wflowBalanceSlot, + signer: coaOwnerAccount + ) + + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: moetAddress, + tokenBAddress: morphoVaultAddress, + fee: 100, + priceTokenBPerTokenA: feeAdjustedPrice(1.0, fee: 100, reverse: false), + tokenABalanceSlot: moetBalanceSlot, + tokenBBalanceSlot: fusdevBalanceSlot, + signer: coaOwnerAccount + ) + + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: moetAddress, + tokenBAddress: pyusd0Address, + fee: 100, + priceTokenBPerTokenA: feeAdjustedPrice(1.0, fee: 100, reverse: false), + tokenABalanceSlot: moetBalanceSlot, + tokenBBalanceSlot: pyusd0BalanceSlot, + signer: coaOwnerAccount + ) + + setBandOraclePrices(signer: bandOracleAccount, symbolPrices: { + "FLOW": 1.0, + "USD": 1.0 + }) + + let reserveAmount = 100_000_00.0 + transferFlow(signer: whaleFlowAccount, recipient: flowALPAccount.address, amount: reserveAmount) + mintMoet(signer: flowALPAccount, to: flowALPAccount.address, amount: reserveAmount, beFailed: false) + + transferFlow(signer: whaleFlowAccount, recipient: flowYieldVaultsAccount.address, amount: reserveAmount) + transferFlow(signer: whaleFlowAccount, recipient: coaOwnerAccount.address, amount: reserveAmount) +} + +// ============================================================================ +// HELPERS +// ============================================================================ + +access(all) fun getFlowCollateralFromPosition(pid: UInt64): UFix64 { + let positionDetails = getPositionDetails(pid: pid, beFailed: false) + for balance in positionDetails.balances { + if balance.vaultType == Type<@FlowToken.Vault>() { + if balance.direction == FlowALPv0.BalanceDirection.Credit { + return balance.balance + } + } + } + return 0.0 +} + +access(all) fun getMOETDebtFromPosition(pid: UInt64): UFix64 { + let positionDetails = getPositionDetails(pid: pid, beFailed: false) + for balance in positionDetails.balances { + if balance.vaultType == Type<@MOET.Vault>() { + if balance.direction == FlowALPv0.BalanceDirection.Debit { + return balance.balance + } + } + } + return 0.0 +} + +/// Normalize a fixture price to a ratio relative to the initial price. +/// e.g. 80000 / 100000 = 0.8 +access(all) fun normalizePrice(_ absolutePrice: UFix64): UFix64 { + return absolutePrice / initialPrice +} + +/// Compute deterministic YT (ERC4626 vault share) price at a given minute. +/// price = 1.0 + yieldAPR * (minute / minutesPerYear) +access(all) fun ytPriceAtMinute(_ minute: Int): UFix64 { + return 1.0 + yieldAPR * (UFix64(minute) / minutesPerYear) +} + +/// Update all prices for a given simulation tick. +/// Sets BandOracle FLOW price, Uniswap V3 pool prices, and ERC4626 vault share price. +access(all) fun applyPriceTick(flowPrice: UFix64, ytPrice: UFix64, user: Test.TestAccount) { + // BandOracle: FLOW price + setBandOraclePrices(signer: bandOracleAccount, symbolPrices: { + "FLOW": flowPrice, + "USD": 1.0 + }) + + // PYUSD0/WFLOW pool: 1 WFLOW = flowPrice PYUSD0 + // When flowPrice < 1.0, deficit rebalance sells FUSDEV→PYUSD0→WFLOW + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: wflowAddress, + tokenBAddress: pyusd0Address, + fee: 3000, + priceTokenBPerTokenA: feeAdjustedPrice(UFix128(flowPrice), fee: 3000, reverse: true), + tokenABalanceSlot: wflowBalanceSlot, + tokenBBalanceSlot: pyusd0BalanceSlot, + signer: coaOwnerAccount + ) + + // MOET/FUSDEV pool: price depends on YT price + // 1 FUSDEV = ytPrice MOET + if flowPrice < 1.0 { + // Deficit: swaps FUSDEV→MOET (reverse) + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: moetAddress, + tokenBAddress: morphoVaultAddress, + fee: 100, + priceTokenBPerTokenA: feeAdjustedPrice(UFix128(ytPrice), fee: 100, reverse: true), + tokenABalanceSlot: moetBalanceSlot, + tokenBBalanceSlot: fusdevBalanceSlot, + signer: coaOwnerAccount + ) + } else { + // Surplus: swaps MOET→FUSDEV (forward) + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: moetAddress, + tokenBAddress: morphoVaultAddress, + fee: 100, + priceTokenBPerTokenA: feeAdjustedPrice(UFix128(ytPrice), fee: 100, reverse: false), + tokenABalanceSlot: moetBalanceSlot, + tokenBBalanceSlot: fusdevBalanceSlot, + signer: coaOwnerAccount + ) + } + + // ERC4626 vault share price (YT) + setVaultSharePrice( + vaultAddress: morphoVaultAddress, + assetAddress: pyusd0Address, + assetBalanceSlot: pyusd0BalanceSlot, + totalSupplySlot: morphoVaultTotalSupplySlot, + vaultTotalAssetsSlot: morphoVaultTotalAssetsSlot, + priceMultiplier: ytPrice, + signer: user + ) +} + +// ============================================================================ +// TEST: Flash Crash Moderate — Zero Liquidations +// ============================================================================ + +access(all) +fun test_FlashCrashModerate_ZeroLiquidations() { + let prices = flash_crash_moderate_prices + + // Create agents — each gets their own account, yield vault, and ALP position + let users: [Test.TestAccount] = [] + let pids: [UInt64] = [] + let vaultIds: [UInt64] = [] + + var i = 0 + while i < numAgents { + let user = Test.createAccount() + transferFlow(signer: whaleFlowAccount, recipient: user.address, amount: fundingPerAgent) + grantBeta(flowYieldVaultsAccount, user) + + setVaultSharePrice( + vaultAddress: morphoVaultAddress, + assetAddress: pyusd0Address, + assetBalanceSlot: pyusd0BalanceSlot, + totalSupplySlot: morphoVaultTotalSupplySlot, + vaultTotalAssetsSlot: morphoVaultTotalAssetsSlot, + priceMultiplier: 1.0, + signer: user + ) + + createYieldVault( + signer: user, + strategyIdentifier: strategyIdentifier, + vaultIdentifier: flowTokenIdentifier, + amount: fundingPerAgent, + beFailed: false + ) + + let pid = (getLastPositionOpenedEvent(Test.eventsOfType(Type())) as! FlowALPv0.Opened).pid + let yieldVaultIDs = getYieldVaultIDs(address: user.address)! + let vaultId = yieldVaultIDs[0] + + users.append(user) + pids.append(pid) + vaultIds.append(vaultId) + + log(" Agent \(i): pid=\(pid) vaultId=\(vaultId)") + i = i + 1 + } + + log("\n=== FLASH CRASH MODERATE SIMULATION ===") + log("Agents: \(numAgents)") + log("Funding per agent: \(fundingPerAgent) FLOW") + log("Duration: \(flash_crash_moderate_durationMinutes) minutes") + log("Price points: \(prices.length)") + log("Notes: \(flash_crash_moderate_notes)") + + // Track state + var liquidationCount = 0 + var rebalanceCount = 0 + var previousNormalizedPrice: UFix64 = 1.0 + var lowestPrice: UFix64 = initialPrice + var lowestHF: UFix64 = 100.0 // start high + + // Record start timestamp so we can advance to minute-aligned times + let startTimestamp = getCurrentBlockTimestamp() + + // Simulation loop — only act on price changes to avoid redundant work + var step = 0 + while step < prices.length { + let absolutePrice = prices[step] + let normalizedPrice = normalizePrice(absolutePrice) + let ytPrice = ytPriceAtMinute(step) + + // Track lowest price + if absolutePrice < lowestPrice { + lowestPrice = absolutePrice + } + + // Only update state when price has changed (skip flat regions) + if normalizedPrice != previousNormalizedPrice { + // Advance blockchain time to match the tick's minute offset + let expectedTimestamp = startTimestamp + UFix64(step) * 60.0 + let currentTimestamp = getCurrentBlockTimestamp() + if expectedTimestamp > currentTimestamp { + Test.moveTime(by: Fix64(expectedTimestamp - currentTimestamp)) + } + + // --- PROFILING: log wall-clock timestamps between steps --- + let profileTick = rebalanceCount < 5 // only profile first 5 ticks + if profileTick { log(" PROFILE [t=\(step)m] START") } + + // 1. BandOracle price + setBandOraclePrices(signer: bandOracleAccount, symbolPrices: { + "FLOW": normalizedPrice, + "USD": 1.0 + }) + if profileTick { log(" PROFILE [t=\(step)m] after setBandOraclePrices") } + + // 2. WFLOW/PYUSD0 pool + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: wflowAddress, + tokenBAddress: pyusd0Address, + fee: 3000, + priceTokenBPerTokenA: feeAdjustedPrice(UFix128(normalizedPrice), fee: 3000, reverse: true), + tokenABalanceSlot: wflowBalanceSlot, + tokenBBalanceSlot: pyusd0BalanceSlot, + signer: coaOwnerAccount + ) + if profileTick { log(" PROFILE [t=\(step)m] after setPoolToPrice WFLOW/PYUSD0") } + + // 3. MOET/FUSDEV pool + if normalizedPrice < 1.0 { + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: moetAddress, + tokenBAddress: morphoVaultAddress, + fee: 100, + priceTokenBPerTokenA: feeAdjustedPrice(UFix128(ytPrice), fee: 100, reverse: true), + tokenABalanceSlot: moetBalanceSlot, + tokenBBalanceSlot: fusdevBalanceSlot, + signer: coaOwnerAccount + ) + } else { + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: moetAddress, + tokenBAddress: morphoVaultAddress, + fee: 100, + priceTokenBPerTokenA: feeAdjustedPrice(UFix128(ytPrice), fee: 100, reverse: false), + tokenABalanceSlot: moetBalanceSlot, + tokenBBalanceSlot: fusdevBalanceSlot, + signer: coaOwnerAccount + ) + } + if profileTick { log(" PROFILE [t=\(step)m] after setPoolToPrice MOET/FUSDEV") } + + // 4. ERC4626 vault share price + setVaultSharePrice( + vaultAddress: morphoVaultAddress, + assetAddress: pyusd0Address, + assetBalanceSlot: pyusd0BalanceSlot, + totalSupplySlot: morphoVaultTotalSupplySlot, + vaultTotalAssetsSlot: morphoVaultTotalAssetsSlot, + priceMultiplier: ytPrice, + signer: users[0] + ) + if profileTick { log(" PROFILE [t=\(step)m] after setVaultSharePrice") } + + // 5. Rebalance all agents + var a = 0 + while a < numAgents { + rebalanceYieldVault(signer: flowYieldVaultsAccount, id: vaultIds[a], force: true, beFailed: false) + if profileTick { log(" PROFILE [t=\(step)m] after rebalanceYieldVault agent=\(a)") } + rebalancePosition(signer: flowALPAccount, pid: pids[a], force: true, beFailed: false) + if profileTick { log(" PROFILE [t=\(step)m] after rebalancePosition agent=\(a)") } + a = a + 1 + } + rebalanceCount = rebalanceCount + 1 + + // 6. Check health factor for all agents + a = 0 + while a < numAgents { + let flowCollateral = getFlowCollateralFromPosition(pid: pids[a]) + let flowCollateralValue = flowCollateral * normalizedPrice + let debt = getMOETDebtFromPosition(pid: pids[a]) + + if debt > 0.0 { + let hf = flowCollateralValue / debt + if hf < lowestHF { + lowestHF = hf + } + + // Log at critical moments (only agent 0 to avoid spam) + if a == 0 { + log(" [t=\(step)m] price=\(absolutePrice) yt=\(ytPrice) HF=\(hf) collateral=\(flowCollateralValue) debt=\(debt)") + } + + // Check for liquidation (HF < 1.0) + if hf < 1.0 { + liquidationCount = liquidationCount + 1 + log(" *** LIQUIDATION agent=\(a) at t=\(step)m! HF=\(hf) ***") + } + } + a = a + 1 + } + if profileTick { log(" PROFILE [t=\(step)m] after HF checks") } + + previousNormalizedPrice = normalizedPrice + } + + step = step + 1 + } + + // Final state (report agent 0 as representative) + let finalFlowCollateral = getFlowCollateralFromPosition(pid: pids[0]) + let finalDebt = getMOETDebtFromPosition(pid: pids[0]) + let finalYieldTokens = getAutoBalancerBalance(id: vaultIds[0])! + let finalNormalizedPrice = normalizePrice(prices[prices.length - 1]) + let finalHF = (finalFlowCollateral * finalNormalizedPrice) / finalDebt + + log("\n=== SIMULATION RESULTS ===") + log("Agents: \(numAgents)") + log("Rebalance events: \(rebalanceCount)") + log("Liquidation count: \(liquidationCount)") + log("Lowest FLOW price: \(lowestPrice)") + log("Lowest HF observed: \(lowestHF)") + log("Final FLOW price: \(finalNormalizedPrice)") + log("Final HF (agent 0): \(finalHF)") + log("Final collateral: \(finalFlowCollateral) FLOW") + log("Final debt: \(finalDebt) MOET") + log("Final yield tokens: \(finalYieldTokens)") + log("===========================\n") + + // PASS CRITERIA: Zero liquidations across all agents + Test.assertEqual(flash_crash_moderate_expectedLiquidationCount, liquidationCount) + Test.assert(finalHF > 1.0, message: "Expected final HF > 1.0 but got \(finalHF)") + Test.assert(lowestHF > 1.0, message: "Expected lowest HF > 1.0 but got \(lowestHF)") + + log("=== TEST PASSED: Zero liquidations under 20% flash crash (\(numAgents) agents) ===") +} From 567ad2927c4046c9a39cec848f32e8279484b60e Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Tue, 10 Mar 2026 14:02:13 -0400 Subject: [PATCH 02/10] Add initial attempt at daily rebalancing simulation with real 2025 BTC prices. --- cadence/tests/btc_daily_2025_helpers.cdc | 833 +++++ cadence/tests/forked_btc_daily_2025_test.cdc | 385 +++ .../scripts/simulations/btc_daily_2025.json | 771 +++++ .../simulations/flash_crash_moderate.json | 2921 +++++++++++++++++ .../scripts/simulations/generate_fixture.py | 430 +++ 5 files changed, 5340 insertions(+) create mode 100644 cadence/tests/btc_daily_2025_helpers.cdc create mode 100644 cadence/tests/forked_btc_daily_2025_test.cdc create mode 100644 cadence/tests/scripts/simulations/btc_daily_2025.json create mode 100644 cadence/tests/scripts/simulations/flash_crash_moderate.json create mode 100644 cadence/tests/scripts/simulations/generate_fixture.py diff --git a/cadence/tests/btc_daily_2025_helpers.cdc b/cadence/tests/btc_daily_2025_helpers.cdc new file mode 100644 index 00000000..680a56ce --- /dev/null +++ b/cadence/tests/btc_daily_2025_helpers.cdc @@ -0,0 +1,833 @@ +import Test + +// AUTO-GENERATED from btc_daily_2025.json — do not edit manually +// Run: python3 generate_fixture.py generate + +access(all) struct SimAgent { + access(all) let count: Int + access(all) let initialHF: UFix64 + access(all) let rebalancingHF: UFix64 + access(all) let targetHF: UFix64 + access(all) let debtPerAgent: UFix64 + access(all) let totalSystemDebt: UFix64 + + init( + count: Int, + initialHF: UFix64, + rebalancingHF: UFix64, + targetHF: UFix64, + debtPerAgent: UFix64, + totalSystemDebt: UFix64 + ) { + self.count = count + self.initialHF = initialHF + self.rebalancingHF = rebalancingHF + self.targetHF = targetHF + self.debtPerAgent = debtPerAgent + self.totalSystemDebt = totalSystemDebt + } +} + +access(all) struct SimPool { + access(all) let size: UFix64 + access(all) let concentration: UFix64 + access(all) let feeTier: UFix64 + + init(size: UFix64, concentration: UFix64, feeTier: UFix64) { + self.size = size + self.concentration = concentration + self.feeTier = feeTier + } +} + +access(all) struct SimConstants { + access(all) let btcCollateralFactor: UFix64 + access(all) let btcLiquidationThreshold: UFix64 + access(all) let yieldAPR: UFix64 + access(all) let directMintYT: Bool + + init( + btcCollateralFactor: UFix64, + btcLiquidationThreshold: UFix64, + yieldAPR: UFix64, + directMintYT: Bool + ) { + self.btcCollateralFactor = btcCollateralFactor + self.btcLiquidationThreshold = btcLiquidationThreshold + self.yieldAPR = yieldAPR + self.directMintYT = directMintYT + } +} + +access(all) let btc_daily_2025_prices: [UFix64] = [ + 94419.76000000, + 96886.88000000, + 98107.43000000, + 98236.23000000, + 98314.96000000, + 102078.09000000, + 96922.71000000, + 95043.52000000, + 92484.04000000, + 94701.46000000, + 94566.59000000, + 94488.44000000, + 94516.53000000, + 96534.04000000, + 100504.49000000, + 99756.91000000, + 104462.04000000, + 104408.07000000, + 101089.61000000, + 102016.66000000, + 106146.26000000, + 103653.07000000, + 103960.17000000, + 104819.48000000, + 104714.65000000, + 102682.50000000, + 102087.69000000, + 101332.48000000, + 103703.21000000, + 104735.30000000, + 102405.03000000, + 100655.91000000, + 97688.98000000, + 101405.42000000, + 97871.82000000, + 96615.44000000, + 96593.30000000, + 96529.08000000, + 96482.45000000, + 96500.09000000, + 97437.56000000, + 95747.43000000, + 97885.86000000, + 96623.87000000, + 97508.97000000, + 97580.35000000, + 96175.03000000, + 95773.38000000, + 95539.54000000, + 96635.61000000, + 98333.94000000, + 96125.54000000, + 96577.76000000, + 96273.92000000, + 91418.17000000, + 88736.17000000, + 84347.02000000, + 84704.22000000, + 84373.01000000, + 86031.91000000, + 94248.35000000, + 86065.67000000, + 87222.19000000, + 90623.56000000, + 89961.73000000, + 86742.68000000, + 86154.59000000, + 80601.04000000, + 78532.00000000, + 82862.21000000, + 83722.36000000, + 81066.70000000, + 83969.10000000, + 84343.11000000, + 82579.69000000, + 84075.69000000, + 82718.50000000, + 86854.23000000, + 84167.19000000, + 84043.25000000, + 83832.49000000, + 86054.37000000, + 87498.91000000, + 87471.70000000, + 86900.89000000, + 87177.10000000, + 84353.15000000, + 82597.58000000, + 82334.52000000, + 82548.91000000, + 85169.17000000, + 82485.71000000, + 83102.83000000, + 83843.80000000, + 83504.80000000, + 78214.48000000, + 79235.33000000, + 76271.95000000, + 82573.95000000, + 79626.14000000, + 83404.84000000, + 85287.11000000, + 83684.98000000, + 84542.39000000, + 83668.99000000, + 84033.87000000, + 84895.75000000, + 84450.81000000, + 85063.41000000, + 85174.30000000, + 87518.91000000, + 93441.89000000, + 93699.11000000, + 93943.79000000, + 94720.50000000, + 94646.93000000, + 93754.85000000, + 94978.75000000, + 94284.79000000, + 94207.31000000, + 96492.34000000, + 96910.07000000, + 95891.80000000, + 94315.97000000, + 94748.05000000, + 96802.48000000, + 97032.32000000, + 103241.46000000, + 102970.85000000, + 104696.33000000, + 104106.36000000, + 102812.95000000, + 104169.81000000, + 103539.42000000, + 103744.64000000, + 103489.29000000, + 103191.09000000, + 106446.01000000, + 105606.18000000, + 106791.09000000, + 109678.08000000, + 111673.28000000, + 107287.80000000, + 107791.16000000, + 109035.39000000, + 109440.37000000, + 108994.64000000, + 107802.32000000, + 105641.76000000, + 103998.57000000, + 104638.09000000, + 105652.10000000, + 105881.53000000, + 105432.47000000, + 104731.98000000, + 101575.95000000, + 104390.35000000, + 105615.63000000, + 105793.65000000, + 110294.10000000, + 110257.24000000, + 108686.63000000, + 105929.05000000, + 106090.97000000, + 105472.41000000, + 105552.03000000, + 106796.76000000, + 104601.12000000, + 104883.33000000, + 104684.29000000, + 103309.60000000, + 102257.41000000, + 100987.14000000, + 105577.77000000, + 106045.63000000, + 107361.26000000, + 106960.00000000, + 107088.43000000, + 107327.70000000, + 108385.57000000, + 107135.33000000, + 105698.28000000, + 108859.32000000, + 109647.98000000, + 108034.34000000, + 108231.18000000, + 109232.07000000, + 108299.85000000, + 108950.28000000, + 111326.55000000, + 115987.21000000, + 117516.99000000, + 117435.23000000, + 119116.12000000, + 119849.71000000, + 117777.19000000, + 118738.51000000, + 119289.84000000, + 118003.22000000, + 117939.98000000, + 117300.79000000, + 117439.54000000, + 119995.42000000, + 118754.96000000, + 118368.00000000, + 117635.88000000, + 117947.37000000, + 119448.49000000, + 117924.47000000, + 117922.15000000, + 117831.19000000, + 115758.20000000, + 113320.09000000, + 112526.91000000, + 114217.67000000, + 115071.88000000, + 114141.44000000, + 115028.00000000, + 117496.90000000, + 116688.73000000, + 116500.36000000, + 119306.76000000, + 118731.45000000, + 120172.91000000, + 123344.06000000, + 118359.58000000, + 117398.35000000, + 117491.35000000, + 117453.06000000, + 116252.31000000, + 112831.18000000, + 114274.74000000, + 112419.03000000, + 116874.09000000, + 115374.33000000, + 113458.43000000, + 110124.35000000, + 111802.66000000, + 111222.06000000, + 112544.80000000, + 108410.84000000, + 108808.07000000, + 108236.71000000, + 109250.59000000, + 111200.59000000, + 111723.21000000, + 110723.60000000, + 110650.99000000, + 110224.70000000, + 111167.62000000, + 112071.43000000, + 111530.55000000, + 113955.36000000, + 115507.54000000, + 116101.58000000, + 115950.51000000, + 115407.65000000, + 115444.87000000, + 116843.18000000, + 116468.51000000, + 117137.20000000, + 115688.86000000, + 115721.96000000, + 115306.10000000, + 112748.51000000, + 112014.50000000, + 113328.63000000, + 109049.29000000, + 109712.83000000, + 109681.94000000, + 112122.64000000, + 114400.39000000, + 114056.08000000, + 118648.93000000, + 120681.26000000, + 122266.53000000, + 122425.43000000, + 123513.47000000, + 124752.53000000, + 121451.38000000, + 123354.87000000, + 121705.58000000, + 113214.37000000, + 110807.88000000, + 115169.76000000, + 115271.08000000, + 113118.67000000, + 110783.17000000, + 108186.04000000, + 106467.79000000, + 107198.27000000, + 108666.71000000, + 110588.93000000, + 108476.89000000, + 107688.59000000, + 110069.72000000, + 111033.92000000, + 111641.73000000, + 114472.44000000, + 114119.32000000, + 112956.17000000, + 110055.30000000, + 108305.55000000, + 109556.16000000, + 110064.02000000, + 110639.63000000, + 106547.52000000, + 101590.52000000, + 103891.84000000, + 101301.29000000, + 103372.41000000, + 102282.11000000, + 104719.64000000, + 105996.60000000, + 102997.47000000, + 101663.19000000, + 99697.49000000, + 94397.79000000, + 95549.15000000, + 94177.07000000, + 92093.87000000, + 92948.87000000, + 91465.99000000, + 86631.90000000, + 85090.69000000, + 84648.36000000, + 86805.01000000, + 88270.56000000, + 87341.89000000, + 90518.37000000, + 91285.37000000, + 90919.27000000, + 90851.75000000, + 90394.31000000, + 86321.57000000, + 91350.21000000, + 93527.80000000, + 92141.62000000, + 89387.75000000, + 89272.37000000, + 90405.64000000, + 90640.21000000, + 92691.71000000, + 92020.95000000, + 92511.34000000, + 90270.41000000, + 90298.71000000, + 88175.18000000, + 86419.78000000, + 87843.99000000, + 86143.76000000, + 85462.51000000, + 88103.38000000, + 88344.00000000, + 88621.75000000, + 88490.02000000, + 87414.00000000, + 87611.96000000, + 87234.74000000, + 87301.43000000, + 87802.16000000, + 87835.83000000, + 87138.14000000, + 88430.14000000, + 87508.83000000 +] + +access(all) let btc_daily_2025_dates: [String] = [ + "2025-01-01", + "2025-01-02", + "2025-01-03", + "2025-01-04", + "2025-01-05", + "2025-01-06", + "2025-01-07", + "2025-01-08", + "2025-01-09", + "2025-01-10", + "2025-01-11", + "2025-01-12", + "2025-01-13", + "2025-01-14", + "2025-01-15", + "2025-01-16", + "2025-01-17", + "2025-01-18", + "2025-01-19", + "2025-01-20", + "2025-01-21", + "2025-01-22", + "2025-01-23", + "2025-01-24", + "2025-01-25", + "2025-01-26", + "2025-01-27", + "2025-01-28", + "2025-01-29", + "2025-01-30", + "2025-01-31", + "2025-02-01", + "2025-02-02", + "2025-02-03", + "2025-02-04", + "2025-02-05", + "2025-02-06", + "2025-02-07", + "2025-02-08", + "2025-02-09", + "2025-02-10", + "2025-02-11", + "2025-02-12", + "2025-02-13", + "2025-02-14", + "2025-02-15", + "2025-02-16", + "2025-02-17", + "2025-02-18", + "2025-02-19", + "2025-02-20", + "2025-02-21", + "2025-02-22", + "2025-02-23", + "2025-02-24", + "2025-02-25", + "2025-02-26", + "2025-02-27", + "2025-02-28", + "2025-03-01", + "2025-03-02", + "2025-03-03", + "2025-03-04", + "2025-03-05", + "2025-03-06", + "2025-03-07", + "2025-03-08", + "2025-03-09", + "2025-03-10", + "2025-03-11", + "2025-03-12", + "2025-03-13", + "2025-03-14", + "2025-03-15", + "2025-03-16", + "2025-03-17", + "2025-03-18", + "2025-03-19", + "2025-03-20", + "2025-03-21", + "2025-03-22", + "2025-03-23", + "2025-03-24", + "2025-03-25", + "2025-03-26", + "2025-03-27", + "2025-03-28", + "2025-03-29", + "2025-03-30", + "2025-03-31", + "2025-04-01", + "2025-04-02", + "2025-04-03", + "2025-04-04", + "2025-04-05", + "2025-04-06", + "2025-04-07", + "2025-04-08", + "2025-04-09", + "2025-04-10", + "2025-04-11", + "2025-04-12", + "2025-04-13", + "2025-04-14", + "2025-04-15", + "2025-04-16", + "2025-04-17", + "2025-04-18", + "2025-04-19", + "2025-04-20", + "2025-04-21", + "2025-04-22", + "2025-04-23", + "2025-04-24", + "2025-04-25", + "2025-04-26", + "2025-04-27", + "2025-04-28", + "2025-04-29", + "2025-04-30", + "2025-05-01", + "2025-05-02", + "2025-05-03", + "2025-05-04", + "2025-05-05", + "2025-05-06", + "2025-05-07", + "2025-05-08", + "2025-05-09", + "2025-05-10", + "2025-05-11", + "2025-05-12", + "2025-05-13", + "2025-05-14", + "2025-05-15", + "2025-05-16", + "2025-05-17", + "2025-05-18", + "2025-05-19", + "2025-05-20", + "2025-05-21", + "2025-05-22", + "2025-05-23", + "2025-05-24", + "2025-05-25", + "2025-05-26", + "2025-05-27", + "2025-05-28", + "2025-05-29", + "2025-05-30", + "2025-05-31", + "2025-06-01", + "2025-06-02", + "2025-06-03", + "2025-06-04", + "2025-06-05", + "2025-06-06", + "2025-06-07", + "2025-06-08", + "2025-06-09", + "2025-06-10", + "2025-06-11", + "2025-06-12", + "2025-06-13", + "2025-06-14", + "2025-06-15", + "2025-06-16", + "2025-06-17", + "2025-06-18", + "2025-06-19", + "2025-06-20", + "2025-06-21", + "2025-06-22", + "2025-06-23", + "2025-06-24", + "2025-06-25", + "2025-06-26", + "2025-06-27", + "2025-06-28", + "2025-06-29", + "2025-06-30", + "2025-07-01", + "2025-07-02", + "2025-07-03", + "2025-07-04", + "2025-07-05", + "2025-07-06", + "2025-07-07", + "2025-07-08", + "2025-07-09", + "2025-07-10", + "2025-07-11", + "2025-07-12", + "2025-07-13", + "2025-07-14", + "2025-07-15", + "2025-07-16", + "2025-07-17", + "2025-07-18", + "2025-07-19", + "2025-07-20", + "2025-07-21", + "2025-07-22", + "2025-07-23", + "2025-07-24", + "2025-07-25", + "2025-07-26", + "2025-07-27", + "2025-07-28", + "2025-07-29", + "2025-07-30", + "2025-07-31", + "2025-08-01", + "2025-08-02", + "2025-08-03", + "2025-08-04", + "2025-08-05", + "2025-08-06", + "2025-08-07", + "2025-08-08", + "2025-08-09", + "2025-08-10", + "2025-08-11", + "2025-08-12", + "2025-08-13", + "2025-08-14", + "2025-08-15", + "2025-08-16", + "2025-08-17", + "2025-08-18", + "2025-08-19", + "2025-08-20", + "2025-08-21", + "2025-08-22", + "2025-08-23", + "2025-08-24", + "2025-08-25", + "2025-08-26", + "2025-08-27", + "2025-08-28", + "2025-08-29", + "2025-08-30", + "2025-08-31", + "2025-09-01", + "2025-09-02", + "2025-09-03", + "2025-09-04", + "2025-09-05", + "2025-09-06", + "2025-09-07", + "2025-09-08", + "2025-09-09", + "2025-09-10", + "2025-09-11", + "2025-09-12", + "2025-09-13", + "2025-09-14", + "2025-09-15", + "2025-09-16", + "2025-09-17", + "2025-09-18", + "2025-09-19", + "2025-09-20", + "2025-09-21", + "2025-09-22", + "2025-09-23", + "2025-09-24", + "2025-09-25", + "2025-09-26", + "2025-09-27", + "2025-09-28", + "2025-09-29", + "2025-09-30", + "2025-10-01", + "2025-10-02", + "2025-10-03", + "2025-10-04", + "2025-10-05", + "2025-10-06", + "2025-10-07", + "2025-10-08", + "2025-10-09", + "2025-10-10", + "2025-10-11", + "2025-10-12", + "2025-10-13", + "2025-10-14", + "2025-10-15", + "2025-10-16", + "2025-10-17", + "2025-10-18", + "2025-10-19", + "2025-10-20", + "2025-10-21", + "2025-10-22", + "2025-10-23", + "2025-10-24", + "2025-10-25", + "2025-10-26", + "2025-10-27", + "2025-10-28", + "2025-10-29", + "2025-10-30", + "2025-10-31", + "2025-11-01", + "2025-11-02", + "2025-11-03", + "2025-11-04", + "2025-11-05", + "2025-11-06", + "2025-11-07", + "2025-11-08", + "2025-11-09", + "2025-11-10", + "2025-11-11", + "2025-11-12", + "2025-11-13", + "2025-11-14", + "2025-11-15", + "2025-11-16", + "2025-11-17", + "2025-11-18", + "2025-11-19", + "2025-11-20", + "2025-11-21", + "2025-11-22", + "2025-11-23", + "2025-11-24", + "2025-11-25", + "2025-11-26", + "2025-11-27", + "2025-11-28", + "2025-11-29", + "2025-11-30", + "2025-12-01", + "2025-12-02", + "2025-12-03", + "2025-12-04", + "2025-12-05", + "2025-12-06", + "2025-12-07", + "2025-12-08", + "2025-12-09", + "2025-12-10", + "2025-12-11", + "2025-12-12", + "2025-12-13", + "2025-12-14", + "2025-12-15", + "2025-12-16", + "2025-12-17", + "2025-12-18", + "2025-12-19", + "2025-12-20", + "2025-12-21", + "2025-12-22", + "2025-12-23", + "2025-12-24", + "2025-12-25", + "2025-12-26", + "2025-12-27", + "2025-12-28", + "2025-12-29", + "2025-12-30", + "2025-12-31" +] + +access(all) let btc_daily_2025_agents: [SimAgent] = [ + SimAgent( + count: 1, + initialHF: 1.15000000, + rebalancingHF: 1.05000000, + targetHF: 1.08000000, + debtPerAgent: 133333.00000000, + totalSystemDebt: 20000000.00000000 + ) +] + +access(all) let btc_daily_2025_pools: {String: SimPool} = { + "moet_yt": SimPool( + size: 500000.00000000, + concentration: 0.95000000, + feeTier: 0.00050000 + ), + "moet_btc": SimPool( + size: 5000000.00000000, + concentration: 0.80000000, + feeTier: 0.00300000 + ) +} + +access(all) let btc_daily_2025_constants: SimConstants = SimConstants( + btcCollateralFactor: 0.80000000, + btcLiquidationThreshold: 0.85000000, + yieldAPR: 0.10000000, + directMintYT: true +) + +access(all) let btc_daily_2025_expectedLiquidationCount: Int = 0 +access(all) let btc_daily_2025_expectedAllAgentsSurvive: Bool = true + +access(all) let btc_daily_2025_durationDays: Int = 365 +access(all) let btc_daily_2025_notes: String = "Daily BTC/USD close prices from CoinMarketCap, 2025-01-01 to 2025-12-31. 365 data points." diff --git a/cadence/tests/forked_btc_daily_2025_test.cdc b/cadence/tests/forked_btc_daily_2025_test.cdc new file mode 100644 index 00000000..930a00b8 --- /dev/null +++ b/cadence/tests/forked_btc_daily_2025_test.cdc @@ -0,0 +1,385 @@ +#test_fork(network: "mainnet-fork", height: 143292255) + +import Test +import BlockchainHelpers + +import "test_helpers.cdc" +import "evm_state_helpers.cdc" +import "btc_daily_2025_helpers.cdc" + +import "FlowYieldVaults" +import "FlowToken" +import "MOET" +import "FlowYieldVaultsStrategiesV2" +import "FlowALPv0" + + +// ============================================================================ +// CADENCE ACCOUNTS +// ============================================================================ + +access(all) let flowYieldVaultsAccount = Test.getAccount(0xb1d63873c3cc9f79) +access(all) let flowALPAccount = Test.getAccount(0x6b00ff876c299c61) +access(all) let bandOracleAccount = Test.getAccount(0x6801a6222ebf784a) +access(all) let whaleFlowAccount = Test.getAccount(0x92674150c9213fc9) +access(all) let coaOwnerAccount = Test.getAccount(0xe467b9dd11fa00df) + +access(all) var strategyIdentifier = Type<@FlowYieldVaultsStrategiesV2.FUSDEVStrategy>().identifier +access(all) var flowTokenIdentifier = Type<@FlowToken.Vault>().identifier +access(all) var moetTokenIdentifier = Type<@MOET.Vault>().identifier + +// ============================================================================ +// PROTOCOL ADDRESSES +// ============================================================================ + +access(all) let factoryAddress = "0xca6d7Bb03334bBf135902e1d919a5feccb461632" + +// ============================================================================ +// VAULT & TOKEN ADDRESSES +// ============================================================================ + +access(all) let morphoVaultAddress = "0xd069d989e2F44B70c65347d1853C0c67e10a9F8D" +access(all) let pyusd0Address = "0x99aF3EeA856556646C98c8B9b2548Fe815240750" +access(all) let moetAddress = "0x213979bB8A9A86966999b3AA797C1fcf3B967ae2" +access(all) let wflowAddress = "0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e" + +// ============================================================================ +// STORAGE SLOT CONSTANTS +// ============================================================================ + +access(all) let moetBalanceSlot = 0 as UInt256 +access(all) let pyusd0BalanceSlot = 1 as UInt256 +access(all) let fusdevBalanceSlot = 12 as UInt256 +access(all) let wflowBalanceSlot = 3 as UInt256 + +access(all) let morphoVaultTotalSupplySlot = 11 as UInt256 +access(all) let morphoVaultTotalAssetsSlot = 15 as UInt256 + +// ============================================================================ +// SIMULATION CONFIG +// ============================================================================ + +access(all) let numAgents = 1 + +access(all) let fundingPerAgent = 1000.0 + +access(all) let initialPrice = btc_daily_2025_prices[0] + +access(all) let yieldAPR = btc_daily_2025_constants.yieldAPR +access(all) let daysPerYear = 365.0 +access(all) let secondsPerDay = 86400.0 + +// ============================================================================ +// SETUP +// ============================================================================ + +access(all) +fun setup() { + deployContractsForFork() + + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: pyusd0Address, + tokenBAddress: morphoVaultAddress, + fee: 100, + priceTokenBPerTokenA: feeAdjustedPrice(1.0, fee: 100, reverse: false), + tokenABalanceSlot: pyusd0BalanceSlot, + tokenBBalanceSlot: fusdevBalanceSlot, + signer: coaOwnerAccount + ) + + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: pyusd0Address, + tokenBAddress: wflowAddress, + fee: 3000, + priceTokenBPerTokenA: feeAdjustedPrice(1.0, fee: 3000, reverse: false), + tokenABalanceSlot: pyusd0BalanceSlot, + tokenBBalanceSlot: wflowBalanceSlot, + signer: coaOwnerAccount + ) + + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: moetAddress, + tokenBAddress: morphoVaultAddress, + fee: 100, + priceTokenBPerTokenA: feeAdjustedPrice(1.0, fee: 100, reverse: false), + tokenABalanceSlot: moetBalanceSlot, + tokenBBalanceSlot: fusdevBalanceSlot, + signer: coaOwnerAccount + ) + + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: moetAddress, + tokenBAddress: pyusd0Address, + fee: 100, + priceTokenBPerTokenA: feeAdjustedPrice(1.0, fee: 100, reverse: false), + tokenABalanceSlot: moetBalanceSlot, + tokenBBalanceSlot: pyusd0BalanceSlot, + signer: coaOwnerAccount + ) + + setBandOraclePrices(signer: bandOracleAccount, symbolPrices: { + "FLOW": 1.0, + "USD": 1.0 + }) + + let reserveAmount = 100_000_00.0 + transferFlow(signer: whaleFlowAccount, recipient: flowALPAccount.address, amount: reserveAmount) + mintMoet(signer: flowALPAccount, to: flowALPAccount.address, amount: reserveAmount, beFailed: false) + + transferFlow(signer: whaleFlowAccount, recipient: flowYieldVaultsAccount.address, amount: reserveAmount) + transferFlow(signer: whaleFlowAccount, recipient: coaOwnerAccount.address, amount: reserveAmount) +} + +// ============================================================================ +// HELPERS +// ============================================================================ + +access(all) fun getFlowCollateralFromPosition(pid: UInt64): UFix64 { + let positionDetails = getPositionDetails(pid: pid, beFailed: false) + for balance in positionDetails.balances { + if balance.vaultType == Type<@FlowToken.Vault>() { + if balance.direction == FlowALPv0.BalanceDirection.Credit { + return balance.balance + } + } + } + return 0.0 +} + +access(all) fun getMOETDebtFromPosition(pid: UInt64): UFix64 { + let positionDetails = getPositionDetails(pid: pid, beFailed: false) + for balance in positionDetails.balances { + if balance.vaultType == Type<@MOET.Vault>() { + if balance.direction == FlowALPv0.BalanceDirection.Debit { + return balance.balance + } + } + } + return 0.0 +} + +access(all) fun normalizePrice(_ absolutePrice: UFix64): UFix64 { + return absolutePrice / initialPrice +} + +/// Compute deterministic YT (ERC4626 vault share) price at a given day. +/// price = 1.0 + yieldAPR * (day / 365) +access(all) fun ytPriceAtDay(_ day: Int): UFix64 { + return 1.0 + yieldAPR * (UFix64(day) / daysPerYear) +} + +/// Update all prices for a given simulation day. +access(all) fun applyPriceTick(flowPrice: UFix64, ytPrice: UFix64, user: Test.TestAccount) { + setBandOraclePrices(signer: bandOracleAccount, symbolPrices: { + "FLOW": flowPrice, + "USD": 1.0 + }) + + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: wflowAddress, + tokenBAddress: pyusd0Address, + fee: 3000, + priceTokenBPerTokenA: feeAdjustedPrice(UFix128(flowPrice), fee: 3000, reverse: true), + tokenABalanceSlot: wflowBalanceSlot, + tokenBBalanceSlot: pyusd0BalanceSlot, + signer: coaOwnerAccount + ) + + if flowPrice < 1.0 { + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: moetAddress, + tokenBAddress: morphoVaultAddress, + fee: 100, + priceTokenBPerTokenA: feeAdjustedPrice(UFix128(ytPrice), fee: 100, reverse: true), + tokenABalanceSlot: moetBalanceSlot, + tokenBBalanceSlot: fusdevBalanceSlot, + signer: coaOwnerAccount + ) + } else { + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: moetAddress, + tokenBAddress: morphoVaultAddress, + fee: 100, + priceTokenBPerTokenA: feeAdjustedPrice(UFix128(ytPrice), fee: 100, reverse: false), + tokenABalanceSlot: moetBalanceSlot, + tokenBBalanceSlot: fusdevBalanceSlot, + signer: coaOwnerAccount + ) + } + + setVaultSharePrice( + vaultAddress: morphoVaultAddress, + assetAddress: pyusd0Address, + assetBalanceSlot: pyusd0BalanceSlot, + totalSupplySlot: morphoVaultTotalSupplySlot, + vaultTotalAssetsSlot: morphoVaultTotalAssetsSlot, + priceMultiplier: ytPrice, + signer: user + ) +} + +// ============================================================================ +// TEST: BTC Daily 2025 -- Daily Rebalancing with Real Prices +// ============================================================================ + +access(all) +fun test_BtcDaily2025_DailyRebalancing() { + let prices = btc_daily_2025_prices + let dates = btc_daily_2025_dates + + // Create agents + let users: [Test.TestAccount] = [] + let pids: [UInt64] = [] + let vaultIds: [UInt64] = [] + + var i = 0 + while i < numAgents { + let user = Test.createAccount() + transferFlow(signer: whaleFlowAccount, recipient: user.address, amount: fundingPerAgent) + grantBeta(flowYieldVaultsAccount, user) + + setVaultSharePrice( + vaultAddress: morphoVaultAddress, + assetAddress: pyusd0Address, + assetBalanceSlot: pyusd0BalanceSlot, + totalSupplySlot: morphoVaultTotalSupplySlot, + vaultTotalAssetsSlot: morphoVaultTotalAssetsSlot, + priceMultiplier: 1.0, + signer: user + ) + + createYieldVault( + signer: user, + strategyIdentifier: strategyIdentifier, + vaultIdentifier: flowTokenIdentifier, + amount: fundingPerAgent, + beFailed: false + ) + + let pid = (getLastPositionOpenedEvent(Test.eventsOfType(Type())) as! FlowALPv0.Opened).pid + let yieldVaultIDs = getYieldVaultIDs(address: user.address)! + let vaultId = yieldVaultIDs[0] + + users.append(user) + pids.append(pid) + vaultIds.append(vaultId) + + log(" Agent \(i): pid=\(pid) vaultId=\(vaultId)") + i = i + 1 + } + + log("\n=== BTC DAILY 2025 SIMULATION ===") + log("Agents: \(numAgents)") + log("Funding per agent: \(fundingPerAgent) FLOW") + log("Duration: \(btc_daily_2025_durationDays) days") + log("Price points: \(prices.length)") + log("Initial BTC price: $\(prices[0])") + log("Notes: \(btc_daily_2025_notes)") + + var liquidationCount = 0 + var rebalanceCount = 0 + var previousNormalizedPrice = 1.0 + var lowestPrice = initialPrice + var highestPrice = initialPrice + var lowestHF = 100.0 + + let startTimestamp = getCurrentBlockTimestamp() + + var day = 0 + while day < prices.length { + let absolutePrice = prices[day] + let normalizedPrice = normalizePrice(absolutePrice) + let ytPrice = ytPriceAtDay(day) + + if absolutePrice < lowestPrice { + lowestPrice = absolutePrice + } + if absolutePrice > highestPrice { + highestPrice = absolutePrice + } + + // Advance blockchain time by 1 day per step + let expectedTimestamp = startTimestamp + UFix64(day) * secondsPerDay + let currentTimestamp = getCurrentBlockTimestamp() + if expectedTimestamp > currentTimestamp { + Test.moveTime(by: Fix64(expectedTimestamp - currentTimestamp)) + } + + // Apply all price updates + applyPriceTick(flowPrice: normalizedPrice, ytPrice: ytPrice, user: users[0]) + + // Rebalance all agents + var a = 0 + while a < numAgents { + rebalanceYieldVault(signer: flowYieldVaultsAccount, id: vaultIds[a], force: true, beFailed: false) + rebalancePosition(signer: flowALPAccount, pid: pids[a], force: true, beFailed: false) + a = a + 1 + } + rebalanceCount = rebalanceCount + 1 + + // Check health factors + a = 0 + while a < numAgents { + let flowCollateral = getFlowCollateralFromPosition(pid: pids[a]) + let flowCollateralValue = flowCollateral * normalizedPrice + let debt = getMOETDebtFromPosition(pid: pids[a]) + + if debt > 0.0 { + let hf = flowCollateralValue / debt + if hf < lowestHF { + lowestHF = hf + } + + // Log weekly + at price extremes + if a == 0 && (day % 7 == 0 || absolutePrice == lowestPrice || absolutePrice == highestPrice) { + log(" [day \(day)] \(dates[day]) price=$\(absolutePrice) ratio=\(normalizedPrice) yt=\(ytPrice) HF=\(hf) collateral=\(flowCollateralValue) debt=\(debt)") + } + + if hf < 1.0 { + liquidationCount = liquidationCount + 1 + log(" *** LIQUIDATION agent=\(a) on day \(day) (\(dates[day]))! HF=\(hf) ***") + } + } + a = a + 1 + } + + previousNormalizedPrice = normalizedPrice + day = day + 1 + } + + // Final state + let finalFlowCollateral = getFlowCollateralFromPosition(pid: pids[0]) + let finalDebt = getMOETDebtFromPosition(pid: pids[0]) + let finalYieldTokens = getAutoBalancerBalance(id: vaultIds[0])! + let finalNormalizedPrice = normalizePrice(prices[prices.length - 1]) + let finalHF = (finalFlowCollateral * finalNormalizedPrice) / finalDebt + + log("\n=== SIMULATION RESULTS ===") + log("Agents: \(numAgents)") + log("Days simulated: \(prices.length)") + log("Rebalance events: \(rebalanceCount)") + log("Liquidation count: \(liquidationCount)") + log("Initial BTC price: $\(initialPrice)") + log("Lowest BTC price: $\(lowestPrice)") + log("Highest BTC price: $\(highestPrice)") + log("Final BTC price: $\(prices[prices.length - 1])") + log("Lowest HF observed: \(lowestHF)") + log("Final HF (agent 0): \(finalHF)") + log("Final collateral: \(finalFlowCollateral) FLOW") + log("Final debt: \(finalDebt) MOET") + log("Final yield tokens: \(finalYieldTokens)") + log("===========================\n") + + Test.assertEqual(btc_daily_2025_expectedLiquidationCount, liquidationCount) + Test.assert(finalHF > 1.0, message: "Expected final HF > 1.0 but got \(finalHF)") + Test.assert(lowestHF > 1.0, message: "Expected lowest HF > 1.0 but got \(lowestHF)") + + log("=== TEST PASSED: Zero liquidations over 1 year of real BTC prices (\(numAgents) agents) ===") +} diff --git a/cadence/tests/scripts/simulations/btc_daily_2025.json b/cadence/tests/scripts/simulations/btc_daily_2025.json new file mode 100644 index 00000000..05e127cb --- /dev/null +++ b/cadence/tests/scripts/simulations/btc_daily_2025.json @@ -0,0 +1,771 @@ +{ + "scenario": "btc_daily_2025", + "duration_days": 365, + "btc_prices": [ + 94419.76, + 96886.88, + 98107.43, + 98236.23, + 98314.96, + 102078.09, + 96922.71, + 95043.52, + 92484.04, + 94701.46, + 94566.59, + 94488.44, + 94516.53, + 96534.04, + 100504.49, + 99756.91, + 104462.04, + 104408.07, + 101089.61, + 102016.66, + 106146.26, + 103653.07, + 103960.17, + 104819.48, + 104714.65, + 102682.5, + 102087.69, + 101332.48, + 103703.21, + 104735.3, + 102405.03, + 100655.91, + 97688.98, + 101405.42, + 97871.82, + 96615.44, + 96593.3, + 96529.08, + 96482.45, + 96500.09, + 97437.56, + 95747.43, + 97885.86, + 96623.87, + 97508.97, + 97580.35, + 96175.03, + 95773.38, + 95539.54, + 96635.61, + 98333.94, + 96125.54, + 96577.76, + 96273.92, + 91418.17, + 88736.17, + 84347.02, + 84704.22, + 84373.01, + 86031.91, + 94248.35, + 86065.67, + 87222.19, + 90623.56, + 89961.73, + 86742.68, + 86154.59, + 80601.04, + 78532.0, + 82862.21, + 83722.36, + 81066.7, + 83969.1, + 84343.11, + 82579.69, + 84075.69, + 82718.5, + 86854.23, + 84167.19, + 84043.25, + 83832.49, + 86054.37, + 87498.91, + 87471.7, + 86900.89, + 87177.1, + 84353.15, + 82597.58, + 82334.52, + 82548.91, + 85169.17, + 82485.71, + 83102.83, + 83843.8, + 83504.8, + 78214.48, + 79235.33, + 76271.95, + 82573.95, + 79626.14, + 83404.84, + 85287.11, + 83684.98, + 84542.39, + 83668.99, + 84033.87, + 84895.75, + 84450.81, + 85063.41, + 85174.3, + 87518.91, + 93441.89, + 93699.11, + 93943.79, + 94720.5, + 94646.93, + 93754.85, + 94978.75, + 94284.79, + 94207.31, + 96492.34, + 96910.07, + 95891.8, + 94315.97, + 94748.05, + 96802.48, + 97032.32, + 103241.46, + 102970.85, + 104696.33, + 104106.36, + 102812.95, + 104169.81, + 103539.42, + 103744.64, + 103489.29, + 103191.09, + 106446.01, + 105606.18, + 106791.09, + 109678.08, + 111673.28, + 107287.8, + 107791.16, + 109035.39, + 109440.37, + 108994.64, + 107802.32, + 105641.76, + 103998.57, + 104638.09, + 105652.1, + 105881.53, + 105432.47, + 104731.98, + 101575.95, + 104390.35, + 105615.63, + 105793.65, + 110294.1, + 110257.24, + 108686.63, + 105929.05, + 106090.97, + 105472.41, + 105552.03, + 106796.76, + 104601.12, + 104883.33, + 104684.29, + 103309.6, + 102257.41, + 100987.14, + 105577.77, + 106045.63, + 107361.26, + 106960.0, + 107088.43, + 107327.7, + 108385.57, + 107135.33, + 105698.28, + 108859.32, + 109647.98, + 108034.34, + 108231.18, + 109232.07, + 108299.85, + 108950.28, + 111326.55, + 115987.21, + 117516.99, + 117435.23, + 119116.12, + 119849.71, + 117777.19, + 118738.51, + 119289.84, + 118003.22, + 117939.98, + 117300.79, + 117439.54, + 119995.42, + 118754.96, + 118368.0, + 117635.88, + 117947.37, + 119448.49, + 117924.47, + 117922.15, + 117831.19, + 115758.2, + 113320.09, + 112526.91, + 114217.67, + 115071.88, + 114141.44, + 115028.0, + 117496.9, + 116688.73, + 116500.36, + 119306.76, + 118731.45, + 120172.91, + 123344.06, + 118359.58, + 117398.35, + 117491.35, + 117453.06, + 116252.31, + 112831.18, + 114274.74, + 112419.03, + 116874.09, + 115374.33, + 113458.43, + 110124.35, + 111802.66, + 111222.06, + 112544.8, + 108410.84, + 108808.07, + 108236.71, + 109250.59, + 111200.59, + 111723.21, + 110723.6, + 110650.99, + 110224.7, + 111167.62, + 112071.43, + 111530.55, + 113955.36, + 115507.54, + 116101.58, + 115950.51, + 115407.65, + 115444.87, + 116843.18, + 116468.51, + 117137.2, + 115688.86, + 115721.96, + 115306.1, + 112748.51, + 112014.5, + 113328.63, + 109049.29, + 109712.83, + 109681.94, + 112122.64, + 114400.39, + 114056.08, + 118648.93, + 120681.26, + 122266.53, + 122425.43, + 123513.47, + 124752.53, + 121451.38, + 123354.87, + 121705.58, + 113214.37, + 110807.88, + 115169.76, + 115271.08, + 113118.67, + 110783.17, + 108186.04, + 106467.79, + 107198.27, + 108666.71, + 110588.93, + 108476.89, + 107688.59, + 110069.72, + 111033.92, + 111641.73, + 114472.44, + 114119.32, + 112956.17, + 110055.3, + 108305.55, + 109556.16, + 110064.02, + 110639.63, + 106547.52, + 101590.52, + 103891.84, + 101301.29, + 103372.41, + 102282.11, + 104719.64, + 105996.6, + 102997.47, + 101663.19, + 99697.49, + 94397.79, + 95549.15, + 94177.07, + 92093.87, + 92948.87, + 91465.99, + 86631.9, + 85090.69, + 84648.36, + 86805.01, + 88270.56, + 87341.89, + 90518.37, + 91285.37, + 90919.27, + 90851.75, + 90394.31, + 86321.57, + 91350.21, + 93527.8, + 92141.62, + 89387.75, + 89272.37, + 90405.64, + 90640.21, + 92691.71, + 92020.95, + 92511.34, + 90270.41, + 90298.71, + 88175.18, + 86419.78, + 87843.99, + 86143.76, + 85462.51, + 88103.38, + 88344.0, + 88621.75, + 88490.02, + 87414.0, + 87611.96, + 87234.74, + 87301.43, + 87802.16, + 87835.83, + 87138.14, + 88430.14, + 87508.83 + ], + "dates": [ + "2025-01-01", + "2025-01-02", + "2025-01-03", + "2025-01-04", + "2025-01-05", + "2025-01-06", + "2025-01-07", + "2025-01-08", + "2025-01-09", + "2025-01-10", + "2025-01-11", + "2025-01-12", + "2025-01-13", + "2025-01-14", + "2025-01-15", + "2025-01-16", + "2025-01-17", + "2025-01-18", + "2025-01-19", + "2025-01-20", + "2025-01-21", + "2025-01-22", + "2025-01-23", + "2025-01-24", + "2025-01-25", + "2025-01-26", + "2025-01-27", + "2025-01-28", + "2025-01-29", + "2025-01-30", + "2025-01-31", + "2025-02-01", + "2025-02-02", + "2025-02-03", + "2025-02-04", + "2025-02-05", + "2025-02-06", + "2025-02-07", + "2025-02-08", + "2025-02-09", + "2025-02-10", + "2025-02-11", + "2025-02-12", + "2025-02-13", + "2025-02-14", + "2025-02-15", + "2025-02-16", + "2025-02-17", + "2025-02-18", + "2025-02-19", + "2025-02-20", + "2025-02-21", + "2025-02-22", + "2025-02-23", + "2025-02-24", + "2025-02-25", + "2025-02-26", + "2025-02-27", + "2025-02-28", + "2025-03-01", + "2025-03-02", + "2025-03-03", + "2025-03-04", + "2025-03-05", + "2025-03-06", + "2025-03-07", + "2025-03-08", + "2025-03-09", + "2025-03-10", + "2025-03-11", + "2025-03-12", + "2025-03-13", + "2025-03-14", + "2025-03-15", + "2025-03-16", + "2025-03-17", + "2025-03-18", + "2025-03-19", + "2025-03-20", + "2025-03-21", + "2025-03-22", + "2025-03-23", + "2025-03-24", + "2025-03-25", + "2025-03-26", + "2025-03-27", + "2025-03-28", + "2025-03-29", + "2025-03-30", + "2025-03-31", + "2025-04-01", + "2025-04-02", + "2025-04-03", + "2025-04-04", + "2025-04-05", + "2025-04-06", + "2025-04-07", + "2025-04-08", + "2025-04-09", + "2025-04-10", + "2025-04-11", + "2025-04-12", + "2025-04-13", + "2025-04-14", + "2025-04-15", + "2025-04-16", + "2025-04-17", + "2025-04-18", + "2025-04-19", + "2025-04-20", + "2025-04-21", + "2025-04-22", + "2025-04-23", + "2025-04-24", + "2025-04-25", + "2025-04-26", + "2025-04-27", + "2025-04-28", + "2025-04-29", + "2025-04-30", + "2025-05-01", + "2025-05-02", + "2025-05-03", + "2025-05-04", + "2025-05-05", + "2025-05-06", + "2025-05-07", + "2025-05-08", + "2025-05-09", + "2025-05-10", + "2025-05-11", + "2025-05-12", + "2025-05-13", + "2025-05-14", + "2025-05-15", + "2025-05-16", + "2025-05-17", + "2025-05-18", + "2025-05-19", + "2025-05-20", + "2025-05-21", + "2025-05-22", + "2025-05-23", + "2025-05-24", + "2025-05-25", + "2025-05-26", + "2025-05-27", + "2025-05-28", + "2025-05-29", + "2025-05-30", + "2025-05-31", + "2025-06-01", + "2025-06-02", + "2025-06-03", + "2025-06-04", + "2025-06-05", + "2025-06-06", + "2025-06-07", + "2025-06-08", + "2025-06-09", + "2025-06-10", + "2025-06-11", + "2025-06-12", + "2025-06-13", + "2025-06-14", + "2025-06-15", + "2025-06-16", + "2025-06-17", + "2025-06-18", + "2025-06-19", + "2025-06-20", + "2025-06-21", + "2025-06-22", + "2025-06-23", + "2025-06-24", + "2025-06-25", + "2025-06-26", + "2025-06-27", + "2025-06-28", + "2025-06-29", + "2025-06-30", + "2025-07-01", + "2025-07-02", + "2025-07-03", + "2025-07-04", + "2025-07-05", + "2025-07-06", + "2025-07-07", + "2025-07-08", + "2025-07-09", + "2025-07-10", + "2025-07-11", + "2025-07-12", + "2025-07-13", + "2025-07-14", + "2025-07-15", + "2025-07-16", + "2025-07-17", + "2025-07-18", + "2025-07-19", + "2025-07-20", + "2025-07-21", + "2025-07-22", + "2025-07-23", + "2025-07-24", + "2025-07-25", + "2025-07-26", + "2025-07-27", + "2025-07-28", + "2025-07-29", + "2025-07-30", + "2025-07-31", + "2025-08-01", + "2025-08-02", + "2025-08-03", + "2025-08-04", + "2025-08-05", + "2025-08-06", + "2025-08-07", + "2025-08-08", + "2025-08-09", + "2025-08-10", + "2025-08-11", + "2025-08-12", + "2025-08-13", + "2025-08-14", + "2025-08-15", + "2025-08-16", + "2025-08-17", + "2025-08-18", + "2025-08-19", + "2025-08-20", + "2025-08-21", + "2025-08-22", + "2025-08-23", + "2025-08-24", + "2025-08-25", + "2025-08-26", + "2025-08-27", + "2025-08-28", + "2025-08-29", + "2025-08-30", + "2025-08-31", + "2025-09-01", + "2025-09-02", + "2025-09-03", + "2025-09-04", + "2025-09-05", + "2025-09-06", + "2025-09-07", + "2025-09-08", + "2025-09-09", + "2025-09-10", + "2025-09-11", + "2025-09-12", + "2025-09-13", + "2025-09-14", + "2025-09-15", + "2025-09-16", + "2025-09-17", + "2025-09-18", + "2025-09-19", + "2025-09-20", + "2025-09-21", + "2025-09-22", + "2025-09-23", + "2025-09-24", + "2025-09-25", + "2025-09-26", + "2025-09-27", + "2025-09-28", + "2025-09-29", + "2025-09-30", + "2025-10-01", + "2025-10-02", + "2025-10-03", + "2025-10-04", + "2025-10-05", + "2025-10-06", + "2025-10-07", + "2025-10-08", + "2025-10-09", + "2025-10-10", + "2025-10-11", + "2025-10-12", + "2025-10-13", + "2025-10-14", + "2025-10-15", + "2025-10-16", + "2025-10-17", + "2025-10-18", + "2025-10-19", + "2025-10-20", + "2025-10-21", + "2025-10-22", + "2025-10-23", + "2025-10-24", + "2025-10-25", + "2025-10-26", + "2025-10-27", + "2025-10-28", + "2025-10-29", + "2025-10-30", + "2025-10-31", + "2025-11-01", + "2025-11-02", + "2025-11-03", + "2025-11-04", + "2025-11-05", + "2025-11-06", + "2025-11-07", + "2025-11-08", + "2025-11-09", + "2025-11-10", + "2025-11-11", + "2025-11-12", + "2025-11-13", + "2025-11-14", + "2025-11-15", + "2025-11-16", + "2025-11-17", + "2025-11-18", + "2025-11-19", + "2025-11-20", + "2025-11-21", + "2025-11-22", + "2025-11-23", + "2025-11-24", + "2025-11-25", + "2025-11-26", + "2025-11-27", + "2025-11-28", + "2025-11-29", + "2025-11-30", + "2025-12-01", + "2025-12-02", + "2025-12-03", + "2025-12-04", + "2025-12-05", + "2025-12-06", + "2025-12-07", + "2025-12-08", + "2025-12-09", + "2025-12-10", + "2025-12-11", + "2025-12-12", + "2025-12-13", + "2025-12-14", + "2025-12-15", + "2025-12-16", + "2025-12-17", + "2025-12-18", + "2025-12-19", + "2025-12-20", + "2025-12-21", + "2025-12-22", + "2025-12-23", + "2025-12-24", + "2025-12-25", + "2025-12-26", + "2025-12-27", + "2025-12-28", + "2025-12-29", + "2025-12-30", + "2025-12-31" + ], + "agents": [ + { + "count": 1, + "initial_hf": 1.15, + "rebalancing_hf": 1.05, + "target_hf": 1.08, + "debt_per_agent": 133333, + "total_system_debt": 20000000 + } + ], + "pools": { + "moet_yt": { + "size": 500000, + "concentration": 0.95, + "fee_tier": 0.0005 + }, + "moet_btc": { + "size": 5000000, + "concentration": 0.8, + "fee_tier": 0.003 + } + }, + "constants": { + "btc_collateral_factor": 0.8, + "btc_liquidation_threshold": 0.85, + "yield_apr": 0.1, + "direct_mint_yt": true + }, + "expected": { + "liquidation_count": 0, + "all_agents_survive": true + }, + "notes": "Daily BTC/USD close prices from CoinMarketCap, 2025-01-01 to 2025-12-31. 365 data points." +} \ No newline at end of file diff --git a/cadence/tests/scripts/simulations/flash_crash_moderate.json b/cadence/tests/scripts/simulations/flash_crash_moderate.json new file mode 100644 index 00000000..e67ea99c --- /dev/null +++ b/cadence/tests/scripts/simulations/flash_crash_moderate.json @@ -0,0 +1,2921 @@ +{ + "scenario": "flash_crash_moderate", + "duration_minutes": 2880, + "btc_prices": [ + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 100000.0, + 96000.0, + 92000.0, + 88000.0, + 84000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80000.0, + 80015.34, + 80030.68, + 80046.02, + 80061.35, + 80076.68, + 80092.0, + 80107.32, + 80122.64, + 80137.95, + 80153.26, + 80168.56, + 80183.86, + 80199.16, + 80214.45, + 80229.74, + 80245.02, + 80260.3, + 80275.58, + 80290.85, + 80306.12, + 80321.38, + 80336.64, + 80351.9, + 80367.15, + 80382.4, + 80397.65, + 80412.89, + 80428.13, + 80443.36, + 80458.59, + 80473.81, + 80489.03, + 80504.25, + 80519.46, + 80534.67, + 80549.88, + 80565.08, + 80580.28, + 80595.47, + 80610.66, + 80625.85, + 80641.03, + 80656.2, + 80671.38, + 80686.55, + 80701.71, + 80716.88, + 80732.03, + 80747.19, + 80762.34, + 80777.48, + 80792.62, + 80807.76, + 80822.9, + 80838.03, + 80853.15, + 80868.27, + 80883.39, + 80898.51, + 80913.62, + 80928.72, + 80943.82, + 80958.92, + 80974.02, + 80989.11, + 81004.19, + 81019.27, + 81034.35, + 81049.43, + 81064.5, + 81079.56, + 81094.62, + 81109.68, + 81124.74, + 81139.79, + 81154.83, + 81169.87, + 81184.91, + 81199.95, + 81214.98, + 81230.0, + 81245.02, + 81260.04, + 81275.06, + 81290.07, + 81305.07, + 81320.07, + 81335.07, + 81350.07, + 81365.06, + 81380.04, + 81395.02, + 81410.0, + 81424.97, + 81439.94, + 81454.91, + 81469.87, + 81484.83, + 81499.78, + 81514.73, + 81529.68, + 81544.62, + 81559.56, + 81574.49, + 81589.42, + 81604.35, + 81619.27, + 81634.19, + 81649.1, + 81664.01, + 81678.91, + 81693.81, + 81708.71, + 81723.6, + 81738.49, + 81753.38, + 81768.26, + 81783.14, + 81798.01, + 81812.88, + 81827.74, + 81842.6, + 81857.46, + 81872.31, + 81887.16, + 81902.01, + 81916.85, + 81931.68, + 81946.52, + 81961.34, + 81976.17, + 81990.99, + 82005.81, + 82020.62, + 82035.43, + 82050.23, + 82065.03, + 82079.83, + 82094.62, + 82109.4, + 82124.19, + 82138.97, + 82153.74, + 82168.51, + 82183.28, + 82198.04, + 82212.8, + 82227.56, + 82242.31, + 82257.06, + 82271.8, + 82286.54, + 82301.27, + 82316.0, + 82330.73, + 82345.45, + 82360.17, + 82374.88, + 82389.59, + 82404.3, + 82419.0, + 82433.7, + 82448.39, + 82463.08, + 82477.77, + 82492.45, + 82507.13, + 82521.8, + 82536.47, + 82551.13, + 82565.8, + 82580.45, + 82595.11, + 82609.75, + 82624.4, + 82639.04, + 82653.67, + 82668.31, + 82682.93, + 82697.56, + 82712.18, + 82726.79, + 82741.41, + 82756.01, + 82770.62, + 82785.22, + 82799.81, + 82814.4, + 82828.99, + 82843.57, + 82858.15, + 82872.72, + 82887.29, + 82901.86, + 82916.42, + 82930.98, + 82945.53, + 82960.08, + 82974.63, + 82989.17, + 83003.71, + 83018.24, + 83032.77, + 83047.29, + 83061.81, + 83076.33, + 83090.84, + 83105.35, + 83119.85, + 83134.35, + 83148.85, + 83163.34, + 83177.83, + 83192.31, + 83206.79, + 83221.26, + 83235.74, + 83250.2, + 83264.66, + 83279.12, + 83293.58, + 83308.03, + 83322.47, + 83336.91, + 83351.35, + 83365.78, + 83380.21, + 83394.64, + 83409.06, + 83423.47, + 83437.89, + 83452.3, + 83466.7, + 83481.1, + 83495.49, + 83509.89, + 83524.27, + 83538.66, + 83553.03, + 83567.41, + 83581.78, + 83596.15, + 83610.51, + 83624.87, + 83639.22, + 83653.57, + 83667.91, + 83682.26, + 83696.59, + 83710.92, + 83725.25, + 83739.58, + 83753.9, + 83768.21, + 83782.53, + 83796.83, + 83811.14, + 83825.43, + 83839.73, + 83854.02, + 83868.31, + 83882.59, + 83896.87, + 83911.14, + 83925.41, + 83939.68, + 83953.94, + 83968.19, + 83982.45, + 83996.69, + 84010.94, + 84025.18, + 84039.41, + 84053.65, + 84067.87, + 84082.1, + 84096.31, + 84110.53, + 84124.74, + 84138.95, + 84153.15, + 84167.34, + 84181.54, + 84195.73, + 84209.91, + 84224.09, + 84238.27, + 84252.44, + 84266.61, + 84280.77, + 84294.93, + 84309.09, + 84323.24, + 84337.38, + 84351.53, + 84365.66, + 84379.8, + 84393.93, + 84408.05, + 84422.17, + 84436.29, + 84450.4, + 84464.51, + 84478.62, + 84492.71, + 84506.81, + 84520.9, + 84534.99, + 84549.07, + 84563.15, + 84577.22, + 84591.29, + 84605.36, + 84619.42, + 84633.48, + 84647.53, + 84661.58, + 84675.62, + 84689.66, + 84703.7, + 84717.73, + 84731.75, + 84745.78, + 84759.8, + 84773.81, + 84787.82, + 84801.82, + 84815.83, + 84829.82, + 84843.81, + 84857.8, + 84871.79, + 84885.77, + 84899.74, + 84913.71, + 84927.68, + 84941.64, + 84955.6, + 84969.55, + 84983.5, + 84997.45, + 85011.39, + 85025.32, + 85039.26, + 85053.18, + 85067.11, + 85081.03, + 85094.94, + 85108.85, + 85122.76, + 85136.66, + 85150.56, + 85164.45, + 85178.34, + 85192.22, + 85206.1, + 85219.98, + 85233.85, + 85247.72, + 85261.58, + 85275.44, + 85289.29, + 85303.14, + 85316.99, + 85330.83, + 85344.67, + 85358.5, + 85372.33, + 85386.15, + 85399.97, + 85413.78, + 85427.6, + 85441.4, + 85455.2, + 85469.0, + 85482.79, + 85496.58, + 85510.37, + 85524.15, + 85537.92, + 85551.69, + 85565.46, + 85579.22, + 85592.98, + 85606.73, + 85620.48, + 85634.23, + 85647.97, + 85661.71, + 85675.44, + 85689.16, + 85702.89, + 85716.61, + 85730.32, + 85744.03, + 85757.74, + 85771.44, + 85785.13, + 85798.83, + 85812.51, + 85826.2, + 85839.88, + 85853.55, + 85867.22, + 85880.89, + 85894.55, + 85908.21, + 85921.86, + 85935.51, + 85949.15, + 85962.79, + 85976.42, + 85990.06, + 86003.68, + 86017.3, + 86030.92, + 86044.53, + 86058.14, + 86071.75, + 86085.35, + 86098.94, + 86112.53, + 86126.12, + 86139.7, + 86153.28, + 86166.85, + 86180.42, + 86193.98, + 86207.54, + 86221.1, + 86234.65, + 86248.19, + 86261.74, + 86275.27, + 86288.81, + 86302.34, + 86315.86, + 86329.38, + 86342.89, + 86356.41, + 86369.91, + 86383.41, + 86396.91, + 86410.4, + 86423.89, + 86437.38, + 86450.86, + 86464.33, + 86477.8, + 86491.27, + 86504.73, + 86518.19, + 86531.64, + 86545.09, + 86558.53, + 86571.97, + 86585.4, + 86598.84, + 86612.26, + 86625.68, + 86639.1, + 86652.51, + 86665.92, + 86679.32, + 86692.72, + 86706.12, + 86719.51, + 86732.89, + 86746.27, + 86759.65, + 86773.02, + 86786.39, + 86799.75, + 86813.11, + 86826.46, + 86839.81, + 86853.16, + 86866.5, + 86879.83, + 86893.16, + 86906.49, + 86919.81, + 86933.13, + 86946.44, + 86959.75, + 86973.06, + 86986.36, + 86999.65, + 87012.94, + 87026.23, + 87039.51, + 87052.79, + 87066.06, + 87079.33, + 87092.59, + 87105.85, + 87119.1, + 87132.35, + 87145.6, + 87158.84, + 87172.07, + 87185.3, + 87198.53, + 87211.75, + 87224.97, + 87238.19, + 87251.39, + 87264.6, + 87277.8, + 87290.99, + 87304.18, + 87317.37, + 87330.55, + 87343.73, + 87356.9, + 87370.07, + 87383.23, + 87396.39, + 87409.54, + 87422.69, + 87435.84, + 87448.98, + 87462.11, + 87475.24, + 87488.37, + 87501.49, + 87514.61, + 87527.72, + 87540.83, + 87553.93, + 87567.03, + 87580.13, + 87593.22, + 87606.3, + 87619.38, + 87632.46, + 87645.53, + 87658.59, + 87671.66, + 87684.71, + 87697.77, + 87710.81, + 87723.86, + 87736.9, + 87749.93, + 87762.96, + 87775.99, + 87789.01, + 87802.02, + 87815.03, + 87828.04, + 87841.04, + 87854.04, + 87867.03, + 87880.02, + 87893.0, + 87905.98, + 87918.96, + 87931.93, + 87944.89, + 87957.85, + 87970.81, + 87983.76, + 87996.7, + 88009.64, + 88022.58, + 88035.51, + 88048.44, + 88061.36, + 88074.28, + 88087.2, + 88100.11, + 88113.01, + 88125.91, + 88138.8, + 88151.69, + 88164.58, + 88177.46, + 88190.34, + 88203.21, + 88216.08, + 88228.94, + 88241.8, + 88254.65, + 88267.5, + 88280.34, + 88293.18, + 88306.01, + 88318.84, + 88331.67, + 88344.49, + 88357.3, + 88370.11, + 88382.92, + 88395.72, + 88408.52, + 88421.31, + 88434.09, + 88446.88, + 88459.65, + 88472.43, + 88485.2, + 88497.96, + 88510.72, + 88523.47, + 88536.22, + 88548.97, + 88561.71, + 88574.44, + 88587.17, + 88599.9, + 88612.62, + 88625.33, + 88638.05, + 88650.75, + 88663.45, + 88676.15, + 88688.84, + 88701.53, + 88714.22, + 88726.89, + 88739.57, + 88752.24, + 88764.9, + 88777.56, + 88790.21, + 88802.86, + 88815.51, + 88828.15, + 88840.78, + 88853.42, + 88866.04, + 88878.66, + 88891.28, + 88903.89, + 88916.5, + 88929.1, + 88941.7, + 88954.29, + 88966.88, + 88979.46, + 88992.04, + 89004.61, + 89017.18, + 89029.74, + 89042.3, + 89054.86, + 89067.41, + 89079.95, + 89092.49, + 89105.02, + 89117.56, + 89130.08, + 89142.6, + 89155.12, + 89167.63, + 89180.13, + 89192.64, + 89205.13, + 89217.62, + 89230.11, + 89242.59, + 89255.07, + 89267.54, + 89280.01, + 89292.47, + 89304.93, + 89317.38, + 89329.83, + 89342.27, + 89354.71, + 89367.15, + 89379.58, + 89392.0, + 89404.42, + 89416.83, + 89429.24, + 89441.65, + 89454.05, + 89466.44, + 89478.83, + 89491.22, + 89503.6, + 89515.97, + 89528.34, + 89540.71, + 89553.07, + 89565.42, + 89577.78, + 89590.12, + 89602.46, + 89614.8, + 89627.13, + 89639.46, + 89651.78, + 89664.1, + 89676.41, + 89688.72, + 89701.02, + 89713.31, + 89725.61, + 89737.89, + 89750.18, + 89762.46, + 89774.73, + 89787.0, + 89799.26, + 89811.52, + 89823.77, + 89836.02, + 89848.26, + 89860.5, + 89872.73, + 89884.96, + 89897.19, + 89909.4, + 89921.62, + 89933.83, + 89946.03, + 89958.23, + 89970.42, + 89982.61, + 89994.8, + 90006.98, + 90019.15, + 90031.32, + 90043.48, + 90055.64, + 90067.8, + 90079.95, + 90092.09, + 90104.23, + 90116.37, + 90128.5, + 90140.62, + 90152.74, + 90164.85, + 90176.96, + 90189.07, + 90201.17, + 90213.26, + 90225.35, + 90237.44, + 90249.52, + 90261.59, + 90273.66, + 90285.73, + 90297.79, + 90309.84, + 90321.89, + 90333.94, + 90345.98, + 90358.01, + 90370.04, + 90382.07, + 90394.09, + 90406.1, + 90418.11, + 90430.12, + 90442.12, + 90454.11, + 90466.1, + 90478.09, + 90490.07, + 90502.04, + 90514.01, + 90525.98, + 90537.93, + 90549.89, + 90561.84, + 90573.78, + 90585.72, + 90597.66, + 90609.59, + 90621.51, + 90633.43, + 90645.34, + 90657.25, + 90669.16, + 90681.06, + 90692.95, + 90704.84, + 90716.72, + 90728.6, + 90740.48, + 90752.34, + 90764.21, + 90776.07, + 90787.92, + 90799.77, + 90811.61, + 90823.45, + 90835.28, + 90847.11, + 90858.93, + 90870.75, + 90882.56, + 90894.37, + 90906.17, + 90917.97, + 90929.76, + 90941.55, + 90953.33, + 90965.11, + 90976.88, + 90988.65, + 91000.41, + 91012.17, + 91023.92, + 91035.66, + 91047.41, + 91059.14, + 91070.87, + 91082.6, + 91094.32, + 91106.03, + 91117.74, + 91129.45, + 91141.15, + 91152.84, + 91164.53, + 91176.22, + 91187.9, + 91199.57, + 91211.24, + 91222.91, + 91234.56, + 91246.22, + 91257.87, + 91269.51, + 91281.15, + 91292.78, + 91304.41, + 91316.03, + 91327.65, + 91339.26, + 91350.87, + 91362.47, + 91374.07, + 91385.66, + 91397.24, + 91408.82, + 91420.4, + 91431.97, + 91443.54, + 91455.1, + 91466.65, + 91478.2, + 91489.75, + 91501.28, + 91512.82, + 91524.35, + 91535.87, + 91547.39, + 91558.9, + 91570.41, + 91581.91, + 91593.41, + 91604.9, + 91616.39, + 91627.87, + 91639.35, + 91650.82, + 91662.29, + 91673.75, + 91685.2, + 91696.65, + 91708.1, + 91719.54, + 91730.97, + 91742.4, + 91753.83, + 91765.24, + 91776.66, + 91788.07, + 91799.47, + 91810.87, + 91822.26, + 91833.64, + 91845.03, + 91856.4, + 91867.77, + 91879.14, + 91890.5, + 91901.86, + 91913.21, + 91924.55, + 91935.89, + 91947.22, + 91958.55, + 91969.88, + 91981.19, + 91992.51, + 92003.81, + 92015.12, + 92026.41, + 92037.7, + 92048.99, + 92060.27, + 92071.55, + 92082.82, + 92094.08, + 92105.34, + 92116.59, + 92127.84, + 92139.09, + 92150.32, + 92161.56, + 92172.78, + 92184.0, + 92195.22, + 92206.43, + 92217.64, + 92228.84, + 92240.03, + 92251.22, + 92262.41, + 92273.59, + 92284.76, + 92295.93, + 92307.09, + 92318.25, + 92329.4, + 92340.55, + 92351.69, + 92362.82, + 92373.95, + 92385.08, + 92396.2, + 92407.31, + 92418.42, + 92429.52, + 92440.62, + 92451.71, + 92462.8, + 92473.88, + 92484.96, + 92496.03, + 92507.09, + 92518.15, + 92529.21, + 92540.25, + 92551.3, + 92562.34, + 92573.37, + 92584.39, + 92595.42, + 92606.43, + 92617.44, + 92628.45, + 92639.45, + 92650.44, + 92661.43, + 92672.41, + 92683.39, + 92694.36, + 92705.33, + 92716.29, + 92727.25, + 92738.2, + 92749.14, + 92760.08, + 92771.01, + 92781.94, + 92792.87, + 92803.78, + 92814.69, + 92825.6, + 92836.5, + 92847.4, + 92858.29, + 92869.17, + 92880.05, + 92890.92, + 92901.79, + 92912.65, + 92923.51, + 92934.36, + 92945.2, + 92956.04, + 92966.88, + 92977.7, + 92988.53, + 92999.34, + 93010.16, + 93020.96, + 93031.76, + 93042.56, + 93053.35, + 93064.13, + 93074.91, + 93085.68, + 93096.45, + 93107.21, + 93117.97, + 93128.72, + 93139.46, + 93150.2, + 93160.94, + 93171.66, + 93182.39, + 93193.1, + 93203.81, + 93214.52, + 93225.22, + 93235.91, + 93246.6, + 93257.28, + 93267.96, + 93278.63, + 93289.3, + 93299.96, + 93310.61, + 93321.26, + 93331.91, + 93342.54, + 93353.18, + 93363.8, + 93374.42, + 93385.04, + 93395.65, + 93406.25, + 93416.85, + 93427.44, + 93438.03, + 93448.61, + 93459.19, + 93469.76, + 93480.32, + 93490.88, + 93501.43, + 93511.98, + 93522.52, + 93533.05, + 93543.58, + 93554.11, + 93564.62, + 93575.14, + 93585.64, + 93596.14, + 93606.64, + 93617.13, + 93627.61, + 93638.09, + 93648.56, + 93659.03, + 93669.49, + 93679.95, + 93690.4, + 93700.84, + 93711.28, + 93721.71, + 93732.13, + 93742.56, + 93752.97, + 93763.38, + 93773.78, + 93784.18, + 93794.57, + 93804.96, + 93815.34, + 93825.71, + 93836.08, + 93846.44, + 93856.8, + 93867.15, + 93877.49, + 93887.83, + 93898.17, + 93908.49, + 93918.81, + 93929.13, + 93939.44, + 93949.74, + 93960.04, + 93970.34, + 93980.62, + 93990.9, + 94001.18, + 94011.45, + 94021.71, + 94031.97, + 94042.22, + 94052.46, + 94062.7, + 94072.94, + 94083.17, + 94093.39, + 94103.6, + 94113.81, + 94124.02, + 94134.22, + 94144.41, + 94154.6, + 94164.78, + 94174.95, + 94185.12, + 94195.28, + 94205.44, + 94215.59, + 94225.74, + 94235.87, + 94246.01, + 94256.14, + 94266.26, + 94276.37, + 94286.48, + 94296.59, + 94306.68, + 94316.77, + 94326.86, + 94336.94, + 94347.01, + 94357.08, + 94367.14, + 94377.2, + 94387.25, + 94397.29, + 94407.33, + 94417.36, + 94427.39, + 94437.41, + 94447.42, + 94457.43, + 94467.43, + 94477.42, + 94487.41, + 94497.4, + 94507.38, + 94517.35, + 94527.31, + 94537.27, + 94547.22, + 94557.17, + 94567.11, + 94577.05, + 94586.98, + 94596.9, + 94606.82, + 94616.73, + 94626.63, + 94636.53, + 94646.42, + 94656.31, + 94666.19, + 94676.07, + 94685.93, + 94695.8, + 94705.65, + 94715.5, + 94725.35, + 94735.18, + 94745.02, + 94754.84, + 94764.66, + 94774.47, + 94784.28, + 94794.08, + 94803.88, + 94813.67, + 94823.45, + 94833.22, + 94842.99, + 94852.76, + 94862.52, + 94872.27, + 94882.01, + 94891.75, + 94901.49, + 94911.21, + 94920.93, + 94930.65, + 94940.36, + 94950.06, + 94959.75, + 94969.44, + 94979.13, + 94988.8, + 94998.48, + 95008.14, + 95017.8, + 95027.45, + 95037.1, + 95046.74, + 95056.37, + 95066.0, + 95075.62, + 95085.23, + 95094.84, + 95104.44, + 95114.04, + 95123.63, + 95133.21, + 95142.79, + 95152.36, + 95161.93, + 95171.48, + 95181.04, + 95190.58, + 95200.12, + 95209.65, + 95219.18, + 95228.7, + 95238.22, + 95247.72, + 95257.22, + 95266.72, + 95276.21, + 95285.69, + 95295.17, + 95304.64, + 95314.1, + 95323.56, + 95333.01, + 95342.45, + 95351.89, + 95361.32, + 95370.75, + 95380.16, + 95389.58, + 95398.98, + 95408.38, + 95417.77, + 95427.16, + 95436.54, + 95445.91, + 95455.28, + 95464.64, + 95474.0, + 95483.35, + 95492.69, + 95502.02, + 95511.35, + 95520.67, + 95529.99, + 95539.3, + 95548.6, + 95557.9, + 95567.19, + 95576.47, + 95585.75, + 95595.02, + 95604.28, + 95613.54, + 95622.79, + 95632.03, + 95641.27, + 95650.5, + 95659.73, + 95668.95, + 95678.16, + 95687.36, + 95696.56, + 95705.75, + 95714.94, + 95724.12, + 95733.29, + 95742.46, + 95751.62, + 95760.77, + 95769.92, + 95779.05, + 95788.19, + 95797.31, + 95806.43, + 95815.55, + 95824.65, + 95833.75, + 95842.85, + 95851.93, + 95861.01, + 95870.09, + 95879.15, + 95888.21, + 95897.27, + 95906.31, + 95915.35, + 95924.39, + 95933.41, + 95942.43, + 95951.45, + 95960.46, + 95969.46, + 95978.45, + 95987.44, + 95996.42, + 96005.39, + 96014.36, + 96023.31, + 96032.27, + 96041.21, + 96050.15, + 96059.09, + 96068.01, + 96076.93, + 96085.85, + 96094.75, + 96103.65, + 96112.54, + 96121.43, + 96130.31, + 96139.18, + 96148.04, + 96156.9, + 96165.75, + 96174.6, + 96183.44, + 96192.27, + 96201.09, + 96209.91, + 96218.72, + 96227.53, + 96236.32, + 96245.11, + 96253.9, + 96262.67, + 96271.44, + 96280.21, + 96288.96, + 96297.71, + 96306.45, + 96315.19, + 96323.92, + 96332.64, + 96341.35, + 96350.06, + 96358.76, + 96367.45, + 96376.14, + 96384.82, + 96393.49, + 96402.16, + 96410.82, + 96419.47, + 96428.12, + 96436.75, + 96445.39, + 96454.01, + 96462.63, + 96471.24, + 96479.84, + 96488.44, + 96497.03, + 96505.61, + 96514.18, + 96522.75, + 96531.31, + 96539.87, + 96548.41, + 96556.95, + 96565.49, + 96574.01, + 96582.53, + 96591.04, + 96599.55, + 96608.04, + 96616.54, + 96625.02, + 96633.5, + 96641.96, + 96650.43, + 96658.88, + 96667.33, + 96675.77, + 96684.2, + 96692.63, + 96701.05, + 96709.46, + 96717.87, + 96726.26, + 96734.65, + 96743.04, + 96751.41, + 96759.78, + 96768.15, + 96776.5, + 96784.85, + 96793.19, + 96801.52, + 96809.85, + 96818.16, + 96826.48, + 96834.78, + 96843.08, + 96851.37, + 96859.65, + 96867.92, + 96876.19, + 96884.45, + 96892.7, + 96900.95, + 96909.19, + 96917.42, + 96925.64, + 96933.86, + 96942.07, + 96950.27, + 96958.47, + 96966.65, + 96974.83, + 96983.01, + 96991.17, + 96999.33, + 97007.48, + 97015.62, + 97023.76, + 97031.89, + 97040.01, + 97048.12, + 97056.23, + 97064.32, + 97072.41, + 97080.5, + 97088.57, + 97096.64, + 97104.7, + 97112.76, + 97120.8, + 97128.84, + 97136.87, + 97144.9, + 97152.91, + 97160.92, + 97168.92, + 97176.92, + 97184.9, + 97192.88, + 97200.85, + 97208.82, + 97216.77, + 97224.72, + 97232.66, + 97240.59, + 97248.52, + 97256.44, + 97264.35, + 97272.25, + 97280.15, + 97288.03, + 97295.91, + 97303.78, + 97311.65, + 97319.51, + 97327.36, + 97335.2, + 97343.03, + 97350.86, + 97358.68, + 97366.49, + 97374.29, + 97382.08, + 97389.87, + 97397.65, + 97405.42, + 97413.19, + 97420.94, + 97428.69, + 97436.43, + 97444.17, + 97451.89, + 97459.61, + 97467.32, + 97475.02, + 97482.72, + 97490.4, + 97498.08, + 97505.75, + 97513.42, + 97521.07, + 97528.72, + 97536.36, + 97543.99, + 97551.61, + 97559.23, + 97566.83, + 97574.43, + 97582.03, + 97589.61, + 97597.19, + 97604.75, + 97612.31, + 97619.87, + 97627.41, + 97634.95, + 97642.47, + 97649.99, + 97657.51, + 97665.01, + 97672.51, + 97679.99, + 97687.47, + 97694.94, + 97702.41, + 97709.86, + 97717.31, + 97724.75, + 97732.18, + 97739.61, + 97747.02, + 97754.43, + 97761.83, + 97769.22, + 97776.6, + 97783.97, + 97791.34, + 97798.7, + 97806.05, + 97813.39, + 97820.72, + 97828.05, + 97835.37, + 97842.68, + 97849.98, + 97857.27, + 97864.55, + 97871.83, + 97879.1, + 97886.36, + 97893.61, + 97900.85, + 97908.08, + 97915.31, + 97922.53, + 97929.74, + 97936.94, + 97944.13, + 97951.31, + 97958.49, + 97965.66, + 97972.82, + 97979.97, + 97987.11, + 97994.24, + 98001.37, + 98008.48, + 98015.59, + 98022.69, + 98029.78, + 98036.87, + 98043.94, + 98051.01, + 98058.06, + 98065.11, + 98072.15, + 98079.18, + 98086.21, + 98093.22, + 98100.23, + 98107.23, + 98114.21, + 98121.19, + 98128.17, + 98135.13, + 98142.08, + 98149.03, + 98155.96, + 98162.89, + 98169.81, + 98176.72, + 98183.63, + 98190.52, + 98197.4, + 98204.28, + 98211.15, + 98218.0, + 98224.85, + 98231.69, + 98238.53, + 98245.35, + 98252.16, + 98258.97, + 98265.76, + 98272.55, + 98279.33, + 98286.1, + 98292.86, + 98299.61, + 98306.36, + 98313.09, + 98319.82, + 98326.53, + 98333.24, + 98339.94, + 98346.63, + 98353.31, + 98359.98, + 98366.64, + 98373.29, + 98379.94, + 98386.57, + 98393.2, + 98399.82, + 98406.42, + 98413.02, + 98419.61, + 98426.19, + 98432.76, + 98439.33, + 98445.88, + 98452.42, + 98458.96, + 98465.48, + 98472.0, + 98478.51, + 98485.0, + 98491.49, + 98497.97, + 98504.44, + 98510.9, + 98517.35, + 98523.79, + 98530.22, + 98536.65, + 98543.06, + 98549.46, + 98555.86, + 98562.24, + 98568.62, + 98574.99, + 98581.34, + 98587.69, + 98594.03, + 98600.36, + 98606.68, + 98612.99, + 98619.29, + 98625.58, + 98631.86, + 98638.13, + 98644.39, + 98650.64, + 98656.88, + 98663.12, + 98669.34, + 98675.55, + 98681.76, + 98687.95, + 98694.13, + 98700.31, + 98706.47, + 98712.63, + 98718.77, + 98724.91, + 98731.03, + 98737.15, + 98743.25, + 98749.35, + 98755.44, + 98761.51, + 98767.58, + 98773.63, + 98779.68, + 98785.72, + 98791.74, + 98797.76, + 98803.76, + 98809.76, + 98815.75, + 98821.72, + 98827.69, + 98833.64, + 98839.59, + 98845.53, + 98851.45, + 98857.37, + 98863.27, + 98869.17, + 98875.05, + 98880.93, + 98886.79, + 98892.64, + 98898.49, + 98904.32, + 98910.14, + 98915.96, + 98921.76, + 98927.55, + 98933.33, + 98939.1, + 98944.86, + 98950.61, + 98956.35, + 98962.08, + 98967.8, + 98973.51, + 98979.21, + 98984.89, + 98990.57, + 98996.23, + 99001.89, + 99007.53, + 99013.17, + 99018.79, + 99024.4, + 99030.0, + 99035.59, + 99041.17, + 99046.74, + 99052.3, + 99057.85, + 99063.39, + 99068.91, + 99074.43, + 99079.93, + 99085.42, + 99090.91, + 99096.38, + 99101.84, + 99107.29, + 99112.72, + 99118.15, + 99123.57, + 99128.97, + 99134.36, + 99139.75, + 99145.12, + 99150.48, + 99155.83, + 99161.16, + 99166.49, + 99171.8, + 99177.11, + 99182.4, + 99187.68, + 99192.95, + 99198.21, + 99203.45, + 99208.69, + 99213.91, + 99219.12, + 99224.32, + 99229.51, + 99234.69, + 99239.85, + 99245.01, + 99250.15, + 99255.28, + 99260.4, + 99265.5, + 99270.6, + 99275.68, + 99280.75, + 99285.81, + 99290.86, + 99295.89, + 99300.92, + 99305.93, + 99310.93, + 99315.91, + 99320.89, + 99325.85, + 99330.8, + 99335.74, + 99340.67, + 99345.58, + 99350.48, + 99355.37, + 99360.25, + 99365.12, + 99369.97, + 99374.81, + 99379.64, + 99384.45, + 99389.26, + 99394.05, + 99398.82, + 99403.59, + 99408.34, + 99413.08, + 99417.81, + 99422.52, + 99427.22, + 99431.91, + 99436.59, + 99441.25, + 99445.9, + 99450.54, + 99455.16, + 99459.77, + 99464.37, + 99468.95, + 99473.52, + 99478.08, + 99482.63, + 99487.16, + 99491.68, + 99496.18, + 99500.67, + 99505.15, + 99509.62, + 99514.07, + 99518.51, + 99522.93, + 99527.34, + 99531.74, + 99536.12, + 99540.49, + 99544.84, + 99549.19, + 99553.51, + 99557.83, + 99562.13, + 99566.41, + 99570.69, + 99574.94, + 99579.19, + 99583.42, + 99587.63, + 99591.83, + 99596.02, + 99600.19, + 99604.35, + 99608.49, + 99612.62, + 99616.73, + 99620.83, + 99624.92, + 99628.99, + 99633.04, + 99637.08, + 99641.11, + 99645.12, + 99649.11, + 99653.09, + 99657.05, + 99661.0, + 99664.94, + 99668.86, + 99672.76, + 99676.65, + 99680.52, + 99684.38, + 99688.22, + 99692.04, + 99695.85, + 99699.65, + 99703.43, + 99707.19, + 99710.93, + 99714.66, + 99718.38, + 99722.08, + 99725.76, + 99729.42, + 99733.07, + 99736.7, + 99740.32, + 99743.91, + 99747.5, + 99751.06, + 99754.61, + 99758.14, + 99761.65, + 99765.15, + 99768.63, + 99772.09, + 99775.53, + 99778.96, + 99782.37, + 99785.76, + 99789.14, + 99792.49, + 99795.83, + 99799.15, + 99802.45, + 99805.73, + 99809.0, + 99812.25, + 99815.47, + 99818.68, + 99821.87, + 99825.04, + 99828.2, + 99831.33, + 99834.44, + 99837.54, + 99840.61, + 99843.67, + 99846.7, + 99849.72, + 99852.72, + 99855.69, + 99858.65, + 99861.58, + 99864.49, + 99867.39, + 99870.26, + 99873.11, + 99875.94, + 99878.75, + 99881.54, + 99884.3, + 99887.05, + 99889.77, + 99892.47, + 99895.15, + 99897.8, + 99900.43, + 99903.04, + 99905.63, + 99908.19, + 99910.73, + 99913.24, + 99915.73, + 99918.2, + 99920.64, + 99923.06, + 99925.45, + 99927.82, + 99930.16, + 99932.47, + 99934.76, + 99937.02, + 99939.26, + 99941.47, + 99943.65, + 99945.8, + 99947.93, + 99950.02, + 99952.09, + 99954.13, + 99956.14, + 99958.12, + 99960.07, + 99961.98, + 99963.87, + 99965.72, + 99967.54, + 99969.33, + 99971.08, + 99972.8, + 99974.48, + 99976.12, + 99977.73, + 99979.31, + 99980.84, + 99982.33, + 99983.78, + 99985.19, + 99986.56, + 99987.88, + 99989.16, + 99990.38, + 99991.56, + 99992.68, + 99993.75, + 99994.76, + 99995.71, + 99996.6, + 99997.41, + 99998.15, + 99998.8, + 99999.35, + 99999.77 + ], + "agents": [ + { + "count": 150, + "initial_hf": 1.15, + "rebalancing_hf": 1.05, + "target_hf": 1.08, + "btc_collateral_per_agent": "calculated", + "debt_per_agent": 133333, + "total_system_debt": 20000000 + } + ], + "pools": { + "moet_yt": { + "size": 500000, + "concentration": 0.95, + "token0_ratio": 0.75, + "fee_tier": 0.0005 + }, + "moet_btc": { + "size": 5000000, + "concentration": 0.8, + "fee_tier": 0.003 + } + }, + "constants": { + "btc_collateral_factor": 0.8, + "btc_liquidation_threshold": 0.85, + "yield_apr": 0.1, + "direct_mint_yt": true + }, + "expected": { + "liquidation_count": 0, + "all_agents_survive": true + }, + "notes": "20% BTC crash over 5 min, floor for 20 min, exponential recovery. No oracle manipulation, no liquidity evaporation, no random noise." +} \ No newline at end of file diff --git a/cadence/tests/scripts/simulations/generate_fixture.py b/cadence/tests/scripts/simulations/generate_fixture.py new file mode 100644 index 00000000..c42c32ae --- /dev/null +++ b/cadence/tests/scripts/simulations/generate_fixture.py @@ -0,0 +1,430 @@ +#!/usr/bin/env python3 +"""Simulation fixture tooling: fetch BTC prices and generate Cadence helpers. + +Subcommands +----------- +fetch Scrape daily BTC/USD close prices from CoinMarketCap and write a fixture JSON. +generate Convert a fixture JSON into a Cadence test-helper (.cdc) file. + +Examples +-------- + # Fetch 2025 daily BTC prices + python3 generate_fixture.py fetch --output btc_daily_2025.json \\ + --start 2025-01-01 --end 2025-12-31 + + # Convert to Cadence helpers + python3 generate_fixture.py generate btc_daily_2025.json \\ + ../../btc_daily_2025_helpers.cdc +""" + +import argparse +import json +import os +import ssl +import time +from datetime import datetime, timezone +from urllib.request import urlopen, Request +from urllib.error import HTTPError + + +# --------------------------------------------------------------------------- +# Shared helpers +# --------------------------------------------------------------------------- + + +def to_ufix64(v: float) -> str: + """Format a float as a Cadence UFix64 literal (8 decimal places).""" + return f"{v:.8f}" + + +# --------------------------------------------------------------------------- +# fetch: pull daily BTC/USD prices from CoinMarketCap data API +# --------------------------------------------------------------------------- + +CMC_BTC_ID = 1 +CMC_USD_ID = 2781 +CMC_CHUNK_DAYS = 90 + + +def _ssl_context() -> ssl.SSLContext: + ctx = ssl.create_default_context() + try: + import certifi + + ctx.load_verify_locations(certifi.where()) + except ImportError: + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + return ctx + + +def _http_get_json(url: str) -> dict: + """GET a JSON endpoint with retries on 429.""" + req = Request( + url, + headers={ + "Accept": "application/json", + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", + }, + ) + ctx = _ssl_context() + for attempt in range(3): + try: + with urlopen(req, timeout=30, context=ctx) as resp: + return json.loads(resp.read()) + except HTTPError as exc: + if exc.code == 429 and attempt < 2: + wait = 10 * (attempt + 1) + print(f" Rate-limited (429), retrying in {wait}s ...") + time.sleep(wait) + continue + raise + raise RuntimeError("HTTP request failed after retries") + + +def _fetch_cmc_chunk(time_start: int, time_end: int) -> list[dict]: + """Fetch a single chunk of daily BTC prices from CMC data API.""" + url = ( + f"https://api.coinmarketcap.com/data-api/v3/cryptocurrency/historical" + f"?id={CMC_BTC_ID}&convertId={CMC_USD_ID}" + f"&timeStart={time_start}&timeEnd={time_end}" + ) + body = _http_get_json(url) + results: list[dict] = [] + for item in body["data"]["quotes"]: + q = item["quote"] + date_str = item["timeOpen"][:10] + results.append({"date": date_str, "price": round(q["close"], 2)}) + return results + + +def fetch_daily_prices(start: str, end: str) -> list[dict]: + """Return [{date, price}, ...] daily BTC/USD close prices from CoinMarketCap. + + Fetches in 90-day chunks to stay within API limits. + """ + from datetime import timedelta + + start_dt = datetime.strptime(start, "%Y-%m-%d").replace(tzinfo=timezone.utc) + end_dt = datetime.strptime(end, "%Y-%m-%d").replace( + hour=23, minute=59, second=59, tzinfo=timezone.utc + ) + + now = datetime.now(tz=timezone.utc) + if end_dt > now: + end_dt = now + + all_daily: list[dict] = [] + chunk_start = start_dt + + while chunk_start < end_dt: + chunk_end = min(chunk_start + timedelta(days=CMC_CHUNK_DAYS), end_dt) + ts_start = int(chunk_start.timestamp()) + ts_end = int(chunk_end.timestamp()) + + print( + f" Fetching {chunk_start.strftime('%Y-%m-%d')} -> {chunk_end.strftime('%Y-%m-%d')} ..." + ) + chunk = _fetch_cmc_chunk(ts_start, ts_end) + all_daily.extend(chunk) + + chunk_start = chunk_end + timedelta(seconds=1) + if chunk_start < end_dt: + time.sleep(1) + + seen: set[str] = set() + deduped: list[dict] = [] + for entry in all_daily: + if entry["date"] not in seen: + seen.add(entry["date"]) + deduped.append(entry) + deduped.sort(key=lambda d: d["date"]) + return deduped + + +def build_fixture(daily: list[dict], scenario: str, start: str, end: str) -> dict: + """Assemble a fixture dict from daily price data.""" + return { + "scenario": scenario, + "duration_days": len(daily), + "btc_prices": [d["price"] for d in daily], + "dates": [d["date"] for d in daily], + "agents": [ + { + "count": 1, + "initial_hf": 1.15, + "rebalancing_hf": 1.05, + "target_hf": 1.08, + "debt_per_agent": 133333, + "total_system_debt": 20000000, + } + ], + "pools": { + "moet_yt": { + "size": 500000, + "concentration": 0.95, + "fee_tier": 0.0005, + }, + "moet_btc": { + "size": 5000000, + "concentration": 0.8, + "fee_tier": 0.003, + }, + }, + "constants": { + "btc_collateral_factor": 0.8, + "btc_liquidation_threshold": 0.85, + "yield_apr": 0.1, + "direct_mint_yt": True, + }, + "expected": { + "liquidation_count": 0, + "all_agents_survive": True, + }, + "notes": ( + f"Daily BTC/USD close prices from CoinMarketCap, {start} to {end}. " + f"{len(daily)} data points." + ), + } + + +def cmd_fetch(args: argparse.Namespace) -> None: + print(f"Fetching daily BTC/USD prices {args.start} -> {args.end} ...") + daily = fetch_daily_prices(args.start, args.end) + print(f" Retrieved {len(daily)} daily prices") + + if not daily: + raise SystemExit("No price data returned — check date range") + + print(f" First: {daily[0]['date']} ${daily[0]['price']:,.2f}") + print(f" Last: {daily[-1]['date']} ${daily[-1]['price']:,.2f}") + + scenario = args.scenario or f"btc_daily_{args.start[:4]}" + fixture = build_fixture(daily, scenario, args.start, args.end) + + os.makedirs(os.path.dirname(args.output) or ".", exist_ok=True) + with open(args.output, "w") as f: + json.dump(fixture, f, indent=2) + print(f" Wrote {args.output}") + + +# --------------------------------------------------------------------------- +# generate: convert fixture JSON -> Cadence _helpers.cdc +# --------------------------------------------------------------------------- + + +def generate_cdc(data: dict) -> str: + scenario = data["scenario"] + is_daily = "duration_days" in data + + lines: list[str] = [] + lines.append("import Test") + lines.append("") + lines.append(f"// AUTO-GENERATED from {scenario}.json — do not edit manually") + lines.append( + "// Run: python3 generate_fixture.py generate " + ) + lines.append("") + + # --- Inline struct definitions --- + lines.append("access(all) struct SimAgent {") + lines.append(" access(all) let count: Int") + lines.append(" access(all) let initialHF: UFix64") + lines.append(" access(all) let rebalancingHF: UFix64") + lines.append(" access(all) let targetHF: UFix64") + lines.append(" access(all) let debtPerAgent: UFix64") + lines.append(" access(all) let totalSystemDebt: UFix64") + lines.append("") + lines.append(" init(") + lines.append(" count: Int,") + lines.append(" initialHF: UFix64,") + lines.append(" rebalancingHF: UFix64,") + lines.append(" targetHF: UFix64,") + lines.append(" debtPerAgent: UFix64,") + lines.append(" totalSystemDebt: UFix64") + lines.append(" ) {") + lines.append(" self.count = count") + lines.append(" self.initialHF = initialHF") + lines.append(" self.rebalancingHF = rebalancingHF") + lines.append(" self.targetHF = targetHF") + lines.append(" self.debtPerAgent = debtPerAgent") + lines.append(" self.totalSystemDebt = totalSystemDebt") + lines.append(" }") + lines.append("}") + lines.append("") + + lines.append("access(all) struct SimPool {") + lines.append(" access(all) let size: UFix64") + lines.append(" access(all) let concentration: UFix64") + lines.append(" access(all) let feeTier: UFix64") + lines.append("") + lines.append(" init(size: UFix64, concentration: UFix64, feeTier: UFix64) {") + lines.append(" self.size = size") + lines.append(" self.concentration = concentration") + lines.append(" self.feeTier = feeTier") + lines.append(" }") + lines.append("}") + lines.append("") + + lines.append("access(all) struct SimConstants {") + lines.append(" access(all) let btcCollateralFactor: UFix64") + lines.append(" access(all) let btcLiquidationThreshold: UFix64") + lines.append(" access(all) let yieldAPR: UFix64") + lines.append(" access(all) let directMintYT: Bool") + lines.append("") + lines.append(" init(") + lines.append(" btcCollateralFactor: UFix64,") + lines.append(" btcLiquidationThreshold: UFix64,") + lines.append(" yieldAPR: UFix64,") + lines.append(" directMintYT: Bool") + lines.append(" ) {") + lines.append(" self.btcCollateralFactor = btcCollateralFactor") + lines.append(" self.btcLiquidationThreshold = btcLiquidationThreshold") + lines.append(" self.yieldAPR = yieldAPR") + lines.append(" self.directMintYT = directMintYT") + lines.append(" }") + lines.append("}") + lines.append("") + + # --- Price array --- + lines.append(f"access(all) let {scenario}_prices: [UFix64] = [") + for i, price in enumerate(data["btc_prices"]): + comma = "," if i < len(data["btc_prices"]) - 1 else "" + lines.append(f" {to_ufix64(price)}{comma}") + lines.append("]") + lines.append("") + + # --- Date labels (daily fixtures only) --- + if is_daily and "dates" in data: + lines.append(f"access(all) let {scenario}_dates: [String] = [") + for i, date in enumerate(data["dates"]): + comma = "," if i < len(data["dates"]) - 1 else "" + lines.append(f' "{date}"{comma}') + lines.append("]") + lines.append("") + + # --- Agent array --- + lines.append(f"access(all) let {scenario}_agents: [SimAgent] = [") + for i, agent in enumerate(data["agents"]): + comma = "," if i < len(data["agents"]) - 1 else "" + debt = ( + agent["debt_per_agent"] + if isinstance(agent["debt_per_agent"], (int, float)) + else 0 + ) + total_debt = agent.get("total_system_debt", 0) + lines.append(" SimAgent(") + lines.append(f" count: {agent['count']},") + lines.append(f" initialHF: {to_ufix64(agent['initial_hf'])},") + lines.append(f" rebalancingHF: {to_ufix64(agent['rebalancing_hf'])},") + lines.append(f" targetHF: {to_ufix64(agent['target_hf'])},") + lines.append(f" debtPerAgent: {to_ufix64(float(debt))},") + lines.append(f" totalSystemDebt: {to_ufix64(float(total_debt))}") + lines.append(f" ){comma}") + lines.append("]") + lines.append("") + + # --- Pool dict --- + lines.append(f"access(all) let {scenario}_pools: {{String: SimPool}} = {{") + pool_items = list(data["pools"].items()) + for i, (name, pool) in enumerate(pool_items): + comma = "," if i < len(pool_items) - 1 else "" + lines.append(f' "{name}": SimPool(') + lines.append(f" size: {to_ufix64(float(pool['size']))},") + lines.append(f" concentration: {to_ufix64(pool['concentration'])},") + lines.append(f" feeTier: {to_ufix64(pool['fee_tier'])}") + lines.append(f" ){comma}") + lines.append("}") + lines.append("") + + # --- Constants --- + c = data["constants"] + lines.append(f"access(all) let {scenario}_constants: SimConstants = SimConstants(") + lines.append(f" btcCollateralFactor: {to_ufix64(c['btc_collateral_factor'])},") + lines.append( + f" btcLiquidationThreshold: {to_ufix64(c['btc_liquidation_threshold'])}," + ) + lines.append(f" yieldAPR: {to_ufix64(c['yield_apr'])},") + lines.append(f" directMintYT: {'true' if c['direct_mint_yt'] else 'false'}") + lines.append(")") + lines.append("") + + # --- Expected outcomes --- + e = data["expected"] + lines.append( + f"access(all) let {scenario}_expectedLiquidationCount: Int = {e['liquidation_count']}" + ) + lines.append( + f"access(all) let {scenario}_expectedAllAgentsSurvive: Bool = {'true' if e['all_agents_survive'] else 'false'}" + ) + lines.append("") + + # --- Duration & notes --- + if is_daily: + lines.append( + f"access(all) let {scenario}_durationDays: Int = {data['duration_days']}" + ) + else: + lines.append( + f"access(all) let {scenario}_durationMinutes: Int = {data['duration_minutes']}" + ) + lines.append(f'access(all) let {scenario}_notes: String = "{data["notes"]}"') + lines.append("") + + return "\n".join(lines) + + +def cmd_generate(args: argparse.Namespace) -> None: + with open(args.input) as f: + data = json.load(f) + + cdc = generate_cdc(data) + + os.makedirs(os.path.dirname(args.output) or ".", exist_ok=True) + with open(args.output, "w") as f: + f.write(cdc) + + scenario = data["scenario"] + n_prices = len(data["btc_prices"]) + print(f"Generated {args.output} ({n_prices} prices, scenario: {scenario})") + + +# --------------------------------------------------------------------------- +# CLI +# --------------------------------------------------------------------------- + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Simulation fixture tooling: fetch prices & generate Cadence helpers" + ) + sub = parser.add_subparsers(dest="command", required=True) + + # -- fetch -- + fetch_p = sub.add_parser( + "fetch", help="Fetch daily BTC/USD prices from CoinMarketCap" + ) + fetch_p.add_argument("--start", required=True, help="Start date YYYY-MM-DD") + fetch_p.add_argument("--end", required=True, help="End date YYYY-MM-DD") + fetch_p.add_argument("--output", required=True, help="Output JSON path") + fetch_p.add_argument( + "--scenario", default=None, help="Scenario name (default: btc_daily_)" + ) + + # -- generate -- + gen_p = sub.add_parser( + "generate", help="Convert fixture JSON to Cadence _helpers.cdc" + ) + gen_p.add_argument("input", help="Input fixture JSON path") + gen_p.add_argument("output", help="Output .cdc path") + + args = parser.parse_args() + + if args.command == "fetch": + cmd_fetch(args) + elif args.command == "generate": + cmd_generate(args) + + +if __name__ == "__main__": + main() From 616c29331c727d360bb9bea745ee23a02aa185ce Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Thu, 12 Mar 2026 14:58:38 -0400 Subject: [PATCH 03/10] Add profit/loss calculations in output. --- cadence/tests/forked_btc_daily_2025_test.cdc | 43 +++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/cadence/tests/forked_btc_daily_2025_test.cdc b/cadence/tests/forked_btc_daily_2025_test.cdc index 930a00b8..d34fe8f4 100644 --- a/cadence/tests/forked_btc_daily_2025_test.cdc +++ b/cadence/tests/forked_btc_daily_2025_test.cdc @@ -315,11 +315,11 @@ fun test_BtcDaily2025_DailyRebalancing() { // Apply all price updates applyPriceTick(flowPrice: normalizedPrice, ytPrice: ytPrice, user: users[0]) - // Rebalance all agents + // Potentially rebalance all agents (not forced) var a = 0 while a < numAgents { - rebalanceYieldVault(signer: flowYieldVaultsAccount, id: vaultIds[a], force: true, beFailed: false) - rebalancePosition(signer: flowALPAccount, pid: pids[a], force: true, beFailed: false) + rebalanceYieldVault(signer: flowYieldVaultsAccount, id: vaultIds[a], force: false, beFailed: false) + rebalancePosition(signer: flowALPAccount, pid: pids[a], force: false, beFailed: false) a = a + 1 } rebalanceCount = rebalanceCount + 1 @@ -359,22 +359,55 @@ fun test_BtcDaily2025_DailyRebalancing() { let finalDebt = getMOETDebtFromPosition(pid: pids[0]) let finalYieldTokens = getAutoBalancerBalance(id: vaultIds[0])! let finalNormalizedPrice = normalizePrice(prices[prices.length - 1]) + let finalYtPrice = ytPriceAtDay(prices.length - 1) let finalHF = (finalFlowCollateral * finalNormalizedPrice) / finalDebt + // P&L: net equity = collateral_value + yt_value - debt (all in stablecoin/MOET terms) + let collateralValueMOET = finalFlowCollateral * finalNormalizedPrice + let ytValueMOET = finalYieldTokens * finalYtPrice + let netEquityMOET = collateralValueMOET + ytValueMOET - finalDebt + let initialDepositMOET = fundingPerAgent + + // UFix64 is unsigned, so track sign separately to avoid underflow + let moetProfit = netEquityMOET >= initialDepositMOET + let pnlMOETAbs = moetProfit ? (netEquityMOET - initialDepositMOET) : (initialDepositMOET - netEquityMOET) + let pnlPctMOETAbs = pnlMOETAbs / initialDepositMOET + let pnlMOETSign = moetProfit ? "+" : "-" + + let netEquityFLOW = netEquityMOET / finalNormalizedPrice + let flowProfit = netEquityFLOW >= fundingPerAgent + let pnlFLOWAbs = flowProfit ? (netEquityFLOW - fundingPerAgent) : (fundingPerAgent - netEquityFLOW) + let pnlPctFLOWAbs = pnlFLOWAbs / fundingPerAgent + let pnlFLOWSign = flowProfit ? "+" : "-" + + let priceUp = finalNormalizedPrice >= 1.0 + let priceChangePctAbs = priceUp ? (finalNormalizedPrice - 1.0) : (1.0 - finalNormalizedPrice) + let priceChangeSign = priceUp ? "+" : "-" + log("\n=== SIMULATION RESULTS ===") log("Agents: \(numAgents)") log("Days simulated: \(prices.length)") log("Rebalance events: \(rebalanceCount)") log("Liquidation count: \(liquidationCount)") + log("") + log("--- Price ---") log("Initial BTC price: $\(initialPrice)") log("Lowest BTC price: $\(lowestPrice)") log("Highest BTC price: $\(highestPrice)") log("Final BTC price: $\(prices[prices.length - 1])") + log("Price change: \(priceChangeSign)\(priceChangePctAbs)") + log("") + log("--- Position ---") log("Lowest HF observed: \(lowestHF)") log("Final HF (agent 0): \(finalHF)") - log("Final collateral: \(finalFlowCollateral) FLOW") + log("Final collateral: \(finalFlowCollateral) FLOW (value: \(collateralValueMOET) MOET)") log("Final debt: \(finalDebt) MOET") - log("Final yield tokens: \(finalYieldTokens)") + log("Final yield tokens: \(finalYieldTokens) (value: \(ytValueMOET) MOET @ yt=\(finalYtPrice))") + log("") + log("--- P&L ---") + log("Initial deposit: \(fundingPerAgent) FLOW") + log("Net equity (MOET): \(netEquityMOET) (P&L: \(pnlMOETSign)\(pnlMOETAbs), \(pnlMOETSign)\(pnlPctMOETAbs))") + log("Net equity (FLOW): \(netEquityFLOW) (P&L: \(pnlFLOWSign)\(pnlFLOWAbs), \(pnlFLOWSign)\(pnlPctFLOWAbs))") log("===========================\n") Test.assertEqual(btc_daily_2025_expectedLiquidationCount, liquidationCount) From f3e40b6f2b8a7e7bfc30f7f9d11fda4bfd17fdcf Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Wed, 18 Mar 2026 16:01:43 -0400 Subject: [PATCH 04/10] Update BTC simulation to use WBTC collateral, address review. --- cadence/tests/forked_btc_daily_2025_test.cdc | 149 ++++++++---------- cadence/tests/test_helpers.cdc | 80 +++++++++- .../tests/transactions/set_erc20_balance.cdc | 36 +++++ cadence/tests/transactions/transfer_wbtc.cdc | 30 ++++ 4 files changed, 208 insertions(+), 87 deletions(-) create mode 100644 cadence/tests/transactions/set_erc20_balance.cdc create mode 100644 cadence/tests/transactions/transfer_wbtc.cdc diff --git a/cadence/tests/forked_btc_daily_2025_test.cdc b/cadence/tests/forked_btc_daily_2025_test.cdc index d34fe8f4..5ffceaf7 100644 --- a/cadence/tests/forked_btc_daily_2025_test.cdc +++ b/cadence/tests/forked_btc_daily_2025_test.cdc @@ -24,9 +24,16 @@ access(all) let bandOracleAccount = Test.getAccount(0x6801a6222ebf784a) access(all) let whaleFlowAccount = Test.getAccount(0x92674150c9213fc9) access(all) let coaOwnerAccount = Test.getAccount(0xe467b9dd11fa00df) +// WBTC on Flow EVM: 717dae2baf7656be9a9b01dee31d571a9d4c9579 +access(all) let WBTC_TOKEN_ID = "A.1e4aa0b87d10b141.EVMVMBridgedToken_717dae2baf7656be9a9b01dee31d571a9d4c9579.Vault" +access(all) let WBTC_TYPE = CompositeType(WBTC_TOKEN_ID)! + +// 0x01b7e73CDAd95D407e8696E04194a75F19744801 + access(all) var strategyIdentifier = Type<@FlowYieldVaultsStrategiesV2.FUSDEVStrategy>().identifier -access(all) var flowTokenIdentifier = Type<@FlowToken.Vault>().identifier -access(all) var moetTokenIdentifier = Type<@MOET.Vault>().identifier +access(all) var wbtcTokenIdentifier = WBTC_TOKEN_ID +access(all) let MOET_TYPE = Type<@MOET.Vault>() +access(all) var moetTokenIdentifier = MOET_TYPE.identifier // ============================================================================ // PROTOCOL ADDRESSES @@ -41,7 +48,7 @@ access(all) let factoryAddress = "0xca6d7Bb03334bBf135902e1d919a5feccb461632" access(all) let morphoVaultAddress = "0xd069d989e2F44B70c65347d1853C0c67e10a9F8D" access(all) let pyusd0Address = "0x99aF3EeA856556646C98c8B9b2548Fe815240750" access(all) let moetAddress = "0x213979bB8A9A86966999b3AA797C1fcf3B967ae2" -access(all) let wflowAddress = "0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e" +access(all) let wbtcAddress = "0x717dae2baf7656be9a9b01dee31d571a9d4c9579" // ============================================================================ // STORAGE SLOT CONSTANTS @@ -50,7 +57,7 @@ access(all) let wflowAddress = "0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e" access(all) let moetBalanceSlot = 0 as UInt256 access(all) let pyusd0BalanceSlot = 1 as UInt256 access(all) let fusdevBalanceSlot = 12 as UInt256 -access(all) let wflowBalanceSlot = 3 as UInt256 +access(all) let wbtcBalanceSlot = 5 as UInt256 access(all) let morphoVaultTotalSupplySlot = 11 as UInt256 access(all) let morphoVaultTotalAssetsSlot = 15 as UInt256 @@ -61,7 +68,7 @@ access(all) let morphoVaultTotalAssetsSlot = 15 as UInt256 access(all) let numAgents = 1 -access(all) let fundingPerAgent = 1000.0 +access(all) let fundingPerAgent = 1.0 access(all) let initialPrice = btc_daily_2025_prices[0] @@ -82,29 +89,18 @@ fun setup() { tokenAAddress: pyusd0Address, tokenBAddress: morphoVaultAddress, fee: 100, - priceTokenBPerTokenA: feeAdjustedPrice(1.0, fee: 100, reverse: false), + priceTokenBPerTokenA: 1.0, tokenABalanceSlot: pyusd0BalanceSlot, tokenBBalanceSlot: fusdevBalanceSlot, signer: coaOwnerAccount ) - setPoolToPrice( - factoryAddress: factoryAddress, - tokenAAddress: pyusd0Address, - tokenBAddress: wflowAddress, - fee: 3000, - priceTokenBPerTokenA: feeAdjustedPrice(1.0, fee: 3000, reverse: false), - tokenABalanceSlot: pyusd0BalanceSlot, - tokenBBalanceSlot: wflowBalanceSlot, - signer: coaOwnerAccount - ) - setPoolToPrice( factoryAddress: factoryAddress, tokenAAddress: moetAddress, tokenBAddress: morphoVaultAddress, fee: 100, - priceTokenBPerTokenA: feeAdjustedPrice(1.0, fee: 100, reverse: false), + priceTokenBPerTokenA: 1.0, tokenABalanceSlot: moetBalanceSlot, tokenBBalanceSlot: fusdevBalanceSlot, signer: coaOwnerAccount @@ -115,17 +111,12 @@ fun setup() { tokenAAddress: moetAddress, tokenBAddress: pyusd0Address, fee: 100, - priceTokenBPerTokenA: feeAdjustedPrice(1.0, fee: 100, reverse: false), + priceTokenBPerTokenA: 1.0, tokenABalanceSlot: moetBalanceSlot, tokenBBalanceSlot: pyusd0BalanceSlot, signer: coaOwnerAccount ) - setBandOraclePrices(signer: bandOracleAccount, symbolPrices: { - "FLOW": 1.0, - "USD": 1.0 - }) - let reserveAmount = 100_000_00.0 transferFlow(signer: whaleFlowAccount, recipient: flowALPAccount.address, amount: reserveAmount) mintMoet(signer: flowALPAccount, to: flowALPAccount.address, amount: reserveAmount, beFailed: false) @@ -138,10 +129,10 @@ fun setup() { // HELPERS // ============================================================================ -access(all) fun getFlowCollateralFromPosition(pid: UInt64): UFix64 { +access(all) fun getBTCCollateralFromPosition(pid: UInt64): UFix64 { let positionDetails = getPositionDetails(pid: pid, beFailed: false) for balance in positionDetails.balances { - if balance.vaultType == Type<@FlowToken.Vault>() { + if balance.vaultType == WBTC_TYPE { if balance.direction == FlowALPv0.BalanceDirection.Credit { return balance.balance } @@ -153,7 +144,7 @@ access(all) fun getFlowCollateralFromPosition(pid: UInt64): UFix64 { access(all) fun getMOETDebtFromPosition(pid: UInt64): UFix64 { let positionDetails = getPositionDetails(pid: pid, beFailed: false) for balance in positionDetails.balances { - if balance.vaultType == Type<@MOET.Vault>() { + if balance.vaultType == MOET_TYPE { if balance.direction == FlowALPv0.BalanceDirection.Debit { return balance.balance } @@ -162,10 +153,6 @@ access(all) fun getMOETDebtFromPosition(pid: UInt64): UFix64 { return 0.0 } -access(all) fun normalizePrice(_ absolutePrice: UFix64): UFix64 { - return absolutePrice / initialPrice -} - /// Compute deterministic YT (ERC4626 vault share) price at a given day. /// price = 1.0 + yieldAPR * (day / 365) access(all) fun ytPriceAtDay(_ day: Int): UFix64 { @@ -173,46 +160,33 @@ access(all) fun ytPriceAtDay(_ day: Int): UFix64 { } /// Update all prices for a given simulation day. -access(all) fun applyPriceTick(flowPrice: UFix64, ytPrice: UFix64, user: Test.TestAccount) { +access(all) fun applyPriceTick(btcPrice: UFix64, ytPrice: UFix64, user: Test.TestAccount) { setBandOraclePrices(signer: bandOracleAccount, symbolPrices: { - "FLOW": flowPrice, + "BTC": btcPrice, "USD": 1.0 }) setPoolToPrice( factoryAddress: factoryAddress, - tokenAAddress: wflowAddress, + tokenAAddress: wbtcAddress, tokenBAddress: pyusd0Address, fee: 3000, - priceTokenBPerTokenA: feeAdjustedPrice(UFix128(flowPrice), fee: 3000, reverse: true), - tokenABalanceSlot: wflowBalanceSlot, + priceTokenBPerTokenA: UFix128(btcPrice), + tokenABalanceSlot: wbtcBalanceSlot, tokenBBalanceSlot: pyusd0BalanceSlot, signer: coaOwnerAccount ) - if flowPrice < 1.0 { - setPoolToPrice( - factoryAddress: factoryAddress, - tokenAAddress: moetAddress, - tokenBAddress: morphoVaultAddress, - fee: 100, - priceTokenBPerTokenA: feeAdjustedPrice(UFix128(ytPrice), fee: 100, reverse: true), - tokenABalanceSlot: moetBalanceSlot, - tokenBBalanceSlot: fusdevBalanceSlot, - signer: coaOwnerAccount - ) - } else { - setPoolToPrice( - factoryAddress: factoryAddress, - tokenAAddress: moetAddress, - tokenBAddress: morphoVaultAddress, - fee: 100, - priceTokenBPerTokenA: feeAdjustedPrice(UFix128(ytPrice), fee: 100, reverse: false), - tokenABalanceSlot: moetBalanceSlot, - tokenBBalanceSlot: fusdevBalanceSlot, - signer: coaOwnerAccount - ) - } + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: moetAddress, + tokenBAddress: morphoVaultAddress, + fee: 100, + priceTokenBPerTokenA: UFix128(ytPrice), + tokenABalanceSlot: moetBalanceSlot, + tokenBBalanceSlot: fusdevBalanceSlot, + signer: coaOwnerAccount + ) setVaultSharePrice( vaultAddress: morphoVaultAddress, @@ -239,10 +213,14 @@ fun test_BtcDaily2025_DailyRebalancing() { let pids: [UInt64] = [] let vaultIds: [UInt64] = [] + // Apply initial pricing + applyPriceTick(btcPrice: initialPrice, ytPrice: ytPriceAtDay(0), user: coaOwnerAccount) + var i = 0 while i < numAgents { let user = Test.createAccount() - transferFlow(signer: whaleFlowAccount, recipient: user.address, amount: fundingPerAgent) + transferFlow(signer: whaleFlowAccount, recipient: user.address, amount: 10.0) + mintBTC(signer: user, amount: fundingPerAgent) grantBeta(flowYieldVaultsAccount, user) setVaultSharePrice( @@ -258,7 +236,7 @@ fun test_BtcDaily2025_DailyRebalancing() { createYieldVault( signer: user, strategyIdentifier: strategyIdentifier, - vaultIdentifier: flowTokenIdentifier, + vaultIdentifier: wbtcTokenIdentifier, amount: fundingPerAgent, beFailed: false ) @@ -277,7 +255,7 @@ fun test_BtcDaily2025_DailyRebalancing() { log("\n=== BTC DAILY 2025 SIMULATION ===") log("Agents: \(numAgents)") - log("Funding per agent: \(fundingPerAgent) FLOW") + log("Funding per agent: \(fundingPerAgent) BTC (~\(fundingPerAgent * initialPrice) MOET)") log("Duration: \(btc_daily_2025_durationDays) days") log("Price points: \(prices.length)") log("Initial BTC price: $\(prices[0])") @@ -285,7 +263,7 @@ fun test_BtcDaily2025_DailyRebalancing() { var liquidationCount = 0 var rebalanceCount = 0 - var previousNormalizedPrice = 1.0 + var previousBTCPrice = initialPrice var lowestPrice = initialPrice var highestPrice = initialPrice var lowestHF = 100.0 @@ -295,7 +273,6 @@ fun test_BtcDaily2025_DailyRebalancing() { var day = 0 while day < prices.length { let absolutePrice = prices[day] - let normalizedPrice = normalizePrice(absolutePrice) let ytPrice = ytPriceAtDay(day) if absolutePrice < lowestPrice { @@ -313,7 +290,7 @@ fun test_BtcDaily2025_DailyRebalancing() { } // Apply all price updates - applyPriceTick(flowPrice: normalizedPrice, ytPrice: ytPrice, user: users[0]) + applyPriceTick(btcPrice: absolutePrice, ytPrice: ytPrice, user: users[0]) // Potentially rebalance all agents (not forced) var a = 0 @@ -327,19 +304,19 @@ fun test_BtcDaily2025_DailyRebalancing() { // Check health factors a = 0 while a < numAgents { - let flowCollateral = getFlowCollateralFromPosition(pid: pids[a]) - let flowCollateralValue = flowCollateral * normalizedPrice + let btcCollateral = getBTCCollateralFromPosition(pid: pids[a]) + let btcCollateralValue = btcCollateral * absolutePrice let debt = getMOETDebtFromPosition(pid: pids[a]) if debt > 0.0 { - let hf = flowCollateralValue / debt + let hf = btcCollateralValue / debt if hf < lowestHF { lowestHF = hf } // Log weekly + at price extremes if a == 0 && (day % 7 == 0 || absolutePrice == lowestPrice || absolutePrice == highestPrice) { - log(" [day \(day)] \(dates[day]) price=$\(absolutePrice) ratio=\(normalizedPrice) yt=\(ytPrice) HF=\(hf) collateral=\(flowCollateralValue) debt=\(debt)") + log(" [day \(day)] \(dates[day]) price=$\(absolutePrice) yt=\(ytPrice) HF=\(hf) collateral=\(btcCollateralValue) debt=\(debt)") } if hf < 1.0 { @@ -350,23 +327,22 @@ fun test_BtcDaily2025_DailyRebalancing() { a = a + 1 } - previousNormalizedPrice = normalizedPrice + previousBTCPrice = absolutePrice day = day + 1 } // Final state - let finalFlowCollateral = getFlowCollateralFromPosition(pid: pids[0]) + let finalBTCCollateral = getBTCCollateralFromPosition(pid: pids[0]) let finalDebt = getMOETDebtFromPosition(pid: pids[0]) let finalYieldTokens = getAutoBalancerBalance(id: vaultIds[0])! - let finalNormalizedPrice = normalizePrice(prices[prices.length - 1]) let finalYtPrice = ytPriceAtDay(prices.length - 1) - let finalHF = (finalFlowCollateral * finalNormalizedPrice) / finalDebt + let finalHF = (finalBTCCollateral * previousBTCPrice) / finalDebt // P&L: net equity = collateral_value + yt_value - debt (all in stablecoin/MOET terms) - let collateralValueMOET = finalFlowCollateral * finalNormalizedPrice + let collateralValueMOET = finalBTCCollateral * previousBTCPrice let ytValueMOET = finalYieldTokens * finalYtPrice let netEquityMOET = collateralValueMOET + ytValueMOET - finalDebt - let initialDepositMOET = fundingPerAgent + let initialDepositMOET = fundingPerAgent * initialPrice // UFix64 is unsigned, so track sign separately to avoid underflow let moetProfit = netEquityMOET >= initialDepositMOET @@ -374,14 +350,15 @@ fun test_BtcDaily2025_DailyRebalancing() { let pnlPctMOETAbs = pnlMOETAbs / initialDepositMOET let pnlMOETSign = moetProfit ? "+" : "-" - let netEquityFLOW = netEquityMOET / finalNormalizedPrice - let flowProfit = netEquityFLOW >= fundingPerAgent - let pnlFLOWAbs = flowProfit ? (netEquityFLOW - fundingPerAgent) : (fundingPerAgent - netEquityFLOW) - let pnlPctFLOWAbs = pnlFLOWAbs / fundingPerAgent - let pnlFLOWSign = flowProfit ? "+" : "-" + let netEquityBTC = netEquityMOET / previousBTCPrice + let btcProfit = netEquityBTC >= fundingPerAgent + let pnlBTCAbs = btcProfit ? (netEquityBTC - fundingPerAgent) : (fundingPerAgent - netEquityBTC) + let pnlPctBTCAbs = pnlBTCAbs / fundingPerAgent + let pnlBTCSign = btcProfit ? "+" : "-" - let priceUp = finalNormalizedPrice >= 1.0 - let priceChangePctAbs = priceUp ? (finalNormalizedPrice - 1.0) : (1.0 - finalNormalizedPrice) + let priceUp = previousBTCPrice >= initialPrice + let priceChangeAbs = priceUp ? (previousBTCPrice - initialPrice) : (initialPrice - previousBTCPrice) + let priceChangePct = priceChangeAbs / initialPrice let priceChangeSign = priceUp ? "+" : "-" log("\n=== SIMULATION RESULTS ===") @@ -395,19 +372,19 @@ fun test_BtcDaily2025_DailyRebalancing() { log("Lowest BTC price: $\(lowestPrice)") log("Highest BTC price: $\(highestPrice)") log("Final BTC price: $\(prices[prices.length - 1])") - log("Price change: \(priceChangeSign)\(priceChangePctAbs)") + log("Price change: \(priceChangeSign)\(priceChangePct)") log("") log("--- Position ---") log("Lowest HF observed: \(lowestHF)") log("Final HF (agent 0): \(finalHF)") - log("Final collateral: \(finalFlowCollateral) FLOW (value: \(collateralValueMOET) MOET)") + log("Final collateral: \(finalBTCCollateral) BTC (value: \(collateralValueMOET) MOET)") log("Final debt: \(finalDebt) MOET") log("Final yield tokens: \(finalYieldTokens) (value: \(ytValueMOET) MOET @ yt=\(finalYtPrice))") log("") log("--- P&L ---") - log("Initial deposit: \(fundingPerAgent) FLOW") + log("Initial deposit: \(fundingPerAgent) BTC (~\(fundingPerAgent * initialPrice) MOET)") log("Net equity (MOET): \(netEquityMOET) (P&L: \(pnlMOETSign)\(pnlMOETAbs), \(pnlMOETSign)\(pnlPctMOETAbs))") - log("Net equity (FLOW): \(netEquityFLOW) (P&L: \(pnlFLOWSign)\(pnlFLOWAbs), \(pnlFLOWSign)\(pnlPctFLOWAbs))") + log("Net equity (BTC): \(netEquityBTC) (P&L: \(pnlBTCSign)\(pnlBTCAbs), \(pnlBTCSign)\(pnlPctBTCAbs))") log("===========================\n") Test.assertEqual(btc_daily_2025_expectedLiquidationCount, liquidationCount) diff --git a/cadence/tests/test_helpers.cdc b/cadence/tests/test_helpers.cdc index cc5567ac..e55377ce 100644 --- a/cadence/tests/test_helpers.cdc +++ b/cadence/tests/test_helpers.cdc @@ -700,8 +700,11 @@ fun setBandOraclePrices(signer: Test.TestAccount, symbolPrices: {String: UFix64} for symbol in symbolPrices.keys { // BandOracle uses 1e9 multiplier for prices // e.g., $1.00 = 1_000_000_000, $0.50 = 500_000_000 + // Split into whole + fractional to avoid UFix64 overflow for large prices (e.g. BTC > $184) let price = symbolPrices[symbol]! - symbolsRates[symbol] = UInt64(price * 1_000_000_000.0) + let whole = UInt64(price) + let frac = price - UFix64(whole) + symbolsRates[symbol] = whole * 1_000_000_000 + UInt64(frac * 1_000_000_000.0) } let setRes = _executeTransaction( @@ -835,6 +838,81 @@ fun transferFlow(signer: Test.TestAccount, recipient: Address, amount: UFix64) { Test.expect(transferResult, Test.beSucceeded()) } +access(all) +fun setupGenericVault(signer: Test.TestAccount, vaultIdentifier: String) { + let setupResult = _executeTransaction( + "../../lib/flow-evm-bridge/cadence/transactions/example-assets/setup/setup_generic_vault.cdc", + [vaultIdentifier], + signer + ) + Test.expect(setupResult, Test.beSucceeded()) +} + +access(all) +fun transferBTC(signer: Test.TestAccount, recipient: Test.TestAccount, amount: UFix64) { + setupGenericVault( + signer: recipient, + vaultIdentifier: "A.1e4aa0b87d10b141.EVMVMBridgedToken_717dae2baf7656be9a9b01dee31d571a9d4c9579.Vault" + ) + let transferResult = _executeTransaction( + "transactions/transfer_wbtc.cdc", + [recipient.address, amount], + signer + ) + Test.expect(transferResult, Test.beSucceeded()) +} + +access(all) +fun setERC20Balance( + signer: Test.TestAccount, + tokenAddress: String, + holderAddress: String, + balanceSlot: UInt256, + amount: UInt256 +) { + let res = _executeTransaction( + "transactions/set_erc20_balance.cdc", + [tokenAddress, holderAddress, balanceSlot, amount], + signer + ) + Test.expect(res, Test.beSucceeded()) +} + +access(all) +fun mintBTC(signer: Test.TestAccount, amount: UFix64) { + let wbtcAddress = "0x717dae2baf7656be9a9b01dee31d571a9d4c9579" + let wbtcTokenId = "A.1e4aa0b87d10b141.EVMVMBridgedToken_717dae2baf7656be9a9b01dee31d571a9d4c9579.Vault" + let wbtcBalanceSlot: UInt256 = 5 + + // Ensure signer has a COA (needs some FLOW for gas) + if getCOA(signer.address) == nil { + createCOA(signer, fundingAmount: 1.0) + } + let coaAddress = getCOA(signer.address)! + + // Set wBTC ERC20 balance for the signer's COA on EVM + // wBTC has 8 decimals, so multiply amount by 1e8 + // Split to avoid UFix64 overflow for large amounts + let whole = UInt256(amount) + let frac = amount - UFix64(UInt64(amount)) + let amountSmallestUnit = whole * 100_000_000 + UInt256(frac * 100_000_000.0) + setERC20Balance( + signer: signer, + tokenAddress: wbtcAddress, + holderAddress: coaAddress, + balanceSlot: wbtcBalanceSlot, + amount: amountSmallestUnit + ) + + // Bridge wBTC from EVM to Cadence + let bridgeRes = _executeTransaction( + "../../lib/flow-evm-bridge/cadence/transactions/bridge/tokens/bridge_tokens_from_evm.cdc", + [wbtcTokenId, amountSmallestUnit], + signer + ) + Test.expect(bridgeRes, Test.beSucceeded()) +} + access(all) fun createCOA(_ signer: Test.TestAccount, fundingAmount: UFix64) { let createCOAResult = _executeTransaction( diff --git a/cadence/tests/transactions/set_erc20_balance.cdc b/cadence/tests/transactions/set_erc20_balance.cdc new file mode 100644 index 00000000..24af3423 --- /dev/null +++ b/cadence/tests/transactions/set_erc20_balance.cdc @@ -0,0 +1,36 @@ +import EVM from "MockEVM" + +/// Sets the ERC20 balanceOf for a given holder address via direct storage manipulation. +/// +/// @param tokenAddress: hex EVM address of the ERC20 contract +/// @param holderAddress: hex EVM address whose balance to set +/// @param balanceSlot: the storage slot index of the _balances mapping in the ERC20 contract +/// @param amount: the raw balance value to write (in smallest token units, e.g. satoshis for wBTC) +/// +transaction(tokenAddress: String, holderAddress: String, balanceSlot: UInt256, amount: UInt256) { + prepare(signer: auth(Storage) &Account) { + let token = EVM.addressFromString(tokenAddress) + + var addrHex = holderAddress + if holderAddress.slice(from: 0, upTo: 2) == "0x" { + addrHex = holderAddress.slice(from: 2, upTo: holderAddress.length) + } + let addrBytes = addrHex.decodeHex() + let holder = EVM.EVMAddress(bytes: addrBytes.toConstantSized<[UInt8; 20]>()!) + + let encoded = EVM.encodeABI([holder, balanceSlot]) + let slotHash = String.encodeHex(HashAlgorithm.KECCAK_256.hash(encoded)) + + let raw = amount.toBigEndianBytes() + var padded: [UInt8] = [] + var padCount = 32 - raw.length + while padCount > 0 { + padded.append(0) + padCount = padCount - 1 + } + padded = padded.concat(raw) + let valueHex = String.encodeHex(padded) + + EVM.store(target: token, slot: slotHash, value: valueHex) + } +} diff --git a/cadence/tests/transactions/transfer_wbtc.cdc b/cadence/tests/transactions/transfer_wbtc.cdc new file mode 100644 index 00000000..f3be55aa --- /dev/null +++ b/cadence/tests/transactions/transfer_wbtc.cdc @@ -0,0 +1,30 @@ +import "FungibleToken" + +transaction(recipient: Address, amount: UFix64) { + + let providerVault: auth(FungibleToken.Withdraw) &{FungibleToken.Vault} + let receiver: &{FungibleToken.Receiver} + + let storagePath: StoragePath + let receiverPath: PublicPath + + prepare(signer: auth(BorrowValue) &Account) { + self.storagePath = /storage/EVMVMBridgedToken_717dae2baf7656be9a9b01dee31d571a9d4c9579Vault + self.receiverPath = /public/EVMVMBridgedToken_717dae2baf7656be9a9b01dee31d571a9d4c9579Receiver + + self.providerVault = signer.storage.borrow( + from: self.storagePath + ) ?? panic("Could not borrow wBTC vault reference from signer") + + self.receiver = getAccount(recipient).capabilities.borrow<&{FungibleToken.Receiver}>(self.receiverPath) + ?? panic("Could not borrow receiver reference from recipient") + } + + execute { + self.receiver.deposit( + from: <-self.providerVault.withdraw( + amount: amount + ) + ) + } +} From 8e158a1ff3049a28e324f8e480c7e5e37fd0a5e6 Mon Sep 17 00:00:00 2001 From: Raymond Zhang Date: Thu, 19 Mar 2026 16:18:56 -0400 Subject: [PATCH 05/10] Add support for setting TVL and concentrated liquidity for pools. --- cadence/tests/btc_daily_2025_helpers.cdc | 10 + cadence/tests/evm_state_helpers.cdc | 30 +- cadence/tests/evm_state_helpers_test.cdc | 157 ++ cadence/tests/forked_btc_daily_2025_test.cdc | 13 +- .../scripts/simulations/btc_daily_2025.json | 1546 +++++++++-------- .../scripts/simulations/generate_fixture.py | 10 + .../set_uniswap_v3_pool_price.cdc | 132 +- 7 files changed, 1105 insertions(+), 793 deletions(-) diff --git a/cadence/tests/btc_daily_2025_helpers.cdc b/cadence/tests/btc_daily_2025_helpers.cdc index 680a56ce..ffe944c0 100644 --- a/cadence/tests/btc_daily_2025_helpers.cdc +++ b/cadence/tests/btc_daily_2025_helpers.cdc @@ -816,6 +816,16 @@ access(all) let btc_daily_2025_pools: {String: SimPool} = { size: 5000000.00000000, concentration: 0.80000000, feeTier: 0.00300000 + ), + "pyusd_btc": SimPool( + size: 10000000.00000000, + concentration: 0.80000000, + feeTier: 0.00300000 + ), + "moet_fusdev": SimPool( + size: 500000.00000000, + concentration: 0.95000000, + feeTier: 0.00010000 ) } diff --git a/cadence/tests/evm_state_helpers.cdc b/cadence/tests/evm_state_helpers.cdc index dff7a128..97f2c473 100644 --- a/cadence/tests/evm_state_helpers.cdc +++ b/cadence/tests/evm_state_helpers.cdc @@ -45,7 +45,35 @@ access(all) fun setPoolToPrice( code: Test.readFile("transactions/set_uniswap_v3_pool_price.cdc"), authorizers: [signer.address], signers: [signer], - arguments: [factoryAddress, tokenAAddress, tokenBAddress, fee, priceTokenBPerTokenA, tokenABalanceSlot, tokenBBalanceSlot] + arguments: [factoryAddress, tokenAAddress, tokenBAddress, fee, priceTokenBPerTokenA, tokenABalanceSlot, tokenBBalanceSlot, 0.0, 0.0, 1.0] + ) + ) + Test.expect(seedResult, Test.beSucceeded()) +} + +/// Set Uniswap V3 pool to a specific price with finite TVL and concentrated liquidity. +/// tvl: total pool TVL in USD (e.g. 10_000_000.0 for $10M) +/// concentration: fraction 0.0-1.0 (e.g. 0.80 for 80% of liquidity in narrow range) +/// tokenBPriceUSD: USD price of tokenB (e.g. 1.0 for stablecoins) +access(all) fun setPoolToPriceWithTVL( + factoryAddress: String, + tokenAAddress: String, + tokenBAddress: String, + fee: UInt64, + priceTokenBPerTokenA: UFix128, + tokenABalanceSlot: UInt256, + tokenBBalanceSlot: UInt256, + tvl: UFix64, + concentration: UFix64, + tokenBPriceUSD: UFix64, + signer: Test.TestAccount +) { + let seedResult = Test.executeTransaction( + Test.Transaction( + code: Test.readFile("transactions/set_uniswap_v3_pool_price.cdc"), + authorizers: [signer.address], + signers: [signer], + arguments: [factoryAddress, tokenAAddress, tokenBAddress, fee, priceTokenBPerTokenA, tokenABalanceSlot, tokenBBalanceSlot, tvl, concentration, tokenBPriceUSD] ) ) Test.expect(seedResult, Test.beSucceeded()) diff --git a/cadence/tests/evm_state_helpers_test.cdc b/cadence/tests/evm_state_helpers_test.cdc index a432fcd7..51707183 100644 --- a/cadence/tests/evm_state_helpers_test.cdc +++ b/cadence/tests/evm_state_helpers_test.cdc @@ -154,3 +154,160 @@ fun test_ERC4626PriceSetAndDeposit() { log("Multiplier \(multiplier): expected=\(expectedShares) actual=\(fusdevBalance)") } } + +access(all) +fun test_ConcentratedLiquiditySlippage() { + // Use fee=500 (0.05%) to get a fresh pool with no pre-existing state + let testFee: UInt64 = 500 + + // --- Baseline: infinite liquidity --- + Test.reset(to: snapshot) + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: wflowAddress, + tokenBAddress: pyusd0Address, + fee: testFee, + priceTokenBPerTokenA: 1.0, + tokenABalanceSlot: wflowBalanceSlot, + tokenBBalanceSlot: pyusd0BalanceSlot, + signer: testAccount + ) + + let infBefore = getBalance(address: testAccount.address, vaultPublicPath: pyusd0PublicPath)! + var swapRes = Test.executeTransaction( + Test.Transaction( + code: Test.readFile("transactions/execute_univ3_swap.cdc"), + authorizers: [testAccount.address], + signers: [testAccount], + arguments: [factoryAddress, routerAddress, quoterAddress, wflowAddress, pyusd0Address, testFee, 10000.0] + ) + ) + Test.expect(swapRes, Test.beSucceeded()) + let infiniteOutput = getBalance(address: testAccount.address, vaultPublicPath: pyusd0PublicPath)! - infBefore + + // --- Concentrated: $500K TVL, 80% concentration --- + Test.reset(to: snapshot) + setPoolToPriceWithTVL( + factoryAddress: factoryAddress, + tokenAAddress: wflowAddress, + tokenBAddress: pyusd0Address, + fee: testFee, + priceTokenBPerTokenA: 1.0, + tokenABalanceSlot: wflowBalanceSlot, + tokenBBalanceSlot: pyusd0BalanceSlot, + tvl: 500_000.0, + concentration: 0.80, + tokenBPriceUSD: 1.0, + signer: testAccount + ) + + let concBefore = getBalance(address: testAccount.address, vaultPublicPath: pyusd0PublicPath)! + swapRes = Test.executeTransaction( + Test.Transaction( + code: Test.readFile("transactions/execute_univ3_swap.cdc"), + authorizers: [testAccount.address], + signers: [testAccount], + arguments: [factoryAddress, routerAddress, quoterAddress, wflowAddress, pyusd0Address, testFee, 10000.0] + ) + ) + Test.expect(swapRes, Test.beSucceeded()) + let concOutput = getBalance(address: testAccount.address, vaultPublicPath: pyusd0PublicPath)! - concBefore + + let slippage10k_500k = (infiniteOutput - concOutput) / infiniteOutput + log("10K swap — Infinite: \(infiniteOutput), $500K TVL/80%: \(concOutput), Slippage: \(slippage10k_500k)") + + Test.assert(concOutput < infiniteOutput, message: "Concentrated pool should produce less output than infinite liquidity") + Test.assert(slippage10k_500k > 0.0001, message: "Expected meaningful slippage for $10K against $500K TVL") + + // --- More TVL ($5M) = less slippage --- + Test.reset(to: snapshot) + setPoolToPriceWithTVL( + factoryAddress: factoryAddress, + tokenAAddress: wflowAddress, + tokenBAddress: pyusd0Address, + fee: testFee, + priceTokenBPerTokenA: 1.0, + tokenABalanceSlot: wflowBalanceSlot, + tokenBBalanceSlot: pyusd0BalanceSlot, + tvl: 5_000_000.0, + concentration: 0.80, + tokenBPriceUSD: 1.0, + signer: testAccount + ) + + let bigBefore = getBalance(address: testAccount.address, vaultPublicPath: pyusd0PublicPath)! + swapRes = Test.executeTransaction( + Test.Transaction( + code: Test.readFile("transactions/execute_univ3_swap.cdc"), + authorizers: [testAccount.address], + signers: [testAccount], + arguments: [factoryAddress, routerAddress, quoterAddress, wflowAddress, pyusd0Address, testFee, 10000.0] + ) + ) + Test.expect(swapRes, Test.beSucceeded()) + let bigTvlOutput = getBalance(address: testAccount.address, vaultPublicPath: pyusd0PublicPath)! - bigBefore + + let slippage10k_5m = (infiniteOutput - bigTvlOutput) / infiniteOutput + log("10K swap — $5M TVL/80%: \(bigTvlOutput), Slippage: \(slippage10k_5m)") + + Test.assert(bigTvlOutput > concOutput, message: "Larger TVL should yield more output (less slippage)") + Test.assert(slippage10k_5m < slippage10k_500k, message: "Larger TVL should have lower slippage %") + + // --- Smaller swap ($1K) = less slippage --- + Test.reset(to: snapshot) + setPoolToPriceWithTVL( + factoryAddress: factoryAddress, + tokenAAddress: wflowAddress, + tokenBAddress: pyusd0Address, + fee: testFee, + priceTokenBPerTokenA: 1.0, + tokenABalanceSlot: wflowBalanceSlot, + tokenBBalanceSlot: pyusd0BalanceSlot, + tvl: 500_000.0, + concentration: 0.80, + tokenBPriceUSD: 1.0, + signer: testAccount + ) + + let smallBefore = getBalance(address: testAccount.address, vaultPublicPath: pyusd0PublicPath)! + swapRes = Test.executeTransaction( + Test.Transaction( + code: Test.readFile("transactions/execute_univ3_swap.cdc"), + authorizers: [testAccount.address], + signers: [testAccount], + arguments: [factoryAddress, routerAddress, quoterAddress, wflowAddress, pyusd0Address, testFee, 1000.0] + ) + ) + Test.expect(swapRes, Test.beSucceeded()) + let smallOutput = getBalance(address: testAccount.address, vaultPublicPath: pyusd0PublicPath)! - smallBefore + + // Get 1K infinite baseline for comparison + Test.reset(to: snapshot) + setPoolToPrice( + factoryAddress: factoryAddress, + tokenAAddress: wflowAddress, + tokenBAddress: pyusd0Address, + fee: testFee, + priceTokenBPerTokenA: 1.0, + tokenABalanceSlot: wflowBalanceSlot, + tokenBBalanceSlot: pyusd0BalanceSlot, + signer: testAccount + ) + + let smallInfBefore = getBalance(address: testAccount.address, vaultPublicPath: pyusd0PublicPath)! + swapRes = Test.executeTransaction( + Test.Transaction( + code: Test.readFile("transactions/execute_univ3_swap.cdc"), + authorizers: [testAccount.address], + signers: [testAccount], + arguments: [factoryAddress, routerAddress, quoterAddress, wflowAddress, pyusd0Address, testFee, 1000.0] + ) + ) + Test.expect(swapRes, Test.beSucceeded()) + let smallInfOutput = getBalance(address: testAccount.address, vaultPublicPath: pyusd0PublicPath)! - smallInfBefore + + let slippage1k_500k = (smallInfOutput - smallOutput) / smallInfOutput + log("1K swap — $500K TVL/80%: \(smallOutput), Slippage: \(slippage1k_500k)") + + Test.assert(slippage1k_500k < slippage10k_500k, message: "Smaller swap should have less slippage %") +} diff --git a/cadence/tests/forked_btc_daily_2025_test.cdc b/cadence/tests/forked_btc_daily_2025_test.cdc index 5ffceaf7..32b5fa39 100644 --- a/cadence/tests/forked_btc_daily_2025_test.cdc +++ b/cadence/tests/forked_btc_daily_2025_test.cdc @@ -166,7 +166,10 @@ access(all) fun applyPriceTick(btcPrice: UFix64, ytPrice: UFix64, user: Test.Tes "USD": 1.0 }) - setPoolToPrice( + let btcPool = btc_daily_2025_pools["pyusd_btc"]! + let ytPool = btc_daily_2025_pools["moet_fusdev"]! + + setPoolToPriceWithTVL( factoryAddress: factoryAddress, tokenAAddress: wbtcAddress, tokenBAddress: pyusd0Address, @@ -174,10 +177,13 @@ access(all) fun applyPriceTick(btcPrice: UFix64, ytPrice: UFix64, user: Test.Tes priceTokenBPerTokenA: UFix128(btcPrice), tokenABalanceSlot: wbtcBalanceSlot, tokenBBalanceSlot: pyusd0BalanceSlot, + tvl: btcPool.size, + concentration: btcPool.concentration, + tokenBPriceUSD: 1.0, signer: coaOwnerAccount ) - setPoolToPrice( + setPoolToPriceWithTVL( factoryAddress: factoryAddress, tokenAAddress: moetAddress, tokenBAddress: morphoVaultAddress, @@ -185,6 +191,9 @@ access(all) fun applyPriceTick(btcPrice: UFix64, ytPrice: UFix64, user: Test.Tes priceTokenBPerTokenA: UFix128(ytPrice), tokenABalanceSlot: moetBalanceSlot, tokenBBalanceSlot: fusdevBalanceSlot, + tvl: ytPool.size, + concentration: ytPool.concentration, + tokenBPriceUSD: ytPrice, signer: coaOwnerAccount ) diff --git a/cadence/tests/scripts/simulations/btc_daily_2025.json b/cadence/tests/scripts/simulations/btc_daily_2025.json index 05e127cb..1a37d105 100644 --- a/cadence/tests/scripts/simulations/btc_daily_2025.json +++ b/cadence/tests/scripts/simulations/btc_daily_2025.json @@ -1,771 +1,781 @@ { - "scenario": "btc_daily_2025", - "duration_days": 365, - "btc_prices": [ - 94419.76, - 96886.88, - 98107.43, - 98236.23, - 98314.96, - 102078.09, - 96922.71, - 95043.52, - 92484.04, - 94701.46, - 94566.59, - 94488.44, - 94516.53, - 96534.04, - 100504.49, - 99756.91, - 104462.04, - 104408.07, - 101089.61, - 102016.66, - 106146.26, - 103653.07, - 103960.17, - 104819.48, - 104714.65, - 102682.5, - 102087.69, - 101332.48, - 103703.21, - 104735.3, - 102405.03, - 100655.91, - 97688.98, - 101405.42, - 97871.82, - 96615.44, - 96593.3, - 96529.08, - 96482.45, - 96500.09, - 97437.56, - 95747.43, - 97885.86, - 96623.87, - 97508.97, - 97580.35, - 96175.03, - 95773.38, - 95539.54, - 96635.61, - 98333.94, - 96125.54, - 96577.76, - 96273.92, - 91418.17, - 88736.17, - 84347.02, - 84704.22, - 84373.01, - 86031.91, - 94248.35, - 86065.67, - 87222.19, - 90623.56, - 89961.73, - 86742.68, - 86154.59, - 80601.04, - 78532.0, - 82862.21, - 83722.36, - 81066.7, - 83969.1, - 84343.11, - 82579.69, - 84075.69, - 82718.5, - 86854.23, - 84167.19, - 84043.25, - 83832.49, - 86054.37, - 87498.91, - 87471.7, - 86900.89, - 87177.1, - 84353.15, - 82597.58, - 82334.52, - 82548.91, - 85169.17, - 82485.71, - 83102.83, - 83843.8, - 83504.8, - 78214.48, - 79235.33, - 76271.95, - 82573.95, - 79626.14, - 83404.84, - 85287.11, - 83684.98, - 84542.39, - 83668.99, - 84033.87, - 84895.75, - 84450.81, - 85063.41, - 85174.3, - 87518.91, - 93441.89, - 93699.11, - 93943.79, - 94720.5, - 94646.93, - 93754.85, - 94978.75, - 94284.79, - 94207.31, - 96492.34, - 96910.07, - 95891.8, - 94315.97, - 94748.05, - 96802.48, - 97032.32, - 103241.46, - 102970.85, - 104696.33, - 104106.36, - 102812.95, - 104169.81, - 103539.42, - 103744.64, - 103489.29, - 103191.09, - 106446.01, - 105606.18, - 106791.09, - 109678.08, - 111673.28, - 107287.8, - 107791.16, - 109035.39, - 109440.37, - 108994.64, - 107802.32, - 105641.76, - 103998.57, - 104638.09, - 105652.1, - 105881.53, - 105432.47, - 104731.98, - 101575.95, - 104390.35, - 105615.63, - 105793.65, - 110294.1, - 110257.24, - 108686.63, - 105929.05, - 106090.97, - 105472.41, - 105552.03, - 106796.76, - 104601.12, - 104883.33, - 104684.29, - 103309.6, - 102257.41, - 100987.14, - 105577.77, - 106045.63, - 107361.26, - 106960.0, - 107088.43, - 107327.7, - 108385.57, - 107135.33, - 105698.28, - 108859.32, - 109647.98, - 108034.34, - 108231.18, - 109232.07, - 108299.85, - 108950.28, - 111326.55, - 115987.21, - 117516.99, - 117435.23, - 119116.12, - 119849.71, - 117777.19, - 118738.51, - 119289.84, - 118003.22, - 117939.98, - 117300.79, - 117439.54, - 119995.42, - 118754.96, - 118368.0, - 117635.88, - 117947.37, - 119448.49, - 117924.47, - 117922.15, - 117831.19, - 115758.2, - 113320.09, - 112526.91, - 114217.67, - 115071.88, - 114141.44, - 115028.0, - 117496.9, - 116688.73, - 116500.36, - 119306.76, - 118731.45, - 120172.91, - 123344.06, - 118359.58, - 117398.35, - 117491.35, - 117453.06, - 116252.31, - 112831.18, - 114274.74, - 112419.03, - 116874.09, - 115374.33, - 113458.43, - 110124.35, - 111802.66, - 111222.06, - 112544.8, - 108410.84, - 108808.07, - 108236.71, - 109250.59, - 111200.59, - 111723.21, - 110723.6, - 110650.99, - 110224.7, - 111167.62, - 112071.43, - 111530.55, - 113955.36, - 115507.54, - 116101.58, - 115950.51, - 115407.65, - 115444.87, - 116843.18, - 116468.51, - 117137.2, - 115688.86, - 115721.96, - 115306.1, - 112748.51, - 112014.5, - 113328.63, - 109049.29, - 109712.83, - 109681.94, - 112122.64, - 114400.39, - 114056.08, - 118648.93, - 120681.26, - 122266.53, - 122425.43, - 123513.47, - 124752.53, - 121451.38, - 123354.87, - 121705.58, - 113214.37, - 110807.88, - 115169.76, - 115271.08, - 113118.67, - 110783.17, - 108186.04, - 106467.79, - 107198.27, - 108666.71, - 110588.93, - 108476.89, - 107688.59, - 110069.72, - 111033.92, - 111641.73, - 114472.44, - 114119.32, - 112956.17, - 110055.3, - 108305.55, - 109556.16, - 110064.02, - 110639.63, - 106547.52, - 101590.52, - 103891.84, - 101301.29, - 103372.41, - 102282.11, - 104719.64, - 105996.6, - 102997.47, - 101663.19, - 99697.49, - 94397.79, - 95549.15, - 94177.07, - 92093.87, - 92948.87, - 91465.99, - 86631.9, - 85090.69, - 84648.36, - 86805.01, - 88270.56, - 87341.89, - 90518.37, - 91285.37, - 90919.27, - 90851.75, - 90394.31, - 86321.57, - 91350.21, - 93527.8, - 92141.62, - 89387.75, - 89272.37, - 90405.64, - 90640.21, - 92691.71, - 92020.95, - 92511.34, - 90270.41, - 90298.71, - 88175.18, - 86419.78, - 87843.99, - 86143.76, - 85462.51, - 88103.38, - 88344.0, - 88621.75, - 88490.02, - 87414.0, - 87611.96, - 87234.74, - 87301.43, - 87802.16, - 87835.83, - 87138.14, - 88430.14, - 87508.83 - ], - "dates": [ - "2025-01-01", - "2025-01-02", - "2025-01-03", - "2025-01-04", - "2025-01-05", - "2025-01-06", - "2025-01-07", - "2025-01-08", - "2025-01-09", - "2025-01-10", - "2025-01-11", - "2025-01-12", - "2025-01-13", - "2025-01-14", - "2025-01-15", - "2025-01-16", - "2025-01-17", - "2025-01-18", - "2025-01-19", - "2025-01-20", - "2025-01-21", - "2025-01-22", - "2025-01-23", - "2025-01-24", - "2025-01-25", - "2025-01-26", - "2025-01-27", - "2025-01-28", - "2025-01-29", - "2025-01-30", - "2025-01-31", - "2025-02-01", - "2025-02-02", - "2025-02-03", - "2025-02-04", - "2025-02-05", - "2025-02-06", - "2025-02-07", - "2025-02-08", - "2025-02-09", - "2025-02-10", - "2025-02-11", - "2025-02-12", - "2025-02-13", - "2025-02-14", - "2025-02-15", - "2025-02-16", - "2025-02-17", - "2025-02-18", - "2025-02-19", - "2025-02-20", - "2025-02-21", - "2025-02-22", - "2025-02-23", - "2025-02-24", - "2025-02-25", - "2025-02-26", - "2025-02-27", - "2025-02-28", - "2025-03-01", - "2025-03-02", - "2025-03-03", - "2025-03-04", - "2025-03-05", - "2025-03-06", - "2025-03-07", - "2025-03-08", - "2025-03-09", - "2025-03-10", - "2025-03-11", - "2025-03-12", - "2025-03-13", - "2025-03-14", - "2025-03-15", - "2025-03-16", - "2025-03-17", - "2025-03-18", - "2025-03-19", - "2025-03-20", - "2025-03-21", - "2025-03-22", - "2025-03-23", - "2025-03-24", - "2025-03-25", - "2025-03-26", - "2025-03-27", - "2025-03-28", - "2025-03-29", - "2025-03-30", - "2025-03-31", - "2025-04-01", - "2025-04-02", - "2025-04-03", - "2025-04-04", - "2025-04-05", - "2025-04-06", - "2025-04-07", - "2025-04-08", - "2025-04-09", - "2025-04-10", - "2025-04-11", - "2025-04-12", - "2025-04-13", - "2025-04-14", - "2025-04-15", - "2025-04-16", - "2025-04-17", - "2025-04-18", - "2025-04-19", - "2025-04-20", - "2025-04-21", - "2025-04-22", - "2025-04-23", - "2025-04-24", - "2025-04-25", - "2025-04-26", - "2025-04-27", - "2025-04-28", - "2025-04-29", - "2025-04-30", - "2025-05-01", - "2025-05-02", - "2025-05-03", - "2025-05-04", - "2025-05-05", - "2025-05-06", - "2025-05-07", - "2025-05-08", - "2025-05-09", - "2025-05-10", - "2025-05-11", - "2025-05-12", - "2025-05-13", - "2025-05-14", - "2025-05-15", - "2025-05-16", - "2025-05-17", - "2025-05-18", - "2025-05-19", - "2025-05-20", - "2025-05-21", - "2025-05-22", - "2025-05-23", - "2025-05-24", - "2025-05-25", - "2025-05-26", - "2025-05-27", - "2025-05-28", - "2025-05-29", - "2025-05-30", - "2025-05-31", - "2025-06-01", - "2025-06-02", - "2025-06-03", - "2025-06-04", - "2025-06-05", - "2025-06-06", - "2025-06-07", - "2025-06-08", - "2025-06-09", - "2025-06-10", - "2025-06-11", - "2025-06-12", - "2025-06-13", - "2025-06-14", - "2025-06-15", - "2025-06-16", - "2025-06-17", - "2025-06-18", - "2025-06-19", - "2025-06-20", - "2025-06-21", - "2025-06-22", - "2025-06-23", - "2025-06-24", - "2025-06-25", - "2025-06-26", - "2025-06-27", - "2025-06-28", - "2025-06-29", - "2025-06-30", - "2025-07-01", - "2025-07-02", - "2025-07-03", - "2025-07-04", - "2025-07-05", - "2025-07-06", - "2025-07-07", - "2025-07-08", - "2025-07-09", - "2025-07-10", - "2025-07-11", - "2025-07-12", - "2025-07-13", - "2025-07-14", - "2025-07-15", - "2025-07-16", - "2025-07-17", - "2025-07-18", - "2025-07-19", - "2025-07-20", - "2025-07-21", - "2025-07-22", - "2025-07-23", - "2025-07-24", - "2025-07-25", - "2025-07-26", - "2025-07-27", - "2025-07-28", - "2025-07-29", - "2025-07-30", - "2025-07-31", - "2025-08-01", - "2025-08-02", - "2025-08-03", - "2025-08-04", - "2025-08-05", - "2025-08-06", - "2025-08-07", - "2025-08-08", - "2025-08-09", - "2025-08-10", - "2025-08-11", - "2025-08-12", - "2025-08-13", - "2025-08-14", - "2025-08-15", - "2025-08-16", - "2025-08-17", - "2025-08-18", - "2025-08-19", - "2025-08-20", - "2025-08-21", - "2025-08-22", - "2025-08-23", - "2025-08-24", - "2025-08-25", - "2025-08-26", - "2025-08-27", - "2025-08-28", - "2025-08-29", - "2025-08-30", - "2025-08-31", - "2025-09-01", - "2025-09-02", - "2025-09-03", - "2025-09-04", - "2025-09-05", - "2025-09-06", - "2025-09-07", - "2025-09-08", - "2025-09-09", - "2025-09-10", - "2025-09-11", - "2025-09-12", - "2025-09-13", - "2025-09-14", - "2025-09-15", - "2025-09-16", - "2025-09-17", - "2025-09-18", - "2025-09-19", - "2025-09-20", - "2025-09-21", - "2025-09-22", - "2025-09-23", - "2025-09-24", - "2025-09-25", - "2025-09-26", - "2025-09-27", - "2025-09-28", - "2025-09-29", - "2025-09-30", - "2025-10-01", - "2025-10-02", - "2025-10-03", - "2025-10-04", - "2025-10-05", - "2025-10-06", - "2025-10-07", - "2025-10-08", - "2025-10-09", - "2025-10-10", - "2025-10-11", - "2025-10-12", - "2025-10-13", - "2025-10-14", - "2025-10-15", - "2025-10-16", - "2025-10-17", - "2025-10-18", - "2025-10-19", - "2025-10-20", - "2025-10-21", - "2025-10-22", - "2025-10-23", - "2025-10-24", - "2025-10-25", - "2025-10-26", - "2025-10-27", - "2025-10-28", - "2025-10-29", - "2025-10-30", - "2025-10-31", - "2025-11-01", - "2025-11-02", - "2025-11-03", - "2025-11-04", - "2025-11-05", - "2025-11-06", - "2025-11-07", - "2025-11-08", - "2025-11-09", - "2025-11-10", - "2025-11-11", - "2025-11-12", - "2025-11-13", - "2025-11-14", - "2025-11-15", - "2025-11-16", - "2025-11-17", - "2025-11-18", - "2025-11-19", - "2025-11-20", - "2025-11-21", - "2025-11-22", - "2025-11-23", - "2025-11-24", - "2025-11-25", - "2025-11-26", - "2025-11-27", - "2025-11-28", - "2025-11-29", - "2025-11-30", - "2025-12-01", - "2025-12-02", - "2025-12-03", - "2025-12-04", - "2025-12-05", - "2025-12-06", - "2025-12-07", - "2025-12-08", - "2025-12-09", - "2025-12-10", - "2025-12-11", - "2025-12-12", - "2025-12-13", - "2025-12-14", - "2025-12-15", - "2025-12-16", - "2025-12-17", - "2025-12-18", - "2025-12-19", - "2025-12-20", - "2025-12-21", - "2025-12-22", - "2025-12-23", - "2025-12-24", - "2025-12-25", - "2025-12-26", - "2025-12-27", - "2025-12-28", - "2025-12-29", - "2025-12-30", - "2025-12-31" - ], - "agents": [ - { - "count": 1, - "initial_hf": 1.15, - "rebalancing_hf": 1.05, - "target_hf": 1.08, - "debt_per_agent": 133333, - "total_system_debt": 20000000 - } - ], - "pools": { - "moet_yt": { - "size": 500000, - "concentration": 0.95, - "fee_tier": 0.0005 + "scenario": "btc_daily_2025", + "duration_days": 365, + "btc_prices": [ + 94419.76, + 96886.88, + 98107.43, + 98236.23, + 98314.96, + 102078.09, + 96922.71, + 95043.52, + 92484.04, + 94701.46, + 94566.59, + 94488.44, + 94516.53, + 96534.04, + 100504.49, + 99756.91, + 104462.04, + 104408.07, + 101089.61, + 102016.66, + 106146.26, + 103653.07, + 103960.17, + 104819.48, + 104714.65, + 102682.5, + 102087.69, + 101332.48, + 103703.21, + 104735.3, + 102405.03, + 100655.91, + 97688.98, + 101405.42, + 97871.82, + 96615.44, + 96593.3, + 96529.08, + 96482.45, + 96500.09, + 97437.56, + 95747.43, + 97885.86, + 96623.87, + 97508.97, + 97580.35, + 96175.03, + 95773.38, + 95539.54, + 96635.61, + 98333.94, + 96125.54, + 96577.76, + 96273.92, + 91418.17, + 88736.17, + 84347.02, + 84704.22, + 84373.01, + 86031.91, + 94248.35, + 86065.67, + 87222.19, + 90623.56, + 89961.73, + 86742.68, + 86154.59, + 80601.04, + 78532.0, + 82862.21, + 83722.36, + 81066.7, + 83969.1, + 84343.11, + 82579.69, + 84075.69, + 82718.5, + 86854.23, + 84167.19, + 84043.25, + 83832.49, + 86054.37, + 87498.91, + 87471.7, + 86900.89, + 87177.1, + 84353.15, + 82597.58, + 82334.52, + 82548.91, + 85169.17, + 82485.71, + 83102.83, + 83843.8, + 83504.8, + 78214.48, + 79235.33, + 76271.95, + 82573.95, + 79626.14, + 83404.84, + 85287.11, + 83684.98, + 84542.39, + 83668.99, + 84033.87, + 84895.75, + 84450.81, + 85063.41, + 85174.3, + 87518.91, + 93441.89, + 93699.11, + 93943.79, + 94720.5, + 94646.93, + 93754.85, + 94978.75, + 94284.79, + 94207.31, + 96492.34, + 96910.07, + 95891.8, + 94315.97, + 94748.05, + 96802.48, + 97032.32, + 103241.46, + 102970.85, + 104696.33, + 104106.36, + 102812.95, + 104169.81, + 103539.42, + 103744.64, + 103489.29, + 103191.09, + 106446.01, + 105606.18, + 106791.09, + 109678.08, + 111673.28, + 107287.8, + 107791.16, + 109035.39, + 109440.37, + 108994.64, + 107802.32, + 105641.76, + 103998.57, + 104638.09, + 105652.1, + 105881.53, + 105432.47, + 104731.98, + 101575.95, + 104390.35, + 105615.63, + 105793.65, + 110294.1, + 110257.24, + 108686.63, + 105929.05, + 106090.97, + 105472.41, + 105552.03, + 106796.76, + 104601.12, + 104883.33, + 104684.29, + 103309.6, + 102257.41, + 100987.14, + 105577.77, + 106045.63, + 107361.26, + 106960.0, + 107088.43, + 107327.7, + 108385.57, + 107135.33, + 105698.28, + 108859.32, + 109647.98, + 108034.34, + 108231.18, + 109232.07, + 108299.85, + 108950.28, + 111326.55, + 115987.21, + 117516.99, + 117435.23, + 119116.12, + 119849.71, + 117777.19, + 118738.51, + 119289.84, + 118003.22, + 117939.98, + 117300.79, + 117439.54, + 119995.42, + 118754.96, + 118368.0, + 117635.88, + 117947.37, + 119448.49, + 117924.47, + 117922.15, + 117831.19, + 115758.2, + 113320.09, + 112526.91, + 114217.67, + 115071.88, + 114141.44, + 115028.0, + 117496.9, + 116688.73, + 116500.36, + 119306.76, + 118731.45, + 120172.91, + 123344.06, + 118359.58, + 117398.35, + 117491.35, + 117453.06, + 116252.31, + 112831.18, + 114274.74, + 112419.03, + 116874.09, + 115374.33, + 113458.43, + 110124.35, + 111802.66, + 111222.06, + 112544.8, + 108410.84, + 108808.07, + 108236.71, + 109250.59, + 111200.59, + 111723.21, + 110723.6, + 110650.99, + 110224.7, + 111167.62, + 112071.43, + 111530.55, + 113955.36, + 115507.54, + 116101.58, + 115950.51, + 115407.65, + 115444.87, + 116843.18, + 116468.51, + 117137.2, + 115688.86, + 115721.96, + 115306.1, + 112748.51, + 112014.5, + 113328.63, + 109049.29, + 109712.83, + 109681.94, + 112122.64, + 114400.39, + 114056.08, + 118648.93, + 120681.26, + 122266.53, + 122425.43, + 123513.47, + 124752.53, + 121451.38, + 123354.87, + 121705.58, + 113214.37, + 110807.88, + 115169.76, + 115271.08, + 113118.67, + 110783.17, + 108186.04, + 106467.79, + 107198.27, + 108666.71, + 110588.93, + 108476.89, + 107688.59, + 110069.72, + 111033.92, + 111641.73, + 114472.44, + 114119.32, + 112956.17, + 110055.3, + 108305.55, + 109556.16, + 110064.02, + 110639.63, + 106547.52, + 101590.52, + 103891.84, + 101301.29, + 103372.41, + 102282.11, + 104719.64, + 105996.6, + 102997.47, + 101663.19, + 99697.49, + 94397.79, + 95549.15, + 94177.07, + 92093.87, + 92948.87, + 91465.99, + 86631.9, + 85090.69, + 84648.36, + 86805.01, + 88270.56, + 87341.89, + 90518.37, + 91285.37, + 90919.27, + 90851.75, + 90394.31, + 86321.57, + 91350.21, + 93527.8, + 92141.62, + 89387.75, + 89272.37, + 90405.64, + 90640.21, + 92691.71, + 92020.95, + 92511.34, + 90270.41, + 90298.71, + 88175.18, + 86419.78, + 87843.99, + 86143.76, + 85462.51, + 88103.38, + 88344.0, + 88621.75, + 88490.02, + 87414.0, + 87611.96, + 87234.74, + 87301.43, + 87802.16, + 87835.83, + 87138.14, + 88430.14, + 87508.83 + ], + "dates": [ + "2025-01-01", + "2025-01-02", + "2025-01-03", + "2025-01-04", + "2025-01-05", + "2025-01-06", + "2025-01-07", + "2025-01-08", + "2025-01-09", + "2025-01-10", + "2025-01-11", + "2025-01-12", + "2025-01-13", + "2025-01-14", + "2025-01-15", + "2025-01-16", + "2025-01-17", + "2025-01-18", + "2025-01-19", + "2025-01-20", + "2025-01-21", + "2025-01-22", + "2025-01-23", + "2025-01-24", + "2025-01-25", + "2025-01-26", + "2025-01-27", + "2025-01-28", + "2025-01-29", + "2025-01-30", + "2025-01-31", + "2025-02-01", + "2025-02-02", + "2025-02-03", + "2025-02-04", + "2025-02-05", + "2025-02-06", + "2025-02-07", + "2025-02-08", + "2025-02-09", + "2025-02-10", + "2025-02-11", + "2025-02-12", + "2025-02-13", + "2025-02-14", + "2025-02-15", + "2025-02-16", + "2025-02-17", + "2025-02-18", + "2025-02-19", + "2025-02-20", + "2025-02-21", + "2025-02-22", + "2025-02-23", + "2025-02-24", + "2025-02-25", + "2025-02-26", + "2025-02-27", + "2025-02-28", + "2025-03-01", + "2025-03-02", + "2025-03-03", + "2025-03-04", + "2025-03-05", + "2025-03-06", + "2025-03-07", + "2025-03-08", + "2025-03-09", + "2025-03-10", + "2025-03-11", + "2025-03-12", + "2025-03-13", + "2025-03-14", + "2025-03-15", + "2025-03-16", + "2025-03-17", + "2025-03-18", + "2025-03-19", + "2025-03-20", + "2025-03-21", + "2025-03-22", + "2025-03-23", + "2025-03-24", + "2025-03-25", + "2025-03-26", + "2025-03-27", + "2025-03-28", + "2025-03-29", + "2025-03-30", + "2025-03-31", + "2025-04-01", + "2025-04-02", + "2025-04-03", + "2025-04-04", + "2025-04-05", + "2025-04-06", + "2025-04-07", + "2025-04-08", + "2025-04-09", + "2025-04-10", + "2025-04-11", + "2025-04-12", + "2025-04-13", + "2025-04-14", + "2025-04-15", + "2025-04-16", + "2025-04-17", + "2025-04-18", + "2025-04-19", + "2025-04-20", + "2025-04-21", + "2025-04-22", + "2025-04-23", + "2025-04-24", + "2025-04-25", + "2025-04-26", + "2025-04-27", + "2025-04-28", + "2025-04-29", + "2025-04-30", + "2025-05-01", + "2025-05-02", + "2025-05-03", + "2025-05-04", + "2025-05-05", + "2025-05-06", + "2025-05-07", + "2025-05-08", + "2025-05-09", + "2025-05-10", + "2025-05-11", + "2025-05-12", + "2025-05-13", + "2025-05-14", + "2025-05-15", + "2025-05-16", + "2025-05-17", + "2025-05-18", + "2025-05-19", + "2025-05-20", + "2025-05-21", + "2025-05-22", + "2025-05-23", + "2025-05-24", + "2025-05-25", + "2025-05-26", + "2025-05-27", + "2025-05-28", + "2025-05-29", + "2025-05-30", + "2025-05-31", + "2025-06-01", + "2025-06-02", + "2025-06-03", + "2025-06-04", + "2025-06-05", + "2025-06-06", + "2025-06-07", + "2025-06-08", + "2025-06-09", + "2025-06-10", + "2025-06-11", + "2025-06-12", + "2025-06-13", + "2025-06-14", + "2025-06-15", + "2025-06-16", + "2025-06-17", + "2025-06-18", + "2025-06-19", + "2025-06-20", + "2025-06-21", + "2025-06-22", + "2025-06-23", + "2025-06-24", + "2025-06-25", + "2025-06-26", + "2025-06-27", + "2025-06-28", + "2025-06-29", + "2025-06-30", + "2025-07-01", + "2025-07-02", + "2025-07-03", + "2025-07-04", + "2025-07-05", + "2025-07-06", + "2025-07-07", + "2025-07-08", + "2025-07-09", + "2025-07-10", + "2025-07-11", + "2025-07-12", + "2025-07-13", + "2025-07-14", + "2025-07-15", + "2025-07-16", + "2025-07-17", + "2025-07-18", + "2025-07-19", + "2025-07-20", + "2025-07-21", + "2025-07-22", + "2025-07-23", + "2025-07-24", + "2025-07-25", + "2025-07-26", + "2025-07-27", + "2025-07-28", + "2025-07-29", + "2025-07-30", + "2025-07-31", + "2025-08-01", + "2025-08-02", + "2025-08-03", + "2025-08-04", + "2025-08-05", + "2025-08-06", + "2025-08-07", + "2025-08-08", + "2025-08-09", + "2025-08-10", + "2025-08-11", + "2025-08-12", + "2025-08-13", + "2025-08-14", + "2025-08-15", + "2025-08-16", + "2025-08-17", + "2025-08-18", + "2025-08-19", + "2025-08-20", + "2025-08-21", + "2025-08-22", + "2025-08-23", + "2025-08-24", + "2025-08-25", + "2025-08-26", + "2025-08-27", + "2025-08-28", + "2025-08-29", + "2025-08-30", + "2025-08-31", + "2025-09-01", + "2025-09-02", + "2025-09-03", + "2025-09-04", + "2025-09-05", + "2025-09-06", + "2025-09-07", + "2025-09-08", + "2025-09-09", + "2025-09-10", + "2025-09-11", + "2025-09-12", + "2025-09-13", + "2025-09-14", + "2025-09-15", + "2025-09-16", + "2025-09-17", + "2025-09-18", + "2025-09-19", + "2025-09-20", + "2025-09-21", + "2025-09-22", + "2025-09-23", + "2025-09-24", + "2025-09-25", + "2025-09-26", + "2025-09-27", + "2025-09-28", + "2025-09-29", + "2025-09-30", + "2025-10-01", + "2025-10-02", + "2025-10-03", + "2025-10-04", + "2025-10-05", + "2025-10-06", + "2025-10-07", + "2025-10-08", + "2025-10-09", + "2025-10-10", + "2025-10-11", + "2025-10-12", + "2025-10-13", + "2025-10-14", + "2025-10-15", + "2025-10-16", + "2025-10-17", + "2025-10-18", + "2025-10-19", + "2025-10-20", + "2025-10-21", + "2025-10-22", + "2025-10-23", + "2025-10-24", + "2025-10-25", + "2025-10-26", + "2025-10-27", + "2025-10-28", + "2025-10-29", + "2025-10-30", + "2025-10-31", + "2025-11-01", + "2025-11-02", + "2025-11-03", + "2025-11-04", + "2025-11-05", + "2025-11-06", + "2025-11-07", + "2025-11-08", + "2025-11-09", + "2025-11-10", + "2025-11-11", + "2025-11-12", + "2025-11-13", + "2025-11-14", + "2025-11-15", + "2025-11-16", + "2025-11-17", + "2025-11-18", + "2025-11-19", + "2025-11-20", + "2025-11-21", + "2025-11-22", + "2025-11-23", + "2025-11-24", + "2025-11-25", + "2025-11-26", + "2025-11-27", + "2025-11-28", + "2025-11-29", + "2025-11-30", + "2025-12-01", + "2025-12-02", + "2025-12-03", + "2025-12-04", + "2025-12-05", + "2025-12-06", + "2025-12-07", + "2025-12-08", + "2025-12-09", + "2025-12-10", + "2025-12-11", + "2025-12-12", + "2025-12-13", + "2025-12-14", + "2025-12-15", + "2025-12-16", + "2025-12-17", + "2025-12-18", + "2025-12-19", + "2025-12-20", + "2025-12-21", + "2025-12-22", + "2025-12-23", + "2025-12-24", + "2025-12-25", + "2025-12-26", + "2025-12-27", + "2025-12-28", + "2025-12-29", + "2025-12-30", + "2025-12-31" + ], + "agents": [ + { + "count": 1, + "initial_hf": 1.15, + "rebalancing_hf": 1.05, + "target_hf": 1.08, + "debt_per_agent": 133333, + "total_system_debt": 20000000 + } + ], + "pools": { + "moet_yt": { + "size": 500000, + "concentration": 0.95, + "fee_tier": 0.0005 + }, + "moet_btc": { + "size": 5000000, + "concentration": 0.8, + "fee_tier": 0.003 + }, + "pyusd_btc": { + "size": 10000000, + "concentration": 0.8, + "fee_tier": 0.003 + }, + "moet_fusdev": { + "size": 500000, + "concentration": 0.95, + "fee_tier": 0.0001 + } }, - "moet_btc": { - "size": 5000000, - "concentration": 0.8, - "fee_tier": 0.003 - } - }, - "constants": { - "btc_collateral_factor": 0.8, - "btc_liquidation_threshold": 0.85, - "yield_apr": 0.1, - "direct_mint_yt": true - }, - "expected": { - "liquidation_count": 0, - "all_agents_survive": true - }, - "notes": "Daily BTC/USD close prices from CoinMarketCap, 2025-01-01 to 2025-12-31. 365 data points." + "constants": { + "btc_collateral_factor": 0.8, + "btc_liquidation_threshold": 0.85, + "yield_apr": 0.1, + "direct_mint_yt": true + }, + "expected": { + "liquidation_count": 0, + "all_agents_survive": true + }, + "notes": "Daily BTC/USD close prices from CoinMarketCap, 2025-01-01 to 2025-12-31. 365 data points." } \ No newline at end of file diff --git a/cadence/tests/scripts/simulations/generate_fixture.py b/cadence/tests/scripts/simulations/generate_fixture.py index c42c32ae..209408ce 100644 --- a/cadence/tests/scripts/simulations/generate_fixture.py +++ b/cadence/tests/scripts/simulations/generate_fixture.py @@ -170,6 +170,16 @@ def build_fixture(daily: list[dict], scenario: str, start: str, end: str) -> dic "concentration": 0.8, "fee_tier": 0.003, }, + "pyusd_btc": { + "size": 10000000, + "concentration": 0.8, + "fee_tier": 0.003, + }, + "moet_fusdev": { + "size": 500000, + "concentration": 0.95, + "fee_tier": 0.0001, + }, }, "constants": { "btc_collateral_factor": 0.8, diff --git a/cadence/tests/transactions/set_uniswap_v3_pool_price.cdc b/cadence/tests/transactions/set_uniswap_v3_pool_price.cdc index 6b4b1b0a..01731e1e 100644 --- a/cadence/tests/transactions/set_uniswap_v3_pool_price.cdc +++ b/cadence/tests/transactions/set_uniswap_v3_pool_price.cdc @@ -48,6 +48,7 @@ access(all) fun slotToNum(_ slotHex: String): UInt256 { // Properly seed Uniswap V3 pool with STRUCTURALLY VALID state // This creates: slot0, observations, liquidity, ticks (with initialized flag), bitmap, and token balances +// Pass 0.0 for tvl and concentration to create a full-range infinite liquidity pool (useful for no slippage) transaction( factoryAddress: String, tokenAAddress: String, @@ -55,7 +56,10 @@ transaction( fee: UInt64, priceTokenBPerTokenA: UFix128, tokenABalanceSlot: UInt256, - tokenBBalanceSlot: UInt256 + tokenBBalanceSlot: UInt256, + tvl: UFix64, + concentration: UFix64, + tokenBPriceUSD: UFix64 ) { let coa: auth(EVM.Call) &EVM.CadenceOwnedAccount prepare(signer: auth(Storage) &Account) { @@ -171,9 +175,98 @@ transaction( let tickSpacing = (EVM.decodeABI(types: [Type()], data: spacingResult.data)[0] as! Int256) - // Use FULL RANGE ticks (min/max for Uniswap V3), aligned to tickSpacing - let tickLower = (-887272 as Int256) / tickSpacing * tickSpacing - let tickUpper = (887272 as Int256) / tickSpacing * tickSpacing + // Compute tick range, liquidity, and token balances based on TVL mode + let Q96: UInt256 = UInt256(1) << 96 + var tickLower: Int256 = 0 + var tickUpper: Int256 = 0 + var liquidityAmount: UInt256 = 0 + var token0Balance: UInt256 = 0 + var token1Balance: UInt256 = 0 + + if tvl > 0.0 && concentration > 0.0 && concentration < 1.0 { + // --- Concentrated liquidity mode --- + let halfWidth = 1.0 - concentration + + // sqrt(1 +/- halfWidth) via integer sqrt at 1e16 scale for 8-digit precision + let PRECISION: UInt256 = 10_000_000_000_000_000 + let SQRT_PRECISION: UInt256 = 100_000_000 + let halfWidthScaled = UInt256(UInt64(halfWidth * 100_000_000.0)) * 100_000_000 + + let upperMultNum = isqrt(PRECISION + halfWidthScaled) + let lowerMultNum = isqrt(PRECISION - halfWidthScaled) + + var sqrtPriceUpper = targetSqrtPriceX96 * upperMultNum / SQRT_PRECISION + var sqrtPriceLower = targetSqrtPriceX96 * lowerMultNum / SQRT_PRECISION + + let MAX_SQRT: UInt256 = 1461446703485210103287273052203988822378723970341 + let MIN_SQRT: UInt256 = 4295128739 + if sqrtPriceUpper > MAX_SQRT { sqrtPriceUpper = MAX_SQRT } + if sqrtPriceLower < MIN_SQRT + 1 { sqrtPriceLower = MIN_SQRT + 1 } + + let rawTickUpper = getTickAtSqrtRatio(sqrtPriceX96: sqrtPriceUpper) + let rawTickLower = getTickAtSqrtRatio(sqrtPriceX96: sqrtPriceLower) + + // Align tickLower down, tickUpper up to tickSpacing + tickLower = rawTickLower / tickSpacing * tickSpacing + if rawTickLower < 0 && rawTickLower % tickSpacing != 0 { + tickLower = tickLower - tickSpacing + } + tickUpper = rawTickUpper / tickSpacing * tickSpacing + if rawTickUpper > 0 && rawTickUpper % tickSpacing != 0 { + tickUpper = tickUpper + tickSpacing + } + + assert(tickLower < tickUpper, message: "Concentrated tick range is empty after alignment") + + let sqrtPa = getSqrtRatioAtTick(tick: tickLower) + let sqrtPb = getSqrtRatioAtTick(tick: tickUpper) + + // Convert TVL/2 from USD to token1 smallest units using token prices + let effectiveBPrice = tokenBPriceUSD > 0.0 ? tokenBPriceUSD : 1.0 + var token1PriceUSD = effectiveBPrice + if tokenAAddress >= tokenBAddress { + // token1 = tokenA; tokenA is worth priceTokenBPerTokenA * tokenBPrice in USD + token1PriceUSD = UFix64(priceTokenBPerTokenA) * effectiveBPrice + } + let tvlHalfToken1 = tvl / 2.0 / token1PriceUSD + let tvlHalfWhole = UInt256(UInt64(tvlHalfToken1)) + var tvlHalfSmallest = tvlHalfWhole + var td: UInt8 = 0 + while td < token1Decimals { + tvlHalfSmallest = tvlHalfSmallest * 10 + td = td + 1 + } + + // L = tvlHalfSmallest * Q96 / (sqrtP - sqrtPa) + let sqrtPDiffA = targetSqrtPriceX96 - sqrtPa + assert(sqrtPDiffA > 0, message: "sqrtP must be > sqrtPa for liquidity calculation") + liquidityAmount = tvlHalfSmallest * Q96 / sqrtPDiffA + + // token1 = L * (sqrtP - sqrtPa) / Q96 + token1Balance = liquidityAmount * sqrtPDiffA / Q96 + + // token0 = L * (sqrtPb - sqrtP) / sqrtPb * Q96 / sqrtP + let sqrtPDiffB = sqrtPb - targetSqrtPriceX96 + token0Balance = liquidityAmount * sqrtPDiffB / sqrtPb * Q96 / targetSqrtPriceX96 + } else { + // --- Full-range infinite liquidity mode (backward compatible) --- + tickLower = (-887272 as Int256) / tickSpacing * tickSpacing + tickUpper = (887272 as Int256) / tickSpacing * tickSpacing + liquidityAmount = 340282366920938463463374607431768211455 // 2^128 - 1 + + token0Balance = 1000000000 + var ti: UInt8 = 0 + while ti < token0Decimals { + token0Balance = token0Balance * 10 + ti = ti + 1 + } + token1Balance = 1000000000 + ti = 0 + while ti < token1Decimals { + token1Balance = token1Balance * 10 + ti = ti + 1 + } + } // Pack slot0 for Solidity storage layout // Struct fields packed right-to-left (LSB to MSB): @@ -214,8 +307,7 @@ transaction( EVM.store(target: poolAddr, slot: "2", value: zero32) EVM.store(target: poolAddr, slot: "3", value: zero32) - // --- Slot 4: liquidity = uint128 max --- - let liquidityAmount: UInt256 = 340282366920938463463374607431768211455 // 2^128 - 1 + // --- Slot 4: liquidity --- EVM.store(target: poolAddr, slot: "4", value: toHex32(liquidityAmount)) // --- Initialize boundary ticks --- @@ -296,22 +388,6 @@ transaction( EVM.store(target: poolAddr, slot: "8", value: String.encodeHex(obs0Bytes)) // --- Fund pool with token balances --- - // Calculate 1 billion tokens in each token's decimal format - var token0Balance: UInt256 = 1000000000 - var i: UInt8 = 0 - while i < token0Decimals { - token0Balance = token0Balance * 10 - i = i + 1 - } - - var token1Balance: UInt256 = 1000000000 - i = 0 - while i < token1Decimals { - token1Balance = token1Balance * 10 - i = i + 1 - } - - // Set token balances (padded to 32 bytes) let token0BalanceSlotComputed = computeBalanceOfSlot(holderAddress: poolAddress, balanceSlot: token0BalanceSlot) EVM.store(target: token0, slot: token0BalanceSlotComputed, value: toHex32(token0Balance)) @@ -670,6 +746,18 @@ access(all) fun bytesToUInt256(_ bytes: [UInt8]): UInt256 { return result } +/// Integer square root via Newton's method. Returns floor(sqrt(x)). +access(all) fun isqrt(_ x: UInt256): UInt256 { + if x == 0 { return 0 } + var z = x + var y = (z + 1) / 2 + while y < z { + z = y + y = (z + x / z) / 2 + } + return z +} + access(all) fun getTokenDecimals(evmContractAddress: EVM.EVMAddress): UInt8 { let zeroAddress = EVM.addressFromString("0x0000000000000000000000000000000000000000") let callResult = EVM.dryCall( From 2731eb0007a9ecf272a159b3205c5bdd1e4e8d94 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Fri, 27 Mar 2026 09:35:34 -0700 Subject: [PATCH 06/10] Add scheduled transaction reset --- cadence/tests/forked_btc_daily_2025_test.cdc | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/cadence/tests/forked_btc_daily_2025_test.cdc b/cadence/tests/forked_btc_daily_2025_test.cdc index 32b5fa39..d9731767 100644 --- a/cadence/tests/forked_btc_daily_2025_test.cdc +++ b/cadence/tests/forked_btc_daily_2025_test.cdc @@ -84,6 +84,8 @@ access(all) fun setup() { deployContractsForFork() + resetTransactionScheduler() + setPoolToPrice( factoryAddress: factoryAddress, tokenAAddress: pyusd0Address, @@ -141,18 +143,6 @@ access(all) fun getBTCCollateralFromPosition(pid: UInt64): UFix64 { return 0.0 } -access(all) fun getMOETDebtFromPosition(pid: UInt64): UFix64 { - let positionDetails = getPositionDetails(pid: pid, beFailed: false) - for balance in positionDetails.balances { - if balance.vaultType == MOET_TYPE { - if balance.direction == FlowALPv0.BalanceDirection.Debit { - return balance.balance - } - } - } - return 0.0 -} - /// Compute deterministic YT (ERC4626 vault share) price at a given day. /// price = 1.0 + yieldAPR * (day / 365) access(all) fun ytPriceAtDay(_ day: Int): UFix64 { From a11d47b9966cad9a861f5d7c88a9b40241d05e08 Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Fri, 27 Mar 2026 11:06:00 -0700 Subject: [PATCH 07/10] add actual rebalance events --- cadence/tests/forked_btc_daily_2025_test.cdc | 25 ++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/cadence/tests/forked_btc_daily_2025_test.cdc b/cadence/tests/forked_btc_daily_2025_test.cdc index d9731767..33b93252 100644 --- a/cadence/tests/forked_btc_daily_2025_test.cdc +++ b/cadence/tests/forked_btc_daily_2025_test.cdc @@ -12,6 +12,7 @@ import "FlowToken" import "MOET" import "FlowYieldVaultsStrategiesV2" import "FlowALPv0" +import "DeFiActions" // ============================================================================ @@ -261,11 +262,12 @@ fun test_BtcDaily2025_DailyRebalancing() { log("Notes: \(btc_daily_2025_notes)") var liquidationCount = 0 - var rebalanceCount = 0 var previousBTCPrice = initialPrice var lowestPrice = initialPrice var highestPrice = initialPrice var lowestHF = 100.0 + var prevVaultRebalanceCount = 0 + var prevPositionRebalanceCount = 0 let startTimestamp = getCurrentBlockTimestamp() @@ -298,7 +300,14 @@ fun test_BtcDaily2025_DailyRebalancing() { rebalancePosition(signer: flowALPAccount, pid: pids[a], force: false, beFailed: false) a = a + 1 } - rebalanceCount = rebalanceCount + 1 + + // Count actual rebalances that occurred this day + let currentVaultRebalanceCount = Test.eventsOfType(Type()).length + let currentPositionRebalanceCount = Test.eventsOfType(Type()).length + let dayVaultRebalances = currentVaultRebalanceCount - prevVaultRebalanceCount + let dayPositionRebalances = currentPositionRebalanceCount - prevPositionRebalanceCount + prevVaultRebalanceCount = currentVaultRebalanceCount + prevPositionRebalanceCount = currentPositionRebalanceCount // Check health factors a = 0 @@ -315,7 +324,7 @@ fun test_BtcDaily2025_DailyRebalancing() { // Log weekly + at price extremes if a == 0 && (day % 7 == 0 || absolutePrice == lowestPrice || absolutePrice == highestPrice) { - log(" [day \(day)] \(dates[day]) price=$\(absolutePrice) yt=\(ytPrice) HF=\(hf) collateral=\(btcCollateralValue) debt=\(debt)") + log(" [day \(day)] \(dates[day]) price=$\(absolutePrice) yt=\(ytPrice) HF=\(hf) vaultRebalances=\(dayVaultRebalances) positionRebalances=\(dayPositionRebalances)") } if hf < 1.0 { @@ -330,6 +339,12 @@ fun test_BtcDaily2025_DailyRebalancing() { day = day + 1 } + // Count actual rebalance events (not just attempts) + let vaultRebalanceEvents = Test.eventsOfType(Type()) + let positionRebalanceEvents = Test.eventsOfType(Type()) + let vaultRebalanceCount = vaultRebalanceEvents.length + let positionRebalanceCount = positionRebalanceEvents.length + // Final state let finalBTCCollateral = getBTCCollateralFromPosition(pid: pids[0]) let finalDebt = getMOETDebtFromPosition(pid: pids[0]) @@ -363,7 +378,9 @@ fun test_BtcDaily2025_DailyRebalancing() { log("\n=== SIMULATION RESULTS ===") log("Agents: \(numAgents)") log("Days simulated: \(prices.length)") - log("Rebalance events: \(rebalanceCount)") + log("Rebalance attempts: \(prices.length * numAgents)") + log("Vault rebalances: \(vaultRebalanceCount)") + log("Position rebalances: \(positionRebalanceCount)") log("Liquidation count: \(liquidationCount)") log("") log("--- Price ---") From 7a6e09ff93e21cbecf1b9a4234975b03bb26605f Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Fri, 27 Mar 2026 13:47:05 -0700 Subject: [PATCH 08/10] adding before and after HF and VR to logs --- cadence/tests/forked_btc_daily_2025_test.cdc | 97 +++++++++++++++++--- cadence/tests/test_helpers.cdc | 7 ++ 2 files changed, 91 insertions(+), 13 deletions(-) diff --git a/cadence/tests/forked_btc_daily_2025_test.cdc b/cadence/tests/forked_btc_daily_2025_test.cdc index 33b93252..5346b2f5 100644 --- a/cadence/tests/forked_btc_daily_2025_test.cdc +++ b/cadence/tests/forked_btc_daily_2025_test.cdc @@ -77,6 +77,30 @@ access(all) let yieldAPR = btc_daily_2025_constants.yieldAPR access(all) let daysPerYear = 365.0 access(all) let secondsPerDay = 86400.0 +// Collateral factor for BTC in the FlowALP pool. +// This determines how much of the collateral value counts toward borrowing capacity. +// effectiveCollateral = collateralValue * collateralFactor +// effectiveHF = effectiveCollateral / debt +// +// Position rebalance thresholds (in effective HF terms): +// - minHealth = 1.1: triggers top-up from source when effectiveHF < 1.1 +// - targetHealth = 1.3: rebalance aims to restore effectiveHF to 1.3 +// - maxHealth = 1.5: triggers push to sink when effectiveHF > 1.5 +// See: lib/FlowALP/cadence/contracts/FlowALPv0.cdc:544-546 (InternalPosition.init defaults) +access(all) let collateralFactor = 0.8 + +// Vault (AutoBalancer) rebalance thresholds. +// The vault tracks yield token value relative to historical deposits. +// vaultRatio = currentValue / valueOfDeposits +// +// Vault rebalance triggers when: +// - vaultRatio < 0.95 (lowerThreshold): pulls from source to buy more yield tokens +// - vaultRatio > 1.05 (upperThreshold): pushes to sink to sell yield tokens +// See: lib/FlowALP/FlowActions/cadence/contracts/interfaces/DeFiActions.cdc:763-764 +// See: cadence/contracts/FlowYieldVaultsStrategiesV2.cdc:706-707 (FUSDEVStrategy defaults) +access(all) let vaultLowerThreshold = 0.95 +access(all) let vaultUpperThreshold = 1.05 + // ============================================================================ // SETUP // ============================================================================ @@ -260,6 +284,10 @@ fun test_BtcDaily2025_DailyRebalancing() { log("Price points: \(prices.length)") log("Initial BTC price: $\(prices[0])") log("Notes: \(btc_daily_2025_notes)") + log("") + log("Rebalance Triggers:") + log(" HF (Position): triggers when HF < 1.1 or HF > 1.5, rebalances to HF = 1.3") + log(" VR (Vault): triggers when VR < 0.95 or VR > 1.05, rebalances to VR ~ 1.0") var liquidationCount = 0 var previousBTCPrice = initialPrice @@ -293,8 +321,36 @@ fun test_BtcDaily2025_DailyRebalancing() { // Apply all price updates applyPriceTick(btcPrice: absolutePrice, ytPrice: ytPrice, user: users[0]) - // Potentially rebalance all agents (not forced) + // Calculate HF BEFORE rebalancing to see pre-rebalance state + // effectiveHF = (collateralValue * collateralFactor) / debt + // This is what determines rebalance triggers (minHealth=1.1, maxHealth=1.5) + var preRebalanceHF: UFix64 = 0.0 var a = 0 + while a < numAgents { + if a == 0 { + let btcCollateral = getBTCCollateralFromPosition(pid: pids[a]) + let btcCollateralValue = btcCollateral * absolutePrice + let effectiveCollateral = btcCollateralValue * collateralFactor + let debt = getMOETDebtFromPosition(pid: pids[a]) + if debt > 0.0 { + preRebalanceHF = effectiveCollateral / debt + } + } + a = a + 1 + } + + // Calculate vault ratio BEFORE rebalancing + // vaultRatio = currentValue / valueOfDeposits + // Triggers when ratio < 0.95 or ratio > 1.05 + var preVaultRatio: UFix64 = 1.0 + let preCurrentValue = getAutoBalancerCurrentValue(id: vaultIds[0]) ?? 0.0 + let preValueOfDeposits = getAutoBalancerValueOfDeposits(id: vaultIds[0]) ?? 0.0 + if preValueOfDeposits > 0.0 { + preVaultRatio = preCurrentValue / preValueOfDeposits + } + + // Potentially rebalance all agents (not forced) + a = 0 while a < numAgents { rebalanceYieldVault(signer: flowYieldVaultsAccount, id: vaultIds[a], force: false, beFailed: false) rebalancePosition(signer: flowALPAccount, pid: pids[a], force: false, beFailed: false) @@ -309,27 +365,41 @@ fun test_BtcDaily2025_DailyRebalancing() { prevVaultRebalanceCount = currentVaultRebalanceCount prevPositionRebalanceCount = currentPositionRebalanceCount - // Check health factors + // Calculate vault ratio AFTER rebalancing + var postVaultRatio: UFix64 = 1.0 + let postCurrentValue = getAutoBalancerCurrentValue(id: vaultIds[0]) ?? 0.0 + let postValueOfDeposits = getAutoBalancerValueOfDeposits(id: vaultIds[0]) ?? 0.0 + if postValueOfDeposits > 0.0 { + postVaultRatio = postCurrentValue / postValueOfDeposits + } + + // Calculate HF AFTER rebalancing a = 0 while a < numAgents { let btcCollateral = getBTCCollateralFromPosition(pid: pids[a]) let btcCollateralValue = btcCollateral * absolutePrice + let effectiveCollateral = btcCollateralValue * collateralFactor let debt = getMOETDebtFromPosition(pid: pids[a]) if debt > 0.0 { - let hf = btcCollateralValue / debt - if hf < lowestHF { - lowestHF = hf + let postRebalanceHF = effectiveCollateral / debt + // Track lowest HF (use pre-rebalance to capture the actual low point) + if preRebalanceHF < lowestHF && preRebalanceHF > 0.0 { + lowestHF = preRebalanceHF } // Log weekly + at price extremes + // Show both pre and post values to see rebalance effects: + // HF: position health factor (triggers at <1.1 or >1.5) + // VR: vault ratio (triggers at <0.95 or >1.05) if a == 0 && (day % 7 == 0 || absolutePrice == lowestPrice || absolutePrice == highestPrice) { - log(" [day \(day)] \(dates[day]) price=$\(absolutePrice) yt=\(ytPrice) HF=\(hf) vaultRebalances=\(dayVaultRebalances) positionRebalances=\(dayPositionRebalances)") + log(" [day \(day)] \(dates[day]) price=$\(absolutePrice) yt=\(ytPrice) HF=\(preRebalanceHF)->\(postRebalanceHF) VR=\(preVaultRatio)->\(postVaultRatio) vaultRebalances=\(dayVaultRebalances) positionRebalances=\(dayPositionRebalances)") } - if hf < 1.0 { + // Liquidation occurs when effectiveHF < 1.0 (check pre-rebalance) + if preRebalanceHF < 1.0 && preRebalanceHF > 0.0 { liquidationCount = liquidationCount + 1 - log(" *** LIQUIDATION agent=\(a) on day \(day) (\(dates[day]))! HF=\(hf) ***") + log(" *** LIQUIDATION agent=\(a) on day \(day) (\(dates[day]))! HF=\(preRebalanceHF) ***") } } a = a + 1 @@ -350,7 +420,8 @@ fun test_BtcDaily2025_DailyRebalancing() { let finalDebt = getMOETDebtFromPosition(pid: pids[0]) let finalYieldTokens = getAutoBalancerBalance(id: vaultIds[0])! let finalYtPrice = ytPriceAtDay(prices.length - 1) - let finalHF = (finalBTCCollateral * previousBTCPrice) / finalDebt + // Compute effective HF to match contract's rebalancing logic + let finalEffectiveHF = (finalBTCCollateral * previousBTCPrice * collateralFactor) / finalDebt // P&L: net equity = collateral_value + yt_value - debt (all in stablecoin/MOET terms) let collateralValueMOET = finalBTCCollateral * previousBTCPrice @@ -390,9 +461,9 @@ fun test_BtcDaily2025_DailyRebalancing() { log("Final BTC price: $\(prices[prices.length - 1])") log("Price change: \(priceChangeSign)\(priceChangePct)") log("") - log("--- Position ---") + log("--- Position (effective HF with collateralFactor=\(collateralFactor)) ---") log("Lowest HF observed: \(lowestHF)") - log("Final HF (agent 0): \(finalHF)") + log("Final HF (agent 0): \(finalEffectiveHF)") log("Final collateral: \(finalBTCCollateral) BTC (value: \(collateralValueMOET) MOET)") log("Final debt: \(finalDebt) MOET") log("Final yield tokens: \(finalYieldTokens) (value: \(ytValueMOET) MOET @ yt=\(finalYtPrice))") @@ -404,8 +475,8 @@ fun test_BtcDaily2025_DailyRebalancing() { log("===========================\n") Test.assertEqual(btc_daily_2025_expectedLiquidationCount, liquidationCount) - Test.assert(finalHF > 1.0, message: "Expected final HF > 1.0 but got \(finalHF)") - Test.assert(lowestHF > 1.0, message: "Expected lowest HF > 1.0 but got \(lowestHF)") + Test.assert(finalEffectiveHF > 1.0, message: "Expected final effective HF > 1.0 but got \(finalEffectiveHF)") + Test.assert(lowestHF > 1.0, message: "Expected lowest effective HF > 1.0 but got \(lowestHF)") log("=== TEST PASSED: Zero liquidations over 1 year of real BTC prices (\(numAgents) agents) ===") } diff --git a/cadence/tests/test_helpers.cdc b/cadence/tests/test_helpers.cdc index 9b06651c..38b50547 100644 --- a/cadence/tests/test_helpers.cdc +++ b/cadence/tests/test_helpers.cdc @@ -553,6 +553,13 @@ fun getAutoBalancerCurrentValue(id: UInt64): UFix64? { return res.returnValue as! UFix64? } +access(all) +fun getAutoBalancerValueOfDeposits(id: UInt64): UFix64? { + let res = _executeScript("../scripts/flow-yield-vaults/get_auto_balancer_value_of_deposits_by_id.cdc", [id]) + Test.expect(res, Test.beSucceeded()) + return res.returnValue as! UFix64? +} + access(all) fun getPositionDetails(pid: UInt64, beFailed: Bool): FlowALPv0.PositionDetails { let res = _executeScript("../../lib/FlowALP/cadence/scripts/flow-alp/position_details.cdc", From d98f11f20ed37daf710e6e4c96ce0fa5fe7b1bd9 Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Fri, 27 Mar 2026 14:12:24 -0700 Subject: [PATCH 09/10] explain the resetScheduledTransaction --- cadence/tests/forked_btc_daily_2025_test.cdc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cadence/tests/forked_btc_daily_2025_test.cdc b/cadence/tests/forked_btc_daily_2025_test.cdc index 5346b2f5..98a3037c 100644 --- a/cadence/tests/forked_btc_daily_2025_test.cdc +++ b/cadence/tests/forked_btc_daily_2025_test.cdc @@ -109,6 +109,12 @@ access(all) fun setup() { deployContractsForFork() + // it removes all the existing scheduled transactions, it is useful to speed up the tests, + // because otherwise, the existing 41 scheduled transactions will be executed every time we + // move the time forward, and each taking about 40ms, which make the tests very slow + // after 365 days of simulation, the total delay would be 10mins. + // resetting the transaction scheduler will not affect the test results, new scheduled + // transaction can also be created by the tests. resetTransactionScheduler() setPoolToPrice( From 9e76cf81a04891af63ed7af19a2476cd9bcd037e Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Fri, 27 Mar 2026 14:41:19 -0700 Subject: [PATCH 10/10] speed up by combining scripts --- .../get_auto_balancer_metrics_by_id.cdc | 16 ++++++++++++++++ ...t_auto_balancer_value_of_deposits_by_id.cdc | 9 +++++++++ cadence/tests/forked_btc_daily_2025_test.cdc | 18 ++++++++---------- cadence/tests/test_helpers.cdc | 9 +++++++++ 4 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 cadence/scripts/flow-yield-vaults/get_auto_balancer_metrics_by_id.cdc create mode 100644 cadence/scripts/flow-yield-vaults/get_auto_balancer_value_of_deposits_by_id.cdc diff --git a/cadence/scripts/flow-yield-vaults/get_auto_balancer_metrics_by_id.cdc b/cadence/scripts/flow-yield-vaults/get_auto_balancer_metrics_by_id.cdc new file mode 100644 index 00000000..59080343 --- /dev/null +++ b/cadence/scripts/flow-yield-vaults/get_auto_balancer_metrics_by_id.cdc @@ -0,0 +1,16 @@ +import "FlowYieldVaultsAutoBalancers" + +/// Returns both currentValue and valueOfDeposits for the AutoBalancer in a single script call. +/// This reduces script call overhead when both values are needed. +/// +/// Returns: [currentValue, valueOfDeposits] or nil if AutoBalancer doesn't exist +/// +access(all) +fun main(id: UInt64): [UFix64]? { + if let autoBalancer = FlowYieldVaultsAutoBalancers.borrowAutoBalancer(id: id) { + let currentValue = autoBalancer.currentValue() ?? 0.0 + let valueOfDeposits = autoBalancer.valueOfDeposits() + return [currentValue, valueOfDeposits] + } + return nil +} diff --git a/cadence/scripts/flow-yield-vaults/get_auto_balancer_value_of_deposits_by_id.cdc b/cadence/scripts/flow-yield-vaults/get_auto_balancer_value_of_deposits_by_id.cdc new file mode 100644 index 00000000..68b51f10 --- /dev/null +++ b/cadence/scripts/flow-yield-vaults/get_auto_balancer_value_of_deposits_by_id.cdc @@ -0,0 +1,9 @@ +import "FlowYieldVaultsAutoBalancers" + +/// Returns the value of deposits tracked by the AutoBalancer related to the provided YieldVault ID or `nil` if none exists +/// This is the historical cumulative value used to compute the rebalance ratio: currentValue / valueOfDeposits +/// +access(all) +fun main(id: UInt64): UFix64? { + return FlowYieldVaultsAutoBalancers.borrowAutoBalancer(id: id)?.valueOfDeposits() ?? nil +} diff --git a/cadence/tests/forked_btc_daily_2025_test.cdc b/cadence/tests/forked_btc_daily_2025_test.cdc index 98a3037c..1615a630 100644 --- a/cadence/tests/forked_btc_daily_2025_test.cdc +++ b/cadence/tests/forked_btc_daily_2025_test.cdc @@ -345,14 +345,13 @@ fun test_BtcDaily2025_DailyRebalancing() { a = a + 1 } - // Calculate vault ratio BEFORE rebalancing + // Calculate vault ratio BEFORE rebalancing (single script call for efficiency) // vaultRatio = currentValue / valueOfDeposits // Triggers when ratio < 0.95 or ratio > 1.05 var preVaultRatio: UFix64 = 1.0 - let preCurrentValue = getAutoBalancerCurrentValue(id: vaultIds[0]) ?? 0.0 - let preValueOfDeposits = getAutoBalancerValueOfDeposits(id: vaultIds[0]) ?? 0.0 - if preValueOfDeposits > 0.0 { - preVaultRatio = preCurrentValue / preValueOfDeposits + let preMetrics = getAutoBalancerMetrics(id: vaultIds[0]) ?? [0.0, 0.0] + if preMetrics[1] > 0.0 { + preVaultRatio = preMetrics[0] / preMetrics[1] } // Potentially rebalance all agents (not forced) @@ -371,12 +370,11 @@ fun test_BtcDaily2025_DailyRebalancing() { prevVaultRebalanceCount = currentVaultRebalanceCount prevPositionRebalanceCount = currentPositionRebalanceCount - // Calculate vault ratio AFTER rebalancing + // Calculate vault ratio AFTER rebalancing (single script call for efficiency) var postVaultRatio: UFix64 = 1.0 - let postCurrentValue = getAutoBalancerCurrentValue(id: vaultIds[0]) ?? 0.0 - let postValueOfDeposits = getAutoBalancerValueOfDeposits(id: vaultIds[0]) ?? 0.0 - if postValueOfDeposits > 0.0 { - postVaultRatio = postCurrentValue / postValueOfDeposits + let postMetrics = getAutoBalancerMetrics(id: vaultIds[0]) ?? [0.0, 0.0] + if postMetrics[1] > 0.0 { + postVaultRatio = postMetrics[0] / postMetrics[1] } // Calculate HF AFTER rebalancing diff --git a/cadence/tests/test_helpers.cdc b/cadence/tests/test_helpers.cdc index 38b50547..3b6f0364 100644 --- a/cadence/tests/test_helpers.cdc +++ b/cadence/tests/test_helpers.cdc @@ -560,6 +560,15 @@ fun getAutoBalancerValueOfDeposits(id: UInt64): UFix64? { return res.returnValue as! UFix64? } +/// Returns [currentValue, valueOfDeposits] for the AutoBalancer in a single script call. +/// This is more efficient than calling getAutoBalancerCurrentValue and getAutoBalancerValueOfDeposits separately. +access(all) +fun getAutoBalancerMetrics(id: UInt64): [UFix64]? { + let res = _executeScript("../scripts/flow-yield-vaults/get_auto_balancer_metrics_by_id.cdc", [id]) + Test.expect(res, Test.beSucceeded()) + return res.returnValue as! [UFix64]? +} + access(all) fun getPositionDetails(pid: UInt64, beFailed: Bool): FlowALPv0.PositionDetails { let res = _executeScript("../../lib/FlowALP/cadence/scripts/flow-alp/position_details.cdc",