Skip to content

Conversation

@guzus
Copy link
Owner

@guzus guzus commented Nov 25, 2025

MX stands for "MM experience"

@claude
Copy link

claude bot commented Nov 25, 2025

PR Review: Better MX (Market Maker Experience)

Summary

This PR introduces significant improvements to market maker experience with order fill tracking, NAV calculation, and enhanced WebSocket support. The changes add ~793 lines across 9 files.

Code Quality & Best Practices

✅ Strengths

  1. Clean Architecture: New features follow existing patterns and integrate well with the base Exchange class
  2. Good Documentation: Functions have clear docstrings with examples
  3. Thread Safety: OrderTracker properly uses locks for concurrent access
  4. Type Hints: Good use of type annotations (dataclasses, Callable types)
  5. Error Handling: Callbacks wrapped in try-catch to prevent failures from propagating

⚠️ Areas for Improvement

1. Authentication Security (polymarket_ws.py:453-465)
The authentication implementation sends credentials without proper HMAC signing:

return {
    "auth": {
        "apiKey": self.api_key,
        "secret": self.api_secret,  # Secret sent directly
        "passphrase": self.api_passphrase,
        "timestamp": timestamp,
    }
}

Most WebSocket APIs require HMAC signatures, not raw secrets. Verify this matches Polymarket's actual auth requirements.

2. Float Comparison Issues (order_tracker.py:140, spread_strategy.py:361)

if tracked.total_filled >= tracked.order.size:  # Line 140
if abs(order.price - our_bid) < 0.001:  # Line 361

Using >= for float comparison can cause issues with floating-point precision. Consider using epsilon comparison like >= (size - 1e-9).

3. Missing Input Validation (base/exchange.py:76-84)

def update_mid_price(self, token_id: str, mid_price: float) -> None:
    self._mid_price_cache[str(token_id)] = mid_price

No validation that mid_price is in valid range [0, 1] for prediction markets. Invalid prices could corrupt NAV calculations.

4. Resource Cleanup (polymarket_ws.py:593-606)

def stop(self):
    self._running = False
    if self.ws and self._loop:
        async def close():
            if self.ws:
                await self.ws.close()
        asyncio.run_coroutine_threadsafe(close(), self._loop)

The coroutine is scheduled but not awaited. The thread join may complete before the WebSocket closes, leaving connections open.

Potential Bugs

1. Race Condition (order_tracker.py:129)

tracked.total_filled += trade.size

If multiple trades arrive simultaneously for the same order, this could cause double-counting since the lock is released between updates.

2. Memory Leak (base/exchange.py:50)

self._mid_price_cache: Dict[str, float] = {}

The mid-price cache never expires. In long-running bots, this could grow unbounded if markets are added/removed.

3. Infinite Loop Risk (polymarket_ws.py:492-510)

while self._running:
    try:
        if not self._connected:
            await self._connect()
        async for message in self.ws:
            # process message
    except Exception as e:
        await asyncio.sleep(3)

If _connect() consistently fails, this creates an infinite reconnection loop with no backoff limit.

4. Missing None Check (spread_strategy.py:313-318)
When calculating inverse prices for binary markets, if the first token's orderbook is missing, the code continues but best_bid/best_ask may be None, causing issues downstream.

Performance Considerations

  1. WebSocket Efficiency: Smart optimization to only subscribe to Yes token for binary markets, calculating No as inverse. Saves 50% bandwidth. ✅

  2. Cache TTL: Mid-price cache has no expiration, but this might be acceptable for real-time data that's constantly updated.

  3. Lock Contention: OrderTracker uses a single lock for all operations. For high-frequency trading with many orders, consider finer-grained locking.

Security Concerns

CRITICAL: API credentials handling (polymarket.py:1346-1349)

creds = self._clob_client.creds
self._user_ws = PolymarketUserWebSocket(
    api_key=creds.api_key,
    api_secret=creds.api_secret,
    api_passphrase=creds.api_passphrase,
)

The PR passes API secrets to WebSocket without verification that the auth mechanism is secure. Ensure:

  • Secrets are never logged (check verbose mode)
  • WebSocket connection uses TLS
  • Authentication method matches Polymarket's requirements

Test Coverage

Major Gap: No tests for new functionality

  • No tests for OrderTracker fill detection
  • No tests for NAV calculation edge cases
  • No tests for WebSocket authentication
  • No tests for mid-price cache updates

Per CLAUDE.md #5 ("Run and Debug yourself PROACTIVELY"), tests should be added.

Recommendations

