File size: 20,049 Bytes
8e0b458
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
# utils/google_sheets.py

import gspread
from google.oauth2.service_account import Credentials
import pandas as pd
from datetime import datetime
import streamlit as st
import json

class TradingGoogleSheets:
    def __init__(self, credentials_json_path=None, credentials_dict=None, sheet_name="Trading_Log"):
        """
        Initialize Google Sheets connection
        
        Parameters:
            credentials_json_path (str): Path to service account JSON file
            credentials_dict (dict): Service account credentials as dictionary (for Streamlit secrets)
            sheet_name (str): Name of the Google Sheet to create/use
        """
        self.sheet_name = sheet_name
        self.scope = [
            "https://spreadsheets.google.com/feeds",
            "https://www.googleapis.com/auth/drive"
        ]
        
        # Initialize credentials
        if credentials_dict:
            # For Streamlit deployment with secrets
            self.creds = Credentials.from_service_account_info(credentials_dict, scopes=self.scope)
        elif credentials_json_path:
            # For local development with JSON file
            self.creds = Credentials.from_service_account_file(credentials_json_path, scopes=self.scope)
        else:
            raise ValueError("Either credentials_json_path or credentials_dict must be provided")
        
        self.client = gspread.authorize(self.creds)
        self.spreadsheet = None
        
    def create_or_get_spreadsheet(self):
        """Create a new spreadsheet or get existing one"""
        try:
            # Try to open existing spreadsheet
            self.spreadsheet = self.client.open(self.sheet_name)
            print(f"Opened existing spreadsheet: {self.sheet_name}")
        except gspread.SpreadsheetNotFound:
            # Create new spreadsheet
            self.spreadsheet = self.client.create(self.sheet_name)
            print(f"Created new spreadsheet: {self.sheet_name}")
            
            # Share with your email (replace with your email)
            # self.spreadsheet.share('your-email@gmail.com', perm_type='user', role='writer')
        
        # Create the three required worksheets
        self.setup_worksheets()
        return self.spreadsheet
    
    def setup_worksheets(self):
        """Setup the required worksheets with headers"""
        worksheets_config = {
            "Trade_Log": [
                "Timestamp", "Stock", "Strategy", "Signal_Type", "Price", "RSI", 
                "MA_Short", "MA_Long", "Entry_Date", "Exit_Date", "Entry_Price", 
                "Exit_Price", "Shares", "Profit_Loss", "Return_Pct", "Exit_Reason", "Duration_Days"
            ],
            "Summary_PL": [
                "Date", "Stock", "Strategy", "Total_Trades", "Winning_Trades", 
                "Losing_Trades", "Win_Rate", "Total_PL", "Best_Trade", "Worst_Trade", 
                "Avg_Win", "Avg_Loss", "Profit_Factor", "Max_Drawdown", "Sharpe_Ratio", 
                "Final_Portfolio_Value", "Total_Return"
            ],
            "Performance_Metrics": [
                "Date", "Stock", "Strategy", "Initial_Capital", "Final_Value", 
                "Total_Return", "Buy_Hold_Return", "Alpha", "Volatility", 
                "Sharpe_Ratio", "Max_Drawdown", "Total_Trades", "Win_Rate", 
                "Avg_Trade_Duration", "Transaction_Cost", "Notes"
            ]
        }
        
        existing_sheets = [ws.title for ws in self.spreadsheet.worksheets()]
        
        for sheet_name, headers in worksheets_config.items():
            if sheet_name not in existing_sheets:
                # Create new worksheet
                worksheet = self.spreadsheet.add_worksheet(title=sheet_name, rows=1000, cols=len(headers))
                worksheet.append_row(headers)
                print(f"Created worksheet: {sheet_name}")
            else:
                print(f"Worksheet already exists: {sheet_name}")
    
    def log_trade_signals(self, df, strategy_name, stock_symbol):
        """Log all trade signals to Trade_Log worksheet"""
        try:
            worksheet = self.spreadsheet.worksheet("Trade_Log")
            
            # Get signals from dataframe
            signal_col = f'{strategy_name}_Signal'
            signals_df = df[df[signal_col] != 0].copy()
            
            if signals_df.empty:
                print("No signals to log")
                return
            
            # Prepare data for logging
            current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            
            rows_to_add = []
            for idx, row in signals_df.iterrows():
                signal_type = "BUY" if row[signal_col] == 1 else "SELL"
                
                row_data = [
                    current_time,  # Timestamp
                    stock_symbol,  # Stock
                    strategy_name,  # Strategy
                    signal_type,  # Signal_Type
                    round(row['Close'], 2),  # Price
                    round(row['RSI'], 2),  # RSI
                    round(row[f'{strategy_name}20'], 2),  # MA_Short
                    round(row[f'{strategy_name}50'], 2),  # MA_Long
                    "",  # Entry_Date (filled when trade completes)
                    "",  # Exit_Date
                    "",  # Entry_Price
                    "",  # Exit_Price
                    "",  # Shares
                    "",  # Profit_Loss
                    "",  # Return_Pct
                    "",  # Exit_Reason
                    ""   # Duration_Days
                ]
                rows_to_add.append(row_data)
            
            # Add all rows at once
            if rows_to_add:
                worksheet.append_rows(rows_to_add)
                print(f"Logged {len(rows_to_add)} signals to Trade_Log")
                
        except Exception as e:
            print(f"Error logging trade signals: {e}")
    
    def log_completed_trades(self, trades_df, strategy_name, stock_symbol):
        """Log completed trades to Trade_Log worksheet"""
        try:
            worksheet = self.spreadsheet.worksheet("Trade_Log")
            
            if trades_df.empty:
                print("No completed trades to log")
                return
            
            current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            
            rows_to_add = []
            for _, trade in trades_df.iterrows():
                row_data = [
                    current_time,  # Timestamp
                    stock_symbol,  # Stock
                    strategy_name,  # Strategy
                    "COMPLETED_TRADE",  # Signal_Type
                    round(trade['exit_price'], 2),  # Price (exit price)
                    "",  # RSI
                    "",  # MA_Short
                    "",  # MA_Long
                    trade['entry_date'],  # Entry_Date
                    trade['exit_date'],  # Exit_Date
                    round(trade['entry_price'], 2),  # Entry_Price
                    round(trade['exit_price'], 2),  # Exit_Price
                    round(trade['shares'], 4),  # Shares
                    round(trade['profit_loss'], 2),  # Profit_Loss
                    round(trade['return_pct'] * 100, 2),  # Return_Pct
                    trade['exit_reason'],  # Exit_Reason
                    (pd.to_datetime(trade['exit_date']) - pd.to_datetime(trade['entry_date'])).days  # Duration_Days
                ]
                rows_to_add.append(row_data)
            
            if rows_to_add:
                worksheet.append_rows(rows_to_add)
                print(f"Logged {len(rows_to_add)} completed trades to Trade_Log")
                
        except Exception as e:
            print(f"Error logging completed trades: {e}")
    
    def log_summary_pl(self, metrics, strategy_name, stock_symbol, trades_df):
        """Log summary P&L to Summary_PL worksheet"""
        try:
            worksheet = self.spreadsheet.worksheet("Summary_PL")
            
            current_date = datetime.now().strftime("%Y-%m-%d")
            
            # Calculate additional metrics
            total_trades = len(trades_df) if not trades_df.empty else 0
            winning_trades = len(trades_df[trades_df['return_pct'] > 0]) if not trades_df.empty else 0
            losing_trades = len(trades_df[trades_df['return_pct'] < 0]) if not trades_df.empty else 0
            
            # Extract numeric values from metrics
            win_rate = float(metrics['Win Rate'].strip('%')) if metrics['Win Rate'] != '0.00%' else 0
            total_pl = float(metrics['Final Portfolio Value'].replace('β‚Ή', '').replace(',', '')) - 100000  # Assuming 100k initial
            best_trade = trades_df['return_pct'].max() * 100 if not trades_df.empty else 0
            worst_trade = trades_df['return_pct'].min() * 100 if not trades_df.empty else 0
            avg_win = float(metrics['Average Win'].strip('%')) if metrics['Average Win'] != '0.00%' else 0
            avg_loss = float(metrics['Average Loss'].strip('%')) if metrics['Average Loss'] != '0.00%' else 0
            profit_factor = float(metrics['Profit Factor']) if metrics['Profit Factor'] != '0.00' else 0
            max_drawdown = float(metrics['Maximum Drawdown'].strip('%'))
            sharpe_ratio = float(metrics['Sharpe Ratio'])
            final_value = float(metrics['Final Portfolio Value'].replace('β‚Ή', '').replace(',', ''))
            total_return = float(metrics['Total Return'].strip('%'))
            
            row_data = [
                current_date,  # Date
                stock_symbol,  # Stock
                strategy_name,  # Strategy
                total_trades,  # Total_Trades
                winning_trades,  # Winning_Trades
                losing_trades,  # Losing_Trades
                round(win_rate, 2),  # Win_Rate
                round(total_pl, 2),  # Total_PL
                round(best_trade, 2),  # Best_Trade
                round(worst_trade, 2),  # Worst_Trade
                round(avg_win, 2),  # Avg_Win
                round(avg_loss, 2),  # Avg_Loss
                round(profit_factor, 2),  # Profit_Factor
                round(max_drawdown, 2),  # Max_Drawdown
                round(sharpe_ratio, 2),  # Sharpe_Ratio
                round(final_value, 2),  # Final_Portfolio_Value
                round(total_return, 2)  # Total_Return
            ]
            
            worksheet.append_row(row_data)
            print("Logged summary P&L to Summary_PL")
            
        except Exception as e:
            print(f"Error logging summary P&L: {e}")
    
    def log_performance_metrics(self, metrics, strategy_name, stock_symbol, initial_capital, 
                              transaction_cost, notes=""):
        """Log performance metrics to Performance_Metrics worksheet"""
        try:
            worksheet = self.spreadsheet.worksheet("Performance_Metrics")
            
            current_date = datetime.now().strftime("%Y-%m-%d")
            
            # Extract and clean numeric values
            final_value = float(metrics['Final Portfolio Value'].replace('β‚Ή', '').replace(',', ''))
            total_return = float(metrics['Total Return'].strip('%'))
            buy_hold_return = float(metrics['Buy & Hold Return'].strip('%'))
            alpha = total_return - buy_hold_return
            volatility = float(metrics['Volatility (Annual)'].strip('%'))
            sharpe_ratio = float(metrics['Sharpe Ratio'])
            max_drawdown = float(metrics['Maximum Drawdown'].strip('%'))
            total_trades = metrics['Total Trades']
            win_rate = float(metrics['Win Rate'].strip('%'))
            
            # Calculate average trade duration (you might need to pass this from trades_df)
            avg_trade_duration = 0  # You can calculate this from trades_df if needed
            
            row_data = [
                current_date,  # Date
                stock_symbol,  # Stock
                strategy_name,  # Strategy
                initial_capital,  # Initial_Capital
                round(final_value, 2),  # Final_Value
                round(total_return, 2),  # Total_Return
                round(buy_hold_return, 2),  # Buy_Hold_Return
                round(alpha, 2),  # Alpha
                round(volatility, 2),  # Volatility
                round(sharpe_ratio, 2),  # Sharpe_Ratio
                round(max_drawdown, 2),  # Max_Drawdown
                total_trades,  # Total_Trades
                round(win_rate, 2),  # Win_Rate
                avg_trade_duration,  # Avg_Trade_Duration
                transaction_cost * 100,  # Transaction_Cost (as percentage)
                notes  # Notes
            ]
            
            worksheet.append_row(row_data)
            print("Logged performance metrics to Performance_Metrics")
            
        except Exception as e:
            print(f"Error logging performance metrics: {e}")
    
    def get_sheet_url(self):
        """Get the URL of the Google Sheet"""
        if self.spreadsheet:
            return self.spreadsheet.url
        return None
    
    def clear_worksheet(self, worksheet_name):
        """Clear all data from a worksheet (except headers)"""
        try:
            worksheet = self.spreadsheet.worksheet(worksheet_name)
            worksheet.clear()
            # Re-add headers based on the worksheet
            if worksheet_name == "Trade_Log":
                headers = ["Timestamp", "Stock", "Strategy", "Signal_Type", "Price", "RSI", 
                          "MA_Short", "MA_Long", "Entry_Date", "Exit_Date", "Entry_Price", 
                          "Exit_Price", "Shares", "Profit_Loss", "Return_Pct", "Exit_Reason", "Duration_Days"]
            elif worksheet_name == "Summary_PL":
                headers = ["Date", "Stock", "Strategy", "Total_Trades", "Winning_Trades", 
                          "Losing_Trades", "Win_Rate", "Total_PL", "Best_Trade", "Worst_Trade", 
                          "Avg_Win", "Avg_Loss", "Profit_Factor", "Max_Drawdown", "Sharpe_Ratio", 
                          "Final_Portfolio_Value", "Total_Return"]
            elif worksheet_name == "Performance_Metrics":
                headers = ["Date", "Stock", "Strategy", "Initial_Capital", "Final_Value", 
                          "Total_Return", "Buy_Hold_Return", "Alpha", "Volatility", 
                          "Sharpe_Ratio", "Max_Drawdown", "Total_Trades", "Win_Rate", 
                          "Avg_Trade_Duration", "Transaction_Cost", "Notes"]
            
            worksheet.append_row(headers)
            print(f"Cleared and reset worksheet: {worksheet_name}")
            
        except Exception as e:
            print(f"Error clearing worksheet {worksheet_name}: {e}")


