File size: 11,125 Bytes
eb27803
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
import sys
import json
import logging
import traceback
from datetime import datetime
from typing import Dict, Any

from dotenv import load_dotenv
load_dotenv()

from src.crypto_analysis.crew import BitcoinAnalysisCrew

# Set up logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler(os.path.join('logs', f'bitcoin_analysis_{datetime.now().strftime("%Y%m%d")}.log'))
    ]
)
logger = logging.getLogger("bitcoin_analysis")

# Ensure logs directory exists
os.makedirs('logs', exist_ok=True)

def run() -> Dict[str, Any]:
    """
    Run the Bitcoin analysis crew and return the results
    
    Returns:
        Dictionary with analysis results
    """
    logger.info("Starting Bitcoin Price Sentiment Analysis")
    print("## Starting Bitcoin Price Sentiment Analysis")
    print("## " + "=" * 50)
    
    try:
        # Create and run the Bitcoin analysis crew
        bitcoin_crew = BitcoinAnalysisCrew()
        result = bitcoin_crew.run_analysis()
        
        # Check for errors
        if "error" in result:
            logger.error(f"Error in Bitcoin analysis: {result['error']}")
            print(f"## ERROR: {result['error']}")
        
        return result
    except Exception as e:
        error_traceback = traceback.format_exc()
        logger.error(f"Unexpected error in run(): {str(e)}\n{error_traceback}")
        return {
            "error": str(e),
            "traceback": error_traceback,
            "signal": "hold",  # Default to hold on error
            "confidence": 0,
            "portfolio_allocation": 0,
            "reasoning": f"Unexpected error: {str(e)}"
        }

def save_result(result: Dict[str, Any], output_dir: str = "results") -> str:
    """
    Save analysis result to a JSON file
    
    Args:
        result: The analysis result to save
        output_dir: Directory to save results in
        
    Returns:
        Path to the saved file
    """
    try:
        # Create output directory if it doesn't exist
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)
        
        # Generate filename with timestamp
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"bitcoin_analysis_{timestamp}.json"
        filepath = os.path.join(output_dir, filename)
        
        # Save result as JSON
        with open(filepath, "w") as f:
            json.dump(result, f, indent=2)
        
        logger.info(f"Saved analysis result to {filepath}")
        return filepath
    except Exception as e:
        error_traceback = traceback.format_exc()
        logger.error(f"Error saving result: {str(e)}\n{error_traceback}")
        
        # Try to save to a fallback location
        try:
            fallback_path = os.path.join(".", f"bitcoin_analysis_error_{timestamp}.json")
            with open(fallback_path, "w") as f:
                json.dump(result, f, indent=2)
            logger.info(f"Saved analysis result to fallback location {fallback_path}")
            return fallback_path
        except:
            logger.error("Failed to save result even to fallback location")
            return "ERROR_SAVING_RESULT"

def format_output(result: Dict[str, Any]) -> str:
    """
    Format the analysis result as a readable string
    
    Args:
        result: The analysis result to format
        
    Returns:
        Formatted string representation
    """
    signal = result.get("signal", "hold").upper()
    confidence = result.get("confidence", 0)
    allocation = result.get("portfolio_allocation", 0)
    reasoning = result.get("reasoning", "No reasoning provided")
    
    # Check for tool errors
    tool_errors = result.get("tool_error_summary", "")
    error_message = ""
    if tool_errors:
        error_message = f"## TOOL ERRORS DETECTED\n{tool_errors}\n\n"
    
    # Check for data reliability information
    data_reliability = result.get("data_reliability", "")
    reliability_message = ""
    if data_reliability:
        reliability_message = f"## DATA RELIABILITY\n{data_reliability}\n\n"
    
    output = [
        "## BITCOIN TRADING RECOMMENDATION",
        "## " + "=" * 50,
        f"SIGNAL:       {signal}",
        f"CONFIDENCE:   {confidence}%",
        f"ALLOCATION:   {allocation}% of portfolio",
        ""
    ]
    
    if error_message:
        output.append(error_message)
    
    if reliability_message:
        output.append(reliability_message)
    
    output.extend([
        "## REASONING",
        reasoning
    ])
    
    # Add market outlook if available
    market_outlook = result.get("market_outlook", "")
    if market_outlook:
        output.extend([
            "",
            "## MARKET OUTLOOK",
            market_outlook
        ])
    
    # Add risk assessment if available
    risk_assessment = result.get("risk_assessment", "")
    if risk_assessment:
        output.extend([
            "",
            "## RISK ASSESSMENT",
            risk_assessment
        ])
    
    # Add trade execution details if available
    order_execution = result.get("order_execution_text", "")
    if order_execution:
        output.extend([
            "",
            "## TRADE EXECUTION",
            order_execution
        ])
    
    return "\n".join(output)

