Stage 7 numeric pass and market legibility (price moves, history, insider exits)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6c5db17758
commit
7b5080984a
9 files changed
+152
-21
No files matched your search
@@ -22,6 +22,8 @@ namespace Decentralized.Domain
|
||||
public double Param;
|
||||
public int OpenDay, FromDay, ToDay;
|
||||
public double P0, B, QYes, QNo;
|
||||
/// <summary>昨晚消息反应之前的价格(-1 = 还没反应过):列表里显示涨跌,让玩家看到消息怎样推动价格。</summary>
|
||||
public double PrevP = -1;
|
||||
/// <summary>-1 未结算,0 否,1 是,2 作废(缺观测数据,退还本金)。</summary>
|
||||
public int Outcome = -1;
|
||||
|
||||
@@ -83,6 +85,21 @@ namespace Decentralized.Domain
|
||||
public double Paid;
|
||||
}
|
||||
|
||||
public sealed class PredictStat
|
||||
{
|
||||
public int Count, Hits;
|
||||
public double SumP0;
|
||||
public double AvgP0 => Count > 0 ? SumP0 / Count : 0;
|
||||
public double HitRate => Count > 0 ? Hits / (double)Count : 0;
|
||||
}
|
||||
|
||||
/// <summary>一次夜间价格反应。</summary>
|
||||
public sealed class PredictMove
|
||||
{
|
||||
public PredictMarket Market;
|
||||
public double From, To;
|
||||
}
|
||||
|
||||
/// <summary>结算记录(盘口列表下面的"最近结果")。</summary>
|
||||
public sealed class PredictRecord
|
||||
{
|
||||
@@ -118,6 +135,8 @@ namespace Decentralized.Domain
|
||||
public List<PredictPos> Positions = new List<PredictPos>();
|
||||
public List<PredictRecord> Recent = new List<PredictRecord>();
|
||||
public List<PredictTip> Tips = new List<PredictTip>();
|
||||
/// <summary>按盘口类型的历史:开过几个、开盘价之和、实际发生几次。玩家据此看出哪类盘口系统性地定价偏高或偏低。</summary>
|
||||
public Dictionary<string, PredictStat> Stats = new Dictionary<string, PredictStat>();
|
||||
public int Seq;
|
||||
/// <summary>每晚观测:当天新出现的事件标识(和 "F:派系")。窗口里缺任何一天的观测,盘口作废退款。</summary>
|
||||
public Dictionary<int, List<string>> Started = new Dictionary<int, List<string>>();
|
||||
@@ -159,16 +178,28 @@ namespace Decentralized.Domain
|
||||
}
|
||||
|
||||
/// <summary>全部卖出这个钱包在盘口里的持仓(盘口关闭前):收回 LMSR 的卖价减 2% 手续费,污点按比例退回,赚的部分是干净的。返回收回的 CRC。</summary>
|
||||
public PredictResult SellAll(Wallet acct, PredictMarket m, int day, out double got)
|
||||
public PredictResult SellAll(Wallet acct, PredictMarket m, int day, out double got) => SellAll(acct, m, day, null, out got, out _);
|
||||
|
||||
/// <summary>
|
||||
/// 全部卖出;内幕持仓(这个盘口合计)达到查处线、而且卖出有赚,按查处概率被查:本金和收益全部没收,收益的一半记罪证(fraud > 0)。
|
||||
/// 先买后卖、赶在结算前离场,同样会被查。
|
||||
/// </summary>
|
||||
public PredictResult SellAll(Wallet acct, PredictMarket m, int day, Rng rng, out double got, out double fraud)
|
||||
{
|
||||
got = 0;
|
||||
got = 0; fraud = 0;
|
||||
var pos = PosOf(m.Id, acct.Address);
|
||||
if (pos == null) return PredictResult.NoPosition;
|
||||
if (!m.IsOpen(day)) return PredictResult.Closed;
|
||||
double value = m.SellValue(pos.Yes, pos.Shares);
|
||||
got = value * (1 - Tuning.PredictFee);
|
||||
bool insider = PosAll(m.Id).Sum(x => x.Insider) >= Tuning.PredictInsiderShare * m.ShareCap;
|
||||
if (pos.Yes) m.QYes -= pos.Shares; else m.QNo -= pos.Shares;
|
||||
Positions.Remove(pos);
|
||||
if (rng != null && insider && pos.Insider > 0 && got > pos.Cost && rng.Chance(Tuning.PredictInsiderCatch))
|
||||
{
|
||||
fraud = Math.Min(Tuning.PredictFraudCap, (got - pos.Cost) * Tuning.PredictFraudShare);
|
||||
got = 0;
|
||||
}
|
||||
Pay(acct, pos, got);
|
||||
return PredictResult.Ok;
|
||||
}
|
||||
@@ -181,7 +212,7 @@ namespace Decentralized.Domain
|
||||
|
||||
/// <summary>
|
||||
/// 夜间:先登记今晚的观测,再结算 ToDay 到了的盘口(赢的钱回到下注的钱包,wallet 按地址找)。outcome 由接入层给出(事件 / 派系读观测,纯度和行情读游戏数值),null 表示拿不到数据。
|
||||
/// 赢家每份兑付 1 信用点;内幕持仓达到份额上限的 <see cref="Tuning.PredictInsiderShare"/> 后,有概率被查:只退本金,收益的一半记罪证。
|
||||
/// 赢家每份兑付 1 信用点;内幕持仓达到份额上限的 <see cref="Tuning.PredictInsiderShare"/> 后,有概率被查:本金和收益全部没收,收益的一半记罪证。
|
||||
/// </summary>
|
||||
public List<PredictEvent> Night(int day, IEnumerable<string> activeNow, Func<PredictMarket, bool?> outcome, Func<string, Wallet> wallet, Rng rng, double push = 0)
|
||||
{
|
||||
@@ -205,6 +236,9 @@ namespace Decentralized.Domain
|
||||
continue;
|
||||
}
|
||||
m.Outcome = res.Value ? 1 : 0;
|
||||
string sk = m.Kind.ToString();
|
||||
if (!Stats.TryGetValue(sk, out var st)) Stats[sk] = st = new PredictStat();
|
||||
st.Count++; st.SumP0 += m.P0; if (res.Value) st.Hits++;
|
||||
var winners = all.Where(x => x.Yes == res.Value).ToList();
|
||||
// 操纵:行情盘上,赢家的方向和玩家推价的方向一致时按推价幅度被查——本金和收益都没收,收益的一半记罪证
|
||||
if (m.Kind == PredictKind.Price && winners.Count > 0 && winners.Any(x => x.Yes ? push > 0 : push < 0) && rng.Chance(Market.ManipChance(push)))
|
||||
@@ -225,9 +259,9 @@ namespace Decentralized.Domain
|
||||
// 内幕持仓按盘口合计(拆到几个钱包里也一样)
|
||||
if (all.Sum(x => x.Insider) >= Tuning.PredictInsiderShare * m.ShareCap && rng.Chance(Tuning.PredictInsiderCatch))
|
||||
{
|
||||
foreach (var pos in winners) Pay(wallet(pos.Wallet), pos, pos.Cost);
|
||||
evs.Add(new PredictEvent { Kind = "caught", Title = m.Title, Market = m, Amount = profit, Fraud = Math.Min(Tuning.PredictFraudCap, Math.Max(0, profit) * Tuning.PredictFraudShare) });
|
||||
Record(day, m, m.Outcome, 0, true);
|
||||
// 查实:本金和收益全部没收
|
||||
evs.Add(new PredictEvent { Kind = "caught", Title = m.Title, Market = m, Amount = payout, Fraud = Math.Min(Tuning.PredictFraudCap, Math.Max(0, profit) * Tuning.PredictFraudShare) });
|
||||
Record(day, m, m.Outcome, -cost, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -264,17 +298,21 @@ namespace Decentralized.Domain
|
||||
/// 夜间的消息反应:还能交易的盘口(明天仍开着)向"公开信息下的合理概率"靠拢 PredictDrift,再加 logit 噪声(NPC 流量)。
|
||||
/// 价格在夜里才反应,所以白天先看到报纸、传闻的人可以抢在前面买、等涨价再卖。target 返回 null 表示这个盘口没有新信息。
|
||||
/// </summary>
|
||||
public void Drift(int day, Func<PredictMarket, double?> target, Rng rng)
|
||||
public List<PredictMove> Drift(int day, Func<PredictMarket, double?> target, Rng rng)
|
||||
{
|
||||
var moves = new List<PredictMove>();
|
||||
foreach (var m in Markets.Where(x => x.Outcome < 0 && x.FromDay > day + 1))
|
||||
{
|
||||
m.PrevP = m.PYes;
|
||||
double p = m.PYes, t = target(m) ?? p;
|
||||
t = Math.Max(Tuning.PredictPMin, Math.Min(Tuning.PredictPMax, t));
|
||||
double pt = p + Tuning.PredictDrift * (t - p);
|
||||
double lp = Math.Log(pt / (1 - pt)) + Tuning.PredictNoise * rng.Gauss();
|
||||
pt = Math.Max(Tuning.PredictPMin, Math.Min(Tuning.PredictPMax, 1 / (1 + Math.Exp(-lp))));
|
||||
m.QYes = m.QNo + m.B * Math.Log(pt / (1 - pt));
|
||||
moves.Add(new PredictMove { Market = m, From = m.PrevP, To = m.PYes });
|
||||
}
|
||||
return moves;
|
||||
}
|
||||
|
||||
/// <summary>读观测:窗口里每一天都有记录才能判断;有任何一天缺就返回 null(作废)。</summary>
|
||||
|
||||
Reference in new issue
Block a user