# Integration function for your Streamlit app
def log_to_google_sheets(df, results, metrics, strategy_name, stock_symbol, 
                        initial_cash, transaction_cost, credentials_dict=None, 
                        credentials_json_path=None):
    """
    Main function to log all data to Google Sheets
    
    Parameters:
        df: DataFrame with signals and indicators
        results: Backtest results DataFrame
        metrics: Performance metrics dictionary
        strategy_name: Name of the strategy (SMA/EMA)
        stock_symbol: Stock symbol
        initial_cash: Initial capital
        transaction_cost: Transaction cost percentage
        credentials_dict: Google service account credentials (for Streamlit)
        credentials_json_path: Path to JSON credentials file (for local)
    """
    try:
        # Initialize Google Sheets connection
        sheets_logger = TradingGoogleSheets(
            credentials_dict=credentials_dict,
            credentials_json_path=credentials_json_path,
            sheet_name=f"Trading_Log_{stock_symbol}"
        )
        
        # Create or get spreadsheet
        spreadsheet = sheets_logger.create_or_get_spreadsheet()
        
        # Log trade signals
        sheets_logger.log_trade_signals(df, strategy_name, stock_symbol)
        
        # Log completed trades if available
        if not metrics['Trades DataFrame'].empty:
            sheets_logger.log_completed_trades(metrics['Trades DataFrame'], strategy_name, stock_symbol)
        
        # Log summary P&L
        trades_df = metrics['Trades DataFrame'] if not metrics['Trades DataFrame'].empty else pd.DataFrame()
        sheets_logger.log_summary_pl(metrics, strategy_name, stock_symbol, trades_df)
        
        # Log performance metrics
        sheets_logger.log_performance_metrics(
            metrics, strategy_name, stock_symbol, initial_cash, transaction_cost
        )
        
        return sheets_logger.get_sheet_url()
        
    except Exception as e:
        print(f"Error in log_to_google_sheets: {e}")
        return None


