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