Circuit Breakers and Market Event Handling

Prepare your algo trading systems for circuit breakers, trading halts, and extreme market events in NSE and BSE.

March 2020: Nifty crashes 13% in a day. Circuit breakers trigger. Your algo keeps sending orders into a halted market. Positions stuck. Losses mounting. Sound familiar?

Market events—circuit breakers, trading halts, flash crashes—happen. This guide ensures your system handles them gracefully, protecting capital when markets go crazy.

Understanding Circuit Breakers in India

NSE/BSE Circuit Breaker Rules

Index-Level (Nifty 50/Sensex):

  • 10% move: 45-minute halt
  • 15% move: Additional 1.75-hour halt
  • 20% move: Trading suspended for the day

Stock-Level:

  • 5% circuit (90% of stocks)
  • 10% circuit (actively traded)
  • 20% circuit (highly liquid)
class CircuitBreakerMonitor:
    """
    Monitor circuit breakers and trading halts
    """
    
    def __init__(self, broker_api):
        self.broker = broker_api
        self.circuit_breaker_status = {}
        self.halted_symbols = set()
    
    def check_circuit_breaker(self, symbol: str, current_price: float, reference_price: float) -> dict:
        """Check if stock near circuit limits"""
        price_change_pct = (current_price - reference_price) / reference_price
        
        # Get circuit limit for symbol
        circuit_limit = self.get_circuit_limit(symbol)
        
        # Check proximity to limits
        upper_limit = reference_price * (1 + circuit_limit)
        lower_limit = reference_price * (1 - circuit_limit)
        
        distance_to_upper = (upper_limit - current_price) / current_price
        distance_to_lower = (current_price - lower_limit) / current_price
        
        status = {
            'symbol': symbol,
            'current_price': current_price,
            'price_change_pct': price_change_pct,
            'circuit_limit': circuit_limit,
            'upper_limit': upper_limit,
            'lower_limit': lower_limit,
            'distance_to_upper_pct': distance_to_upper,
            'distance_to_lower_pct': distance_to_lower
        }
        
        # Warning if within 1% of circuit
        if distance_to_upper < 0.01:
            status['warning'] = 'NEAR_UPPER_CIRCUIT'
        elif distance_to_lower < 0.01:
            status['warning'] = 'NEAR_LOWER_CIRCUIT'
        else:
            status['warning'] = None
        
        return status
    
    def get_circuit_limit(self, symbol: str) -> float:
        """Get circuit limit for symbol"""
        # Simplified: most stocks have 5% or 10% circuits
        # In practice, fetch from broker API
        return 0.05  # 5% default
    
    def emergency_close_positions(self):
        """Emergency position closure during market event"""
        positions = self.broker.get_positions()
        
        for position in positions:
            try:
                # Close position at market price
                order = {
                    'symbol': position['symbol'],
                    'quantity': abs(position['quantity']),
                    'side': 'sell' if position['quantity'] > 0 else 'buy',
                    'order_type': 'MARKET',
                    'product': position['product']
                }
                
                self.broker.place_order(order)
                print(f"Emergency close: {position['symbol']}")
            
            except Exception as e:
                print(f"Failed to close {position['symbol']}: {e}")

monitor = CircuitBreakerMonitor(broker_api=None)
status = monitor.check_circuit_breaker('RELIANCE', 2450, 2500)
print(f"Circuit Status: {status}")