第十阶段 stage 2(二):CRA 部分抵押(储备、抵押率、挪用与崩盘线、劫掠补储备)、官方铸造与销毁排队、官方跑路、质押多币种入口

This commit is contained in:
ZHU MINCHAO committed 2026-11-05 07:14:38 +08:00
1 parent 3758049dcb
commit d0dfa93226
17 files changed
+493 -117

No files matched your search

+103 -15
View File
@@ -376,18 +376,108 @@ internal static class Program
"CRT 冻结概率:声望 0 为 40%,25 为 20%,50 为 0");
});
Test("稳定币:CRA 补贴池告急即为预兆;螺旋三天 0.85 / 0.30 / 0.02", () =>
Test("稳定币:CRA 垫子告急即为预兆;储备吃空必崩,螺旋三天 0.85 / 0.30 / 0.02", () =>
{
var s = new Stablecoins { CraSubsidy = Tuning.CraSubsidy0 * 0.19 };
Check(s.CraWarning(1) && s.CraRate == Tuning.CraRateLow, "补贴池低于 20%:预兆出现、利率降到 0.2%");
var t = new Stablecoins { CraSubsidy = 1 };
var s = new Stablecoins { Reserve = Tuning.CraCrashRatio * Tuning.CraS0 + Stablecoins.Cushion0 * 0.19 };
Check(s.CraWarning(1) && s.CraRate == Tuning.CraRateLow, "垫子低于 20%:预兆出现、利率降到 0.2%");
var t = new Stablecoins { Reserve = Tuning.CraS0 * 0.2 };
var rng = new Rng(3);
t.Night(1, rng, 100000);
Check(t.CraWarnDay == 1 && t.CraSpiralDay >= 5 && t.CraSpiralDay <= 8, $"补贴池吃空触发螺旋,提前 4–7 天:第 {t.CraSpiralDay} 天开始");
Check(t.CraWarnDay == 1 && t.CraEventDay >= 5 && t.CraEventDay <= 8, $"垫子吃空触发预兆,提前 4–7 天:第 {t.CraEventDay} 天挪用");
int ev = t.CraEventDay;
var path = new List<double>();
for (int d = 2; d <= t.CraSpiralDay + 3; d++) { t.Night(d, rng, 0); if (d >= t.CraSpiralDay) path.Add(t.Cra); }
Check(path.Take(3).SequenceEqual(new[] { 0.85, 0.30, 0.02 }), $"螺旋路径:{string.Join(" / ", path)}");
Check(!t.CraWarning(t.CraSpiralDay + 1), "螺旋开始后不再算预兆");
for (int d = 2; d <= ev + 3; d++) { t.Night(d, rng, 0); if (d >= ev) path.Add(t.Cra); }
Check(t.CraCrashDay == ev && path.Take(3).SequenceEqual(new[] { 0.85, 0.30, 0.02 }), $"崩盘路径:{string.Join(" / ", path)}");
Check(!t.CraWarning(ev + 1), "崩盘开始后不再算预兆");
});
Test("稳定币:挪用后 R/S 低于崩盘线才崩,否则只是折价;劫掠上层补回储备", () =>
{
int crashes = 0, discounts = 0;
for (ulong seed = 1; seed <= 60; seed++)
{
var s = new Stablecoins { Supply = 100000, Reserve = 100000, C = 0.8, CraWarnDay = 1, CraEventDay = 2 };
s.Night(2, new Rng(seed), 0);
double r = s.Ratio;
Check(r >= 1 - Tuning.CraEmbezzleMax - 0.01 && r <= 1 - Tuning.CraEmbezzleMin + 0.01 && s.CraEventDay < 0 && s.CraWarnDay < 0, $"储备被拿走 40%–80%:R/S {r:0.00}");
if (r < Tuning.CraCrashRatio) { crashes++; Check(s.CraCrashDay == 2 && s.Cra == Tuning.CraSpiralPath[0], "R/S 跌破崩盘线:当天进入螺旋"); }
else { discounts++; Check(s.CraCrashDay < 0 && Near(s.Cra, Stablecoins.PriceOfRatio(r), 1e-9), $"折价:{s.Cra:0.000}"); }
}
Check(crashes > 5 && discounts > 5, $"两种结果都会出现:崩盘 {crashes}、折价 {discounts}");
var t = new Stablecoins(); double before = t.Reserve;
t.OnEventStart(MarketEvent.RevRaidUpper, 3, new Rng(1));
Check(Near(t.Reserve, before + Tuning.CraLoot * t.Supply, 1e-6), "劫掠上层把流通量 × 9% 的 CRC 并入储备");
Check(Stablecoins.PriceOfRatio(Tuning.CraSafeRatio) == 1 && Near(Stablecoins.PriceOfRatio(Tuning.CraCrashRatio), 1 - Tuning.CraDiscountMax, 1e-9), "价格曲线:安全线 1,崩盘线折价 15%");
});
Test("CRA 长期行为:200 天里有的局崩盘有的不崩,质押越多崩得越多,崩盘前有预兆", () =>
{
Func<double, (double crash, double lead)> run = staked =>
{
int crashes = 0, n = 300; double leadSum = 0; int leads = 0;
for (int i = 0; i < n; i++)
{
var s = new Stablecoins(); var rng = new Rng((ulong)(i + 1));
int warn = -1;
for (int d = 1; d <= 200 && s.CraCrashDay < 0; d++)
{
if (rng.Chance(0.03)) s.OnEventStart(MarketEvent.RevRaidUpper, d, rng);
s.Night(d, rng, staked);
if (warn < 0 && s.CraWarnDay >= 0) warn = s.CraWarnDay;
if (s.CraWarnDay < 0 && s.CraCrashDay < 0) warn = -1;
}
if (s.CraCrashDay >= 0) { crashes++; if (warn >= 0) { leadSum += s.CraCrashDay - warn; leads++; } }
}
return (crashes / (double)n, leads > 0 ? leadSum / leads : 0);
};
var lo = run(0); var hi = run(100000);
Check(lo.crash > 0.02 && lo.crash < 0.9, $"无质押 200 天崩盘比例 {lo.crash:P0}");
Check(hi.crash >= lo.crash, $"质押 10 万:{hi.crash:P0} ≥ 无质押 {lo.crash:P0}");
Check(lo.lead >= Tuning.CraWarnLeadMin - 0.01 && lo.lead <= Tuning.CraWarnLeadMax + 0.01, $"预兆到崩盘平均 {lo.lead:0.0} 天");
});
Test("CRA 一级市场:铸造按抵押率存 CRC 加 SPARK,排队后进流通量与储备;销毁按 min(c, R/S) 兑付", () =>
{
var s = new Stablecoins(); var rng = new Rng(2);
var w = new Wallet { Address = "w" };
s.Collateral(1000, out double crc, out double spark);
Check(Near(crc, 1000 * Tuning.CraC0, 1e-9) && Near(spark * s.Spark, 1000 * (1 - Tuning.CraC0), 1e-9), $"抵押:CRC {crc:0},SPARK {spark:0.##}");
Check(s.RequestMint(w, 1000, 1, rng) == null, "钱包里没有币:拒绝");
w.Add(Assets.CRC, crc); w.Add(Assets.SPARK, spark);
double S0 = s.Supply, R0 = s.Reserve;
var o = s.RequestMint(w, 1000, 1, rng);
Check(o != null && w.Get(Assets.CRC) == 0 && w.Get(Assets.SPARK) == 0 && o.Due >= 2 && o.Due <= 3, "存入后排队 1~2 天");
var evs = s.NightOrders(1, rng);
Check(evs.Count == 0 && s.Supply == S0, "没到期不结算");
evs = s.NightOrders(o.Due, rng);
Check(evs.Count == 1 && evs[0].Kind == CraOrderKind.Minted && Near(s.Supply, S0 + 1000, 1e-9) && Near(s.Reserve, R0 + crc, 1e-9), "到期:流通量加 1000,储备加 CRC 部分");
w.Add(Assets.CRA, 500);
double ratio = s.Ratio;
var b = s.RequestBurn(w, 500, 5);
Check(b != null && b.Due == 5 + Tuning.CraUnlockDays && w.Get(Assets.CRA) == 0, "销毁排队解锁期");
evs = s.NightOrders(b.Due, rng);
Check(evs.Count == 1 && evs[0].Kind == CraOrderKind.Burned && Near(evs[0].Crc, 500 * Math.Min(Tuning.CraC0, ratio), 1e-6) && evs[0].Spark > 0, $"兑付 CRC {evs[0].Crc:0}、SPARK {evs[0].Spark:0.##}");
var low = new Stablecoins { Supply = 1000, Reserve = 200 };
low.Redeem(100, 0.8, out double c2, out double sp2);
Check(Near(c2, 20, 1e-9) && Near(sp2 * low.Spark, 80, 1e-9), "储备只有 R/S = 0.2:CRC 份额降到 0.2,其余用 SPARK 补");
});
Test("CRA 一级市场:官方跑路概率 = 系数 × (1 − CRA 价格),命中整笔丢失", () =>
{
var s = new Stablecoins();
Check(s.RugChance == 0, "价格 1 时不会跑路");
s.Cra = 0.9;
Check(Near(s.RugChance, Tuning.CraRugPerDepeg * 0.1, 1e-9), "价格 0.9 时概率 10%");
int lost = 0, n = 4000;
for (int i = 0; i < n; i++)
{
var t = new Stablecoins { Cra = 0.9, Supply = 1000, Reserve = 800 };
var w = new Wallet { Address = "w" }; w.Add(Assets.CRA, 10);
t.RequestBurn(w, 10, 1);
var ev = t.NightOrders(1, new Rng((ulong)i));
if (ev.Count == 1 && ev[0].Kind == CraOrderKind.Lost) { lost++; Check(t.Orders.Count == 0 && w.Get(Assets.CRA) == 0, "丢失不退款"); }
}
Check(Math.Abs(lost / (double)n - 0.1) < 0.03, $"命中率 {lost / (double)n:P1}");
});
Test("合规出金:干净小额不查;污点高时重度命中记罪证;轻度命中只没收污点部分;只对收益计税", () =>
@@ -599,7 +689,7 @@ internal static class Program
Check(Near(back.Market.Price, m.Price, 1e-9) && back.Market.Pools.Count == m.Pools.Count && back.Market.Pools[1].Id == m.Pools[1].Id, "交易对与价格");
Check(back.Hot.Get(Assets.PSC) == 5 && back.Hot.Taint == 12 && back.Hot.Basis == 34, "钱包余额、污点、成本");
Check(back.Exchange.History.Count == 1 && back.Exchange.History[0].Value == 100, "出金记录");
Check(Near(back.Network.NpcHash, net.NpcHash, 1e-9) && Near(back.Stable.CraSubsidy, st.CraSubsidy, 1e-9), "全网算力、CRA 补贴池");
Check(Near(back.Network.NpcHash, net.NpcHash, 1e-9) && Near(back.Stable.Reserve, st.Reserve, 1e-9), "全网算力、CRA 储备");
for (int d = 6; d <= 10; d++)
{
m.Night(new MarketEvent[0], rng, d, a => 1);
@@ -1042,12 +1132,12 @@ internal static class Program
Check(Math.Abs(w.Get(Assets.CRA) - 700) < 1e-6, $"到期自动回钱包 200:{w.Get(Assets.CRA):0.##}");
Check(w.Taint > 400 - lotTaint + 1, "污点按比例回来");
// 螺旋:质押的 CRA 取回后只值当时的价格
var s = new Stablecoins(); s.CraSpiralDay = 5;
var s = new Stablecoins(); s.CraCrashDay = 5;
for (int d = 5; d <= 7; d++) s.Night(d, new Rng(1), 0);
Check(s.Cra < 0.05 && s.Spark < Tuning.SparkStart * 0.1, $"螺旋后 CRA {s.Cra},SPARK {s.Spark:0.00}");
});
Test("SPARK:随机游走有界,铸销来回只损手续费,事件冲击", () =>
Test("SPARK:随机游走有界,铸销事件冲击", () =>
{
double lo = 99, hi = 0;
for (ulong seed = 1; seed <= 20; seed++)
@@ -1056,9 +1146,7 @@ internal static class Program
for (int d = 1; d <= 150 && s.CraWarnDay < 0; d++) { s.Night(d, rng, 0); lo = Math.Min(lo, s.Spark); hi = Math.Max(hi, s.Spark); } // 螺旋前
}
Check(lo >= Tuning.SparkStart * Tuning.SparkMinMult - 1e-9 && hi <= Tuning.SparkStart * Tuning.SparkMaxMult + 1e-9 && hi / lo > 1.5, $"螺旋前的范围 {lo:0.0}–{hi:0.0}");
var s2 = new Stablecoins(); var w = new Wallet { Address = "w" }; w.Add(Assets.CRA, 100);
double sp = s2.Mint(w, 100), back = s2.Burn(w, sp);
Check(Math.Abs(back - 100 * Math.Pow(1 - Tuning.SparkFee, 2)) < 1e-6 && s2.Mint(w, 1e6) < 0, $"来回 100 → {back:0.00},余额不足返回 -1");
var s2 = new Stablecoins();
double before = s2.Spark; s2.OnEventStart(MarketEvent.RevRaidUpper, 1, new Rng(1));
Check(Math.Abs(s2.Spark - before * Tuning.SparkRaidShock) < 1e-9, "袭击上层 +15%");
});
@@ -1333,7 +1421,7 @@ internal static class Program
Check(Math.Abs(hit / (double)runs - PredictMath.GasAtLeast(10, 3, 1.3)) < 0.02, "gas 概率和真实抽样对得上");
double h = 1 - Math.Pow(1 - Tuning.CraSpiralWeekly, 1.0 / 7);
Check(Math.Abs(PredictMath.CraWarnProb(1e9, 100, 10, 12, 14, h) - (1 - Math.Pow(1 - h, 3)) * Math.Pow(1 - h, 2)) < 1e-9, "补贴池充足时按日概率,窗口前发出的不算");
Check(PredictMath.CraWarnProb(150, 100, 10, 11, 12, h) > 0.99, "补贴池撑不过窗口前一晚就必发");
Check(Math.Abs(PredictMath.CraWarnProb(150, 100, 10, 11, 12, h) - (1 - h)) < 1e-9, "垫子撑不过窗口前一晚:窗口第一晚必发,只要之前没按日概率发出");
Check(PredictMath.CraWarnProb(50, 100, 10, 10, 10, h) == 1, "归零那晚必发");
Check(PredictMath.EraProb(60, 61, 63, -1) > 0 && PredictMath.EraProb(60, 61, 63, -1) < 0.2, "没预警时在 50–90 里均匀,3 天窗口很小");
Check(Math.Abs(PredictMath.EraProb(60, 62, 63, 58) - 0.5) < 1e-9 || PredictMath.EraProb(60, 62, 63, 58) > 0, "预警后落在 4–7 天内");