Stage 7.1: prediction markets (LMSR, event/faction/purity/price markets, insider check)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7d13ae2fe8
commit
e5a6e3e22c
11 files changed
+810
-4
No files matched your search
@@ -349,6 +349,93 @@ namespace Decentralized
|
||||
});
|
||||
}
|
||||
|
||||
private static int _predPick;
|
||||
private static readonly float[] PredW = { 330f, 90f };
|
||||
|
||||
/// <summary>预测页:左边盘口列表和最近结果,右边选中盘口的买卖。资金走 Poinbase 的 CRC 账户,不用签名。</summary>
|
||||
private static void PredictPage(CustomUIBuilder b)
|
||||
{
|
||||
var d = Session.Data;
|
||||
var bk = d.Predict;
|
||||
var acct = d.Poinbase;
|
||||
int day = StoreStation.GetDayCounter();
|
||||
var open = bk.Markets.Where(m => m.Outcome < 0).OrderBy(m => m.FromDay).ThenBy(m => m.Id).ToList();
|
||||
var sel = open.Find(m => m.Id == _predPick) ?? open.FirstOrDefault();
|
||||
_predPick = sel?.Id ?? 0;
|
||||
Columns(b, l =>
|
||||
{
|
||||
l.AddHeader(L.T("predict.h"));
|
||||
l.AddLabel(L.T("predict.acct", Fmt.Num(acct.Avail(Assets.CRC))));
|
||||
l.AddSeparator();
|
||||
if (open.Count == 0) l.AddLabel(L.T("predict.none"));
|
||||
foreach (var m in open)
|
||||
{
|
||||
var mk = m;
|
||||
var pos = bk.PosOf(m.Id);
|
||||
string q = PredictGame.Question(m);
|
||||
l.BeginRow(4f);
|
||||
l.AddButton(m.Id == _predPick ? "[" + q + "]" : q, () => { _predPick = mk.Id; _message = null; Open(); });
|
||||
l.End();
|
||||
string state = m.IsOpen(day) ? "" : " " + L.T("predict.closed");
|
||||
l.AddLabel(L.T("predict.row", (m.PYes * 100).ToString("0"), ((1 - m.PYes) * 100).ToString("0")) + (pos != null ? " " + L.T("predict.held", L.T(pos.Yes ? "predict.yes" : "predict.no"), Fmt.Num(pos.Shares)) : "") + state);
|
||||
}
|
||||
if (bk.Recent.Count > 0)
|
||||
{
|
||||
l.AddSeparator();
|
||||
foreach (var r in Enumerable.Reverse(bk.Recent))
|
||||
l.AddLabel(L.T("predict.result", r.Day, r.Title, r.Outcome == 2 ? L.T("predict.void") : L.T(r.Outcome == 1 ? "predict.yes" : "predict.no"), r.Held ? (r.Pnl >= 0 ? "+" : "") + Fmt.Num(r.Pnl) : "—"));
|
||||
}
|
||||
}, r =>
|
||||
{
|
||||
if (sel == null) { r.AddHeader(L.T("predict.h")); return; }
|
||||
r.AddHeader(L.T("predict.pick"));
|
||||
r.AddLabel(PredictGame.Question(sel));
|
||||
r.AddLabel(L.T("predict.price", (sel.PYes * 100).ToString("0.0"), ((1 - sel.PYes) * 100).ToString("0.0")));
|
||||
var pos = bk.PosOf(sel.Id);
|
||||
if (pos != null)
|
||||
{
|
||||
r.AddLabel(L.T("predict.pos", L.T(pos.Yes ? "predict.yes" : "predict.no"), Fmt.Num(pos.Shares), Fmt.Num(pos.Cost)));
|
||||
r.AddLabel(L.T("predict.pos2", Fmt.Num(sel.SellValue(pos.Yes, pos.Shares) * (1 - Tuning.PredictFee))));
|
||||
}
|
||||
if (!sel.IsOpen(day)) { r.AddLabel(L.T("predict.closed.note")); return; }
|
||||
AmountRow(r);
|
||||
if (double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double v) && v > 0 && v < 1e7)
|
||||
{
|
||||
foreach (bool yes in new[] { true, false })
|
||||
{
|
||||
double shares = sel.SharesFor(yes, v / (1 + Tuning.PredictFee));
|
||||
r.AddLabel(L.T("predict.preview", L.T(yes ? "predict.yes" : "predict.no"), Fmt.Num(shares), Fmt.Num(shares - v)));
|
||||
}
|
||||
}
|
||||
r.BeginRow();
|
||||
r.AddButton(L.T("predict.buy.yes"), () => PredictBuy(sel, true));
|
||||
r.AddButton(L.T("predict.buy.no"), () => PredictBuy(sel, false));
|
||||
r.End();
|
||||
r.AddButton(L.T("predict.sell"), () => PredictSell(sel));
|
||||
});
|
||||
}
|
||||
|
||||
private static void PredictBuy(PredictMarket m, bool yes)
|
||||
{
|
||||
if (!ReadAmount(out double v)) return;
|
||||
var d = Session.Data;
|
||||
int day = StoreStation.GetDayCounter();
|
||||
bool insider = PredictGame.Known(m, yes);
|
||||
var res = d.Predict.Buy(d.Poinbase, m, yes, v, day, Session.Price, insider, out double shares);
|
||||
_message = res == PredictResult.Ok ? L.T("predict.bought", L.T(yes ? "predict.yes" : "predict.no"), Fmt.Num(shares), Fmt.Num(v)) : L.T("predict.err." + res);
|
||||
if (res == PredictResult.Ok) { Log.Info($"预测下注 #{m.Id} {(yes ? "是" : "否")} {shares:0.0} 份,花 {v:0.##},内幕 {insider}"); Session.Save(); }
|
||||
Open();
|
||||
}
|
||||
|
||||
private static void PredictSell(PredictMarket m)
|
||||
{
|
||||
var d = Session.Data;
|
||||
var res = d.Predict.SellAll(d.Poinbase, m, StoreStation.GetDayCounter(), out double got);
|
||||
_message = res == PredictResult.Ok ? L.T("predict.sold", Fmt.Num(got)) : L.T("predict.err." + res);
|
||||
if (res == PredictResult.Ok) Session.Save();
|
||||
Open();
|
||||
}
|
||||
|
||||
private static string PerpErr(PerpResult res) => L.T("perp.err." + res);
|
||||
|
||||
private static void PerpOpen()
|
||||
@@ -494,16 +581,18 @@ namespace Decentralized
|
||||
{
|
||||
if (_web3Tab == Web3Tab.Mixer && !Unlock.MixerUnlocked) _web3Tab = Web3Tab.Trade;
|
||||
if (_web3Tab == Web3Tab.Black && !Unlock.BlackUnlocked) _web3Tab = Web3Tab.Trade;
|
||||
if (_web3Tab == Web3Tab.Predict && !PredictGame.Available(StoreStation.GetDayCounter())) _web3Tab = Web3Tab.Trade;
|
||||
b.BeginRow();
|
||||
foreach (Web3Tab t in Enum.GetValues(typeof(Web3Tab)))
|
||||
{
|
||||
if ((t == Web3Tab.Mixer && !Unlock.MixerUnlocked) || (t == Web3Tab.Black && !Unlock.BlackUnlocked)) continue;
|
||||
if ((t == Web3Tab.Mixer && !Unlock.MixerUnlocked) || (t == Web3Tab.Black && !Unlock.BlackUnlocked) || (t == Web3Tab.Predict && !PredictGame.Available(StoreStation.GetDayCounter()))) continue;
|
||||
var tab = t;
|
||||
string name = L.T("web3.sub." + t.ToString().ToLowerInvariant());
|
||||
b.AddButton(tab == _web3Tab ? $"[{name}]" : name, () => { _web3Tab = tab; _message = null; _edit = null; Open(); });
|
||||
}
|
||||
b.End();
|
||||
if (_web3Tab == Web3Tab.Stake) { StakePage(b); return; }
|
||||
if (_web3Tab == Web3Tab.Predict) { PredictPage(b); return; }
|
||||
if (_web3Tab == Web3Tab.Mining) { ReportPage(b); return; }
|
||||
if (_web3Tab == Web3Tab.Mixer) { MixerPage(b); return; }
|
||||
if (_web3Tab == Web3Tab.Black) { BlackPage(b); return; }
|
||||
@@ -968,7 +1057,7 @@ namespace Decentralized
|
||||
Open();
|
||||
}
|
||||
|
||||
private enum Web3Tab { Trade, Stake, Mining, Mixer, Black }
|
||||
private enum Web3Tab { Trade, Stake, Predict, Mining, Mixer, Black }
|
||||
private static Web3Tab _web3Tab = Web3Tab.Trade;
|
||||
private static string _mixAsset = Assets.PSC;
|
||||
private static int _mixRounds = 2;
|
||||
|
||||
Reference in new issue
Block a user