jetpackjules commited on
Commit
404b51f
Β·
1 Parent(s): d79188e

Add Premium Trading Dashboard with enhanced Reddit search and sentiment analysis

Browse files
Files changed (4) hide show
  1. README.md +68 -7
  2. app.py +0 -0
  3. requirements.txt +12 -0
  4. vm_data_server.py +332 -0
README.md CHANGED
@@ -1,14 +1,75 @@
1
  ---
2
- title: Portfolio
3
- emoji: 🐨
4
- colorFrom: red
5
- colorTo: pink
6
  sdk: gradio
7
- sdk_version: 5.38.2
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
11
- short_description: v2
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Premium Trading Dashboard
3
+ emoji: πŸ“ˆ
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: gradio
7
+ sdk_version: 5.35.0
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
11
+ short_description: IPO Trading Dashboard with Sentiment Analysis
12
  ---
13
 
14
+ # πŸš€ Premium Trading Dashboard
15
+
16
+ A comprehensive real-time trading dashboard with automated IPO discovery, sentiment analysis, and backtesting capabilities.
17
+
18
+ ## ✨ Features
19
+
20
+ ### πŸ“Š Portfolio Overview
21
+ - Real-time account monitoring (portfolio value, buying power, cash)
22
+ - Interactive portfolio performance charts
23
+ - Day change tracking with visual indicators
24
+
25
+ ### πŸ” IPO Discoveries
26
+ - Automated IPO detection and classification
27
+ - Investment decision analytics
28
+ - Recent discoveries with detailed breakdowns
29
+
30
+ ### πŸ’° Investment Performance
31
+ - Complete P&L analysis for all IPO investments
32
+ - **NEW: Integrated sentiment analysis backtesting**
33
+ - Advanced trading statistics and metrics
34
+ - Risk analysis and performance breakdowns
35
+
36
+ ### 🧠 Sentiment Analysis Engine
37
+
38
+ The dashboard implements a sophisticated sentiment analysis system:
39
+
40
+ - **Data Sources**: Reddit (including WallStreetBets), Google News
41
+ - **Analysis Window**: 12 hours before each actual investment
42
+ - **Sentiment Engines**: VADER + TextBlob with engagement weighting
43
+ - **Target**: First-hour stock performance prediction
44
+ - **Validation**: Compares predictions vs actual market performance
45
+
46
+ ### πŸ’» VM Terminal
47
+ - Remote command execution on trading VM
48
+ - Real-time log monitoring
49
+ - File system navigation and analysis
50
+
51
+ ### πŸ“‹ System Logs
52
+ - Parsed trading bot activity logs
53
+ - Raw cron job outputs
54
+ - Color-coded error tracking
55
+
56
+ ## πŸ”§ Technical Stack
57
+
58
+ - **Frontend**: Gradio with custom CSS styling
59
+ - **Backend**: Flask API integration with VM
60
+ - **Trading API**: Alpaca Markets (Paper Trading)
61
+ - **Data Sources**: Reddit API, Google News RSS, Yahoo Finance
62
+ - **Sentiment Analysis**: VADER, TextBlob
63
+ - **Charts**: Plotly for interactive visualizations
64
+
65
+ ## πŸš€ Recent Updates
66
+
67
+ - βœ… Added IPO Sentiment Analysis Backtesting
68
+ - βœ… WallStreetBets integration for Reddit sentiment
69
+ - βœ… Historical performance validation
70
+ - βœ… Multi-source sentiment aggregation
71
+ - βœ… Enhanced logging and error handling
72
+
73
+ **Built with ❀️ for automated IPO trading and sentiment analysis**
74
+
75
+ *Fresh deployment with enhanced Reddit search and comprehensive logging*
app.py ADDED
The diff for this file is too large to render. See raw diff
 
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Trading Dashboard Requirements - Full Featured
2
+ gradio>=5.0.0
3
+ plotly>=6.0.0
4
+ pandas>=2.0.0
5
+ numpy>=1.20.0
6
+ alpaca-py>=0.8.0
7
+ requests>=2.28.0
8
+ flask>=2.0.0
9
+ flask-cors>=4.0.0
10
+ textblob>=0.17.1
11
+ vaderSentiment>=3.3.2
12
+ yfinance>=0.2.18
vm_data_server.py ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ VM Data Server
4
+ Exposes local trading bot data via API for dashboard consumption
5
+ Runs on the VM alongside your trading bot
6
+ """
7
+
8
+ import os
9
+ import json
10
+ import pandas as pd
11
+ from datetime import datetime, timedelta
12
+ from flask import Flask, jsonify, request
13
+ from flask_cors import CORS
14
+ import logging
15
+
16
+ app = Flask(__name__)
17
+ CORS(app) # Allow dashboard to connect from anywhere
18
+
19
+ # Configure logging
20
+ logging.basicConfig(level=logging.INFO)
21
+ logger = logging.getLogger(__name__)
22
+
23
+ # File paths
24
+ PORTFOLIO_FILE = 'portfolio.txt'
25
+ NEW_TICKERS_LOG_FILE = 'new_tickers_log.csv'
26
+ SCRIPT_LOG_FILE = 'script.log'
27
+ BUY_QUEUE_FILE = 'buy_queue.json'
28
+ CURRENT_TICKERS_FILE = 'current_tickers.txt'
29
+
30
+ def load_portfolio_data():
31
+ """Load portfolio CSV data"""
32
+ try:
33
+ if os.path.exists(PORTFOLIO_FILE):
34
+ df = pd.read_csv(PORTFOLIO_FILE)
35
+ return df.to_dict('records')
36
+ return []
37
+ except Exception as e:
38
+ logger.error(f"Error loading portfolio: {e}")
39
+ return []
40
+
41
+ def load_new_tickers_with_decisions():
42
+ """Load IPO discoveries with investment decisions"""
43
+ try:
44
+ if not os.path.exists(NEW_TICKERS_LOG_FILE):
45
+ return []
46
+
47
+ df = pd.read_csv(NEW_TICKERS_LOG_FILE)
48
+ portfolio_data = load_portfolio_data()
49
+
50
+ # Get symbols we actually invested in
51
+ invested_symbols = set()
52
+ for trade in portfolio_data:
53
+ invested_symbols.add(trade.get('symbol', ''))
54
+
55
+ # Add investment decision to each IPO
56
+ enriched_ipos = []
57
+ for _, row in df.iterrows():
58
+ symbol = row.get('Symbol', '')
59
+ security_type = row.get('Security_Type', '')
60
+
61
+ # Determine investment status
62
+ if symbol in invested_symbols:
63
+ investment_status = 'INVESTED'
64
+ status_color = 'success'
65
+ status_emoji = '🟒'
66
+ elif security_type == 'CS':
67
+ investment_status = 'ELIGIBLE_NOT_INVESTED'
68
+ status_color = 'warning'
69
+ status_emoji = '🟑'
70
+ elif security_type in ['SP', 'WARRANT', 'UNIT']:
71
+ investment_status = 'WRONG_TYPE'
72
+ status_color = 'neutral'
73
+ status_emoji = 'βšͺ'
74
+ else:
75
+ investment_status = 'UNKNOWN'
76
+ status_color = 'error'
77
+ status_emoji = 'πŸ”΄'
78
+
79
+ enriched_ipos.append({
80
+ 'symbol': symbol,
81
+ 'security_type': security_type,
82
+ 'trading_price': row.get('Trading_Price', 'N/A'),
83
+ 'detected_at': row.get('Detected_At', 'N/A'),
84
+ 'investment_status': investment_status,
85
+ 'status_color': status_color,
86
+ 'status_emoji': status_emoji
87
+ })
88
+
89
+ # Sort by detection date (newest first)
90
+ enriched_ipos.sort(key=lambda x: x['detected_at'], reverse=True)
91
+
92
+ return enriched_ipos
93
+ except Exception as e:
94
+ logger.error(f"Error loading IPO data: {e}")
95
+ return []
96
+
97
+ def load_script_logs(lines=100):
98
+ """Load recent script logs"""
99
+ try:
100
+ if not os.path.exists(SCRIPT_LOG_FILE):
101
+ return []
102
+
103
+ with open(SCRIPT_LOG_FILE, 'r') as f:
104
+ all_lines = f.readlines()
105
+
106
+ # Get recent lines
107
+ recent_lines = all_lines[-lines:] if len(all_lines) > lines else all_lines
108
+
109
+ # Parse log lines
110
+ logs = []
111
+ for line in recent_lines:
112
+ line = line.strip()
113
+ if line:
114
+ # Try to parse timestamp and level
115
+ parts = line.split(' - ', 2)
116
+ if len(parts) >= 3:
117
+ timestamp = parts[0]
118
+ level = parts[1]
119
+ message = parts[2]
120
+
121
+ # Determine log type for color coding
122
+ if 'ERROR' in level:
123
+ log_type = 'error'
124
+ emoji = 'πŸ”΄'
125
+ elif 'WARNING' in level or 'WARN' in level:
126
+ log_type = 'warning'
127
+ emoji = '🟑'
128
+ elif 'Buy order placed' in message or 'Sold' in message:
129
+ log_type = 'trade'
130
+ emoji = 'πŸ’°'
131
+ elif 'Found' in message and 'new ticker' in message:
132
+ log_type = 'discovery'
133
+ emoji = 'πŸ”'
134
+ elif 'INFO' in level:
135
+ log_type = 'info'
136
+ emoji = 'πŸ”΅'
137
+ else:
138
+ log_type = 'default'
139
+ emoji = 'βšͺ'
140
+
141
+ logs.append({
142
+ 'timestamp': timestamp,
143
+ 'level': level,
144
+ 'message': message,
145
+ 'log_type': log_type,
146
+ 'emoji': emoji,
147
+ 'full_line': line
148
+ })
149
+ else:
150
+ # Fallback for unparseable lines
151
+ logs.append({
152
+ 'timestamp': 'N/A',
153
+ 'level': 'RAW',
154
+ 'message': line,
155
+ 'log_type': 'default',
156
+ 'emoji': 'βšͺ',
157
+ 'full_line': line
158
+ })
159
+
160
+ return logs
161
+ except Exception as e:
162
+ logger.error(f"Error loading logs: {e}")
163
+ return []
164
+
165
+ def load_buy_queue():
166
+ """Load current buy queue"""
167
+ try:
168
+ if os.path.exists(BUY_QUEUE_FILE):
169
+ with open(BUY_QUEUE_FILE, 'r') as f:
170
+ return json.load(f)
171
+ return []
172
+ except Exception as e:
173
+ logger.error(f"Error loading buy queue: {e}")
174
+ return []
175
+
176
+ def get_system_stats():
177
+ """Get system statistics"""
178
+ try:
179
+ portfolio_data = load_portfolio_data()
180
+ ipo_data = load_new_tickers_with_decisions()
181
+
182
+ # Calculate stats
183
+ total_ipos_detected = len(ipo_data)
184
+ ipos_invested = len([ipo for ipo in ipo_data if ipo['investment_status'] == 'INVESTED'])
185
+ current_positions = len(portfolio_data)
186
+
187
+ # Get detection stats by type
188
+ cs_stocks = len([ipo for ipo in ipo_data if ipo['security_type'] == 'CS'])
189
+ other_types = total_ipos_detected - cs_stocks
190
+
191
+ return {
192
+ 'total_ipos_detected': total_ipos_detected,
193
+ 'ipos_invested': ipos_invested,
194
+ 'current_positions': current_positions,
195
+ 'cs_stocks_detected': cs_stocks,
196
+ 'other_types_detected': other_types,
197
+ 'investment_rate': round((ipos_invested / cs_stocks * 100) if cs_stocks > 0 else 0, 1),
198
+ 'last_updated': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
199
+ }
200
+ except Exception as e:
201
+ logger.error(f"Error calculating stats: {e}")
202
+ return {}
203
+
204
+ # API Endpoints
205
+
206
+ @app.route('/health')
207
+ def health_check():
208
+ """Health check endpoint"""
209
+ return jsonify({'status': 'healthy', 'timestamp': datetime.now().isoformat()})
210
+
211
+ @app.route('/api/portfolio')
212
+ def get_portfolio():
213
+ """Get portfolio data"""
214
+ return jsonify(load_portfolio_data())
215
+
216
+ @app.route('/api/ipos')
217
+ def get_ipos():
218
+ """Get IPO discoveries with investment decisions"""
219
+ limit = request.args.get('limit', 50, type=int)
220
+ ipos = load_new_tickers_with_decisions()
221
+ return jsonify(ipos[:limit])
222
+
223
+ @app.route('/api/logs')
224
+ def get_logs():
225
+ """Get script logs"""
226
+ lines = request.args.get('lines', 100, type=int)
227
+ logs = load_script_logs(lines)
228
+ return jsonify(logs)
229
+
230
+ @app.route('/api/buy_queue')
231
+ def get_buy_queue():
232
+ """Get current buy queue"""
233
+ return jsonify(load_buy_queue())
234
+
235
+ @app.route('/api/stats')
236
+ def get_stats():
237
+ """Get system statistics"""
238
+ return jsonify(get_system_stats())
239
+
240
+ @app.route('/api/logs/raw')
241
+ def get_raw_logs():
242
+ """Get raw log file content"""
243
+ lines = request.args.get('lines', 200, type=int)
244
+ try:
245
+ if not os.path.exists(SCRIPT_LOG_FILE):
246
+ return jsonify({'content': 'No log file found'})
247
+
248
+ with open(SCRIPT_LOG_FILE, 'r') as f:
249
+ all_lines = f.readlines()
250
+
251
+ recent_lines = all_lines[-lines:] if len(all_lines) > lines else all_lines
252
+ content = ''.join(recent_lines)
253
+
254
+ return jsonify({
255
+ 'content': content,
256
+ 'total_lines': len(all_lines),
257
+ 'showing_lines': len(recent_lines)
258
+ })
259
+ except Exception as e:
260
+ return jsonify({'error': str(e), 'content': ''})
261
+
262
+ @app.route('/api/execute', methods=['POST'])
263
+ def execute_command():
264
+ """Execute a command on the VM"""
265
+ try:
266
+ data = request.get_json()
267
+ command = data.get('command', '').strip()
268
+
269
+ if not command:
270
+ return jsonify({'error': 'No command provided', 'output': '', 'exit_code': 1})
271
+
272
+ # Security: only allow safe commands
273
+ dangerous_commands = ['rm', 'sudo', 'passwd', 'shutdown', 'reboot', 'mkfs', 'fdisk', 'dd']
274
+ if any(cmd in command.lower() for cmd in dangerous_commands):
275
+ return jsonify({
276
+ 'error': 'Command not allowed for security reasons',
277
+ 'output': f"β›” Command '{command}' contains restricted operations",
278
+ 'exit_code': 1
279
+ })
280
+
281
+ # Execute command with timeout
282
+ import subprocess
283
+ import os
284
+
285
+ # Use current working directory (where the server was started)
286
+ cwd = os.getcwd()
287
+
288
+ result = subprocess.run(
289
+ command,
290
+ shell=True,
291
+ capture_output=True,
292
+ text=True,
293
+ timeout=30, # 30 second timeout
294
+ cwd=cwd # Run in current directory
295
+ )
296
+
297
+ return jsonify({
298
+ 'output': result.stdout + result.stderr,
299
+ 'exit_code': result.returncode,
300
+ 'command': command
301
+ })
302
+
303
+ except subprocess.TimeoutExpired:
304
+ return jsonify({
305
+ 'error': 'Command timed out',
306
+ 'output': '⏰ Command execution timed out (30s limit)',
307
+ 'exit_code': 124
308
+ })
309
+ except Exception as e:
310
+ return jsonify({
311
+ 'error': str(e),
312
+ 'output': f'❌ Error executing command: {str(e)}',
313
+ 'exit_code': 1
314
+ })
315
+
316
+ if __name__ == '__main__':
317
+ print("πŸš€ Starting VM Data Server...")
318
+ print("πŸ“‘ This exposes your trading bot data via API")
319
+ print("🌐 Dashboard can now access:")
320
+ print(" β€’ IPO discoveries with investment decisions")
321
+ print(" β€’ Raw cron logs with color coding")
322
+ print(" β€’ Portfolio data from VM files")
323
+ print(" β€’ Buy queue and system stats")
324
+ print(" β€’ Remote command execution")
325
+ print("-" * 50)
326
+
327
+ # Run on all interfaces so dashboard can connect
328
+ app.run(
329
+ host='0.0.0.0', # Allow external connections
330
+ port=8090, # Different from dashboard port
331
+ debug=False
332
+ )