Skip to content

Nifty #1117

@smbudni-sudo

Description

@smbudni-sudo

Nifty 50 Options Trading Assistant - PWA App

Complete Implementation Guide


1. Project Structure

nifty-options-pwa/
├── index.html
├── css/
│   └── styles.css
├── js/
│   ├── app.js
│   ├── calculator.js
│   ├── signals.js
│   ├── storage.js
│   └── ui.js
├── manifest.json
├── service-worker.js
└── README.md

2. HTML (index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Nifty 50 Options Trading Assistant - AI-Powered Entry/Exit Tips">
    <meta name="theme-color" content="#1a73e8">
    <title>Nifty 50 Options Trading Assistant</title>
    <link rel="manifest" href="manifest.json">
    <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 192'><rect fill='%231a73e8' width='192' height='192'/><text x='50%' y='50%' font-size='100' fill='white' text-anchor='middle' dy='.3em'>N50</text></svg>">
    <link rel="apple-touch-icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 192'><rect fill='%231a73e8' width='192' height='192'/><text x='50%' y='50%' font-size='100' fill='white' text-anchor='middle' dy='.3em'>N50</text></svg>">
    <link rel="stylesheet" href="css/styles.css">
</head>
<body>
    <div class="container">
        <!-- Header -->
        <header class="header">
            <h1>📈 Nifty 50 Options Assistant</h1>
            <p class="subtitle">Entry/Exit Tips & Greeks Calculator</p>
        </header>

        <!-- Navigation Tabs -->
        <div class="tabs">
            <button class="tab-btn active" data-tab="dashboard">Dashboard</button>
            <button class="tab-btn" data-tab="input">Input Data</button>
            <button class="tab-btn" data-tab="calculator">Calculator</button>
            <button class="tab-btn" data-tab="signals">Signals</button>
            <button class="tab-btn" data-tab="history">History</button>
            <button class="tab-btn" data-tab="settings">Settings</button>
        </div>

        <!-- Dashboard Tab -->
        <section id="dashboard" class="tab-content active">
            <div class="dashboard-grid">
                <div class="card">
                    <h3>Current Status</h3>
                    <div id="status-display" class="status-box">
                        <p>Nifty Spot: <span id="status-spot">-</span></p>
                        <p>Option Strike: <span id="status-strike">-</span></p>
                        <p>Premium: <span id="status-premium">-</span></p>
                        <p>Delta: <span id="status-delta">-</span></p>
                    </div>
                </div>

                <div class="card">
                    <h3>Next Action</h3>
                    <div id="signal-display" class="signal-box">
                        <p id="signal-text" class="signal-neutral">� No data. Enter market data to begin.</p>
                    </div>
                </div>

                <div class="card">
                    <h3>Entry/Exit Targets</h3>
                    <div id="targets-display" class="targets-box">
                        <p>Entry: <span id="target-entry">-</span></p>
                        <p>Target: <span id="target-target">-</span></p>
                        <p>Stop Loss: <span id="target-sl">-</span></p>
                        <p>Risk:Reward: <span id="target-rr">-</span></p>
                    </div>
                </div>

                <div class="card">
                    <h3>Quick Actions</h3>
                    <div class="action-buttons">
                        <button id="btn-refresh-data" class="btn btn-primary">🔄 Refresh Data</button>
                        <button id="btn-export-data" class="btn btn-secondary">📥 Export</button>
                    </div>
                </div>
            </div>
        </section>

        <!-- Input Data Tab -->
        <section id="input" class="tab-content">
            <div class="card">
                <h3>Enter Market Data</h3>
                <form id="market-form">
                    <div class="form-group">
                        <label>Nifty 50 Spot Price</label>
                        <input type="number" id="input-spot" step="0.05" placeholder="e.g., 25843.15" required>
                    </div>

                    <div class="form-group">
                        <label>Strike Price</label>
                        <input type="number" id="input-strike" step="50" placeholder="e.g., 26150" required>
                    </div>

                    <div class="form-group">
                        <label>Option Type</label>
                        <select id="input-type" required>
                            <option value="CE">Call (CE)</option>
                            <option value="PE">Put (PE)</option>
                        </select>
                    </div>

                    <div class="form-group">
                        <label>Current Premium</label>
                        <input type="number" id="input-premium" step="0.05" placeholder="e.g., 51.90" required>
                    </div>

                    <div class="form-group">
                        <label>Delta</label>
                        <input type="number" id="input-delta" step="0.01" min="0" max="1" placeholder="e.g., 0.25" required>
                    </div>

                    <div class="form-group">
                        <label>Implied Volatility (%)</label>
                        <input type="number" id="input-iv" step="0.1" placeholder="e.g., 9.0" required>
                    </div>

                    <div class="form-group">
                        <label>Days to Expiry</label>
                        <input type="number" id="input-dte" step="1" min="1" placeholder="e.g., 7" required>
                    </div>

                    <div class="form-group">
                        <label>Open Interest (Optional)</label>
                        <input type="number" id="input-oi" step="1" placeholder="e.g., 500000">
                    </div>

                    <button type="submit" class="btn btn-primary btn-block">💾 Save & Analyze</button>
                </form>
            </div>
        </section>

        <!-- Calculator Tab -->
        <section id="calculator" class="tab-content">
            <div class="card">
                <h3>Greeks & Price Calculator</h3>
                <form id="calc-form">
                    <div class="form-group">
                        <label>Nifty Move (points)</label>
                        <input type="number" id="calc-move" step="1" placeholder="e.g., 100" value="0">
                    </div>

                    <button type="button" id="btn-calculate" class="btn btn-primary btn-block">🔢 Calculate New Premium</button>
                </form>

                <div id="calc-results" class="calculation-results" style="display:none;">
                    <h4>Calculation Results</h4>
                    <p>New Premium: <span id="calc-new-premium">-</span></p>
                    <p>Premium Change: <span id="calc-premium-change">-</span></p>
                    <p>Expected P&L (per lot): <span id="calc-pnl">-</span> (75 contracts)</p>
                    <p>New Delta: <span id="calc-new-delta">-</span> (estimated)</p>
                    <p>Breakeven Price: <span id="calc-breakeven">-</span></p>
                </div>
            </div>

            <div class="card">
                <h3>Scenario Analysis</h3>
                <table id="scenario-table" class="scenario-table">
                    <thead>
                        <tr>
                            <th>Nifty Move</th>
                            <th>New Premium</th>
                            <th>P&L (per lot)</th>
                            <th>Signal</th>
                        </tr>
                    </thead>
                    <tbody id="scenario-tbody">
                    </tbody>
                </table>
            </div>
        </section>

        <!-- Signals Tab -->
        <section id="signals" class="tab-content">
            <div class="card">
                <h3>Trading Signals & AI Tips</h3>
                <div id="signals-container" class="signals-container">
                    <p class="text-neutral">Enter data to generate signals.</p>
                </div>
            </div>

            <div class="card">
                <h3>Signal Rules Configuration</h3>
                <form id="rules-form">
                    <div class="form-group">
                        <label>Entry Delta Threshold (Min)</label>
                        <input type="number" id="rule-delta-min" step="0.05" min="0" max="1" placeholder="e.g., 0.20" value="0.20">
                    </div>

                    <div class="form-group">
                        <label>Entry Delta Threshold (Max)</label>
                        <input type="number" id="rule-delta-max" step="0.05" min="0" max="1" placeholder="e.g., 0.60" value="0.60">
                    </div>

                    <div class="form-group">
                        <label>Profit Target (%)</label>
                        <input type="number" id="rule-target-pct" step="5" placeholder="e.g., 30" value="30">
                    </div>

                    <div class="form-group">
                        <label>Stop Loss (%)</label>
                        <input type="number" id="rule-sl-pct" step="5" placeholder="e.g., 15" value="15">
                    </div>

                    <button type="submit" class="btn btn-primary btn-block">💾 Save Rules</button>
                </form>
            </div>
        </section>

        <!-- History Tab -->
        <section id="history" class="tab-content">
            <div class="card">
                <h3>Trade History & Analysis</h3>
                <div id="history-container" class="history-container">
                    <p class="text-neutral">No trades recorded yet.</p>
                </div>
                <button id="btn-clear-history" class="btn btn-danger">🗑� Clear History</button>
            </div>
        </section>

        <!-- Settings Tab -->
        <section id="settings" class="tab-content">
            <div class="card">
                <h3>App Settings</h3>
                <form id="settings-form">
                    <div class="form-group">
                        <label>
                            <input type="checkbox" id="setting-notifications" checked>
                            Enable Browser Notifications
                        </label>
                    </div>

                    <div class="form-group">
                        <label>
                            <input type="checkbox" id="setting-dark-mode">
                            Dark Mode
                        </label>
                    </div>

                    <div class="form-group">
                        <label>Default Option Type</label>
                        <select id="setting-default-type">
                            <option value="CE">Call (CE)</option>
                            <option value="PE">Put (PE)</option>
                        </select>
                    </div>

                    <button type="submit" class="btn btn-primary btn-block">💾 Save Settings</button>
                </form>
            </div>

            <div class="card">
                <h3>Data Management</h3>
                <button id="btn-export-all" class="btn btn-secondary btn-block">📤 Export All Data</button>
                <button id="btn-import-data" class="btn btn-secondary btn-block">📥 Import Data</button>
                <input type="file" id="import-file" accept=".json" style="display:none;">
                <button id="btn-reset-app" class="btn btn-danger btn-block">🔄 Reset App</button>
            </div>

            <div class="card">
                <h3>About</h3>
                <p><strong>Nifty 50 Options Trading Assistant</strong></p>
                <p>Version: 1.0.0</p>
                <p>A lightweight PWA for options traders with no API dependency.</p>
                <p>Works offline with local data storage.</p>
            </div>
        </section>
    </div>

    <!-- Notification -->
    <div id="notification" class="notification"></div>

    <!-- Scripts -->
    <script src="js/storage.js"></script>
    <script src="js/calculator.js"></script>
    <script src="js/signals.js"></script>
    <script src="js/ui.js"></script>
    <script src="js/app.js"></script>

    <!-- Service Worker Registration -->
    <script>
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker.register('service-worker.js').then(reg => {
                console.log('Service Worker registered:', reg);
            }).catch(err => {
                console.log('Service Worker registration failed:', err);
            });
        }
    </script>
