"""30초마다 전 종목 현재가를 Oracle price_tick 테이블에 적재. + 60초마다 backtest_ohlcv 1분봉 최신 데이터 갱신. """ import sys, os, time, logging from datetime import datetime sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from dotenv import load_dotenv load_dotenv(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), '.env')) import pyupbit import oracledb TICKERS = [ 'KRW-XRP', 'KRW-BTC', 'KRW-ETH', 'KRW-SOL', 'KRW-DOGE', 'KRW-ADA', 'KRW-SUI', 'KRW-NEAR', 'KRW-KAVA', 'KRW-SXP', 'KRW-AKT', 'KRW-SONIC', 'KRW-IP', 'KRW-ORBS', 'KRW-VIRTUAL', 'KRW-BARD', 'KRW-XPL', 'KRW-KITE', 'KRW-ENSO', 'KRW-0G', 'KRW-MANTRA', 'KRW-EDGE', 'KRW-CFG', 'KRW-ARDR', 'KRW-SIGN', 'KRW-AZTEC', 'KRW-ATH', 'KRW-HOLO', 'KRW-BREV', 'KRW-SHIB', ] INTERVAL = 30 # 초 OHLCV_INTERVAL = 60 # 1분봉 갱신 주기 (초) logging.basicConfig( level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s', handlers=[ logging.FileHandler('/tmp/tick_collector.log'), logging.StreamHandler(sys.stdout), ] ) log = logging.getLogger(__name__) def get_conn(): kwargs = dict(user=os.environ["ORACLE_USER"], password=os.environ["ORACLE_PASSWORD"], dsn=os.environ["ORACLE_DSN"]) if w := os.environ.get("ORACLE_WALLET"): kwargs["config_dir"] = w return oracledb.connect(**kwargs) def collect(conn): prices = pyupbit.get_current_price(TICKERS) if not prices: log.warning("현재가 조회 실패") return ts = datetime.now().replace(microsecond=0) rows = [(t, ts, p) for t, p in prices.items() if p] cur = conn.cursor() cur.executemany( "INSERT INTO price_tick (ticker, ts, price) VALUES (:1, :2, :3)", rows ) conn.commit() log.info(f"적재 {len(rows)}건 ts={ts}") def refresh_ohlcv(conn): """backtest_ohlcv 1분봉을 최근 5개씩 갱신 (중복 무시).""" total = 0 for ticker in TICKERS: try: df = pyupbit.get_ohlcv(ticker, interval='minute1', count=5) if df is None or df.empty: continue rows = [ (ticker, 'minute1', ts.to_pydatetime(), float(r['open']), float(r['high']), float(r['low']), float(r['close']), float(r['volume'])) for ts, r in df.iterrows() ] cur = conn.cursor() cur.executemany( "INSERT INTO backtest_ohlcv " "(ticker,interval_cd,ts,open_p,high_p,low_p,close_p,volume_p) " "VALUES (:1,:2,:3,:4,:5,:6,:7,:8)", rows, batcherrors=True, ) inserted = len(rows) - len(cur.getbatcherrors()) total += inserted time.sleep(0.15) except Exception as e: log.warning(f"[ohlcv] {ticker} 오류: {e}") conn.commit() if total > 0: log.info(f"[ohlcv] 1분봉 갱신 {total}건") def main(): log.info("=== tick_collector 시작 (30초 간격 + 1분봉 갱신) ===") conn = get_conn() last_ohlcv = 0 while True: t0 = time.time() try: collect(conn) # 1분봉 갱신 (OHLCV_INTERVAL마다) if t0 - last_ohlcv >= OHLCV_INTERVAL: refresh_ohlcv(conn) last_ohlcv = t0 except oracledb.DatabaseError as e: log.error(f"DB 오류: {e} — 재연결") try: conn.close() except: pass conn = get_conn() except Exception as e: log.error(f"오류: {e}") elapsed = time.time() - t0 time.sleep(max(1.0, INTERVAL - elapsed)) if __name__ == '__main__': main()