top of page

# Stock Trade Automation with Commodity Channel Index in Python

A step-by-step guide to making seamless stock trades by algorithmic trading with a powerful technical indicator in python

## Introduction

With the increasing amount of technological inventions, the methods of stock trading have also evolved since then. When we look back to the distant past like the 1980s or even 1990s, Wall Street would look something like a ground to hustle among the traders. We could barely understand what’s going around and traders would yell and shout among themselves to buy a stock in an auction. Could you believe that stocks are bought in an auction? Thanks to the researchers and engineers who built and revolutionized the stock market division by introducing it to the world of technology.

Instead, they build their conditions or algorithms using a tool called technical indicators. There is a basket of technical indicators out there but in this article, we are going to discuss a special one called Commodity Channel Index, shortly known as CCI and how can it be implemented to trade stocks in python. Without further ado, let’s dive into the article.

## Commodity Channel Index (CCI)

The CCI is a leading indicator that measures the difference between the current price and the historical average price over a specified period of time, i.e. when the value of CCI reveals extreme positive readings, then it is considered that the current price is well above the historical average. Likewise, when the value of CCI reveals extreme negative readings, then it is considered that the current price is below the historical average. By judging its name, people might think CCI is used to trade only commodities but that's not it. It can be used to trade in any form of market let it be equity or forex. The most general setting of CCI is 20 as the specified number of period. The formula to calculate CCI with 20 as the number of periods can be represented as follows:

```
CCI = (TP - SMA 20 OF TP) / (0.15 * MEAN DEVIATION)

where,
Typical Price (TP) = (HIGH + LOW + CLOSE) / 3
SMA 20 of TP = Typical Price / 20
Mean Deviation = Absolute values of Typical Price / 20

```

This indicator is unique from the rest of the leading indicators as many of the leading ones have values bound between 0 to 100 but CCI can reach extreme values acting as an unbounded oscillator. Since CCI has indefinite values, traders determine the overbought and oversold levels for individual assets by looking for extreme points of CCI where the price is reversed. For example, imagine the forex pair EURUSD (Euro/USD) which reaches an extreme CCI level of 150 and suddenly a change in trend occurs (price reversal), and hence our overbought level for EURUSD would be 150. Similarly, imagine the same pair has reached an extreme negative CCI level of -150 and suddenly a change in trend occurs, and thus our oversold level for EURUSD would -150. This way of determining overbought and oversold levels is called a Reversal Strategy.

About our trading strategy: Our trading strategy is going to be a Reversal strategy where we are going to analyze the CCI values of Facebook and determine the levels of overbought and oversold. The strategy will reveal a buy signal when the previous CCI value of Facebook is greater than the oversold level and the current CCI value is lesser than the oversold level. Likewise, the strategy will reveal a sell signal when the previous CCI value of Facebook is lesser than the overbought level and the current CCI value is greater than the overbought level. Our trading strategy can be represented as follows:

```
IF PREVIOUS CCI > OVERSOLD LEVEL AND CCI < COVERSOLD LEVEL = BUY
IF PREVIOUS CCI < OVERBOUGHT LEVEL AND CCI > COVERBOUGHT LEVEL = SELL

```

Now that we have an understanding of what CCI means and how can it be used to build a trading strategy. Let’s use Python to implement our own CCI-based trading strategy and backtest it to see how well it performs in the real-world market.

Before moving on, a note on disclaimer: This article’s sole purpose is to educate people and must be considered as an information piece but not as investment advice or so.

## Python Implementation

The coding part is classified into various steps as follows:

```
1. Importing Packages
2. Extracting Data from Alpha Vantage
3. Extracting the CCI values from Alpha Vantage
4. CCI Plot
7. Creating our Position
8. Backtesting

```

We will be following the order mentioned in the above list and buckle up your seat belts to follow every upcoming coding part.

### Step-1: Importing Packages

Importing the required packages into the python environment is a non-skippable step. The primary packages are going to be Pandas to work with data, NumPy to work with arrays and for complex functions, Matplotlib for plotting purposes, and Requests to make API calls. The secondary packages are going to be Math for mathematical functions and Termcolor for font customization (optional).

Python Implementation:

```
import pandas as pd
import numpy as np
import requests
from termcolor import colored as cl
from math import floor
import matplotlib.pyplot as plt
plt.rcParams[‘figure.figsize’] = (20, 10)
plt.style.use(‘fivethirtyeight’)

```

