docs(sec): SEC-9 закрыт — возврат залога при ничьей
Сводка: открыто 8 (было 9), закрыто 8. SEC-9 перенесён в закрытые с полной историей: причина (close_bet.rs:66), замер доли ничьих (33.6%), решение владельца (правка контракта), состав правок, собственная мутационная приёмка, деплой и живой эффект (залипшие #0/#1 закрылись сами, ваулт −1000). Хвост SEC-8 о «вечном залипании» помечен как устранённый. Инструменты приёмки: - tie-refund-accept.cjs — ставки циклом, проверка статуса на цепи (живой прогон: 6 возвратов из 25 ставок) - tie-distribution.cjs, policy-economics.cjs — выбор политики ничьей - vault-check.cjs — разбор Bet-аккаунтов сырыми байтами
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
// Экономика: что делает каждая политика ничьей с балансом дома.
|
||||
// Считаем по реальному распределению |Δ| из живой истории.
|
||||
// Ставка 1 фантик, выигрыш 1.3× (multiplier_bps=13000). Ничья: возврат 1.0 (залог целиком).
|
||||
const { DatabaseSync } = require('node:sqlite');
|
||||
const db = new DatabaseSync(process.env.DB || '/app/data/price-history.db');
|
||||
const rows = db.prepare('SELECT ts, price FROM price_history ORDER BY ts ASC').all();
|
||||
const vals = rows.map((r) => ({ ts: Number(r.ts), p: Number(r.price) }));
|
||||
const SCALE = 1e8, W = 15000, STEP = 1000;
|
||||
const at = (ts) => { let lo = 0, hi = vals.length - 1, res = null;
|
||||
while (lo <= hi) { const m = (lo + hi) >> 1;
|
||||
if (vals[m].ts <= ts) { res = vals[m].p; lo = m + 1; } else hi = m - 1; } return res; };
|
||||
|
||||
const hist = {};
|
||||
let n = 0;
|
||||
for (let t = vals[0].ts; t + W <= vals[vals.length - 1].ts; t += STEP) {
|
||||
const a = at(t), b = at(t + W);
|
||||
if (a === null || b === null) continue;
|
||||
const kop = Math.round(Math.abs(b - a) / SCALE * 100);
|
||||
hist[kop] = (hist[kop] || 0) + 1; n++;
|
||||
}
|
||||
|
||||
// Считаем деньги. Игрок ставит вслепую (случайный UP/DOWN) => половина окон выигрышная,
|
||||
// половина проигрышная. На ничьих политика решает.
|
||||
const MULT = 1.3;
|
||||
function simulate(policy) {
|
||||
let vaultDelta = 0, refunded = 0, decided = 0;
|
||||
for (const [k, cnt] of Object.entries(hist)) {
|
||||
const kop = Number(k);
|
||||
if (kop === 0 && policy === 'refund') { vaultDelta += 0; refunded += cnt; continue; }
|
||||
if (kop === 0 && policy === 'neighbour') { /* трактуем как решение: половина выиграла */
|
||||
vaultDelta += cnt * (0.5 * (1 - MULT) + 0.5 * 1); decided += cnt; continue; }
|
||||
if (kop === 0 && policy === 'house') { vaultDelta += cnt * 1; continue; } // дом забирает
|
||||
decided += cnt;
|
||||
vaultDelta += cnt * (0.5 * (1 - MULT) + 0.5 * 1);
|
||||
}
|
||||
return { vaultDelta, refunded, decided };
|
||||
}
|
||||
console.log('окон:', n);
|
||||
for (const p of ['refund', 'neighbour', 'house']) {
|
||||
const r = simulate(p);
|
||||
const per = r.vaultDelta / n;
|
||||
console.log(`политика=${p.padEnd(10)} дом за окно=${per.toFixed(4)} фантика | возвращено окон=${r.refunded} (${((100*r.refunded)/n).toFixed(1)}%)`);
|
||||
}
|
||||
console.log('--- при 1000 ставках ---');
|
||||
for (const p of ['refund', 'neighbour', 'house']) {
|
||||
const r = simulate(p);
|
||||
console.log(` ${p.padEnd(10)} дом: ${(r.vaultDelta / n * 1000).toFixed(1)} фантиков на 1000 ставок`);
|
||||
}
|
||||
Reference in New Issue
Block a user