forked from mementum/backtrader
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlineroot.py
More file actions
359 lines (276 loc) · 10.4 KB
/
lineroot.py
File metadata and controls
359 lines (276 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2020 Daniel Rodriguez
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
'''
.. module:: lineroot
Definition of the base class LineRoot and base classes LineSingle/LineMultiple
to define interfaces and hierarchy for the real operational classes
.. moduleauthor:: Daniel Rodriguez
'''
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import operator
from .utils.py3 import range, with_metaclass
from . import metabase
class MetaLineRoot(metabase.MetaParams):
'''
Once the object is created (effectively pre-init) the "owner" of this
class is sought
'''
def donew(cls, *args, **kwargs):
_obj, args, kwargs = super(MetaLineRoot, cls).donew(*args, **kwargs)
# Find the owner and store it
# startlevel = 4 ... to skip intermediate call stacks
ownerskip = kwargs.pop('_ownerskip', None)
_obj._owner = metabase.findowner(_obj,
_obj._OwnerCls or LineMultiple,
skip=ownerskip)
# Parameter values have now been set before __init__
return _obj, args, kwargs
class LineRoot(with_metaclass(MetaLineRoot, object)):
'''
Defines a common base and interfaces for Single and Multiple
LineXXX instances
Period management
Iteration management
Operation (dual/single operand) Management
Rich Comparison operator definition
'''
_OwnerCls = None
_minperiod = 1
_opstage = 1
IndType, StratType, ObsType = range(3)
def _stage1(self):
self._opstage = 1
def _stage2(self):
self._opstage = 2
def _operation(self, other, operation, r=False, intify=False):
if self._opstage == 1:
return self._operation_stage1(
other, operation, r=r, intify=intify)
return self._operation_stage2(other, operation, r=r)
def _operationown(self, operation):
if self._opstage == 1:
return self._operationown_stage1(operation)
return self._operationown_stage2(operation)
def qbuffer(self, savemem=0):
'''Change the lines to implement a minimum size qbuffer scheme'''
raise NotImplementedError
def minbuffer(self, size):
'''Receive notification of how large the buffer must at least be'''
raise NotImplementedError
def setminperiod(self, minperiod):
'''
Direct minperiod manipulation. It could be used for example
by a strategy
to not wait for all indicators to produce a value
'''
self._minperiod = minperiod
def updateminperiod(self, minperiod):
'''
Update the minperiod if needed. The minperiod will have been
calculated elsewhere
and has to take over if greater that self's
'''
self._minperiod = max(self._minperiod, minperiod)
def addminperiod(self, minperiod):
'''
Add a minperiod to own ... to be defined by subclasses
'''
raise NotImplementedError
def incminperiod(self, minperiod):
'''
Increment the minperiod with no considerations
'''
raise NotImplementedError
def prenext(self):
'''
It will be called during the "minperiod" phase of an iteration.
'''
pass
def nextstart(self):
'''
It will be called when the minperiod phase is over for the 1st
post-minperiod value. Only called once and defaults to automatically
calling next
'''
self.next()
def next(self):
'''
Called to calculate values when the minperiod is over
'''
pass
def preonce(self, start, end):
'''
It will be called during the "minperiod" phase of a "once" iteration
'''
pass
def oncestart(self, start, end):
'''
It will be called when the minperiod phase is over for the 1st
post-minperiod value
Only called once and defaults to automatically calling once
'''
self.once(start, end)
def once(self, start, end):
'''
Called to calculate values at "once" when the minperiod is over
'''
pass
# Arithmetic operators
def _makeoperation(self, other, operation, r=False, _ownerskip=None):
raise NotImplementedError
def _makeoperationown(self, operation, _ownerskip=None):
raise NotImplementedError
def _operationown_stage1(self, operation):
'''
Operation with single operand which is "self"
'''
return self._makeoperationown(operation, _ownerskip=self)
def _roperation(self, other, operation, intify=False):
'''
Relies on self._operation to and passes "r" True to define a
reverse operation
'''
return self._operation(other, operation, r=True, intify=intify)
def _operation_stage1(self, other, operation, r=False, intify=False):
'''
Two operands' operation. Scanning of other happens to understand
if other must be directly an operand or rather a subitem thereof
'''
if isinstance(other, LineMultiple):
other = other.lines[0]
return self._makeoperation(other, operation, r, self)
def _operation_stage2(self, other, operation, r=False):
'''
Rich Comparison operators. Scans other and returns either an
operation with other directly or a subitem from other
'''
if isinstance(other, LineRoot):
other = other[0]
# operation(float, other) ... expecting other to be a float
if r:
return operation(other, self[0])
return operation(self[0], other)
def _operationown_stage2(self, operation):
return operation(self[0])
def __add__(self, other):
return self._operation(other, operator.__add__)
def __radd__(self, other):
return self._roperation(other, operator.__add__)
def __sub__(self, other):
return self._operation(other, operator.__sub__)
def __rsub__(self, other):
return self._roperation(other, operator.__sub__)
def __mul__(self, other):
return self._operation(other, operator.__mul__)
def __rmul__(self, other):
return self._roperation(other, operator.__mul__)
def __div__(self, other):
return self._operation(other, operator.__div__)
def __rdiv__(self, other):
return self._roperation(other, operator.__div__)
def __floordiv__(self, other):
return self._operation(other, operator.__floordiv__)
def __rfloordiv__(self, other):
return self._roperation(other, operator.__floordiv__)
def __truediv__(self, other):
return self._operation(other, operator.__truediv__)
def __rtruediv__(self, other):
return self._roperation(other, operator.__truediv__)
def __pow__(self, other):
return self._operation(other, operator.__pow__)
def __rpow__(self, other):
return self._roperation(other, operator.__pow__)
def __abs__(self):
return self._operationown(operator.__abs__)
def __neg__(self):
return self._operationown(operator.__neg__)
def __lt__(self, other):
return self._operation(other, operator.__lt__)
def __gt__(self, other):
return self._operation(other, operator.__gt__)
def __le__(self, other):
return self._operation(other, operator.__le__)
def __ge__(self, other):
return self._operation(other, operator.__ge__)
def __eq__(self, other):
return self._operation(other, operator.__eq__)
def __ne__(self, other):
return self._operation(other, operator.__ne__)
def __nonzero__(self):
return self._operationown(bool)
__bool__ = __nonzero__
# Python 3 forces explicit implementation of hash if
# the class has redefined __eq__
__hash__ = object.__hash__
class LineMultiple(LineRoot):
'''
Base class for LineXXX instances that hold more than one line
'''
def reset(self):
self._stage1()
self.lines.reset()
def _stage1(self):
super(LineMultiple, self)._stage1()
for line in self.lines:
line._stage1()
def _stage2(self):
super(LineMultiple, self)._stage2()
for line in self.lines:
line._stage2()
def addminperiod(self, minperiod):
'''
The passed minperiod is fed to the lines
'''
# pass it down to the lines
for line in self.lines:
line.addminperiod(minperiod)
def incminperiod(self, minperiod):
'''
The passed minperiod is fed to the lines
'''
# pass it down to the lines
for line in self.lines:
line.incminperiod(minperiod)
def _makeoperation(self, other, operation, r=False, _ownerskip=None):
return self.lines[0]._makeoperation(other, operation, r, _ownerskip)
def _makeoperationown(self, operation, _ownerskip=None):
return self.lines[0]._makeoperationown(operation, _ownerskip)
def qbuffer(self, savemem=0):
for line in self.lines:
line.qbuffer(savemem=1)
def minbuffer(self, size):
for line in self.lines:
line.minbuffer(size)
class LineSingle(LineRoot):
'''
Base class for LineXXX instances that hold a single line
'''
def addminperiod(self, minperiod):
'''
Add the minperiod (substracting the overlapping 1 minimum period)
'''
self._minperiod += minperiod - 1
def incminperiod(self, minperiod):
'''
Increment the minperiod with no considerations
'''
self._minperiod += minperiod