diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/AssetMonitor.iml b/.idea/AssetMonitor.iml
new file mode 100644
index 0000000..eb70346
--- /dev/null
+++ b/.idea/AssetMonitor.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..445a3f4
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..26d5e4d
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..06fde9a
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 6654144..85075d2 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,39 @@
-# AssetMonitor
+# 주식 모니터링 시스템
+
+이 프로그램은 주식 시장을 모니터링하고 볼린저 밴드 기반의 매수 시그널을 텔레그램으로 알려주는 시스템입니다.
+
+## 주요 기능
+
+- 미국 주식 데이터 자동 수집
+- 볼린저 밴드 분석
+- 텔레그램 알림 발송
+
+## 설치 방법
+
+1. 필요한 패키지 설치:
+```bash
+pip install -r requirements.txt
+```
+
+2. 환경 변수 설정:
+`.env` 파일을 생성하고 다음 내용을 추가:
+```
+TELEGRAM_BOT_TOKEN=your_bot_token
+TELEGRAM_CHAT_ID=your_chat_id
+```
+
+## 사용 방법
+
+프로그램 실행:
+```bash
+python stock_monitor.py
+```
+
+## 설정
+
+- `config.py` 파일에서 다음 설정을 변경할 수 있습니다:
+ - 볼린저 밴드 기간 (기본값: 20일)
+ - 표준편차 승수 (기본값: 2)
+ - 알림 임계값 (기본값: 10%)
+ - 모니터링할 주식 목록
diff --git a/config.py b/config.py
new file mode 100644
index 0000000..72b4251
--- /dev/null
+++ b/config.py
@@ -0,0 +1,101 @@
+import os
+
+# 텔레그램 설정
+TELEGRAM_BOT_TOKEN = "6435061393:AAHOh9wB5yGNGUdb3SfCYJrrWTBe7wgConM"
+TELEGRAM_CHAT_ID = '574661323'
+
+# 주식 설정
+US_STOCKS = {
+ 'VOO': 'Vanguard S&P 500 ETF',
+ 'SQQQ': 'ProShares UltraPro Short QQQ',
+ 'QID': 'ProShares UltraShort QQQ',
+ 'PSQ': 'ProShares Short QQQ',
+ 'TQQQ': 'ProShares UltraPro QQQ',
+ 'QQQ': 'Invesco QQQ Trust',
+ 'SCO': 'ProShares UltraShort Bloomberg Crude Oil',
+ 'UCO': 'ProShares Ultra Bloomberg Crude Oil',
+ 'GLL': 'ProShares UltraShort Gold',
+ 'UGL': 'ProShares Ultra Gold',
+ 'SOXS': 'Direxion Daily Semiconductor Bear -3X Shares',
+ 'SOXL': 'Direxion Daily Semiconductor Bull 3X Shares',
+ 'FNGD': 'MicroSectors™ FANG+™ Index -3X Inverse Leveraged ETN',
+ 'FNGU': 'MicroSectors™ FANG+™ Index 3X Leveraged ETN',
+ 'FXI': 'iShares China Large-Cap ETF',
+
+ 'AAPL': 'Apple',
+ 'MSFT': 'Microsoft',
+ 'GOOG': 'Alphabet C',
+ 'AMZN': 'Amazon.com',
+ 'AVGO': 'Broadcom',
+ 'NVDA': 'NVIDIA',
+ 'UNH': 'UnitedHealth',
+ 'TSM': 'Taiwan Semiconductor',
+ 'JNJ': 'Johnson & Johnson (JNJ)',
+ 'TCTZF': 'Tencent Holdings',
+ 'V': 'Visa A',
+ 'WMT': 'Walmart',
+ 'XOM': 'Exxon Mobil',
+ 'JPM': 'JPMorgan',
+ 'MA': 'Mastercard',
+ 'CVX': 'Chevron Corp',
+ 'HD': 'Home Depot',
+ 'BAC': 'Bank of America',
+ 'KO': 'Coca-Cola',
+ 'COST': 'Costco',
+ 'DIS': 'Walt Disney',
+ 'VZ': 'Verizon',
+ 'CSCO': 'Cisco',
+ 'ORCL': 'Oracle',
+ 'NKE': 'Nike',
+ 'ACN': 'Accenture',
+ 'ADBE': 'Adobe',
+ 'CRM': 'Salesforce.com',
+ 'INTC': 'Intel',
+ 'QCOM': 'Qualcomm',
+ 'AMD': 'AMD',
+ 'MS': 'Morgan Stanley',
+ 'T': 'AT&T',
+ 'HON': 'Honeywell',
+ 'IBM': 'IBM',
+ 'DQ': 'Daqo New Energy Corp ADR',
+ 'EBAY': 'eBay Inc',
+ 'NTAP': 'NetApp Inc',
+ 'ASML': 'ASML Holding NV ADR',
+ 'CPNG': 'Coupang LLC',
+ 'X': 'United States Steel Corporation',
+ 'BABA': 'Alibaba Group Holdings Ltd ADR'
+}
+
+# 한국 ETF 설정
+KR_ETFS = {
+ "251340.KS": 'KODEX 코스닥150선물인버스',
+ "233740.KS": 'KODEX 코스닥150 레버리지',
+ "252670.KS": 'KODEX 200선물인버스2X',
+ "122630.KS": 'KODEX 레버리지',
+ "114800.KS": 'KODEX 인버스',
+ "283580.KS": 'KODEX 중국본토CSI300',
+ "256750.KS": 'KODEX 심천ChiNext(합성)',
+ "185680.KS": 'KODEX 미국S&P바이오(합성)',
+ "218420.KS": 'KODEX 미국S&P에너지(합성)',
+ "132030.KS": 'KODEX 골드선물(H)',
+ "138920.KS": 'KODEX 콩선물(H)',
+ "271060.KS": 'KODEX 3대농산물선물(H)',
+ "117700.KS": 'KODEX 건설',
+ "266420.KS": 'KODEX 헬스케어',
+ "276990.KS": 'KODEX 글로벌4차산업로보틱스(합성)',
+ "244580.KS": 'KODEX 바이오',
+ "091160.KS": 'KODEX 반도체',
+ "140700.KS": 'KODEX 보험',
+ "266410.KS": 'KODEX 필수소비재',
+ "305720.KS": 'KODEX 2차전지산업',
+ "266390.KS": 'KODEX 경기소비재',
+ "117680.KS": 'KODEX 철강',
+ "117460.KS": 'KODEX 에너지화학',
+ "091170.KS": 'KODEX 은행',
+ "376410.KS": 'TIGER 탄소효율그린뉴딜'
+}
+
+# 볼린저 밴드 설정
+BOLLINGER_PERIOD = 20 # 볼린저 밴드 기간
+BOLLINGER_STD = 2 # 표준편차 승수
+ALERT_THRESHOLD = 0.15 # 하단 밴드 대비 10% 근접 시 알림
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..ce48393
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,3 @@
+yfinance
+pandas
+numpy
\ No newline at end of file
diff --git a/stock_monitor.py b/stock_monitor.py
new file mode 100644
index 0000000..5634cca
--- /dev/null
+++ b/stock_monitor.py
@@ -0,0 +1,121 @@
+import yfinance as yf
+import pandas as pd
+from datetime import datetime, timedelta
+import telegram
+import time
+import asyncio
+from multiprocessing import Pool
+from config import *
+
+def send(text):
+ client = telegram.Bot(token=TELEGRAM_BOT_TOKEN)
+ asyncio.run(client.send_message(chat_id=TELEGRAM_CHAT_ID, text=text))
+ return
+
+def send_telegram_message(message):
+ pool = Pool(12)
+ pool.map(send, [message])
+
+def calculate_bollinger_bands(data):
+ data['MA'] = data['Close'].rolling(window=BOLLINGER_PERIOD).mean()
+ data['STD'] = data['Close'].rolling(window=BOLLINGER_PERIOD).std()
+ data['Upper'] = data['MA'] + (BOLLINGER_STD * data['STD'])
+ data['Lower'] = data['MA'] - (BOLLINGER_STD * data['STD'])
+ return data
+
+def check_bollinger_bands(symbol, data):
+ if len(data) < BOLLINGER_PERIOD:
+ return None
+ latest = data.iloc[-1]
+ upper_band = latest['Upper'].iloc[0]
+ lower_band = latest['Lower'].iloc[0]
+ current_price = latest['Close'].iloc[0]
+ distance = (current_price - lower_band) / (upper_band - lower_band)
+ return {
+ 'symbol': symbol,
+ 'price': current_price,
+ 'lower_band': lower_band,
+ 'distance': distance
+ }
+
+def get_stock_data(symbol, retries=3):
+ for attempt in range(retries):
+ try:
+ end = datetime.now()
+ start = end - timedelta(days=60)
+ data = yf.download(
+ symbol,
+ start=start.strftime('%Y-%m-%d'),
+ end=end.strftime('%Y-%m-%d'),
+ interval='1d',
+ auto_adjust=True,
+ progress=False
+ )
+ if not data.empty:
+ return data
+ print(f"No data received for {symbol}, attempt {attempt + 1}")
+ time.sleep(2)
+ except Exception as e:
+ print(f"Attempt {attempt + 1} failed for {symbol}: {str(e)}")
+ if attempt < retries - 1:
+ time.sleep(5)
+ continue
+ return None
+
+def sendAlertMsg(info, market="US"):
+ if market == "US":
+ message = "🔔 [US] {} ({}) 현재가: ${:.2f}, 근접도: {:.2f}%".format(info['name'], info['symbol'], info['price'], info['distance'])
+ else:
+ message = "🔔 [KR] {} ({}) 현재가: ₩{:.0f}, 근접도: {:.2f}%".format(info['name'], info['symbol'].replace('.KS', ''), info['price'], info['distance'])
+
+ try:
+ send_telegram_message(message)
+ except Exception as e:
+ print(f"Error sending Telegram message: {str(e)}")
+ return
+
+def monitor_stocks():
+ # 미국 주식 모니터링
+ print("Monitoring US stocks...")
+ for symbol in US_STOCKS:
+ data = get_stock_data(symbol)
+ if data is not None and not data.empty:
+ try:
+ data = calculate_bollinger_bands(data)
+ info = check_bollinger_bands(symbol, data)
+ info['name'] = US_STOCKS[symbol]
+ print(" - {} ({}): {:.2f} ({:.2f})".format(info['name'], symbol, info['price'], info['distance']))
+
+ if info['distance'] <= ALERT_THRESHOLD:
+ sendAlertMsg(info, "US")
+ print(f"Alert generated for {symbol}")
+ except Exception as e:
+ print(f"Error processing data for {symbol}: {str(e)}")
+ else:
+ print(f"Data for {symbol} is empty or None.")
+ time.sleep(0.5)
+
+ # 한국 ETF 모니터링
+ print("\nMonitoring Korean ETFs...")
+ for symbol in KR_ETFS:
+ data = get_stock_data(symbol)
+ if data is not None and not data.empty:
+ try:
+ data = calculate_bollinger_bands(data)
+ info = check_bollinger_bands(symbol, data)
+ info['name'] = KR_ETFS[symbol]
+ print(" - {} ({}): {:.2f} ({:.2f})".format(info['name'], symbol, info['price'], info['distance']))
+
+ if info['distance'] <= ALERT_THRESHOLD:
+ sendAlertMsg(info, "KR")
+ print(f"Alert generated for {symbol}")
+ except Exception as e:
+ print(f"Error processing data for {symbol}: {str(e)}")
+ else:
+ print(f"Data for {symbol} is empty or None.")
+ time.sleep(0.5)
+
+ return
+
+if __name__ == "__main__":
+ monitor_stocks()