첫 번째 커밋

This commit is contained in:
javamon
2025-12-06 22:31:19 +09:00
commit 849a100fa9
33 changed files with 6613 additions and 0 deletions

277
cron.py Normal file
View File

@@ -0,0 +1,277 @@
# -*- coding: utf-8 -*-
import importlib
import os
import random
import sys
from tzlocal import get_localzone
import logging
from apscheduler.schedulers.background import BackgroundScheduler
from datetime import datetime
from db import DB
from trader import Trader
from bot import Bot
'''
- (매시간 단위)Base 테이블의 새 데이터 추가시 테이블 생성(종목-거래소)
- (매일 단위) 거래소-종목 거래량 미달 시 해당 종목 포지션 청산 후 테이블 삭제
- (매분 단위)종목-거래소별 Tinker 정보 저장(1분, 1시간, 1일) => 1분만 할지 다시 고민
- (매시간 단위) 시간봉 데이터 저장 및 분석 클래스에 전달
- (매일 단위) 일봉 데이터 저장 및 분석 클래스에 전달
* 잡 단위는 함수
- 크론 테이블에 데이터 등록
=> 업비트-비트코인 아이템 테이블에 분, 시간, 일을 구분하는 컬럼을 추가하여, 봉별 시간을 유지.
=> 업비트-비트코인 아이템 테이블에 2개의 used 컬럼을 추가하여, 해당 데이터가 머신러닝, 보조지표에 활용 되었는 지 식별
=> 거래소-종목 아이템이 추가 될때 크론 테이블에 추가(3개가 한 세트로 분마다, 시간마다, 일 마다)
- 크론 테이블 데이터 로드 후 스케쥴러에 등록
* 작업 목록 생성 -> 등록 -> 작업 목록 실행
- 다음 작업 스케쥴러 라이브러리 연동 후 데이터 저장 프로세스 구현하기
'''
# base path
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + '/collectors'
# time array for curl data
columns = ['hour', 'day'] # 분봉, 시간봉, 일봉
added_job = ['bot', 'trader']
class Cron:
mode = 'Test'
# mode = 'Service'
exchange_instance_list = {}
def __init__(self):
# init variables
self.db = DB()
# set logger
# self._set_logger_for_cron()
logging.basicConfig()
# set base data
self.f_list = self.db.select_base_table()
# def _set_logger_for_cron(self):
# logging.basicConfig()
def start(self):
print('For %s Working. ' % self.mode)
if self.mode != 'Test':
# 배치 프로세스를 위한 인스턴스 저장
self.save_exchange_instances_2_cron()
# 배치 목록 생성
self.save_batch_job_list()
# 배치 목록 실행
self.excute_batch_job_list()
else:
self.save_exchange_instances_2_cron()
self.save_batch_job_list() # DB에 배치 목록 없을 시 주석 해제
# 거래 종목 생성 및 데이터 저장 로직
self.test_excute_batch_job_list()
# 거래소 거래 기준 업데이트 => 외부에서 호출 => 크론에 잡 추가 예정
def update_exchange_standard_options(self):
for e in self.f_list:
obj = self.exchange_instance_list[e['exchange_name']]
obj.update_exchange_standard_options()
def save_exchange_instances_2_cron(self):
# for finance, exchange in self.f_list.items() : # dynamically create an exchange instance
for f_item in self.f_list: # dynamically create an exchange instance
finance = f_item['finance_name']
exchange = f_item['exchange_name']
# add dynamically path to os.path
if not BASE_DIR + '/' + finance in sys.path:
sys.path.append(BASE_DIR + '/' + finance)
module = importlib.import_module('c_' + exchange)
self.exchange_instance_list.update({
exchange: getattr(module, str(exchange).capitalize())()
})
self.exchange_instance_list[exchange].finance = finance # set finance name
self.exchange_instance_list[exchange].db = self.db # set db object
# 잡 리스트를 디비에 등록하기(시간, 일 세트)
def save_batch_job_list(self, job_type='price'):
# 거래소별 잡 등록
for f_item in self.f_list:
finance = f_item['finance_name']
exchange = f_item['exchange_name']
trade_type = f_item['trade_type']
# 거래소 정보 로드
e_info = self.db.select_row_data_from_exchange(exchange)
# 거래소 종목 로드
items = self.db.select_items_data_from_tb_items_by_exchange_id(e_info['id'])
# 아이템별 잡 디비= 저장
for i in items:
# 상태값 준비-시작의 아이템만 잡 리스트 추가
if i['status'] == 'ready' or i['status'] == 'started':
self.db.insert_job_data_to_cron(finance, exchange, job_type, i['name'], columns, 1, trade_type)
# 크론 잡 등록
def _add_job_to_sched(self, job_info):
# custum job
if job_info == 'bot':
self.sched.add_job(self.excute_outer_obj, 'interval', minutes=60, id='bot', args=[job_info])
elif job_info == 'trader':
self.sched.add_job(self.excute_outer_obj, 'interval', minutes=10, id='trader', args=[job_info])
# curl job
else:
self.add(self.save_candel_data, 'cron', job_info['time_unit'], job_info['job'], job_info)
# self.add(self.save_candel_data, 'cron', job_info['time_unit'], job_info['job'], job_info)
# self.save_candel_data(job_info) # for test
# self.sched.add_job(job, 'cron', second='5', id="test_10") # 매 지정 일-시간 실행(매일)
# self.sched.add_job(job, 'cron', minute="01", second='5', id="test_10") # 매 지정 시간 실행(매 시간)
# self.sched.add_job(job, 'cron', hour="9", minute="01", second='5', id="test_10") # 매 지정 초 실행(매 분)
# self.sched.add_job(self.save_candel_data, 'interval', seconds=3, id=job_info['job'], args=[job_info], replace_existing=True) # 초 마다 실행
def excute_outer_obj(self, obj_name):
o = importlib.import_module(obj_name)
c = getattr(o, obj_name.capitalize())()
c.run()
del o, c
def test_excute_batch_job_list(self):
print('function called : test_excute_batch_job_list()')
job_list = self.db.select_job_list()
for job in job_list:
# print('this job :', job)
self.save_candel_data(job)
def excute_batch_job_list(self): # 디비에서 잡 리스트 가져온 뒤 크론 등록
# 스케쥴러 실행
# self.sched = BackgroundScheduler({'apscheduler.timezone': 'UTC'})
# self.sched = BackgroundScheduler({'apscheduler.timezone': get_localzone()})
self.sched = BackgroundScheduler({'apscheduler.timezone': 'Asia/Seoul'})
# reset job list
self.sched.remove_all_jobs()
# added jobs
if self.mode == 'Service':
for job in added_job:
# continue # for test
self._add_job_to_sched(job)
# curl data job
job_list = self.db.select_job_list()
for job in job_list:
self._add_job_to_sched(job)
self.sched.start()
# 스케쥴러 전체 잡 리스트 출력
self.sched.print_jobs()
def _convert_slash_string_to_array(self, str):
return str.split('/')
# 봉데이터 저장
def save_candel_data(self, job_info):
job = self._convert_slash_string_to_array(job_info['job'])
obj = self.exchange_instance_list[job[1]]
print('now job :', job, datetime.now())
# 종목별 day 테이블 생성 => 종목 당 min-day 테이블 두개 운영
if 'min' in job_info['time_unit']:
obj.save_current_min_data(job[2])
elif 'hour' in job_info['time_unit']:
obj.save_current_hour_data(job[2])
elif 'day' in job_info['time_unit']:
obj.save_current_day_data(job[2])
# 정적 시간 스케쥴 등록
def add(self, func, job_type, time_type, id, data, day_of_week=0, day=1):
job = self._convert_slash_string_to_array(data['job'])
obj = self.exchange_instance_list[job[1]]
hour = 9
# hour = obj.get_based_time_from_data(data)
sec = str(random.randrange(2, 57))
min = str(1)
# min = str(random.randrange(2, 10))
''' https://apscheduler.readthedocs.io/en/3.0/modules/triggers/cron.html#module-apscheduler.triggers.cron
year (int|str) 4-digit year
month (int|str) month (1-12)
day (int|str) day of the (1-31)
week (int|str) ISO week (1-53)
day_of_week (int|str) number or name of weekday (0-6 or mon,tue,wed,thu,fri,sat,sun)
hour (int|str) hour (0-23)
minute (int|str) minute (0-59)
second (int|str) second (0-59)
start_date (datetime|str) earliest possible date/time to trigger on (inclusive)
end_date (datetime|str) latest possible date/time to trigger on (inclusive)
timezone (datetime.tzinfo|str) time zone to use for the date/time calculations (defaults to scheduler timezone)
kst : 한국 기준 시간 9시 마감
utc : 세계 공용 기준 시간 0시 마감
'''
if 'min' in time_type: # 매분
self.sched.add_job(func, job_type, second=sec, id=id, args=[data], replace_existing=True)
elif 'hour' in time_type: # 매시
# self.sched.add_job(func, job_type, minute=10, second=sec, id=id, args=[data], replace_existing=True)
self.sched.add_job(func, job_type, minute=min, second=sec, id=id, args=[data], replace_existing=True)
elif 'day' in time_type: # 매일
self.sched.add_job(func, job_type, hour=hour, minute=min, second=sec, id=id, args=[data],
replace_existing=True)
elif 'day_of_week' in time_type: # 요일 마다
self.sched.add_job(func, job_type, day_of_week=day_of_week, hour=hour, minute=min, second=sec, id=id,
args=[data], replace_existing=True)
elif 'month' in time_type: # 매달-일 마다
self.sched.add_job(func, job_type, day=day, hour=hour, minute=min, second=sec, id=id, args=[data],
replace_existing=True)
def remove_job(self, id):
self.sched.remove_job(id)
def get_job(self, id):
return self.sched.get_job(id)
def update_job(self, job_id, jobstore=None, trigger=None, **trigger_args):
'''
reschedule_job(job_id, jobstore=None, trigger=None, **trigger_args)
Constructs a new trigger for a job and updates its next run time.
Extra keyword arguments are passed directly to the triggers constructor.
Parameters
job_id (str|unicode) the identifier of the job
jobstore (str|unicode) alias of the job store that contains the job
trigger alias of the trigger type or a trigger instance
Return Job
the relevant job instance
'''
return self.sched.reschedule_job(job_id, jobstore, trigger, trigger_args)
def remove_all_jobs(self):
self.sched.remove_all_jobs()