Skip to content

Conversation

@0xGh-st
Copy link
Collaborator

@0xGh-st 0xGh-st commented Dec 30, 2025

close #34

  • Improved WebSocket shutdown process in ExchangeClient to ensure proper disconnection and task cancellation.
  • Added a new SpikeStrategy class for mean reversion trading, which detects price spikes and executes buy orders based on defined thresholds and conditions.
  • Included detailed logging for strategy configuration and execution, enhancing observability during trading operations.

- Improved WebSocket shutdown process in ExchangeClient to ensure proper disconnection and task cancellation.
- Added a new SpikeStrategy class for mean reversion trading, which detects price spikes and executes buy orders based on defined thresholds and conditions.
- Included detailed logging for strategy configuration and execution, enhancing observability during trading operations.
@claude
Copy link

claude bot commented Dec 30, 2025

Code Review: PR #44 - Enhance ExchangeClient and introduce Spike Strategy

Summary

This PR improves the WebSocket shutdown process and adds a new mean reversion trading strategy. Overall the changes are well-implemented, but there are several areas for improvement.


Code Quality & Best Practices

Positives:

  • Clean separation of concerns between the strategy and base classes
  • Good use of dataclasses for Position tracking
  • Proper inheritance from the Strategy base class
  • Consistent logging with colored output

Issues:

  1. Violation of CLAUDE.md Rule impl polymarket #1 (examples/spike_strategy.py:94-96, 162-170)
    The codebase instructions explicitly state "Avoid emojis and other non-essential characters", but the spike strategy heavily uses emojis in logging:

    f"  {Colors.green('SPIKE')} {Colors.magenta(outcome)}: "
    f"  Threshold: {Colors.yellow(f'{self.spike_threshold * 100:.1f}%')} | "

    While colors are acceptable for readability, the emojis should be removed per project guidelines.

  2. Silent Exception Handling (dr_manhattan/base/exchange_client.py:487-490)

    try:
        future.result(timeout=3.0)
    except Exception:
        pass

    This swallows all exceptions without logging. At minimum, log the exception for debugging purposes, especially since this is a critical shutdown path.

  3. Duplicate Code (examples/spike_strategy.py:265-294)
    The find_market_id() function is duplicated from spread_strategy.py (lines 37-87). This violates DRY principles and should be extracted to a shared utility module.


Potential Bugs & Issues

  1. Race Condition in WebSocket Shutdown (dr_manhattan/base/exchange_client.py:483-492)
    The check if self._market_ws.loop and self._market_ws.loop.is_running() is not atomic. The loop could stop between the check and the run_coroutine_threadsafe call, causing a RuntimeError. Consider:

    if self._market_ws.loop:
        try:
            if self._market_ws.loop.is_running():
                future = asyncio.run_coroutine_threadsafe(...)
                future.result(timeout=3.0)
        except (RuntimeError, Exception) as e:
            logger.debug(f"WebSocket disconnect failed: {e}")
  2. Position Tracking Inconsistency (examples/spike_strategy.py:177-186)
    When a position is exited, you check if exchange_pos < 1 to delete the entry. However, positions could be partially filled, leaving exchange_pos between 0 and the original size. This could cause the entry to be deleted prematurely while still holding a partial position.

  3. Missing Price Validation (examples/spike_strategy.py:150-165)
    The strategy checks if ask is None or ask <= 0 but doesn't validate that ask is a reasonable price (e.g., not > 1.0 for binary markets). This could lead to invalid orders.

  4. EMA Initialization Issue (examples/spike_strategy.py:135-141)
    The EMA is initialized to 0.0 and only updated when price data arrives. If _detect_spike_down is called before enough prices accumulate, it returns False, which is correct. However, the check len(self.price_history[outcome]) < self.ema_period could be more defensive by also checking if EMA is significantly different from current price (cold start issue).