</body>
</html>

3. CSS (css/styles.css)

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

:root {
    --primary-color: #1a73e8;
    --success-color: #34a853;
    --warning-color: #fbbc04;
    --danger-color: #ea4335;
    --bg-color: #ffffff;
    --text-color: #202124;
    --border-color: #dadce0;
    --card-shadow: 0 1px 2px 0 rgba(60,64,67,.3), 0 1px 3px 1px rgba(60,64,67,.15);
}

body.dark-mode {
    --bg-color: #202124;
    --text-color: #e8eaed;
    --border-color: #5f6368;
    --card-shadow: 0 1px 2px 0 rgba(0,0,0,.3), 0 1px 3px 1px rgba(0,0,0,.15);
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
    background-color: var(--bg-color);
    color: var(--text-color);
    transition: all 0.3s ease;
}

.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
}

header {
    text-align: center;
    margin-bottom: 30px;
    padding: 20px 0;
    background: linear-gradient(135deg, var(--primary-color) 0%, #1557b0 100%);
    color: white;
    border-radius: 10px;
}

header h1 {
    font-size: 28px;
    margin-bottom: 5px;
}

header .subtitle {
    font-size: 14px;
    opacity: 0.9;
}

.tabs {
    display: flex;
    gap: 10px;
    margin-bottom: 20px;
    flex-wrap: wrap;
    border-bottom: 2px solid var(--border-color);
}

.tab-btn {
    padding: 12px 20px;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
    color: var(--text-color);
    border-bottom: 3px solid transparent;
    transition: all 0.3s ease;
}

.tab-btn:hover {
    color: var(--primary-color);
}

.tab-btn.active {
    color: var(--primary-color);
    border-bottom-color: var(--primary-color);
}

.tab-content {
    display: none;
}

.tab-content.active {
    display: block;
    animation: fadeIn 0.3s ease;
}

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

.dashboard-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 20px;
}

.card {
    background: var(--bg-color);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 20px;
    box-shadow: var(--card-shadow);
}

.card h3 {
    font-size: 16px;
    margin-bottom: 15px;
    color: var(--primary-color);
}

.card h4 {
    font-size: 14px;
    margin-bottom: 10px;
}

.status-box, .signal-box, .targets-box {
    background: var(--border-color);
    padding: 15px;
    border-radius: 6px;
    font-size: 14px;
    line-height: 1.8;
}

.status-box p, .targets-box p {
    display: flex;
    justify-content: space-between;
}

.signal-box {
    text-align: center;
    font-size: 16px;
    font-weight: 500;
}

.signal-buy {
    color: var(--success-color);
}

.signal-sell {
    color: var(--danger-color);
}

.signal-hold {
    color: var(--warning-color);
}

.signal-neutral {
    color: var(--text-color);
}

.form-group {
    margin-bottom: 15px;
}

.form-group label {
    display: block;
    margin-bottom: 5px;
    font-weight: 500;
    font-size: 14px;
}

.form-group input,
.form-group select {
    width: 100%;
    padding: 10px;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    font-size: 14px;
    background-color: var(--bg-color);
    color: var(--text-color);
    transition: border-color 0.3s ease;
}

.form-group input:focus,
.form-group select:focus {
    outline: none;
    border-color: var(--primary-color);
    box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.1);
}

.btn {
    padding: 10px 20px;
    border: none;
    border-radius: 6px;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.3s ease;
}

.btn-primary {
    background-color: var(--primary-color);
    color: white;
}

.btn-primary:hover {
    background-color: #1557b0;
    box-shadow: var(--card-shadow);
}

.btn-secondary {
    background-color: var(--border-color);
    color: var(--text-color);
}

.btn-secondary:hover {
    background-color: #dadce0;
}

.btn-danger {
    background-color: var(--danger-color);
    color: white;
}

.btn-danger:hover {
    background-color: #d33627;
}

.btn-block {
    width: 100%;
    display: block;
    margin-top: 10px;
}

.action-buttons {
    display: flex;
    gap: 10px;
}

.action-buttons .btn {
    flex: 1;
}

.calculation-results {
    background: var(--border-color);
    padding: 15px;
    border-radius: 6px;
    margin-top: 15px;
}

.calculation-results p {
    display: flex;
    justify-content: space-between;
    margin-bottom: 8px;
    font-size: 14px;
}

.scenario-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 15px;
    font-size: 13px;
}

.scenario-table th,
.scenario-table td {
    padding: 10px;
    text-align: right;
    border-bottom: 1px solid var(--border-color);
}

.scenario-table th {
    background-color: var(--border-color);
    font-weight: 600;
}

.scenario-table td:first-child,
.scenario-table th:first-child {
    text-align: left;
}

.signals-container {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.signal-item {
    background: var(--border-color);
    padding: 12px;
    border-radius: 6px;
    border-left: 4px solid var(--primary-color);
}

.signal-item.bullish {
    border-left-color: var(--success-color);
}

.signal-item.bearish {
    border-left-color: var(--danger-color);
}

.signal-item.neutral {
    border-left-color: var(--warning-color);
}

.signal-item strong {
    display: block;
    margin-bottom: 5px;
}

.history-container {
    max-height: 400px;
    overflow-y: auto;
}

.history-item {
    background: var(--border-color);
    padding: 12px;
    border-radius: 6px;
    margin-bottom: 10px;
    font-size: 13px;
}

.notification {
    position: fixed;
    top: 20px;
    right: 20px;
    padding: 15px 20px;
    background: var(--success-color);
    color: white;
    border-radius: 6px;
    box-shadow: var(--card-shadow);
    display: none;
    z-index: 1000;
    animation: slideIn 0.3s ease;
}

.notification.show {
    display: block;
}

.notification.error {
    background: var(--danger-color);
}

.notification.warning {
    background: var(--warning-color);
    color: var(--text-color);
}

@keyframes slideIn {
    from {
        transform: translateX(400px);
        opacity: 0;
    }
    to {
        transform: translateX(0);
        opacity: 1;
    }
}

@media (max-width: 768px) {
    .container {
        padding: 10px;
    }

    header h1 {
        font-size: 20px;
    }

    .tabs {
        flex-direction: column;
    }

    .tab-btn {
        border-bottom: none;
        border-radius: 6px;
        background: var(--border-color);
        margin-bottom: 5px;
    }

    .tab-btn.active {
        background: var(--primary-color);
        color: white;
    }

    .dashboard-grid {
        grid-template-columns: 1fr;
    }

    .action-buttons {
        flex-direction: column;
    }
}

4. JavaScript - Storage Module (js/storage.js)

class TradeStorage {
    constructor() {
        this.storageKey = 'nifty-options-data';
        this.historyKey = 'nifty-trade-history';
        this.rulesKey = 'nifty-trading-rules';
        this.settingsKey = 'nifty-app-settings';
    }

    saveMarketData(data) {
        const timestamp = new Date().toISOString();
        const dataWithTime = { ...data, timestamp };
        localStorage.setItem(this.storageKey, JSON.stringify(dataWithTime));
        return dataWithTime;
    }

    getMarketData() {
        const data = localStorage.getItem(this.storageKey);
        return data ? JSON.parse(data) : null;
    }

    addToHistory(trade) {
        const history = this.getHistory();
        history.push({ ...trade, id: Date.now() });
        localStorage.setItem(this.historyKey, JSON.stringify(history));
        return history;
    }

    getHistory() {
        const data = localStorage.getItem(this.historyKey);
        return data ? JSON.parse(data) : [];
    }

    clearHistory() {
        localStorage.removeItem(this.historyKey);
    }

    saveRules(rules) {
        localStorage.setItem(this.rulesKey, JSON.stringify(rules));
        return rules;
    }

    getRules() {
        const data = localStorage.getItem(this.rulesKey);
        return data ? JSON.parse(data) : {
            deltaMin: 0.20,
            deltaMax: 0.60,
            targetPct: 30,
            slPct: 15
        };
    }

    saveSettings(settings) {
        localStorage.setItem(this.settingsKey, JSON.stringify(settings));
        return settings;
    }

    getSettings() {
        const data = localStorage.getItem(this.settingsKey);
        return data ? JSON.parse(data) : {
            notifications: true,
            darkMode: false,
            defaultType: 'CE'
        };
    }

    exportData() {
        return {
            market: this.getMarketData(),
            history: this.getHistory(),
            rules: this.getRules(),
            settings: this.getSettings(),
            exportTime: new Date().toISOString()
        };
    }

    importData(jsonData) {
        const data = JSON.parse(jsonData);
        if (data.market) this.saveMarketData(data.market);
        if (data.history) localStorage.setItem(this.historyKey, JSON.stringify(data.history));
        if (data.rules) this.saveRules(data.rules);
        if (data.settings) this.saveSettings(data.settings);
        return true;
    }

    reset() {
        localStorage.removeItem(this.storageKey);
        localStorage.removeItem(this.historyKey);
        localStorage.removeItem(this.rulesKey);
        localStorage.removeItem(this.settingsKey);
    }
}

// Initialize global storage
const storage = new TradeStorage();

5. JavaScript - Calculator Module (js/calculator.js)

class OptionsCalculator {
    constructor() {
        this.riskFreeRate = 0.06; // Assumed 6% annual
        this.dividendYield = 0.015; // Assumed 1.5% dividend yield
        this.contractSize = 75; // Nifty contracts are 75 units per lot
    }

    // Black-Scholes Option Pricing Model (simplified)
    blackScholes(spot, strike, dte, iv, optionType = 'CE') {
        const daysPerYear = 365;
        const t = dte / daysPerYear;
        const r = this.riskFreeRate;
        const q = this.dividendYield;
        const sigma = iv / 100;

        const d1 = (Math.log(spot / strike) + (r - q + 0.5 * sigma * sigma) * t) / (sigma * Math.sqrt(t));
        const d2 = d1 - sigma * Math.sqrt(t);

        const N_d1 = this.normCDF(d1);
        const N_d2 = this.normCDF(d2);
        const N_minus_d1 = this.normCDF(-d1);
        const N_minus_d2 = this.normCDF(-d2);

        let price;
        if (optionType === 'CE') {
            price = spot * Math.exp(-q * t) * N_d1 - strike * Math.exp(-r * t) * N_d2;
        } else {
            price = strike * Math.exp(-r * t) * N_minus_d2 - spot * Math.exp(-q * t) * N_minus_d1;
        }

        return Math.max(price, 0);
    }

    // Cumulative Normal Distribution Function
    normCDF(x) {
        const a1 = 0.254829592;
        const a2 = -0.284496736;
        const a3 = 1.421413741;
        const a4 = -1.453152027;
        const a5 = 1.061405429;
        const p = 0.3275911;

        const sign = x < 0 ? -1 : 1;
        x = Math.abs(x) / Math.sqrt(2);

        const t = 1.0 / (1.0 + p * x);
        const t2 = t * t;
        const t3 = t2 * t;
        const t4 = t3 * t;
        const t5 = t4 * t;

        const y = 1.0 - (((((a5 * t5 + a4 * t4) + a3 * t3) + a2 * t2) + a1 * t) * t * Math.exp(-x * x));

        return 0.5 * (1.0 + sign * y);
    }

    // Calculate Delta
    calculateDelta(spot, strike, dte, iv, optionType = 'CE') {
        const daysPerYear = 365;
        const t = dte / daysPerYear;
        const r = this.riskFreeRate;
        const q = this.dividendYield;
        const sigma = iv / 100;

        const d1 = (Math.log(spot / strike) + (r - q + 0.5 * sigma * sigma) * t) / (sigma * Math.sqrt(t));
        
        if (optionType === 'CE') {
            return Math.exp(-q * t) * this.normCDF(d1);
        } else {
            return Math.exp(-q * t) * (this.normCDF(d1) - 1);
        }
    }

    // Calculate Gamma
    calculateGamma(spot, strike, dte, iv) {
        const daysPerYear = 365;
        const t = dte / daysPerYear;
        const q = this.dividendYield;
        const sigma = iv / 100;

        const d1 = (Math.log(spot / strike) + (this.riskFreeRate - q + 0.5 * sigma * sigma) * t) / (sigma * Math.sqrt(t));
        
        return (Math.exp(-q * t) * this.normPDF(d1)) / (spot * sigma * Math.sqrt(t));
    }

    // Calculate Vega
    calculateVega(spot, strike, dte, iv) {
        const daysPerYear = 365;
        const t = dte / daysPerYear;
        const q = this.dividendYield;
        const sigma = iv / 100;

        const d1 = (Math.log(spot / strike) + (this.riskFreeRate - q + 0.5 * sigma * sigma) * t) / (sigma * Math.sqrt(t));
        
        return spot * Math.exp(-q * t) * this.normPDF(d1) * Math.sqrt(t) / 100;
    }

    // Normal PDF
    normPDF(x) {
        return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI);
    }

    // Estimate new premium based on delta and nifty move
    estimateNewPremium(currentPremium, delta, niftyMove, gamma = 0) {
        // First order approximation: premium change = delta * move + 0.5 * gamma * move^2
        const deltaChange = delta * niftyMove;
        const gammaChange = 0.5 * gamma * niftyMove * niftyMove;
        const newPremium = currentPremium + deltaChange + gammaChange;
        return Math.max(newPremium, 0);
    }

    // Calculate P&L
    calculatePnL(entryPrice, exitPrice, quantity = 1, contractSize = 75) {
        const priceDiff = exitPrice - entryPrice;
        return priceDiff * quantity * contractSize;
    }

    // Calculate breakeven price
    calculateBreakeven(currentPremium, spot, strike, optionType = 'CE') {
        if (optionType === 'CE') {
            return strike + currentPremium;
        } else {
            return strike - currentPremium;
        }
    }

    // Implied Volatility Range Analysis
    ivPercentile(currentIV, historicalIVs = []) {
        if (historicalIVs.length === 0) return 0.5;
        const lowerCount = historicalIVs.filter(iv => iv < currentIV).length;
        return lowerCount / historicalIVs.length;
    }
}

const calculator = new OptionsCalculator();

6. JavaScript - Signals Module (js/signals.js)

class SignalGenerator {
    constructor() {
        this.signals = [];
    }

    generateSignals(marketData, rules) {
        this.signals = [];
        const { spot, strike, premium, delta, iv, dte, optionType } = marketData;

        // Signal 1: Delta-based Entry
        if (delta >= rules.deltaMin && delta <= rules.deltaMax) {
            this.signals.push({
                type: 'entry',
                confidence: this.calculateConfidence(delta, rules),
                message: `✅ ENTRY SIGNAL: Delta ${delta.toFixed(2)} in ideal range [${rules.deltaMin}-${rules.deltaMax}]`,
                signal: 'BUY'
            });
        } else if (delta < rules.deltaMin) {
            this.signals.push({
                type: 'wait',
                confidence: 0.3,
                message: `� WAIT: Delta ${delta.toFixed(2)} too low. Good for rich premium selling but risky for buying.`,
                signal: 'NEUTRAL'
            });
        }

        // Signal 2: IV Analysis
        const ivPercentile = this.analyzeIV(iv);
        if (ivPercentile > 0.75) {
            this.signals.push({
                type: 'volatility',
                confidence: 0.7,
                message: `⚠�  HIGH IV (${iv}): Consider selling options or widening stops.`,
                signal: optionType === 'CE' ? 'SELL' : 'BUY'
            });
        } else if (ivPercentile < 0.25) {
            this.signals.push({
                type: 'volatility',
                confidence: 0.7,
                message: `📈 LOW IV (${iv}): Good buying opportunity. IV likely to expand.`,
                signal: optionType === 'CE' ? 'BUY' : 'SELL'
            });
        }

        // Signal 3: Time Decay Analysis
        if (dte <= 2) {
            this.signals.push({
                type: 'expiry',
                confidence: 0.8,
                message: `🔴 EXPIRY NEAR (${dte} days): High theta decay. Square off or roll.`,
                signal: 'HOLD/EXIT'
            });
        } else if (dte <= 7) {
            this.signals.push({
                type: 'expiry',
                confidence: 0.6,
                message: `🟡 EXPIRY WEEK: Moderate theta decay. Monitor closely.`,
                signal: 'HOLD'
            });
        }

        // Signal 4: ATM or OTM Analysis
        const moneyness = spot / strike;
        if (Math.abs(Math.log(moneyness)) < 0.01) {
            this.signals.push({
                type: 'moneyness',
                confidence: 0.75,
                message: `🎯 ATM STRIKE: Maximum theta decay and gamma. High risk/reward.`,
                signal: optionType === 'CE' ? 'NEUTRAL' : 'NEUTRAL'
            });
        }

        // Signal 5: Premium Value
        const premiumVsStrike = (premium / strike) * 100;
        if (premiumVsStrike < 0.5) {
            this.signals.push({
                type: 'premium',
                confidence: 0.6,
                message: `💰 CHEAP: Premium only ${premiumVsStrike.toFixed(2)}% of strike. Good value.`,
                signal: 'BUY'
            });
        } else if (premiumVsStrike > 3) {
            this.signals.push({
                type: 'premium',
                confidence: 0.7,
                message: `🤑 EXPENSIVE: Premium ${premiumVsStrike.toFixed(2)}% of strike. Consider taking profits.`,
                signal: 'SELL'
            });
        }

        return this.signals;
    }

    calculateConfidence(delta, rules) {
        const midpoint = (rules.deltaMin + rules.deltaMax) / 2;
        const range = (rules.deltaMax - rules.deltaMin) / 2;
        return 1 - (Math.abs(delta - midpoint) / range);
    }

    analyzeIV(iv) {
        // Simple IV percentile: assuming 5-25 is the normal range for Nifty
        const min = 5, max = 25;
        const percentile = Math.max(0, Math.min(1, (iv - min) / (max - min)));
        return percentile;
    }

