Files
pump-game-ops/tools/policy-economics.cjs
T
subochev b8a1f8dc39 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-аккаунтов сырыми байтами
2026-09-14 02:19:28 +03:00

49 lines
2.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Экономика: что делает каждая политика ничьей с балансом дома.
// Считаем по реальному распределению |Δ| из живой истории.
// Ставка 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 ставок`);
}