Performance Considerations

  1. Inefficient Lookups (examples/spike_strategy.py:118-121)
    Calling get_token_id() multiple times per tick creates O(n) lookups. Cache token_id in the loop:

    for outcome in self.outcomes:
        token_id = self.get_token_id(outcome)
        if not token_id:
            continue
        # use token_id throughout instead of calling get_token_id() again
  2. Memory Usage (examples/spike_strategy.py:85)
    deque(maxlen=60) stores 60 prices per outcome. For markets with many outcomes, this could accumulate unnecessary data since only the EMA is used for spike detection. Consider reducing maxlen to 30 or using it for additional analytics.


Security Concerns

  1. Division by Zero (examples/spike_strategy.py:195)

    pnl = (price - pos.entry_price) / pos.entry_price

    If pos.entry_price is 0 (shouldn't happen but could due to bugs), this will crash. Add a guard:

    if pos.entry_price <= 0:
        logger.error(f"Invalid entry price for {outcome}: {pos.entry_price}")
        del self.entries[outcome]
        return
    pnl = (price - pos.entry_price) / pos.entry_price
  2. Unbounded Order Creation (examples/spike_strategy.py:171-177)
    While there's a cash check, there's no rate limiting on order creation. A malfunctioning price feed could spam orders rapidly, hitting exchange rate limits or creating unexpected exposure.


Test Coverage

Critical Issue: No tests were added for either change.

  1. The WebSocket shutdown improvement should have tests covering:

    • Normal shutdown flow
    • Shutdown when loop is not running
    • Timeout handling
    • Exception handling
  2. The SpikeStrategy needs tests for:

    • EMA calculation
    • Spike detection logic
    • Position entry/exit logic
    • Edge cases (zero prices, negative positions, etc.)

Consider adding at minimum:

  • tests/test_spike_strategy.py with unit tests for core logic
  • Integration test in tests/test_integration.py for the WebSocket changes

Recommendations

High Priority:

  1. Remove emojis from spike_strategy.py per CLAUDE.md guidelines
  2. Add error logging to the exception handler in exchange_client.py:489
  3. Extract find_market_id() to a shared utility module
  4. Add basic unit tests for SpikeStrategy core logic
  5. Fix the division by zero vulnerability

Medium Priority:
6. Improve position tracking to handle partial fills correctly
7. Add price validation (0 < price <= 1.0 for binary markets)
8. Cache token_id lookups in the main loop
9. Add rate limiting or cooldown enforcement

Low Priority:
10. Reduce price_history maxlen if not used for analytics
11. Add more defensive checks for EMA cold start


Positive Notes

  • The WebSocket shutdown improvement is a good fix that makes disconnection more graceful
  • The spike strategy implementation is well-structured and follows the existing Strategy pattern
  • Good use of configuration parameters with sensible defaults
  • Proper cleanup in the strategy's cleanup() method

Overall, this is a solid contribution that adds useful functionality, but needs refinement to meet the project's quality and testing standards.

- Handle race condition in WebSocket shutdown with try-except
- Add debug logging for disconnect exceptions
- Add division by zero protection for entry_price
- Validate ask price <= 1.0 for Polymarket

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

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

claude bot commented Dec 30, 2025

PR Review: Spike Strategy and ExchangeClient Improvements

Summary

This PR introduces a new mean reversion trading strategy and improves WebSocket shutdown handling. Overall, the code is well-structured and follows the repository patterns, but there are several areas that need attention.


Code Quality & Best Practices

Positive Aspects

  1. Good separation of concerns: The SpikeStrategy class properly extends the base Strategy class
  2. Consistent with codebase patterns: Follows the same structure as spread_strategy.py
  3. Detailed logging: Good use of colored logging for observability
  4. Type hints: Proper use of type annotations throughout

Issues Found

1. Code Duplication - DRY Principle Violation

The find_market_id() function in spike_strategy.py (lines 268-304) is nearly identical to the one in spread_strategy.py (lines 37-87). This is 37 lines of duplicated code.

Recommendation: Extract to a shared utility module (dr_manhattan/utils/market_lookup.py) and import in both strategies.


Potential Bugs & Issues

Critical Issues

1. Race Condition in Position Management

In _manage_position() at spike_strategy.py:187-192, there is a bug where positions < 1 share are deleted. Position could be 0.5 shares which is valid, but would be deleted. Should check <= 0 instead of < 1.

2. Incomplete Cleanup Logic

In cleanup() at spike_strategy.py:236-259, the code silently catches ALL exceptions with bare except and no logging when cleanup fails. Add proper error logging.

3. Validation Gap for Polymarket

Line 162 validates ask > 1.0, which is Polymarket-specific (prices are probabilities 0-1). This makes the strategy not exchange-agnostic. Consider documenting this limitation.


Performance Considerations

1. Inefficient Price History Storage

Stores 60 price samples per outcome but EMA period default is 30. Either reduce maxlen to match ema_period or use the history for additional analytics.

2. Multiple get_best_bid_ask Calls

In _check_spike_and_buy(), the code calls get_best_bid_ask() twice for the same token. Cache the result.


Security Concerns

1. No Maximum Position Validation

The strategy checks cash but doesn't validate that max_position is sensible relative to account size. A user could set max_position=1000000 with only 00.

2. Price Manipulation Risk

Spike detection could be triggered by flash crashes, orderbook manipulation, or WebSocket glitches. Add minimum volume/liquidity checks and maximum spike size validation.

3. No Order Size Validation

Missing validation for minimum order size, maximum order size relative to liquidity, and dust position handling.


Test Coverage

Critical: Missing Tests

No tests were added for the new SpikeStrategy class. The PR adds 389 lines with 0% test coverage.

Required tests:

  • Unit tests for _detect_spike_down() with various EMA scenarios
  • Unit tests for _manage_position() P&L calculations
  • Integration tests for the full strategy flow
  • Edge case tests for division by zero, empty orderbook, cooldown, multiple positions

Recommendation: Add tests/test_spike_strategy.py following the pattern in tests/test_base.py


ExchangeClient Changes

WebSocket Shutdown Improvements ✅

The changes in exchange_client.py are solid with proper timeout handling (3s), specific exception catching, graceful degradation with debug logging, and proper disconnect before stopping loop.

Minor suggestion: Consider making timeout configurable or documenting why 3s was chosen.


Summary

Blocking Issues

  1. Add tests for SpikeStrategy
  2. Fix position comparison bug (< 1 should be <= 0)
  3. Remove code duplication (extract find_market_id)

Recommended Before Merge

  1. Add position size validation
  2. Improve error handling in cleanup
  3. Cache orderbook lookups to reduce duplicate calls
  4. Document Polymarket-specific assumptions

The strategy implementation is promising, but needs test coverage and bug fixes before merging.

- Introduced documentation for the new spike_strategy.py, detailing its mean reversion functionality and usage instructions.
- Updated SpikeStrategy class to include token_id in price retrieval and position management methods, improving accuracy and handling of market data.
- Added error logging for cleanup failures during position management.

This commit enhances both the usability and robustness of the spike trading strategy.
@claude
Copy link

claude bot commented Dec 30, 2025

Pull Request Review: Spike Strategy Implementation

Summary

This PR introduces a mean reversion trading strategy and improves WebSocket shutdown handling. The changes include:

  • Enhanced WebSocket disconnect logic in ExchangeClient
  • New SpikeStrategy class for mean reversion trading
  • Documentation updates

Code Quality & Best Practices

Strengths

  1. Clean architecture - The SpikeStrategy properly inherits from the base Strategy class and follows established patterns from SpreadStrategy
  2. Good separation of concerns - Position tracking, EMA calculation, and order management are well separated
  3. Comprehensive logging - Excellent use of colored logging for observability
  4. Configuration - Good parameterization with sensible defaults

Issues & Recommendations

1. WebSocket Shutdown Logic (exchange_client.py:482-492)

The new disconnect approach is better than the old task cancellation, but there are concerns:

future = asyncio.run_coroutine_threadsafe(
    self._market_ws.disconnect(), self._market_ws.loop
)
future.result(timeout=3.0)

Issue: If disconnect() hangs or the WebSocket is in a bad state, this will always wait the full 3 seconds before timing out.

Recommendation: Consider adding a disconnect flag check or using asyncio.wait_for() with proper cleanup:

if not self._market_ws._disconnected:  # if you have such a flag
    future = asyncio.run_coroutine_threadsafe(...)

2. Price Validation Missing (spike_strategy.py:163-164)

if ask is None or ask <= 0 or ask > 1.0:
    return

Good: You validate the ask price is between 0-1 for binary markets.

Issue: The mid-price calculation in _get_mid_price() (lines 122-133) doesn't validate that bid < ask. You could get invalid mid prices if the orderbook is crossed.

Recommendation:

if bid is None or ask is None or bid <= 0 or ask <= 0 or bid >= ask:
    return self.market.prices.get(outcome)

3. Potential Division by Zero (spike_strategy.py:190-196)

if pos.entry_price <= 0:
    del self.entries[outcome]
    return

pnl = (price - pos.entry_price) / pos.entry_price

Good: You check for entry_price <= 0 and return early.

Issue: However, if price is None or <= 0 (which it could be from _get_mid_price()), you'll calculate invalid PnL.

Recommendation: Add price validation before PnL calculation:

if price is None or price <= 0 or pos.entry_price <= 0:
    del self.entries[outcome]
    return

4. EMA Initialization (spike_strategy.py:135-140)

The EMA initialization uses the first price it sees:

if self.ema_prices[outcome] == 0.0:
    self.ema_prices[outcome] = price

Issue: This means the strategy behavior depends on what price happens to come first, and the EMA won't be meaningful until ema_period samples are collected. However, you do check:

if ema <= 0 or len(self.price_history[outcome]) < self.ema_period:
    return False

Good: You wait for enough history before trading. This is correct.

5. Position Size Mismatch Risk (spike_strategy.py:207-210)

exit_size = min(exchange_pos, pos.size)

Issue: pos.size stores the order size from entry, but the actual filled size could be different if the order was partially filled. Using exchange_pos is more reliable.

Recommendation: Just use exchange_pos directly since you already check exchange_pos < 1 above:

exit_size = exchange_pos

6. Code Duplication (spike_strategy.py:266-292 vs spread_strategy.py:37-87)

The find_market_id() function is duplicated between both strategy files. This violates DRY and CLAUDE.md principle #2 ("Purge unnecessary code").

Recommendation: Extract this to a shared utility module:

# dr_manhattan/utils/market.py
def find_market_id(exchange: Exchange, slug: str, market_index: Optional[int] = None) -> Optional[str]:
    ...

Then import it in both strategy files.

Performance Considerations

  1. EMA Calculation: The EMA is recalculated every tick with exponential smoothing, which is efficient O(1)
  2. Price History: Using deque(maxlen=60) is memory-efficient and provides O(1) append
  3. No Major Performance Issues: The strategy uses proper data structures

Minor Optimization: The _log_status() method recalculates mid-price for every position on every tick just for logging. Consider caching this or only logging every N ticks.

Security Concerns

High Priority

  1. Order Placement Without Balance Checks (spike_strategy.py:158-177)

    You check if self.cash < self.order_size: which is good, but you don't account for:

    • Multiple positions being entered simultaneously before cash is updated
    • Fees/slippage reducing available balance
    • Race conditions with other orders

    Recommendation: Add a safety margin:

    if self.cash < self.order_size * 1.1:  # 10% buffer for fees
        return
  2. No Maximum Loss Protection

    The strategy has stop-loss per position (default 2%), but no overall max drawdown protection. If multiple positions hit stop-loss, losses could accumulate.

    Recommendation: Track total PnL and stop trading if overall loss exceeds a threshold:

    if self.total_realized_pnl < -self.max_acceptable_loss:
        logger.error("Max loss exceeded, stopping strategy")
        self.stop()
  3. No Circuit Breaker for Extreme Market Conditions

    If prices spike extremely (flash crash, market manipulation), the strategy will blindly enter positions.

    Recommendation: Add volatility checks:

    recent_volatility = np.std(list(self.price_history[outcome]))
    if recent_volatility > self.max_volatility_threshold:
        logger.warning(f"Volatility too high: {recent_volatility}")
        return

Medium Priority

  1. Exception Handling Swallows Errors (multiple locations)

    Several try/except blocks catch generic exceptions and continue:

    except Exception as e:
        logger.error(f"  Buy failed: {e}")

    This could hide serious issues like authentication failures, network problems, or exchange API changes.

    Recommendation: Be more specific with exception types or at least log the full traceback in debug mode.

Test Coverage

Critical Issue: There are NO tests for the new SpikeStrategy class.

The PR adds 390 lines of new code with complex logic (EMA calculation, spike detection, position management, PnL tracking) but zero test coverage.

Recommendations:

  1. Unit Tests Needed:

    • test_spike_strategy.py with tests for:
      • EMA calculation accuracy
      • Spike detection logic
      • Position entry/exit conditions
      • PnL calculation
      • Cooldown enforcement
      • Edge cases (zero prices, missing data, etc.)
  2. Integration Tests:

    • Test with mocked exchange responses
    • Test cleanup behavior
    • Test multi-position scenarios

Example test structure:

# tests/test_spike_strategy.py
def test_ema_calculation():
    strategy = SpikeStrategy(...)
    # Test EMA converges to correct value
    
def test_spike_detection_below_threshold():
    # Test it DOES trigger when price drops > threshold
    
def test_no_spike_detection_above_threshold():
    # Test it DOES NOT trigger when price drop < threshold
    
def test_position_exit_on_profit_target():
    # Test position exits when profit target hit

Adherence to CLAUDE.md

✅ Good

  1. UV Usage: Examples show correct uv run python examples/spike_strategy.py usage
  2. Code Quality: Generally clean and understandable

⚠️ Issues

  1. Emojis: Line 1 of CLAUDE.md says "Avoid emojis" but the code uses them extensively in logging (🤖 in cleanup messages per spike_strategy.py:229).

    • Counter-argument: These might be considered "essential characters" for user experience in trading logs
  2. Code Duplication: The find_market_id() function duplication violates "Purge unnecessary code"

  3. .env Variables: The strategy uses many environment variables (EXCHANGE, MARKET_SLUG, MARKET_ID) which contradicts "DO NOT place many variables in .env file"

    • Counter-argument: These are CLI argument defaults, not core configuration

Additional Observations

Documentation (examples/README.md)

  • Clear and helpful documentation added for the spike strategy
  • Good examples with different parameter configurations
  • Warning about real money is prominent ✅

Strategy Logic

The mean reversion approach is sound:

  • Using EMA as baseline is a standard approach
  • Spike detection via threshold comparison is reasonable
  • Position management with TP/SL is good risk management
  • Cooldown prevents overtrading after exits

Final Recommendations

Before Merging:

  1. Add tests - This is critical for a trading strategy
  2. Fix the duplicate find_market_id() - Extract to shared utility
  3. Add price validation before PnL calculation
  4. Consider adding max loss protection

Nice to Have:

  1. Circuit breaker for extreme volatility
  2. More specific exception handling
  3. Overall PnL tracking and reporting
  4. Backtesting capability

Verdict

Approve with changes requested ⚠️

The code quality is generally good and follows the established patterns well. The WebSocket improvements are solid. However, the complete lack of tests for a trading strategy that handles real money is concerning. Additionally, the code duplication and some edge cases around price validation should be addressed.

The strategy logic itself appears sound, but without tests, it's difficult to verify the correctness of the EMA calculation, spike detection, and position management under various market conditions.


Estimated Risk Level: Medium-High (untested trading logic with real money)

- Updated README.md to include a new section for trading strategies, linking to the Spread and Spike strategies.
- Added detailed documentation for the Spike Strategy, outlining its mean reversion approach and usage instructions.
- Introduced documentation for the Spread Strategy, describing its market-making functionality and parameters.
@claude
Copy link

claude bot commented Jan 1, 2026

PR Review: Enhance ExchangeClient and introduce Spike Strategy

Overview

This PR introduces a mean reversion trading strategy (SpikeStrategy) and improves WebSocket shutdown handling. Overall well-structured implementation with good documentation.

Code Quality & Best Practices

Strengths:

  • Clean separation of concerns with Position dataclass
  • Good use of type hints throughout
  • Comprehensive documentation in wiki/
  • Proper inheritance from Strategy base class
  • Consistent code style and formatting

Areas for Improvement:

  1. ExchangeClient shutdown (exchange_client.py:486-492) - Silent exception handling with only debug logging. Consider warning level for TimeoutError.

  2. SpikeStrategy parameter validation (spike_strategy.py:45-72) - Missing validation for parameters. Add validation in init for spike_threshold, profit_target, stop_loss ranges.

  3. Hardcoded magic numbers (spike_strategy.py:165, 189) - Extract ask > 1.0 and exchange_pos < 1 as class constants per CLAUDE.md fix: polymarket impl #4.

Potential Bugs & Issues

Critical:

  1. Race condition in position management (spike_strategy.py:177-178) - If order fills immediately before next tick, position tracking could be out of sync. Consider tracking pending orders separately.

  2. Stale price fallback (spike_strategy.py:130-132) - Falls back to potentially stale market price when orderbook unavailable. Could trigger false spike signals. Return None instead or add timestamp check.

Moderate:

  1. Position size mismatch (spike_strategy.py:212) - If fills are partial, pos.size won't reflect actual position. Track actual filled size.

  2. EMA warmup period (spike_strategy.py:149-150) - Strategy won't trade until EMA warmup complete (30s default). Not documented. Add warmup status to setup() logging.

Test Coverage

Missing:

  • No unit tests for SpikeStrategy
  • No tests for WebSocket shutdown improvements
  • No integration tests for mean reversion logic

Recommendations:

  1. Add unit tests for _detect_spike_down(), _update_ema(), position entry/exit logic
  2. Add integration test for cleanup()

Documentation Quality

Strengths:

  • Excellent wiki documentation (wiki/strategies/spike.md)
  • Clear ASCII diagrams explaining strategy logic
  • Comprehensive parameter tables
  • Usage examples with different configurations

Additional Observations

Positive:

  • Adheres to CLAUDE.md principles (no unnecessary emojis, uses UV, clean code)
  • Good error handling with try/except blocks
  • Proper use of dataclasses
  • Cooldown mechanism prevents excessive trading

Suggestions:

  1. Consider adding a dry-run mode for backtesting
  2. Add metrics tracking (total trades, win rate)
  3. Consider exposing min_order_size as parameter

Recommendation

Approve with minor changes requested

The PR introduces valuable functionality with good documentation. The critical issues around position tracking and stale prices should be addressed.

Priority fixes:

  1. Add parameter validation in SpikeStrategy.init()
  2. Handle stale price fallback more safely
  3. Add basic unit tests for core logic
  4. Consider logging level for shutdown timeout exceptions

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

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

claude bot commented Jan 2, 2026

Pull Request Review: Enhance ExchangeClient and introduce Spike Strategy

Overview

This PR introduces a new mean reversion trading strategy (SpikeStrategy) and improves the WebSocket shutdown process. The changes are well-structured and add valuable functionality to the codebase.


Code Quality & Best Practices

✅ Strengths

  1. Clean Architecture: The SpikeStrategy follows the Strategy base class pattern consistently
  2. Good Documentation: Comprehensive wiki documentation and inline comments
  3. Type Hints: Proper use of type annotations throughout
  4. Dataclass Usage: Clean Position dataclass for state tracking
  5. Error Handling: Try-except blocks around order operations
  6. Logging: Excellent use of structured logging with color-coded output

⚠️ Concerns

1. WebSocket Shutdown Logic (exchange_client.py:482-492)

Issue: The disconnect logic has a race condition risk

if self._market_ws.loop.is_running():
    future = asyncio.run_coroutine_threadsafe(
        self._market_ws.disconnect(), self._market_ws.loop
    )
    future.result(timeout=3.0)

Risk: Between checking is_running() and executing the coroutine, the loop could stop, causing the coroutine to never complete.

Suggestion: Add a check after the future.result() or handle the case where the loop stops mid-execution.

2. EMA Initialization (spike_strategy.py:137-142)

Issue: EMA starts at 0.0 and jumps to first price

if self.ema_prices[outcome] == 0.0:
    self.ema_prices[outcome] = price

Risk: During the warmup period (< ema_period samples), the EMA is less reliable but still used for spike detection.

Suggestion: Consider adding a minimum data requirement before trading:

if len(self.price_history[outcome]) < self.ema_period:
    return  # Skip trading until EMA is stable

This is already checked in _detect_spike_down() (line 149), but the EMA value itself could be unstable.

3. Position Tracking Inconsistency (spike_strategy.py:177-178, 188-190)

Issue: Position size tracking may drift from reality

self.entries[outcome] = Position(entry_price, self.order_size, time.time())

This assumes the order filled at exactly self.order_size, but orders can be partially filled or filled at different sizes.

Later (line 188-190):

if exchange_pos < 1:
    del self.entries[outcome]
    return

Risk: If an order fills at 0.5 but you expect 5.0, the position gets deleted immediately.

Suggestion: Use track_fills=True (already enabled) and update self.entries[outcome].size based on actual fills from self._positions.get(outcome, 0).

4. Price Validation (spike_strategy.py:165-166)

Issue: Binary market price validation may be too strict

if ask is None or ask <= 0 or ask > 1.0:
    return

Question: Can ask prices equal exactly 1.0 in practice? If so, this should be ask >= 1.0 to avoid edge cases.


Potential Bugs

🐛 Bug #1: Position Size Drift

Location: spike_strategy.py:210

exit_size = min(exchange_pos, pos.size)

Issue: If pos.size (tracked) doesn't match exchange_pos (actual), this could lead to:

  • Partial position exits
  • Stale entries remaining in self.entries
  • Re-entry blocked by stale entry

Fix: After successful exit, verify the position is actually closed:

self.refresh_state()
if self._positions.get(outcome, 0) < 1:
    del self.entries[outcome]

🐛 Bug #2: Cooldown Not Enforced on Partial Fills

Location: spike_strategy.py:220

del self.entries[outcome]
self.last_exit_time[outcome] = time.time()

Issue: If only a partial exit occurs (because exit_size < exchange_pos), the entry is deleted and cooldown starts, but a position still remains.

Scenario:

  1. Buy 5 units
  2. Only 3 units sell (exit_size = 3)
  3. Entry deleted, cooldown starts
  4. 2 units still open but no longer managed
  5. Strategy can't re-enter for 30 seconds despite having an unmanaged position

Fix: Only delete entry and start cooldown if position is fully closed.

🐛 Bug #3: Exception Swallowing in Cleanup

Location: spike_strategy.py:257

except Exception as e:
    logger.debug(f"Cleanup sell failed for {outcome}: {e}")

Issue: Uses logger.debug instead of logger.warning or logger.error.

Impact: User won't see cleanup failures in normal logging, potentially leaving positions open.

Fix: Change to logger.warning for visibility during shutdown.


Performance Considerations

⚡ Good

  1. Efficient Data Structures: Uses deque with maxlen=60 for price history (O(1) append)
  2. Minimal API Calls: Leverages cached state via refresh_state()
  3. WebSocket Integration: Real-time orderbook data reduces REST calls

⚠️ Optimization Opportunities

1. Redundant Price Calculations

Location: spike_strategy.py:230

price = self._get_mid_price(outcome)

Called in _log_status() for every position, every tick. This fetches orderbook again even though it was just fetched in on_tick().

Suggestion: Cache mid prices during the main loop:

self._cached_prices: Dict[str, float] = {}  # Add to __init__

def on_tick(self):
    self.refresh_state()
    self._cached_prices.clear()
    
    for outcome in self.outcomes:
        price = self._get_mid_price(outcome, token_id)
        self._cached_prices[outcome] = price
        # ... rest of logic

2. Check Interval

Default: 1.0 second (line 56)
Impact: For mean reversion, 1s may be too fast and cause overtrading on noise.

Suggestion: Consider defaulting to 3-5 seconds for more stable signals.


Security Concerns

🔒 Good Practices

  1. No hardcoded credentials: Uses environment variables
  2. Input validation: Price and size checks before order placement
  3. Position limits: max_position prevents runaway exposure
  4. Stop loss: Automatic risk management

⚠️ Considerations

1. Order Size Validation

Location: spike_strategy.py:160

if self.cash < self.order_size:
    return

Issue: Doesn't account for fees or minimum order sizes.

Risk: Order could fail if cash is exactly order_size but fees make it insufficient.

Suggestion: Add fee buffer:

required_cash = self.order_size * 1.01  # 1% buffer for fees
if self.cash < required_cash:
    return

2. No Rate Limiting

Location: Multiple order operations throughout
Risk: Aggressive trading during volatile markets could hit exchange rate limits.

Suggestion: Add rate limiting or track API call frequency.


Test Coverage

❌ Missing Tests

Major Gap: No test files added for the new SpikeStrategy

Recommended Tests:

  1. Unit Tests:

    • test_ema_calculation(): Verify EMA updates correctly
    • test_spike_detection(): Test threshold detection logic
    • test_position_tracking(): Verify entry/exit lifecycle
    • test_cooldown(): Ensure re-entry blocked during cooldown
  2. Integration Tests:

    • Mock exchange and verify order placement
    • Test cleanup/shutdown sequence
    • Test WebSocket disconnect improvements
  3. Edge Cases:

    • Partial fills
    • Order failures
    • WebSocket disconnects during trading
    • Zero prices / null orderbook

Suggestion: Add tests/strategies/test_spike_strategy.py following the pattern of existing tests.


Alignment with CLAUDE.md

✅ Follows Guidelines

  1. UV-only: Uses uv run consistently ✓
  2. Clean code: Well-structured, readable ✓
  3. No unnecessary docs: Wiki docs are functional, not excessive ✓

⚠️ Potential Issues

  1. Documentation creation: Added 3 new wiki files (CLAUDE.md says "DO NOT create a new document")

    • Mitigation: These are functional documentation, not fluff, so acceptable
    • Note: Future PRs should avoid creating docs unless necessary
  2. No proactive testing: CLAUDE.md Create debugging frontend for trading strategies #5 says "Run and Debug yourself PROACTIVELY"

    • Missing: No evidence of running the strategy in the PR description
    • Suggestion: Add test run output or backtesting results to demonstrate it works

Recommendations

High Priority

  1. Add unit tests for SpikeStrategy core logic
  2. Fix position tracking to handle partial fills correctly
  3. Test the WebSocket disconnect changes under various shutdown scenarios

Medium Priority

  1. ⚠️ Add fee buffer to cash checks
  2. ⚠️ Improve cleanup logging (debug → warning)
  3. ⚠️ Document backtest results or live test evidence

Low Priority (Consider for follow-up)

  1. 💡 Add rate limiting for API calls
  2. 💡 Cache mid prices during tick to reduce orderbook fetches
  3. 💡 Add position size reconciliation on each tick

Summary

Overall Assessment: ⭐⭐⭐⭐ (4/5)

This is a solid PR that adds valuable functionality with clean code. The main concerns are:

  • Position tracking edge cases (partial fills)
  • Missing test coverage
  • Lack of demonstrated testing per CLAUDE.md

The WebSocket improvement is good, though it could handle edge cases better. The SpikeStrategy is well-implemented but needs tests and minor robustness improvements.

Recommendation: Request changes to add tests and fix position tracking before merging.


Great work overall! The strategy logic is sound and the code is clean. Once the position tracking is tightened up and tests are added, this will be ready to merge. 🚀

@guzus guzus merged commit a959ad8 into main Jan 2, 2026
6 checks passed
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.

Spike-bot

3 participants