Prediction markets use on-chain wallets (signed contract calls) instead of the Poinbase account
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e5a6e3e22c
commit
723724aa4b
8 files changed
+138
-73
No files matched your search
+3
-3
@@ -4,13 +4,13 @@
|
|||||||
|
|
||||||
### 第七阶段:预测市场与操纵(7.1 预测市场)
|
### 第七阶段:预测市场与操纵(7.1 预测市场)
|
||||||
|
|
||||||
- Web3 页"预测"子页(游戏第 8 天起,Web3 治理社区已买下):每个盘口是一道是非题,开盘当天到窗口开始前可以买卖,窗口最后一天夜里结算;每天开门后补盘,同时开放 3–6 个,每天最多新开 2 个。资金走 Poinbase 账户的 CRC(托管账户,不用签名),污点与成本按花掉的价值比例随资金进持仓,赢的钱继承押注资金的污点比例,卖回时按比例退。
|
- Web3 页"预测"子页(游戏第 8 天起,Web3 治理社区已买下):每个盘口是一道是非题,开盘当天到窗口开始前可以买卖,窗口最后一天夜里结算;每天开门后补盘,同时开放 3–6 个,每天最多新开 2 个。资金走链上钱包的 CRC:买入、全部卖出都是合约调用,要在钱包上签名(和质押一样;买入的手续费在核对页选档、用 CRC 付,卖出用价值最高的币付);持仓记在签名的钱包名下,结算时兑付回这个钱包,同一盘口各钱包只能押同一边,持仓上限和内幕判定按盘口合计(拆到几个钱包躲不过)。污点与成本按花掉的价值比例随资金进持仓,赢的钱继承押注资金的污点比例,卖回时按比例退。
|
||||||
- 盘口类型:新闻事件("出现某常规事件",先验 = 游戏掷骰规则:5 天后每天 50 / 35 / 25% 有事件,再按权重抽)、派系行动("某派系有行动开始",每天开始概率 6%,估计值)、水纯度(明天上层 / 下层水纯度 ≥ 阈值,先验精确)、PSC 价格(窗口最后一天夜间结算前的价格 ≥ 阈值,先验用价格过程的正态近似)。事件和派系行动靠夜间对比"昨晚生效的事件"得到当天新出现的;窗口里缺任何一天的记录(没睡觉结算)盘口作废、退回本金。
|
- 盘口类型:新闻事件("出现某常规事件",先验 = 游戏掷骰规则:5 天后每天 50 / 35 / 25% 有事件,再按权重抽)、派系行动("某派系有行动开始",每天开始概率 6%,估计值)、水纯度(明天上层 / 下层水纯度 ≥ 阈值,先验精确)、PSC 价格(窗口最后一天夜间结算前的价格 ≥ 阈值,先验用价格过程的正态近似)。事件和派系行动靠夜间对比"昨晚生效的事件"得到当天新出现的;窗口里缺任何一天的记录(没睡觉结算)盘口作废、退回本金。
|
||||||
- 定价:LMSR,初始价 = 先验,单盘系统最大亏损 = 300 + 10 × 天数,流动性参数由它反推;买卖各收 2%;单人单盘份额上限 = 最大亏损 × 3;一个盘口只持有一边。
|
- 定价:LMSR,初始价 = 先验,单盘系统最大亏损 = 300 + 10 × 天数,流动性参数由它反推;买卖各收 2%;单人单盘份额上限 = 最大亏损 × 3;一个盘口只持有一边。
|
||||||
- 内幕:下注时这个事件(或派系行动)已经以"玩家看得到"的形式排进日程(传闻纸条、派系特权),而且落在窗口里,押「是」算内幕单。内幕持仓达到份额上限的 40% 后,结算时 50% 概率被查:只退本金,收益的一半(上限 400)记 FRAUD 罪证。情报顾客没接。
|
- 内幕:下注时这个事件(或派系行动)已经以"玩家看得到"的形式排进日程(传闻纸条、派系特权),而且落在窗口里,押「是」算内幕单。内幕持仓达到份额上限的 40% 后,结算时 50% 概率被查:只退本金,收益的一半(上限 400)记 FRAUD 罪证。情报顾客没接。
|
||||||
- 复核(ops `research/predsim`):不知情随机押期望为负;谨慎的内幕(压在查处线下)在低先验盘口一盘净赚 270–520(第 30 天最大亏损 600),买满上限的期望反而更低。行情先验和真实 `Market` 过程的蒙特卡洛对得上(单元测试,误差 < 4%)。
|
- 复核(ops `research/predsim`):不知情随机押期望为负;谨慎的内幕(压在查处线下)在低先验盘口一盘净赚 270–520(第 30 天最大亏损 600),买满上限的期望反而更低。行情先验和真实 `Market` 过程的蒙特卡洛对得上(单元测试,误差 < 4%)。
|
||||||
- 游戏内验证:新开盘口列表显示问题、先验、持仓;买入 100 得 319 份并涨价、全部卖出收回 96.08(扣 2%);调试推进几天后纯度盘和价格盘押中,兑付 118 和 199,晨间日志"预测押中";一个真实的革命军行动被观测到并结算为"是";没押的盘口也进"最近结果"。没有在游戏里验证:常规新闻事件盘口的自然结算(本次测试存档里没碰上)、作废、内幕判定与被查、手册文字、英文界面。派系每天 6% 的先验没有真实数据校准。
|
- 游戏内验证:新开盘口列表显示问题、先验、持仓;在 PLEDGER 上签名买入 100 CRC 得 117–424 份并涨价(钱包 CRC 1000 → 899,含 1.5 手续费)、签名全部卖出收回 96.08(扣 2%);调试推进几天后押中的纯度盘兑付 117 回到钱包(CRC 995 → 1,012),晨间日志"预测押中";此前用 Poinbase 账户的版本还验证过价格盘兑付 199;一个真实的革命军行动被观测到并结算为"是";没押的盘口也进"最近结果"。没有在游戏里验证:常规新闻事件盘口的自然结算(本次测试存档里没碰上)、作废、内幕判定与被查、手册文字、英文界面。派系每天 6% 的先验没有真实数据校准。
|
||||||
- 单元测试 11590 通过。
|
- 单元测试 11628 通过(含多钱包持仓:各回各的钱包、限额与内幕合计)。
|
||||||
|
|
||||||
### 第六阶段:高收益与高风险(进行中)
|
### 第六阶段:高收益与高风险(进行中)
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ namespace Decentralized.Domain
|
|||||||
public sealed class PredictPos
|
public sealed class PredictPos
|
||||||
{
|
{
|
||||||
public int Market;
|
public int Market;
|
||||||
|
/// <summary>持仓所在的钱包地址(下注签名的钱包,结算和卖出都回到它)。</summary>
|
||||||
|
public string Wallet;
|
||||||
public bool Yes;
|
public bool Yes;
|
||||||
public double Shares, Cost, Taint, Basis, Insider;
|
public double Shares, Cost, Taint, Basis, Insider;
|
||||||
}
|
}
|
||||||
@@ -113,7 +115,8 @@ namespace Decentralized.Domain
|
|||||||
public List<string> Prev;
|
public List<string> Prev;
|
||||||
|
|
||||||
public PredictMarket Find(int id) => Markets.Find(m => m.Id == id);
|
public PredictMarket Find(int id) => Markets.Find(m => m.Id == id);
|
||||||
public PredictPos PosOf(int id) => Positions.Find(p => p.Market == id);
|
public PredictPos PosOf(int id, string wallet) => Positions.Find(p => p.Market == id && p.Wallet == wallet);
|
||||||
|
public List<PredictPos> PosAll(int id) => Positions.Where(p => p.Market == id).ToList();
|
||||||
|
|
||||||
private static double Share(Wallet acct, double amount, Func<string, double> price)
|
private static double Share(Wallet acct, double amount, Func<string, double> price)
|
||||||
{
|
{
|
||||||
@@ -130,26 +133,27 @@ namespace Decentralized.Domain
|
|||||||
if (!m.IsOpen(day)) return PredictResult.Closed;
|
if (!m.IsOpen(day)) return PredictResult.Closed;
|
||||||
if (spend <= 0) return PredictResult.Amount;
|
if (spend <= 0) return PredictResult.Amount;
|
||||||
if (acct.Avail(Assets.CRC) + 1e-9 < spend) return PredictResult.Funds;
|
if (acct.Avail(Assets.CRC) + 1e-9 < spend) return PredictResult.Funds;
|
||||||
var pos = PosOf(m.Id);
|
var all = PosAll(m.Id);
|
||||||
if (pos != null && pos.Yes != yes) return PredictResult.Opposite;
|
if (all.Any(x => x.Yes != yes)) return PredictResult.Opposite;
|
||||||
|
var pos = PosOf(m.Id, acct.Address);
|
||||||
double cost = spend / (1 + Tuning.PredictFee);
|
double cost = spend / (1 + Tuning.PredictFee);
|
||||||
shares = m.SharesFor(yes, cost);
|
shares = m.SharesFor(yes, cost);
|
||||||
if ((pos?.Shares ?? 0) + shares > m.ShareCap + 1e-9) { shares = 0; return PredictResult.Cap; }
|
if (all.Sum(x => x.Shares) + shares > m.ShareCap + 1e-9) { shares = 0; return PredictResult.Cap; }
|
||||||
double share = Share(acct, spend, pr);
|
double share = Share(acct, spend, pr);
|
||||||
double taint = acct.Taint * share, basis = acct.Basis * share;
|
double taint = acct.Taint * share, basis = acct.Basis * share;
|
||||||
acct.Add(Assets.CRC, -spend); acct.Taint -= taint; acct.Basis -= basis;
|
acct.Add(Assets.CRC, -spend); acct.Taint -= taint; acct.Basis -= basis;
|
||||||
if (yes) m.QYes += shares; else m.QNo += shares;
|
if (yes) m.QYes += shares; else m.QNo += shares;
|
||||||
if (pos == null) Positions.Add(pos = new PredictPos { Market = m.Id, Yes = yes });
|
if (pos == null) Positions.Add(pos = new PredictPos { Market = m.Id, Yes = yes, Wallet = acct.Address });
|
||||||
pos.Shares += shares; pos.Cost += cost; pos.Taint += taint; pos.Basis += basis;
|
pos.Shares += shares; pos.Cost += cost; pos.Taint += taint; pos.Basis += basis;
|
||||||
if (insider) pos.Insider += shares;
|
if (insider) pos.Insider += shares;
|
||||||
return PredictResult.Ok;
|
return PredictResult.Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>全部卖出(盘口关闭前):收回 LMSR 的卖价减 2% 手续费,污点按比例退回,赚的部分是干净的。返回收回的 CRC。</summary>
|
/// <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)
|
||||||
{
|
{
|
||||||
got = 0;
|
got = 0;
|
||||||
var pos = PosOf(m.Id);
|
var pos = PosOf(m.Id, acct.Address);
|
||||||
if (pos == null) return PredictResult.NoPosition;
|
if (pos == null) return PredictResult.NoPosition;
|
||||||
if (!m.IsOpen(day)) return PredictResult.Closed;
|
if (!m.IsOpen(day)) return PredictResult.Closed;
|
||||||
double value = m.SellValue(pos.Yes, pos.Shares);
|
double value = m.SellValue(pos.Yes, pos.Shares);
|
||||||
@@ -167,10 +171,10 @@ namespace Decentralized.Domain
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 夜间:先登记今晚的观测,再结算 ToDay 到了的盘口。outcome 由接入层给出(事件 / 派系读观测,纯度和行情读游戏数值),null 表示拿不到数据。
|
/// 夜间:先登记今晚的观测,再结算 ToDay 到了的盘口(赢的钱回到下注的钱包,wallet 按地址找)。outcome 由接入层给出(事件 / 派系读观测,纯度和行情读游戏数值),null 表示拿不到数据。
|
||||||
/// 赢家每份兑付 1 信用点;内幕持仓达到份额上限的 <see cref="Tuning.PredictInsiderShare"/> 后,有概率被查:只退本金,收益的一半记罪证。
|
/// 赢家每份兑付 1 信用点;内幕持仓达到份额上限的 <see cref="Tuning.PredictInsiderShare"/> 后,有概率被查:只退本金,收益的一半记罪证。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<PredictEvent> Night(int day, IEnumerable<string> activeNow, Func<PredictMarket, bool?> outcome, Wallet acct, Rng rng)
|
public List<PredictEvent> Night(int day, IEnumerable<string> activeNow, Func<PredictMarket, bool?> outcome, Func<string, Wallet> wallet, Rng rng)
|
||||||
{
|
{
|
||||||
var now = activeNow.Distinct().ToList();
|
var now = activeNow.Distinct().ToList();
|
||||||
if (Prev != null) Started[day] = now.Where(x => !Prev.Contains(x)).ToList();
|
if (Prev != null) Started[day] = now.Where(x => !Prev.Contains(x)).ToList();
|
||||||
@@ -180,38 +184,39 @@ namespace Decentralized.Domain
|
|||||||
foreach (var m in Markets.Where(x => x.Outcome < 0 && x.ToDay <= day).ToList())
|
foreach (var m in Markets.Where(x => x.Outcome < 0 && x.ToDay <= day).ToList())
|
||||||
{
|
{
|
||||||
bool? res = outcome(m);
|
bool? res = outcome(m);
|
||||||
var pos = PosOf(m.Id);
|
var all = PosAll(m.Id);
|
||||||
|
foreach (var pos in all) Positions.Remove(pos);
|
||||||
|
double cost = all.Sum(x => x.Cost);
|
||||||
if (res == null)
|
if (res == null)
|
||||||
{
|
{
|
||||||
m.Outcome = 2;
|
m.Outcome = 2;
|
||||||
if (pos != null) { Positions.Remove(pos); Pay(acct, pos, pos.Cost); evs.Add(new PredictEvent { Kind = "void", Title = m.Title, Market = m, Amount = pos.Cost }); }
|
foreach (var pos in all) Pay(wallet(pos.Wallet), pos, pos.Cost);
|
||||||
Record(day, m, 2, 0, pos != null);
|
if (all.Count > 0) evs.Add(new PredictEvent { Kind = "void", Title = m.Title, Market = m, Amount = cost });
|
||||||
|
Record(day, m, 2, 0, all.Count > 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
m.Outcome = res.Value ? 1 : 0;
|
m.Outcome = res.Value ? 1 : 0;
|
||||||
if (pos != null)
|
var winners = all.Where(x => x.Yes == res.Value).ToList();
|
||||||
|
if (all.Count > 0 && winners.Count == 0)
|
||||||
{
|
{
|
||||||
Positions.Remove(pos);
|
evs.Add(new PredictEvent { Kind = "lose", Title = m.Title, Market = m, Amount = cost });
|
||||||
if (pos.Yes != res.Value)
|
Record(day, m, m.Outcome, -cost, true);
|
||||||
|
}
|
||||||
|
else if (winners.Count > 0)
|
||||||
|
{
|
||||||
|
double payout = winners.Sum(x => x.Shares), profit = payout - cost;
|
||||||
|
// 内幕持仓按盘口合计(拆到几个钱包里也一样)
|
||||||
|
if (all.Sum(x => x.Insider) >= Tuning.PredictInsiderShare * m.ShareCap && rng.Chance(Tuning.PredictInsiderCatch))
|
||||||
{
|
{
|
||||||
evs.Add(new PredictEvent { Kind = "lose", Title = m.Title, Market = m, Amount = pos.Cost });
|
foreach (var pos in winners) Pay(wallet(pos.Wallet), pos, pos.Cost);
|
||||||
Record(day, m, m.Outcome, -pos.Cost, true);
|
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);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
double payout = pos.Shares, profit = payout - pos.Cost;
|
foreach (var pos in winners) Pay(wallet(pos.Wallet), pos, pos.Shares);
|
||||||
if (pos.Insider >= Tuning.PredictInsiderShare * m.ShareCap && rng.Chance(Tuning.PredictInsiderCatch))
|
evs.Add(new PredictEvent { Kind = "win", Title = m.Title, Market = m, Amount = payout });
|
||||||
{
|
Record(day, m, m.Outcome, profit, true);
|
||||||
Pay(acct, 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);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Pay(acct, pos, payout);
|
|
||||||
evs.Add(new PredictEvent { Kind = "win", Title = m.Title, Market = m, Amount = payout });
|
|
||||||
Record(day, m, m.Outcome, profit, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else Record(day, m, m.Outcome, 0, false);
|
else Record(day, m, m.Outcome, 0, false);
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ namespace Decentralized
|
|||||||
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Predict: window = tomorrow (test)", () => { foreach (var m in Session.Data.Predict.Markets) { m.FromDay = m.ToDay = StoreStation.GetDayCounter() + 1; } Log.Info("测试:所有盘口窗口改成明天"); });
|
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Predict: window = tomorrow (test)", () => { foreach (var m in Session.Data.Predict.Markets) { m.FromDay = m.ToDay = StoreStation.GetDayCounter() + 1; } Log.Info("测试:所有盘口窗口改成明天"); });
|
||||||
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Perp fund 0 (test)", () => { Session.Data.Perps.InsuranceFund = 0; Log.Info("测试:保险基金清零"); });
|
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Perp fund 0 (test)", () => { Session.Data.Perps.InsuranceFund = 0; Log.Info("测试:保险基金清零"); });
|
||||||
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "SPARK x0.3 (test)", () => { var st = Session.Data.Stable; st.Spark *= 0.3; Log.Info($"测试:SPARK 价格 {st.Spark:0.00}"); });
|
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "SPARK x0.3 (test)", () => { var st = Session.Data.Stable; st.Spark *= 0.3; Log.Info($"测试:SPARK 价格 {st.Spark:0.00}"); });
|
||||||
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "CRA + PSC (test)", () => { var w = Session.Data.Hot; w.Add(Assets.CRA, 1000); w.Add(Assets.PSC, 20); w.Basis += 1000; Log.Info($"测试:{WalletWindow.NameOf(w)} CRA {w.Get(Assets.CRA):0}"); });
|
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Wallet gift: CRC, CRA, PSC (test)", () => { var w = Session.Data.Hot; w.Add(Assets.CRA, 1000); w.Add(Assets.CRC, 1000); w.Add(Assets.PSC, 20); w.Basis += 2000; Log.Info($"测试:{WalletWindow.NameOf(w)} CRA {w.Get(Assets.CRA):0}"); });
|
||||||
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "CRA spiral warning (test)", () =>
|
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "CRA spiral warning (test)", () =>
|
||||||
{
|
{
|
||||||
var st = Session.Data.Stable; int day = StoreStation.GetDayCounter();
|
var st = Session.Data.Stable; int day = StoreStation.GetDayCounter();
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ black.kind.Contraband.3,Contraband (high)
|
|||||||
black.kind.Tech,High-tech (markup)
|
black.kind.Tech,High-tech (markup)
|
||||||
nightlog.bm.ready,"Black market: {0} arrived; a courier will come by once you open."
|
nightlog.bm.ready,"Black market: {0} arrived; a courier will come by once you open."
|
||||||
item.web3guide.name,Web3 Handbook
|
item.web3guide.name,Web3 Handbook
|
||||||
item.web3guide.desc,"Bank: depositing turns credits into CRC in your Poinbase account; withdrawing turns CRC back into credits and goes through compliance screening, which looks at the Poinbase account's taint. Trading: buys are paid in the quote coin, sells in the traded coin. Mixer: pick a coin, amount and rounds; each round takes a fee and lowers taint, rounds equal days of waiting, and the coins come back to the same address; the more you deposit, the worse the cleaning. Hardware wallets: on-chain coins sit in PLEDGER hardware wallets, each with its own balances and taint; open and connect them on the terminal's Wallets page. The Web3 page's trading, black market and mixer use the connected wallet, and coin-paying customers and mining rewards go into it too. Payments (black market, mixer, Ponero desk, listing fees) start as an invoice on the terminal; type the payee address on the wallet and hold ✓ to sign. The last two characters of an address are a checksum, so a one-character typo is usually caught; once in a while it happens to be valid and the coins go to an address nobody uses. DEX swaps also need a signature on the wallet. RatDigg miners (M1 / M2 / M3) are standalone machines with far more hash than a card and a lower power draw per hash; they need a battery, take modules and use no rig slots. Only the black market's miner dealers sell them, gated by black-market standing (M1 40, M2 60, M3 80), sometimes second-hand. When one burns out, repair it by putting electronic components in its repair slot in order; it comes back 20 health lower. Somewhere between day 50 and day 90 a "miner era" arrives, announced in the paper a few days earlier: the big farms halve their power costs, network hash climbs, GPU miners are diluted and low-end cards barely earn, while miners still do fine. Perps: the Perps tab on Poinbase uses your account CRC as margin, up to 5× on PSC and 3× on SPARK, one position per product, never long and short at once. Fills are worse than the quote (more so for bigger orders, like the spot pool) and opening and closing both cost a fee. Liquidation is checked only overnight against the day's average price: equity under 10% of notional wipes the margin. There is also a nightly funding payment; when the price is above its 7-day average, longs pay shorts. If a liquidation loses more than the margin, the insurance fund covers the gap; when it runs dry (a SPARK collapse can do that) winning positions are auto-deleveraged and give up half their profit; anything beyond that becomes your debt. Debt grows 2% a day, is repaid first from CRC in the account every night, blocks new positions and cash-outs, and after seven days items in your shop are seized to cover it; whatever is still short is written off and the perp account is banned for 30 days. Poinbase is a custodial account and can withdraw straight to the connected wallet; depositing from a wallet means sending to Poinbase's deposit address, which takes only CRC and PSC. More hardware wallets are sold by Dr. Jackson; drag one onto the trading terminal to add it, up to 5. The black market has cheaper ones. A wallet you no longer want can be destroyed on the Wallets page once its coins are moved out. Transfers pay a fee: slow, normal or fast, and gas prices rise and fall over roughly a week; when the network is congested fees rise and slow transactions may be dropped from the mempool. Check the full address before sending. Staking: CRA staked in the Revolution pool earns a daily rate that compounds into the stake; withdrawing means requesting first and waiting four days, with no interest while waiting and no way to take it early. The pool pays from a subsidy fund; when it runs low the rate is cut and word of big holders pulling out may spread. Treat that as a warning: once it collapses CRA is worthless within three days and four days of unlocking is too slow. The balance token SPARK can be minted and burned against CRA at the going price (with a fee); its price swings hard and crashes with the collapse. Staking, minting and burning are contract calls and need a signature on the wallet. Black market: coin only, no CRC, 30% above normal channels, with extra markup on high-tech goods and GPUs; the coin you pay leaves its share of taint with the seller and the goods carry no stolen tag; GPUs may be fake, inspect them yourself. Orders are cheaper than spot but take days and may be intercepted; merchants sell by type at their own price factor, 5+ items get 5% off and 10+ get 10% off, and a bulk merchant appears at random with the lowest prices but six days of waiting; escrow costs 5%, is released on delivery and refunded if the seller runs; advance release takes another 10% off, but a scammer keeps the money and only sellers with 30+ trades accept; the platform itself can rug, be taken over by enforcement, or a seller can be arrested, after which your orders get traced and crime logged; forum rumors can be warnings. An intercepted parcel sometLine truncated
|
item.web3guide.desc,"Bank: depositing turns credits into CRC in your Poinbase account; withdrawing turns CRC back into credits and goes through compliance screening, which looks at the Poinbase account's taint. Trading: buys are paid in the quote coin, sells in the traded coin. Mixer: pick a coin, amount and rounds; each round takes a fee and lowers taint, rounds equal days of waiting, and the coins come back to the same address; the more you deposit, the worse the cleaning. Hardware wallets: on-chain coins sit in PLEDGER hardware wallets, each with its own balances and taint; open and connect them on the terminal's Wallets page. The Web3 page's trading, black market and mixer use the connected wallet, and coin-paying customers and mining rewards go into it too. Payments (black market, mixer, Ponero desk, listing fees) start as an invoice on the terminal; type the payee address on the wallet and hold ✓ to sign. The last two characters of an address are a checksum, so a one-character typo is usually caught; once in a while it happens to be valid and the coins go to an address nobody uses. DEX swaps also need a signature on the wallet. RatDigg miners (M1 / M2 / M3) are standalone machines with far more hash than a card and a lower power draw per hash; they need a battery, take modules and use no rig slots. Only the black market's miner dealers sell them, gated by black-market standing (M1 40, M2 60, M3 80), sometimes second-hand. When one burns out, repair it by putting electronic components in its repair slot in order; it comes back 20 health lower. Somewhere between day 50 and day 90 a "miner era" arrives, announced in the paper a few days earlier: the big farms halve their power costs, network hash climbs, GPU miners are diluted and low-end cards barely earn, while miners still do fine. Perps: the Perps tab on Poinbase uses your account CRC as margin, up to 5× on PSC and 3× on SPARK, one position per product, never long and short at once. Fills are worse than the quote (more so for bigger orders, like the spot pool) and opening and closing both cost a fee. Liquidation is checked only overnight against the day's average price: equity under 10% of notional wipes the margin. There is also a nightly funding payment; when the price is above its 7-day average, longs pay shorts. If a liquidation loses more than the margin, the insurance fund covers the gap; when it runs dry (a SPARK collapse can do that) winning positions are auto-deleveraged and give up half their profit; anything beyond that becomes your debt. Debt grows 2% a day, is repaid first from CRC in the account every night, blocks new positions and cash-outs, and after seven days items in your shop are seized to cover it; whatever is still short is written off and the perp account is banned for 30 days. Poinbase is a custodial account and can withdraw straight to the connected wallet; depositing from a wallet means sending to Poinbase's deposit address, which takes only CRC and PSC. More hardware wallets are sold by Dr. Jackson; drag one onto the trading terminal to add it, up to 5. The black market has cheaper ones. A wallet you no longer want can be destroyed on the Wallets page once its coins are moved out. Transfers pay a fee: slow, normal or fast, and gas prices rise and fall over roughly a week; when the network is congested fees rise and slow transactions may be dropped from the mempool. Check the full address before sending. Staking: CRA staked in the Revolution pool earns a daily rate that compounds into the stake; withdrawing means requesting first and waiting four days, with no interest while waiting and no way to take it early. The pool pays from a subsidy fund; when it runs low the rate is cut and word of big holders pulling out may spread. Treat that as a warning: once it collapses CRA is worthless within three days and four days of unlocking is too slow. The balance token SPARK can be minted and burned against CRA at the going price (with a fee); its price swings hard and crashes with the collapse. Staking, minting and burning are contract calls and need a signature on the wallet. Black market: coin only, no CRC, 30% above normal channels, with extra markup on high-tech goods and GPUs; the coin you pay leaves its share of taint with the seller and the goods carry no stolen tag; GPUs may be fake, inspect them yourself. Orders are cheaper than spot but take days and may be intercepted; merchants sell by type at their own price factor, 5+ items get 5% off and 10+ get 10% off, and a bulk merchant appears at random with the lowest prices but six days of waiting; escrow costs 5%, is released on delivery and refunded if the seller runs; advance release takes another 10% off, but a scammer keeps the money and only sellers with 30+ trades accept; the platform itself can rug, be taken over by enforcement, or a seller can be arrested, after which your orders get traced and crime logged; forum rumors can be warnings. An intercepted parcel sometLine truncated
|
||||||
courier.odd1,...Package is here.
|
courier.odd1,...Package is here.
|
||||||
courier.odd2,"(His hand stays in his pocket.) Sign here."
|
courier.odd2,"(His hand stays in his pocket.) Sign here."
|
||||||
courier.sign,Sign
|
courier.sign,Sign
|
||||||
@@ -537,7 +537,7 @@ news.era.title,The miner era
|
|||||||
news.era.desc,A new generation of dedicated miners is in mass production and the big farms have halved their power costs. Network hash will climb and GPU miners' returns will be diluted.
|
news.era.desc,A new generation of dedicated miners is in mass production and the big farms have halved their power costs. Network hash will climb and GPU miners' returns will be diluted.
|
||||||
web3.sub.predict,Predict
|
web3.sub.predict,Predict
|
||||||
predict.h,Prediction markets
|
predict.h,Prediction markets
|
||||||
predict.acct,Poinbase CRC: {0}
|
predict.acct,On-chain wallet {0}: CRC {1}
|
||||||
predict.none,No open markets today.
|
predict.none,No open markets today.
|
||||||
predict.closed,closed
|
predict.closed,closed
|
||||||
predict.row,Yes {0}% No {1}%
|
predict.row,Yes {0}% No {1}%
|
||||||
@@ -548,8 +548,10 @@ predict.void,void
|
|||||||
predict.result,Day {0} {1} result: {2} {3}
|
predict.result,Day {0} {1} result: {2} {3}
|
||||||
predict.pick,Trade
|
predict.pick,Trade
|
||||||
predict.price,Yes {0}% No {1}%
|
predict.price,Yes {0}% No {1}%
|
||||||
predict.pos,"Position {0} x{1}, cost {2}"
|
predict.pos,"{0}: {1} x{2}"
|
||||||
predict.pos2,"Selling all now returns {0}"
|
predict.pos2,"Cost {0} sells for {1}"
|
||||||
|
predict.sum.buy,"Buy {1} for {2} CRC: {0}"
|
||||||
|
predict.sum.sell,Sell prediction position: {0}
|
||||||
predict.closed.note,"Closed, waiting for settlement."
|
predict.closed.note,"Closed, waiting for settlement."
|
||||||
predict.preview,"Buy {0}: about {1} shares, net {2} if right"
|
predict.preview,"Buy {0}: about {1} shares, net {2} if right"
|
||||||
predict.buy.yes,Buy Yes
|
predict.buy.yes,Buy Yes
|
||||||
@@ -559,7 +561,7 @@ predict.bought,Bought {0} x{1} for {2}.
|
|||||||
predict.sold,"Sold, received {0}."
|
predict.sold,"Sold, received {0}."
|
||||||
predict.err.Closed,This market is closed.
|
predict.err.Closed,This market is closed.
|
||||||
predict.err.Amount,Invalid amount.
|
predict.err.Amount,Invalid amount.
|
||||||
predict.err.Funds,Not enough CRC in the Poinbase account.
|
predict.err.Funds,Not enough CRC in the wallet.
|
||||||
predict.err.Cap,Over this market's position limit.
|
predict.err.Cap,Over this market's position limit.
|
||||||
predict.err.Opposite,You hold the other side; sell it first.
|
predict.err.Opposite,You hold the other side; sell it first.
|
||||||
predict.err.NoPosition,No position in this market.
|
predict.err.NoPosition,No position in this market.
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ black.kind.Contraband.3,违禁品(高)
|
|||||||
black.kind.Tech,高科技物品(加价)
|
black.kind.Tech,高科技物品(加价)
|
||||||
nightlog.bm.ready,"黑市:{0} 到货,开店后会有快递员上门。"
|
nightlog.bm.ready,"黑市:{0} 到货,开店后会有快递员上门。"
|
||||||
item.web3guide.name,Web3 手册
|
item.web3guide.name,Web3 手册
|
||||||
item.web3guide.desc,"银行:存入把信用点换成 Poinbase 账户里的 CRC;提取把 CRC 换回信用点,要过合规审查,审查看 Poinbase 账户的污点。交易:买入按计价币的数量支付,卖出按所交易币的数量卖出。混币器:选币种、填数量和轮数,每轮扣手续费并降低污点,轮数即等待天数,到期提回同一个地址;存入越多,清洗越差。硬件钱包:链上的币放在 PLEDGER 硬件钱包里,每个钱包的余额和污点各算各的;在终端的钱包页打开、连接。Web3 页的交易、黑市、混币器用已连接的钱包,用币付款的顾客和挖矿收益也进它。付款(黑市、混币器、Ponero 兑换处、上架费)先在终端开账单,再在钱包里手输收款地址、按住 ✓ 签名;地址末尾两位是校验位,输错一位多半会被拦下,偶尔碰巧合法,币就转进了没人用的地址。DEX 兑换也要在钱包上签名。Poinbase 是托管账户,可以直接提到已连接的钱包;从钱包充值要转到 Poinbase 的充值地址,只收 CRC 和 PSC。更多硬件钱包向杰克逊博士购买,拖到交易终端上添加,最多 5 个;黑市上也有更便宜的。不要的钱包可以在钱包页销毁,要先把币转走。转账要付手续费,分慢、普通、快三档,gas 价格大约一周一个起落;网络拥堵时手续费变贵,慢档可能被挤出内存池。转账前核对完整地址。质押:把 CRA 质押进革命军质押池每天有日息,利息并入本金;取出要先申请,等四天才到账,等待期间没有利息,也不能提前拿。质押池靠补贴池付息,补贴池见底会降息,有时还会传出大户撤出的消息,那是预兆:一旦崩盘,三天内 CRA 就一文不值,四天的解锁期来不及。平衡代币 SPARK 可以和 CRA 按价格互相铸销(收手续费),价格波动很大,崩盘时会跟着暴跌。质押、铸销都是合约调用,要在钱包上签名。RatDigg 矿机(M1 / M2 / M3)是独立的机器,算力比显卡大得多、耗电更省,要放电池才挖,可以放模块,不占矿架槽位;只在黑市的矿机商那里有,要黑市声望(M1 40、M2 60、M3 80),偶尔有二手的。暴毙后要往修理槽里放电子元件按顺序修好,修好健康少 20。第 50 到 90 天之间会有一次"矿机时代",提前几天上报纸预告,之后大矿场的耗电成本砍半,全网算力涨上去,显卡矿工的收益被摊薄,低档显卡几乎不赚钱,矿机还行。永续合约:Poinbase 的"永续"页,用账户里的 CRC 当保证金,PSC 最高 5 倍、SPARK 最高 3 倍,一个品种只有一个仓位,不能同时多空。成交价比现价差一截(单越大差得越多,和现货池一样),开平仓还收手续费。强平只在夜里按当天均价检查,权益不到名义价值的 10% 就被强平,保证金没了;每晚还有资金费,价格比近 7 天均价高时多头付给空头。强平后亏光保证金还不够的缺口,先由保险基金垫,基金见底(SPARK 崩盘这类大行情会抽干它)后盈利的仓位会被自动减仓、少拿一半盈利,再不够就记成你的欠款:欠款每天涨 2%,账户里的 CRC 每晚先拿去还,没还清不能开仓、不能出金,七天不还就没收店里的物品抵债,抵不够的核销并拉黑永续账户 30 天。黑市:只收币,不收 CRC,价格比正常渠道贵 30%,高科技物品和显卡另有加价;付出去的币带着按比例的污点留在卖家那边,货不带赃物标签;显卡不保证真假,自己鉴定。订单比现货便宜,要等几天,路上可能被截获;商家按类型卖货,价格系数各不相同,一次订 5 件以上 95 折、10 件以上 9 折,大宗商家随机出现,价格最低但要等六天;托管多付 5% 托管费,收货后放款,卖家跑路时退款;提前放款再打九折,卖家若是骗子钱就没了,只有成交 30 单以上的卖家接受;平台自己也可能跑路,也可能被执法接管,或者卖家落网,之后你的订单会被追查、记罪证;论坛上的传闻可以当预兆。被截获的包裹有时会变成执法人员上门:签收当场记罪证并查封,拒签只损失货款。出售:在黑市的卖货页把储藏区里的违禁品、高科技物品和显卡上架,标价是参考价的 0.6 到 1.8 倍。倍率越高成交越慢(0.9 以下约一天,1.0 约两天,1.8 约十四天),货款带污点的可能性和比例也越高,近 7 天出售的量越大,污点越重;同时最多上架 10 件,上架收 5% 上架费,上架后可以改价(收 2% 改价费,标价按参考价重算,成交的快慢跟着新倍率走),成交扣 8% 抽成,货款托管 3 天后进钱包;卖假卡可能被买家看穿而退货,拉低好评;平台跑路时上架的货和未放款的货款都没了Line truncated
|
item.web3guide.desc,"银行:存入把信用点换成 Poinbase 账户里的 CRC;提取把 CRC 换回信用点,要过合规审查,审查看 Poinbase 账户的污点。交易:买入按计价币的数量支付,卖出按所交易币的数量卖出。混币器:选币种、填数量和轮数,每轮扣手续费并降低污点,轮数即等待天数,到期提回同一个地址;存入越多,清洗越差。硬件钱包:链上的币放在 PLEDGER 硬件钱包里,每个钱包的余额和污点各算各的;在终端的钱包页打开、连接。Web3 页的交易、黑市、混币器用已连接的钱包,用币付款的顾客和挖矿收益也进它。付款(黑市、混币器、Ponero 兑换处、上架费)先在终端开账单,再在钱包里手输收款地址、按住 ✓ 签名;地址末尾两位是校验位,输错一位多半会被拦下,偶尔碰巧合法,币就转进了没人用的地址。DEX 兑换也要在钱包上签名。Poinbase 是托管账户,可以直接提到已连接的钱包;从钱包充值要转到 Poinbase 的充值地址,只收 CRC 和 PSC。更多硬件钱包向杰克逊博士购买,拖到交易终端上添加,最多 5 个;黑市上也有更便宜的。不要的钱包可以在钱包页销毁,要先把币转走。转账要付手续费,分慢、普通、快三档,gas 价格大约一周一个起落;网络拥堵时手续费变贵,慢档可能被挤出内存池。转账前核对完整地址。质押:把 CRA 质押进革命军质押池每天有日息,利息并入本金;取出要先申请,等四天才到账,等待期间没有利息,也不能提前拿。质押池靠补贴池付息,补贴池见底会降息,有时还会传出大户撤出的消息,那是预兆:一旦崩盘,三天内 CRA 就一文不值,四天的解锁期来不及。平衡代币 SPARK 可以和 CRA 按价格互相铸销(收手续费),价格波动很大,崩盘时会跟着暴跌。质押、铸销都是合约调用,要在钱包上签名。RatDigg 矿机(M1 / M2 / M3)是独立的机器,算力比显卡大得多、耗电更省,要放电池才挖,可以放模块,不占矿架槽位;只在黑市的矿机商那里有,要黑市声望(M1 40、M2 60、M3 80),偶尔有二手的。暴毙后要往修理槽里放电子元件按顺序修好,修好健康少 20。第 50 到 90 天之间会有一次"矿机时代",提前几天上报纸预告,之后大矿场的耗电成本砍半,全网算力涨上去,显卡矿工的收益被摊薄,低档显卡几乎不赚钱,矿机还行。永续合约:Poinbase 的"永续"页,用账户里的 CRC 当保证金,PSC 最高 5 倍、SPARK 最高 3 倍,一个品种只有一个仓位,不能同时多空。成交价比现价差一截(单越大差得越多,和现货池一样),开平仓还收手续费。强平只在夜里按当天均价检查,权益不到名义价值的 10% 就被强平,保证金没了;每晚还有资金费,价格比近 7 天均价高时多头付给空头。强平后亏光保证金还不够的缺口,先由保险基金垫,基金见底(SPARK 崩盘这类大行情会抽干它)后盈利的仓位会被自动减仓、少拿一半盈利,再不够就记成你的欠款:欠款每天涨 2%,账户里的 CRC 每晚先拿去还,没还清不能开仓、不能出金,七天不还就没收店里的物品抵债,抵不够的核销并拉黑永续账户 30 天。黑市:只收币,不收 CRC,价格比正常渠道贵 30%,高科技物品和显卡另有加价;付出去的币带着按比例的污点留在卖家那边,货不带赃物标签;显卡不保证真假,自己鉴定。订单比现货便宜,要等几天,路上可能被截获;商家按类型卖货,价格系数各不相同,一次订 5 件以上 95 折、10 件以上 9 折,大宗商家随机出现,价格最低但要等六天;托管多付 5% 托管费,收货后放款,卖家跑路时退款;提前放款再打九折,卖家若是骗子钱就没了,只有成交 30 单以上的卖家接受;平台自己也可能跑路,也可能被执法接管,或者卖家落网,之后你的订单会被追查、记罪证;论坛上的传闻可以当预兆。被截获的包裹有时会变成执法人员上门:签收当场记罪证并查封,拒签只损失货款。出售:在黑市的卖货页把储藏区里的违禁品、高科技物品和显卡上架,标价是参考价的 0.6 到 1.8 倍。倍率越高成交越慢(0.9 以下约一天,1.0 约两天,1.8 约十四天),货款带污点的可能性和比例也越高,近 7 天出售的量越大,污点越重;同时最多上架 10 件,上架收 5% 上架费,上架后可以改价(收 2% 改价费,标价按参考价重算,成交的快慢跟着新倍率走),成交扣 8% 抽成,货款托管 3 天后进钱包;卖假卡可能被买家看穿而退货,拉低好评;平台跑路时上架的货和未放款的货款都没了Line truncated
|
||||||
courier.odd1,……东西到了。
|
courier.odd1,……东西到了。
|
||||||
courier.odd2,"(他的手一直放在口袋里)在这儿签个字。"
|
courier.odd2,"(他的手一直放在口袋里)在这儿签个字。"
|
||||||
courier.sign,签收
|
courier.sign,签收
|
||||||
@@ -536,7 +536,7 @@ news.era.title,矿机时代
|
|||||||
news.era.desc,新一代专用矿机开始量产,大型矿场的耗电成本砍掉一半。全网算力会涨上去,显卡矿工的收益要被摊薄。
|
news.era.desc,新一代专用矿机开始量产,大型矿场的耗电成本砍掉一半。全网算力会涨上去,显卡矿工的收益要被摊薄。
|
||||||
web3.sub.predict,预测
|
web3.sub.predict,预测
|
||||||
predict.h,预测市场
|
predict.h,预测市场
|
||||||
predict.acct,Poinbase CRC:{0}
|
predict.acct,链上钱包 {0}:CRC {1}
|
||||||
predict.none,今天没有开放的盘口。
|
predict.none,今天没有开放的盘口。
|
||||||
predict.closed,已封盘
|
predict.closed,已封盘
|
||||||
predict.row,是 {0}% 否 {1}%
|
predict.row,是 {0}% 否 {1}%
|
||||||
@@ -547,8 +547,10 @@ predict.void,作废
|
|||||||
predict.result,第 {0} 天 {1} 结果:{2} {3}
|
predict.result,第 {0} 天 {1} 结果:{2} {3}
|
||||||
predict.pick,买卖
|
predict.pick,买卖
|
||||||
predict.price,是 {0}% 否 {1}%
|
predict.price,是 {0}% 否 {1}%
|
||||||
predict.pos,持仓「{0}」{1} 份,成本 {2}
|
predict.pos,{0}:「{1}」{2} 份
|
||||||
predict.pos2,现在全部卖出可得 {0}
|
predict.pos2,成本 {0} 全部卖出可得 {1}
|
||||||
|
predict.sum.buy,"买入「{1}」,花 {2} CRC:{0}"
|
||||||
|
predict.sum.sell,卖出预测持仓:{0}
|
||||||
predict.closed.note,已封盘,等结算。
|
predict.closed.note,已封盘,等结算。
|
||||||
predict.preview,买「{0}」约 {1} 份,押中净赚 {2}
|
predict.preview,买「{0}」约 {1} 份,押中净赚 {2}
|
||||||
predict.buy.yes,买入「是」
|
predict.buy.yes,买入「是」
|
||||||
@@ -558,7 +560,7 @@ predict.bought,买入「{0}」{1} 份,花 {2}。
|
|||||||
predict.sold,卖出,收回 {0}。
|
predict.sold,卖出,收回 {0}。
|
||||||
predict.err.Closed,这个盘口已经封盘。
|
predict.err.Closed,这个盘口已经封盘。
|
||||||
predict.err.Amount,金额不对。
|
predict.err.Amount,金额不对。
|
||||||
predict.err.Funds,Poinbase 账户里的 CRC 不够。
|
predict.err.Funds,钱包里的 CRC 不够。
|
||||||
predict.err.Cap,超过这个盘口的持仓上限。
|
predict.err.Cap,超过这个盘口的持仓上限。
|
||||||
predict.err.Opposite,这个盘口已经持有另一边,先全部卖出。
|
predict.err.Opposite,这个盘口已经持有另一边,先全部卖出。
|
||||||
predict.err.NoPosition,这个盘口没有持仓。
|
predict.err.NoPosition,这个盘口没有持仓。
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ namespace Decentralized
|
|||||||
if (d.Network.Reward < rewardBefore) News.Show("pd_news_halving_" + day, L.T("news.halving.title"), L.T("news.halving.desc"), 2);
|
if (d.Network.Reward < rewardBefore) News.Show("pd_news_halving_" + day, L.T("news.halving.title"), L.T("news.halving.desc"), 2);
|
||||||
// 预测市场:先登记今晚的观测再结算到期的盘口;行情盘读夜间更新之前的价格
|
// 预测市场:先登记今晚的观测再结算到期的盘口;行情盘读夜间更新之前的价格
|
||||||
var predictEvents = PredictGame.Available(day) || d.Predict.Markets.Count > 0
|
var predictEvents = PredictGame.Available(day) || d.Predict.Markets.Count > 0
|
||||||
? d.Predict.Night(day, PredictGame.ActiveIds(), m => PredictGame.Outcome(m, d), d.Poinbase, d.Rng) : new List<PredictEvent>();
|
? d.Predict.Night(day, PredictGame.ActiveIds(), m => PredictGame.Outcome(m, d), d.WalletAt, d.Rng) : new List<PredictEvent>();
|
||||||
// 永续:用今天的标记价(当日均价)结算,要在今晚的价格更新之前
|
// 永续:用今天的标记价(当日均价)结算,要在今晚的价格更新之前
|
||||||
var perpEvents = d.Perps.Night(day, new Dictionary<string, double> { ["PSC"] = d.Market.Mark, ["SPARK"] = d.Stable.Spark }, d.Poinbase, Session.Price);
|
var perpEvents = d.Perps.Night(day, new Dictionary<string, double> { ["PSC"] = d.Market.Mark, ["SPARK"] = d.Stable.Spark }, d.Poinbase, Session.Price);
|
||||||
if (perpEvents.Any(e => e.Kind == "seize")) SeizeForDebt(d, day);
|
if (perpEvents.Any(e => e.Kind == "seize")) SeizeForDebt(d, day);
|
||||||
|
|||||||
@@ -350,14 +350,13 @@ namespace Decentralized
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int _predPick;
|
private static int _predPick;
|
||||||
private static readonly float[] PredW = { 330f, 90f };
|
|
||||||
|
|
||||||
/// <summary>预测页:左边盘口列表和最近结果,右边选中盘口的买卖。资金走 Poinbase 的 CRC 账户,不用签名。</summary>
|
/// <summary>预测页:左边盘口列表和最近结果,右边选中盘口的买卖。下注和卖出是链上合约调用,用当前钱包的 CRC,要在钱包上签名;持仓记在签名的钱包名下。</summary>
|
||||||
private static void PredictPage(CustomUIBuilder b)
|
private static void PredictPage(CustomUIBuilder b)
|
||||||
{
|
{
|
||||||
var d = Session.Data;
|
var d = Session.Data;
|
||||||
var bk = d.Predict;
|
var bk = d.Predict;
|
||||||
var acct = d.Poinbase;
|
var w = d.Hot;
|
||||||
int day = StoreStation.GetDayCounter();
|
int day = StoreStation.GetDayCounter();
|
||||||
var open = bk.Markets.Where(m => m.Outcome < 0).OrderBy(m => m.FromDay).ThenBy(m => m.Id).ToList();
|
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();
|
var sel = open.Find(m => m.Id == _predPick) ?? open.FirstOrDefault();
|
||||||
@@ -365,19 +364,19 @@ namespace Decentralized
|
|||||||
Columns(b, l =>
|
Columns(b, l =>
|
||||||
{
|
{
|
||||||
l.AddHeader(L.T("predict.h"));
|
l.AddHeader(L.T("predict.h"));
|
||||||
l.AddLabel(L.T("predict.acct", Fmt.Num(acct.Avail(Assets.CRC))));
|
l.AddLabel(L.T("predict.acct", WalletWindow.NameOf(w), Fmt.Num(w.Avail(Assets.CRC))));
|
||||||
l.AddSeparator();
|
l.AddSeparator();
|
||||||
if (open.Count == 0) l.AddLabel(L.T("predict.none"));
|
if (open.Count == 0) l.AddLabel(L.T("predict.none"));
|
||||||
foreach (var m in open)
|
foreach (var m in open)
|
||||||
{
|
{
|
||||||
var mk = m;
|
var mk = m;
|
||||||
var pos = bk.PosOf(m.Id);
|
var all = bk.PosAll(m.Id);
|
||||||
string q = PredictGame.Question(m);
|
string q = PredictGame.Question(m);
|
||||||
l.BeginRow(4f);
|
l.BeginRow(4f);
|
||||||
l.AddButton(m.Id == _predPick ? "[" + q + "]" : q, () => { _predPick = mk.Id; _message = null; Open(); });
|
l.AddButton(m.Id == _predPick ? "[" + q + "]" : q, () => { _predPick = mk.Id; _message = null; Open(); });
|
||||||
l.End();
|
l.End();
|
||||||
string state = m.IsOpen(day) ? "" : " " + L.T("predict.closed");
|
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);
|
l.AddLabel(L.T("predict.row", (m.PYes * 100).ToString("0"), ((1 - m.PYes) * 100).ToString("0")) + (all.Count > 0 ? " " + L.T("predict.held", L.T(all[0].Yes ? "predict.yes" : "predict.no"), Fmt.Num(all.Sum(x => x.Shares))) : "") + state);
|
||||||
}
|
}
|
||||||
if (bk.Recent.Count > 0)
|
if (bk.Recent.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -391,12 +390,13 @@ namespace Decentralized
|
|||||||
r.AddHeader(L.T("predict.pick"));
|
r.AddHeader(L.T("predict.pick"));
|
||||||
r.AddLabel(PredictGame.Question(sel));
|
r.AddLabel(PredictGame.Question(sel));
|
||||||
r.AddLabel(L.T("predict.price", (sel.PYes * 100).ToString("0.0"), ((1 - sel.PYes) * 100).ToString("0.0")));
|
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);
|
foreach (var pos in bk.PosAll(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)));
|
var ow = d.Own(pos.Wallet);
|
||||||
r.AddLabel(L.T("predict.pos2", Fmt.Num(sel.SellValue(pos.Yes, pos.Shares) * (1 - Tuning.PredictFee))));
|
r.AddLabel(L.T("predict.pos", ow != null ? WalletWindow.NameOf(ow) : pos.Wallet, L.T(pos.Yes ? "predict.yes" : "predict.no"), Fmt.Num(pos.Shares)));
|
||||||
}
|
}
|
||||||
|
var mine = bk.PosOf(sel.Id, w.Address);
|
||||||
|
if (mine != null) r.AddLabel(L.T("predict.pos2", Fmt.Num(mine.Cost), Fmt.Num(sel.SellValue(mine.Yes, mine.Shares) * (1 - Tuning.PredictFee))));
|
||||||
if (!sel.IsOpen(day)) { r.AddLabel(L.T("predict.closed.note")); return; }
|
if (!sel.IsOpen(day)) { r.AddLabel(L.T("predict.closed.note")); return; }
|
||||||
AmountRow(r);
|
AmountRow(r);
|
||||||
if (double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double v) && v > 0 && v < 1e7)
|
if (double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double v) && v > 0 && v < 1e7)
|
||||||
@@ -419,21 +419,47 @@ namespace Decentralized
|
|||||||
{
|
{
|
||||||
if (!ReadAmount(out double v)) return;
|
if (!ReadAmount(out double v)) return;
|
||||||
var d = Session.Data;
|
var d = Session.Data;
|
||||||
int day = StoreStation.GetDayCounter();
|
var w = d.Hot;
|
||||||
bool insider = PredictGame.Known(m, yes);
|
if (w.Avail(Assets.CRC) + 1e-9 < v) { _message = L.T("predict.err.Funds"); Open(); return; }
|
||||||
var res = d.Predict.Buy(d.Poinbase, m, yes, v, day, Session.Price, insider, out double shares);
|
if (d.Predict.PosAll(m.Id).Any(x => x.Yes != yes)) { _message = L.T("predict.err.Opposite"); Open(); return; }
|
||||||
_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);
|
string side = L.T(yes ? "predict.yes" : "predict.no");
|
||||||
if (res == PredictResult.Ok) { Log.Info($"预测下注 #{m.Id} {(yes ? "是" : "否")} {shares:0.0} 份,花 {v:0.##},内幕 {insider}"); Session.Save(); }
|
_message = L.T("hw.wait");
|
||||||
Open();
|
Open();
|
||||||
|
WalletWindow.OpenForBill(new WalletWindow.Bill
|
||||||
|
{
|
||||||
|
Contract = true, Asset = Assets.CRC, Amount = v,
|
||||||
|
Summary = L.T("predict.sum.buy", PredictGame.Question(m), side, Fmt.Num(v)),
|
||||||
|
Execute = pw =>
|
||||||
|
{
|
||||||
|
int day = StoreStation.GetDayCounter();
|
||||||
|
bool insider = PredictGame.Known(m, yes);
|
||||||
|
var res = d.Predict.Buy(pw, m, yes, v, day, Session.Price, insider, out double shares);
|
||||||
|
_message = res == PredictResult.Ok ? L.T("predict.bought", side, 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.##},钱包 {pw.Address},内幕 {insider}"); Session.Save(); }
|
||||||
|
Open();
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PredictSell(PredictMarket m)
|
private static void PredictSell(PredictMarket m)
|
||||||
{
|
{
|
||||||
var d = Session.Data;
|
var d = Session.Data;
|
||||||
var res = d.Predict.SellAll(d.Poinbase, m, StoreStation.GetDayCounter(), out double got);
|
var w = d.Hot;
|
||||||
_message = res == PredictResult.Ok ? L.T("predict.sold", Fmt.Num(got)) : L.T("predict.err." + res);
|
if (d.Predict.PosOf(m.Id, w.Address) == null) { _message = L.T("predict.err.NoPosition"); Open(); return; }
|
||||||
if (res == PredictResult.Ok) Session.Save();
|
_message = L.T("hw.wait");
|
||||||
Open();
|
Open();
|
||||||
|
WalletWindow.OpenForBill(new WalletWindow.Bill
|
||||||
|
{
|
||||||
|
Contract = true, Asset = Assets.CRC, Amount = 0, Spend = 0, FeeAsset = BestFeeAsset(w),
|
||||||
|
Summary = L.T("predict.sum.sell", PredictGame.Question(m)),
|
||||||
|
Execute = pw =>
|
||||||
|
{
|
||||||
|
var res = d.Predict.SellAll(pw, 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 string PerpErr(PerpResult res) => L.T("perp.err." + res);
|
||||||
|
|||||||
@@ -1080,14 +1080,14 @@ internal static class Program
|
|||||||
Check(bk.Buy(acct, m, true, 100, 6, pr, false, out _) == PredictResult.Closed, "窗口开始后封盘");
|
Check(bk.Buy(acct, m, true, 100, 6, pr, false, out _) == PredictResult.Closed, "窗口开始后封盘");
|
||||||
var res = bk.Buy(acct, m, true, 102, 5, pr, false, out double sh);
|
var res = bk.Buy(acct, m, true, 102, 5, pr, false, out double sh);
|
||||||
Check(res == PredictResult.Ok && sh > 0, "买入成功");
|
Check(res == PredictResult.Ok && sh > 0, "买入成功");
|
||||||
var pos = bk.PosOf(m.Id);
|
var pos = bk.PosOf(m.Id, acct.Address);
|
||||||
Check(Math.Abs(pos.Cost - 100) < 1e-6 && Math.Abs(acct.Get(Assets.CRC) - 898) < 1e-6, $"成本 100(手续费 2):{pos.Cost:0.00}");
|
Check(Math.Abs(pos.Cost - 100) < 1e-6 && Math.Abs(acct.Get(Assets.CRC) - 898) < 1e-6, $"成本 100(手续费 2):{pos.Cost:0.00}");
|
||||||
Check(Math.Abs(pos.Taint - 40.8) < 1e-6 && Math.Abs(acct.Taint - 359.2) < 1e-6, $"污点按花掉的价值比例带进持仓:{pos.Taint:0.00}");
|
Check(Math.Abs(pos.Taint - 40.8) < 1e-6 && Math.Abs(acct.Taint - 359.2) < 1e-6, $"污点按花掉的价值比例带进持仓:{pos.Taint:0.00}");
|
||||||
Check(bk.Buy(acct, m, false, 10, 5, pr, false, out _) == PredictResult.Opposite, "不能同盘反方向");
|
Check(bk.Buy(acct, m, false, 10, 5, pr, false, out _) == PredictResult.Opposite, "不能同盘反方向");
|
||||||
// 卖出:价差为 0,只付 2% 手续费
|
// 卖出:价差为 0,只付 2% 手续费
|
||||||
double before = acct.Get(Assets.CRC);
|
double before = acct.Get(Assets.CRC);
|
||||||
Check(bk.SellAll(acct, m, 5, out double got) == PredictResult.Ok && Math.Abs(got - 98) < 1e-6, $"原价卖回只扣手续费:{got:0.00}");
|
Check(bk.SellAll(acct, m, 5, out double got) == PredictResult.Ok && Math.Abs(got - 98) < 1e-6, $"原价卖回只扣手续费:{got:0.00}");
|
||||||
Check(Math.Abs(acct.Get(Assets.CRC) - before - got) < 1e-6 && bk.PosOf(m.Id) == null, "回到账户");
|
Check(Math.Abs(acct.Get(Assets.CRC) - before - got) < 1e-6 && bk.PosOf(m.Id, acct.Address) == null, "回到账户");
|
||||||
Check(acct.Taint < 400 && acct.Taint > 359.2, "卖回时污点按退回的比例回来");
|
Check(acct.Taint < 400 && acct.Taint > 359.2, "卖回时污点按退回的比例回来");
|
||||||
// 持仓上限
|
// 持仓上限
|
||||||
var big = new Wallet { Address = "x" }; big.Add(Assets.CRC, 1e7);
|
var big = new Wallet { Address = "x" }; big.Add(Assets.CRC, 1e7);
|
||||||
@@ -1103,7 +1103,7 @@ internal static class Program
|
|||||||
acct.Add(Assets.CRC, 100000);
|
acct.Add(Assets.CRC, 100000);
|
||||||
var bk = new PredictBook();
|
var bk = new PredictBook();
|
||||||
// 观测:第一晚没有 Prev,不算 Started
|
// 观测:第一晚没有 Prev,不算 Started
|
||||||
bk.Night(4, new[] { "oxymoreWave" }, m => null, acct, rng);
|
bk.Night(4, new[] { "oxymoreWave" }, m => null, _ => acct, rng);
|
||||||
Check(!bk.Started.ContainsKey(4), "第一晚没有对照,不记新出现的事件");
|
Check(!bk.Started.ContainsKey(4), "第一晚没有对照,不记新出现的事件");
|
||||||
var win = PredictMarket.Create(++bk.Seq, PredictKind.Event, "powerShortage", "停电", 0, 4, 5, 6, 0.2);
|
var win = PredictMarket.Create(++bk.Seq, PredictKind.Event, "powerShortage", "停电", 0, 4, 5, 6, 0.2);
|
||||||
var lose = PredictMarket.Create(++bk.Seq, PredictKind.Event, "blackout", "x", 0, 4, 5, 6, 0.2);
|
var lose = PredictMarket.Create(++bk.Seq, PredictKind.Event, "blackout", "x", 0, 4, 5, 6, 0.2);
|
||||||
@@ -1113,9 +1113,9 @@ internal static class Program
|
|||||||
bk.Buy(acct, lose, true, 51, 4, pr, false, out _);
|
bk.Buy(acct, lose, true, 51, 4, pr, false, out _);
|
||||||
bk.Buy(acct, voided, true, 51, 4, pr, false, out _);
|
bk.Buy(acct, voided, true, 51, 4, pr, false, out _);
|
||||||
double cash = acct.Get(Assets.CRC);
|
double cash = acct.Get(Assets.CRC);
|
||||||
bk.Night(5, new[] { "oxymoreWave", "powerShortage" }, bk.Happened, acct, rng);
|
bk.Night(5, new[] { "oxymoreWave", "powerShortage" }, bk.Happened, _ => acct, rng);
|
||||||
Check(win.Outcome < 0 && lose.Outcome < 0, "窗口没结束不结算");
|
Check(win.Outcome < 0 && lose.Outcome < 0, "窗口没结束不结算");
|
||||||
var evs = bk.Night(6, new[] { "oxymoreWave", "powerShortage" }, bk.Happened, acct, rng);
|
var evs = bk.Night(6, new[] { "oxymoreWave", "powerShortage" }, bk.Happened, _ => acct, rng);
|
||||||
Check(win.Outcome == -1 || bk.Markets.All(x => x.Id != win.Id), "到期的盘口移出列表");
|
Check(win.Outcome == -1 || bk.Markets.All(x => x.Id != win.Id), "到期的盘口移出列表");
|
||||||
Check(evs.Any(e => e.Kind == "win" && e.Title == "停电" && Math.Abs(e.Amount - sw) < 1e-6), "押中兑付份数");
|
Check(evs.Any(e => e.Kind == "win" && e.Title == "停电" && Math.Abs(e.Amount - sw) < 1e-6), "押中兑付份数");
|
||||||
Check(evs.Any(e => e.Kind == "lose" && e.Title == "x"), "押错没收");
|
Check(evs.Any(e => e.Kind == "lose" && e.Title == "x"), "押错没收");
|
||||||
@@ -1123,7 +1123,7 @@ internal static class Program
|
|||||||
Check(bk.Markets.Count == 1 && bk.Markets[0].Id == voided.Id, "后面的窗口还没到");
|
Check(bk.Markets.Count == 1 && bk.Markets[0].Id == voided.Id, "后面的窗口还没到");
|
||||||
// 缺观测:8 号之前 7、8 的记录缺,到期作废退本金
|
// 缺观测:8 号之前 7、8 的记录缺,到期作废退本金
|
||||||
double c2 = acct.Get(Assets.CRC);
|
double c2 = acct.Get(Assets.CRC);
|
||||||
var ev2 = bk.Night(9, new string[0], bk.Happened, acct, rng);
|
var ev2 = bk.Night(9, new string[0], bk.Happened, _ => acct, rng);
|
||||||
Check(ev2.Any(e => e.Kind == "void") && Math.Abs(acct.Get(Assets.CRC) - c2 - 50) < 1e-6, $"缺数据作废并退本金:{acct.Get(Assets.CRC) - c2:0.00}");
|
Check(ev2.Any(e => e.Kind == "void") && Math.Abs(acct.Get(Assets.CRC) - c2 - 50) < 1e-6, $"缺数据作废并退本金:{acct.Get(Assets.CRC) - c2:0.00}");
|
||||||
// 内幕被查:持仓达到上限的 40% 就有概率;200 次里大约一半
|
// 内幕被查:持仓达到上限的 40% 就有概率;200 次里大约一半
|
||||||
int caught = 0, wins = 0;
|
int caught = 0, wins = 0;
|
||||||
@@ -1134,7 +1134,7 @@ internal static class Program
|
|||||||
double target = Tuning.PredictInsiderShare * mk.ShareCap * 1.05, spend = 0;
|
double target = Tuning.PredictInsiderShare * mk.ShareCap * 1.05, spend = 0;
|
||||||
spend = (mk.BuyCost(true, target)) * (1 + Tuning.PredictFee);
|
spend = (mk.BuyCost(true, target)) * (1 + Tuning.PredictFee);
|
||||||
b2.Buy(a2, mk, true, spend, 4, pr, true, out _);
|
b2.Buy(a2, mk, true, spend, 4, pr, true, out _);
|
||||||
var e = b2.Night(5, new string[0], mm => true, a2, new Rng((ulong)(i + 1)));
|
var e = b2.Night(5, new string[0], mm => true, _ => a2, new Rng((ulong)(i + 1)));
|
||||||
if (e[0].Kind == "caught") { caught++; Check(e[0].Fraud > 0 && e[0].Fraud <= Tuning.PredictFraudCap, "罪证有上限"); }
|
if (e[0].Kind == "caught") { caught++; Check(e[0].Fraud > 0 && e[0].Fraud <= Tuning.PredictFraudCap, "罪证有上限"); }
|
||||||
else wins++;
|
else wins++;
|
||||||
}
|
}
|
||||||
@@ -1143,7 +1143,7 @@ internal static class Program
|
|||||||
var b3 = new PredictBook(); var a3 = new Wallet { Address = "p" }; a3.Add(Assets.CRC, 1e6);
|
var b3 = new PredictBook(); var a3 = new Wallet { Address = "p" }; a3.Add(Assets.CRC, 1e6);
|
||||||
var m3 = PredictMarket.Create(++b3.Seq, PredictKind.Event, "e", "t", 0, 4, 5, 5, 0.1); b3.Markets.Add(m3);
|
var m3 = PredictMarket.Create(++b3.Seq, PredictKind.Event, "e", "t", 0, 4, 5, 5, 0.1); b3.Markets.Add(m3);
|
||||||
b3.Buy(a3, m3, true, m3.BuyCost(true, 0.2 * m3.ShareCap) * 1.02, 4, pr, true, out _);
|
b3.Buy(a3, m3, true, m3.BuyCost(true, 0.2 * m3.ShareCap) * 1.02, 4, pr, true, out _);
|
||||||
Check(b3.Night(5, new string[0], mm => true, a3, new Rng(1))[0].Kind == "win", "小额内幕安全");
|
Check(b3.Night(5, new string[0], mm => true, _ => a3, new Rng(1))[0].Kind == "win", "小额内幕安全");
|
||||||
// 被查:只退本金
|
// 被查:只退本金
|
||||||
var b4 = new PredictBook(); var a4 = new Wallet { Address = "p" }; a4.Add(Assets.CRC, 1e6);
|
var b4 = new PredictBook(); var a4 = new Wallet { Address = "p" }; a4.Add(Assets.CRC, 1e6);
|
||||||
var m4 = PredictMarket.Create(++b4.Seq, PredictKind.Event, "e", "t", 0, 4, 5, 5, 0.1); b4.Markets.Add(m4);
|
var m4 = PredictMarket.Create(++b4.Seq, PredictKind.Event, "e", "t", 0, 4, 5, 5, 0.1); b4.Markets.Add(m4);
|
||||||
@@ -1152,14 +1152,44 @@ internal static class Program
|
|||||||
double c4 = a4.Get(Assets.CRC);
|
double c4 = a4.Get(Assets.CRC);
|
||||||
for (ulong sd = 1; sd < 50; sd++)
|
for (ulong sd = 1; sd < 50; sd++)
|
||||||
{
|
{
|
||||||
var bb = new PredictBook(); bb.Markets.Add(m4); bb.Positions.AddRange(b4.Positions.Select(x => new PredictPos { Market = x.Market, Yes = x.Yes, Shares = x.Shares, Cost = x.Cost, Insider = x.Insider }));
|
var bb = new PredictBook(); bb.Markets.Add(m4); bb.Positions.AddRange(b4.Positions.Select(x => new PredictPos { Market = x.Market, Wallet = x.Wallet, Yes = x.Yes, Shares = x.Shares, Cost = x.Cost, Insider = x.Insider }));
|
||||||
var aa = new Wallet { Address = "p" };
|
var aa = new Wallet { Address = "p" };
|
||||||
m4.Outcome = -1;
|
m4.Outcome = -1;
|
||||||
var ee = bb.Night(5, new string[0], mm => true, aa, new Rng(sd));
|
var ee = bb.Night(5, new string[0], mm => true, _ => aa, new Rng(sd));
|
||||||
if (ee[0].Kind == "caught") { Check(Math.Abs(aa.Get(Assets.CRC) - (b4.Positions[0].Cost)) < 1e-6, "被查只退本金"); break; }
|
if (ee[0].Kind == "caught") { Check(Math.Abs(aa.Get(Assets.CRC) - (b4.Positions[0].Cost)) < 1e-6, "被查只退本金"); break; }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Test("预测市场:多钱包持仓各回各的钱包,限额与内幕按盘口合计", () =>
|
||||||
|
{
|
||||||
|
Func<string, double> pr = a => 1;
|
||||||
|
var w1 = new Wallet { Address = "0xa" }; var w2 = new Wallet { Address = "0xb" };
|
||||||
|
w1.Add(Assets.CRC, 1e6); w2.Add(Assets.CRC, 1e6);
|
||||||
|
Func<string, Wallet> pick = ad => ad == "0xa" ? w1 : w2;
|
||||||
|
var bk = new PredictBook();
|
||||||
|
var m = PredictMarket.Create(++bk.Seq, PredictKind.Event, "e", "t", 0, 4, 5, 5, 0.1); bk.Markets.Add(m);
|
||||||
|
double half = Tuning.PredictInsiderShare * m.ShareCap * 0.6;
|
||||||
|
Check(bk.Buy(w1, m, true, m.BuyCost(true, half) * 1.02, 4, pr, true, out _) == PredictResult.Ok, "钱包 A 买入");
|
||||||
|
Check(bk.Buy(w2, m, false, 10, 4, pr, false, out _) == PredictResult.Opposite, "另一个钱包也不能买对面");
|
||||||
|
Check(bk.Buy(w2, m, true, m.BuyCost(true, half) * 1.02, 4, pr, true, out _) == PredictResult.Ok, "钱包 B 买入同一边");
|
||||||
|
Check(bk.PosAll(m.Id).Count == 2 && bk.PosOf(m.Id, "0xa") != null && bk.PosOf(m.Id, "0xb") != null, "每个钱包各一个持仓");
|
||||||
|
Check(bk.SellAll(w2, m, 4, out double got) == PredictResult.Ok && got > 0 && bk.PosOf(m.Id, "0xb") == null && bk.PosOf(m.Id, "0xa") != null, "卖出只动自己钱包的持仓");
|
||||||
|
Check(bk.Buy(w2, m, true, m.BuyCost(true, half) * 1.02, 4, pr, true, out _) == PredictResult.Ok, "再买");
|
||||||
|
// 两边各 0.6×查处线,合计超线:拆钱包躲不过
|
||||||
|
int caught = 0;
|
||||||
|
for (ulong sd = 1; sd <= 60; sd++)
|
||||||
|
{
|
||||||
|
var b2 = new PredictBook(); b2.Markets.Add(new PredictMarket { Id = m.Id, Kind = m.Kind, Key = m.Key, Title = m.Title, FromDay = 5, ToDay = 5, P0 = m.P0, B = m.B, QYes = m.QYes, QNo = m.QNo });
|
||||||
|
foreach (var p in bk.Positions) b2.Positions.Add(new PredictPos { Market = p.Market, Wallet = p.Wallet, Yes = p.Yes, Shares = p.Shares, Cost = p.Cost, Insider = p.Insider });
|
||||||
|
double a0 = w1.Get(Assets.CRC), b0 = w2.Get(Assets.CRC);
|
||||||
|
var ev = b2.Night(5, new string[0], x => true, pick, new Rng(sd));
|
||||||
|
if (ev[0].Kind == "caught") caught++;
|
||||||
|
else Check(w1.Get(Assets.CRC) > a0 && w2.Get(Assets.CRC) > b0, "赢了各自的钱包都收到兑付");
|
||||||
|
w1.Balances[Assets.CRC] = a0; w2.Balances[Assets.CRC] = b0;
|
||||||
|
}
|
||||||
|
Check(caught > 15 && caught < 45, $"分到两个钱包的内幕持仓合计后约一半被查:{caught}/60");
|
||||||
|
});
|
||||||
|
|
||||||
Test("预测市场:先验概率", () =>
|
Test("预测市场:先验概率", () =>
|
||||||
{
|
{
|
||||||
Check(Math.Abs(PredictMath.PurityAtLeast(8500, 8000, 8999) - 0.5) < 1e-9, "纯度 8500 以上一半");
|
Check(Math.Abs(PredictMath.PurityAtLeast(8500, 8000, 8999) - 0.5) < 1e-9, "纯度 8500 以上一半");
|
||||||
|
|||||||
Reference in new issue
Block a user