How to properly overload the __add__ method? - python

I am required to write a class involving dates. I am supposed to overload the + operator to allow days being added to dates. To explain how it works: A Date object is represented as (2016, 4, 15) in the format (year, month, date). Adding integer 10 to this should yield (2016, 4, 25). The Date class has values self.year, self.month, self.day.
My problem is that the code is supposed to work in the form Date + 10 as well as 10 + Date. Also Date - 1 should work in the sense of adding a negative number of days. Date(2016, 4, 25) - 1 returns Date(2016, 4, 24).
My code works perfectly in the form of Date + 10 but not in the form 10 + D or D - 1.
def __add__(self,value):
if type(self) != int and type(self) != Date or (type(value) != int and type(value) != Date):
raise TypeError
if type(self) == Date:
day = self.day
month = self.month
year = self.year
value = value
if type(value) != int:
raise TypeError
days_to_add = value
while days_to_add > 0:
day+=1
if day == Date.days_in(year,month):
month+=1
if month > 12:
day = 0
month = 1
year+=1
day = 0
days_to_add -=1
return(Date(year,month,day))
These are the errors I get
TypeError: unsupported operand type(s) for +: 'int' and 'Date'
TypeError: unsupported operand type(s) for -: 'Date' and 'int'

__radd__ handles right side addition so you need to implement that as well.
I am seeing some flaws in your implementation so I recommend you using datetime module (especially datetime.timedelta class) to at least handle basic date arithmetic correctly:
import datetime
class Date(object):
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def as_date(self):
return datetime.date(self.year, self.month, self.day)
def __add__(self, other):
if isinstance(other, int):
date = self.as_date() + datetime.timedelta(days=other)
return Date(date.year, date.month, date.day)
else:
raise ValueError("int value is required")
def __radd__(self, other):
return self.__add__(other)
def __sub__(self, other):
return self.__add__(-other)
def __rsub__(self, other):
raise RuntimeError("Doesn't make sense.")
def __repr__(self):
return str(self.as_date())
Demo:
>>> date = Date(2015, 10, 23)
>>> print date + 10 # __add__ is called
2015-11-02
>>> print 20 + date # __radd__ is called
2015-11-12
>>> print date - 25 # __sub__ is called
2015-09-28
>>> print 25 - date # __rsub__ is called
RuntimeError: Doesn't make sense

Related

how to get a price that the order was submitted backtester python

Here is my code
class TestStrategy(bt.Strategy):
params = dict(
stop_loss=0.02, # price is 2% less than the entry point
trail=False,
)
def log(self, txt, dt=None):
''' Logging function fot this strategy'''
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def __init__(self):
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
self.bearish = pd.read_csv('bearish_comments.csv')
self.bullish = pd.read_csv('bullish_comments.csv')
self.bearish['dt'] = pd.to_datetime(self.bearish['dt'])
self.bullish['dt'] = pd.to_datetime(self.bullish['dt'])
self.bullish['tt'] = np.where(self.bullish['TSLA'] > (2 * self.bearish['TSLA']), True, False)
self.order = None
self.buyprice = None
self.buycomm = None
def notify_order(self, order):
self.order = None
def next(self):
dt = self.datas[0].datetime.date(0)
# Simply log the closing price of the series from the reference
buy = self.bullish[self.bullish['dt'].isin(
[datetime.datetime(dt.year, dt.month, dt.day, 0, 0, 0, 0),
datetime.datetime(dt.year, dt.month, dt.day, 23, 59, 59, 99)])].iloc[0].tt
self.log('Close, %.2f' % self.dataclose[0])
print(buy)
if self.order:
return
if not self.position:
if buy:
# BUY, BUY, BUY!!! (with all possible default parameters)
self.log('BUY CREATE, %.2f' % self.dataclose[0])
self.order = self.buy()
print(self.order)
else:
if buy == False:
if self.dataclose[0] >= ((self.order * 0.01) + self.order):
# TAKE PROFIT!!! (with all possible default parameters)
self.log('TAKE PROFIT, %.2f' % self.dataclose[0])
self.order = self.sell()
So for line
if self.dataclose[0] >= ((self.order * 0.01) + self.order):
when I use self.order it gives an error TypeError: unsupported operand type(s) for *: 'NoneType' and 'float'
So what I want to do is to get price at what the order was bought and then take profit when the bought price increase with 1%
The error is self-explanatory: the variable self.order is None, so it has not been assigned a value yet.
From what I see, that must happen when you sell before having bought.

SQLAlchemy window frames as time interval

