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.
- cotmetrics/CotDatabase.py +220 -0
- cotmetrics/CotIndexer.py +1707 -0
- cotmetrics/__init__.py +12 -0
- cotmetrics/conditions.py +395 -0
- cotmetrics/config.py +53 -0
- cotmetrics/constants.py +527 -0
- cotmetrics/database.py +9 -0
- cotmetrics/etl.py +149 -0
- cotmetrics/indexer.py +73 -0
- cotmetrics/indicators.py +294 -0
- cotmetrics/market_data.py +28 -0
- cotmetrics/models.py +174 -0
- cotmetrics/movers.py +287 -0
- cotmetrics/options_data.py +376 -0
- cotmetrics/params.yaml +41 -0
- cotmetrics/pipelines/01_etl_downloader.py +56 -0
- cotmetrics/pipelines/__init__.py +0 -0
- cotmetrics/pipelines/etl_scheduler.py +235 -0
- cotmetrics/report_styles.py +225 -0
- cotmetrics/reports.py +289 -0
- cotmetrics/signals.py +1091 -0
- cotmetrics/symbol_code_map.py +613 -0
- cotmetrics/synthesis.py +278 -0
- cotmetrics/utils.py +260 -0
- cotmetrics-0.1.0.dist-info/METADATA +75 -0
- cotmetrics-0.1.0.dist-info/RECORD +29 -0
- cotmetrics-0.1.0.dist-info/WHEEL +5 -0
- cotmetrics-0.1.0.dist-info/licenses/LICENSE +21 -0
- cotmetrics-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -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
|
+
|