I have a program which I require to cease some of it's activities at specific times. This can change day-to-day, week-to-week.
The code below (working) is a test script. My program would check use the sleep_data() data set to check if the current date falls within any of the periods for a given day alongside the other updating of the program's state (every 2 sec). If so, it would call a method and the program sleeps a particular sub-set of it's activities.
There must be a better way.
#!/usr/bin/env python3
import datetime
import logging
import time
from dateutil.parser import parse
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(levelname)s - %(message)s")
#logging.disable(logging.CRITICAL)
def sleep_data():
"""Return a dict of lists containing tuples of the periods in which to stand
down selected bot's activities
"""
#
# parse("00:00") + oneday_delta = end of current day
#
oneday_delta = datetime.timedelta(hours=24)
#
# 1 = Monday, 5 = Friday etc. parse returns datetime.datetime object.
#
times = {
1 : [(parse("21:55"), parse("23:15"))],
2 : [(parse("21:55"), parse("23:15"))],
3 : [(parse("21:55"), parse("23:15"))],
4 : [(parse("21:55"), parse("23:15")),
(parse("15:45"), parse("15:45:10"))], # test use
5: [(parse("20:55"), parse("00:00") + oneday_delta)],
6: [(parse("00:00"), parse("00:00") + oneday_delta)], # sleep all Sat
7: [(parse("00:00"), parse("23:15"))],
}
return times
class Test:
def __init__(self, sleep_times: dict):
self.sleep_times = sleep_times
self.trading = True
def check_times(self, day: int, date_time: object):
"""If time within an exclusion period, set trading attrib to False"""
exclusion_periods = self.sleep_times.get(day)
for period in exclusion_periods:
_from, to = period
if date_time > _from and date_time < to:
self.trading = False
return
self.trading = True
def run(self):
while True:
date_time = datetime.datetime.now()
day = date_time.isoweekday()
self.check_times(day, date_time)
print(self.trading)
time.sleep(1)
if __name__ == "__main__":
t = Test(sleep_data()).run()
1 Answer 1
Rather than making your times a dictionary indexed by an int just make it a list of tuples (start, end) and iterate through the list of trading times.
Input the list in order or sort it before starting so you can be assured that time[n].start < time[n].end
and time[n].end < time[n+1].start
.
For something as simple as your example it is clear enough what the code is doing. It doesn't scale as you add more complexity to the decision to trade or not. Consider different exchanges, different products, order types, etc. Your single check_times function is going to get very complex.
To prepare for such coming complexity I would have a Trader class for each combination of things that could influence trading time, each with its own check_times function. A Controller class would be responsible for choosing the appropriate Trader to handle things based on the attributes at hand. Currently the only attribute you are looking at is the current time and so there would only be two Traders, and ActiveTrader and a DeactivatedTrader.
Again, depending upon the complexity of what is being implemented, these Trader's could be classes that descend from a common Trader class or could be duck-typed to have the same API. Let Don't-Repeat-Yourself be your guide here. If you are writing more Python code to set up the inheritance than you have saved by inheriting from a common ancestor you are doing it wrong.