Must Fix

  1. Add input validation for update_mid_price() to ensure prices are in [0, 1]
  2. Fix WebSocket cleanup to properly await connection closure
  3. Add exponential backoff to reconnection logic
  4. Verify authentication implementation matches Polymarket API docs

Should Fix

  1. Add epsilon comparisons for float arithmetic
  2. Implement cache eviction policy for mid-price cache
  3. Add None checks when calculating inverse prices
  4. Add basic unit tests for NAV calculation

Consider

  1. Add metrics/monitoring hooks for order tracker
  2. Document expected behavior when WebSocket disconnects mid-trade
  3. Consider making reconnection behavior configurable

Verdict

The PR adds valuable functionality with generally good code quality. However, it has several bugs and missing safeguards that should be addressed before merging. The lack of tests is concerning given the complexity of WebSocket handling and order tracking.

Recommendation: Request changes - address critical issues (auth security, float comparisons, resource cleanup) and add basic tests before merging.

@guzus guzus merged commit edffad5 into main Dec 3, 2025
1 check passed
bullish-lee added a commit that referenced this pull request Jan 3, 2026
Critical Issues Fixed:
- #1: Status cache thread safety - protected with _instance_lock
- #2: Global credentials thread safety - added _CREDENTIALS_LOCK
- #3: RPC session race condition - double-checked locking pattern
- #4: Signal handler deadlock - use flag instead of complex cleanup

Quality Issues Fixed:
- #5: Redundant singleton init - __init__ is now no-op
- #6: Long if-elif chain - replaced with TOOL_DISPATCH dict
- #7: ThreadPoolExecutor cleanup - unified _run_with_timeout() helper
- #8: Rate limiter busy-wait - calculate exact wait time
- #9: Status cache memory leak - added eviction and max size limit

All 54 MCP tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
guzus added a commit that referenced this pull request Jan 10, 2026
* Add MCP (Model Context Protocol) server support

- Add MCP server implementation for Claude Desktop integration
- Support Polymarket dual-wallet system (Funder + Proxy)
- Configure signature_type=0 (EOA) for normal MetaMask accounts
- Add comprehensive English documentation with examples
- Include security measures and troubleshooting guide
- Add tests for MCP functionality

Features:
- Exchange management (Polymarket, Opinion, Limitless)
- Market data fetching and analysis
- Order creation and management
- Portfolio tracking (balance, positions, NAV)
- Strategy execution (market making)
- Dual-wallet balance display (Funder + Proxy)

Technical:
- MCP protocol integration with stdio transport
- Thread-safe session management
- Proper error handling and translation
- Environment-based configuration
- Polygon RPC integration for balance queries

Version: 0.0.2

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(mcp): Address code review feedback

High Priority Fixes:
- Fix singleton race condition in ExchangeSessionManager and StrategySessionManager
  (moved initialization into __new__ with proper locking)
- Add price validation (0-1 range) in create_order
- Improve error handling in get_usdc_balance_polygon
  (no longer silently returns 0.0, now logs errors and returns None with warnings)
- Add RPC endpoint fallbacks for better reliability

Medium Priority Fixes:
- Change strategy threads to daemon=True for clean shutdown
- Make POLYGON_RPC_URL configurable via environment variable
- Add fallback RPC endpoints for Polygon

Code Quality:
- Fix all ruff lint errors (import sorting, E402 noqa comments)
- Add @pytest.mark.asyncio decorators to async tests
- Fix test isolation issues with singleton cleanup

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Fix remaining lint errors and improve robustness

- Add credential validation in exchange_manager.py before exchange creation
- Move JSON import to module level in server.py
- Add thread join timeout verification in strategy_manager.py
- Fix lint errors in test files (noqa for import tests, is True comparison)
- All 54 MCP tests passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Fix CI formatting and skip tests when mcp not installed

- Apply ruff format to all MCP files
- Add pytest.importorskip for mcp module in test_mcp_tools.py
- Tests skip gracefully when mcp optional dependency is not installed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(mcp): Address code review feedback

- Remove emojis from log messages (CLAUDE.md compliance)
- Fix misleading "lazy loading" comment for MCP_CREDENTIALS
- Make timeout values configurable via environment variables:
  - MCP_EXCHANGE_INIT_TIMEOUT (default: 10s)
  - MCP_CLIENT_INIT_TIMEOUT (default: 5s)
- Add comprehensive logging architecture documentation in server.py

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Address all code review feedback

