Invalid Argument when implementing sched - python

I've written a basic wrapper around sched.scheduler, but I'm getting:
File "/usr/local/anaconda3/lib/python3.7/sched.py", line 149, in run
delayfunc(time - now)
OSError: [Errno 22] Invalid argument
What is the cause of this error? I'm following https://docs.python.org/3/library/sched.html
Code:
#!/usr/local/anaconda3/bin/python
from time import time, sleep
from datetime import datetime, timezone
from sched import scheduler
class Scheduler:
# specify one or t'other e.g. "2021-03-17 02:29:00"
def __init__(self, event_time_utc:str='', local_unix_timestamp_override:float=0):
self.event_local_timestamp = Scheduler.to_local_unix_timestamp(event_time_utc) \
if event_time_utc else local_unix_timestamp_override
# self.seconds_until_event = event_local_timestamp - time()
print(self.event_local_timestamp - time())
self.sch = scheduler()
def run(self, blocking=False):
self.sch.run(blocking)
#staticmethod
def to_local_unix_timestamp(time_utc: str):
dt_utc = datetime.strptime(time_utc, "%Y-%m-%d %H:%M:%S")
dt_local = dt_utc.replace(tzinfo=timezone.utc)
return dt_local.timestamp()
#staticmethod
def to_human_readable(unix_timestamp):
return datetime.fromtimestamp(unix_timestamp).strftime('%Y-%m-%d %H:%M:%S.%f')
def schedule(self, offset_from_event_time_s:float, callback):
print('Event scheduled for local unix timestamp:', self.event_local_timestamp + offset_from_event_time_s)
self.sch.enterabs(
time = self.event_local_timestamp + offset_from_event_time_s,
priority = 1,
action = callback
)
if __name__ == '__main__':
sch = Scheduler(local_unix_timestamp_override=time()+5)
print(f'Started at time: {Scheduler.to_human_readable(time())}')
def callback():
print(f'Callback at time: {Scheduler.to_human_readable(time())}')
sch.schedule(offset_from_event_time_s=1, callback=callback)
sch.run(blocking=True)
Output:
> ./scheduler.py
4.999997854232788
Started at time: 2021-03-17 11:00:03.424191
f 1615953609.424139
Traceback (most recent call last):
File "./scheduler.py", line 55, in <module>
sch.run(blocking=True)
File "./scheduler.py", line 20, in run
self.sch.run(blocking)
File "/usr/local/anaconda3/lib/python3.7/sched.py", line 149, in run
delayfunc(time - now)
OSError: [Errno 22] Invalid argument

The root of the problem is that YOU are assuming that the sched module is using time.time() for its timing function. That is a false assumption. It is using time.monotonic() by default, which returns the time since your program started. That is going to be a small number, like 6. When you try to delay until 1615644884.1231558, that makes it go crazy.
Change this:
self.sch = scheduler()
to this:
self.sch = scheduler(time)

