forked from nautechsystems/nautilus_trader
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ema_python.py
130 lines (102 loc) · 4.17 KB
/
ema_python.py
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
# -------------------------------------------------------------------------------------------------
# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
# You may not use this file except in compliance with the License.
# You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -------------------------------------------------------------------------------------------------
from nautilus_trader.core.correctness import PyCondition
from nautilus_trader.indicators.base.indicator import Indicator
from nautilus_trader.model.data.bar import Bar
from nautilus_trader.model.data.tick import QuoteTick
from nautilus_trader.model.data.tick import TradeTick
from nautilus_trader.model.enums import PriceType
# It's generally recommended to code indicators in Cython as per the built-in
# indicators found in the `indicators` subpackage. However this is an example
# demonstrating an equivalent EMA indicator written in Python.
# Note: The `MovingAverage` base class has not being used in this example to
# provide more clarity on how to implement custom indicators. Basically you need
# to inherit from `Indicator` and override the methods shown below.
class PyExponentialMovingAverage(Indicator):
"""
An indicator which calculates an exponential moving average across a
rolling window.
Parameters
----------
period : int
The rolling window period for the indicator (> 0).
price_type : PriceType
The specified price type for extracting values from quote ticks.
Raises
------
ValueError
If `period` is not positive (> 0).
"""
def __init__(self, period: int, price_type: PriceType = PriceType.LAST):
PyCondition.positive_int(period, "period")
super().__init__(params=[period])
self.period = period
self.price_type = price_type
self.alpha = 2.0 / (period + 1.0)
self.value = 0.0 # <-- stateful value
self.count = 0 # <-- stateful value
def handle_quote_tick(self, tick: QuoteTick):
"""
Update the indicator with the given quote tick.
Parameters
----------
tick : QuoteTick
The update tick to handle.
"""
PyCondition.not_none(tick, "tick")
self.update_raw(tick.extract_price(self.price_type).as_double())
def handle_trade_tick(self, tick: TradeTick):
"""
Update the indicator with the given trade tick.
Parameters
----------
tick : TradeTick
The update tick to handle.
"""
PyCondition.not_none(tick, "tick")
self.update_raw(tick.price.as_double())
def handle_bar(self, bar: Bar):
"""
Update the indicator with the given bar.
Parameters
----------
bar : Bar
The update bar to handle.
"""
PyCondition.not_none(bar, "bar")
self.update_raw(bar.close.as_double())
def update_raw(self, value: float):
"""
Update the indicator with the given raw value.
Parameters
----------
value : double
The update value.
"""
# Check if this is the initial input
if not self.has_inputs:
self.value = value
self.value = self.alpha * value + ((1.0 - self.alpha) * self.value)
self.count += 1
# Initialization logic
if not self.initialized:
self._set_has_inputs(True)
if self.count >= self.period:
self._set_initialized(True)
def _reset(self):
# Override this method to reset stateful values introduced in the class.
# This method will be called by the base when `.reset()` is called.
self.value = 0.0
self.count = 0