strange_crypto2.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import requests
  2. import pandas as pd
  3. import numpy as np
  4. import time
  5. import schedule
  6. import smtplib
  7. from email.mime.text import MIMEText
  8. from email.mime.multipart import MIMEMultipart
  9. from datetime import datetime
  10. import threading
  11. import os
  12. from flask import Flask, render_template_string, jsonify
  13. # Configuration
  14. CRYPTO_LIST = ['solana', 'dogecoin', 'ethereum', 'ripple', 'tron'] # CoinGecko IDs
  15. CRYPTO_LABELS = {
  16. 'solana': 'SOL',
  17. 'dogecoin': 'DOGE',
  18. 'ethereum': 'ETH',
  19. 'ripple': 'XRP',
  20. 'tron': 'TRX'
  21. }
  22. CURRENCY = 'usd'
  23. API_URL = 'https://api.coingecko.com/api/v3/simple/price'
  24. CHECK_INTERVAL_SECONDS = 60 # Check every 60 seconds
  25. Z_SCORE_THRESHOLD = 3.0
  26. HISTORY_WINDOW = 100
  27. EMAIL_FROM = 'vortify-lc@algometic.com'
  28. EMAIL_TO = 'larry1chan@qq.com'
  29. EMAIL_PASSWORD = 'g33kPoppy!'
  30. SMTP_SERVER = 'hwsmtp.exmail.qq.com'
  31. SMTP_PORT = 465
  32. DATA_FILE = "crypto_history.csv"
  33. # In-memory storage for historical prices and z-scores per crypto
  34. price_history = {crypto: [] for crypto in CRYPTO_LIST}
  35. zscore_history = {crypto: [] for crypto in CRYPTO_LIST}
  36. timestamp_history = []
  37. def fetch_prices():
  38. """Fetch current prices for all cryptos from CoinGecko API."""
  39. try:
  40. ids = ','.join(CRYPTO_LIST)
  41. params = {'ids': ids, 'vs_currencies': CURRENCY}
  42. response = requests.get(API_URL, params=params)
  43. response.raise_for_status()
  44. data = response.json()
  45. return {crypto: data.get(crypto, {}).get(CURRENCY) for crypto in CRYPTO_LIST}
  46. except Exception as e:
  47. print(f"Error fetching prices: {e}")
  48. return {}
  49. def detect_anomaly(crypto, current_price):
  50. """Detect if the current price is an anomaly based on Z-score."""
  51. history = price_history[crypto]
  52. if len(history) < 2:
  53. return False, 0.0
  54. df = pd.DataFrame(history, columns=['price'])
  55. mean = df['price'].mean()
  56. std_dev = df['price'].std()
  57. if std_dev == 0:
  58. return False, 0.0
  59. z_score = (current_price - mean) / std_dev
  60. is_anomaly = abs(z_score) > Z_SCORE_THRESHOLD
  61. return is_anomaly, z_score
  62. def send_email_alert(crypto, current_price, z_score):
  63. """Send email alert for anomaly."""
  64. symbol = CRYPTO_LABELS.get(crypto, crypto.upper())
  65. subject = f"Anomaly Detected in {symbol} Price!"
  66. body = f"""
  67. Alert Time: {datetime.now()}
  68. Crypto: {symbol}
  69. Current Price: ${current_price:.4f}
  70. Z-Score: {z_score:.2f} (Threshold: {Z_SCORE_THRESHOLD})
  71. This indicates a potential anomalous movement (spike or drop).
  72. """
  73. msg = MIMEMultipart()
  74. msg['From'] = EMAIL_FROM
  75. msg['To'] = EMAIL_TO
  76. msg['Subject'] = subject
  77. msg.attach(MIMEText(body, 'plain'))
  78. try:
  79. server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
  80. server.starttls()
  81. server.login(EMAIL_FROM, EMAIL_PASSWORD)
  82. server.sendmail(EMAIL_FROM, EMAIL_TO, msg.as_string())
  83. server.quit()
  84. print(f"Email alert sent for {symbol}.")
  85. except Exception as e:
  86. print(f"Error sending email for {symbol}: {e}")
  87. def save_history():
  88. """Save the price and z-score history to a CSV file."""
  89. if not timestamp_history:
  90. return
  91. rows = []
  92. for idx, ts in enumerate(timestamp_history):
  93. row = {'timestamp': ts}
  94. for crypto in CRYPTO_LIST:
  95. row[f'{crypto}_price'] = price_history[crypto][idx] if idx < len(price_history[crypto]) else ''
  96. row[f'{crypto}_zscore'] = zscore_history[crypto][idx] if idx < len(zscore_history[crypto]) else ''
  97. rows.append(row)
  98. df = pd.DataFrame(rows)
  99. df.to_csv(DATA_FILE, index=False)
  100. def monitor():
  101. """Main monitoring function: Fetch prices, check for anomaly, alert if needed, and save history."""
  102. current_prices = fetch_prices()
  103. now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  104. timestamp_history.append(now)
  105. for crypto, price in current_prices.items():
  106. if price is None:
  107. price_history[crypto].append(np.nan)
  108. zscore_history[crypto].append(np.nan)
  109. continue
  110. # Append to history (maintain fixed window size)
  111. price_history[crypto].append(price)
  112. if len(price_history[crypto]) > HISTORY_WINDOW:
  113. price_history[crypto].pop(0)
  114. is_anomaly, z_score = detect_anomaly(crypto, price)
  115. zscore_history[crypto].append(z_score)
  116. if len(zscore_history[crypto]) > HISTORY_WINDOW:
  117. zscore_history[crypto].pop(0)
  118. symbol = CRYPTO_LABELS.get(crypto, crypto.upper())
  119. print(f"{now} - {symbol} Price: ${price:.4f} | Z-Score: {z_score:.2f}")
  120. if is_anomaly:
  121. print(f"ANOMALY DETECTED for {symbol}! Z-Score: {z_score:.2f}")
  122. send_email_alert(crypto, price, z_score)
  123. # Keep timestamp_history in sync
  124. if len(timestamp_history) > HISTORY_WINDOW:
  125. timestamp_history.pop(0)
  126. save_history()
  127. # --- Web server for visualization ---
  128. app = Flask(__name__)
  129. HTML_TEMPLATE = """
  130. <!DOCTYPE html>
  131. <html>
  132. <head>
  133. <title>Crypto Z-Score & Price Monitor</title>
  134. <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  135. </head>
  136. <body>
  137. <h2>Crypto Z-Score & Price Monitor</h2>
  138. <div id="charts"></div>
  139. <script>
  140. async function fetchData() {
  141. const resp = await fetch('/data');
  142. return await resp.json();
  143. }
  144. function renderCharts(data) {
  145. const container = document.getElementById('charts');
  146. container.innerHTML = '';
  147. for (const crypto of data.cryptos) {
  148. const label = data.labels[crypto];
  149. const canvasPrice = document.createElement('canvas');
  150. const canvasZ = document.createElement('canvas');
  151. container.appendChild(document.createElement('hr'));
  152. container.appendChild(document.createTextNode(label + " Price"));
  153. container.appendChild(canvasPrice);
  154. container.appendChild(document.createTextNode(label + " Z-Score"));
  155. container.appendChild(canvasZ);
  156. new Chart(canvasPrice, {
  157. type: 'line',
  158. data: {
  159. labels: data.timestamps,
  160. datasets: [{
  161. label: label + ' Price',
  162. data: data.prices[crypto],
  163. borderColor: 'blue',
  164. fill: false
  165. }]
  166. }
  167. });
  168. new Chart(canvasZ, {
  169. type: 'line',
  170. data: {
  171. labels: data.timestamps,
  172. datasets: [{
  173. label: label + ' Z-Score',
  174. data: data.zscores[crypto],
  175. borderColor: 'red',
  176. fill: false
  177. }]
  178. }
  179. });
  180. }
  181. }
  182. fetchData().then(renderCharts);
  183. setInterval(() => fetchData().then(renderCharts), 60000);
  184. </script>
  185. </body>
  186. </html>
  187. """
  188. @app.route('/')
  189. def index():
  190. return render_template_string(HTML_TEMPLATE)
  191. @app.route('/data')
  192. def data():
  193. # Prepare data for chart
  194. return jsonify({
  195. 'cryptos': CRYPTO_LIST,
  196. 'labels': CRYPTO_LABELS,
  197. 'timestamps': timestamp_history,
  198. 'prices': price_history,
  199. 'zscores': zscore_history
  200. })
  201. def start_flask():
  202. app.run(host='0.0.0.0', port=5000, debug=False, use_reloader=False)
  203. # --- Main ---
  204. def main():
  205. # Start web server in a separate thread
  206. threading.Thread(target=start_flask, daemon=True).start()
  207. # Schedule the monitor to run every CHECK_INTERVAL_SECONDS
  208. schedule.every(CHECK_INTERVAL_SECONDS).seconds.do(monitor)
  209. print("Starting multi-crypto price anomaly monitor and web server on http://localhost:5000 ...")
  210. while True:
  211. schedule.run_pending()
  212. time.sleep(1)
  213. if __name__ == "__main__":
  214. main()