Is there any way in SQLAlchemy to specify window frame as time interval like this?
OVER(
PARTITION BY some_col
ORDER BY other_date_type_col
RANGE BETWEEN '30 days'::INTERVAL PRECEDING AND CURRENT ROW
)
There's a method sqlalchemy.sql.functions.FunctionElement.over(partition_by=None, order_by=None, rows=None, range_=None) in their docs. By it takes only numeric data as range_.
As of SQLAlchemy 1.4.25, there is no built-in support.
Workaround
Implement a str subclass that overrides __abs__ and __lt__.
class RangeDays(str):
def __new__(cls, x):
obj = super().__new__(cls, f"{abs(x)} day" if abs(x) == 1 else f"{abs(x)} days")
obj.x = x
return obj
def __abs__(self):
# abs(range_[0]) called in SQLCompiler._format_frame_clause
return self
def __lt__(self, other):
# range_[0] < 0 called in SQLCompiler._format_frame_clause
return self.x.__lt__(other)
Patch Over._interpret_range to handle RangeDays.
from sqlalchemy.sql.elements import Over
_old_interpret_range = Over._interpret_range
def _interpret_range(self, range_):
lower, lower_ = (None, range_[0]) if isinstance(range_[0], RangeDays) else (range_[0], None)
upper, upper_ = (None, range_[1]) if isinstance(range_[1], RangeDays) else (range_[1], None)
lower, upper = _old_interpret_range(self, (lower, upper))
return lower_ or lower, upper_ or upper
Over._interpret_range = _interpret_range
Usage:
# '30 days' PRECEDING AND CURRENT ROW
range_=(RangeDays(-30), 0)
# '1 day' PRECEDING AND '10 days' FOLLOWING
range_=(RangeDays(-1), RangeDays(10))

Overload __init__() for a subclass of Enum

I'm trying to overload the __init__() method of a subclass of an enum. Strangely, the pattern that work with a normal class doesn't work anymore with Enum.
The following show the desired pattern working with a normal class:
class Integer:
def __init__(self, a):
"""Accepts only int"""
assert isinstance(a, int)
self.a = a
def __repr__(self):
return str(self.a)
class RobustInteger(Integer):
def __init__(self, a):
"""Accepts int or str"""
if isinstance(a, str):
super().__init__(int(a))
else:
super().__init__(a)
print(Integer(1))
# 1
print(RobustInteger(1))
# 1
print(RobustInteger('1'))
# 1
The same pattern then breaks if used with an Enum:
from enum import Enum
from datetime import date
class WeekDay(Enum):
MONDAY = 0
TUESDAY = 1
WEDNESDAY = 2
THURSDAY = 3
FRIDAY = 4
SATURDAY = 5
SUNDAY = 6
def __init__(self, value):
"""Accepts int or date"""
if isinstance(value, date):
super().__init__(date.weekday())
else:
super().__init__(value)
assert WeekDay(0) == WeekDay.MONDAY
assert WeekDay(date(2019, 4, 3)) == WeekDay.MONDAY
# ---------------------------------------------------------------------------
# TypeError Traceback (most recent call last)
# /path/to/my/test/file.py in <module>()
# 27
# 28
# ---> 29 class WeekDay(Enum):
# 30 MONDAY = 0
# 31 TUESDAY = 1
# /path/to/my/virtualenv/lib/python3.6/enum.py in __new__(metacls, cls, bases, classdict)
# 208 enum_member._name_ = member_name
# 209 enum_member.__objclass__ = enum_class
# --> 210 enum_member.__init__(*args)
# 211 # If another member with the same value was already defined, the
# 212 # new member becomes an alias to the existing one.
# /path/to/my/test/file.py in __init__(self, value)
# 40 super().__init__(date.weekday())
# 41 else:
# ---> 42 super().__init__(value)
# 43
# 44
# TypeError: object.__init__() takes no parameters
You have to overload the _missing_ hook. All instances of WeekDay are created when the class is first defined; WeekDay(date(...)) is an indexing operation rather than a creation operation, and __new__ is initially looking for pre-existing values bound to the integers 0 to 6. Failing that, it calls _missing_, in which you can convert the date object into such an integer.
class WeekDay(Enum):
MONDAY = 0
TUESDAY = 1
WEDNESDAY = 2
THURSDAY = 3
FRIDAY = 4
SATURDAY = 5
SUNDAY = 6
#classmethod
def _missing_(cls, value):
if isinstance(value, date):
return cls(value.weekday())
return super()._missing_(value)
A few examples:
>>> WeekDay(date(2019,3,7))
<WeekDay.THURSDAY: 3>
>>> assert WeekDay(date(2019, 4, 1)) == WeekDay.MONDAY
>>> assert WeekDay(date(2019, 4, 3)) == WeekDay.MONDAY
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
(Note: _missing_ is not available prior to Python 3.6.)
Prior to 3.6, it seems you can override EnumMeta.__call__ to make the same check, but I'm not sure if this will have unintended side effects. (Reasoning about __call__ always makes my head spin a little.)
# Silently convert an instance of datatime.date to a day-of-week
# integer for lookup.
class WeekDayMeta(EnumMeta):
def __call__(cls, value, *args, **kwargs):
if isinstance(value, date):
value = value.weekday())
return super().__call__(value, *args, **kwargs)
class WeekDay(Enum, metaclass=WeekDayMeta):
MONDAY = 0
TUESDAY = 1
WEDNESDAY = 2
THURSDAY = 3
FRIDAY = 4
SATURDAY = 5
SUNDAY = 6
There is a much better answer, but I post this anyway as it might be helpful for understanding the issue.
The docs gives this hint:
EnumMeta creates them all while it is creating the Enum class itself,
and then puts a custom new() in place to ensure that no new ones
are ever instantiated by returning only the existing member instances.
So we have to wait with redefining __new__ until the class is created. With some ugly patching this passes the test:
from enum import Enum
from datetime import date
class WeekDay(Enum):
MONDAY = 0
TUESDAY = 1
WEDNESDAY = 2
THURSDAY = 3
FRIDAY = 4
SATURDAY = 5
SUNDAY = 6
wnew = WeekDay.__new__
def _new(cls, value):
if isinstance(value, date):
return wnew(cls, value.weekday()) # not date.weekday()
else:
return wnew(cls, value)
WeekDay.__new__ = _new
assert WeekDay(0) == WeekDay.MONDAY
assert WeekDay(date(2019, 3, 4)) == WeekDay.MONDAY # not 2019,4,3

Why does my class raise an AttributeError?

I might be blind, but I really can't see why this class fails with:
AttributeError: NextSunday instance has no attribute 'trigger'
from datetime import datetime
from datetime import time
from datetime import timedelta
class NextSunday():
def __init__(self, trigger=None):
"""
Get date of, and number of days until, next Sunday.
Arguments:
- `trigger`: Add an extra week if less than trigger number of days.
"""
self.str = u'%s (%s days)' % (self.date().date(), self.no_days())
self.trigger = trigger
def __unicode__(self):
return self.str
def __str__(self):
return unicode(self).encode('utf-8')
def __repr__(self):
return '<Next Sunday: ' + self.str + '>'
def no_days(self):
"""Get date of next sunday. """
days = None
for i in range(7):
dt = datetime.now() + timedelta(days=i)
if dt.weekday() == 6:
days = i
# Add another week if there are less days left then trigger
if self.trigger:
if days < self.trigger:
days += 7
return days
def date(self):
# The datetime obj contains the next sunday, but also the current time
dt_of_next_sun = datetime.now() + timedelta(days=self.no_days())
# Get the whole day
date_of_next_sun = datetime.combine(dt_of_next_sun.date(),
time(23, 59))
return date_of_next_sun
You need to switch these
self.str = u'%s (%s days)' % (self.date().date(), self.no_days())
self.trigger = trigger
like this
self.trigger = trigger
self.str = u'%s (%s days)' % (self.date().date(), self.no_days())
because otherwise the no_days method is called before the self.trigger attribute is created. This is bad because the no_days method tries to read the value of the self.trigger attribute:
if self.trigger:
if days < self.trigger:

why do I get unsupported operand type(s) type error?

ok, so I got a little exercise for python and I want to find the values of speed and location at every second from 0 to 30th within a list. also it must notify me when they intersect. while finding the location of the first car I am to use the method of Trapezoid code only.
the speed equation of the first car is V1=t^3-3*t^2+2*t and the second is V2=10*t
my code:
def cars():
def LocationV1(x):
x=x*1.0
h=x/1000.0
m=0
n=m+h
L=0.0
for i in range (0,1000):
def f(u):
return u**3+3*u**2+2*u
L=L+(f(m)+f(n))*h/2
m=m+h
n=n+h
return L
def LocationV2(x):
x=x*1.0
def g(x):
return 5*x**2/2
def SpeedV1 (x):
x=x*1.0
return x**3-3*x**2+2*x
def SpeedV2 (x):
x=x*1.0
return 10*x
V1=[]
V2=[]
t=0
a=LocationV1(t)
b=LocationV2(t)
while t<=30:
t=t+1
V1=V1+[[LocationV1(t), SpeedV1(t), t]]
V2=V2+[[LocationV2(t), SpeedV2(t), t]]
print V1
print V2
if (a-b)<=0.1:
print "cars meet"
when I use this code it gives me such an error:
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
cars()
File "C:/Users/ÖZGÜR/Desktop/ödev", line 35, in cars
if (a-b)<=1:
TypeError: unsupported operand type(s) for -: 'int' and 'NoneType'
what do I do now?
I don't know Python, but your function LocationV2() doesn't seem to be returning anything.
b=LocationV2(t)
The problem is, this returns None and hence a-b gives the error you are getting.
def LocationV2(x):
x=x*1.0
def g(x):
return 5*x**2/2
should really be:
def LocationV2(x):
x=x*1.0
return 5*x**2/2
And this should fix your problem.
As far as I can see you are doing
b = LocationV2(t)
and LocationV2 does not return anything, so it implicitly returns NoneType. Then you call
if (a-b)<=0.1:
and it is trying to subtract NoneType from an int. In fact, I have no idea what LocationV2 actually does. It seems to multiply x by 1. Return the value from LocationV2 and you should be fine.
The LocationV2 function does not return explicitly return a value, thus Python runtime make it return None.
LocationV2 does not return anything, therefore b is None.
A grossly over-engineered version:
rng = xrange if xrange else range
class Polynomial(object):
def __init__(self, *args):
"""
Args are coefficients of x**0, x**1, x**2, ...
ie, Polynomial(3,0,2) is 2*(x**2) + 3
"""
self.coeffs = [float(a) for a in args]
def __call__(self, x):
"Evaluate poly at x"
return sum(a*(x**n) for n,a in enumerate(self.coeffs))
def integrate(self, yAtZero=0.0):
"Do symbolic integration of poly, return result as new poly"
newCoeffs = [yAtZero] + [a/(n+1.0) for n,a in enumerate(self.coeffs)]
return Polynomial(*newCoeffs)
class TrapezoidIntegrator(object):
def __init__(self, fn, steps=1000):
self.fn = fn
self.steps = int(steps)
def __call__(self, x):
"Integrate fn from 0 to x in steps pieces"
myfn = self.fn
w = float(x)/self.steps
return -0.5*w*myfn(0.0) + w*sum(myfn(w*step) for step in rng(self.steps)) + 0.5*w*myfn(x)
class Car(object):
def __init__(self, posFn, speedFn):
self.pos = posFn
self.speed = speedFn
def at(self, t):
return self.pos(t), self.speed(t)
class Ahead(object):
def __init__(self, fmt, *args):
"""
#param fmt, string: format-string with one parameter, to report a change in leader
#param args, strings: contestant names
"""
self.was_ahead = None
self.fmt = fmt
self.names = list(args)
def __call__(self, *args):
"Compare an arbitrary number of racers and report whenever the leader changes"
state = zip(args, self.names) # we assume that len(args)==len(self.names)
state.sort(reverse=True, key=lambda x: x[0])
leader = state[0][1]
if leader==self.was_ahead:
return ''
else:
self.was_ahead = leader
return self.fmt.format(leader)
def niceFloat(val, width, decimals):
"""
Really wretchedly annoying - I have not yet found a nice way to do
decimal-aligned floats with the new-style string formatting.
"""
fmt = "%{0}.{1}f".format(width, decimals)
return fmt % val
def main():
v1 = Polynomial(0,2,-3,1) # speed of first car = t^3 - 3t^2 + 2t
d1 = TrapezoidIntegrator(v1) # must use trapezoidal numeric integration
car1 = Car(d1, v1)
v2 = Polynomial(0,10) # speed of second car is 10t
d2 = v2.integrate() # use symbolic integration
car2 = Car(d2, v2)
fmt = "{0:>4}: {1:>10} {2:>10}{3}"
print(fmt.format('Time','Car1','Car2',''))
ahead = Ahead(' Car {0} is in the lead!', 1, 2)
log = []
for t in rng(31):
a, da = car1.at(t)
b, db = car2.at(t)
print(fmt.format(t, niceFloat(a,10,2), niceFloat(b,10,2), ahead(a,b)))
log.append((t,a,da,b,db))
if __name__=="__main__":
main()
which results in:
Time: Car1 Car2
0: 0.00 0.00 Car 1 is in the lead!
1: 0.25 5.00 Car 2 is in the lead!
2: 0.00 20.00
3: 2.25 45.00
4: 16.00 80.00
5: 56.25 125.00
6: 144.00 180.00
7: 306.25 245.00 Car 1 is in the lead!
8: 576.00 320.00
9: 992.25 405.00
10: 1600.00 500.00
11: 2450.25 605.00
12: 3600.00 720.00
13: 5112.26 845.00
14: 7056.01 980.00
15: 9506.26 1125.00
16: 12544.01 1280.00
17: 16256.27 1445.00
18: 20736.02 1620.00
19: 26082.28 1805.00
20: 32400.04 2000.00
21: 39800.29 2205.00
22: 48400.05 2420.00
23: 58322.31 2645.00
24: 69696.08 2880.00
25: 82656.34 3125.00
26: 97344.11 3380.00
27: 113906.37 3645.00
28: 132496.14 3920.00
29: 153272.41 4205.00
30: 176400.19 4500.00

Categories