Kani-18 commited on
Commit
21d0900
Β·
verified Β·
1 Parent(s): 8b66ef8

Backtesting dashboard

Browse files
Files changed (5) hide show
  1. .gitattributes +1 -0
  2. README.md +69 -19
  3. app.py +618 -0
  4. market_data.db +3 -0
  5. requirements.txt +4 -3
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ market_data.db filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,19 +1,69 @@
1
- ---
2
- title: Tick By Tick Backtesting
3
- emoji: πŸš€
4
- colorFrom: red
5
- colorTo: red
6
- sdk: docker
7
- app_port: 8501
8
- tags:
9
- - streamlit
10
- pinned: false
11
- short_description: Backtesting the first minute breakout strategy
12
- ---
13
-
14
- # Welcome to Streamlit!
15
-
16
- Edit `/src/streamlit_app.py` to customize this app to your heart's desire. :heart:
17
-
18
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
19
- forums](https://discuss.streamlit.io).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Tick Data Analysis - First Minute Breakout
3
+ emoji: πŸ“Š
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: streamlit
7
+ sdk_version: "1.31.1"
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ ---
12
+
13
+ # πŸ“Š Tick Data Analysis - First Minute Breakout Strategy
14
+
15
+ Analyze tick-by-tick market data using the First Minute Breakout trading strategy. Visualize and backtest intraday breakout strategies based on the first minute's high (H1) and low (L1) levels.
16
+
17
+ ## 🎯 Key Features
18
+
19
+ - **First Minute Breakout**: Uses H1/L1 from first minute as key trading levels
20
+ - **Automatic Signals**: BUY on breakout above H1, SELL on breakdown below L1
21
+ - **Risk Management**: Configurable target % and stop-loss %
22
+ - **Interactive Charts**: Plotly visualizations with trade markers and entry/exit points
23
+ - **Multi-Instrument**: Analyze multiple instruments from SQLite database
24
+ - **Export Options**: Download analysis as JSON or CSV
25
+
26
+ ## πŸš€ Quick Start
27
+
28
+ 1. **Upload Database**: Include `market_data.db` with tick data (schema below)
29
+ 2. **Select Instrument**: Choose from available instruments
30
+ 3. **Set Date**: Pick analysis date
31
+ 4. **Configure**: Set target % (default: 1.0%) and stop loss % (default: 0.2%)
32
+ 5. **Analyze**: Click "πŸ” Analyze Ticks" to run
33
+
34
+ ### Database Schema
35
+ ```sql
36
+ CREATE TABLE ticks (
37
+ timestamp DATETIME NOT NULL,
38
+ instrument TEXT NOT NULL,
39
+ ltp REAL NOT NULL,
40
+ high REAL,
41
+ low REAL
42
+ );
43
+ ```
44
+
45
+ ## πŸ“‹ How It Works
46
+
47
+ **Strategy Logic:**
48
+ 1. Extract H1 (high) and L1 (low) from first minute (9:15 AM or first available)
49
+ 2. Enter BUY when price breaks above H1
50
+ 3. Enter SELL when price breaks below L1
51
+ 4. Exit on target hit or stop loss hit
52
+ 5. One position at a time, no pyramiding
53
+
54
+ **Metrics:** Total trades, win rate, P&L, average P&L per trade
55
+
56
+ ## πŸ”§ Chart Elements
57
+
58
+ - **Blue Line**: Tick-by-tick LTP
59
+ - **Blue/Orange Dashed Lines**: H1/L1 breakout levels
60
+ - **Green/Red Triangles**: BUY/SELL entry points
61
+ - **X Markers**: Exit points (green=profit, red=loss)
62
+
63
+ ## ⚠️ Disclaimer
64
+
65
+ Educational purposes only. Past performance doesn't guarantee future results. Always practice proper risk management.
66
+
67
+ ---
68
+
69
+ Built with Streamlit, Pandas, and Plotly | MIT License
app.py ADDED
@@ -0,0 +1,618 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Tick Data Analysis Dashboard for First Minute Breakout Strategy
3
+ Analyzes tick-by-tick data stored in market_data.db and calculates potential trades
4
+ """
5
+
6
+ import sqlite3
7
+ import streamlit as st
8
+ import pandas as pd
9
+ import plotly.graph_objects as go
10
+ from datetime import datetime, timedelta
11
+ from typing import Dict, List, Tuple, Optional
12
+ import json
13
+
14
+ # Page configuration
15
+ st.set_page_config(
16
+ page_title="Tick Analysis - First Minute Breakout",
17
+ page_icon="πŸ“Š",
18
+ layout="wide",
19
+ initial_sidebar_state="expanded"
20
+ )
21
+
22
+ # Constants
23
+ DEFAULT_INSTRUMENT = "NSE_EQ|INE669E01016"
24
+ DB_PATH = 'market_data.db'
25
+
26
+
27
+ def get_available_instruments(db_path: str = DB_PATH) -> List[str]:
28
+ """Get list of instruments with tick data"""
29
+ try:
30
+ with sqlite3.connect(db_path) as conn:
31
+ cursor = conn.cursor()
32
+ cursor.execute("""
33
+ SELECT DISTINCT instrument, COUNT(*) as tick_count,
34
+ DATE(MIN(timestamp)) as first_date,
35
+ DATE(MAX(timestamp)) as last_date
36
+ FROM ticks
37
+ GROUP BY instrument
38
+ ORDER BY tick_count DESC
39
+ """)
40
+ results = cursor.fetchall()
41
+ return results
42
+ except Exception as e:
43
+ st.error(f"Error fetching instruments: {e}")
44
+ return []
45
+
46
+
47
+ def get_tick_data(instrument: str, date: str, db_path: str = DB_PATH) -> pd.DataFrame:
48
+ """Get all tick data for a specific instrument and date"""
49
+ try:
50
+ with sqlite3.connect(db_path) as conn:
51
+ query = """
52
+ SELECT
53
+ timestamp,
54
+ ltp,
55
+ COALESCE(high, ltp) as high,
56
+ COALESCE(low, ltp) as low,
57
+ close_price
58
+ FROM ticks
59
+ WHERE instrument = ?
60
+ AND DATE(timestamp) = ?
61
+ ORDER BY timestamp
62
+ """
63
+
64
+ df = pd.read_sql_query(query, conn, params=(instrument, date))
65
+
66
+ if not df.empty:
67
+ df['timestamp'] = pd.to_datetime(df['timestamp'])
68
+
69
+ return df
70
+ except Exception as e:
71
+ st.error(f"Error fetching tick data: {e}")
72
+ return pd.DataFrame()
73
+
74
+
75
+ def get_first_minute_data(df: pd.DataFrame) -> Tuple[Optional[float], Optional[float], str]:
76
+ """Extract first minute high and low (9:15 AM or first available minute)"""
77
+ if df.empty:
78
+ return None, None, "No data"
79
+
80
+ # Try to find 9:15 AM data first
81
+ first_minute = df[
82
+ (df['timestamp'].dt.hour == 9) &
83
+ (df['timestamp'].dt.minute == 15)
84
+ ]
85
+
86
+ if not first_minute.empty:
87
+ h1 = first_minute['high'].max()
88
+ l1 = first_minute['low'].min()
89
+ return h1, l1, "9:15 AM"
90
+
91
+ # If 9:15 AM data not found, use the first available minute of data
92
+ first_timestamp = df['timestamp'].min()
93
+ first_minute_time = first_timestamp.replace(second=0, microsecond=0)
94
+ next_minute_time = first_minute_time + timedelta(minutes=1)
95
+
96
+ first_minute = df[
97
+ (df['timestamp'] >= first_minute_time) &
98
+ (df['timestamp'] < next_minute_time)
99
+ ]
100
+
101
+ if first_minute.empty:
102
+ # Fallback: use first 60 seconds of data
103
+ first_minute = df.head(min(60, len(df)))
104
+
105
+ h1 = first_minute['high'].max()
106
+ l1 = first_minute['low'].min()
107
+ first_min_str = first_minute_time.strftime('%H:%M')
108
+
109
+ return h1, l1, f"{first_min_str} (First available minute)"
110
+
111
+
112
+ def calculate_trades(df: pd.DataFrame, h1: float, l1: float,
113
+ target_pct: float = 0.01, stop_loss_pct: float = 0.002,
114
+ first_minute_end_time: pd.Timestamp = None) -> List[Dict]:
115
+ """Calculate all trades based on first minute breakout strategy"""
116
+ trades = []
117
+ position = None
118
+ entry_price = None
119
+ entry_time = None
120
+ trade_id = 0
121
+
122
+ for idx, row in df.iterrows():
123
+ timestamp = row['timestamp']
124
+ high = row['high']
125
+ low = row['low']
126
+ ltp = row['ltp']
127
+
128
+ # Skip first minute based on actual first minute end time
129
+ if first_minute_end_time and timestamp < first_minute_end_time:
130
+ continue
131
+
132
+ # Check for exit if in position
133
+ if position:
134
+ if position == 'BUY':
135
+ target = entry_price * (1 + target_pct)
136
+ stop_loss = entry_price * (1 - stop_loss_pct)
137
+
138
+ if ltp >= target:
139
+ trade_id += 1
140
+ trades.append({
141
+ 'trade_id': trade_id,
142
+ 'type': 'BUY',
143
+ 'entry_time': entry_time,
144
+ 'entry_price': entry_price,
145
+ 'exit_time': timestamp,
146
+ 'exit_price': ltp,
147
+ 'exit_reason': 'TARGET',
148
+ 'pnl': ltp - entry_price,
149
+ 'pnl_pct': ((ltp - entry_price) / entry_price) * 100,
150
+ 'duration': str(timestamp - entry_time)
151
+ })
152
+ position = None
153
+ entry_price = None
154
+ entry_time = None
155
+
156
+ elif ltp <= stop_loss:
157
+ trade_id += 1
158
+ trades.append({
159
+ 'trade_id': trade_id,
160
+ 'type': 'BUY',
161
+ 'entry_time': entry_time,
162
+ 'entry_price': entry_price,
163
+ 'exit_time': timestamp,
164
+ 'exit_price': ltp,
165
+ 'exit_reason': 'STOPLOSS',
166
+ 'pnl': ltp - entry_price,
167
+ 'pnl_pct': ((ltp - entry_price) / entry_price) * 100,
168
+ 'duration': str(timestamp - entry_time)
169
+ })
170
+ position = None
171
+ entry_price = None
172
+ entry_time = None
173
+
174
+ elif position == 'SELL':
175
+ target = entry_price * (1 - target_pct)
176
+ stop_loss = entry_price * (1 + stop_loss_pct)
177
+
178
+ if ltp <= target:
179
+ trade_id += 1
180
+ trades.append({
181
+ 'trade_id': trade_id,
182
+ 'type': 'SELL',
183
+ 'entry_time': entry_time,
184
+ 'entry_price': entry_price,
185
+ 'exit_time': timestamp,
186
+ 'exit_price': ltp,
187
+ 'exit_reason': 'TARGET',
188
+ 'pnl': entry_price - ltp,
189
+ 'pnl_pct': ((entry_price - ltp) / entry_price) * 100,
190
+ 'duration': str(timestamp - entry_time)
191
+ })
192
+ position = None
193
+ entry_price = None
194
+ entry_time = None
195
+
196
+ elif ltp >= stop_loss:
197
+ trade_id += 1
198
+ trades.append({
199
+ 'trade_id': trade_id,
200
+ 'type': 'SELL',
201
+ 'entry_time': entry_time,
202
+ 'entry_price': entry_price,
203
+ 'exit_time': timestamp,
204
+ 'exit_price': ltp,
205
+ 'exit_reason': 'STOPLOSS',
206
+ 'pnl': entry_price - ltp,
207
+ 'pnl_pct': ((entry_price - ltp) / entry_price) * 100,
208
+ 'duration': str(timestamp - entry_time)
209
+ })
210
+ position = None
211
+ entry_price = None
212
+ entry_time = None
213
+
214
+ # Check for entry if not in position
215
+ if not position:
216
+ # Breakout above H1
217
+ if high > h1:
218
+ position = 'BUY'
219
+ entry_price = ltp
220
+ entry_time = timestamp
221
+
222
+ # Breakdown below L1
223
+ elif low < l1:
224
+ position = 'SELL'
225
+ entry_price = ltp
226
+ entry_time = timestamp
227
+
228
+ return trades
229
+
230
+
231
+ def create_tick_chart(df: pd.DataFrame, h1: float, l1: float, trades: List[Dict]) -> go.Figure:
232
+ """Create detailed tick chart with trades"""
233
+ fig = go.Figure()
234
+
235
+ # Add tick line
236
+ fig.add_trace(go.Scatter(
237
+ x=df['timestamp'],
238
+ y=df['ltp'],
239
+ mode='lines',
240
+ line=dict(color='blue', width=1),
241
+ name='LTP (Tick by Tick)',
242
+ hovertemplate='Time: %{x}<br>Price: β‚Ή%{y:.2f}<extra></extra>'
243
+ ))
244
+
245
+ # Add high/low bands
246
+ fig.add_trace(go.Scatter(
247
+ x=df['timestamp'],
248
+ y=df['high'],
249
+ mode='lines',
250
+ line=dict(color='lightgreen', width=1, dash='dot'),
251
+ name='High',
252
+ opacity=0.5
253
+ ))
254
+
255
+ fig.add_trace(go.Scatter(
256
+ x=df['timestamp'],
257
+ y=df['low'],
258
+ mode='lines',
259
+ line=dict(color='lightcoral', width=1, dash='dot'),
260
+ name='Low',
261
+ opacity=0.5
262
+ ))
263
+
264
+ # Add H1 and L1 lines
265
+ fig.add_hline(
266
+ y=h1,
267
+ line_dash="dash",
268
+ line_color="blue",
269
+ line_width=3,
270
+ annotation_text=f"H1 Breakout: β‚Ή{h1:.2f}",
271
+ annotation_position="right"
272
+ )
273
+
274
+ fig.add_hline(
275
+ y=l1,
276
+ line_dash="dash",
277
+ line_color="orange",
278
+ line_width=3,
279
+ annotation_text=f"L1 Breakdown: β‚Ή{l1:.2f}",
280
+ annotation_position="right"
281
+ )
282
+
283
+ # Add buy signals
284
+ buy_entries = [t for t in trades if t['type'] == 'BUY']
285
+ if buy_entries:
286
+ fig.add_trace(go.Scatter(
287
+ x=[t['entry_time'] for t in buy_entries],
288
+ y=[t['entry_price'] for t in buy_entries],
289
+ mode='markers+text',
290
+ marker=dict(symbol='triangle-up', size=15, color='lime',
291
+ line=dict(color='darkgreen', width=2)),
292
+ name='BUY Entry',
293
+ text=['BUY' for _ in buy_entries],
294
+ textposition="top center",
295
+ textfont=dict(size=10, color='darkgreen')
296
+ ))
297
+
298
+ # Add sell signals
299
+ sell_entries = [t for t in trades if t['type'] == 'SELL']
300
+ if sell_entries:
301
+ fig.add_trace(go.Scatter(
302
+ x=[t['entry_time'] for t in sell_entries],
303
+ y=[t['entry_price'] for t in sell_entries],
304
+ mode='markers+text',
305
+ marker=dict(symbol='triangle-down', size=15, color='red',
306
+ line=dict(color='darkred', width=2)),
307
+ name='SELL Entry',
308
+ text=['SELL' for _ in sell_entries],
309
+ textposition="bottom center",
310
+ textfont=dict(size=10, color='darkred')
311
+ ))
312
+
313
+ # Add exit points
314
+ for trade in trades:
315
+ exit_color = 'green' if trade['pnl'] > 0 else 'red'
316
+ fig.add_trace(go.Scatter(
317
+ x=[trade['exit_time']],
318
+ y=[trade['exit_price']],
319
+ mode='markers',
320
+ marker=dict(symbol='x', size=12, color=exit_color),
321
+ name=f"Exit ({trade['exit_reason']})",
322
+ showlegend=False,
323
+ hovertemplate=f"Exit: β‚Ή{trade['exit_price']:.2f}<br>" +
324
+ f"P&L: β‚Ή{trade['pnl']:.2f} ({trade['pnl_pct']:.2f}%)<br>" +
325
+ f"Reason: {trade['exit_reason']}<extra></extra>"
326
+ ))
327
+
328
+ fig.update_layout(
329
+ title='Tick-by-Tick Price Chart with First Minute Breakout Trades',
330
+ xaxis_title='Time',
331
+ yaxis_title='Price (β‚Ή)',
332
+ height=700,
333
+ hovermode='x unified',
334
+ showlegend=True
335
+ )
336
+
337
+ return fig
338
+
339
+
340
+ def main():
341
+ st.title("πŸ“Š Tick Data Analysis - First Minute Breakout Strategy")
342
+ st.markdown("Analyze tick-by-tick data and calculate potential trades using first minute breakout strategy")
343
+
344
+ # Sidebar controls
345
+ st.sidebar.header("βš™οΈ Settings")
346
+
347
+ # Get available instruments
348
+ instruments_data = get_available_instruments()
349
+
350
+ if not instruments_data:
351
+ st.error("No instruments found in database!")
352
+ st.info("Please upload a market_data.db file with tick data in the 'ticks' table.")
353
+ return
354
+
355
+ # Display instrument selection
356
+ st.sidebar.subheader("Available Instruments")
357
+ instrument_options = []
358
+ for inst, count, first_date, last_date in instruments_data:
359
+ label = f"{inst} ({count:,} ticks, {first_date} to {last_date})"
360
+ instrument_options.append((inst, label))
361
+
362
+ selected_idx = st.sidebar.selectbox(
363
+ "Select Instrument",
364
+ options=range(len(instrument_options)),
365
+ format_func=lambda x: instrument_options[x][1],
366
+ index=0
367
+ )
368
+
369
+ selected_instrument = instrument_options[selected_idx][0]
370
+
371
+ # Date selection
372
+ analysis_date = st.sidebar.date_input(
373
+ "Analysis Date",
374
+ value=datetime.today().date(),
375
+ help="Select date to analyze tick data"
376
+ )
377
+
378
+ # Strategy parameters
379
+ st.sidebar.markdown("---")
380
+ st.sidebar.subheader("πŸ“ˆ Strategy Parameters")
381
+ target_pct = st.sidebar.number_input(
382
+ "Target %",
383
+ min_value=0.1,
384
+ max_value=10.0,
385
+ value=1.0,
386
+ step=0.1,
387
+ help="Profit target as percentage"
388
+ ) / 100
389
+
390
+ stop_loss_pct = st.sidebar.number_input(
391
+ "Stop Loss %",
392
+ min_value=0.05,
393
+ max_value=5.0,
394
+ value=0.2,
395
+ step=0.05,
396
+ help="Stop loss as percentage"
397
+ ) / 100
398
+
399
+ # Analyze button
400
+ if st.sidebar.button("πŸ” Analyze Ticks", type="primary"):
401
+ st.session_state.analyze_requested = True
402
+
403
+ # Auto-run analysis on first load
404
+ if 'first_load' not in st.session_state:
405
+ st.session_state.first_load = True
406
+ st.session_state.analyze_requested = True
407
+
408
+ # Main analysis
409
+ if st.session_state.get('analyze_requested', False):
410
+ with st.spinner(f"Loading tick data for {selected_instrument} on {analysis_date}..."):
411
+ df = get_tick_data(selected_instrument, analysis_date.strftime('%Y-%m-%d'))
412
+
413
+ if df.empty:
414
+ st.warning(f"No tick data found for {selected_instrument} on {analysis_date}")
415
+ return
416
+
417
+ # Display basic statistics
418
+ st.header("πŸ“‹ Tick Data Summary")
419
+
420
+ col1, col2, col3, col4, col5 = st.columns(5)
421
+
422
+ with col1:
423
+ st.metric("Total Ticks", f"{len(df):,}")
424
+
425
+ with col2:
426
+ st.metric("First Tick", df['timestamp'].min().strftime('%H:%M:%S'))
427
+
428
+ with col3:
429
+ st.metric("Last Tick", df['timestamp'].max().strftime('%H:%M:%S'))
430
+
431
+ with col4:
432
+ st.metric("Day High", f"β‚Ή{df['high'].max():.2f}")
433
+
434
+ with col5:
435
+ st.metric("Day Low", f"β‚Ή{df['low'].min():.2f}")
436
+
437
+ # Get first minute data
438
+ h1, l1, first_minute_label = get_first_minute_data(df)
439
+
440
+ if h1 is None or l1 is None:
441
+ st.error("Could not extract first minute data from the available ticks!")
442
+ return
443
+
444
+ # Calculate first minute end time
445
+ first_timestamp = df['timestamp'].min()
446
+ first_minute_end = first_timestamp.replace(second=0, microsecond=0) + timedelta(minutes=1)
447
+
448
+ # Show info about first minute
449
+ if "9:15" not in first_minute_label:
450
+ st.info(f"ℹ️ Note: Data doesn't start at market open (9:15 AM). Using {first_minute_label} as the reference period for H1/L1 calculation.")
451
+
452
+ # Display first minute levels
453
+ st.markdown("---")
454
+ st.header("🎯 First Minute Breakout Levels")
455
+
456
+ col1, col2, col3 = st.columns(3)
457
+
458
+ with col1:
459
+ st.markdown(f"""
460
+ <div style='background-color: #e3f2fd; padding: 20px; border-radius: 10px;
461
+ border-left: 5px solid #2196F3; text-align: center;'>
462
+ <h3 style='color: #1976D2; margin: 0;'>πŸ”΅ H1 (High)</h3>
463
+ <h2 style='color: #1976D2; margin: 10px 0;'>β‚Ή{h1:.2f}</h2>
464
+ <p style='margin: 0;'>Breakout trigger for BUY</p>
465
+ <p style='margin: 5px 0; font-size: 0.85em;'>From: {first_minute_label}</p>
466
+ </div>
467
+ """, unsafe_allow_html=True)
468
+
469
+ with col2:
470
+ st.markdown(f"""
471
+ <div style='background-color: #fff3e0; padding: 20px; border-radius: 10px;
472
+ border-left: 5px solid #FF9800; text-align: center;'>
473
+ <h3 style='color: #F57C00; margin: 0;'>🟠 L1 (Low)</h3>
474
+ <h2 style='color: #F57C00; margin: 10px 0;'>β‚Ή{l1:.2f}</h2>
475
+ <p style='margin: 0;'>Breakdown trigger for SELL</p>
476
+ <p style='margin: 5px 0; font-size: 0.85em;'>From: {first_minute_label}</p>
477
+ </div>
478
+ """, unsafe_allow_html=True)
479
+
480
+ with col3:
481
+ range_val = h1 - l1
482
+ range_pct = (range_val / l1) * 100
483
+ st.markdown(f"""
484
+ <div style='background-color: #f3e5f5; padding: 20px; border-radius: 10px;
485
+ border-left: 5px solid #9C27B0; text-align: center;'>
486
+ <h3 style='color: #7B1FA2; margin: 0;'>πŸ“ Range</h3>
487
+ <h2 style='color: #7B1FA2; margin: 10px 0;'>β‚Ή{range_val:.2f}</h2>
488
+ <p style='margin: 0;'>{range_pct:.2f}% of price</p>
489
+ </div>
490
+ """, unsafe_allow_html=True)
491
+
492
+ # Calculate trades
493
+ st.markdown("---")
494
+ st.header("πŸ’Ό Trade Analysis")
495
+
496
+ with st.spinner("Calculating potential trades..."):
497
+ trades = calculate_trades(df, h1, l1, target_pct, stop_loss_pct, first_minute_end)
498
+
499
+ if not trades:
500
+ st.info("No trades would be placed based on the strategy parameters.")
501
+ else:
502
+ # Trade statistics
503
+ total_trades = len(trades)
504
+ winning_trades = len([t for t in trades if t['pnl'] > 0])
505
+ losing_trades = len([t for t in trades if t['pnl'] <= 0])
506
+ total_pnl = sum(t['pnl'] for t in trades)
507
+ avg_pnl = total_pnl / total_trades
508
+ win_rate = (winning_trades / total_trades) * 100 if total_trades > 0 else 0
509
+
510
+ # Display trade metrics
511
+ col1, col2, col3, col4, col5 = st.columns(5)
512
+
513
+ with col1:
514
+ st.metric("Total Trades", total_trades)
515
+
516
+ with col2:
517
+ st.metric("Winning Trades", winning_trades, delta=f"{win_rate:.1f}%")
518
+
519
+ with col3:
520
+ st.metric("Losing Trades", losing_trades)
521
+
522
+ with col4:
523
+ pnl_color = "🟒" if total_pnl > 0 else "πŸ”΄"
524
+ st.metric("Total P&L", f"{pnl_color} β‚Ή{total_pnl:.2f}")
525
+
526
+ with col5:
527
+ avg_color = "🟒" if avg_pnl > 0 else "πŸ”΄"
528
+ st.metric("Avg P&L/Trade", f"{avg_color} β‚Ή{avg_pnl:.2f}")
529
+
530
+ # Display chart
531
+ st.markdown("---")
532
+ st.subheader("πŸ“ˆ Tick Chart with Trade Signals")
533
+ fig = create_tick_chart(df, h1, l1, trades)
534
+ st.plotly_chart(fig, use_container_width=True)
535
+
536
+ # Trade details table
537
+ st.markdown("---")
538
+ st.subheader("πŸ“Š Trade Details")
539
+
540
+ trades_df = pd.DataFrame(trades)
541
+
542
+ # Format for display
543
+ display_df = trades_df.copy()
544
+ display_df['entry_time'] = pd.to_datetime(display_df['entry_time']).dt.strftime('%H:%M:%S')
545
+ display_df['exit_time'] = pd.to_datetime(display_df['exit_time']).dt.strftime('%H:%M:%S')
546
+ display_df['entry_price'] = display_df['entry_price'].apply(lambda x: f"β‚Ή{x:.2f}")
547
+ display_df['exit_price'] = display_df['exit_price'].apply(lambda x: f"β‚Ή{x:.2f}")
548
+ display_df['pnl'] = display_df['pnl'].apply(lambda x: f"β‚Ή{x:.2f}")
549
+ display_df['pnl_pct'] = display_df['pnl_pct'].apply(lambda x: f"{x:.2f}%")
550
+
551
+ # Rename columns
552
+ display_df = display_df.rename(columns={
553
+ 'trade_id': 'Trade #',
554
+ 'type': 'Type',
555
+ 'entry_time': 'Entry Time',
556
+ 'entry_price': 'Entry Price',
557
+ 'exit_time': 'Exit Time',
558
+ 'exit_price': 'Exit Price',
559
+ 'exit_reason': 'Exit Reason',
560
+ 'pnl': 'P&L',
561
+ 'pnl_pct': 'P&L %',
562
+ 'duration': 'Duration'
563
+ })
564
+
565
+ st.dataframe(display_df, use_container_width=True, height=400)
566
+
567
+ # Export trades
568
+ st.markdown("---")
569
+ st.subheader("πŸ’Ύ Export Analysis")
570
+
571
+ col1, col2 = st.columns(2)
572
+
573
+ with col1:
574
+ # Export as JSON
575
+ export_data = {
576
+ 'instrument': selected_instrument,
577
+ 'date': analysis_date.strftime('%Y-%m-%d'),
578
+ 'first_minute': {
579
+ 'high': h1,
580
+ 'low': l1,
581
+ 'range': h1 - l1
582
+ },
583
+ 'strategy_params': {
584
+ 'target_pct': target_pct * 100,
585
+ 'stop_loss_pct': stop_loss_pct * 100
586
+ },
587
+ 'summary': {
588
+ 'total_trades': total_trades,
589
+ 'winning_trades': winning_trades,
590
+ 'losing_trades': losing_trades,
591
+ 'win_rate': win_rate,
592
+ 'total_pnl': total_pnl,
593
+ 'avg_pnl': avg_pnl
594
+ },
595
+ 'trades': trades
596
+ }
597
+
598
+ json_str = json.dumps(export_data, indent=2, default=str)
599
+ st.download_button(
600
+ label="πŸ“₯ Download as JSON",
601
+ data=json_str,
602
+ file_name=f"tick_analysis_{selected_instrument.replace('|', '_')}_{analysis_date.strftime('%Y%m%d')}.json",
603
+ mime="application/json"
604
+ )
605
+
606
+ with col2:
607
+ # Export as CSV
608
+ csv_data = trades_df.to_csv(index=False)
609
+ st.download_button(
610
+ label="πŸ“₯ Download Trades as CSV",
611
+ data=csv_data,
612
+ file_name=f"trades_{selected_instrument.replace('|', '_')}_{analysis_date.strftime('%Y%m%d')}.csv",
613
+ mime="text/csv"
614
+ )
615
+
616
+
617
+ if __name__ == "__main__":
618
+ main()
market_data.db ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:296abd499e0eb3a145fd4203599f78b2946dc8a802d51a8950c73a158d7ad630
3
+ size 41594880
requirements.txt CHANGED
@@ -1,3 +1,4 @@
1
- altair
2
- pandas
3
- streamlit
 
 
1
+ streamlit==1.31.1
2
+ pandas==2.2.0
3
+ plotly==5.18.0
4
+ python-dateutil==2.8.2