OPTIMAL — финальный алгоритм transit-налива
5-слойная архитектура замены текущего 7-key ORDER BY. Доказано в prod-faithful симуляции v5 (24+ итераций sprint'ов, 48ч прогон, 3 runs avg).
Индия · UPI P2P · INR
Виджет: Orbit / MoXB / MRGB
Tier-A: KPMI P2P + INTENT
Реализация: ~1 месяц
✅ Достигнутые цели
✓
Tier-A p90
36 мин
target ≤ 60 мин
было: 8.2ч (КPMI) → стало −97%
✓
Tier-B p90
143 мин
target ≤ 180 мин
было: 11.9ч (Orbit) → стало −80%
✓
Loss budget
0.44 %
target ≤ 2 %
было: 2.5% → стало в 5.7× ниже
🔄 Как работает (визуально)
Вход
Новый payin от Orbit / MoXB / MRGB виджета
↓
L2: Sender scoring
Определить bucket sender'а из истории. RISKY (≥5 prior, ≤10% paid) → 80% сразу в trader pool.
↓
L1: Filter кандидатов
Asymmetric tolerance (payin ≥ payout), per-merchant partial/full split, фильтрация blocked / problematic.
↓
L3: Score-функция (вместо ORDER BY)
Рассчитать utility для каждого кандидата по формуле ниже. Выбрать max.
↓
Match
Закрыть payout. Если переплата → L5 customerOverpayLedger (refund-batch), не тихий gift.
⤴ при failure
L4: Failure recovery
H-2 sync unlock (0 мин). Exp-backoff: 1ч после 3, 2ч после 4, 4ч после 5. Skip-problematic после 4ч.
🧩 5-слойная архитектура
1Source-side— расширение кандидатов
- Asymmetric tolerance:
payout ≤ payin ≤ payout + 199 ₹. Переплата OK, недоплата запрещена.
- Per-merchant partial split: KPMI / MDS / MKC / MPUP → full-match only. ORBIT / MRJB → partial allowed.
- Round-500 для full-match мерчантов требует переговоров с KPMI. Coverage 100% при exact-match.
2Sender scoring (Variant 1, rule-based)— 4 bucket'а по истории
TRUSTED
86%
≥5 prior, ≥90% paid, 0 appeals
→ score +30, fast-track
STANDARD
59%
mid-history
→ default
COLD
47%
new, <5 prior
→ default
RISKY
6%
≥5 prior, ≤10% paid
→ 80% fast-cancel
3Selection score-функция— вместо 7-key ORDER BY
utility(payin, payout) =
+ 1.2 × tier_bonus
− 0.5 × age
− 0.5 × overpay_amount
− 1.0 × remainder_amount
− 1.2 × death_march_penalty
+ 1.3 × sender_bonus
− 1000 × exact_mismatch
− 100 × is_problematic_payout
pick max(utility)
4Failure recovery— уничтожить хвост
- H-2 sync unlock: unlock = 0 мин (убираем acqUnlocker 3-мин cycle)
- Exponential backoff: lock 1ч после 3 failures, 2ч после 4, 4ч после 5, 8ч после 6 (вместо forever after 5)
- Skip-problematic: 1.5% «проблемных» payouts (bimodal cluster из prod-данных) выкидываются после 4ч → free trader pool
maxFailures = 5 — не 3, агрессивнее ухудшает результат
5Overpay handling— честный учёт
customerOverpayLedger вместо тихого Expense(SYSTEM_ERRORS)
- Batch refund job для возврата переплат клиенту
- Эффект: Loss% с 2.09% (preset E) → 0.44% (×4.7 снижение)
📊 Сравнение пресетов (sim v5)
48ч прогон, 3 runs avg. Baseline overshoot ×1.6 vs prod 672м → реальные результаты могут быть лучше прогноза.
Полные данные
| Preset | p50 | p90 | Tier-A p90 | Tier-B p90 | Loss% | Closed% |
| A) PROD current (baseline) | 197м | 1076м | 1038м | 1112м | 0.86% | 63% |
| B) H-2 unlock only | 35м | 978м | 949м | 1008м | 0.84% | 69% |
| C) Score-fix only | 4м | 194м | 163м | 236м | 1.74% | 63% |
| D) + Round-500 | 2м | 136м | 47м | 204м | 2.07% | 55% |
| E) + Sender scoring | 1м | 98м | 37м | 144м | 2.09% | 59% |
| G) OPTIMAL ★ | 1м | 98м | 36м | 143м | 0.44% | 59% |
| F) BEST (max aggressive) | 2м | 143м | 60м | 182м | 0.42% | 59% |
🗑️ Что выкидываем из текущего алгоритма
| Текущее поведение | Почему убираем | Замена |
| Old-day-first (ключ 1 ORDER BY) |
Positive feedback loop: старые → ещё старее. Главный источник хвоста. |
Возрастной decay в score: −0.5 × age |
| Roundness guard (сохранять круглый остаток) |
Оптимизирует под бот, генерирует death-march остатки |
Smart roundness — штраф за создание узкого окна |
| Forever-exclusion после 5 failures |
Dead inventory растёт линейно |
Exp-backoff: 1ч/2ч/4ч/8ч… |
| Симметричный BETWEEN tolerance |
Разрешает недоплату (бизнесово запрещено) |
Asymmetric: только payin ≥ payout |
| 3-мин acqUnlocker cycle |
Каждый stuck = 3 мин простоя минимум |
H-2 sync unlock (0 мин) |
| Global partial flag |
KPMI требует full, Orbit разрешает partial |
Per-merchant config |
| Gift переплат в Expense |
Тихая потеря денег без шанса на refund |
customerOverpayLedger + batch-refund |
📅 Implementation roadmap
Неделя 1 · параллельно
Переговоры с KPMI про Round-500 (внешний трек, не от нас) внешняя зависимость
Неделя 1-2 · код keystone
Asymmetric tolerance (WHERE clause) ·
Score-функция (заменить ORDER BY) ·
H-2 sync unlock ·
Exp-backoff failures
Неделя 2 · новая модель данных
customerOverpayLedger таблица + batch refund job + замена createExpense на ledger entry
Неделя 2-3 · scoring
Sender scoring V1 (4-bucket на SenderAccountPayinAggregate, без ML)
Неделя 3 · production A/B
Ramp: 5% → 20% → 50% → 100% трафика, каждый шаг 24-48ч наблюдения
Неделя 4 · доводка
Финальная калибровка весов score-функции на real-data feedback
⚠️ Caveats
Sim baseline overshoot ×1.6. В sim p90 = 1076м, в реальности 672м. Это значит модель чуть пессимистична — на реальном prod результаты OPTIMAL могут быть лучше прогноза.
Round-500 — внешняя зависимость. Если переговоры с KPMI не сработают — Tier-A может остановиться на ~80-100м (всё ещё в 5-6× лучше текущих 8.2ч, но не <60м).
Sender scoring V1 — простой rule-based. Через 2-3 недели данных Logging D можно поднять до LR/GBM модели на behavioral features.
Симуляция упрощает реальность. Не моделирует banks compatibility, mutex contentions. Validation через prod A/B обязателен.
🔗 Ссылки
- Интерактивный симулятор — играй с параметрами в браузере
- BPMN-диаграмма transit-flow — все исходы и точки потерь
/home/user/projects/transit/sim/best_algorithm_v5.js — финальный Node.js симулятор
/home/user/projects/transit/sim/results_v5.json — полные результаты
~/.claude/projects/-home-user-projects-transit/memory/transit_optimal_algorithm.md — спецификация в memory