Now that we have imported all the essential packages into our python environment. Let’s proceed with pulling the historical data of Facebook with Alpha Vantage’s powerful stock API.

### Step-2: Extracting Data from Alpha Vantage

In this step, we are going to pull the historical data of Facebook using an API endpoint provided by Alpha Vantage. Before that, a note on Alpha Vantage: Alpha Vantage provides free stock APIs through which users can access a wide range of data like real-time updates, and historical data on equities, currencies, and cryptocurrencies. Make sure that you have an account on Alpha Vantage, only then, you will be able to access your secret API key (a crucial element for pulling data using an API).

Python Implementation:

```
def get_historical_data(symbol, start_date = None):
api_key = open(r'api_key.txt')
raw_df = requests.get(api_url).json()
df = pd.DataFrame(raw_df[f'Time Series (Daily)']).T
df = df.rename(columns = {'1. open': 'open', '2. high': 'high', '3. low': 'low', '4. close': 'close', '5. adjusted close': 'adj close', '6. volume': 'volume'})
for i in df.columns:
df[i] = df[i].astype(float)
df.index = pd.to_datetime(df.index)
df = df.iloc[::-1].drop(['7. dividend amount', '8. split coefficient'], axis = 1)
if start_date:
df = df[df.index >= start_date]
return df

fb = get_historical_data('FB', '2020-01-01').iloc[:,:4]
fb

```

Output:

Code Explanation: The first thing we did is to define a function named ‘get_historical_data’ that takes the stock’s symbol (‘symbol’) as a required parameter and the starting date of the historical data (‘start_date’) as an optional parameter. Inside the function, we are defining the API key and the URL and stored them into their respective variable. Next, we are extracting the historical data in JSON format using the ‘get’ function and stored it into the ‘raw_df’ variable. After doing some processes to clean and format the raw JSON data, we are returning it in the form of a clean Pandas dataframe. Finally, we are calling the created function to pull the historic data of Facebook from the starting of 2020 and stored it into the ‘fb’ variable.

### Step-3: Extracting the CCI values

In this step, we are going to pull the Commodity Channel Index values of Facebook with the help of an API endpoint provided by Alpha Vantage. This step is almost similar to what we did in the previous step.

Python Implementation:

```
def get_cci(symbol, n, start_date):
api_key = open(r'api_key.txt')
url = f'https://www.alphavantage.co/query?function=CCI&symbol={symbol}&interval=daily&time_period={n}&apikey={api_key}'
raw = requests.get(url).json()
df = pd.DataFrame(raw['Technical Analysis: CCI']).T.iloc[::-1]
df = df[df.index >= start_date]
df.index = pd.to_datetime(df.index)
df = df.astype(float)
return df

fb['cci'] = get_cci('FB', 20, '2020-01-01')
fb = fb.dropna()
fb

```

Output:

Code Explanation: Firstly, we are defining a function named ‘get_cci’ which takes the stock’s symbol (‘symbol’), the number of periods for the CCI (‘n’), and the starting date of the data (‘start_date’) as parameters. Inside the function, we are first assigning two variables named ‘api_key’ and ‘url’ to store the API key and the API URL respectively. Using the ‘get’ function provided by the Requests package, we are calling the API and stored the response into the ‘raw’ variable. After doing some data manipulations, we are returning the CCI values. Finally, we are calling the function to extract the Commodity Channel Index values of Facebook.

### Step-4: CCI Plot

In this step, we are going to plot the extracted Commodity Channel Index values of Facebook to make more sense out of it. The main aim of this part is not on the coding section but instead to observe the plot to gain a solid understanding of the Commodity Channel Index.

Python Implementation:

```
ax1 = plt.subplot2grid((10,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((10,1), (6,0), rowspan = 4, colspan = 1)
ax1.plot(fb['close'])
ax2.plot(fb['cci'], color = 'orange')
ax2.axhline(150, linestyle = '--', linewidth = 1, color = 'black')
ax2.axhline(-150, linestyle = '--', linewidth = 1, color = 'black')
plt.show()

```

Output:

The above chart is divided into two panels: The above panel representing the closing prices of Facebook and the lower panel with the CCI values. Being a leading indicator, CCI cannot be plotted on the same panel where the closing price is plotted as the values of the indicator and the closing price vary a lot. So, it is plotted apart from the closing price (below the closing price in our case). As you can see, the values of CCI are so indefinite and reached extreme values at times. You could see two dotted lines above and below the CCI plot which is nothing but the overbought and oversold levels. It’s our choice to determine the band levels and in our case, we set the overbought level to 150 and the oversold level to -150, i.e. the stock is considered to be in the state of overbought when the CCI crosses above 150, and similarly, the market is considered to be in the state of oversold when the CCI goes below -150. We will be using these same levels to build our trading strategy.

### Step-5: Creating the trading strategy

In this step, we are going to implement the discussed CCI trading strategy in python with the overbought and oversold levels as 150 and -150 respectively.

Python Implementation:

```
def implement_cci_strategy(prices, cci):
sell_price = []
cci_signal = []
signal = 0

lower_band = (-150)
upper_band = 150

for i in range(len(prices)):
if cci[i-1] > lower_band and cci[i] < lower_band:
if signal != 1:
sell_price.append(np.nan)
signal = 1
cci_signal.append(signal)
else:
sell_price.append(np.nan)
cci_signal.append(0)

elif cci[i-1] < upper_band and cci[i] > upper_band:
if signal != -1:
sell_price.append(prices[i])
signal = -1
cci_signal.append(signal)
else:
sell_price.append(np.nan)
cci_signal.append(0)

else:
sell_price.append(np.nan)
cci_signal.append(0)

buy_price, sell_price, cci_signal = implement_cci_strategy(fb['close'], fb['cci'])

```

Code Explanation: First, we are defining a function named ‘implement_cci_strategy’ which takes the stock prices (‘price), and the CCI values (‘cci’) as parameters.

Inside the function, we are creating three empty lists (buy_price, sell_price, and cci_signal) in which the values will be appended while creating the trading strategy.

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 ‘buy_price’ 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 ‘sell_price’ list, and the signal value will be appended as -1 representing to sell the stock.

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’t make any sense unless we plot the values. So, let’s plot the values of the created trading lists.

### Step-6: Plotting the trading signals

In this step, we are going to plot the created trading lists to make sense out of them.

Python Implementation:

```
ax1 = plt.subplot2grid((10,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((10,1), (6,0), rowspan = 4, colspan = 1)
ax1.plot(fb['close'], color = 'skyblue', label = 'FB')
ax1.plot(fb.index, buy_price, marker = '^', markersize = 12, linewidth = 0, label = 'BUY SIGNAL', color = 'green')
ax1.plot(fb.index, sell_price, marker = 'v', markersize = 12, linewidth = 0, label = 'SELL SIGNAL', color = 'r')
ax1.legend()
ax2.plot(fb['cci'], color = 'orange')
ax2.axhline(150, linestyle = '--', linewidth = 1, color = 'black')
ax2.axhline(-150, linestyle = '--', linewidth = 1, color = 'black')
plt.show()

```

Output:

Code Explanation: We are plotting the Commodity Channel Index values along with the buy and sell signals generated by the trading strategy. We can observe that whenever the CCI goes below the oversold level, a green-colored buy signal is plotted in the chart. Similarly, whenever the CCI crosses above the overbought level, a red-colored sell signal is plotted in the chart.

### Step-7: Creating our Position

In this step, we are going to create a list that indicates 1 if we hold the stock or 0 if we don’t own or hold the stock.

Python Implementation:

```
position = []
for i in range(len(cci_signal)):
if cci_signal[i] > 1:
position.append(0)
else:
position.append(1)

for i in range(len(fb['close'])):
if cci_signal[i] == 1:
position[i] = 1
elif cci_signal[i] == -1:
position[i] = 0
else:
position[i] = position[i-1]

cci = fb['cci']
close_price = fb['close']
cci_signal = pd.DataFrame(cci_signal).rename(columns = {0:'cci_signal'}).set_index(fb.index)
position = pd.DataFrame(position).rename(columns = {0:'cci_position'}).set_index(fb.index)

frames = [close_price, cci, cci_signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)

```

Output:

Code Explanation: First, we are creating an empty list named ‘position’. We are passing two for-loops, one is to generate values for the ‘position’ list to just match the length of the ‘signal’ 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 ‘signal’ list, and the values of the ‘position’ 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’t own the stock. Finally, we are doing some data manipulations to combine all the created lists into one dataframe.

From the output being shown, we can see that in the three rows our position in the stock has remained 1 (since there isn’t any change in the CCI signal) but our position suddenly turned to 0 as we sold the stock when the CCI signal represents a sell signal (-1). Our position will remain 0 until some changes in the trading signal occur. Now it’s time to do implement some backtesting process!

