# docker-compose.yml
version: '3'
services:
web:
build: ./
command: 'rails server -b 0.0.0.0 -p 3000'
ports:
- 4000:3000
stdin_open: true
tty: true
volumes:
- ./:/app
- ${STOCK_TREND_FINDER_DATA_DIR}:${STOCK_TREND_FINDER_DATA_DIR}
jobs:
build: ./
command: 'rake stf:run_jobs_daemon'
stdin_open: true
tty: true
volumes:
- ./:/app
- ${STOCK_TREND_FINDER_DATA_DIR}:${STOCK_TREND_FINDER_DATA_DIR}
# lib/tasks/stf.rake
namespace :stf do
desc "Just test the output of whenever"
task :test_output => :environment do
puts "This is a test output message at #{Time.current}"
end
desc "Runs the daily and weekly market data jobs"
task :run_jobs_daemon => :environment do
JobScheduler::ScheduleJobs.call
while 1 do
# infinite loop until Ctrl+C hit - keeps rake task from ending and allows job threads to run
sleep 600
end
end
end
# lib/job_scheduler/schedule_jobs.rb
module JobScheduler
class ScheduleJobs < CallableServiceBase
include Jobs
include JobLogger
SCHEDULE = [
{ cron: '1 12 * * MON', method: :update_company_list }, # 12:01am Mondays
{ cron: '8 12 * * MON', method: :update_sp500_list }, # 12:08am Mondays
{ cron: '25 4 * * MON-FRI', method: :prepopulate_stock_prices }, # 12:25am weekdays
{ cron: '35,45 9 * * MON-FRI', method: :real_time_quotes_update }, # 9am hour weekday updates
{ cron: '0,30 10-15 * * MON-FRI', method: :real_time_quotes_update }, # 10am-4pm weekday updates (every half hour),
{ cron: '5 16 * * MON-FRI', method: :finalize_daily_quotes }, # 4:05pm weekdays
{ cron: '0 21 * * MON-FRI', method: :save_report_snapshots }, # 9pm weekdays
{ cron: '30 12 * * SAT', method: :pull_tda_fundamentals }, # 12:30am Saturday (~30 minutes?)
{ cron: '0 6 * * SAT', method: :run_db_maintenance }, # 6am on Saturday
# { cron: '*/5 * * * * *', method: :test_cron },
]
def call(_args)
log('Starting Rufus scheduler')
rufus = Rufus::Scheduler.singleton
def rufus.on_error(job, error)
JobLogger.log("rufus-scheduler #{error.inspect} in job #{job.inspect}", level: :error)
end
SCHEDULE.each do |details|
log("Scheduling #{details[:method]}")
rufus.cron(details[:cron], method(details[:method]))
end
log("Done scheduling Rufus")
end
def test_cron
log("Testing Cron - #{Time.current}")
end
end
end
# lib/job_scheduler/jobs.rb
module JobScheduler
module Jobs
module_function
extend JobScheduler::JobLogger
def finalize_daily_quotes
log("Finalizing daily_stock_prices: #{Time.current}")
(log("Market closed today, no real time quotes to finalize necessary"); return) unless StockMarketDays.is_market_day?
ActiveRecord::Base.connection_pool.with_connection do
MarketDataPull::TDAmeritrade::DailyQuotes::FinalizeDailyQuotesFromRealTimeSnapshot.call
end
log("Done finalizing daily_stock_prices: #{Time.current}")
end
def prepopulate_stock_prices
# ...
end
def pull_tda_fundamentals
# ...
end
def real_time_quotes_update
# ...
end
def run_db_maintenance
# ...
end
def save_report_snapshots
# ...
end
def update_company_list
# ...
end
def update_sp500_list
# ...
end
end
end
# lib/job_scheduler/job_logger.rb
module JobScheduler
module JobLogger
module_function
LOG_FILE = Rails.root.join('log', 'jobs.log')
@@jobs_logger = Logger.new(LOG_FILE)
def log(message, level: :info)
puts message
@@jobs_logger.send(level, message)
end
end
end