def monitor_mode():
    """
    Run Bitcoin analysis in monitoring mode (continuous analysis at intervals)
    """
    from time import sleep
    
    logger.info("Starting Bitcoin Price Sentiment Analysis in Monitoring Mode")
    print("## Starting Bitcoin Price Sentiment Analysis in Monitoring Mode")
    print("## Analysis will run once every 4 hours")
    print("## Press Ctrl+C to exit")
    print("## " + "=" * 50)
    
    interval_seconds = 4 * 60 * 60  # 4 hours
    try:
        run_count = 0
        error_count = 0
        max_consecutive_errors = 3
        
        while True:
            # Run analysis
            start_time = datetime.now()
            print(f"\n## Running analysis at {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
            logger.info(f"Running analysis #{run_count + 1} at {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
            
            try:
                result = run()
                run_count += 1
                
                # Check for errors
                if "error" in result:
                    error_count += 1
                    print(f"## WARNING: Analysis completed with errors ({error_count}/{max_consecutive_errors})")
                    logger.warning(f"Analysis completed with errors: {result['error']}")
                    
                    # If we have too many consecutive errors, increase sleep time to back off
                    if error_count >= max_consecutive_errors:
                        print(f"## Too many consecutive errors. Backing off...")
                        logger.warning(f"Too many consecutive errors ({error_count}). Backing off...")
                        interval_seconds = min(interval_seconds * 2, 12 * 60 * 60)  # Max 12 hours
                else:
                    # Reset error count if successful
                    error_count = 0
                    # Reset interval if it was increased
                    interval_seconds = 4 * 60 * 60
                
                filepath = save_result(result)
                
                print(format_output(result))
                print(f"\n## Results saved to {filepath}")
                
                # Calculate sleep time (accounting for analysis duration)
                elapsed = (datetime.now() - start_time).total_seconds()
                sleep_time = max(interval_seconds - elapsed, 0)
                
                print(f"\n## Next analysis in {sleep_time/60/60:.2f} hours")
                logger.info(f"Next analysis in {sleep_time/60/60:.2f} hours")
                sleep(sleep_time)
                
            except Exception as e:
                error_traceback = traceback.format_exc()
                error_count += 1
                logger.error(f"Error in monitoring loop: {str(e)}\n{error_traceback}")
                print(f"## ERROR in monitoring loop: {str(e)}")
                
                # Save error information
                error_result = {
                    "error": str(e),
                    "traceback": error_traceback,
                    "signal": "hold",
                    "confidence": 0,
                    "portfolio_allocation": 0,
                    "reasoning": f"Error in monitoring loop: {str(e)}"
                }
                save_result(error_result)
                
                # If we have too many consecutive errors, increase sleep time
                if error_count >= max_consecutive_errors:
                    print(f"## Too many consecutive errors ({error_count}/{max_consecutive_errors}). Backing off...")
                    logger.warning(f"Too many consecutive errors ({error_count}). Backing off...")
                    interval_seconds = min(interval_seconds * 2, 12 * 60 * 60)  # Max 12 hours
                
                # Sleep a shorter time before retrying
                sleep_time = min(interval_seconds / 4, 60 * 60)  # Min of 1/4 regular interval or 1 hour
                print(f"## Retrying in {sleep_time/60:.0f} minutes...")
                logger.info(f"Retrying in {sleep_time/60:.0f} minutes")
                sleep(sleep_time)
            
    except KeyboardInterrupt:
        logger.info("Monitoring stopped by user")
        print("\n## Monitoring stopped by user")
        return

def train():
    """
    Train the crew for a given number of iterations
    """
    try:
        iterations = int(sys.argv[2]) if len(sys.argv) > 2 else 1
        logger.info(f"Training Bitcoin Analysis Crew for {iterations} iterations")
        print(f"## Training Bitcoin Analysis Crew for {iterations} iterations")
        
        bitcoin_crew = BitcoinAnalysisCrew()
        bitcoin_crew.crew().train(n_iterations=iterations)
        
    except Exception as e:
        error_traceback = traceback.format_exc()
        logger.error(f"Error training crew: {str(e)}\n{error_traceback}")
        print(f"## Error training crew: {str(e)}")

if __name__ == "__main__":
    try:
        if len(sys.argv) > 1 and sys.argv[1] == "monitor":
            # Run in monitoring mode
            monitor_mode()
        elif len(sys.argv) > 1 and sys.argv[1] == "train":
            # Run in training mode
            train()
        else:
            # Run once
            result = run()
            filepath = save_result(result)
            
            print("\n\n" + "=" * 60)
            print(format_output(result))
            print("\n" + "=" * 60)
            print(f"\nFull analysis result saved to: {filepath}")
    except Exception as e:
        error_traceback = traceback.format_exc()
        logger.error(f"Unhandled exception in main: {str(e)}\n{error_traceback}")
        print(f"## CRITICAL ERROR: {str(e)}")
        print("See logs for details.")