cotmetrics 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,220 @@
1
+ import os
2
+ import sqlite3
3
+ from datetime import datetime, timezone
4
+ from zoneinfo import ZoneInfo
5
+
6
+ import pandas as pd
7
+
8
+ import cotmetrics.utils as utils
9
+
10
+
11
+ class CotDatabase:
12
+ """Class to manage COT data"""
13
+ def __init__(self, db_name=None):
14
+ if db_name is None:
15
+ # Resolve absolutely to cot-analyzer/data/cot_data.db
16
+ base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
17
+ self.db_name = os.path.join(base_dir, 'data', 'cot_data.db')
18
+ else:
19
+ self.db_name = db_name
20
+
21
+ # Ensure directories exist
22
+ os.makedirs(os.path.dirname(self.db_name), exist_ok=True)
23
+
24
+ self.setup_database()
25
+
26
+ def setup_database(self):
27
+ """Create the database and the necessary table if it doesn't exist."""
28
+ conn = sqlite3.connect(self.db_name)
29
+ c = conn.cursor()
30
+ c.execute('''
31
+ CREATE TABLE IF NOT EXISTS zip_files (
32
+ year INTEGER PRIMARY KEY,
33
+ last_modified TEXT
34
+ )
35
+ ''')
36
+
37
+ # Add visitor logs table
38
+ c.execute('''
39
+ CREATE TABLE IF NOT EXISTS visitor_logs (
40
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
41
+ timestamp TEXT,
42
+ ip_address TEXT,
43
+ path TEXT,
44
+ user_agent TEXT,
45
+ city TEXT,
46
+ country TEXT
47
+ )
48
+ ''')
49
+
50
+ # Add ml_predictions_v2 table
51
+ c.execute('''
52
+ CREATE TABLE IF NOT EXISTS ml_predictions_v2 (
53
+ symbol TEXT,
54
+ report_date TEXT,
55
+ prob_success REAL,
56
+ meta_side INTEGER,
57
+ expectancy REAL,
58
+ atr_mult_tp REAL,
59
+ atr_mult_sl REAL,
60
+ updated_at TEXT,
61
+ PRIMARY KEY (symbol, report_date)
62
+ )
63
+ ''')
64
+
65
+ # Safely add new columns to existing table
66
+ for col, col_type in [('expectancy', 'REAL'), ('atr_mult_tp', 'REAL'), ('atr_mult_sl', 'REAL')]:
67
+ try:
68
+ c.execute(f'ALTER TABLE ml_predictions_v2 ADD COLUMN {col} {col_type}')
69
+ except sqlite3.OperationalError:
70
+ pass
71
+ conn.commit()
72
+ conn.close()
73
+
74
+ def log_visit(self, ip, path, ua, city="Unknown", country="Unknown"):
75
+ """Records a new visitor event to the database."""
76
+ conn = sqlite3.connect(self.db_name)
77
+ c = conn.cursor()
78
+ now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
79
+ c.execute('''
80
+ INSERT INTO visitor_logs (timestamp, ip_address, path, user_agent, city, country)
81
+ VALUES (?, ?, ?, ?, ?, ?)
82
+ ''', (now, ip, path, ua, city, country))
83
+ conn.commit()
84
+ conn.close()
85
+
86
+ def get_visitor_stats(self):
87
+ """Retrieves recent logs for the admin dashboard."""
88
+ conn = sqlite3.connect(self.db_name)
89
+ df = pd.read_sql_query("SELECT * FROM visitor_logs ORDER BY id DESC LIMIT 500", conn)
90
+ conn.close()
91
+ return df
92
+
93
+ def update_zip_file(self, year, last_modified):
94
+ """Update the last modified date of the zip file in the database."""
95
+ conn = sqlite3.connect(self.db_name)
96
+ c = conn.cursor()
97
+ c.execute('''
98
+ INSERT INTO zip_files (year, last_modified) VALUES (?, ?)
99
+ ON CONFLICT(year) DO UPDATE SET last_modified = ?
100
+ ''', (year, last_modified, last_modified))
101
+ conn.commit()
102
+ conn.close()
103
+ utils.get_cot_logger().warning(f"Updated latest zipfile time in DB {year} {last_modified}")
104
+
105
+ def get_zipfile_last_modified_time(self, year):
106
+ conn = sqlite3.connect(self.db_name)
107
+ c = conn.cursor()
108
+ c.execute('SELECT last_modified FROM zip_files WHERE year = ?', (year,))
109
+ row = c.fetchone()
110
+ conn.close()
111
+
112
+ result = None
113
+ if row:
114
+ try:
115
+ result = datetime.strptime(row[0], '%a, %d %b %Y %H:%M:%S %Z')
116
+ except Exception as e:
117
+ utils.get_cot_logger().debug(f"First attempt exception in date format {result}. {e}")
118
+ try:
119
+ utils.get_cot_logger().debug(f"2nd attempt on {row[0]}")
120
+ result = datetime.strptime(row[0], '%Y-%m-%d %H:%M:%S')
121
+ utils.get_cot_logger().debug(f"2nd time worked {result}")
122
+ except Exception as e:
123
+ utils.get_cot_logger().error(f"2nd time exception in date format {result}. {e}")
124
+ return None
125
+ return result
126
+
127
+ def latest_update_timestamp(self):
128
+ tz_aware = "Unknown"
129
+ year = str(datetime.now().year)
130
+ result = self.get_zipfile_last_modified_time(year)
131
+ if result is not None:
132
+ tz_aware = result.replace(tzinfo=timezone.utc)
133
+ tz_aware = tz_aware.astimezone(ZoneInfo("America/New_York"))
134
+ tz_aware = tz_aware.strftime("%Y-%m-%d %H:%M:%S %Z")
135
+ if tz_aware is None:
136
+ tz_aware = "Unknown"
137
+ return tz_aware
138
+
139
+ def save_predictions(self, symbol, report_date, prob_success, meta_side, expectancy=None, atr_mult_tp=None, atr_mult_sl=None):
140
+ conn = sqlite3.connect(self.db_name)
141
+ c = conn.cursor()
142
+ now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
143
+ c.execute('''
144
+ INSERT INTO ml_predictions_v2 (
145
+ symbol, report_date, prob_success, meta_side, expectancy, atr_mult_tp, atr_mult_sl, updated_at
146
+ )
147
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
148
+ ON CONFLICT(symbol, report_date) DO UPDATE SET
149
+ prob_success = excluded.prob_success,
150
+ meta_side = excluded.meta_side,
151
+ expectancy = excluded.expectancy,
152
+ atr_mult_tp = excluded.atr_mult_tp,
153
+ atr_mult_sl = excluded.atr_mult_sl,
154
+ updated_at = excluded.updated_at
155
+ ''', (symbol, report_date, prob_success, meta_side, expectancy, atr_mult_tp, atr_mult_sl, now))
156
+ conn.commit()
157
+ conn.close()
158
+
159
+ def get_latest_prediction(self, symbol):
160
+ conn = sqlite3.connect(self.db_name)
161
+ c = conn.cursor()
162
+ c.execute('''
163
+ SELECT report_date, prob_success, meta_side, updated_at
164
+ FROM ml_predictions_v2
165
+ WHERE symbol = ?
166
+ ORDER BY report_date DESC LIMIT 1
167
+ ''', (symbol,))
168
+ row = c.fetchone()
169
+ conn.close()
170
+ if row:
171
+ return {
172
+ "report_date": row[0],
173
+ "prob_success": row[1],
174
+ "meta_side": row[2],
175
+ "updated_at": row[3]
176
+ }
177
+ return None
178
+
179
+ def get_prediction(self, symbol, report_date):
180
+ conn = sqlite3.connect(self.db_name)
181
+ c = conn.cursor()
182
+ c.execute('''
183
+ SELECT report_date, prob_success, meta_side, updated_at
184
+ FROM ml_predictions_v2
185
+ WHERE symbol = ? AND report_date = ?
186
+ ''', (symbol, report_date))
187
+ row = c.fetchone()
188
+ conn.close()
189
+ if row:
190
+ return {
191
+ "report_date": row[0],
192
+ "prob_success": row[1],
193
+ "meta_side": row[2],
194
+ "updated_at": row[3]
195
+ }
196
+ return None
197
+
198
+ def get_all_predictions(self, symbol):
199
+ conn = sqlite3.connect(self.db_name)
200
+ c = conn.cursor()
201
+ c.execute('''
202
+ SELECT report_date, prob_success, meta_side, updated_at
203
+ FROM ml_predictions_v2
204
+ WHERE symbol = ?
205
+ ORDER BY report_date ASC
206
+ ''', (symbol,))
207
+ rows = c.fetchall()
208
+ conn.close()
209
+
210
+ predictions = []
211
+ for row in rows:
212
+ predictions.append({
213
+ "report_date": row[0],
214
+ "prob_success": row[1],
215
+ "meta_side": row[2],
216
+ "updated_at": row[3]
217
+ })
218
+ return predictions
219
+
220
+