- Remove unused _initialized flag from session managers
- Add exception chaining (raise ... from e) to all tool functions
- Fix test file path from mcp_server to dr_manhattan/mcp
- Improve cleanup error handling (only remove successfully cleaned items)
- All 54 MCP tests passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Address CLAUDE.md Rule #4 and security concerns

CLAUDE.md Rule #4 Compliance:
- Move non-sensitive config defaults to code (signature_type, verbose)
- Only keep sensitive data (private_key, funder) as required in .env
- Add code constants DEFAULT_SIGNATURE_TYPE, DEFAULT_VERBOSE

Security Improvements:
- Add Security Warning section to documentation
- Add private key security best practices
- Clarify required vs optional environment variables

Other:
- Add clarifying comments for price validation logic
- Make verbose logging configurable via MCP_VERBOSE env var

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Address PR review - CLAUDE.md compliance and error handling

CLAUDE.md Rule #4 Compliance:
- Remove env var overrides for timeouts (now code constants only)
- Remove POLYGON_RPC_URL env var (RPC list in code)
- Remove MCP_VERBOSE env var (use DEFAULT_VERBOSE constant)
- Document why only Polymarket uses MCP credentials

Error Handling Improvements:
- Fail fast on funder balance query failure (raise ValueError)
- Proxy balance failure is non-fatal (returns error field)
- Better error messages with wallet address context

Code Quality:
- Add ERC20_BALANCE_OF_SELECTOR constant with documentation
- Add descriptive comments for POLYGON_USDC_ADDRESS
- Add comments explaining RPC fallback strategy

All 54 MCP tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Address critical security and resource issues

Security Improvements:
- Add explicit ThreadPoolExecutor cleanup on timeout (prevent hanging threads)
- Add RPC response validation for balance queries
- Add connection pooling for RPC requests (requests.Session)
- Add security warning comment for credential handling

CLAUDE.md Rule #2 Compliance:
- Remove non-existent doc references from tests
- Keep only examples/mcp_usage_example.md as documentation

Resource Management:
- executor.shutdown(wait=False, cancel_futures=True) on timeout
- Proper cleanup in finally blocks
- Validate hex format before parsing RPC responses

All 54 MCP tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Address all PR review must-fix items

Input Validation:
- Add validation.py with comprehensive input validators
- Validate all tool parameters (exchange, market_id, token_id, etc.)
- Prevent injection attacks via strict format checking
- Regex patterns for hex addresses, UUIDs, and alphanumeric IDs

Security Improvements:
- Add credential zeroization on cleanup (_zeroize_credentials)
- Filter error context with SAFE_CONTEXT_FIELDS allowlist
- Never expose sensitive data (private_key, funder, etc.) in errors
- Add RPC session cleanup to prevent resource leaks

Singleton Race Condition Fix:
- Add _initialized flag in __new__ and __init__
- Defensive check in __init__ to ensure idempotency

Resource Management:
- cleanup_rpc_session() for HTTP connection pooling cleanup
- ExchangeSessionManager.cleanup() now calls RPC session cleanup
- Proper cleanup order: clients -> exchanges -> RPC -> credentials

All 54 MCP tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: Fix placeholder GitHub URLs in MCP guide

Replace yourusername with guzus/dr-manhattan for:
- Clone URL
- GitHub Issues link
- Discussions link

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: Add MCP Server section to README for open source users

- Add MCP to Key Features list
- Add MCP Server subsection under Usage (follows existing convention)
- Add MCP guide to examples list
- Link to full documentation (examples/mcp_usage_example.md)
- Update last updated date in MCP guide

Makes it easy for open source users to discover and use the MCP server.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: Fix placeholder formatting in MCP guide

- Change 'youruser' to '<username>' for clarity
- Change 'YourName' to '<username>' for consistency
- Makes it obvious these are placeholders to replace

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: Remove real wallet addresses from MCP guide examples

Replace actual wallet address fragments with generic placeholders
to avoid exposing real addresses in documentation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Implement PR review feedback and improve signature type docs

PR Review Feedback (Issues & Concerns):
- Add connection pooling with retry strategy for RPC sessions
- Implement thread force-kill with two-phase shutdown and orphan tracking
- Add credential refresh mechanism for runtime credential reload
- Add pagination support for fetch_markets (limit/offset)
- Implement status caching with TTL to reduce refresh_state() calls
- Add rate limiter (token bucket: 10 req/s, burst 20)

Documentation:
- Clarify signature type 2 allows trading from Proxy wallet
- Type 0: Funder = MetaMask address, trades from Funder wallet
- Type 2: Funder = Proxy wallet address, trades from Proxy wallet
- Update error messages and troubleshooting guide

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Address all PR review critical and quality issues

Critical Issues Fixed:
- #1: Status cache thread safety - protected with _instance_lock
- #2: Global credentials thread safety - added _CREDENTIALS_LOCK
- #3: RPC session race condition - double-checked locking pattern
- #4: Signal handler deadlock - use flag instead of complex cleanup

Quality Issues Fixed:
- #5: Redundant singleton init - __init__ is now no-op
- #6: Long if-elif chain - replaced with TOOL_DISPATCH dict
- #7: ThreadPoolExecutor cleanup - unified _run_with_timeout() helper
- #8: Rate limiter busy-wait - calculate exact wait time
- #9: Status cache memory leak - added eviction and max size limit

All 54 MCP tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Fix CI formatting and skip tests when mcp not installed

- Apply ruff format to all MCP files
- Add pytest.importorskip for mcp module in test_mcp_server_structure.py
- Tests skip gracefully when mcp optional dependency is not installed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Address all 3rd PR review critical and quality issues

Critical Issues Fixed:
- #1: Credential reload race condition - move reload_credentials() inside lock
- #2: Status cache size check - evict BEFORE adding, check size limit
- #3: RPC response validation - add JSON parsing error handling, dict type check
- #4: Rate limiter init - add _rate_limiter_lock for thread-safe singleton

Quality Issues Fixed:
- #5: Orphan tracking verified as complete (already implemented correctly)
- #8: Standardize error messages - add expected format to all "required" errors
- #9: Magic numbers - add explanatory comments for rate limiter and shutdown flag

All 54 MCP tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Address 5th review - register missing tools and improve tests

5th Review Fixes:
- Register 12 missing tools in server.py (total 29 tools now)
  - fetch_order, fetch_positions_for_market
  - pause_strategy, resume_strategy, get_strategy_metrics
  - fetch_token_ids, find_tradeable_market, find_crypto_hourly_market
  - parse_market_identifier, get_tag_by_slug
- Fix type hints: list[Tool] -> List[Tool] for consistency
- Increase STATUS_CACHE_TTL from 1s to 3s for better performance
- Fix RateLimiter.get_status() duplicate lock issue
- Fix test assertions: return True/False -> proper assert statements
- Remove emojis from test output (CLAUDE.md Rule #1)
- Fix test_pyproject_config check for dr_manhattan package

All 54 MCP tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* style(mcp): Fix formatting in test files

Apply ruff format to test_comprehensive.py and test_mcp_server_structure.py

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Fix stdout pollution causing JSON-RPC parse errors

The MCP server was corrupting the JSON-RPC protocol with two issues:

1. **Logger import order bug**: dr_manhattan.utils was imported before
   patching setup_logger, causing default_logger to be created with
   stdout handler and ANSI colors. Fixed by:
   - Patch logger_module.setup_logger BEFORE importing dr_manhattan.utils
   - Recreate default_logger with patched function
   - Move third-party imports after patching

2. **Verbose mode stdout pollution**: DEFAULT_VERBOSE=True caused
   polymarket.py to print debug info (with checkmarks like ✓) to stdout.
   Fixed by setting DEFAULT_VERBOSE=False for MCP.

These fixes prevent errors like:
- "Unexpected token '✓'"
- "Unexpected token '←[90m'" (ANSI escape codes)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(mcp): Fix duration_minutes parameter bug in create_strategy_session

Strategy.__init__() does not accept duration_minutes parameter - it's only
used by run(). The create_session() method was passing all params including
duration_minutes to the constructor, causing:
  "Strategy.__init__() got an unexpected keyword argument 'duration_minutes'"

Fix: Extract duration_minutes from params before passing to strategy
constructor. Pass it directly to _run_strategy() instead.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: Update .env.example and README for new trading configurations

* feat(mcp): Add search_markets tool for keyword-based market retrieval

- Introduced a new tool, `search_markets`, allowing users to search for markets by keyword with pagination support.
- Updated `fetch_markets` description to clarify pagination usage.
- Enhanced input schema for both tools to include limit and offset parameters for better control over results.

This addition improves the functionality of the MCP by enabling more targeted market searches.

* fix(mcp): Improve tool descriptions for better AI tool selection

- Update fetch_markets description to warn about slowness (100+ results)
- Update search_markets description with RECOMMENDED prefix
- Guide AI to prefer search_markets for specific market queries

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: guzus <storminggalaxys3@gmail.com>
Co-authored-by: guzus <50664161+guzus@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants