第十阶段 stage 2(二):CRA 部分抵押(储备、抵押率、挪用与崩盘线、劫掠补储备)、官方铸造与销毁排队、官方跑路、质押多币种入口
This commit is contained in:
1 parent
3758049dcb
commit
d0dfa93226
17 files changed
+493
-117
No files matched your search
@@ -1,19 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Decentralized.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// 三种稳定币的价格与状态:CRC(上层,托管银行出事时脱锚、可能减记),CRT(黑市,镇压或火并时脱锚,
|
||||
/// 镇压期间按黑市声望出卖客户),CRA(革命军,质押付息,补贴池告急或外生触发时进入三天死亡螺旋)。
|
||||
/// 镇压期间按黑市声望出卖客户),CRA(革命军,部分抵押:流通量 S、储备 R(CRC)、抵押率 c;质押利息以无抵押的新 CRA 付出,
|
||||
/// 稀释储备覆盖率 R/S;革命军挪用储备时 R/S 低于崩盘线进入三天死亡螺旋,劫掠上层补回储备)。
|
||||
/// </summary>
|
||||
public enum CraOrderKind { Minted, Burned, Lost }
|
||||
|
||||
/// <summary>官方一级市场的排队单:铸造(存入 CRC 和 SPARK)或销毁(存入 CRA),到期结算,官方可能把它黑掉。</summary>
|
||||
public sealed class CraOrder
|
||||
{
|
||||
public int Id, Due;
|
||||
public string Wallet;
|
||||
public bool Mint;
|
||||
public double Cra, Crc, Spark, C;
|
||||
}
|
||||
|
||||
public sealed class CraOrderEvent
|
||||
{
|
||||
public CraOrder Order;
|
||||
public CraOrderKind Kind;
|
||||
public double Cra, Crc, Spark;
|
||||
}
|
||||
|
||||
public sealed class Stablecoins
|
||||
{
|
||||
public double Crc = 1, Crt = 1, Cra = 1;
|
||||
public int CrcDepegEnd = -1, CrtDepegEnd = -1;
|
||||
public bool CrcHaircutPending;
|
||||
public double CraSubsidy = Tuning.CraSubsidy0;
|
||||
public int CraWarnDay = -1, CraSpiralDay = -1;
|
||||
/// <summary>CRA 流通量与储备(CRC);c 是现在铸造要求的 CRC 份额(0~1)。</summary>
|
||||
public double Supply = Tuning.CraS0, Reserve = Tuning.CraS0 * Tuning.CraC0, C = Tuning.CraC0;
|
||||
/// <summary>预兆日(新闻发出的那天)、预定的挪用日、崩盘开始日(-1 = 没有)。</summary>
|
||||
public int CraWarnDay = -1, CraEventDay = -1, CraCrashDay = -1;
|
||||
public List<CraOrder> Orders = new List<CraOrder>();
|
||||
public int OrderSeq;
|
||||
public double RevPower = 50;
|
||||
/// <summary>平衡代币 SPARK 的价格(信用点);SparkPre 是螺旋开始前一刻的价格,螺旋三天按它的倍数走。</summary>
|
||||
public double Spark = Tuning.SparkStart, SparkPre = -1;
|
||||
@@ -45,7 +69,7 @@ namespace Decentralized.Domain
|
||||
Crt = rng.Range(Tuning.CrtDepegMin, Tuning.CrtDepegMax);
|
||||
CrtDepegEnd = day + rng.Range(Tuning.CrtDepegDaysMin, Tuning.CrtDepegDaysMax);
|
||||
}
|
||||
if (ev == MarketEvent.RevRaidUpper) Spark *= Tuning.SparkRaidShock;
|
||||
if (ev == MarketEvent.RevRaidUpper) { Spark *= Tuning.SparkRaidShock; if (CraCrashDay < 0) Reserve += Tuning.CraLoot * Supply; }
|
||||
if (ev == MarketEvent.LowerUnrest) Spark *= Tuning.SparkUnrestShock;
|
||||
return 1;
|
||||
}
|
||||
@@ -57,36 +81,68 @@ namespace Decentralized.Domain
|
||||
/// <summary>镇压开始时是否冻结玩家的 CRT:按黑市声望,声望越低越容易被黑市"出卖"。</summary>
|
||||
public static bool CrtFrozenByCrackdown(double blackMarketRep, Rng rng) => rng.Chance(CrtFreezeChance(blackMarketRep));
|
||||
|
||||
/// <summary>铸造:用 CRA 按当前价换 SPARK,收手续费。余额不足返回 -1。</summary>
|
||||
public double Mint(Wallet w, double cra)
|
||||
/// <summary>储备覆盖率 R/S。</summary>
|
||||
public double Ratio => Supply > 0 ? Reserve / Supply : 1;
|
||||
|
||||
/// <summary>崩盘线之上的储备(CRC):吃空即必然崩盘。预测盘和线索用它估预兆。</summary>
|
||||
public double Cushion => Reserve - Tuning.CraCrashRatio * Supply;
|
||||
public static double Cushion0 => Tuning.CraS0 * (Tuning.CraC0 - Tuning.CraCrashRatio);
|
||||
|
||||
/// <summary>每晚的垫子消耗:利息按无抵押的新 CRA 付出,每份把崩盘线抬高 CraCrashRatio。</summary>
|
||||
public static double CushionDrain(double stakedTotal, double rate) => stakedTotal * rate * Tuning.CraCrashRatio;
|
||||
|
||||
/// <summary>储备覆盖率对应的价格:高于安全线为 1,线性降到崩盘线的 1 − CraDiscountMax。</summary>
|
||||
public static double PriceOfRatio(double r)
|
||||
{
|
||||
if (cra <= 0 || Spark <= 0 || w.Avail(Assets.CRA) + 1e-9 < cra) return -1;
|
||||
double got = cra * Cra / Spark * (1 - Tuning.SparkFee);
|
||||
w.Add(Assets.CRA, -cra); w.Add(Assets.SPARK, got);
|
||||
w.RecordBuy(Assets.SPARK, got, cra * Cra);
|
||||
return got;
|
||||
if (r >= Tuning.CraSafeRatio) return 1;
|
||||
double t = Math.Max(0, (r - Tuning.CraCrashRatio) / (Tuning.CraSafeRatio - Tuning.CraCrashRatio));
|
||||
return 1 - Tuning.CraDiscountMax * (1 - t);
|
||||
}
|
||||
|
||||
/// <summary>销毁:用 SPARK 按当前价换 CRA,收手续费。余额不足返回 -1。</summary>
|
||||
public double Burn(Wallet w, double spark)
|
||||
/// <summary>铸造 cra 个 CRA 要的 CRC 和 SPARK 数量(SPARK 部分按现价折算)。</summary>
|
||||
public void Collateral(double cra, out double crc, out double spark)
|
||||
{
|
||||
if (spark <= 0 || Cra <= 0 || w.Avail(Assets.SPARK) + 1e-9 < spark) return -1;
|
||||
double got = spark * Spark / Cra * (1 - Tuning.SparkFee);
|
||||
w.Add(Assets.SPARK, -spark); w.Add(Assets.CRA, got);
|
||||
w.RecordBuy(Assets.CRA, got, spark * Spark);
|
||||
return got;
|
||||
crc = cra * C;
|
||||
spark = Spark > 0 ? cra * (1 - C) / Spark : 0;
|
||||
}
|
||||
|
||||
public bool CraSubsidyLow => CraSubsidy <= Tuning.CraSubsidy0 * Tuning.CraSubsidyWarn;
|
||||
/// <summary>
|
||||
/// 铸造申请:从钱包扣 CRC 和 SPARK,排队 CraMintDaysMin..Max 天,到期铸出 CRA。余额不足返回 null。
|
||||
/// </summary>
|
||||
public CraOrder RequestMint(Wallet w, double cra, int day, Rng rng)
|
||||
{
|
||||
if (cra <= 0) return null;
|
||||
Collateral(cra, out double crc, out double spark);
|
||||
if (w.Avail(Assets.CRC) + 1e-9 < crc || w.Avail(Assets.SPARK) + 1e-9 < spark) return null;
|
||||
w.Add(Assets.CRC, -crc); w.Add(Assets.SPARK, -spark);
|
||||
var o = new CraOrder { Id = ++OrderSeq, Wallet = w.Address, Mint = true, Cra = cra, Crc = crc, Spark = spark, C = C, Due = day + rng.Range(Tuning.CraMintDaysMin, Tuning.CraMintDaysMax) };
|
||||
Orders.Add(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
/// <summary>销毁申请:从钱包扣 CRA,排队 CraUnlockDays 天,到期换回 CRC 和 SPARK。余额不足返回 null。</summary>
|
||||
public CraOrder RequestBurn(Wallet w, double cra, int day)
|
||||
{
|
||||
if (cra <= 0 || w.Avail(Assets.CRA) + 1e-9 < cra) return null;
|
||||
w.Add(Assets.CRA, -cra);
|
||||
var o = new CraOrder { Id = ++OrderSeq, Wallet = w.Address, Mint = false, Cra = cra, C = C, Due = day + Tuning.CraUnlockDays };
|
||||
Orders.Add(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
/// <summary>官方每晚黑掉一笔排队单的概率:系数 × (1 − CRA 价格),价格不低于 1 时为 0。</summary>
|
||||
public double RugChance => Math.Max(0, 1 - Cra) * Tuning.CraRugPerDepeg;
|
||||
|
||||
public bool CraSubsidyLow => Cushion <= Cushion0 * Tuning.CraSubsidyWarn;
|
||||
|
||||
/// <summary>CRA 的预兆:外生螺旋的新闻已出,或补贴池告急(利率下调)。螺旋开始后不再算预兆。</summary>
|
||||
public bool CraWarning(int day) => (CraWarnDay >= 0 && CraWarnDay <= day || CraSubsidyLow) && Cra >= 0.97;
|
||||
public bool CraWarning(int day) => (CraWarnDay >= 0 && CraWarnDay <= day || CraSubsidyLow) && CraCrashDay < 0 && Cra >= 0.97;
|
||||
|
||||
public double CraRate => CraSubsidyLow ? Tuning.CraRateLow : Tuning.CraRate;
|
||||
|
||||
/// <summary>
|
||||
/// 夜间结算。返回 CRC 持有量要乘的系数(减记兑现时为 0.95,否则为 1)。
|
||||
/// playerStaked:玩家的 CRA 质押额,用来算补贴支出(玩家的利息由调用方按 <see cref="CraRate"/> 发放)。
|
||||
/// playerStaked:玩家的 CRA 质押额,用来算利息带来的稀释(玩家的利息由调用方按 <see cref="CraRate"/> 发放)。
|
||||
/// </summary>
|
||||
public double Night(int day, Rng rng, double playerStaked)
|
||||
{
|
||||
@@ -107,32 +163,87 @@ namespace Decentralized.Domain
|
||||
if (RevHistory.Count > 7) RevHistory.RemoveAt(0);
|
||||
RevPower = Math.Max(0, Math.Min(100, RevPower + rng.Gauss() * 3));
|
||||
|
||||
if (CraSpiralDay < 0)
|
||||
if (CraCrashDay < 0)
|
||||
{
|
||||
CraSubsidy -= (Tuning.CraNpcStake + playerStaked) * CraRate;
|
||||
bool revFalling = RevHistory.Count == 7 && RevPower < RevHistory[0] - 5;
|
||||
double weekly = Tuning.CraSpiralWeekly * (revFalling ? 3 : 1) * StoryMods.Current.CraSpiral(day);
|
||||
double daily = 1 - Math.Pow(1 - weekly, 1.0 / 7);
|
||||
if (CraWarnDay < 0 && (rng.Chance(daily) || CraSubsidy <= 0))
|
||||
double interest = (Tuning.CraNpcStake + playerStaked) * CraRate;
|
||||
Supply += interest;
|
||||
// 抵押率调整:价格低于 1 上调,回到 1 下调;兑付的 CRC 份额不超过储备均值(见 <see cref="Redeem"/>)
|
||||
if (Cra < 0.995) C = Math.Min(1, C + Tuning.CraCrStep);
|
||||
else if (Cra >= 0.999) C = Math.Max(Tuning.CraCrMin, C - Tuning.CraCrStep);
|
||||
if (CraEventDay < 0)
|
||||
{
|
||||
CraWarnDay = day;
|
||||
CraSpiralDay = day + rng.Range(Tuning.CraWarnLeadMin, Tuning.CraWarnLeadMax);
|
||||
bool revFalling = RevHistory.Count == 7 && RevPower < RevHistory[0] - 5;
|
||||
double weekly = Tuning.CraSpiralWeekly * (revFalling ? 3 : 1) * StoryMods.Current.CraSpiral(day);
|
||||
double daily = 1 - Math.Pow(1 - weekly, 1.0 / 7);
|
||||
if (rng.Chance(daily) || Cushion <= 0)
|
||||
{
|
||||
CraWarnDay = day;
|
||||
CraEventDay = day + rng.Range(Tuning.CraWarnLeadMin, Tuning.CraWarnLeadMax);
|
||||
}
|
||||
}
|
||||
else if (day >= CraEventDay)
|
||||
{
|
||||
// 挪用:储备被革命军拿走一部分,R/S 掉到崩盘线以下就是挤兑
|
||||
Reserve *= 1 - rng.Range(Tuning.CraEmbezzleMin, Tuning.CraEmbezzleMax);
|
||||
CraEventDay = -1; CraWarnDay = -1;
|
||||
if (Ratio < Tuning.CraCrashRatio) CraCrashDay = day;
|
||||
}
|
||||
}
|
||||
if (CraSpiralDay > 0 && day >= CraSpiralDay)
|
||||
if (CraCrashDay >= 0)
|
||||
{
|
||||
int k = Math.Min(day - CraSpiralDay, Tuning.CraSpiralPath.Length - 1);
|
||||
int k = Math.Min(day - CraCrashDay, Tuning.CraSpiralPath.Length - 1);
|
||||
Cra = Tuning.CraSpiralPath[k];
|
||||
if (SparkPre < 0) SparkPre = Spark;
|
||||
Spark = SparkPre * Tuning.SparkSpiralPath[Math.Min(day - CraSpiralDay, Tuning.SparkSpiralPath.Length - 1)];
|
||||
Spark = SparkPre * Tuning.SparkSpiralPath[Math.Min(day - CraCrashDay, Tuning.SparkSpiralPath.Length - 1)];
|
||||
}
|
||||
else
|
||||
{
|
||||
Cra = PriceOfRatio(Ratio);
|
||||
Spark *= Math.Exp(rng.Gauss() * Tuning.SparkVol) * StoryMods.Current.TakeSparkPush();
|
||||
Spark += (Tuning.SparkStart - Spark) * Tuning.SparkRevert;
|
||||
Spark = Math.Max(Tuning.SparkStart * Tuning.SparkMinMult, Math.Min(Tuning.SparkStart * Tuning.SparkMaxMult, Spark));
|
||||
}
|
||||
return crcFactor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 销毁一份 CRA 的兑付:CRC 份额 = min(申请时的 c, 储备均值 R/S),其余按现价用新增发的 SPARK 补。
|
||||
/// 返回 (CRC 数量, SPARK 数量),并扣减储备和流通量。
|
||||
/// </summary>
|
||||
public void Redeem(double cra, double c, out double crc, out double spark)
|
||||
{
|
||||
double share = Math.Min(c, Ratio);
|
||||
crc = Math.Min(Reserve, cra * share);
|
||||
spark = Spark > 0 ? cra * (1 - crc / Math.Max(1e-9, cra)) / Spark : 0;
|
||||
Reserve -= crc; Supply = Math.Max(0, Supply - cra);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 夜间处理排队单:先对每一笔掷官方跑路(整笔丢失),再结算到期的。
|
||||
/// 铸造到期:CRC 进储备、SPARK 销毁、流通量增加;销毁到期:按 <see cref="Redeem"/> 兑付。
|
||||
/// </summary>
|
||||
public List<CraOrderEvent> NightOrders(int day, Rng rng)
|
||||
{
|
||||
var evs = new List<CraOrderEvent>();
|
||||
foreach (var o in Orders.ToList())
|
||||
{
|
||||
if (rng.Chance(RugChance)) { Orders.Remove(o); evs.Add(new CraOrderEvent { Order = o, Kind = CraOrderKind.Lost }); continue; }
|
||||
if (day < o.Due) continue;
|
||||
Orders.Remove(o);
|
||||
var ev = new CraOrderEvent { Order = o };
|
||||
if (o.Mint)
|
||||
{
|
||||
Supply += o.Cra; Reserve += o.Crc;
|
||||
ev.Kind = CraOrderKind.Minted; ev.Cra = o.Cra;
|
||||
}
|
||||
else
|
||||
{
|
||||
Redeem(o.Cra, o.C, out ev.Crc, out ev.Spark);
|
||||
ev.Kind = CraOrderKind.Burned;
|
||||
}
|
||||
evs.Add(ev);
|
||||
}
|
||||
return evs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,8 +51,15 @@ namespace Decentralized.Domain
|
||||
public const double CrtSwapFee = 0.02;
|
||||
public const double CraRate = 0.005, CraRateLow = 0.002;
|
||||
public const int CraUnlockDays = 4;
|
||||
public const double CraSubsidy0 = 30000, CraSubsidyWarn = 0.2, CraNpcStake = 20000;
|
||||
public const double CraSpiralWeekly = 0.02;
|
||||
public const double CraSubsidyWarn = 0.2, CraNpcStake = 20000;
|
||||
/// <summary>CRA 部分抵押:初始流通量与抵押率、抵押率下限与每晚调整步长;R/S 安全线、崩盘线、安全线到崩盘线之间的最大折价;
|
||||
/// 挪用储备的比例范围;劫掠上层补回的储备(占流通量);一级市场铸造等待天数;官方跑路系数(× (1 − CRA 价格))。数值由 research/crasim 定。</summary>
|
||||
public const double CraS0 = 60000, CraC0 = 0.8, CraCrMin = 0.5, CraCrStep = 0.02;
|
||||
public const double CraSafeRatio = 0.6, CraCrashRatio = 0.35, CraDiscountMax = 0.15;
|
||||
public const double CraEmbezzleMin = 0.4, CraEmbezzleMax = 0.8, CraLoot = 0.09;
|
||||
public const int CraMintDaysMin = 1, CraMintDaysMax = 2;
|
||||
public const double CraRugPerDepeg = 1;
|
||||
public const double CraSpiralWeekly = 0.03;
|
||||
public const int CraWarnLeadMin = 4, CraWarnLeadMax = 7;
|
||||
public static readonly double[] CraSpiralPath = { 0.85, 0.30, 0.02 };
|
||||
/// <summary>平衡代币 SPARK:起始价与长期锚(信用点)、日波动率、每晚向锚回归的比例、价格上下限(锚的倍数);铸销手续费;
|
||||
|
||||
@@ -172,11 +172,23 @@ namespace Decentralized.Domain
|
||||
["CoinPayCrt"] = Tuning.CoinPayCrt,
|
||||
["CoinPayPremium"] = Tuning.CoinPayPremium,
|
||||
["CoinPayPsc"] = Tuning.CoinPayPsc,
|
||||
["CraC0"] = Tuning.CraC0,
|
||||
["CraCrMin"] = Tuning.CraCrMin,
|
||||
["CraCrStep"] = Tuning.CraCrStep,
|
||||
["CraCrashRatio"] = Tuning.CraCrashRatio,
|
||||
["CraDiscountMax"] = Tuning.CraDiscountMax,
|
||||
["CraEmbezzleMax"] = Tuning.CraEmbezzleMax,
|
||||
["CraEmbezzleMin"] = Tuning.CraEmbezzleMin,
|
||||
["CraLoot"] = Tuning.CraLoot,
|
||||
["CraMintDaysMax"] = Tuning.CraMintDaysMax,
|
||||
["CraMintDaysMin"] = Tuning.CraMintDaysMin,
|
||||
["CraNpcStake"] = Tuning.CraNpcStake,
|
||||
["CraRate"] = Tuning.CraRate,
|
||||
["CraRateLow"] = Tuning.CraRateLow,
|
||||
["CraRugPerDepeg"] = Tuning.CraRugPerDepeg,
|
||||
["CraS0"] = Tuning.CraS0,
|
||||
["CraSafeRatio"] = Tuning.CraSafeRatio,
|
||||
["CraSpiralWeekly"] = Tuning.CraSpiralWeekly,
|
||||
["CraSubsidy0"] = Tuning.CraSubsidy0,
|
||||
["CraSubsidyWarn"] = Tuning.CraSubsidyWarn,
|
||||
["CraUnlockDays"] = Tuning.CraUnlockDays,
|
||||
["CraWarnLeadMax"] = Tuning.CraWarnLeadMax,
|
||||
|
||||
Reference in new issue
Block a user