### Step-8: Backtesting

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 CCI trading strategy over the Facebook stock data.

Python Implementation:

```
fb_ret = pd.DataFrame(np.diff(fb['close'])).rename(columns = {0:'returns'})
cci_strategy_ret = []

for i in range(len(fb_ret)):
returns = fb_ret['returns'][i]*strategy['cci_position'][i]
cci_strategy_ret.append(returns)

cci_strategy_ret_df = pd.DataFrame(cci_strategy_ret).rename(columns = {0:'cci_returns'})
investment_value = 100000
number_of_stocks = floor(investment_value/fb['close'][-1])
cci_investment_ret = []

for i in range(len(cci_strategy_ret_df['cci_returns'])):
returns = number_of_stocks*cci_strategy_ret_df['cci_returns'][i]
cci_investment_ret.append(returns)

cci_investment_ret_df = pd.DataFrame(cci_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(cci_investment_ret_df['investment_returns']), 2)
profit_percentage = round((total_investment_ret/investment_value)*100, 2)
print(cl('Profit gained from the CCI strategy by investing \$100k in FB : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the CCI strategy : {}%'.format(profit_percentage), attrs = ['bold']))

```

Output:

```
Profit gained from the CCI strategy by investing \$100k in FB : 16598.39
Profit percentage of the CCI strategy : 16.6%

```

Code Explanation: First, we are calculating the returns of the Facebook stock using the ‘diff’ function provided by the NumPy package and we have stored it as a dataframe into the ‘fb_ret’ variable. Next, we are passing a for-loop to iterate over the values of the ‘fb_ret’ variable to calculate the returns we gained from our CCI trading strategy, and these returns values are appended to the ‘fb_strategy_ret’ list. Next, we are converting the ‘cci_strategy_ret’ list into a dataframe and stored it into the ‘cci_strategy_ret_df’ variable.

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 ‘investment_value’ variable. After that, we are calculating the number of Facebook stocks we can buy using the investment amount. You can notice that I’ve used the ‘floor’ function provided by the Math package because, while dividing the investment amount by the closing price of the Facebook stock, it spits out an output with decimal numbers. The number of stocks should be an integer but not a decimal number. Using the ‘floor’ function, we can cut out the decimals. Remember that the ‘floor’ function is way more complex than the ‘round’ function. Then, we are passing a for-loop to find the investment returns followed by some data manipulations tasks.

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 sixteen thousand and five hundred USD in one year. That’s not bad!

## Final Thoughts!

A long way ahead, we have successfully studied what Commodity Channel Index is all about and a trading strategy based on it can be implemented in python. But still, there are spaces to improve this article:

• Risk Management: This is the most important factor to consider for either trading or investing and several measures must be taken to ensure that our trades are constrained to a specific level of risk. The fact of taking risks is indeed an essential task to perform profitable trades but it is also equally important to maintain it to an extent in order to avoid fatal trades.

• Strategy Tuning: The trading strategy we built in this article can be considered as the least basic one and must be improvised in order to implement it in the real world market. Instuitional traders who use algorithmic trading for stock trading purposes filter their strategy by combining various relevant features with different indicators and you can try that too. We didn't cover it as the sole purpose of the article is to just educate people on what CCI means.

That’s it! You’ve reached the end of the article. Hope you learned something useful from this article. If you forgot to follow any of the coding parts, don’t worry. I’ve provided the full source code at the end of the article.

### Full Code:

