Skip to content

Commit f232323

Browse files
committed
Add is_up_trend and is_down_trend indicators
1 parent 4648dec commit f232323

File tree

6 files changed

+223
-2
lines changed

6 files changed

+223
-2
lines changed

README.md

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pip install pyindicators
2424

2525
* Native Python implementation, no external dependencies needed except for Polars or Pandas
2626
* Dataframe first approach, with support for both pandas dataframes and polars dataframes
27-
* Supports python version 3.9 and above.
27+
* Supports python version 3.10 and above.
2828
* [Trend indicators](#trend-indicators)
2929
* [Weighted Moving Average (WMA)](#weighted-moving-average-wma)
3030
* [Simple Moving Average (SMA)](#simple-moving-average-sma)
@@ -40,6 +40,8 @@ pip install pyindicators
4040
* [Is Crossover](#is-crossover)
4141
* [Crossunder](#crossunder)
4242
* [Is Crossunder](#is-crossunder)
43+
* [Is Downtrend](#is-downtrend)
44+
* [Is Uptrend](#is-uptrend)
4345

4446
## Indicators
4547

@@ -615,3 +617,66 @@ if is_crossunder(
615617
if is_crossunder(pd_df, crossover_column="Crossunder_EMA", number_of_data_points=3):
616618
print("Crossunder detected in Pandas DataFrame in the last 3 data points")
617619
```
620+
621+
#### Is Downtrend
622+
623+
The is_downtrend function is used to determine if a downtrend occurred in the last N data points. It returns a boolean value indicating if a downtrend occurred in the last N data points. The function can be used to check for downtrends in a DataFrame that was previously calculated using the crossover function.
624+
625+
```python
626+
627+
def is_down_trend(
628+
data: Union[PdDataFrame, PlDataFrame],
629+
use_death_cross: bool = True,
630+
) -> bool:
631+
```
632+
633+
Example
634+
635+
```python
636+
from polars import DataFrame as plDataFrame
637+
from pandas import DataFrame as pdDataFrame
638+
639+
from investing_algorithm_framework import CSVOHLCVMarketDataSource
640+
from pyindicators import is_down_trend
641+
642+
# For this example the investing algorithm framework is used for dataframe creation,
643+
csv_path = "./tests/test_data/OHLCV_BTC-EUR_BINANCE_15m_2023-12-01:00:00_2023-12-25:00:00.csv"
644+
data_source = CSVOHLCVMarketDataSource(csv_file_path=csv_path)
645+
646+
pl_df = data_source.get_data()
647+
pd_df = data_source.get_data(pandas=True)
648+
649+
print(is_down_trend(pl_df))
650+
print(is_down_trend(pd_df))
651+
```
652+
653+
#### Is Uptrend
654+
655+
The is_up_trend function is used to determine if an uptrend occurred in the last N data points. It returns a boolean value indicating if an uptrend occurred in the last N data points. The function can be used to check for uptrends in a DataFrame that was previously calculated using the crossover function.
656+
657+
```python
658+
def is_up_trend(
659+
data: Union[PdDataFrame, PlDataFrame],
660+
use_golden_cross: bool = True,
661+
) -> bool:
662+
```
663+
664+
Example
665+
666+
```python
667+
from polars import DataFrame as plDataFrame
668+
from pandas import DataFrame as pdDataFrame
669+
670+
from investing_algorithm_framework import CSVOHLCVMarketDataSource
671+
from pyindicators import is_up_trend
672+
673+
# For this example the investing algorithm framework is used for dataframe creation,
674+
csv_path = "./tests/test_data/OHLCV_BTC-EUR_BINANCE_15m_2023-12-01:00:00_2023-12-25:00:00.csv"
675+
data_source = CSVOHLCVMarketDataSource(csv_file_path=csv_path)
676+
677+
pl_df = data_source.get_data()
678+
pd_df = data_source.get_data(pandas=True)
679+
680+
print(is_up_trend(pl_df))
681+
print(is_up_trend(pd_df))
682+
```

pyindicators/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
get_peaks, is_divergence, is_lower_low_detected, \
44
is_below, is_above, get_slope, has_any_higher_then_threshold, \
55
has_slope_above_threshold, has_any_lower_then_threshold, \
6-
has_values_above_threshold, has_values_below_threshold
6+
has_values_above_threshold, has_values_below_threshold, is_down_trend, \
7+
is_up_trend
78
from .exceptions import PyIndicatorException
89

910
__all__ = [
@@ -32,4 +33,6 @@
3233
'has_values_above_threshold',
3334
'has_values_below_threshold',
3435
'PyIndicatorException',
36+
'is_down_trend',
37+
'is_up_trend',
3538
]

pyindicators/indicators/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
has_slope_above_threshold, has_any_lower_then_threshold, \
1313
has_slope_below_threshold, has_values_above_threshold, \
1414
has_values_below_threshold
15+
from .is_down_trend import is_down_trend
16+
from .is_up_trend import is_up_trend
1517

1618
__all__ = [
1719
'sma',
@@ -38,4 +40,6 @@
3840
'has_slope_below_threshold',
3941
'has_values_above_threshold',
4042
'has_values_below_threshold',
43+
'is_down_trend',
44+
'is_up_trend',
4145
]

pyindicators/indicators/exponential_moving_average.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ def ema(
3333
f"only contains {len(data)} data points."
3434
)
3535

36+
# Check if source_column exists in the DataFrame
37+
if source_column not in data.columns:
38+
raise PyIndicatorException(
39+
f"The source column '{source_column}' does not "
40+
"exist in the DataFrame."
41+
)
42+
43+
3644
if result_column is None:
3745
result_column = f"ema_{source_column}_{period}"
3846

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from typing import Union
2+
3+
from pandas import DataFrame as PdDataFrame
4+
from polars import DataFrame as PlDataFrame
5+
6+
from .exponential_moving_average import ema
7+
8+
9+
def is_down_trend(
10+
data: Union[PdDataFrame, PlDataFrame],
11+
use_death_cross: bool = True,
12+
) -> bool:
13+
"""
14+
Check if the market is in a downtrend using various indicators.
15+
16+
The function decides whether it is a downtrend based on a set of
17+
weighted indicators.
18+
19+
If the value of the sum of the indicators is greater than 0.5,
20+
it is considered a downtrend.
21+
22+
Args:
23+
data: Pandas or Polars DataFrame
24+
use_death_cross: If True, use the death cross indicator
25+
to check for a downtrend
26+
27+
Returns:
28+
bool: True if the market is in a downtrend, False otherwise
29+
"""
30+
31+
weights = {
32+
"death_cross": {}
33+
}
34+
35+
source_data = data.copy()
36+
37+
if use_death_cross:
38+
source_data = ema(
39+
source_data,
40+
source_column="Close",
41+
period=50,
42+
result_column="EMA_CLOSE_50"
43+
)
44+
source_data = ema(
45+
source_data,
46+
source_column="Close",
47+
period=200,
48+
result_column="EMA_CLOSE_200"
49+
)
50+
death_cross = source_data["EMA_CLOSE_50"].iloc[-1] \
51+
< source_data["EMA_CLOSE_200"].iloc[-1]
52+
53+
if death_cross:
54+
weights["death_cross"]["value"] = 1
55+
else:
56+
weights["death_cross"]["value"] = 0
57+
58+
weights["death_cross"]["weight"] = 1
59+
60+
# Calculate the weighted sum of the indicators
61+
weighted_sum = 0
62+
63+
for indicator, values in weights.items():
64+
weighted_sum += values["value"] * values["weight"]
65+
66+
# Check if the weighted sum is greater than 0.5
67+
if weighted_sum > 0.5:
68+
return True
69+
else:
70+
return False
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from typing import Union
2+
3+
from pandas import DataFrame as PdDataFrame
4+
from polars import DataFrame as PlDataFrame
5+
6+
from .exponential_moving_average import ema
7+
8+
9+
def is_up_trend(
10+
data: Union[PdDataFrame, PlDataFrame],
11+
use_golden_cross: bool = True,
12+
) -> bool:
13+
"""
14+
Check if the market is in a uptrend using various indicators.
15+
16+
The function decides whether it is a uptrend based on a set of
17+
weighted indicators.
18+
19+
If the value of the sum of the indicators is greater than 0.5,
20+
it is considered a uptrend.
21+
22+
Args:
23+
data: Pandas or Polars DataFrame
24+
use_golden_cross: If True, use the golden cross indicator
25+
to check for a golden cross
26+
27+
Returns:
28+
bool: True if the market is in a uptrend, False otherwise
29+
"""
30+
31+
weights = {
32+
"golden_cross": {}
33+
}
34+
35+
source_data = data.copy()
36+
37+
if use_golden_cross:
38+
# Check for a golden cross
39+
source_data = ema(
40+
source_data,
41+
source_column="Close",
42+
period=50,
43+
result_column="EMA_CLOSE_50"
44+
)
45+
source_data = ema(
46+
source_data,
47+
source_column="Close",
48+
period=200,
49+
result_column="EMA_CLOSE_200"
50+
)
51+
golden_cross = source_data["EMA_CLOSE_50"].iloc[-1] \
52+
< source_data["EMA_CLOSE_200"].iloc[-1]
53+
54+
if golden_cross:
55+
weights["golden_cross"]["value"] = 1
56+
else:
57+
weights["golden_cross"]["value"] = 0
58+
59+
weights["golden_cross"]["weight"] = 1
60+
61+
# Calculate the weighted sum of the indicators
62+
weighted_sum = 0
63+
64+
for indicator, values in weights.items():
65+
weighted_sum += values["value"] * values["weight"]
66+
67+
# Check if the weighted sum is greater than 0.5
68+
if weighted_sum > 0.5:
69+
return True
70+
else:
71+
return False

0 commit comments

Comments
 (0)