    getOverallSignal(signals) {
        if (signals.length === 0) return 'NEUTRAL';

        const buySignals = signals.filter(s => s.signal === 'BUY').length;
        const sellSignals = signals.filter(s => s.signal === 'SELL').length;
        const holdSignals = signals.filter(s => s.signal === 'HOLD' || s.signal === 'NEUTRAL').length;

        if (buySignals > sellSignals && buySignals > holdSignals) return 'BUY';
        if (sellSignals > buySignals && sellSignals > holdSignals) return 'SELL';
        return 'HOLD';
    }

    generateTargetsAndStops(marketData, rules) {
        const { premium, spot, strike, optionType } = marketData;
        const targetPct = rules.targetPct / 100;
        const slPct = rules.slPct / 100;

        const targetPrice = premium * (1 + targetPct);
        const stopPrice = premium * (1 - slPct);

        return {
            entry: premium,
            target: Math.max(targetPrice, 1),
            stopLoss: Math.max(stopPrice, 0.05),
            riskReward: (targetPrice - premium) / (premium - stopPrice)
        };
    }
}

const signalGen = new SignalGenerator();

7. JavaScript - UI Module (js/ui.js)

class UIManager {
    constructor() {
        this.setupEventListeners();
    }

    setupEventListeners() {
        // Tab switching
        document.querySelectorAll('.tab-btn').forEach(btn => {
            btn.addEventListener('click', (e) => this.switchTab(e.target.dataset.tab));
        });

        // Market data form
        document.getElementById('market-form').addEventListener('submit', (e) => {
            e.preventDefault();
            this.handleMarketDataSubmit();
        });

        // Calculator form
        document.getElementById('calc-form').addEventListener('submit', (e) => {
            e.preventDefault();
        });

        document.getElementById('btn-calculate').addEventListener('click', () => this.handleCalculate());

        // Rules form
        document.getElementById('rules-form').addEventListener('submit', (e) => {
            e.preventDefault();
            this.handleRulesSubmit();
        });

        // Settings form
        document.getElementById('settings-form').addEventListener('submit', (e) => {
            e.preventDefault();
            this.handleSettingsSubmit();
        });

        // Buttons
        document.getElementById('btn-refresh-data').addEventListener('click', () => this.refreshDashboard());
        document.getElementById('btn-export-data').addEventListener('click', () => this.exportData());
        document.getElementById('btn-clear-history').addEventListener('click', () => this.clearHistory());
        document.getElementById('btn-export-all').addEventListener('click', () => this.exportAllData());
        document.getElementById('btn-import-data').addEventListener('click', () => {
            document.getElementById('import-file').click();
        });
        document.getElementById('import-file').addEventListener('change', (e) => this.handleImport(e));
        document.getElementById('btn-reset-app').addEventListener('click', () => this.resetApp());

        // Dark mode
        document.getElementById('setting-dark-mode').addEventListener('change', (e) => {
            document.body.classList.toggle('dark-mode', e.target.checked);
        });

        // Load saved settings
        this.loadSettings();
        this.refreshDashboard();
    }