I tried debugging the issue and found that the max length of floating point value allowed in time.sleep() is 8 (I couldn't find any resource on why only 8) but since you are using timestamp the value exceeds that. You can try following code on your machine and check that:
import time
# Will work fine
time.sleep(16156448)
# Will result in error
time.sleep(1615644884.1231558)

Related

Backtrader issue - optstrategy AttributeError: module 'collections' has no attribute 'Iterable'

I'm trying to optimize my strategy on Backtrader with python but keeps getting this error and i can't find anything on the web showing why I'm getting it.
My code is simple and loosely based on the quick start examples:
from alpaca_trade_api.rest import REST, TimeFrame, TimeFrameUnit
import backtrader as bt
from config import API_KEY, SECRET_KEY
class EMACross(bt.Strategy):
params = dict(
ema_short_period=5,
ema_long_period=10
)
def __init__(self):
self.order = None
self.short_ma = bt.indicators.ExponentialMovingAverage(period=self.p.ema_short_period)
self.long_ma = bt.indicators.ExponentialMovingAverage(period=self.p.ema_long_period)
self.crossover = bt.ind.CrossOver(self.short_ma, self.long_ma) # crossover signal
self.crossdown = bt.ind.CrossDown(self.short_ma, self.long_ma)
self.crossdown.plotinfo.subplot = False
self.crossover.plotinfo.subplot = False
def next(self):
self.log('Close, %.2f' % self.data.close[0])
if self.position.size > 0:
if self.crossdown > 0:
self.log('SELL CREATE, %.2f' % self.data.close[0])
self.close()
else:
if self.crossover > 0:
self.log('BUY CREATE, %.2f' % self.data.close[0])
self.buy()
def log(self, txt, dt=None):
dt = dt or self.data.datetime.datetime()
print('%s, %s' % (dt.isoformat(), txt))
def stop(self):
self.log('(short EMA Period %2d) (long EMA Period %2d) Ending Value %.2f' %
(self.p.ema_short_period, self.p.ema_long_period, self.broker.getvalue()))
rest_api = REST(API_KEY, SECRET_KEY, 'https://paper-api.alpaca.markets')
def run_backtest(strategy, symbols, start, end, timeframe, cash=100000):
# initialize backtrader broker
cerebro = bt.Cerebro()
cerebro.broker.setcash(cash)
cerebro.addsizer(bt.sizers.PercentSizer, percents=90)
cerebro.optstrategy(strategy, ema_short_period=4, ema_long_period=6)
# historical data request
if type(symbols) == str:
symbol = symbols
alpaca_data = rest_api.get_bars(symbol, timeframe, start, end, adjustment='all').df
data = bt.feeds.PandasData(dataname=alpaca_data, name=symbol)
cerebro.adddata(data)
elif type(symbols) == list or type(symbols) == set:
for symbol in symbols:
alpaca_data = rest_api.get_bars(symbol, timeframe, start, end, adjustment='all').df
data = bt.feeds.PandasData(dataname=alpaca_data, name=symbol)
cerebro.adddata(data)
# run
initial_portfolio_value = cerebro.broker.getvalue()
print(f'Starting Portfolio Value: {initial_portfolio_value}')
results = cerebro.run()
final_portfolio_value = cerebro.broker.getvalue()
print(
f'Final Portfolio Value: {final_portfolio_value} ---> Return: {(final_portfolio_value / initial_portfolio_value - 1) * 100}%')
run_backtest(EMACross, 'QQQ', '2018-01-01', '2022-01-01', TimeFrame(1, TimeFrameUnit.Day))
Running the script, i get this error:
Traceback (most recent call last):
File "/Users/usrname/PycharmProjects/test3/main.py", line 79, in <module>
run_backtest(EMACross, 'QQQ', '2018-01-01', '2022-01-01', TimeFrame(1, TimeFrameUnit.Day))
File "/Users/usrname/PycharmProjects/test3/main.py", line 54, in run_backtest
cerebro.optstrategy(strategy, ema_short_period=4, ema_long_period=6)
File "/Users/usrname/PycharmProjects/test3/venv/lib/python3.10/site-packages/backtrader/cerebro.py", line 893, in optstrategy
vals = self.iterize(kwargs.values())
File "/Users/usrname/PycharmProjects/test3/venv/lib/python3.10/site-packages/backtrader/cerebro.py", line 333, in iterize
elif not isinstance(elem, collections.Iterable):
AttributeError: module 'collections' has no attribute 'Iterable'
Process finished with exit code 1
When running the script without optstrategy() but rather with addstrategy(), evrything is working great. Only when changing to optstrategy is when i'm getting this error.
I also tried to run the same code on Google colab (with optstrategy() method) and everything worked great there, so this got me really puzzled...
I'm running python 3.10 with PyCharm CE on macOS.
Try downgrading from python 3.10 to python 3.8 version

Run Function at Given DateTime [duplicate]

How can I run a function in Python, at a given time?
For example:
run_it_at(func, '2012-07-17 15:50:00')
and it will run the function func at 2012-07-17 15:50:00.
I tried the sched.scheduler, but it didn't start my function.
import time as time_module
scheduler = sched.scheduler(time_module.time, time_module.sleep)
t = time_module.strptime('2012-07-17 15:50:00', '%Y-%m-%d %H:%M:%S')
t = time_module.mktime(t)
scheduler_e = scheduler.enterabs(t, 1, self.update, ())
What can I do?
Reading the docs from http://docs.python.org/py3k/library/sched.html:
Going from that we need to work out a delay (in seconds)...
from datetime import datetime
now = datetime.now()
Then use datetime.strptime to parse '2012-07-17 15:50:00' (I'll leave the format string to you)
# I'm just creating a datetime in 3 hours... (you'd use output from above)
from datetime import timedelta
run_at = now + timedelta(hours=3)
delay = (run_at - now).total_seconds()
You can then use delay to pass into a threading.Timer instance, eg:
threading.Timer(delay, self.update).start()
Take a look at the Advanced Python Scheduler, APScheduler: http://packages.python.org/APScheduler/index.html
They have an example for just this usecase:
http://packages.python.org/APScheduler/dateschedule.html
from datetime import date
from apscheduler.scheduler import Scheduler
# Start the scheduler
sched = Scheduler()
sched.start()
# Define the function that is to be executed
def my_job(text):
print text
# The job will be executed on November 6th, 2009
exec_date = date(2009, 11, 6)
# Store the job in a variable in case we want to cancel it
job = sched.add_date_job(my_job, exec_date, ['text'])
Might be worth installing this library: https://pypi.python.org/pypi/schedule, basically helps do everything you just described. Here's an example:
import schedule
import time
def job():
print("I'm working...")
schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)
while True:
schedule.run_pending()
time.sleep(1)
Here's an update to stephenbez' answer for version 3.5 of APScheduler using Python 2.7:
import os, time
from apscheduler.schedulers.background import BackgroundScheduler
from datetime import datetime, timedelta
def tick(text):
print(text + '! The time is: %s' % datetime.now())
scheduler = BackgroundScheduler()
dd = datetime.now() + timedelta(seconds=3)
scheduler.add_job(tick, 'date',run_date=dd, args=['TICK'])
dd = datetime.now() + timedelta(seconds=6)
scheduler.add_job(tick, 'date',run_date=dd, kwargs={'text':'TOCK'})
scheduler.start()
print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C'))
try:
# This is here to simulate application activity (which keeps the main thread alive).
while True:
time.sleep(2)
except (KeyboardInterrupt, SystemExit):
# Not strictly necessary if daemonic mode is enabled but should be done if possible
scheduler.shutdown()
I've confirmed the code in the opening post works, just lacking scheduler.run(). Tested and it runs the scheduled event. So that is another valid answer.
>>> import sched
>>> import time as time_module
>>> def myfunc(): print("Working")
...
>>> scheduler = sched.scheduler(time_module.time, time_module.sleep)
>>> t = time_module.strptime('2020-01-11 13:36:00', '%Y-%m-%d %H:%M:%S')
>>> t = time_module.mktime(t)
>>> scheduler_e = scheduler.enterabs(t, 1, myfunc, ())
>>> scheduler.run()
Working
>>>
I ran into the same issue: I could not get absolute time events registered with sched.enterabs to be recognized by sched.run. sched.enter worked for me if I calculated a delay, but is awkward to use since I want jobs to run at specific times of day in particular time zones.
In my case, I found that the issue was that the default timefunc in the sched.scheduler initializer is not time.time (as in the example), but rather is time.monotonic. time.monotonic does not make any sense for "absolute" time schedules as, from the docs, "The reference point of the returned value is undefined, so that only the difference between the results of consecutive calls is valid."
The solution for me was to initialize the scheduler as
scheduler = sched.scheduler(time.time, time.sleep)
It is unclear whether your time_module.time is actually time.time or time.monotonic, but it works fine when I initialize it properly.
dateSTR = datetime.datetime.now().strftime("%H:%M:%S" )
if dateSTR == ("20:32:10"):
#do function
print(dateSTR)
else:
# do something useful till this time
time.sleep(1)
pass
Just looking for a Time of Day / Date event trigger:
as long as the date "string" is tied to an updated "time" string, it works as a simple TOD function. You can extend the string out to a date and time.
whether its lexicographical ordering or chronological order comparison,
as long as the string represents a point in time, the string will too.
someone kindly offered this link:
String Comparison Technique Used by Python
had a really hard time getting these answers to work how i needed it to,
but i got this working and its accurate to .01 seconds
from apscheduler.schedulers.background import BackgroundScheduler
sched = BackgroundScheduler()
sched.start()
def myjob():
print('job 1 done at: ' + str(dt.now())[:-3])
dt = datetime.datetime
Future = dt.now() + datetime.timedelta(milliseconds=2000)
job = sched.add_job(myjob, 'date', run_date=Future)
tested accuracy of timing with this code:
at first i did 2 second and 5 second delay, but wanted to test it with a more accurate measurement so i tried again with 2.55 second delay and 5.55 second delay
dt = datetime.datetime
Future = dt.now() + datetime.timedelta(milliseconds=2550)
Future2 = dt.now() + datetime.timedelta(milliseconds=5550)
def myjob1():
print('job 1 done at: ' + str(dt.now())[:-3])
def myjob2():
print('job 2 done at: ' + str(dt.now())[:-3])
print(' current time: ' + str(dt.now())[:-3])
print(' do job 1 at: ' + str(Future)[:-3] + '''
do job 2 at: ''' + str(Future2)[:-3])
job = sched.add_job(myjob1, 'date', run_date=Future)
job2 = sched.add_job(myjob2, 'date', run_date=Future2)
and got these results:
current time: 2020-12-10 19:50:44.632
do job 1 at: 2020-12-10 19:50:47.182
do job 2 at: 2020-12-10 19:50:50.182
job 1 done at: 2020-12-10 19:50:47.184
job 2 done at: 2020-12-10 19:50:50.183
accurate to .002 of a second with 1 test
but i did run a lot of tests and accuracy ranged from .002 to .011
never going under the 2.55 or 5.55 second delay
#everytime you print action_now it will check your current time and tell you should be done
import datetime
current_time = datetime.datetime.now()
current_time.hour
schedule = {
'8':'prep',
'9':'Note review',
'10':'code',
'11':'15 min teabreak ',
'12':'code',
'13':'Lunch Break',
'14':'Test',
'15':'Talk',
'16':'30 min for code ',
'17':'Free',
'18':'Help ',
'19':'watever',
'20':'watever',
'21':'watever',
'22':'watever'
}
action_now = schedule[str(current_time.hour)]

Multithreading makes my code having some issues

I have tried to implement multithreading into a tool I've written, but I'm getting unexpected results.
Here is the working code:
#Import modules
import os
import time
import sys
#Params
print("___________Auto ShutApp___________\n")
print("Type the AppName then the time before app will shut")
apptokill = str(input("\nWhich App you'd like to Shut: "))
Time = int(input("\nHow long before App should stop(in minutes): "))
def timeS(arg): #arg = time in seconds
time.sleep(arg*60)
def killer(apptokill, Time):
timeS(Time)
os.system("pkill %s"%(apptokill))
killer(apptokill, Time)
This works fine, but I wanted to know when it would kill an app so I added a new function
def Timer():
time_start = time.time()
seconds = 0
minutes = 0
while True:
try:
sys.stdout.write("\rTimer have start since {minutes} Minutes {seconds} Seconds".format(minutes=minutes, seconds=seconds))
sys.stdout.flush()
time.sleep(1)
seconds = int(time.time() - time_start) - minutes * 60
if seconds >= 60:
minutes += 1
seconds = 0
except KeyboardInterrupt:
break
This one also works well, but if I place the function it will run either killer or Timer - not both.
I then tried some mutlithreading, adding the code below:
if __name__ == "__main__":
t1 = threading.Thread(target=killer, name='t1')
t2 = threading.Thread(target=Timer, name='t2')
t1.start()
t2.start()
t1.join()
t2.join()
but from this code I get this error:
Traceback (most recent call last):
File "/usr/lib/python3.7/threading.py", line 926, in _bootstrap_inner
self.run()
File "/usr/lib/python3.7/threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
TypeError: killer() missing 2 required positional arguments: 'apptokill' and 'Time'
I assumed apptokill is saved in memory and so Time as well. I'm completely lost.
You have to provide arguments to killer
When calling by hand you are doing it like this
killer(apptokill, Time)
When starting killer in a thread it still needs to know what to use as arguments. The solution would look like this:
t1 = threading.Thread(target=killer, name='t1', args=(apptokill, Time))

How can I calculate the time taken by a function to get executed with python?

I called a ping command with subprocess. My calculated time difference is lower than the operational time.
latency: 361.953 [actual operation time]
calculated_latency: 0.7531721591949463 [calculated with python]
If python executes line by line, then calculated latency would be higher than the original one. I am not sure what is going on and how can I calculate the time of a function call.
import subprocess
import shlex
import re
import time
from datetime import datetime
command_line = "ping -c 1 {}".format('yahoo.com')
args = shlex.split(command_line)
try:
start = time.time()
output = subprocess.check_output(args)
end = time.time()
ping_latency_calculated = end - start
result = str(output).strip("b'")
m = re.search(r"\/(\d+\.\d+)\/", result)
ping_latency = m.group(1)
result = {'latency': ping_latency, 'calculated_latency': ping_latency_calculated}
except subprocess.CalledProcessError as e:
result = {'Status': 'Ping Failed', 'time': datetime.fromtimestamp(time.time())}
return result
calculated_latency is counted in seconds and latency is counted in milliseconds.
So the result you get actually is:
latency: 361.953 ms
calculated_latency: 753.1721591949463 ms

Python sched scheduler and reboots

I have read about python sched (task scheduler), it works like a cron.
but I have a question :
lets say if I schedule a function to run after every 2 hours and in the mean time my system gets shut down, then I again restart the system...
Does the scheduler automatically start and run the function after 2 hours? Or do I have to start that again after shutting down the system?
Does sched work like a daemon?
Answer to all three questions is No.
sched is different from cron. It takes a generic timer or counter function and a delay function and lets you to schedule a function call after a particular time (an event as defined by your generic timer function).
It won't run after you close your program, unless you maintain state by writing to a file or db. This is complicated and using cron would be better.
sched works on events, but not on background. so, it not is not exactly a deamon, but you can deamonize it running the program in the background using OS facilities.
and if this is so :
will this also work even after system restart?
answer is : NO then how can turbogear scheduler can run using cronos in cron? scheduled events in turbogear will also be vanished after SYSTEM restart.
PLEASE CORRECT ME IF I AM WRONG.
import time
import sched
import datetime
import threading
import calendar
#from datetime import datetime
class test:
def __init__(self):
self.name = ''
def getSec(self):
now = datetime.datetime.now()
print "now - ", now
currentYear = now.year
currentMonth = now.month
currentDay = now.day
currentHour = now.hour
currentMinute = now.minute
currentSecond = now.second
currentMicroseconds = now.microsecond
command = "python runbackup.py"
print "command is - ", command
print "currentMinute - ", currentMinute
print "currentSecond - ", currentSecond
# current time
a = datetime.datetime(currentYear, currentMonth, currentDay, currentHour, currentMinute, currentSecond, currentMicroseconds)
last_date_of_current_month = calendar.monthrange(currentYear, currentMonth)[1]
print "last_date_of_current_month - ", last_date_of_current_month
b = datetime.datetime(currentYear, currentMonth, int(last_date_of_current_month), 23, 59, 59, 000000)
#b = datetime.datetime(currentYear, currentMonth, int(29), 18, 29, 00, 000000)
#print "date time of b is - %s %s " % (18, 29)
c = b-a
print "c is - ", c
time.sleep(1)
scheduler = sched.scheduler(time.time, time.sleep)
#scheduler.cancel(e1)
sec = c.seconds
print "second - ", sec
print "scheduler entered."
e1 = scheduler.enter(sec, 1, self.getSec, ())
t = threading.Thread(target=scheduler.run)
print "thread started."
print "======================================"
t.start()
#scheduler.cancel(e1)
#print "canceled."
return True
if __name__=='__main__' :
obj = test()
obj.getSec()

Categories