간단한 주가 모니터링 프로그램을 만들어 보려고 합니다. 매매는 타이밍 이라는데 주가를 계속 지켜보고 있을 수는 없는 노릇이니 프로그램을 통해 주가를 지켜보다가 필요할 때 메신저로 알림을 받을 수 있도록 하는 방식입니다. 우선 Don’t reinvent the wheel의 조언에 따라 비슷한 서비스가 있는지 검색해봅니다. 자몽그램 이라는 서비스가 있는데 이것과 비슷한 것이 될 것 같습니다.
주가 모니터링을 하려면 주기적으로 스크래핑을 실행해야 합니다. 전에 썼던 재입고 관련 스크래핑 글에서는 무한 루프와 sleep 함수를 이용해 프로그램의 주기를 조정했습니다.이 경우 좀 반복되는 코드의 시간만큼 실행시간이 늦어지는 현상이 나타납니다. 언제 실행할지를 정확히 조절하기는 어려워 집니다. 이럴 때 필요한 것이 스케줄러 입니다.
java에서는 quartz 라는 스케줄러가 대표적으로 있습니다. Spring 등 이런저런 프레임워크에도 포함되어 있습니다. 사용하기도 어렵지 않은 편입니다.
운영체제의 스케줄러를 사용하는 것도 방법입니다. 윈도우의 경우 작업 스케줄러가 있고 유닉스나 리눅스의 경우 cron 이 있습니다. 다만 이러한 정보들이 운영체제에 남게 되는 것이 프로그램의 이식성을 낮게 만드는 원인이 됩니다.
파이썬으로 만드는 프로그램이라 파이썬 쪽 스케줄러들을 찾아봤습니다. 기본적으로는 내장된 sched가 있고 apscheduler, Celery Periodic Task 등이 대표적인 것 같습니다. 그 중 apscheduler가 적용하기 적당해 보여 선택해봤습니다.
예제 코드
현재 우리나라 증시의 영업일은 월 ~ 금요일 이고 개장시간과 패장시간은 9시, 15시 반 입니다. 이 시간 동안만 모니터링을 한다고 하면 2가지 스케줄링이 필요합니다.
- 모니터링 시작/종료 주기 : 월~금요일 9시 모니터링 시작, 15시 30분 모니터링 종료.
- 모니터링 체크 주기 : 1분마다/5분마다/10분마다 등등
1번 스케줄에서는 2번 스케줄을 시작하거나 종료하게 됩니다. 일종의 중첩(nested) 스케줄링이라고 할 수 있겠습니다.
테스트를 위해 이런 코드를 만들어 봤습니다. 실제로 월요일~금요일을 테스트하기는 어려우므로 주기를 짧게 만들었습니다.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from apscheduler.schedulers.blocking import BlockingScheduler | |
| from datetime import datetime | |
| # main code | |
| def runner(): | |
| print(f'{datetime.now()}:runner') | |
| sched = BlockingScheduler() | |
| # main job | |
| runner_job = sched.add_job(runner, 'interval', seconds=10) | |
| # start job | |
| def start_runner(): | |
| print(f'{datetime.now()}:start_runner') | |
| runner_job.resume() | |
| # stop job | |
| def stop_runner(): | |
| print(f'{datetime.now()}:stop_runner') | |
| runner_job.pause() | |
| # to delay main job | |
| stop_runner() | |
| # schedules for main job | |
| sched.add_job(start_runner, 'cron', minute='1,11,21,31,41,51') | |
| sched.add_job(stop_runner, 'cron', minute='5,15,25,35,45,55') | |
| sched.start() |
- 6라인 : 메인 코드가 될 부분입니다. 테스트를 위해 시각만 표시하도록 했습니다.
- 10라인 : 스케줄러 인스턴스를 만듭니다. 하나의 프로세스(process)로 돌아가므로 BlockingScheduler를 사용했습니다.
- 13라인 : 메인 코드를 스케줄러에 추가해 줍니다. 모니터링 체크 주기에 해당하는 작업입니다. 여기서는 테스트를 위해 10초마다 돌아가도록 해봤습니다.
- 16라인/21라인 : 각각 메인 코드의 스케줄을 멈추고(pause) 다시 시작하는(resume) 함수입니다.
- 29라인/30라인 : 위의 16라인/21라인 코드를 실행하는 스케줄을 등록합니다.
처음에는 2개의 스케줄러를 만들었는데 뭔가 정상 작동하지 않았습니다. 하나의 프로세스에서 스케줄러는 하나만 사용해야 하는 것 같았습니다. 여러 주기를 섞어서 사용할 때는 스케줄러를 여러 개 만들어서 사용하는게 아니고 하나의 스케줄러에 각각의 작업 주기를 다르게 등록하는게 맞는 것 같습니다.
참조
- apscheduler 소스 : https://github.com/agronholm/apscheduler
- 파이썬 스크립트 스케줄링하기 : https://medium.com/wasd/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81%ED%95%98%EA%B8%B0-720dcc47b7e7
- sched 예제 : https://pymotw.com/2/sched/
- sched 예제 : https://www.programcreek.com/python/example/91056/sched.scheduler