# # Streamlit integration function
# def add_google_sheets_to_streamlit(df, results, metrics, strategy_name, stock_symbol, 
#                                  initial_cash, transaction_cost):
#     """Add Google Sheets logging functionality to your Streamlit app"""
    
#     st.subheader("πŸ“Š Google Sheets Integration")
    
#     # Check if credentials are configured
#     if 'google_sheets_credentials' in st.secrets:
#         col1, col2 = st.columns([2, 1])
        
#         with col1:
#             if st.button("πŸ“€ Log to Google Sheets", type="primary"):
#                 with st.spinner("Logging data to Google Sheets..."):
#                     sheet_url = log_to_google_sheets(
#                         df=df,
#                         results=results, 
#                         metrics=metrics,
#                         strategy_name=strategy_name,
#                         stock_symbol=stock_symbol,
#                         initial_cash=initial_cash,
#                         transaction_cost=transaction_cost,
#                         credentials_dict=dict(st.secrets.google_sheets_credentials)
#                     )
                    
#                     if sheet_url:
#                         st.success("βœ… Data logged successfully!")
#                         st.markdown(f"πŸ”— [View Google Sheet]({sheet_url})")
#                     else:
#                         st.error("❌ Failed to log data to Google Sheets")
        
#         with col2:
#             st.info("πŸ’‘ **Auto-logging enabled**\n\nData will be saved to:\n- Trade signals\n- P&L summary\n- Performance metrics")
    
#     else:
#         st.warning("⚠️ Google Sheets credentials not configured. Add your service account credentials to Streamlit secrets to enable logging.")
        
#         with st.expander("πŸ“‹ Setup Instructions"):
#             st.markdown("""
#             **To enable Google Sheets integration:**
            
#             1. **Create a Google Cloud Project**
#             2. **Enable Google Sheets & Drive APIs**
#             3. **Create a Service Account**
#             4. **Download the JSON credentials**
#             5. **Add credentials to Streamlit secrets**
            
#             **In your `.streamlit/secrets.toml` file:**
#             ```toml
#             [google_sheets_credentials]
#             type = "service_account"
#             project_id = "your-project-id"
#             private_key_id = "your-private-key-id"
#             private_key = "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
#             client_email = "your-service-account@your-project.iam.gserviceaccount.com"
#             client_id = "your-client-id"
#             auth_uri = "https://accounts.google.com/o/oauth2/auth"
#             token_uri = "https://oauth2.googleapis.com/token"
#             auth_provider_x509_cert_url = "https://www.googleapis.com/oauth2/v1/certs"
#             client_x509_cert_url = "https://www.googleapis.com/robot/v1/metadata/x509/your-service-account%40your-project.iam.gserviceaccount.com"
#             universe_domain = "googleapis.com"
#             ```
#             """)