Files
oh_my_bot_app/cron.py
2025-12-06 22:31:19 +09:00

278 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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()