Тема
147: Restore merge-arb strategy as layer
Identity
- Status: restoration design (open, prereq blockers)
- Spawned from: F6 of ADR-024 pivot, 2026-05-13
- Original module:
crates/pm-bot/src/domain/strategies/merge_arb.rs(~1843 LOC at archive) - Recovery:
git show 619f7f0:crates/pm-bot/src/domain/strategies/merge_arb.rs - Target taxonomy:
binary-arb::JitterCaptureprofile (per ADR-018) - Original ADR: ADR-003 (merge-arb-strategy, ACCEPTED → ARCHIVED-BY-024)
- Type: restoration
Context
Merge-arb captures jitter on 50/50 binary markets by accumulating both YES and NO inventory at sub-mid prices, then calling on-chain mergePositions for collateral recovery + maker rebate. For neg-risk markets (~63.5% of production per Agent A), NegRiskAdapter.convertPositions is the analogue path.
Data model needs (from polymarket-data-model.md)
Market.tokens[0..2].best_bid/best_ask— both sidesMarket.neg_risk— gates merge vs convert pathMarket.condition_id— required for bothmergePositionsandconvertPositionsMarket.tick_size— both sides- Per-side orderbook depth for accumulation pacing
Restoration scope
- New module:
crates/pm-bot/src/domain/strategies/merge_arb.rs(layer) - Profile
JitterCaptureper ADR-018 §binary-arb - Implement
ScannerHooks+Strategytrait per ADR-025 - Strategy config:
merge_arb.*knobs (entry_band, accumulation_cap, merge_threshold, etc.) - Add
StrategyType::MergeArbvariant back + dispatcher branch - Config schema: new
crates/pm-bot/src/config/strategy/merge_arb.rs(recover from 619f7f0)
Prerequisites (CRITICAL BLOCKERS)
pm_sdk::onchain::ctf::convert_positionsотсутствует (per Agent B finding). NegRiskAdapterconvertPositions(bytes32 conditionId, uint256 amount)semantics required per Agent A Q-A2. Hard blocker until implemented in pm-sdk.- NEW Decision variant:
Decision::CallConvertPositions { condition_id, amount }— extension Decision enum must route to NegRiskAdapter executor. - NEW Executor handler: route
CallConvertPositionstopm_sdk::onchain::neg_risk_adapter. - Existing
Decision::CallMergePositions(standard CTF path) for non-neg-risk subset.
Restoration prereq commit chain:
- Implement
pm_sdk::onchain::ctf::convert_positions(or underpm_sdk::onchain::neg_risk_adapter) — NegRiskAdapter contract call wrapper - Add
Decision::CallConvertPositionsvariant - Add executor handler routing to NegRiskAdapter
- Restoration of merge-arb strategy itself
Out of scope
- Не модифицировать core engine за пределами ScannerHooks + Decision variant + executor handler.
- Не trash strategy isolation invariant.
Acceptance criteria
- ScannerHooks impl green per contract test pattern
convertPositionsintegration test against fork (или mocked Polygon RPC)- Neg-risk-aware merge gating: standard CTF path для non-neg-risk, NegRiskAdapter для neg-risk
- VERIFIED markers per
feedback_silent_feature_broken - ADR-003 amendment OR new ADR confirming restoration semantics + neg-risk path
Dependencies
- HARD blocker: pm-sdk
convert_positionsimplementation (separate commit/PR). - Independent of #145, #146, #148, #149.
LOC estimate
- pm-sdk convert_positions wrapper: ~150-250 LOC + tests
- Decision variant + executor: ~200-300 LOC
- New strategy: ~1500-2000 LOC (originally 1843; ReadCtx-slimmer)
- New strategy config: ~150 LOC
- Tests: ~500-800 LOC (fork test for convertPositions critical)
- Total (prereq + strategy): ~3000-4000 LOC
References
- ADR-024, ADR-025, ADR-003
- 619f7f0 (archive reference)
- .tmp/audit-reports/A-data-model.md Q-A2 (convertPositions semantics)
- .tmp/audit-reports/B-pm-sdk.md (convert_positions gap)
- docs/architecture/polymarket-data-model.md (neg_risk distribution)