PKScreener Developer Guide
Table of Contents
Introduction
PKScreener is a comprehensive stock screening and analysis tool for the Indian (NSE) and US (NASDAQ) markets. It provides technical analysis, pattern recognition, backtesting capabilities, and automated scanning through a CLI and Telegram bot interface.
Key Features
Stock Screening: 47+ technical screening criteria
Chart Pattern Recognition: VCP, Inside Bar, Confluence, etc.
Backtesting: Historical performance analysis
Piped Scanners: Chain multiple scanners together
Telegram Integration: Bot-based notifications and commands
Multi-Index Support: NSE indices, NASDAQ, sectoral indices
Getting Started
Prerequisites
# Python 3.11+ recommended
python --version
# Install dependencies
pip install -r requirements.txt
# Optional: TA-Lib for advanced indicators
# Follow instructions at: https://github.com/ta-lib/ta-lib-python
Running the Application
# CLI Mode
python pkscreener/pkscreenercli.py
# With specific options
python pkscreener/pkscreenercli.py -o "X:12:1" # Run scanner X, index 12, option 1
# Telegram Bot Mode
python pkscreener/pkscreenerbot.py
Environment Variables
Variable |
Description |
Default |
|---|---|---|
|
Set when running in CI/CD |
Not set |
|
Logging level (10=DEBUG, 20=INFO) |
30 |
|
Telegram chat ID for notifications |
Required for bot |
|
Telegram bot token |
Required for bot |
Project Structure
PKScreener-main/
├── pkscreener/
│ ├── __init__.py # Package initialization, version info
│ ├── globals.py # Global state, main() function, core workflow
│ ├── pkscreenercli.py # CLI entry point
│ ├── pkscreenerbot.py # Telegram bot entry point
│ ├── MainApplication.py # Application bootstrapping
│ │
│ └── classes/
│ ├── MenuOptions.py # Menu definitions and scan options
│ ├── MenuManager.py # Menu rendering and navigation
│ ├── MenuNavigation.py # User input handling for menus
│ ├── MainLogic.py # Core business logic handlers
│ │
│ ├── StockScreener.py # Main screening orchestrator
│ ├── ScreeningStatistics.py # Technical analysis calculations
│ ├── Pktalib.py # Technical indicators wrapper
│ ├── CandlePatterns.py # Candlestick pattern detection
│ │
│ ├── Fetcher.py # Stock data fetching
│ ├── DataLoader.py # Data loading and caching
│ ├── ConfigManager.py # User configuration management
│ │
│ ├── BacktestHandler.py # Backtesting workflow
│ ├── Backtest.py # Backtest calculations
│ ├── BacktestUtils.py # Backtest utilities
│ │
│ ├── ResultsManager.py # Result formatting and display
│ ├── ResultsLabeler.py # Result labeling and categorization
│ │
│ ├── TelegramNotifier.py # Telegram messaging
│ ├── bot/
│ │ └── BotHandlers.py # Telegram bot command handlers
│ │
│ └── Exchange/ # Exchange-specific implementations
│ └── Index/
│ └── Scanners/
│ └── Scanner.py
│
├── test/ # Unit and integration tests
├── docs/ # Documentation
└── screenshots/ # UI screenshots
Core Concepts
2. Options String Format
Scan options are encoded as colon-separated strings:
Format: MenuOption:IndexOption:ExecuteOption:SubOptions
Examples:
- X:12:1 → Scanner(X), All Nifty(12), Breakouts(1)
- X:1:7:4 → Scanner(X), Nifty50(1), Chart Patterns(7), VCP(4)
- B:12:1 → Backtest(B), All Nifty(12), Breakouts(1)
- P:1:3 → Piped(P), Predefined(1), Scan#3
3. Piped Scanners
Chain multiple scanners using the >| operator:
X:12:9:2.5:>|X:0:31:>|X:0:27:
│ │ │
│ │ └── ATR Cross filter
│ └── High Momentum filter
└── Volume > 2.5x average
4. Data Flow
┌─────────────────┐ ┌──────────────┐ ┌────────────────────┐
│ User Input │───▶│ MenuSystem │───▶│ getScannerMenu │
│ (CLI/Bot/API) │ │ │ │ Choices() │
└─────────────────┘ └──────────────┘ └────────────────────┘
│
▼
┌─────────────────┐ ┌──────────────┐ ┌────────────────────┐
│ Display │◀───│ Results │◀───│ runScanners() │
│ Results │ │ Manager │ │ │
└─────────────────┘ └──────────────┘ └────────────────────┘
│
┌────────────────────────┴──────────────┐
▼ ▼
┌────────────────────┐ ┌────────────────────┐
│ Multiprocessing │ │ StockScreener │
│ Pool │ │ .screenStocks() │
└────────────────────┘ └────────────────────┘
│
▼
┌────────────────────┐
│ ScreeningStatistics│
│ (Technical Calcs) │
└────────────────────┘
Development Workflow
Adding a New Scanner
Define Menu Option in
MenuOptions.py:
level2_X_MenuDict = {
# ... existing options ...
"48": "Your New Scanner Description",
}
MAX_SUPPORTED_MENU_OPTION = 48 # Update this
Implement Logic in
ScreeningStatistics.py:
def validateYourNewScanner(self, df, screenDict, saveDict):
"""
Implement your screening logic here.
Args:
df: pandas DataFrame with OHLCV data
screenDict: Dictionary for display results
saveDict: Dictionary for saved results
Returns:
bool: True if stock passes the criteria
"""
# Your implementation
close = df['Close'].iloc[-1]
sma_50 = df['Close'].rolling(50).mean().iloc[-1]
if close > sma_50:
screenDict['Signal'] = 'Bullish'
saveDict['Signal'] = 'Bullish'
return True
return False
Add to Screening Flow in
StockScreener.py:
# In screenStocks() method, around line 200+
elif executeOption == 48:
isValid = self.screeningStatistics.validateYourNewScanner(
df, screenDict, saveDict
)
Add Tests in
test/ScreeningStatistics_test.py:
def test_validateYourNewScanner_bullish(self):
df = create_sample_df(close_trend='up')
result = self.stats.validateYourNewScanner(df, {}, {})
assert result == True
Adding a New Chart Pattern
Define Sub-Menu in
MenuOptions.py:
level3_X_ChartPattern_MenuDict = {
# ... existing patterns ...
"10": "Your New Pattern",
}
Implement in
CandlePatterns.pyorScreeningStatistics.py:
def findYourNewPattern(self, df):
"""Detect your pattern in the data."""
# Implementation
return pattern_found, pattern_details
Testing
Running Tests
# Run all tests
pytest test/ -v
# Run specific test file
pytest test/ScreeningStatistics_test.py -v
# Run with coverage
coverage run -m pytest test/
coverage report --include="pkscreener/**"
# Run with timeout (for long-running tests)
pytest test/ --timeout=30
Test Structure
# test/YourModule_test.py
import pytest
from unittest.mock import MagicMock, patch
class TestYourFeature:
def setup_method(self):
"""Setup before each test."""
self.mock_config = MagicMock()
def test_feature_positive_case(self):
"""Test description."""
# Arrange
input_data = create_test_data()
# Act
result = your_function(input_data)
# Assert
assert result == expected_value
@patch('pkscreener.classes.Fetcher.fetchData')
def test_with_mocking(self, mock_fetch):
"""Test with external dependency mocked."""
mock_fetch.return_value = mock_data
# ... test implementation
Debugging
Enable Debug Logging
# Set environment variable
export PKDevTools_Default_Log_Level=10
# Or in code
import logging
logging.getLogger('pkscreener').setLevel(logging.DEBUG)
Common Debug Points
Menu Selection:
globals.py→getScannerMenuChoices()Stock Fetching:
Fetcher.py→fetchStockDataWithArgs()Screening Logic:
ScreeningStatistics.py→ specificvalidate*()methodsResults Processing:
ResultsManager.py→formatResults()
Useful Debug Commands
# Print DataFrame info
print(df.info())
print(df.tail())
# Check screening dict
import json
print(json.dumps(screenDict, indent=2, default=str))
Next Steps
See ARCHITECTURE.md for detailed system architecture
See SCAN_WORKFLOWS.md for scan category details
See Contributing Guidelines for contribution guidelines