```
import pandas as pd
import requests
import datetime as dt
import numpy as np
import matplotlib.pyplot as plt
from math import floor
from termcolor import colored as cl

plt.rcParams['figure.figsize'] = (20, 10)
plt.style.use('fivethirtyeight')

def get_historical_data(symbol, start_date = None):
api_key = open(r'api_key.txt')
raw_df = requests.get(api_url).json()
df = pd.DataFrame(raw_df[f'Time Series (Daily)']).T
df = df.rename(columns = {'1. open': 'open', '2. high': 'high', '3. low': 'low', '4. close': 'close', '5. adjusted close': 'adj close', '6. volume': 'volume'})
for i in df.columns:
df[i] = df[i].astype(float)
df.index = pd.to_datetime(df.index)
df = df.iloc[::-1].drop(['7. dividend amount', '8. split coefficient'], axis = 1)
if start_date:
df = df[df.index >= start_date]
return df

fb = get_historical_data('FB', '2020-01-01')
print(fb)

def get_cci(symbol, n, start_date):
api_key = open(r'api_key.txt')
url = f'https://www.alphavantage.co/query?function=CCI&symbol={symbol}&interval=daily&time_period={n}&apikey={api_key}'
raw = requests.get(url).json()
df = pd.DataFrame(raw['Technical Analysis: CCI']).T.iloc[::-1]
df = df[df.index >= start_date]
df.index = pd.to_datetime(df.index)
df = df.astype(float)
return df

fb['cci'] = get_cci('FB', 20, '2020-01-01')
fb = fb.dropna()
print(fb.tail())

ax1 = plt.subplot2grid((10,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((10,1), (6,0), rowspan = 4, colspan = 1)
ax1.plot(fb['close'])
ax2.plot(fb['cci'], color = 'orange')
ax2.axhline(150, linestyle = '--', linewidth = 1, color = 'black')
ax2.axhline(-150, linestyle = '--', linewidth = 1, color = 'black')
plt.show()

def implement_cci_strategy(prices, cci):
sell_price = []
cci_signal = []
signal = 0

lower_band = (-150)
upper_band = 150

for i in range(len(prices)):
if cci[i-1] > lower_band and cci[i] < lower_band:
if signal != 1:
sell_price.append(np.nan)
signal = 1
cci_signal.append(signal)
else:
sell_price.append(np.nan)
cci_signal.append(0)

elif cci[i-1] < upper_band and cci[i] > upper_band:
if signal != -1:
sell_price.append(prices[i])
signal = -1
cci_signal.append(signal)
else:
sell_price.append(np.nan)
cci_signal.append(0)

else:
sell_price.append(np.nan)
cci_signal.append(0)

buy_price, sell_price, cci_signal = implement_cci_strategy(fb['close'], fb['cci'])

ax1 = plt.subplot2grid((10,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((10,1), (6,0), rowspan = 4, colspan = 1)
ax1.plot(fb['close'], color = 'skyblue', label = 'FB')
ax1.plot(fb.index, buy_price, marker = '^', markersize = 12, linewidth = 0, label = 'BUY SIGNAL', color = 'green')
ax1.plot(fb.index, sell_price, marker = 'v', markersize = 12, linewidth = 0, label = 'SELL SIGNAL', color = 'r')
ax1.legend()
ax2.plot(fb['cci'], color = 'orange')
ax2.axhline(150, linestyle = '--', linewidth = 1, color = 'black')
ax2.axhline(-150, linestyle = '--', linewidth = 1, color = 'black')
plt.show()

position = []
for i in range(len(cci_signal)):
if cci_signal[i] > 1:
position.append(0)
else:
position.append(1)

for i in range(len(fb['close'])):
if cci_signal[i] == 1:
position[i] = 1
elif cci_signal[i] == -1:
position[i] = 0
else:
position[i] = position[i-1]

cci = fb['cci']
close_price = fb['close']
cci_signal = pd.DataFrame(cci_signal).rename(columns = {0:'cci_signal'}).set_index(fb.index)
position = pd.DataFrame(position).rename(columns = {0:'cci_position'}).set_index(fb.index)

frames = [close_price, cci, cci_signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)

fb_ret = pd.DataFrame(np.diff(fb['close'])).rename(columns = {0:'returns'})
cci_strategy_ret = []

for i in range(len(fb_ret)):
returns = fb_ret['returns'][i]*strategy['cci_position'][i]
cci_strategy_ret.append(returns)

cci_strategy_ret_df = pd.DataFrame(cci_strategy_ret).rename(columns = {0:'cci_returns'})
investment_value = 100000
number_of_stocks = floor(investment_value/fb['close'][-1])

cci_investment_ret = []

for i in range(len(cci_strategy_ret_df['cci_returns'])):
returns = number_of_stocks*cci_strategy_ret_df['cci_returns'][i]
cci_investment_ret.append(returns)

cci_investment_ret_df = pd.DataFrame(cci_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(cci_investment_ret_df['investment_returns']), 2)
profit_percentage = round((total_investment_ret/investment_value)*100, 2)

print(cl('Profit gained from the CCI strategy by investing \$100k in FB : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the CCI strategy : {}%'.format(profit_percentage), attrs = ['bold']))

```

Tags: