{"id":2116,"date":"2023-09-28T08:58:21","date_gmt":"2023-09-28T08:58:21","guid":{"rendered":"https:\/\/eodhd.com\/financial-academy\/?p=2116"},"modified":"2025-02-05T13:11:13","modified_gmt":"2025-02-05T13:11:13","slug":"algorithmic-trading-with-the-keltner-channel-in-python","status":"publish","type":"post","link":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python","title":{"rendered":"Algorithmic Trading with the Keltner Channel in Python"},"content":{"rendered":"\n<p id=\"3dbd\">Studying technical indicators, you will definitely come across a list comprising curated indicators that are widely considered as \u2018must-know\u2019 indicators that need to be learned before getting your hands dirty in the real-world market. The indicator we are going to explore today adds to this list, given its performance in the market: the Keltner Channel (KC).<\/p>\n\n\n\n<p id=\"6fb2\">In this article, we will first discuss what the Keltner Channel is all about, and the mathematics behind the indicator. Then, we will proceed to the programming part where we will use Python to build the indicator from scratch, construct a simple trading strategy based on the indicator, backtest the strategy on Intel stocks, and finally, compare the strategy returns with those of the SPY ETF (an ETF particularly designed to track the movements of the S&amp;P 500 market index).<\/p>\n\n\n\n<p class=\"has-text-align-center\"><a class=\"maxbutton-1 maxbutton maxbutton-subscribe-to-api external-css btn\" href=\"https:\/\/eodhd.com\/register\"><span class='mb-text'>Register &amp; Get Data<\/span><\/a><\/p>\n\n\n\n\n\n\n<h1 class=\"wp-block-heading\" id=\"deba\">Average True Range (ATR)<\/h1>\n\n\n\n<p id=\"361a\">It is essential to know what the Average True Range (ATR) is, since it is involved in the calculation of the Keltner Channel.<\/p>\n\n\n\n<p id=\"62d8\">Founded by Wilder Wiles (creator of the most popular indicator, the RSI), the Average True Range is a technical indicator that measures how much an asset moves on an average. It is a lagging indicator meaning that it takes into account the historical data of an asset to measure the current value but it\u2019s not capable of predicting the future data points. This is not considered as a drawback while using ATR, as it\u2019s one of the indicators to track the volatility of a market more accurately. Along with being a lagging indicator, ATR is also a non-directional indicator meaning that the movement of ATR is inversely proportional to the actual movement of the market. <\/p>\n\n\n\n<p id=\"62d8\">To calculate ATR, two steps are needed:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Calculate True Range (TR):<\/strong>&nbsp;A True Range of an asset is calculated by taking the greatest values of three price differences which are: market high minus marker low, market high minus previous market close, previous market close minus market low. It can be represented as follows:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-preformatted has-background\" style=\"background-color:#abb7c217\"><strong>MAX <\/strong>[ {<strong>HIGH - LOW<\/strong>}, {<strong>HIGH - P.CLOSE<\/strong>}, {<strong>P.CLOSE - LOW<\/strong>} ]\n\nwhere,\nMAX = Maximum values\nHIGH = Market High\nLOW = Market Low\nP.CLOSE = Previous market close<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Calculate ATR:<\/strong>&nbsp;The calculation for the Average True Range is simple. We just have to take a smoothed average of the previously calculated True Range values for a specified number of periods. The smoothed average is not just any SMA or EMA but an own type of smoothed average created by Wilder Wiles himself which is subtracting one from the Exponential Moving Average of the True Range for a specified number of periods and multiplying the difference with two. The calculation of ATR for a specified number of periods can be represented as follows:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-preformatted has-background\" style=\"background-color:#abb7c217\"><strong>ATR N <\/strong>= <strong>EMA N <\/strong>[ <strong>TR <\/strong>] <strong>- 1<\/strong> * <strong>2<\/strong>\n\nwhere,\nATR N = Average True Range of 'N' period\nSMA N = Simple Moving Average of 'N' period\nTR = True Range<\/pre>\n\n\n\n<p id=\"06e6\">While using ATR as an indicator for trading purposes, traders must ensure that they are more cautious than ever, as the indicator is very lagging. <\/p>\n\n\n\n<p id=\"06e6\">Now that we have an understanding of what the Average True Range is all about, let\u2019s dive into the main concept of this article, the Keltner Channel.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"9699\">Keltner Channel (KC)<\/h1>\n\n\n\n<p id=\"b5df\">Founded by Chester Keltner, the Keltner Channel is a technical indicator that is often used by traders to identify volatility and the direction of the market. The Keltner Channel is composed of three components: The upper band, the lower band, and the middle line. <\/p>\n\n\n\n<p id=\"9e33\">Before diving into the calculation of the Keltner Channel it is essential to know about the three important inputs involved in the calculation. First is the ATR lookback period, which is the number of periods that are taken into account for the calculation of ATR. Secondly, the Keltner Channel lookback period. This input is more or less similar to the first one, but here, we are determining the number of periods that are taken into account for the calculation of the Keltner Channel itself. The final input is the multiplier which is a value determined to multiply with the ATR. The typical values that are taken as inputs are 10 as the ATR lookback period, 20 as the Keltner Channel lookback period, and 2 as the multiplier. Keeping these inputs in mind, let\u2019s calculate the readings of the Keltner Channel\u2019s components.<\/p>\n\n\n\n<p id=\"d062\">The first step in calculating the components of the Keltner Channel is determining the ATR values with 10 as the lookback period and it can be calculated by following the formula discussed before.<\/p>\n\n\n\n<p id=\"85da\">The next step is calculating the middle line of the Keltner Channel. This component is the 20-day Exponential Moving Average of the closing price of the stock. The calculation can be represented as follows:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-background\" style=\"background-color:#abb7c217\"><strong>MIDDLE LINE 20<\/strong> = <strong>EMA 20<\/strong> [ <strong>C.STOCK<\/strong> ]\n\nwhere,\nEMA 20 = 20-day Exponential Moving Average \nC.STOCK = Closing price of the stock<\/pre>\n\n\n\n<p id=\"e40a\">The final step is calculating the upper and lower bands. Let\u2019s start with the upper band. It is calculated by first adding the 20-day Exponential Moving Average of the closing price of the stock by the multiplier (two) and then, multiplied by the 10-day ATR. The lower band calculation is almost similar to that of the upper band but instead of adding, we will be subtracting the 20-day EMA by the multiplier. The calculation of both upper and lower bands can be represented as follows:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-background\" style=\"background-color:#abb7c217\"><strong>UPPER BAND 20<\/strong> = <strong>EMA 20 <\/strong>[ <strong>C.STOCK <\/strong>] + <strong>MULTIPLIER <\/strong>* <strong>ATR 10\nLOWER BAND 20<\/strong> = <strong>EMA 20 <\/strong>[ <strong>C.STOCK <\/strong>] - <strong>MULTIPLIER <\/strong>* <strong>ATR 10<\/strong>\n\nwhere,\nEMA 20 = 20-day Exponential Moving Average \nC.STOCK = Closing price of the stock\nMULTIPLIER = 2\nATR 10 = 10-day Average True Range<\/pre>\n\n\n\n<p id=\"95e8\">That\u2019s the whole process of calculating the components of the Keltner Channel. Now, let\u2019s analyze a chart of the Keltner Channel to build more understanding of the indicator.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1789\" height=\"884\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-8.png\" alt=\"\" class=\"wp-image-2127\"\/><\/figure>\n\n\n\n<p>The above chart is a graphical representation of Intel\u2019s 20-day Keltner Chanel. We could notice that two bands are plotted on either side of the closing price line and those are the upper and lower band and a grey-colored line running in-between the two bands is the middle line or the 20-day EMA. The Keltner Channel can be used in an extensive number of ways but the most popular usages are identifying the market volatility and direction.<\/p>\n\n\n\n<p id=\"73b5\">The volatility of the market can be determined by the space that exists between the upper and lower band. If the space between the bands is wider, then the market is said to be volatile or showing greater price movements. On the other hand, the market is considered to be in a state of non-volatile or consolidating if the space between the bands is narrow. The other popular usage is identifying the market direction. The market direction can be determined by following the direction of the middle line as well as the upper and lower band.<\/p>\n\n\n\n<p id=\"41a3\">While seeing the chart of the Keltner Channel, it might resemble the Bollinger Bands. The only difference between these two indicators is the way each of them is being calculated. The Bollinger Bands use standard deviation for its calculation, whereas, the Keltner Channel utilizes ATR to calculate its readings. Now, let\u2019s talk about the trading strategy we are going to implement in this article.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Keltner Channel Trading Strategy<\/h2>\n\n\n\n<p id=\"104c\">We are going to implement the most popular Keltner Channel trading strategy, which is the Breakout strategy. Since the Keltner Channel is prone to revealing false signals, we are going to tune the traditional breakout strategy. Our tuned strategy will reveal a buy signal whenever the closing price line crosses from above to below the lower band and the current closing price is lesser than the next closing price of the stock. Similarly, a sell signal is revealed whenever the closing price line crosses from below to above the upper band and the current closing price is greater than the next closing price of the stock. Our trading strategy can be represented as follows:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-background\" style=\"background-color:#abb7c217\"><strong>IF C.CLOSE &lt; C.KCLOWER AND C.CLOSE &lt; N.CLOSE ==&gt; BUY SIGNAL\nIF C.CLOSE &gt; C.KCUPPER AND C.CLOSE &gt; N.CLOSE ==&gt; SELL SIGNAL<\/strong><\/pre>\n\n\n\n<p id=\"9b5c\">Many other strategies can also be implemented based on the Keltner Channel indicator but just to make things simple to understand, we are going with the breakout strategy. This concludes our theory part on the Keltner Channel indicator. Now, let\u2019s move on to the programming part where we are first going to build the indicator from scratch, build the breakout strategy which we just discussed, then, compare our strategy\u2019s performance with the SPY ETF\u2019s returns in Python. Let\u2019s do some coding!&nbsp;<strong>Before moving on, a note on disclaimer: This article\u2019s sole purpose is to educate people and must be considered as an information piece, but not as an investment advice.<\/strong><\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"2f7c\">Implementation in Python<\/h1>\n\n\n\n<p id=\"2cba\">The coding part is classified into various steps as follows:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-background\" style=\"background-color:#abb7c217\"><strong>1. Importing Packages<\/strong>\n<strong>2. API Key Activation\n3. Extracting Historical Stock Data\n4. Keltner Channel Calculation\n5. Creating the Breakout Trading Strategy\n6. Plotting the Trading Lists\n7. Creating our Position\n8. Backtesting\n9. SPY ETF Comparison<\/strong><\/pre>\n\n\n\n<p id=\"5130\">We will be following the order mentioned in the above list and buckle up your seat belts to follow every upcoming coding part.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"053b\">Step-1: Importing Packages<\/h2>\n\n\n\n<p id=\"c076\">Importing the required packages into the Python environment is a non-skippable step. The primary packages are going to be eod for extracting historical stock data, Pandas for data formatting and manipulations, NumPy to work with arrays and for complex functions, and Matplotlib for plotting purposes. The secondary packages are going to be Math for mathematical functions and Termcolor for font customization (optional).<\/p>\n\n\n\n<p id=\"8aec\"><strong>Python Implementation:<\/strong><\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code lang=\"python\" class=\"language-python\"># IMPORTING PACKAGES\n\nfrom eodhd import APIClient\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport pandas as pd\nfrom termcolor import colored as cl\nfrom math import floor\n\nplt.rcParams['figure.figsize'] = (20,10)\nplt.style.use('fivethirtyeight')<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p id=\"253e\">With the required packages imported into Python, we can proceed to fetch historical data for Intel using EODHD&#8217;s <code>eodhd<\/code> Python library. Also, if you haven\u2019t installed any of the imported packages, make sure to do so using the&nbsp;<code>pip<\/code>&nbsp;command in your terminal.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"a9f4\">Step-2: API Key Activation<\/h2>\n\n\n\n<p id=\"c710\">It is essential to register the EODHD API key with the package in order to use its functions. If you don\u2019t have an EODHD API key, firstly, head over to their&nbsp;<a href=\"https:\/\/eodhistoricaldata.com\/?utm_source=medium&amp;utm_medium=post&amp;utm_campaign=a_python_package_to_retrieve_financial_data_part2\" rel=\"noreferrer noopener\" target=\"_blank\">website<\/a>, then, finish the&nbsp;<a href=\"https:\/\/eodhistoricaldata.com\/register?utm_source=medium&amp;utm_medium=post&amp;utm_campaign=a_python_package_to_retrieve_financial_data_part2\" rel=\"noreferrer noopener\" target=\"_blank\">registration<\/a>&nbsp;process to create an EODHD account, and finally, navigate to the \u2018<a href=\"https:\/\/eodhistoricaldata.com\/cp\/settings?utm_source=medium&amp;utm_medium=post&amp;utm_campaign=a_python_package_to_retrieve_financial_data_part2\" rel=\"noreferrer noopener\" target=\"_blank\">Settings<\/a>\u2019 page where you could find your secret EODHD API key. It is important to ensure that this secret API key is not revealed to anyone. You can activate the API key by following this code:<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code lang=\"python\" class=\"language-python\">api_key = '&lt;YOUR API KEY&gt;'\nclient = APIClient(api_key)<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p id=\"fd3d\">The code is pretty simple. In the first line, we are storing the secret EODHD API key into the&nbsp;<code>api_key<\/code>&nbsp;and then in the second line, we are using the&nbsp;<code>APIClient<\/code>&nbsp;class provided by the&nbsp;<code>eodhd<\/code>&nbsp;package to activate the API key and stored the response in the&nbsp;<code>client<\/code>&nbsp;variable.<\/p>\n\n\n\n<p id=\"4634\">Note that you need to replace&nbsp;<code>&lt;YOUR API KEY&gt;<\/code>&nbsp;with your secret EODHD API key. Apart from directly storing the API key with text, there are other ways for better security such as utilizing environmental variables, and so on.<\/p>\n\n\n\n<p class=\"has-text-align-center\"><a class=\"maxbutton-1 maxbutton maxbutton-subscribe-to-api external-css btn\" href=\"https:\/\/eodhd.com\/register\"><span class='mb-text'>Register &amp; Get Data<\/span><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"8568\">Step-3: Extracting Historical Data<\/h2>\n\n\n\n<p id=\"ac40\">Before heading into the extraction part, it is first essential to have some background about historical or end-of-day data. In a nutshell, historical data consists of information accumulated over a period of time. It helps in identifying patterns and trends in the data. It also assists in studying market behavior. Now, you can easily extract the historical data of any tradeable assets using the&nbsp;<code>eod<\/code>&nbsp;package by following this code:<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code lang=\"python\" class=\"language-python\"># EXTRACTING HISTORICAL DATA\n\ndef extract_historical_data(ticker, start_date):\n    json_resp = client.get_eod_historical_stock_market_data(symbol = ticker, period = 'd', from_date = start_date, order = 'a')\n    df = pd.DataFrame(json_resp)\n    df = df.set_index('date')\n    df.index = pd.to_datetime(df.index)\n    return df\n\nintc = get_historical_data('INTC', '2020-01-01')\nintc.tail()<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p id=\"c103\">In the above code, we are using the&nbsp;<code>get_eod_historical_stock_market_data<\/code>&nbsp;function provided by the <code>eodhd<\/code> package to extract the split-adjusted historical stock data of Intel. The function consists of the following parameters:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>the&nbsp;<code>ticker<\/code>&nbsp;parameter where the symbol of the stock we are interested in extracting the data should be mentioned<\/li>\n\n\n\n<li>the&nbsp;<code>period<\/code>&nbsp;refers to the time interval between each data point (one-day interval in our case).<\/li>\n\n\n\n<li>the&nbsp;<code>from_date<\/code>&nbsp;and&nbsp;<code>to_date<\/code>&nbsp;parameters which indicate the starting and ending date of the data respectively. The format of the input should be \u201cYYYY-MM-DD\u201d<\/li>\n\n\n\n<li>the&nbsp;<code>order<\/code>&nbsp;parameter which is an optional parameter that can be used to order the dataframe either in ascending (<code>a<\/code>) or descending (<code>d<\/code>). It is ordered based on the dates.<\/li>\n<\/ul>\n\n\n\n<p id=\"a294\">After extracting the historical data, we are performing some data-wrangling processes to clean and format the data. The final dataframe looks like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"782\" height=\"456\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-9.png\" alt=\"\" class=\"wp-image-2128\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"2498\">Step-4: Keltner Channel Calculation<\/h2>\n\n\n\n<p id=\"eb8a\">In this step, we are going to calculate the components of the Keltner Channel indicator by following the methods we discussed before.<\/p>\n\n\n\n<p id=\"2969\"><strong>Python Implementation:<\/strong><\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code lang=\"python\" class=\"language-python\"># KELTNER CHANNEL CALCULATION\n\ndef get_kc(high, low, close, kc_lookback, multiplier, atr_lookback):\n    tr1 = pd.DataFrame(high - low)\n    tr2 = pd.DataFrame(abs(high - close.shift()))\n    tr3 = pd.DataFrame(abs(low - close.shift()))\n    frames = [tr1, tr2, tr3]\n    tr = pd.concat(frames, axis = 1, join = 'inner').max(axis = 1)\n    atr = tr.ewm(alpha = 1\/atr_lookback).mean()\n    \n    kc_middle = close.ewm(kc_lookback).mean()\n    kc_upper = close.ewm(kc_lookback).mean() + multiplier * atr\n    kc_lower = close.ewm(kc_lookback).mean() - multiplier * atr\n    \n    return kc_middle, kc_upper, kc_lower\n    \nintc = intc.iloc[:,:4]\nintc['kc_middle'], intc['kc_upper'], intc['kc_lower'] = get_kc(intc['high'], intc['low'], intc['close'], 20, 2, 10)\nintc.tail()<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p id=\"051f\"><strong>Output:<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1152\" height=\"467\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-10.png\" alt=\"\" class=\"wp-image-2129\"\/><\/figure>\n\n\n\n<p id=\"e563\"><strong>Code Explanation:<\/strong>&nbsp;We are first defining a function named \u2018get_kc\u2019 that takes a stock\u2019s high (\u2018high\u2019), low (\u2018low\u2019), and closing price data (\u2018close\u2019), the lookback period for the Keltner Channel (\u2018kc_lookback\u2019), the multiplier value (\u2018multiplier), and the lookback period for the ATR (\u2018atr_lookback\u2019) as parameters. The code inside the function can be separated into two parts: ATR calculation, and the Keltner Channel calculation.<\/p>\n\n\n\n<p id=\"efbc\">ATR calculation: To determine the readings of the Average True Range, we are first calculating the three differences and stored them into their respective variables. Then we are combining all three differences into one dataframe using the \u2018concat\u2019 function and took the maximum values out of the three collective differences to determine the True Range. Then, using the \u2018ewm\u2019 and \u2018mean\u2019 functions, we are taking the customized Moving Average of True Range for a specified number of periods to get the ATR values.<\/p>\n\n\n\n<p id=\"9a85\">Keltner Channel calculation: Utilizing the previously calculated ATR values, we are first calculating the middle line of the Keltner Channel by taking the EMA of ATR for a specified number of periods. Then comes the calculation of both the upper and lower bands. We are substituting the ATR values into the upper and lower bands formula we discussed before to get the readings of each of them. Finally, we are returning and calling the created function to get the Keltner Channel values of Intel.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"4462\">Step-5: Creating the trading strategy<\/h2>\n\n\n\n<p id=\"8c6b\">In this step, we are going to implement the discussed Keltner Channel indicator breakout trading strategy in python.<\/p>\n\n\n\n<p id=\"43d8\"><strong>Python Implementation:<\/strong><\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code lang=\"python\" class=\"language-python\"># KELTNER CHANNEL STRATEGY\n\ndef implement_kc_strategy(prices, kc_upper, kc_lower):\n    buy_price = []\n    sell_price = []\n    kc_signal = []\n    signal = 0\n    \n    for i in range(len(prices)):\n        if prices[i] &lt; kc_lower[i] and prices[i+1] &gt; prices[i]:\n            if signal != 1:\n                buy_price.append(prices[i])\n                sell_price.append(np.nan)\n                signal = 1\n                kc_signal.append(signal)\n            else:\n                buy_price.append(np.nan)\n                sell_price.append(np.nan)\n                kc_signal.append(0)\n        elif prices[i] &gt; kc_upper[i] and prices[i+1] &lt; prices[i]:\n            if signal != -1:\n                buy_price.append(np.nan)\n                sell_price.append(prices[i])\n                signal = -1\n                kc_signal.append(signal)\n            else:\n                buy_price.append(np.nan)\n                sell_price.append(np.nan)\n                kc_signal.append(0)\n        else:\n            buy_price.append(np.nan)\n            sell_price.append(np.nan)\n            kc_signal.append(0)\n            \n    return buy_price, sell_price, kc_signal\n\nbuy_price, sell_price, kc_signal = implement_kc_strategy(intc['close'], intc['kc_upper'], intc['kc_lower'])<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p id=\"9afe\"><strong>Code Explanation:<\/strong>&nbsp;First, we are defining a function named \u2018implement_kc_strategy\u2019 which takes the stock prices (\u2018prices\u2019), and the components of the Keltner Channel indicator (\u2018kc_upper\u2019, and \u2018kc_lower\u2019) as parameters.<\/p>\n\n\n\n<p id=\"cb3a\">Inside the function, we are creating three empty lists (buy_price, sell_price, and kc_signal) in which the values will be appended while creating the trading strategy.<\/p>\n\n\n\n<p id=\"b816\">After that, we are implementing the trading strategy through a for-loop. Inside the for-loop, we are passing certain conditions, and if the conditions are satisfied, the respective values will be appended to the empty lists. If the condition to buy the stock gets satisfied, the buying price will be appended to the \u2018buy_price\u2019 list, and the signal value will be appended as 1 representing to buy the stock. Similarly, if the condition to sell the stock gets satisfied, the selling price will be appended to the \u2018sell_price\u2019 list, and the signal value will be appended as -1 representing to sell the stock.<\/p>\n\n\n\n<p id=\"e112\">Finally, we are returning the lists appended with values. Then, we are calling the created function and stored the values into their respective variables. The list doesn\u2019t make any sense unless we plot the values. So, let\u2019s plot the values of the created trading lists.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"c7da\">Step-6: Plotting the trading signals<\/h2>\n\n\n\n<p id=\"b242\">In this step, we are going to plot the created trading lists to make sense out of them.<\/p>\n\n\n\n<p id=\"8fd2\"><strong>Python Implementation:<\/strong><\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code lang=\"python\" class=\"language-python\"><strong># TRADING SIGNALS PLOT<br><\/strong><br>plt.plot(intc['close'], linewidth = 2, label = 'INTC')<br>plt.plot(intc['kc_upper'], linewidth = 2, color = 'orange', linestyle = '--', label = 'KC UPPER 20')<br>plt.plot(intc['kc_middle'], linewidth = 1.5, color = 'grey', label = 'KC MIDDLE 20')<br>plt.plot(intc['kc_lower'], linewidth = 2, color = 'orange', linestyle = '--', label = 'KC LOWER 20')<br>plt.plot(intc.index, buy_price, marker = '^', color = 'green', markersize = 15, linewidth = 0, label = 'BUY SIGNAL')<br>plt.plot(intc.index, sell_price, marker = 'v', color= 'r', markersize = 15, linewidth = 0, label = 'SELL SIGNAL')<br>plt.legend(loc = 'lower right')<br>plt.title('INTC KELTNER CHANNEL 20 TRADING SIGNALS')<br>plt.show()<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p id=\"ad5c\"><strong>Output:<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1789\" height=\"884\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-11.png\" alt=\"\" class=\"wp-image-2130\"\/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p id=\"04b9\"><strong>Code Explanation:<\/strong>&nbsp;We are plotting the readings of the components of the Keltner Channel indicator along with the buy and sell signals generated by the breakout trading strategy. We can observe that whenever the closing price line from above to below the lower band line and the current closing price is lower than the next closing price, a green-colored buy signal is plotted in the chart. Similarly, whenever the closing price line crosses from below to above the upper band and the current closing price is greater than the next closing price, a red-colored sell signal is plotted in the chart.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"bcf2\">Step-7: Creating our Position<\/h2>\n\n\n\n<p id=\"4c0d\">In this step, we are going to create a list that indicates 1 if we hold the stock or 0 if we don\u2019t own or hold the stock.<\/p>\n\n\n\n<p id=\"8a97\"><strong>Python Implementation:<\/strong><\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code lang=\"python\" class=\"language-python\"><strong># STOCK POSITION<br><\/strong><br>position = []<br>for i in range(len(kc_signal)):<br>    if kc_signal[i] &gt; 1:<br>        position.append(0)<br>    else:<br>        position.append(1)<br>        <br>for i in range(len(intc['close'])):<br>    if kc_signal[i] == 1:<br>        position[i] = 1<br>    elif kc_signal[i] == -1:<br>        position[i] = 0<br>    else:<br>        position[i] = position[i-1]<br>        <br>close_price = intc['close']<br>kc_upper = intc['kc_upper']<br>kc_lower = intc['kc_lower']<br>kc_signal = pd.DataFrame(kc_signal).rename(columns = {0:'kc_signal'}).set_index(intc.index)<br>position = pd.DataFrame(position).rename(columns = {0:'kc_position'}).set_index(intc.index)<br><br>frames = [close_price, kc_upper, kc_lower, kc_signal, position]<br>strategy = pd.concat(frames, join = 'inner', axis = 1)<br><br>strategy<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p id=\"e15e\"><strong>Output:<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1019\" height=\"472\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-12.png\" alt=\"\" class=\"wp-image-2131\"\/><\/figure>\n\n\n\n<p id=\"aed7\"><strong>Code Explanation:<\/strong>&nbsp;First, we are creating an empty list named \u2018position\u2019. We are passing two for-loops, one is to generate values for the \u2018position\u2019 list to just match the length of the \u2018signal\u2019 list. The other for-loop is the one we are using to generate actual position values. Inside the second for-loop, we are iterating over the values of the \u2018signal\u2019 list, and the values of the \u2018position\u2019 list get appended concerning which condition gets satisfied. The value of the position remains 1 if we hold the stock or remains 0 if we sold or don\u2019t own the stock. Finally, we are doing some data manipulations to combine all the created lists into one dataframe.<\/p>\n\n\n\n<p id=\"b632\">From the output being shown, we can see that in the first row our position in the stock has remained 1 (since there isn\u2019t any change in the Keltner Channel indicator signal) but our position suddenly turned to -1 as we sold the stock when the Keltner Channel indicator trading signal represents a sell signal (-1). Our position will remain 0 until some changes in the trading signal occur. Now it\u2019s time to implement some backtesting process!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"5e88\">Step-8: Backtesting<\/h2>\n\n\n\n<p id=\"c079\">Before moving on, it is essential to know what backtesting is. Backtesting is the process of seeing how well our trading strategy has performed on the given stock data. In our case, we are going to implement a backtesting process for our Keltner Channel indicator trading strategy over the Intel stock data.<\/p>\n\n\n\n<p id=\"d5d1\"><strong>Python Implementation:<\/strong><\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code lang=\"python\" class=\"language-python\"><strong># BACKTESTING\n<\/strong>\nintc_ret = pd.DataFrame(np.diff(intc['close'])).rename(columns = {0:'returns'})\nkc_strategy_ret = []\n\nfor i in range(len(intc_ret)):\n    returns = intc_ret['returns'][i]*strategy['kc_position'][i]\n    kc_strategy_ret.append(returns)\n    \nkc_strategy_ret_df = pd.DataFrame(kc_strategy_ret).rename(columns = {0:'kc_returns'})\ninvestment_value = 100000\nkc_investment_ret = []\n\nfor i in range(len(kc_strategy_ret_df['kc_returns'])):\n    number_of_stocks = floor(investment_value\/intc['close'][i])\n    returns = number_of_stocks*kc_strategy_ret_df['kc_returns'][i]\n    kc_investment_ret.append(returns)\n\nkc_investment_ret_df = pd.DataFrame(kc_investment_ret).rename(columns = {0:'investment_returns'})\ntotal_investment_ret = round(sum(kc_investment_ret_df['investment_returns']), 2)\nprofit_percentage = floor((total_investment_ret\/investment_value)*100)\nprint(cl('Profit gained from the KC strategy by investing $100k in INTC : {}'.format(total_investment_ret), attrs = ['bold']))\nprint(cl('Profit percentage of the KC strategy : {}%'.format(profit_percentage), attrs = ['bold']))<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p id=\"53fb\"><strong>Output:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-background\" style=\"background-color:#abb7c217\"><strong>Profit gained from the KC strategy by investing $100k in INTC : 82463.64\nProfit percentage of the KC strategy : 82%<\/strong><\/pre>\n\n\n\n<p id=\"62a1\"><strong>Code Explanation:<\/strong>&nbsp;First, we are calculating the returns of the Intel stock using the \u2018diff\u2019 function provided by the NumPy package and we have stored it as a dataframe into the \u2018intc_ret\u2019 variable. Next, we are passing a for-loop to iterate over the values of the \u2018intc_ret\u2019 variable to calculate the returns we gained from our Keltner Channel indicator trading strategy, and these returns values are appended to the \u2018kc_strategy_ret\u2019 list. Next, we are converting the \u2018kc_strategy_ret\u2019 list into a dataframe and stored it into the \u2018kc_strategy_ret_df\u2019 variable.<\/p>\n\n\n\n<p id=\"5092\">Next comes the backtesting process. We are going to backtest our strategy by investing a hundred thousand USD into our trading strategy. So first, we are storing the amount of investment into the \u2018investment_value\u2019 variable. After that, we are calculating the number of Intel stocks we can buy using the investment amount. You can notice that I\u2019ve used the \u2018floor\u2019 function provided by the Math package because, while dividing the investment amount by the closing price of Intel stock, it spits out an output with decimal numbers. The number of stocks should be an integer but not a decimal number. Using the \u2018floor\u2019 function, we can cut out the decimals. Remember that the \u2018floor\u2019 function is way more complex than the \u2018round\u2019 function. Then, we are passing a for-loop to find the investment returns followed by some data manipulation tasks.<\/p>\n\n\n\n<p id=\"1d5e\">Finally, we are printing the total return we got by investing a hundred thousand into our trading strategy and it is revealed that we have made an approximate profit of eighty-two thousand USD in one year. That\u2019s not bad at all! Now, let\u2019s compare our returns with SPY ETF (an ETF designed to track the S&amp;P 500 stock market index) returns.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"0f33\">Step-9: SPY ETF Comparison<\/h2>\n\n\n\n<p id=\"8aac\">This step is optional but it is highly recommended as we can get an idea of how well our trading strategy performs against a benchmark (SPY ETF). In this step, we are going to extract the data of the SPY ETF using the \u2018get_historical_data\u2019 function we created and compare the returns we get from the SPY ETF with our Keltner Channel breakout trading strategy returns on Intel.<\/p>\n\n\n\n<p id=\"67fd\"><strong>Python Implementation:<\/strong><\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code lang=\"python\" class=\"language-python\"><strong># SPY ETF COMPARISON\n<\/strong>\ndef get_benchmark(start_date, investment_value):\n    spy = get_historical_data('SPY', start_date)['close']\n    benchmark = pd.DataFrame(np.diff(spy)).rename(columns = {0:'benchmark_returns'})\n    \n    investment_value = investment_value\n    benchmark_investment_ret = []\n    \n    for i in range(len(benchmark['benchmark_returns'])):\n        number_of_stocks = floor(investment_value\/spy[i])\n        returns = number_of_stocks*benchmark['benchmark_returns'][i]\n        benchmark_investment_ret.append(returns)\n\n    benchmark_investment_ret_df = pd.DataFrame(benchmark_investment_ret).rename(columns = {0:'investment_returns'})\n    return benchmark_investment_ret_df\n\nbenchmark = get_benchmark('2020-01-01', 100000)\ninvestment_value = 100000\ntotal_benchmark_investment_ret = round(sum(benchmark['investment_returns']), 2)\nbenchmark_profit_percentage = floor((total_benchmark_investment_ret\/investment_value)*100)\nprint(cl('Benchmark profit by investing $100k : {}'.format(total_benchmark_investment_ret), attrs = ['bold']))\nprint(cl('Benchmark Profit percentage : {}%'.format(benchmark_profit_percentage), attrs = ['bold']))\nprint(cl('KC Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage), attrs = ['bold']))<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p id=\"0367\"><strong>Output:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-background\" style=\"background-color:#abb7c217\"><strong>Benchmark profit by investing $100k : 41144.52\nBenchmark Profit percentage : 41%\nKC Strategy profit is 41% higher than the Benchmark Profit<\/strong><\/pre>\n\n\n\n<p id=\"9333\"><strong>Code Explanation:<\/strong>&nbsp;The code used in this step is almost similar to the one used in the previous backtesting step but, instead of investing in Intel, we are investing in SPY ETF by not implementing any trading strategies. From the output, we can see that our Keltner Channel breakout trading strategy has outperformed the SPY ETF by 41%. That\u2019s great!<\/p>\n\n\n\n<p class=\"has-text-align-center\"><a class=\"maxbutton-1 maxbutton maxbutton-subscribe-to-api external-css btn\" href=\"https:\/\/eodhd.com\/register\"><span class='mb-text'>Register &amp; Get Data<\/span><\/a><\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"69f9\">Final Thoughts!<\/h1>\n\n\n\n<p id=\"5ddf\">After a long process of crushing both theory and coding parts, we have successfully learned what the Keltner Channel indicator is all about, the math behind the indicator, and finally, how to build the indicator from scratch and construct the breakout trading strategy in Python. We also did manage to get some nice results and in fact, apart from surpassing the returns of the SPY ETF, we exceeded the actual Intel stock returns itself with our breakout strategy.<\/p>\n\n\n\n<p id=\"620f\">I often talk about strategy tuning or optimization in my articles and today, we really did implement it by tuning and making some changes to the traditional breakout strategy. As a result, we were able to outdo the returns of the actual market itself. This is just one small example of how to tune a strategy and how the results will be impacted accordingly but, there is a lot more to be explored. Strategy optimization is not only about tuning or making some changes to the traditional strategies that have existed for a long time but also about creating an optimal trading environment and this includes the broker you are using for trading purposes, the risk management system, and so on. So it\u2019s highly recommended to have a look at these spaces to take your strategies to a whole new level.<\/p>\n\n\n\n<p id=\"d056\">With that said, you have reached the end of the article. Hope you learned something new and useful from this article.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Studying technical indicators, you will definitely come across a list comprising curated indicators that are widely considered as \u2018must-know\u2019 indicators that need to be learned before getting your hands dirty in the real-world market. The indicator we are going to explore today adds to this list, given its performance in the market: the Keltner Channel [&hellip;]<\/p>\n","protected":false},"author":18,"featured_media":2117,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[65,59],"tags":[],"coding-language":[30],"ready-to-go-solution":[56],"qualification":[31,32],"financial-apis-category":[36],"financial-apis-manuals":[39,38,40],"class_list":["post-2116","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-backtesting-strategies-examples","category-technical-analysis-examples","coding-language-python","ready-to-go-solution-eodhd-python-financial-library","qualification-experienced","qualification-guru","financial-apis-category-stock-market-prices","financial-apis-manuals-end-of-day","financial-apis-manuals-intraday","financial-apis-manuals-technical-indicators","has_thumb"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v21.9 (Yoast SEO v26.7) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Algorithmic Trading with Keltner Channel: A Python Tutorial | EODHD APIs Academy<\/title>\n<meta name=\"description\" content=\"Learn about the Keltner Channel, a powerful technical indicator for algorithmic trading in Python. Build and backtest a trading strategy using this indicator.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Algorithmic Trading with the Keltner Channel in Python\" \/>\n<meta property=\"og:description\" content=\"Learn about the Keltner Channel, a powerful technical indicator for algorithmic trading in Python. Build and backtest a trading strategy using this indicator.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python\" \/>\n<meta property=\"og:site_name\" content=\"Financial Academy\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/eodhistoricaldata\" \/>\n<meta property=\"article:published_time\" content=\"2023-09-28T08:58:21+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-02-05T13:11:13+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-6.png\" \/>\n\t<meta property=\"og:image:width\" content=\"828\" \/>\n\t<meta property=\"og:image:height\" content=\"552\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Nikhil Adithyan\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@EOD_data\" \/>\n<meta name=\"twitter:site\" content=\"@EOD_data\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Nikhil Adithyan\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"17 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#article\",\"isPartOf\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python\"},\"author\":{\"name\":\"Nikhil Adithyan\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/67681e71050cf7d8d0efb91fee5f0402\"},\"headline\":\"Algorithmic Trading with the Keltner Channel in Python\",\"datePublished\":\"2023-09-28T08:58:21+00:00\",\"dateModified\":\"2025-02-05T13:11:13+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python\"},\"wordCount\":3497,\"publisher\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#organization\"},\"image\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#primaryimage\"},\"thumbnailUrl\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-6.png\",\"articleSection\":[\"Backtesting Strategies Examples\",\"Technical Analysis Examples\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python\",\"name\":\"Algorithmic Trading with Keltner Channel: A Python Tutorial | EODHD APIs Academy\",\"isPartOf\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#primaryimage\"},\"image\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#primaryimage\"},\"thumbnailUrl\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-6.png\",\"datePublished\":\"2023-09-28T08:58:21+00:00\",\"dateModified\":\"2025-02-05T13:11:13+00:00\",\"description\":\"Learn about the Keltner Channel, a powerful technical indicator for algorithmic trading in Python. Build and backtest a trading strategy using this indicator.\",\"breadcrumb\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#primaryimage\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-6.png\",\"contentUrl\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-6.png\",\"width\":828,\"height\":552,\"caption\":\"Algorithmic Trading with the Keltner Channel in Python\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/eodhd.com\/financial-academy\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Algorithmic Trading with the Keltner Channel in Python\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#website\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/\",\"name\":\"Financial APIs Academy | EODHD\",\"description\":\"Financial Stock Market Academy\",\"publisher\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/eodhd.com\/financial-academy\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#organization\",\"name\":\"EODHD (EOD Historical Data)\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/12\/EODHD-Logo.png\",\"contentUrl\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/12\/EODHD-Logo.png\",\"width\":159,\"height\":82,\"caption\":\"EODHD (EOD Historical Data)\"},\"image\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/eodhistoricaldata\",\"https:\/\/x.com\/EOD_data\",\"https:\/\/www.reddit.com\/r\/EODHistoricalData\/\",\"https:\/\/eod-historical-data.medium.com\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/67681e71050cf7d8d0efb91fee5f0402\",\"name\":\"Nikhil Adithyan\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/eb53ce41bde412555cee22b8b4c09c2ff51625fff05ba3696b20cac7a7c0d938?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/eb53ce41bde412555cee22b8b4c09c2ff51625fff05ba3696b20cac7a7c0d938?s=96&d=mm&r=g\",\"caption\":\"Nikhil Adithyan\"},\"description\":\"Founder at BacktestZone | Streamlit Student Ambassador | FinTech &amp; Quantitative Finance enthusiast\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/author\/nikhiladithyan\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Algorithmic Trading with Keltner Channel: A Python Tutorial | EODHD APIs Academy","description":"Learn about the Keltner Channel, a powerful technical indicator for algorithmic trading in Python. Build and backtest a trading strategy using this indicator.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python","og_locale":"en_US","og_type":"article","og_title":"Algorithmic Trading with the Keltner Channel in Python","og_description":"Learn about the Keltner Channel, a powerful technical indicator for algorithmic trading in Python. Build and backtest a trading strategy using this indicator.","og_url":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python","og_site_name":"Financial Academy","article_publisher":"https:\/\/www.facebook.com\/eodhistoricaldata","article_published_time":"2023-09-28T08:58:21+00:00","article_modified_time":"2025-02-05T13:11:13+00:00","og_image":[{"width":828,"height":552,"url":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-6.png","type":"image\/png"}],"author":"Nikhil Adithyan","twitter_card":"summary_large_image","twitter_creator":"@EOD_data","twitter_site":"@EOD_data","twitter_misc":{"Written by":"Nikhil Adithyan","Est. reading time":"17 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#article","isPartOf":{"@id":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python"},"author":{"name":"Nikhil Adithyan","@id":"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/67681e71050cf7d8d0efb91fee5f0402"},"headline":"Algorithmic Trading with the Keltner Channel in Python","datePublished":"2023-09-28T08:58:21+00:00","dateModified":"2025-02-05T13:11:13+00:00","mainEntityOfPage":{"@id":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python"},"wordCount":3497,"publisher":{"@id":"https:\/\/eodhd.com\/financial-academy\/#organization"},"image":{"@id":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#primaryimage"},"thumbnailUrl":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-6.png","articleSection":["Backtesting Strategies Examples","Technical Analysis Examples"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python","url":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python","name":"Algorithmic Trading with Keltner Channel: A Python Tutorial | EODHD APIs Academy","isPartOf":{"@id":"https:\/\/eodhd.com\/financial-academy\/#website"},"primaryImageOfPage":{"@id":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#primaryimage"},"image":{"@id":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#primaryimage"},"thumbnailUrl":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-6.png","datePublished":"2023-09-28T08:58:21+00:00","dateModified":"2025-02-05T13:11:13+00:00","description":"Learn about the Keltner Channel, a powerful technical indicator for algorithmic trading in Python. Build and backtest a trading strategy using this indicator.","breadcrumb":{"@id":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#primaryimage","url":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-6.png","contentUrl":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-6.png","width":828,"height":552,"caption":"Algorithmic Trading with the Keltner Channel in Python"},{"@type":"BreadcrumbList","@id":"https:\/\/eodhd.com\/financial-academy\/backtesting-strategies-examples\/algorithmic-trading-with-the-keltner-channel-in-python#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/eodhd.com\/financial-academy\/"},{"@type":"ListItem","position":2,"name":"Algorithmic Trading with the Keltner Channel in Python"}]},{"@type":"WebSite","@id":"https:\/\/eodhd.com\/financial-academy\/#website","url":"https:\/\/eodhd.com\/financial-academy\/","name":"Financial APIs Academy | EODHD","description":"Financial Stock Market Academy","publisher":{"@id":"https:\/\/eodhd.com\/financial-academy\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/eodhd.com\/financial-academy\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/eodhd.com\/financial-academy\/#organization","name":"EODHD (EOD Historical Data)","url":"https:\/\/eodhd.com\/financial-academy\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/eodhd.com\/financial-academy\/#\/schema\/logo\/image\/","url":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/12\/EODHD-Logo.png","contentUrl":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/12\/EODHD-Logo.png","width":159,"height":82,"caption":"EODHD (EOD Historical Data)"},"image":{"@id":"https:\/\/eodhd.com\/financial-academy\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/eodhistoricaldata","https:\/\/x.com\/EOD_data","https:\/\/www.reddit.com\/r\/EODHistoricalData\/","https:\/\/eod-historical-data.medium.com\/"]},{"@type":"Person","@id":"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/67681e71050cf7d8d0efb91fee5f0402","name":"Nikhil Adithyan","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/eb53ce41bde412555cee22b8b4c09c2ff51625fff05ba3696b20cac7a7c0d938?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/eb53ce41bde412555cee22b8b4c09c2ff51625fff05ba3696b20cac7a7c0d938?s=96&d=mm&r=g","caption":"Nikhil Adithyan"},"description":"Founder at BacktestZone | Streamlit Student Ambassador | FinTech &amp; Quantitative Finance enthusiast","url":"https:\/\/eodhd.com\/financial-academy\/author\/nikhiladithyan"}]}},"jetpack_featured_media_url":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/09\/image-6.png","jetpack_shortlink":"https:\/\/wp.me\/pdOdVT-y8","jetpack_sharing_enabled":true,"acf":[],"_links":{"self":[{"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/posts\/2116","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/users\/18"}],"replies":[{"embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/comments?post=2116"}],"version-history":[{"count":25,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/posts\/2116\/revisions"}],"predecessor-version":[{"id":6277,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/posts\/2116\/revisions\/6277"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/media\/2117"}],"wp:attachment":[{"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/media?parent=2116"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/categories?post=2116"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/tags?post=2116"},{"taxonomy":"coding-language","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/coding-language?post=2116"},{"taxonomy":"ready-to-go-solution","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/ready-to-go-solution?post=2116"},{"taxonomy":"qualification","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/qualification?post=2116"},{"taxonomy":"financial-apis-category","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/financial-apis-category?post=2116"},{"taxonomy":"financial-apis-manuals","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/financial-apis-manuals?post=2116"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}