1. Bot Identity
| Layer | Strategy Strategy |
|---|
| Bot class | Alpha Strategy |
|---|
| Authority | Trade |
|---|
| Status | PLANNED |
|---|
| Readiness | Spec started |
|---|
| Runs before | Risk guardrail pipeline |
|---|
| Runs after | Market scanner / Observation bus |
|---|
| Applies to | Polymarket binary markets that are gaining attention but currently have wide spreads and thin depth, offering attractive conditions for early market-making analytics |
|---|
| Default mode | shadow_only |
|---|
| User-visible | Advanced details only |
|---|
| Developer owner | Polytraders core — Strategy pod |
|---|
2. Purpose
Liquidity-Pulse is a market-analytics tool that monitors emerging Polymarket markets for early liquidity opportunities. When a market's volume and attention signal crosses configured thresholds, the bot emits an OrderIntent for user-controlled maker quote placement. All execution decisions remain with the user; this bot provides structured analytics and sizing recommendations only.
3. Why This Bot Matters
Quote placed before sufficient volume confirmation
Early quotes on unproven markets may sit unmatched or attract adverse selection from informed traders who discovered the market first.
Spread tightening faster than quote updates
Quotes placed at the initial wide spread become the worst price on the book as other makers tighten, leading to adverse selection.
Market resolves before position unwinds
Short-lived markets may reach resolution before an early maker position can be exited, forcing settlement at a potentially unfavorable resolution price.
No worked examples on this bot yet. Worked examples are optional but strongly recommended — they turn an abstract failure mode into something a developer can verify in a fixture.
6. Parameter Guide
| Parameter | Default | Warning | Hard | What it controls |
|---|
| initial_spread_bps | 200 | 100 | 50 | Initial maker quote spread in bps around the current mid-price for early-liquidity quote recommendations. |
| tighten_at_volume_usd | 5000 | 2000 | 500 | Total traded volume threshold in pUSD above which the bot recommends tightening the spread by 50%. |
| max_pre_event_position_usd | 300 | 500 | 750 | Maximum pUSD maker position recommended per market during the pre-event attention phase. |
| event_lead_time_h | 24 | 6 | 1 | Minimum hours before the event for the bot to emit early-liquidity quote recommendations. Avoids posting too close to resolution. |
7. Detailed Parameter Instructions
initial_spread_bps
What it means
Initial maker quote spread in bps around the current mid-price for early-liquidity quote recommendations.
Default
{ "initial_spread_bps": 200 }
Why this default matters
200 bps provides sufficient maker edge on thin, uncertain markets before volume develops.
Threshold logic
| Condition | Action |
|---|
| >= 200 bps | Standard early-liquidity quote |
| 100–200 bps | WARN LP_TIGHT_SPREAD; reduced confidence |
| < 50 bps | HARD_REJECT LP_SPREAD_BELOW_FLOOR |
Developer check
if params.initial_spread_bps < params.hard: return skip('LP_SPREAD_BELOW_FLOOR')
User-facing English
The recommended spread is too tight for the current market conditions.
tighten_at_volume_usd
What it means
Total traded volume threshold in pUSD above which the bot recommends tightening the spread by 50%.
Default
{ "tighten_at_volume_usd": 5000 }
Why this default matters
5000 pUSD signals enough market activity to justify tighter spreads with lower adverse-selection risk.
Threshold logic
| Condition | Action |
|---|
| >= 5000 pUSD volume | Tighten spread recommendation by 50% |
| < 500 pUSD | Maintain wide initial spread |
Developer check
if total_volume >= params.tighten_at_volume_usd: spread = initial_spread_bps * 0.5
User-facing English
Volume has reached the threshold for a tighter spread recommendation.
max_pre_event_position_usd
What it means
Maximum pUSD maker position recommended per market during the pre-event attention phase.
Default
{ "max_pre_event_position_usd": 300 }
Why this default matters
300 pUSD limits adverse-selection exposure on thin pre-event markets.
Threshold logic
| Condition | Action |
|---|
| <= 300 pUSD | Normal pre-event sizing |
| > 750 pUSD | Reject config — PARAMETER_CHANGE_REQUIRES_APPROVAL |
Developer check
if params.max_pre_event_position_usd > params.hard: raise ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL')
User-facing English
Position size was capped at the configured pre-event maximum.
event_lead_time_h
What it means
Minimum hours before the event for the bot to emit early-liquidity quote recommendations. Avoids posting too close to resolution.
Default
{ "event_lead_time_h": 24 }
Why this default matters
24h lead time ensures the market has time to develop depth before resolution risk dominates.
Threshold logic
| Condition | Action |
|---|
| >= 24h | Standard lead time |
| 6–24h | WARN LP_SHORT_LEAD_TIME; halve max position |
| < 1h | HARD_REJECT LP_TOO_CLOSE_TO_RESOLUTION |
Developer check
if hours_to_event < params.hard: return skip('LP_TOO_CLOSE_TO_RESOLUTION')
User-facing English
The market is too close to its resolution time for an early liquidity position.
8. Default Configuration
{
"bot_id": "strat.liquidity_pulse",
"version": "0.1.0",
"mode": "shadow_only",
"defaults": {
"initial_spread_bps": 200,
"tighten_at_volume_usd": 5000,
"max_pre_event_position_usd": 300,
"event_lead_time_h": 24
},
"locked": {
"initial_spread_bps": {
"min": 50
},
"max_pre_event_position_usd": {
"max": 750
},
"event_lead_time_h": {
"min": 1
}
}
}
9. Implementation Flow
- Check KillSwitch; if active, emit no OrderIntents.
- FETCH gamma market metadata; compute hours_to_event.
- IF hours_to_event < hard (1h): skip LP_TOO_CLOSE_TO_RESOLUTION.
- FETCH attention signal from market scanner; if below threshold, skip.
- FETCH ws_market book; compute current spread and total_volume.
- Compute recommended spread: initial_spread_bps * (0.5 if total_volume >= tighten_at_volume_usd else 1.0).
- IF recommended_spread < spread_hard (50 bps): skip LP_SPREAD_BELOW_FLOOR.
- Compute position_size = min(max_pre_event_position_usd, available_capital).
- Adjust size down 50% if hours_to_event < warning (6h).
- EMIT OrderIntent: post_only=true, bid=mid-spread/2, ask=mid+spread/2, size=position_size, builder=code.
- EMIT DecisionReport with intent_emitted=true, reason=LP_QUOTE_EMITTED.
10. Reference Implementation
Pseudocode is language-agnostic. FETCH = read input. EMIT = produce output. IF/THEN/ELSE = decision. Translate directly to TypeScript, Python, Go, or Rust.
FUNCTION onAttentionSignal(market_id, attentionSignal):
ks = FETCH internal.killswitch.status
IF ks.active: RETURN
// Resolve event lead time
meta = FETCH gamma.GET('/markets/' + market_id)
hoursToEvent = (meta.end_date_ms - now_ms()) / 3600000
IF hoursToEvent < params.event_lead_time_h_hard: // 1h
EMIT DecisionReport(intent_emitted=false, reason='LP_TOO_CLOSE_TO_RESOLUTION')
RETURN
// Book and volume snapshot
book = FETCH ws_market.book(market_id)
totalVolume = book.total_traded_volume_usd
mid = (book.best_bid + book.best_ask) / 2
// Spread computation
spreadBps = params.initial_spread_bps
IF totalVolume >= params.tighten_at_volume_usd:
spreadBps = spreadBps * 0.5
IF spreadBps < params.initial_spread_bps_hard: // 50 bps
EMIT DecisionReport(intent_emitted=false, reason='LP_SPREAD_BELOW_FLOOR')
RETURN
IF spreadBps < params.initial_spread_bps_warn: // 100 bps
WARN('LP_TIGHT_SPREAD')
// Sizing
sizeMultiplier = 0.5 IF hoursToEvent < params.event_lead_time_h_warn ELSE 1.0 // < 6h
orderSize = toPusdUnits(params.max_pre_event_position_usd * sizeMultiplier)
bidPrice = mid - (spreadBps / 20000)
askPrice = mid + (spreadBps / 20000)
EMIT OrderIntent(market=market_id, outcome='YES', side='buy', price=bidPrice,
size_pUSD=orderSize, tif='GTC', post_only=true, builder=internalBuilderCode)
EMIT DecisionReport(intent_emitted=true, spread_bps=spreadBps, reason='LP_QUOTE_EMITTED')
SDK calls used
ws_market.subscribe('book', [market_id])gamma.GET('/markets/' + market_id)fetchClobPublic('/markets/' + market_id)internal.killswitch.status()buildOrderTypedData(orderParams, {name:'CTFExchange', version:'2', chainId:137})
Complexity: O(1) per attention signal per market
11. Wire Examples
Input — what arrives on the wire
Attention signal — market gaining volume — internal (market scanner)
{
"market_id": "0xlp000000000000000000000000000000000000000000000000000000000000001",
"attention_score": 0.72,
"total_volume_usd": "850.0",
"hours_to_event": "36.0",
"received_at_ms": 1746790800000
}
Output — what the bot emits
OrderIntent — LP GTC post-only maker YES
{
"intent_id": "oi_01HLP0000001A",
"market_id": "0xlp000000000000000000000000000000000000000000000000000000000000001",
"outcome": "YES",
"side": "buy",
"price": "0.490",
"size_pUSD": "150.00",
"tif": "GTC",
"post_only": true,
"builder": {
"code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
"fee_bps": 10
},
"decision": {
"spread_bps": 200,
"reasons": [
"LP_QUOTE_EMITTED"
]
}
}
12. Decision Logic
APPROVE
hours_to_event >= 1h, attention signal above threshold, spread >= 50 bps, market open, KillSwitch inactive.
RESHAPE_REQUIRED
Not applicable — reshaping handled by downstream Risk guardrail.
REJECT
hours_to_event < 1h; spread < 50 bps; KillSwitch active.
WARNING_ONLY
hours_to_event 6–24h triggers warning and 50% position reduction; spread 100–200 bps triggers tight-spread warning.
13. Standard Decision Output
This bot returns a OrderIntent object. See OrderIntent schema.
{
"intent_id": "oi_01HLP0000001A",
"trace_id": "tr_01HLP000TR001",
"market_id": "0xlp000000000000000000000000000000000000000000000000000000000000001",
"outcome": "YES",
"side": "buy",
"price": "0.490",
"size_pUSD": "150.00",
"tif": "GTC",
"post_only": true,
"builder": {
"code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
"fee_bps": 10
},
"negrisk_aware": false,
"decision": {
"spread_bps": 200,
"hours_to_event": 36.0,
"total_volume_usd": 850.0,
"reasons": [
"LP_QUOTE_EMITTED"
]
},
"comment": "fees are operator-set at match time in V2 \u2014 feeRateBps is NOT on the signed order"
}
14. Reason Codes
| Code | Severity | Meaning | Action | User-facing message |
|---|
LP_QUOTE_EMITTED | INFO | Attention signal fired, spread viable, hours_to_event OK. GTC post-only maker OrderIntent emitted. | Emit GTC maker OrderIntent. | An early-liquidity maker quote was placed. |
LP_TOO_CLOSE_TO_RESOLUTION | HARD_REJECT | hours_to_event < 1h hard floor. Resolution risk too high for new maker positions. | Skip; no OrderIntent. | Too close to the event time for a new maker position. |
LP_TIGHT_SPREAD | WARN | Recommended spread between 100 and 200 bps; adverse selection risk elevated. | Emit with lower confidence; log warning. | The spread is tighter than ideal for early liquidity. |
LP_SPREAD_BELOW_FLOOR | HARD_REJECT | Computed spread < 50 bps hard floor. | Skip; no OrderIntent. | The spread was below the minimum required. |
KILL_SWITCH_ACTIVE | HARD_REJECT | Global kill switch is active. | Skip all markets; no OrderIntents emitted. | Trading is currently paused. |
15. Metrics & Logs
Metrics emitted
| Metric | Type | Unit | Labels | Meaning |
|---|
polytraders_strat_liquiditypulse_decisions_total | counter | count | verdict, reason_code | Total evaluation cycles by verdict and reason. |
polytraders_strat_liquiditypulse_spread_bps | histogram | basis_points | | Distribution of recommended spreads at quote emission. |
polytraders_strat_liquiditypulse_intents_emitted_total | counter | count | spread_tier | Total maker OrderIntents by spread tier (wide/tight). |
polytraders_strat_liquiditypulse_eval_latency_ms | histogram | milliseconds | | Latency from attention signal to OrderIntent emit. |
Alerts
| Alert | Condition | Severity | Runbook |
|---|
LiquidityPulseHighAdverseSelection | rate(polytraders_strat_liquiditypulse_decisions_total{reason_code='LP_TIGHT_SPREAD'}[5m]) > 0.3 | warn | #runbook-lp-spread |
LiquidityPulseKillSwitch | rate(polytraders_strat_liquiditypulse_decisions_total{reason_code='KILL_SWITCH_ACTIVE'}[1m]) > 0 | page | #runbook-killswitch |
LiquidityPulseStaleFeed | rate(polytraders_strat_liquiditypulse_decisions_total{reason_code='STALE_MARKET_DATA'}[5m]) > 0.1 | warn | #runbook-lp-stale |
16. Developer Reporting
{
"bot_id": "strat.liquidity_pulse",
"market_id": "0xlp000000000000000000000000000000000000000000000000000000000000001",
"spread_bps": 200,
"hours_to_event": 36.0,
"total_volume_usd": 850.0,
"intent_emitted": true,
"reason": "LP_QUOTE_EMITTED",
"emitted_at_ms": 1746790800000
}
17. Plain-English Reporting
| Situation | User-facing explanation |
|---|
| Early-liquidity quote placed | This market is attracting attention but still has a wide spread. A maker quote was placed to provide early liquidity at an attractive spread. |
| Market too close to resolution | Less than 1 hour remains before the event. No new maker positions were opened to avoid resolution-time risk. |
| Volume threshold reached — spread tightened | Traded volume crossed the threshold. The recommended spread was narrowed to remain competitive. |
18. Failure-Mode Block
| main_failure_mode | Adverse selection: informed traders arrive first and take liquidity at the initial wide spread before the market develops, leaving the maker with a systematically mispriced position. |
|---|
| false_positive_risk | Attention signal fires on markets that spike briefly but have no underlying trading interest, producing wasted quote placement. |
|---|
| false_negative_risk | event_lead_time_h too large misses genuine early-liquidity windows on rapidly developing markets. |
|---|
| safe_fallback | If ws_market feed stale or gamma metadata unavailable, skip without emitting any OrderIntent. |
|---|
| required_dependencies | ws_market, clob_public, gamma, internal market scanner, KillSwitch, internal builder code |
|---|
19. Failure-Injection Recipes
| Scenario | How to inject | Expected behaviour | Recovery |
|---|
GAMMA_METADATA_UNAVAILABLE | Take gamma API offline; bot cannot compute hours_to_event | | Automatic when gamma API returns. |
NEAR_RESOLUTION_BLOCK | Set hours_to_event=0.5 (< 1h hard) | | Automatic — bot resumes after market resolves or resets. |
KILL_SWITCH_ON | Set killswitch.active=true | | Automatic on manual KillSwitch reset. |
20. State & Persistence
Cold-start recovery
On cold start, attention signals re-polled from market scanner; spread recomputed from ws_market.
21. Concurrency & Idempotency
| Aspect | Specification |
|---|
| Execution model | actor-per-market |
| Max in-flight | 30 |
| Idempotency key | intent_id |
| Per-call timeout (ms) | 300 |
| Backpressure strategy | drop oldest attention signal per market_id when queue > 2 |
| Locking / mutual exclusion | per-market_id mutex for spread-state |
22. Dependencies
Depends on (must run first)
| Bot | Why | Contract |
|---|
| risk.kill_switch | Checked first; blocks all intent emission when active. | |
Emits to (downstream consumers)
External services
| Service | Endpoint | SLA assumed | On failure |
|---|
| Polymarket CLOB WebSocket (ws_market) | | best-effort | |
| Polymarket Gamma API (market metadata) | | 99.9% | |
23. Security Surfaces
Abuse vectors considered
- Attention signal injection to force quotes on low-quality markets
- Front-running of posted GTC quotes by adversaries who monitor emerging markets
Mitigations
- Attention signals sourced from authenticated internal market scanner only
- Quote sizes bounded by max_pre_event_position_usd
- GTC quotes can be cancelled by user at any time
24. Polymarket V2 Compatibility
| Aspect | Value |
|---|
| CLOB version | v2 |
| Collateral asset | pUSD |
| EIP-712 Exchange domain version | 2 |
| Aware of builderCode field | yes |
| Aware of negative-risk markets | no |
| Multi-chain ready | no |
| SDK used | py-clob-client-v2 |
| Settlement contract | CTFExchangeV2 |
| Notes | Bot not yet implemented; designed against V2 schema (pUSD, builder codes, V2 EIP-712 domain). feeRateBps not present on any signed OrderIntent. |
API surfaces declared
clob_publicclob_authws_marketgammainternal
Networks supported
polygon
25. Versioning & Migration
| Field | Value |
|---|
| spec | 2.0.0 |
| implementation | 0.1.0 |
| schema | 2 |
| released | None |
| planned_release | Q3-2026 |
Migration history
| Date | From | To | Reason | Action taken |
|---|
| 2026-04-28 | n/a | v2-spec | Spec drafted post-CLOB-V2 cutover; bot not yet implemented | Designed against V2 schema (pUSD, builder codes, V2 EIP-712 domain) |
26. Acceptance Tests
Unit Tests
| Test | Setup | Expected result |
|---|
| Emit GTC post-only maker quote when attention signal fires, hours_to_event=36 | initial_spread_bps=200, max_pre_event_position_usd=300 | GTC post_only OrderIntent; reason=LP_QUOTE_EMITTED |
| Skip when hours_to_event < 1h hard floor | hours_to_event=0.5 | No OrderIntent; reason=LP_TOO_CLOSE_TO_RESOLUTION |
| Tighten spread when total_volume >= 5000 pUSD | total_volume=5500, initial_spread_bps=200 | OrderIntent with spread_bps=100 |
Integration Tests
| Test | Expected result |
|---|
| Full cycle: attention signal → quote → GTC post-only OrderIntent on Polygon testnet | Order has builder.code, post_only=true, no feeRateBps, EIP-712 domain v2 |
Property Tests
| Property | Required behaviour |
|---|
| Bot never emits OrderIntent when hours_to_event < 1h | Always true |
| feeRateBps never present on any signed OrderIntent | Always true |
27. Operational Runbook
Liquidity-Pulse incidents are typically gamma API outages (blocking event lead-time checks) or kill-switch activations. Near-resolution blocks are expected behavior.
On-call actions
| Alert | First step | Diagnosis | Mitigation | Escalate to |
|---|
LiquidityPulseHighAdverseSelection | | | | |
LiquidityPulseKillSwitch | | | | |
LiquidityPulseStaleFeed | | | | |
Manual overrides
Healthcheck
GET /internal/health/liquidity-pulse -> 200 if Gamma API responding; ws_market feed age < 5s; KillSwitch inactive.. Red: Gamma API down or KillSwitch active..
29. Developer Checklist
Ready-to-ship score: 27/27 sections complete · 100%
| Requirement | Status |
|---|
| Purpose defined | ✓ done |
| Required inputs listed | ✓ done |
| Parameters defined | ✓ done |
| Defaults defined | ✓ done |
| Warning thresholds defined | ✓ done |
| Hard thresholds defined | ✓ done |
| Safe fallback defined | ✓ done |
| Structured output defined | ✓ done |
| Developer log defined | ✓ done |
| Plain-English explanation | ✓ done |
| Unit tests defined | ✓ done |
| Integration tests defined | ✓ done |
| Property tests defined | ✓ done |
| Failure-mode block complete | ✓ done |
| Reference implementation pseudocode | ✓ done |
| Wire examples (input + output) | ✓ done |
| Reason codes listed | ✓ done |
| Metrics & logs defined | ✓ done |
| State & persistence defined | ✓ done |
| Concurrency & idempotency defined | ✓ done |
| Dependencies declared | ✓ done |
| Security surfaces declared | ✓ done |
| Polymarket V2 compatibility declared | ✓ done |
| Version & migration history declared | ✓ done |
| Operational runbook defined | ✓ done |
| Promotion gates defined | ✓ done |
| Failure-injection recipes defined | ✓ done |