-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
18cad32
commit 4448f0f
Showing
3 changed files
with
98 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
from typing import Iterable, Optional, TypeVar | ||
|
||
from stock_indicators._cslib import CsIndicator | ||
from stock_indicators._cstypes import List as CsList | ||
from stock_indicators.indicators.common.results import IndicatorResults, ResultBase | ||
from stock_indicators.indicators.common.quote import Quote | ||
|
||
|
||
def get_dynamic(quotes: Iterable[Quote], lookback_periods: int, k_factor: float = 0.6): | ||
"""Get McGinley Dynamic calculated. | ||
McGinley Dynamic is a more responsive variant of exponential moving average. | ||
Parameters: | ||
`quotes` : Iterable[Quote] | ||
Historical price quotes. | ||
`lookback_periods` : int | ||
Number of periods in the lookback window. | ||
`k_factor` : float, defaults 0.6 | ||
Range adjustment factor. | ||
Returns: | ||
`DynamicResults[DynamicResult]` | ||
DynamicResults is list of DynamicResult with providing useful helper methods. | ||
See more: | ||
- [McGinley Dynamic Reference](https://python.stockindicators.dev/indicators/Dynamic/#content) | ||
- [Helper Methods](https://python.stockindicators.dev/utilities/#content) | ||
""" | ||
results = CsIndicator.GetDynamic[Quote](CsList(Quote, quotes), lookback_periods, k_factor) | ||
return DynamicResults(results, DynamicResult) | ||
|
||
|
||
class DynamicResult(ResultBase): | ||
""" | ||
A wrapper class for a single unit of McGinley Dynamic results. | ||
""" | ||
|
||
@property | ||
def dynamic(self) -> Optional[float]: | ||
return self._csdata.Dynamic | ||
|
||
@dynamic.setter | ||
def dynamic(self, value): | ||
self._csdata.Dynamic = value | ||
|
||
|
||
_T = TypeVar("_T", bound=DynamicResult) | ||
class DynamicResults(IndicatorResults[_T]): | ||
""" | ||
A wrapper class for the list of McGinley Dynamic results. | ||
It is exactly same with built-in `list` except for that it provides | ||
some useful helper methods written in CSharp implementation. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import pytest | ||
from stock_indicators import indicators | ||
|
||
class TestDynamic: | ||
def test_standard(self, quotes): | ||
results = indicators.get_dynamic(quotes, 14) | ||
|
||
assert 502 == len(results) | ||
assert 501 == len(list(filter(lambda x: x.dynamic is not None, results))) | ||
|
||
r = results[1] | ||
assert 212.9465 == round(float(r.dynamic), 4) | ||
|
||
r = results[25] | ||
assert 215.4801 == round(float(r.dynamic), 4) | ||
|
||
r = results[250] | ||
assert 256.0554== round(float(r.dynamic), 4) | ||
|
||
r = results[501] | ||
assert 245.7356 == round(float(r.dynamic), 4) | ||
|
||
def test_bad_data(self, bad_quotes): | ||
r = indicators.get_dynamic(bad_quotes, 15) | ||
|
||
assert 502 == len(r) | ||
|
||
def test_no_quotes(self, quotes): | ||
r = indicators.get_dynamic([], 14) | ||
assert 0 == len(r) | ||
|
||
r = indicators.get_dynamic(quotes[:1], 14) | ||
assert 1 == len(r) | ||
|
||
def test_exceptions(self, quotes): | ||
from System import ArgumentOutOfRangeException | ||
with pytest.raises(ArgumentOutOfRangeException): | ||
indicators.get_dynamic(quotes, 0) | ||
|
||
with pytest.raises(ArgumentOutOfRangeException): | ||
indicators.get_dynamic(quotes, 14, 0) |