Skip to content

Conversation

Copy link

Copilot AI commented Nov 1, 2025

Adds a complete Python implementation of a trend-following RSI pullback strategy for swing trading, including backtesting engine, performance analysis, and signal scanning capabilities.

Strategy Implementation

Core Modules (9 files):

  • indicators.py - EMA, RSI (Wilder's method), ATR, volume ratios
  • entry_signals.py - Multi-condition validation: trend filter (200 EMA) + RSI pullback (35-45 zone) + volume confirmation (≥80% avg)
  • exit_signals.py - Priority-based exits: stop loss (1.5×ATR) > profit target (2R) > trend break > trailing stop > time stop
  • position_manager.py - Position sizing (1% portfolio risk), max 5 concurrent positions, partial exits (50% at 2R)
  • backtest_engine.py - Day-by-day simulation with mark-to-market equity tracking
  • performance_analyzer.py - Metrics: win rate, profit factor, R-multiple, CAGR, Sharpe/Sortino/Calmar ratios, drawdown analysis
  • scanner.py - Real-time signal detection across instrument universe
  • config.py - All parameters configurable (RSI thresholds, ATR multipliers, risk limits, etc.)

Usage

from trading_strategy import BacktestEngine, StrategyConfig

# Run backtest
engine = BacktestEngine(StrategyConfig())
trades, equity_curve = engine.run_backtest(
    data={'STOCK_A': df_a, 'STOCK_B': df_b},
    initial_capital=100000
)

# Analyze performance
from trading_strategy import PerformanceAnalyzer
analyzer = PerformanceAnalyzer(trades, equity_curve)
analyzer.print_report()  # Win rate, Sharpe, drawdown, exit breakdown

Additional Components

  • test_strategy.py - Unit tests for indicators, signals, position management (5 suites, all passing)
  • data_integration.py - Integration examples: PostgreSQL, Yahoo Finance, CSV, MongoDB, Kite API
  • main.py - Demo with synthetic data
  • Comprehensive documentation in README.md and SUMMARY.md

Total: 2,766 LOC across 15 files

Original prompt

RSI Pullback Strategy - Detailed Pseudocode

Strategy Overview

Name: Trend + RSI Pullback with Volume Confirmation
Type: Swing Trading (3 days to 3 months)
Universe: Nifty 500 liquid stocks
Timeframe: Daily bars


1. INITIALIZATION & PARAMETERS

STRATEGY_PARAMETERS:
    # Indicators
    EMA_PERIOD = 200
    RSI_PERIOD = 14
    VOLUME_MA_PERIOD = 20
    ATR_PERIOD = 14
    
    # Entry thresholds
    RSI_PULLBACK_LOWER = 35
    RSI_PULLBACK_UPPER = 45
    RSI_RECOVERY_THRESHOLD = 40
    VOLUME_MULTIPLIER = 0.8  # Min 80% of avg volume
    
    # Risk management
    RISK_PER_TRADE = 0.01  # 1% of portfolio
    ATR_STOP_MULTIPLIER = 1.5
    PROFIT_TAKE_RATIO = 2.0  # 2R
    PARTIAL_EXIT_PERCENT = 0.5  # Take 50% at 2R
    TRAIL_STOP_ATR = 1.5
    
    # Position limits
    MAX_CONCURRENT_POSITIONS = 5
    MAX_POSITION_SIZE_OF_ADV = 0.02  # Max 2% of avg daily volume
    
    # Liquidity filter
    MIN_AVG_VOLUME = 100000
    MIN_PRICE = 50  # Avoid penny stocks

PORTFOLIO_STATE:
    account_equity = 100000
    cash_available = 100000
    open_positions = []
    closed_trades = []
    daily_equity_curve = []

2. DATA LOADING & PREPROCESSING

FUNCTION load_and_prepare_data(instrument_key):
    # Fetch from database
    query = "SELECT timestamp, open, high, low, close, volume 
             FROM candles 
             WHERE instrument_key = ? 
             ORDER BY timestamp ASC"
    
    df = execute_query(query, instrument_key)
    
    # Data quality checks
    IF df.length < 250:
        RETURN None
    
    # Remove invalid rows
    df = df.filter(
        close > 0 AND
        volume >= 0 AND
        high >= low AND
        high >= close AND
        low <= close
    )
    
    # Sort by date
    df = df.sort_by('timestamp')
    
    RETURN df

3. INDICATOR CALCULATIONS

FUNCTION calculate_indicators(df):
    # 1. EMA 200 (exponential moving average)
    df['EMA200'] = EMA(df['close'], period=200)
    
    # 2. RSI 14 (Wilder's method)
    delta = df['close'].diff()
    gain = delta.where(delta > 0, 0)
    loss = -delta.where(delta < 0, 0)
    
    avg_gain = gain.ewm(alpha=1/14, adjust=False).mean()
    avg_loss = loss.ewm(alpha=1/14, adjust=False).mean()
    
    rs = avg_gain / avg_loss
    df['RSI'] = 100 - (100 / (1 + rs))
    
    # 3. ATR 14 (Average True Range)
    high_low = df['high'] - df['low']
    high_prev_close = abs(df['high'] - df['close'].shift(1))
    low_prev_close = abs(df['low'] - df['close'].shift(1))
    
    true_range = max(high_low, high_prev_close, low_prev_close)
    df['ATR'] = true_range.ewm(alpha=1/14, adjust=False).mean()
    
    # 4. Volume indicators
    df['Volume_MA20'] = df['volume'].rolling(20).mean()
    df['Volume_Ratio'] = df['volume'] / df['Volume_MA20']
    
    # 5. Previous RSI (for crossover detection)
    df['RSI_prev'] = df['RSI'].shift(1)
    
    # 6. Swing structure
    df['swing_low_20'] = df['low'].rolling(20).min()
    
    RETURN df

4. ENTRY SIGNAL DETECTION

FUNCTION check_long_entry(df, current_index):
    current_bar = df[current_index]
    prev_bar = df[current_index - 1]
    
    # Skip if not enough history
    IF current_index < 250:
        RETURN False, None
    
    # Skip if indicators are NaN
    IF any_is_nan(current_bar['EMA200', 'RSI', 'ATR', 'Volume_MA20']):
        RETURN False, None
    
    # === ENTRY CONDITIONS ===
    
    # 1. TREND FILTER: Price above 200 EMA
    trend_valid = current_bar['close'] > current_bar['EMA200']
    
    IF NOT trend_valid:
        RETURN False, None
    
    # 2. LIQUIDITY FILTER
    liquid = current_bar['Volume_MA20'] > MIN_AVG_VOLUME AND 
             current_bar['close'] > MIN_PRICE
    
    IF NOT liquid:
        RETURN False, None
    
    # 3. RSI PULLBACK PATTERN
    #    - Previous bars: RSI was in pullback zone (35-45)
    #    - Current bar: RSI crosses above 40 (recovery)
    
    # Check if RSI was in pullback zone in last 5 bars
    pullback_occurred = False
    FOR i in range(1, 6):
        lookback_bar = df[current_index - i]
        IF RSI_PULLBACK_LOWER <= lookback_bar['RSI'] <= RSI_PULLBACK_UPPER:
            pullback_occurred = True
            BREAK
    
    # Check current RSI recovery
    rsi_recovery = (
        current_bar['RSI'] > RSI_RECOVERY_THRESHOLD AND
        prev_bar['RSI'] <= RSI_RECOVERY_THRESHOLD
    )
    
    rsi_valid = pullback_occurred AND rsi_recovery
    
    IF NOT rsi_valid:
        RETURN False, None
    
    # 4. PRICE STRUCTURE: Must not close below 200 EMA on signal bar
    price_structure_valid = current_bar['close'] > current_bar['EMA200']
    
    IF NOT price_structure_valid:
        RETURN False, None
    
    # 5. VOLUME CONFIRMATION
    volume_confirmed = current_bar['Volume_Ratio'] >= VOLUME_MULTIPLIER
    
    IF NOT volume_confirmed:
        RETURN False, None
    
    # === ALL CONDITIONS MET - CALCULATE TRADE PARAMETERS ===
    
    entry_price = current...

</details>



<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for you](https://github.com/thecoderpiyush/DailyDSA/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 3 commits November 1, 2025 06:52
Co-authored-by: thecoderpiyush <234995129+thecoderpiyush@users.noreply.github.com>
Co-authored-by: thecoderpiyush <234995129+thecoderpiyush@users.noreply.github.com>
Co-authored-by: thecoderpiyush <234995129+thecoderpiyush@users.noreply.github.com>
Copilot AI changed the title [WIP] Add RSI pullback strategy with volume confirmation Implement RSI Pullback Trading Strategy with Backtesting Engine Nov 1, 2025
Copilot AI requested a review from thecoderpiyush November 1, 2025 07:00
Copilot finished work on behalf of thecoderpiyush November 1, 2025 07:00
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