    switchTab(tabName) {
        document.querySelectorAll('.tab-content').forEach(tab => tab.classList.remove('active'));
        document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));

        document.getElementById(tabName).classList.add('active');
        document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
    }

    handleMarketDataSubmit() {
        const marketData = {
            spot: parseFloat(document.getElementById('input-spot').value),
            strike: parseFloat(document.getElementById('input-strike').value),
            optionType: document.getElementById('input-type').value,
            premium: parseFloat(document.getElementById('input-premium').value),
            delta: parseFloat(document.getElementById('input-delta').value),
            iv: parseFloat(document.getElementById('input-iv').value),
            dte: parseInt(document.getElementById('input-dte').value),
            oi: parseInt(document.getElementById('input-oi').value) || 0
        };

        storage.saveMarketData(marketData);
        this.showNotification('✅ Market data saved!', 'success');
        this.refreshDashboard();

        // Generate and show signals
        const rules = storage.getRules();
        const signals = signalGen.generateSignals(marketData, rules);
        this.displaySignals(signals);
    }

    handleCalculate() {
        const marketData = storage.getMarketData();
        if (!marketData) {
            this.showNotification('Please enter market data first.', 'error');
            return;
        }

        const move = parseFloat(document.getElementById('calc-move').value);
        const newPremium = calculator.estimateNewPremium(marketData.premium, marketData.delta, move);
        const premiumChange = newPremium - marketData.premium;
        const pnl = calculator.calculatePnL(marketData.premium, newPremium, 1, 75);

        const resultsDiv = document.getElementById('calc-results');
        resultsDiv.style.display = 'block';
        document.getElementById('calc-new-premium').textContent = newPremium.toFixed(2);
        document.getElementById('calc-premium-change').textContent = premiumChange.toFixed(2);
        document.getElementById('calc-pnl').textContent = '₹' + pnl.toFixed(0);
        document.getElementById('calc-new-delta').textContent = (marketData.delta + 0.02 * move / 100).toFixed(3);
        document.getElementById('calc-breakeven').textContent = calculator.calculateBreakeven(newPremium, marketData.spot, marketData.strike, marketData.optionType).toFixed(2);

        // Scenario analysis
        this.generateScenarioTable(marketData);
    }

    generateScenarioTable(marketData) {
        const tbody = document.getElementById('scenario-tbody');
        tbody.innerHTML = '';

        const moves = [-200, -100, -50, 0, 50, 100, 200];
        moves.forEach(move => {
            const newPremium = calculator.estimateNewPremium(marketData.premium, marketData.delta, move);
            const pnl = calculator.calculatePnL(marketData.premium, newPremium, 1, 75);
            const signal = pnl > 0 ? '📈 Profit' : pnl < 0 ? '📉 Loss' : '➡� Breakeven';

            const row = `<tr>
                <td>${move >= 0 ? '+' : ''}${move}</td>
                <td>${newPremium.toFixed(2)}</td>
                <td style="color: ${pnl > 0 ? 'var(--success-color)' : 'var(--danger-color)'}">₹${pnl.toFixed(0)}</td>
                <td>${signal}</td>
            </tr>`;
            tbody.innerHTML += row;
        });
    }

    handleRulesSubmit() {
        const rules = {
            deltaMin: parseFloat(document.getElementById('rule-delta-min').value),
            deltaMax: parseFloat(document.getElementById('rule-delta-max').value),
            targetPct: parseFloat(document.getElementById('rule-target-pct').value),
            slPct: parseFloat(document.getElementById('rule-sl-pct').value)
        };

        storage.saveRules(rules);
        this.showNotification('✅ Trading rules saved!', 'success');
    }

    handleSettingsSubmit() {
        const settings = {
            notifications: document.getElementById('setting-notifications').checked,
            darkMode: document.getElementById('setting-dark-mode').checked,
            defaultType: document.getElementById('setting-default-type').value
        };

        storage.saveSettings(settings);
        this.showNotification('✅ Settings saved!', 'success');
    }

    refreshDashboard() {
        const marketData = storage.getMarketData();

        if (!marketData) {
            document.getElementById('status-display').innerHTML = '<p class="text-neutral">No data entered yet. Go to "Input Data" tab.</p>';
            return;
        }

        // Update status
        document.getElementById('status-spot').textContent = marketData.spot.toFixed(2);
        document.getElementById('status-strike').textContent = marketData.strike;
        document.getElementById('status-premium').textContent = marketData.premium.toFixed(2);
        document.getElementById('status-delta').textContent = marketData.delta.toFixed(3);

        // Generate signals
        const rules = storage.getRules();
        const signals = signalGen.generateSignals(marketData, rules);
        const overallSignal = signalGen.getOverallSignal(signals);

        // Display overall signal
        const signalDiv = document.getElementById('signal-display');
        let signalHTML = `<p id="signal-text" class="signal-${overallSignal.toLowerCase()}">`;
        signalHTML += overallSignal === 'BUY' ? '🟢 BUY SIGNAL' : overallSignal === 'SELL' ? '🔴 SELL SIGNAL' : '🟡 HOLD SIGNAL';
        signalHTML += `</p>`;
        signalDiv.innerHTML = signalHTML;

        // Display targets
        const targets = signalGen.generateTargetsAndStops(marketData, rules);
        document.getElementById('target-entry').textContent = targets.entry.toFixed(2);
        document.getElementById('target-target').textContent = targets.target.toFixed(2);
        document.getElementById('target-sl').textContent = targets.stopLoss.toFixed(2);
        document.getElementById('target-rr').textContent = targets.riskReward.toFixed(2);

        // Update history
        this.displayHistory();
    }

    displaySignals(signals) {
        const container = document.getElementById('signals-container');
        if (signals.length === 0) {
            container.innerHTML = '<p class="text-neutral">No signals generated. Check your data.</p>';
            return;
        }

        let html = '';
        signals.forEach(signal => {
            const confidence = (signal.confidence * 100).toFixed(0);
            const signalClass = signal.signal === 'BUY' ? 'bullish' : signal.signal === 'SELL' ? 'bearish' : 'neutral';
            html += `<div class="signal-item ${signalClass}">
                <strong>${signal.message}</strong>
                <small>Confidence: ${confidence}% | Type: ${signal.type}</small>
            </div>`;
        });
        container.innerHTML = html;
    }

    displayHistory() {
        const history = storage.getHistory();
        const container = document.getElementById('history-container');

        if (history.length === 0) {
            container.innerHTML = '<p class="text-neutral">No trades recorded yet.</p>';
            return;
        }

        let html = '';
        history.reverse().slice(0, 10).forEach(trade => {
            const date = new Date(trade.timestamp).toLocaleString();
            html += `<div class="history-item">
                <strong>${trade.strike} ${trade.optionType}</strong> - ${date}<br>
                Entry: ₹${trade.premium} | Status: ${trade.status || 'Active'}
            </div>`;
        });
        container.innerHTML = html;
    }

    exportData() {
        const marketData = storage.getMarketData();
        if (!marketData) {
            this.showNotification('No data to export.', 'warning');
            return;
        }

        const dataStr = JSON.stringify(storage.exportData(), null, 2);
        const blob = new Blob([dataStr], { type: 'application/json' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `nifty-options-${new Date().toISOString().split('T')[0]}.json`;
        a.click();
        this.showNotification('✅ Data exported!', 'success');
    }

    exportAllData() {
        const dataStr = JSON.stringify(storage.exportData(), null, 2);
        const blob = new Blob([dataStr], { type: 'application/json' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `nifty-complete-${new Date().toISOString().split('T')[0]}.json`;
        a.click();
        this.showNotification('✅ Complete data exported!', 'success');
    }

    handleImport(event) {
        const file = event.target.files[0];
        if (!file) return;

        const reader = new FileReader();
        reader.onload = (e) => {
            try {
                storage.importData(e.target.result);
                this.showNotification('✅ Data imported successfully!', 'success');
                this.refreshDashboard();
            } catch (err) {
                this.showNotification('� Import failed: Invalid file format', 'error');
            }
        };
        reader.readAsText(file);
    }

    clearHistory() {
        if (confirm('Are you sure you want to clear all trade history?')) {
            storage.clearHistory();
            this.showNotification('✅ History cleared!', 'success');
            this.displayHistory();
        }
    }

    resetApp() {
        if (confirm('This will reset the entire app. Are you sure?')) {
            storage.reset();
            location.reload();
        }
    }

    loadSettings() {
        const settings = storage.getSettings();
        document.getElementById('setting-notifications').checked = settings.notifications;
        document.getElementById('setting-dark-mode').checked = settings.darkMode;
        document.getElementById('setting-default-type').value = settings.defaultType;

        if (settings.darkMode) {
            document.body.classList.add('dark-mode');
        }
    }

    showNotification(message, type = 'success') {
        const notif = document.getElementById('notification');
        notif.textContent = message;
        notif.className = `notification show ${type}`;

        setTimeout(() => {
            notif.classList.remove('show');
        }, 3000);
    }
}

const ui = new UIManager();

8. JavaScript - Main App (js/app.js)

// Main app initialization
class NiftyApp {
    constructor() {
        console.log('Nifty 50 Options Trading Assistant v1.0.0 loaded');
        console.log('App running in standby mode with local storage');
        this.initializeApp();
    }

    initializeApp() {
        // Check if app was installed
        if ('storage' in navigator && 'serviceWorker' in navigator) {
            console.log('PWA features available');
        }

        // Check for updates periodically
        this.setupPeriodicChecks();
    }

    setupPeriodicChecks() {
        // Check every 5 minutes if we should remind user to update data
        setInterval(() => {
            const lastData = storage.getMarketData();
            if (lastData) {
                const lastTime = new Date(lastData.timestamp);
                const now = new Date();
                const minutesElapsed = (now - lastTime) / (1000 * 60);

                // Remind after 30 minutes
                if (minutesElapsed > 30 && storage.getSettings().notifications) {
                    const settings = storage.getSettings();
                    if (settings.notifications && 'Notification' in window && Notification.permission === 'granted') {
                        new Notification('Update Market Data', {
                            body: `Your data is ${Math.floor(minutesElapsed)} minutes old. Update for fresh signals!`,
                            icon: 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 192"><rect fill="%231a73e8" width="192" height="192"/><text x="50%" y="50%" font-size="100" fill="white" text-anchor="middle" dy=".3em">N50</text></svg>'
                        });
                    }
                }
            }
        }, 5 * 60 * 1000);
    }
}

// Initialize app when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
    const app = new NiftyApp();
});

// Request notification permission
if ('Notification' in window && Notification.permission === 'default') {
    Notification.requestPermission();
}

9. Service Worker (service-worker.js)

const CACHE_NAME = 'nifty-options-v1.0.0';
const urlsToCache = [
    '/',
    '/index.html',
    '/css/styles.css',
    '/js/app.js',
    '/js/calculator.js',
    '/js/signals.js',
    '/js/storage.js',
    '/js/ui.js',
    '/manifest.json'
];

// Install event
self.addEventListener('install', event => {
    event.waitUntil(
        caches.open(CACHE_NAME).then(cache => {
            console.log('Opened cache:', CACHE_NAME);
            return cache.addAll(urlsToCache.map(url => new Request(url, { cache: 'reload' })).catch(() => null));
        })
    );
    self.skipWaiting();
});

// Activate event
self.addEventListener('activate', event => {
    event.waitUntil(
        caches.keys().then(cacheNames => {
            return Promise.all(
                cacheNames.map(cacheName => {
                    if (cacheName !== CACHE_NAME) {
                        console.log('Deleting old cache:', cacheName);
                        return caches.delete(cacheName);
                    }
                })
            );
        })
    );
    self.clients.claim();
});

// Fetch event
self.addEventListener('fetch', event => {
    event.respondWith(
        caches.match(event.request).then(response => {
            return response || fetch(event.request).catch(() => {
                return caches.match('/index.html');
            });
        })
    );
});

// Background sync for data persistence
self.addEventListener('sync', event => {
    if (event.tag === 'sync-data') {
        event.waitUntil(Promise.resolve());
    }
});

10. Manifest File (manifest.json)

{
    "name": "Nifty 50 Options Trading Assistant",
    "short_name": "N50 Trader",
    "description": "AI-powered entry/exit tips for Nifty 50 options with Greeks calculator",
    "start_url": "/",
    "scope": "/",
    "display": "standalone",
    "orientation": "portrait-primary",
    "background_color": "#ffffff",
    "theme_color": "#1a73e8",
    "icons": [
        {
            "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 192'><rect fill='%231a73e8' width='192' height='192'/><text x='50%' y='50%' font-size='100' fill='white' text-anchor='middle' dy='.3em'>N50</text></svg>",
            "sizes": "192x192",
            "type": "image/svg+xml",
            "purpose": "any"
        },
        {
            "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><rect fill='%231a73e8' width='512' height='512'/><text x='50%' y='50%' font-size='300' fill='white' text-anchor='middle' dy='.3em'>N50</text></svg>",
            "sizes": "512x512",
            "type": "image/svg+xml",
            "purpose": "any maskable"
        }
    ],
    "categories": ["finance", "productivity"],
    "screenshots": [
        {
            "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 540 720'><rect fill='%231a73e8' width='540' height='720'/><text x='50%' y='50%' font-size='100' fill='white' text-anchor='middle'>N50 Trader</text></svg>",
            "sizes": "540x720",
            "type": "image/svg+xml",
            "form_factor": "narrow"
        }
    ],
    "shortcuts": [
        {
            "name": "Enter Market Data",
            "short_name": "New Trade",
            "description": "Enter current market data for analysis",
            "url": "/?tab=input",
            "icons": [
                {
                    "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 192'><rect fill='%231a73e8' width='192' height='192'/><text x='50%' y='50%' font-size='100' fill='white' text-anchor='middle' dy='.3em'>+</text></svg>",
                    "sizes": "192x192"
                }
            ]
        }
    ]
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions