"""
Tick Data Analysis Dashboard for First Minute Breakout Strategy
Analyzes tick-by-tick data stored in market_data.db and calculates potential trades
"""
import sqlite3
import streamlit as st
import pandas as pd
import plotly.graph_objects as go
from datetime import datetime, timedelta
from typing import Dict, List, Tuple, Optional
import json
# Page configuration
st.set_page_config(
page_title="Tick Analysis - First Minute Breakout",
page_icon="đ",
layout="wide",
initial_sidebar_state="expanded"
)
# Constants
DEFAULT_INSTRUMENT = "NSE_EQ|INE669E01016"
DB_PATH = 'market_data.db'
def get_available_instruments(db_path: str = DB_PATH) -> List[str]:
"""Get list of instruments with tick data"""
try:
with sqlite3.connect(db_path) as conn:
cursor = conn.cursor()
cursor.execute("""
SELECT DISTINCT instrument, COUNT(*) as tick_count,
DATE(MIN(timestamp)) as first_date,
DATE(MAX(timestamp)) as last_date
FROM ticks
GROUP BY instrument
ORDER BY tick_count DESC
""")
results = cursor.fetchall()
return results
except Exception as e:
st.error(f"Error fetching instruments: {e}")
return []
def get_tick_data(instrument: str, date: str, db_path: str = DB_PATH) -> pd.DataFrame:
"""Get all tick data for a specific instrument and date"""
try:
with sqlite3.connect(db_path) as conn:
query = """
SELECT
timestamp,
ltp,
COALESCE(high, ltp) as high,
COALESCE(low, ltp) as low,
close_price
FROM ticks
WHERE instrument = ?
AND DATE(timestamp) = ?
ORDER BY timestamp
"""
df = pd.read_sql_query(query, conn, params=(instrument, date))
if not df.empty:
df['timestamp'] = pd.to_datetime(df['timestamp'])
return df
except Exception as e:
st.error(f"Error fetching tick data: {e}")
return pd.DataFrame()
def get_first_minute_data(df: pd.DataFrame) -> Tuple[Optional[float], Optional[float], str]:
"""Extract first minute high and low (9:15 AM or first available minute)"""
if df.empty:
return None, None, "No data"
# Try to find 9:15 AM data first
first_minute = df[
(df['timestamp'].dt.hour == 9) &
(df['timestamp'].dt.minute == 15)
]
if not first_minute.empty:
h1 = first_minute['high'].max()
l1 = first_minute['low'].min()
return h1, l1, "9:15 AM"
# If 9:15 AM data not found, use the first available minute of data
first_timestamp = df['timestamp'].min()
first_minute_time = first_timestamp.replace(second=0, microsecond=0)
next_minute_time = first_minute_time + timedelta(minutes=1)
first_minute = df[
(df['timestamp'] >= first_minute_time) &
(df['timestamp'] < next_minute_time)
]
if first_minute.empty:
# Fallback: use first 60 seconds of data
first_minute = df.head(min(60, len(df)))
h1 = first_minute['high'].max()
l1 = first_minute['low'].min()
first_min_str = first_minute_time.strftime('%H:%M')
return h1, l1, f"{first_min_str} (First available minute)"
def calculate_trades(df: pd.DataFrame, h1: float, l1: float,
target_pct: float = 0.01, stop_loss_pct: float = 0.002,
first_minute_end_time: pd.Timestamp = None) -> List[Dict]:
"""Calculate all trades based on first minute breakout strategy"""
trades = []
position = None
entry_price = None
entry_time = None
trade_id = 0
for idx, row in df.iterrows():
timestamp = row['timestamp']
high = row['high']
low = row['low']
ltp = row['ltp']
# Skip first minute based on actual first minute end time
if first_minute_end_time and timestamp < first_minute_end_time:
continue
# Check for exit if in position
if position:
if position == 'BUY':
target = entry_price * (1 + target_pct)
stop_loss = entry_price * (1 - stop_loss_pct)
if ltp >= target:
trade_id += 1
trades.append({
'trade_id': trade_id,
'type': 'BUY',
'entry_time': entry_time,
'entry_price': entry_price,
'exit_time': timestamp,
'exit_price': ltp,
'exit_reason': 'TARGET',
'pnl': ltp - entry_price,
'pnl_pct': ((ltp - entry_price) / entry_price) * 100,
'duration': str(timestamp - entry_time)
})
position = None
entry_price = None
entry_time = None
elif ltp <= stop_loss:
trade_id += 1
trades.append({
'trade_id': trade_id,
'type': 'BUY',
'entry_time': entry_time,
'entry_price': entry_price,
'exit_time': timestamp,
'exit_price': ltp,
'exit_reason': 'STOPLOSS',
'pnl': ltp - entry_price,
'pnl_pct': ((ltp - entry_price) / entry_price) * 100,
'duration': str(timestamp - entry_time)
})
position = None
entry_price = None
entry_time = None
elif position == 'SELL':
target = entry_price * (1 - target_pct)
stop_loss = entry_price * (1 + stop_loss_pct)
if ltp <= target:
trade_id += 1
trades.append({
'trade_id': trade_id,
'type': 'SELL',
'entry_time': entry_time,
'entry_price': entry_price,
'exit_time': timestamp,
'exit_price': ltp,
'exit_reason': 'TARGET',
'pnl': entry_price - ltp,
'pnl_pct': ((entry_price - ltp) / entry_price) * 100,
'duration': str(timestamp - entry_time)
})
position = None
entry_price = None
entry_time = None
elif ltp >= stop_loss:
trade_id += 1
trades.append({
'trade_id': trade_id,
'type': 'SELL',
'entry_time': entry_time,
'entry_price': entry_price,
'exit_time': timestamp,
'exit_price': ltp,
'exit_reason': 'STOPLOSS',
'pnl': entry_price - ltp,
'pnl_pct': ((entry_price - ltp) / entry_price) * 100,
'duration': str(timestamp - entry_time)
})
position = None
entry_price = None
entry_time = None
# Check for entry if not in position
if not position:
# Breakout above H1
if high > h1:
position = 'BUY'
entry_price = ltp
entry_time = timestamp
# Breakdown below L1
elif low < l1:
position = 'SELL'
entry_price = ltp
entry_time = timestamp
return trades
def create_tick_chart(df: pd.DataFrame, h1: float, l1: float, trades: List[Dict]) -> go.Figure:
"""Create detailed tick chart with trades"""
fig = go.Figure()
# Add tick line
fig.add_trace(go.Scatter(
x=df['timestamp'],
y=df['ltp'],
mode='lines',
line=dict(color='blue', width=1),
name='LTP (Tick by Tick)',
hovertemplate='Time: %{x}
Price: âš%{y:.2f}
" +
f"P&L: âš{trade['pnl']:.2f} ({trade['pnl_pct']:.2f}%)
" +
f"Reason: {trade['exit_reason']}
Breakout trigger for BUY
From: {first_minute_label}
Breakdown trigger for SELL
From: {first_minute_label}
{range_pct:.2f}% of price