-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is there a proper way to extend pandas-ta with Custom Indicator? #264
Comments
Hello @locupleto, Apologies for the delay. I have been meaning to write a guide on this topic, so there is no better time than now. 🤣 I hope these steps are clear and easy to follow so any feedback on the process or wording would be greatly appreciated. 😎 In short, there are basically four steps to fully add an indicator to Pandas TA. For concrete examples, I recommend looking at @rengel8's RSX and @whubsch's MCG indicators. Essentially their indicator submission is some combination of steps outlined below. Creating a Custom Indicator: The Big 4This is a step by step guide to creating a Custom Indicator to provide both Standard and Pandas TA DataFrame Extensionable Indicators. So lets create a "hypothetical indicator" named Step One: Adding the Indicator to it's CategoryFor
Now it should be callable the Standard way like a TA Lib indicator: import pandas as pd
import pandas_ta as ta
df = # ohlcv data
nidf = ta.ni(df["close"]) # = ta.ni(df["close"], 10) Step Two: Adding the Indicator to the Pandas TA DataFrameThe next two steps are required to link
@pd.api.extensions.register_dataframe_accessor("ta")
class AnalysisIndicators(BasePandasObject):
# ...
# Trend
# ...
def ni(self, length=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = ni(close=close, length=length, offset=offset, **kwargs)
return self._post_process(result, **kwargs) That should be it! Now you can run import pandas as pd
import pandas_ta as ta
df = # ohlcv data
df.ta.ni(append=True) Or via a Custom Strategy: import pandas as pd
import pandas_ta as ta
df = # ohlcv data
# (1) Create the Strategy
MyStrategy = ta.Strategy(
name="NI Trend",
ta=[
{"kind": "ohlc4"},
{"kind": "ni", "length": 12},
]
)
# (2) Run the Strategy
df.ta.strategy(MyStrategy, **kwargs) TLDRAdd and edit the file Hope this helps! Please let me know (good/bad)! Future contributions are greatly appreciated! 😎 Kind Regards, |
Hello KJ! Awesome! Absolutely no need to apologise for anything. Your answer was both swift and thorough. And I found your guide to be very easy to follow, showing step by step how to extend pandas-ta with new a indicator of your own in a way that enables you to then use it exactly as the built-in functions. Big thanks! Seeing these discrete steps described got me thinking though. Wouldn't it be sweet if in the future pandas_ta could support a dynamic plugin-style extension of its base functionality as well. With that I mean in a way that eliminate the need to modify pandas-ta itself. E.g. say that I have pandas_ta in my python venv and in my project folder I create a structure mimicking the pandas_ta folder structure for the functions I like to dynamically import like this:
If I then passed the path to my_extension folders to pandas-ta it would try to dynamically load all user functions found there in the same manner it does today in a more static way. The idea would be to use a combination of importlib and decorators in pandas_ta to achieve this optional dynamic loading of user functions much in the same way as you do statically today. That way we could avoid having to re-insert custom user functions each time with update pandas_ta in the pyenv. Just a thought. Again, thank you very much KJ! |
Hello @locupleto,
Great! I appreciate the feedback. You're welcome! Thank you.
Yeah. I have been mulling on the approach to easily implement that feature because I could use it also. You are welcome to contribute as I have not tackled this problem before. 😎 No worries if you can not. This approached sounds logical. 😎 Also I plan to build a Pandas TA YAML config as described in Issue #258 which hopefully paves the way. Thank you for your swift response, feedback, and ideas. I appreciate it. Kind Regards, |
Well, neither have I, at least not with Python. But I will give it a try and let you know if I get anywhere. At least maybe I will learn something. :-) Cheers! |
I look forward to seeing what you come up with. 😎 No worries if you don't. Thanks, |
Thank you KJ. Looking forward to dig into it. It’s an interesting topic. I will let you know how it goes. How hard can it be? 😄
BR
Urban
Skickat från min iPhone
… 10 apr. 2021 kl. 20:00 skrev Kevin Johnson ***@***.***>:
@locupleto,
I look forward to seeing what you come up with. 😎 No worries if you don't.
Thanks,
KJ
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
hi, you need a helping hand on this topic? |
Hello @jacobt77, Sure thing. I am open to ideas and/or implementations. 😎 Thanks, |
hello i have tried to create custom indicator like you said but error appears. below are the error message: File "/root/simulation_test/src/app.py", line 14, in Please help me to solve the issue. |
Hi,
Ok, looks like you've a circular import issue. Could you give me a little
more context please?
Den fre 3 nov. 2023 kl 11:16 skrev utpal4job ***@***.***>:
… hello i have tried to create custom indicator like you said but error
appears. below are the error message:
File "/root/simulation_test/src/app.py", line 14, in
import pandas_ta as ta
File "/root/myenv/lib/python3.11/site-packages/pandas_ta/init.py", line 1,
in
from pandas_ta.core import *
File "/root/myenv/lib/python3.11/site-packages/pandas_ta/core.py", line
14, in
from pandas_ta import Category, Imports, version
ImportError: cannot import name 'Category' from partially initialized
module 'pandas_ta' (most likely due to a circular import)
(/root/myenv/lib/python3.11/site-packages/pandas_ta/init.py)
—
Reply to this email directly, view it on GitHub
<#264 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AA6PKGT5SR4UCHPKHXPMEODYCS75FAVCNFSM42M2FOP2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCNZZGIYTONJQHA3Q>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
--
Urban Ottosson
Mail: ***@***.***
|
Ok, obviously I don't know exactly what you intend to do in your module but
I don't think you need to explicitly import every indicator to be able to
use them in your module. Instead just use them from an instantiated
dataframe directly. If you are new to pandas_ta, you can basically think of
it as an technical-analysis extension to pandas dataframes.
Here's an interactive session example of how it all works and specifically
what you need to do the first time you setup your local environment if you
want to use custom indicators. You can easily mimic what you need in your
module:
*Environment*
Create and activate a venv for this session
*python3 -m venv venv*
*source venv/bin/activate*
*pip install pandas_ta*
***@***.***-[/Volumes/Work/development/projects/local/pdta](venv)
└─> % pip freeze
numpy==1.26.1
pandas==2.1.2
pandas-ta==0.3.14b0
python-dateutil==2.8.2
pytz==2023.3.post1
six==1.16.0
tzdata==2023.3
***@***.***-[/Volumes/Work/development/projects/local/pdta](venv)
└─> % python3
Python 3.11.6 (main, Oct 2 2023, 13:45:54) [Clang 15.0.0
(clang-1500.0.40.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
*>>> import pandas as pd>>> import pandas_ta as ta**>>> # read the example
data-file in the pandas-ta githb repository*
*>>> df = pd.DataFrame()*
*>>> df =
pd.read_csv("/Volumes/Work/development/projects/git/pandas-ta/data/SPY_D.csv")>>>
df.set_index(pd.DatetimeIndex(df["date"]), inplace=True)*
*>>> # these categories of indicators are available*
*>>> df.ta.categories*
['candles', 'cycles', 'momentum', 'overlap', 'performance', 'statistics',
'trend', 'volatility', 'volume']
*>>> # import a set of custom indicators from my local directory>>> # start
by creating a custom indicator root folder with the right sub-folders>>>
import os>>> from os.path import abspath, join, expanduser>>> from
pandas_ta.custom import create_dir, import_dir>>> my_dir =
abspath(join(expanduser("~"), "my_indicators"))>>> create_dir(my_dir)*
[i] Created main directory '/Users/urban/my_indicators'.
[i] Created an empty sub-directory 'candles'.
[i] Created an empty sub-directory 'cycles'.
[i] Created an empty sub-directory 'momentum'.
[i] Created an empty sub-directory 'overlap'.
[i] Created an empty sub-directory 'performance'.
[i] Created an empty sub-directory 'statistics'.
[i] Created an empty sub-directory 'trend'.
[i] Created an empty sub-directory 'volatility'.
[i] Created an empty sub-directory 'volume'.
*>>> # I now have a custom indicator folder tree, and I have copied ni.py
into the trend category*
*>>> os.system('tree "/Users/urban/my_indicators"')*
/Users/urban/my_indicators
├── candles
├── cycles
├── momentum
├── overlap
├── performance
├── statistics
├── trend
│ └── ni.py
├── volatility
└── volume
10 directories, 1 file
*>>> # in order to use it, we must first import it dynamically*
*>>> import_dir(my_dir)*
[i] Successfully imported the custom indicator
'/Users/urban/my_indicators/trend/ni.py' into category 'trend'.
*>>> # at this point your session (or module) is ready to utilize both
built-in and custom indicators>>> df.ta.indicators()*
Pandas TA - Technical Analysis Indicators - v0.3.14b0
Total Indicators & Utilities: 206
Abbreviations:
aberration, above, above_value, accbands, ad, adosc, adx, alma, amat,
ao, aobv, apo, aroon, atr, bbands, below, below_value, bias, bop, brar,
cci, cdl_pattern, cdl_z, cfo, cg, chop, cksp, cmf, cmo, coppock, cross,
cross_value, cti, decay, decreasing, dema, dm, donchian, dpo, ebsw, efi,
ema, entropy, eom, er, eri, fisher, fwma, ha, hilo, hl2, hlc3, hma, hwc,
hwma, ichimoku, increasing, inertia, jma, kama, kc, kdj, kst, kurtosis,
kvo, linreg, log_return, long_run, macd, mad, massi, mcgd, median, mfi,
midpoint, midprice, mom, natr, ni, nvi, obv, ohlc4, pdist, percent_return,
pgo, ppo, psar, psl, pvi, pvo, pvol, pvr, pvt, pwma, qqe, qstick, quantile,
rma, roc, rsi, rsx, rvgi, rvi, short_run, sinwma, skew, slope, sma, smi,
squeeze, squeeze_pro, ssf, stc, stdev, stoch, stochrsi, supertrend, swma,
t3, td_seq, tema, thermo, tos_stdevall, trima, trix, true_range, tsi,
tsignals, ttm_trend, ui, uo, variance, vhf, vidya, vortex, vp, vwap, vwma,
wcp, willr, wma, xsignals, zlma, zscore
Candle Patterns:
2crows, 3blackcrows, 3inside, 3linestrike, 3outside, 3starsinsouth,
3whitesoldiers, abandonedbaby, advanceblock, belthold, breakaway,
closingmarubozu, concealbabyswall, counterattack, darkcloudcover, doji,
dojistar, dragonflydoji, engulfing, eveningdojistar, eveningstar,
gapsidesidewhite, gravestonedoji, hammer, hangingman, harami, haramicross,
highwave, hikkake, hikkakemod, homingpigeon, identical3crows, inneck,
inside, invertedhammer, kicking, kickingbylength, ladderbottom,
longleggeddoji, longline, marubozu, matchinglow, mathold, morningdojistar,
morningstar, onneck, piercing, rickshawman, risefall3methods,
separatinglines, shootingstar, shortline, spinningtop, stalledpattern,
sticksandwich, takuri, tasukigap, thrusting, tristar, unique3river,
upsidegap2crows, xsidegap3methods
*>>> # now we can use all the above ta functions, including our custom
indicator ni*
*>>> # eg. create a Series containing simple moving average SMA_10*
*>>> sma10 = df.ta.sma(length=10)*
*>>> and try your custom ni indicator too*
*>>> ni42 = df.ta.ni (length=42)
Hope that helps
BR
Urban
Den fre 3 nov. 2023 kl 13:05 skrev utpal4job ***@***.***>:
… Hello, thanks for you reply.
on "*init*.py" file i have added "ni" on below section
# Trend "trend": [ "adx", "ni", "amat", "aroon", "chop", "cksp", "decay",
"decreasing", "dpo", "increasing", "long_run", "psar", "qstick",
"short_run", "tsignals", "ttm_trend", "vhf", "vortex", "xsignals" ],
on "core.py" i have added the "ni" method like you said
def ni(self, length=None, offset=None, **kwargs): close =
self._get_column(kwargs.pop("close", "close")) result = ni(close=close,
length=length, offset=offset, **kwargs) return self._post_process(result,
**kwargs)
error appears on "core.py" file. i have attached the screenshot also.
[image: 2023-11-03_17-33_core py]
<https://user-images.githubusercontent.com/3049092/280270918-3a340f40-0e49-424e-ac13-b7a15de3bc34.jpg>
Pls help me.
Thanks.
—
Reply to this email directly, view it on GitHub
<#264 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AA6PKGXD67MN7MTEYKMVQC3YCTMWHAVCNFSM42M2FOP2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCNZZGIZTEMJRHA3A>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
--
Urban Ottosson
Mail: ***@***.***
|
Hello, thanks for your reply. first of all i will not make anything on local environment. I want to know using "Creating a Custom Indicator: The Big 4" steps can i create custom indicators on the server on "pandas-ta" lib because i want to use my custom indicators on different section like we are using "RSI/SMA/MACD/...". Pls let me know. Regards. |
Alright then, I take have a quick look. Just two basic questions
|
Ok, thank you. That looks recent. I get this when installing into a clean venv :
It will probably not be what you want to hear but I would try on a brand new venv with only pandas_ta in it and when you have that up and running including your custom indicator, add the other requirements from your current env and go from there. I had a very similar issue recently trying to add the gradio library to one of my bigger projects which I solved in that manner. It's annoying I know but It is very hard to debug these types of errors and starting over is usually the fastest way to resolve them. Sorry I couldn't be of more help. And just to clarify, I presume you have
Otherwise try that BR |
Hello, thanks for your reply. tomorrow i will try in fresh server what you said and let you know for sure. Thanks again. Regards. |
Hello, to let you know i have installed the "pandas_ta" lib on the fresh server but after adding the "ni" indicator same error appears like yesterday. i am going to be mad not with that issue. Thanks. |
Hi, dang that's disappointing. When you say on a fresh server, what exactly did you do, a complete new vm? I reckon that to test it out the same way I showed you in my recent reply, it would suffice to
Is that what you did? (I'm asking just to understand how different our setups are). |
Hi, i have not made like you said. I am trying to make like you said and let you know. Thanks. |
Hello, ok i will check later and let you know. Regards. |
Hello, i have removed "from pandas_ta import Category, Imports, version" row from core.py file but "circular import" issue appears on different section. I am not getting one thing when i am installing fresh "pandas_ta" library then "circular import" issue not appearing but whenever edited anything then "circular import" issue appeared. One another thing i have noticed, i have removed everything what i have edited and uploaded the files like it's fresh installed still "circular import" issue appears. some thing i am doing wrong for sure. if possible can you please check what i am doing wrong, if you want i will send server details. Regards. |
Hello, ok, i will make the 3 edits and check again. Thanks. |
It is very hard for me to help you with your specific problems on your specific environment since I am unable to re-produce your issues in my own environment. Details regarding your machine and OS wouldn't necessarily help either unless I was able to 100% mimic it. I would suggest the following:
It is comforting at least that the straight import of the standard pantas_ta works. The installation from PyPI compiles the package and its dependencies in a way that may avoid some of the pitfalls of direct module execution, like circular imports. So that may be why you only experience problems when working with the library in edit mode. Best regards |
Hello, i have made what you said, when removed Category then same error appears for Imports that's why i have removed all 3 imports and made all 3 import like you said for "Category/Imports/version. below error appears right now. `>>> import pandas as pd
Thanks. |
Yes, well - as I said, just continue to fix any following problems like I showed you. Read the error and act accordingly. In this case
If you want to avoid this hassel you could always resort to the way I explained to you in my first reply, that is use the official pandas_ta and add your own indicator to it by loading them dynamically at run-time. Sorry, but there's nothing more I can do for you at this point. |
Hello, It's ok. Thanks again... |
Hello @utpal4job, Thanks @locupleto for trying helping to resolve @utpal4job 's issue with adding a new indicator. So @utpal4job, after restarting with a fresh virtual environment as @locupleto has suggested, have you tried doing only Step One listed below (before attempting Step Two to add it to core.py)? If the minimum implementation of Step One is not working, then don't count on Step Two to work. Step One: Adding the Indicator to it's CategoryFor
Now it should be callable the Standard way like a TA Lib indicator: import pandas as pd
import pandas_ta as ta
df = # ohlcv data
nidf = ta.ni(df["close"]) # = ta.ni(df["close"], 10) Also can you share some more of custom indicator, perhaps there is some import that is Additionally, I highly recommend using the development version instead of 0.3.14b0. It has some speed improvements with numpy/numba, cleaner code, and better documentation. Kind Regards, |
Hello @twopirllc / @locupleto . I will check the step1 and let you know. Thanks. |
Hello, i have made the step1 like you said but error appears. pls check it below.
Thanks. |
Hello, here is the "ni.py" file code: # -*- coding: utf-8 -*-
from pandas_ta.utils import get_drift, get_offset, is_percent, verify_series
def ni(close, length=None, offset=None, **kwargs):
"""NI Doc here"""
# Validate Arguments
length = int(length) if length and length > 0 else 1
close = verify_series(close, length)
offset = get_offset(offset)
if close is None:
return
ni = ni(close, length)
# Offset
if offset != 0:
ni = ni.shift(offset)
# Handle fills
if "fillna" in kwargs:
ni.fillna(kwargs["fillna"], inplace=True)
if "fill_method" in kwargs:
ni.fillna(method=kwargs["fill_method"], inplace=True)
# Name and Categorize it
ni.name = f"NI_{length}"
ni.category = "trend"
return ni
Thanks. |
Hello @utpal4job,
Unfortunately you did not follow the instructions or perhaps the instructions were not clear. 🤷🏼♂️ Lets stick this version of # -*- coding: utf-8 -*-
from pandas_ta.overlap import sma
from pandas_ta.utils import get_offset, verify_series
# - Standard definition of your custom indicator function (including docs)-
def ni(close, length=None, centered=False, offset=None, **kwargs):
"""
Example indicator ni
"""
# Validate Arguments
length = int(length) if length and length > 0 else 20
close = verify_series(close, length)
offset = get_offset(offset)
if close is None: return
# Calculate Result
t = int(0.5 * length) + 1
ma = sma(close, length)
ni = close - ma.shift(t)
if centered:
ni = (close.shift(t) - ma).shift(-t)
# Offset
if offset != 0:
ni = ni.shift(offset)
# Handle fills
if "fillna" in kwargs:
ni.fillna(kwargs["fillna"], inplace=True)
if "fill_method" in kwargs:
ni.fillna(method=kwargs["fill_method"], inplace=True)
# Name and Categorize it
ni.name = f"ni_{length}"
ni.category = "trend"
return ni
ni.__doc__ = \
"""Example indicator (NI)
Is an indicator provided solely as an example
Sources:
https://github.com/twopirllc/pandas-ta/issues/264
Calculation:
Default Inputs:
length=20, centered=False
SMA = Simple Moving Average
t = int(0.5 * length) + 1
ni = close.shift(t) - SMA(close, length)
if centered:
ni = ni.shift(-t)
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 20
centered (bool): Shift the ni back by int(0.5 * length) + 1. Default: False
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
fill_method (value, optional): Type of fill method
Returns:
pd.Series: New feature generated.
""" However, you did not follow the instructions when testing import pandas as pd
import pandas_ta as ta
df = # ohlcv data
nidf = ta.ni(df["close"]) # = ta.ni(df["close"], 10) But instead you ran it as a DataFrame Extension which is the testing phase of Step Two. import pandas as pd
import pandas_ta as ta
df = # ohlcv data
df.set_index(pd.DatetimeIndex(df["date"]), inplace=True) # including this fine
df.ta.ni(append=True) As a reminder, first you must be able to get Step One working before integrating it into the DataFrame in core.py in Step Two. In other words, Step Two will not work unless Step One is working. Once you are able to get Step One working by itself, then it should be easier to resolve your Step Two issue(s). KJ |
Hello, thanks for your reply. Just checked step1 is working fine. pls let me know what i will do on step2. Thanks, |
Great! Could you please share a screenshot?
Step Two: Adding the Indicator to the Pandas TA DataFrameThe next two steps are required to link
@pd.api.extensions.register_dataframe_accessor("ta")
class AnalysisIndicators(BasePandasObject):
# ...
# Trend
# ...
def ni(self, length=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = ni(close=close, length=length, offset=offset, **kwargs)
return self._post_process(result, **kwargs) That should be it! Now you can run import pandas as pd
import pandas_ta as ta
df = # ohlcv data
df.ta.ni(append=True) |
Hello, i have checked on my working server and both steps are working fine. Thank you @twopirllc and @locupleto |
Great. Very curious about what the cause of the problem was if any of you know?On Nov 8, 2023, at 09:25, utpal4job ***@***.***> wrote:
Hello, thanks for your reply. Yes both steps are working fine.
Step1:
Step2:
Now i will check both steps in my working server.
Thanks again.
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: ***@***.***>
|
Glad you were able to get the example indicator ni working as expected! 😎 Can you share details about your indicator volume_value()? More specifically, can you share what you are importing in volume_value.py? Perhaps some import in it is causing your circular import issue? In general, circular imports are an issue on your end and very hard for us to solve on our end unless you share more details. 🤷🏼♂️ Kind Regards |
Hello, sorry for late reply. i have attached volume_values.py file code def volume_values(volume, length=None, centered=False, offset=None, **kwargs):
"""
Example indicator volume_values
"""
# Validate Arguments
length = int(length) if length and length > 0 else 20
volume = verify_series(volume, length)
offset = get_offset(offset)
if volume is None:
return
# Calculate Result
t = int(0.5 * length) + 1
ma = sma(volume, length)
# volume_values = volume - ma.shift(t)
volume_values = volume
if centered:
volume_values = (volume.shift(t) - ma).shift(-t)
# Offset
if offset != 0:
volume_values = volume_values.shift(offset)
# Handle fills
if "fillna" in kwargs:
volume_values.fillna(kwargs["fillna"], inplace=True)
if "fill_method" in kwargs:
volume_values.fillna(method=kwargs["fill_method"], inplace=True)
# Name and Categorize it
volume_values.name = f"volume_values_{length}"
volume_values.category = "trend"
return volume_values Thanks. |
Weird. 🤔 It is hard to triage without being on the machine. 🤷🏼♂️ Again, I highly recommend using the development version instead of 0.3.14b0. It has some speed improvements with numpy/numba, cleaner code, and better documentation. Kind Regards |
Hello, thanks for your reply. I will let you know if i am able to solve the issue. |
Which version are you running? The lastest version is Github. Pip is for major releases.
0.2.62b0
Is your feature request related to a problem? Please describe.
I fail to figure out how to add my own custom indicators on top of all the great functions in the pandas-ta module
Describe the solution you'd like
I would love a short example showing the best way to add a simple custom indicator and use that together with pandas-ta without modifying the code within the pandas-ta module.
Describe alternatives you've considered
I considered subclassing AnalysisIndicators in core.py but I was unsure which methods I needed to override. Another way would be to post-process the dataframe myself completely outside the module and adding the cols of the custom indicator after running a pandas-ta strategy I guess. But that's a rather ugly way of doing it I think.
Additional context
Being able to extend the base functionality of Pandas TA and experiment with custom indicators is a common way TA platforms such as MultiCharts or TradeStation are used and it would make Pandas TA even better I believe.
Anyhow, great work guys!
The text was updated successfully, but these errors were encountered: