Badumetsibb commited on
Commit
8c024cb
·
verified ·
1 Parent(s): efbbe86

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +138 -0
app.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # fusion_analysis_v2.py
2
+ import pandas as pd
3
+ import firebase_admin
4
+ from firebase_admin import credentials, db
5
+ import os
6
+ import json
7
+
8
+ # --- CONFIGURATION ---
9
+ # IMPORTANT: This script uses the same environment variables as your agents.
10
+ # Ensure FIRESTORE_SA_KEY and FIREBASE_DB_URL are set.
11
+ SA_KEY_JSON = os.environ.get('FIRESTORE_SA_KEY')
12
+ DB_URL = os.environ.get('FIREBASE_DB_URL')
13
+ AGENT_SIGNALS_REF = 'signals_v2'
14
+ NEWS_SENTIMENT_REF = 'news_sentiment'
15
+
16
+ def initialize_firebase():
17
+ """Initializes the Firebase app if not already done."""
18
+ if not firebase_admin._apps:
19
+ try:
20
+ cred_dict = json.loads(SA_KEY_JSON)
21
+ cred = credentials.Certificate(cred_dict)
22
+ firebase_admin.initialize_app(cred, {'databaseURL': DB_URL})
23
+ print("✅ Firebase connection successful.")
24
+ return True
25
+ except Exception as e:
26
+ print(f"❌ CRITICAL ERROR - Firebase initialization failed: {e}")
27
+ return False
28
+ return True
29
+
30
+ def fetch_data_from_firebase(ref_name):
31
+ """Fetches and processes data from a specified Firebase database reference."""
32
+ try:
33
+ ref = db.reference(ref_name)
34
+ data = ref.get()
35
+ if not data:
36
+ print(f"⚠️ No data found in '{ref_name}'.")
37
+ return pd.DataFrame()
38
+
39
+ df = pd.DataFrame.from_dict(data, orient='index')
40
+
41
+ # Standardize the timestamp column for merging
42
+ if 'timestamp_entry' in df.columns: # For agent signals
43
+ df['timestamp'] = pd.to_datetime(df['timestamp_entry'], utc=True)
44
+ elif 'timestamp_published' in df.columns: # For news sentiment
45
+ df['timestamp'] = pd.to_datetime(df['timestamp_published'], utc=True)
46
+ else:
47
+ print(f"❌ Timestamp column not found in '{ref_name}'.")
48
+ return pd.DataFrame()
49
+
50
+ df = df.set_index('timestamp').sort_index()
51
+ print(f"✅ Fetched {len(df)} records from '{ref_name}'.")
52
+ return df
53
+ except Exception as e:
54
+ print(f"❌ Error fetching data from '{ref_name}': {e}")
55
+ return pd.DataFrame()
56
+
57
+ def run_fusion_analysis():
58
+ """Main function to fetch, fuse, and analyze the data."""
59
+ if not initialize_firebase():
60
+ return
61
+
62
+ # 1. Fetch both datasets
63
+ agent_df = fetch_data_from_firebase(AGENT_SIGNALS_REF)
64
+ news_df = fetch_data_from_firebase(NEWS_SENTIMENT_REF)
65
+
66
+ if agent_df.empty or news_df.empty:
67
+ print("\nCould not proceed with fusion. One or both data sources are empty.")
68
+ return
69
+
70
+ # 2. Fuse the datasets using time
71
+ print("\n--- Fusing Agent Decisions with News Sentiment ---")
72
+
73
+ # This is the core logic: For each agent signal, find the most recent news
74
+ # that was published right before it (within a 30-minute tolerance).
75
+ # This checks if "news was available" and relevant at the time of the decision.
76
+ fused_df = pd.merge_asof(
77
+ left=agent_df,
78
+ right=news_df[['sentiment', 'confidence_score', 'headline']],
79
+ left_index=True,
80
+ right_index=True,
81
+ direction='backward',
82
+ tolerance=pd.Timedelta(minutes=30)
83
+ )
84
+
85
+ # Drop signals where no recent news was available
86
+ fused_df.dropna(subset=['sentiment'], inplace=True)
87
+
88
+ if fused_df.empty:
89
+ print("\nNo agent signals were found to have occurred within 30 minutes of a news headline. Analysis cannot proceed.")
90
+ return
91
+
92
+ print(f"✅ Fusion complete. Matched {len(fused_df)} agent signals with recent news headlines.")
93
+
94
+ # --- 🧠 THE INTELLIGENCE LABORATORY 🧠 ---
95
+ print("\n--- Advanced Analysis Results ---")
96
+
97
+ # Analysis 1: How does news sentiment correlate with the agent's actions?
98
+ print("\n[Analysis 1: Agent Action vs. News Sentiment]")
99
+ action_sentiment_crosstab = pd.crosstab(fused_df['action'], fused_df['sentiment'])
100
+ print(action_sentiment_crosstab)
101
+ print("Insight: This shows if the agent tends to BUY during 'Bullish' news and SELL during 'Bearish' news.")
102
+
103
+ # Analysis 2: Let's bring in the agent's own understanding of the market regime.
104
+ print("\n[Analysis 2: Action vs. Sentiment, Segmented by Market Regime]")
105
+ regime_crosstab = pd.crosstab(
106
+ [fused_df['market_regime'], fused_df['action']],
107
+ fused_df['sentiment']
108
+ )
109
+ print(regime_crosstab)
110
+ print("Insight: This is powerful. It might reveal if the agent only trusts 'Bullish' news when it already believes the market is 'TRENDING'.")
111
+
112
+ # Analysis 3: Does trading in alignment with high-confidence news lead to better outcomes?
113
+ print("\n[Analysis 3: Trade PnL vs. News Sentiment Alignment]")
114
+ # Filter for signals that have already been evaluated (have a PnL value)
115
+ evaluated_trades = fused_df.dropna(subset=['pnl'])
116
+ evaluated_trades['pnl'] = pd.to_numeric(evaluated_trades['pnl'])
117
+
118
+ # Create an 'alignment' score
119
+ def calculate_alignment(row):
120
+ if (row['action'] == 'BUY' and row['sentiment'] == 'Bullish') or \
121
+ (row['action'] == 'SELL' and row['sentiment'] == 'Bearish'):
122
+ return 'Aligned'
123
+ elif (row['action'] == 'BUY' and row['sentiment'] == 'Bearish') or \
124
+ (row['action'] == 'SELL' and row['sentiment'] == 'Bullish'):
125
+ return 'Misaligned'
126
+ return 'Neutral'
127
+
128
+ if not evaluated_trades.empty:
129
+ evaluated_trades['alignment'] = evaluated_trades.apply(calculate_alignment, axis=1)
130
+ pnl_by_alignment = evaluated_trades.groupby('alignment')['pnl'].mean()
131
+ print(pnl_by_alignment)
132
+ print("Insight: This is the ultimate test. It shows if trades that 'agree' with the news are, on average, more profitable.")
133
+ else:
134
+ print("No evaluated trades with PnL found to analyze performance.")
135
+
136
+
137
+ if __name__ == "__main__":
138
+ run_fusion_analysis()