{{announcement.body}}
{{announcement.title}}

Using Technical Analysis Indicators to Send Buy-or-Sell Trading Signals to a Chatroom

DZone 's Guide to

Using Technical Analysis Indicators to Send Buy-or-Sell Trading Signals to a Chatroom

In this article, see how you can use technical analysis indicators to send buy-or-sell trading signals to a chatroom on an ongoing basis.

· AI Zone ·
Free Resource

In this article, I will look at how I can use technical analysis indicators to send buy-or-sell trading signals to a chatroom on an ongoing basis — removing the need to keep eyeballing Technical Analysis charts constantly.

According to Investopedia ‘Technical Analysis is a trading discipline employed to evaluate investments and identify trading opportunities by analyzing statistical trends gathered from trading activity, such as price movement and volume’. It further states ‘technical analysts focus on patterns of price movements, trading signals … to evaluate a security’s strength or weakness’.

According to the various articles etc that I read, three of the more commonly used TA indicators are

  • Simple Moving Averages
  • Relative Strength Index
  • Stochastic Oscillator

Investors and traders who use Technical Analysis as part of their trading strategy may refer to TA charts regularly to help determine their investment choices. So, it would be great if instead of constantly having to interrogate the charts, they could just receive a message when a Buy or Sell condition arises.

In this workflow, I am going to experiment with how I can use these indicators to generate Buy or Sell trading signals. There are several articles on the internet that attempt the same thing — often focusing on a single indicator — and most of these run the analysis on historical data, present the historical results, and stop there.

However, I want to take this a step further by continuing to run the analysis on an ongoing basis at a configured interval — e.g. every minute, hour, day (I did look at using realtime tick data as well but it was pointed out that this does not make much sense from a technical analysis perspective — as the ticks do not occur at regular interval).

Furthermore, when a trading signal is generated I will use a chat BOT to post the signal details into a chat room notifying the users — saving the effort of frequently interrogating the charts.

I have access to the Refinitiv Eikon desktop application so I will be using its Data API to access the data I require - however, it should be relatively straightforward to use your own source of equivalent data and associated API- as a substitute for the Eikon stuff.

I will also use symbology conversion functions to convert from ISINs to RIC (Reuters Instrument Codes) for requesting the various data - as the above API generally requires RIC codes for most of the data functions.

I also need to send messages when a trading signal is generated - so I will use the Refinitiv Messenger BOT API to post a message to an Eikon Messenger Chatroom. No doubt this could easily be replaced with some other form of messaging such as WhatsApp, email, SMS, etc.

Before we go any further I should mention that I am relatively new to the Eikon Data API and to Python itself — so you may come across some ‘not very Pythonic’ ways of doing things. If you find some instances which border on sacrilege (in the Python world) please let me know and I will try and amend the offending code (I am working on the lower_case_with_underscores naming convention - but as a long time Java/C++ developer, not there yet!).

TA-Lib: Technical Analysis Library

When I started on this, I was using various Python scripts/snippets I found online for calculating my indicators and then wondering just how accurate they may truly be. After spending (wasting?) some considerable time testing and fretting about the veracity of these snippets, a colleague mentioned that there already existed a Python wrapper — ta-lib — for the well known Technical Analysis Library — TA-Lib. Of course — this being Python there would have to be a library for it (remember — I am a Python noob)!

Import our libraries

I think I should mention the versions of some of the key libraries I have installed — just in case you have any issues:

  • eikon - 1.1.3a0
  • pandas - 1.0.0
  • numpy - 1.18.1
  • talib - 0.4.17
  • matplotlib - 3.1.3

I have used an alpha version of the eikon library because I wanted to test it & also because I wanted to eliminate the pandas.np deprecation warnings .  If you are an Eikon user and have the official v1.1.2 (at the time of writing) installed it should still work fine and you could just import warnings library and use warnings.filterwarnings("ignore") to suppress them.

If you decide to build the TA-Lib binaries (rather than download the prebuilt ones) pay attention to the instructions on moving the folder — otherwise, you may be left scratching your head (like I did) as to why it won't build properly!

To post messages to the RM Chatroom I am using the existing Messenger BOT API example MessengerChatBot.Python from GitHub — full details are provided on the site.

Key Code Snippets

As there is a considerable amount of code involved, I will be omitting much of the code here and mostly showing only key code snippets — please refer to the GitHub repository for the full source code.

So, let me crack on with the code — in the following order:

  • various helper functions for the Technical Analysis, timing & chart plotting
  • the main control section
  • finishing with the ongoing analysis loop.

Simple Moving Averages

This function uses the TA-Lib SMA function to calculate the Simple Moving Average using the Close price for two periods — which you will note later are 14 for the short period and 200 for the long period. As you will see later, the period interval itself can vary e.g minute, daily, monthly, hourly — so for example, calculate SMA for 14 days and 200 days.

With the calculated SMAs, it then uses the following logic to generate Buy and Sell signals:

  • If the short period SMA crosses up through the long period SMA then this is a buy signal
  • If the short period SMA crosses down through the long period SMA then this is a sell signal
Java
 




x


 
1
def SMA(close,sPeriod,lPeriod):
2
    shortSMA = ta.SMA(close,sPeriod)
3
    longSMA = ta.SMA(close,lPeriod)
4
    smaSell = ((shortSMA <= longSMA) & (shortSMA.shift(1) >= longSMA.shift(1)))
5
    smaBuy = ((shortSMA >= longSMA) & (shortSMA.shift(1) <= longSMA.shift(1)))
6
    return smaSell,smaBuy,shortSMA,longSMA



The resultant smaSell and smaBuy Series will contain the date/time and a flag to indicate a signal e.g. for a daily interval

Java
 




xxxxxxxxxx
1


 
1
Date
2
2018-02-15    False
3
2018-02-16    False
4
2018-02-19    True
5
2018-02-20    False



Relative Strength Index

RSI calculation is usually done for a 14 day period — so once again I feed in the Close price for the instrument to the TA-Lib RSI function. The common methodology is to set high and low thresholds of the RSI at 70 and 30. The idea is that if the lower threshold is crossed, the asset is becoming oversold and we should buy. Conversely, if the upper threshold is crossed then the asset is becoming overbought and we should sell.

Java
 




xxxxxxxxxx
1


 
1
def RSI(close,timePeriod):    
2
    rsi = ta.RSI(close,timePeriod)
3
    rsiSell = (rsi>70) & (rsi.shift(1)<=70)
4
    rsiBuy = (rsi<30) & (rsi.shift(1)>=30)
5
    return rsiSell,rsiBuy, rsi



As per my SMA function, my RSI function also returns a Series containing date/time and a flag to indicate buy/sell signals

Stochastics

The TA-Lib Stoch function returns two lines slowk and slowd which can then be used to generate the buy/sell indicators. A crossover signal occurs when the two lines cross in the overbought region (commonly above 80) or oversold region (commonly below 20). When a slowk line crosses below the slowd line in the overbought region it is considered a sell indicator. Conversely, when an increasing slowk line crosses above the slowd line in the oversold region it is considered a buy indicator.

Java
 




xxxxxxxxxx
1


 
1
def Stoch(close,high,low):    
2
    slowk, slowd = ta.STOCH(high, low, close)    stochSell = ((slowk < slowd) & 
3
        (slowk.shift(1) > slowd.shift(1))) & (slowd > 80)
4
    stochBuy = ((slowk > slowd) & 
5
        (slowk.shift(1) < slowd.shift(1))) & (slowd < 20)    
6
    return stochSell,stochBuy,slowk,slowd



Sending a Trade Signal to the Chat Room

I need a way of letting users know that a Trade signal has been generated by the Technical Analysis. So, I am going to use the Messenger BOT API to send messages to other Refinitiv Messenger users. I am re-purposing the existing MessengerChatBot.Python example from GitHub.
As mentioned previously, it should be fairly straightforward to replace this with your choice of messaging API - essentially we are just posting a message with a name of the Instrument and whether a Buy or Sell signal has been generated.

Java
 




xxxxxxxxxx
1
18


 
1
# Key code snippets - see Github for full source
2
def sendSignaltoChatBot(RIC, signalTime, indicators):
3
    indicatorList = ','.join(indicators.values)
4
    message = f"TA signal(s) Generated : {indicatorList} 
5
      at {signalTime} for {RIC}"
6
    
7
    # Connect, login and send message to chatbot
8
    rdp_token = RDPTokenManagement( bot_username, 
9
        bot_password, app_key)
10
    access_token = cdr.authen_rdp(rdp_token)
11
    
12
    if access_token:
13
        # Join associated Chatroom
14
        joined_rooms = cdr.join_chatroom(access_token, 
15
           chatroom_id)
16
        if joined_rooms:
17
            cdr.post_message_to_chatroom(access_token, 
18
                joined_rooms, chatroom_id, message)



FYI: the acronym ‘RIC’ stands for Reuters Instrument Code — so, for example, the code ‘VOD.L’ represents Vodafone from LSE.

Run the Technical Analysis

Initially, I will do a historical TA run, after which I will use this function to run the above 3 TA methodologies on the data I get as part of the ongoing Technical Analysis.

I am going to repeat some of this code later in the main historical TA run loop — purely for ease of reading.

Java
 




xxxxxxxxxx
1
30


 
1
def runAllTA(myRIC, data):
2
    price = data['CLOSE']
3
    high = data['HIGH']
4
    low = data['LOW']
5
        
6
    # Simple Moving Average calcs
7
    smaSell,smaBuy,shortSMA,longSMA = SMA(price,shortPeriod,longPeriod)
8
    # Do the RSI calcs
9
    rsiSell,rsiBuy,rsi = RSI(price,shortPeriod)
10
    # and now the stochastics 
11
    stochSell,stochBuy,slowk,slowd = Stoch(price, high, low)    
12
 
          
13
    # Now collect buy and sell Signal timestamps into a single df
14
    sigTimeStamps = pd.concat([smaSell, smaBuy, stochSell, 
15
        stochBuy, rsiSell, rsiBuy],axis=1)
16
    sigTimeStamps.columns=['SMA Sell','SMA Buy','Stoch Sell',
17
        'Stoch Buy','RSI Sell','RSI Buy']
18
    signals = sigTimeStamps.loc[sigTimeStamps['SMA Sell'] | 
19
        sigTimeStamps['Stoch Sell'] |sigTimeStamps['RSI Sell'] | 
20
        sigTimeStamps['SMA Buy'] | sigTimeStamps['Stoch Buy'] | 
21
        sigTimeStamps['RSI Buy']]
22
    
23
    # Compare final signal Timestamp with latest data TimeStamp
24
    if (data.index[-1]==signals.index[-1]):
25
        final = signals.iloc[-1]
26
        # filter out the signals set to True and send to ChatBot
27
        signal = final.loc[final]
28
        signalTime = signal.name.strftime("%Y-%m-%dT%H:%M:%S")
29
        indicators = signal.loc[signal].index
30
        sendSignaltoChatBot(myRIC, signalTime, indicators)



If the timestamp of the final TA signal, matches the timestamp of the most recent data point — then we have one or more new trade signal(s) — so inform the Chatroom users via the Chat BOT.

Timing Helper Functions

I also need a few helper functions to calculate some time values based on the selected interval — the main one is…

Java
 




xxxxxxxxxx
1
12


 
1
# Calculate Start & End time for our historical data request window
2
def startEnd(interval):
3
    end = datetime.now()
4
    start = {
5
      'minute': lambda end: end - relativedelta(days=5),
6
      'hour': lambda end: end - relativedelta(months=2),
7
      'daily': lambda end: end - relativedelta(years=2),
8
      'weekly': lambda end: end - relativedelta(years=5),
9
      'monthly': lambda end: end - relativedelta(years=10),
10
    }[interval](end)
11
    return start.strftime("%Y-%m-%dT%H:%M:%S"),
12
        end.strftime("%Y-%m-%dT%H:%M:%S")



Plotting Functions

Whilst not essential to the workflow, I wanted to plot a few charts to provide a visual representation of the various TA indicators — so we can try and visually tie-up instances where a price rises or drops in line with a TA trade signal — so for example when the short SMA crosses up through the long SMA, do we see an upward trend in the price after that point in time?

Java
 




xxxxxxxxxx
1
56


 
1
# As per before, key code snips only...
2
# Use a formatter to remove weekends from date axis to smooth out the line.
3
class MyFormatter(Formatter):
4
    def __init__(self, dates, fmt='%Y-%m-%d'):
5
        self.dates = dates
6
        self.fmt = fmt
7
    def __call__(self, x, pos=0):
8
        'Return the label for time x at position pos'
9
        ind = int(round(x))
10
        if ind>=len(self.dates) or ind<0: return ''
11
        return self.dates[ind].strftime(self.fmt)
12
 
          
13
# Plot the Close price and short and long Simple Moving Averages
14
def plotSMAs(ric,close,sma14,sma200,sell,buy):
15
    x = close.index
16
    plt.rcParams["figure.figsize"] = (28,8)
17
    fig, ax = plt.subplots(facecolor='0.25')
18
    ax.plot(np.arange(len(x)),close, label='Close',color='y')
19
    ax.plot(np.arange(len(x)),sma14,label="SMA 14", color='g')
20
    ax.plot(np.arange(len(x)),sma200,label="SMA 200",  
21
        color='tab:purple')
22
    plt.show()
23
 
          
24
# Plot the Close price in the top chart and RSI in the lower chart
25
def plotRSI(ric,close,rsi):
26
    plt.rcParams["figure.figsize"] = (28,12)
27
    fig = plt.figure(facecolor='0.25')
28
    gs1 = gridspec.GridSpec(2, 1)
29
    # RSI chart
30
    ax = fig.add_subplot(gs1[1])
31
    ax.xaxis.set_major_formatter(formatter)
32
    ax.plot(np.arange(len(rsi.index)), rsi.values,color='b')
33
    plt.axhline(y=70, color='w',linestyle='--')
34
    plt.axhline(y=30, color='w',linestyle='--')
35
    # Close Price chart
36
    axc = fig.add_subplot(gs1[0])
37
    axc.plot(np.arange(len(rsi.index)), close, color='y')
38
    plt.show()
39
 
          
40
# Plot Close price in top chart and in the slowk + slowd lines in lower chart
41
def plotStoch(ric,close,slowK,slowD):
42
    plt.rcParams["figure.figsize"] = (28,12)
43
    fig = plt.figure(facecolor='0.25')
44
    gs1 = gridspec.GridSpec(2, 1)
45
    ax = fig.add_subplot(gs1[1])
46
    # Stochastic lines chart
47
    ax.plot(np.arange(len(slowk.index)), slowk.values,
48
        label="Slow K",color='m')
49
    ax.plot(np.arange(len(slowk.index)), slowd.values,
50
        label="Slow D",color='g')
51
    plt.axhline(y=80, color='w',linestyle='--')
52
    plt.axhline(y=20, color='w',linestyle='--')
53
    # Closing price chart
54
    axc = fig.add_subplot(gs1[0])
55
    axc.plot(np.arange(len(close.index)), close, color='y')
56
    plt.show()



So, that’s the helper functions out of the way — let’s move on the main control section

Connecting to the Eikon Application

To connect to my running instance of Eikon I need to provide my Application Key (which can be generated within Eikon using the AppKey Generator app).

Java
 




xxxxxxxxxx
1


 
1
ek.__version__ ek.set_app_key('<your app key>')



Some Initialisation Code

I need to calculate the start and end date for my price query — based on the chosen periodicity/interval, as well as specify the periods for moving averages.

Also, as I will be requesting the price of each instrument individually, I create a container to hold all the price data for the full basket of instruments.

Finally, I set some display properties for the Pandas dataframe.

Java
 




xxxxxxxxxx
1
12


 
1
myInterval = 'daily'    # 'minute', 'hour', 'daily', 'weekly', 'monthly'
2
myStart, myEnd  = startEnd(myInterval)
3
timestampLen = timeStampLength(myInterval)
4
print(f'Interval {myInterval} from {myStart} to {myEnd} : Timestamp Length {timestampLen}')
5
shortPeriod = 14
6
longPeriod = 200
7
basket={}
8
# Do we want to plot charts?
9
plotCharts = True
10
# Dataframe display setting
11
pd.set_option("display.max_rows", 999)
12
pd.set_option('precision', 3)



TA Analysis Summary Output

Once the initial historical TA has been run, I want to present a summary table of the signal over that period.
For this, I am going to use a Dataframe to output the results in a readable format.
I am also creating some blank columns which I will use for padding the dataframe later.

Java
 




xxxxxxxxxx
1


 
1
outputDF = pd.DataFrame(columns=  
2
    ['RIC','Name','ISIN','Close','Periodicity','Intervals Up',
3
     'Intervals Down','Unchanged','1wk %ch','1M %ch','YTD %ch',
4
     '6M %ch', '1yr %ch','SMA Sell','SMA Buy','Stoch Sell',
5
     'Stoch Buy','RSI Sell', 'RSI Buy' ])blankSignalCols = ['N/A']*6
6
blankNonSignalCols = [' ']*13



Convert my ISIN symbols to RICs

Whilst Eikon can accept various Symbology types for certain interactions, I need to specify RICs (Reuters Instrument Code) for much of the data I intend to access.

Therefore, I could just start with a bunch of RICs — but not everyone works with RICs — so here is an example of symbology conversion at work.

Java
 




xxxxxxxxxx
1


 
1
myISINs = [ 'GB0002634946', 'GB00B1YW4409' ] 
2
myRICs = ek.get_symbology(myISINs, 
3
            from_symbol_type='ISIN',    
4
            to_symbol_type='RIC')



Snapshot Some Summary Data Values

Once I have run the TA I want to present a summary table reflecting the price changes for each instrument over various periods such as a week, month, year etc — along with the TA results.

So, for this, I am using the get_data method to obtain various Percent price change values as well the name of the corporate entity and the most recent Closing price

Java
 




xxxxxxxxxx
1


 
1
listRICs = list(myRICs.RIC) pcts, err = ek.get_data( instruments = listRICs, 
2
    fields = [  
3
    'TR.CommonName', 'TR.CLOSEPRICE', 'TR.PricePctChgWTD', 
4
    'TR.PricePctChgMTD', 'TR.PricePctChgYTD', 'TR.PricePctChg6M', 
5
    'TR.PricePctChg1Y', ] 
6
) pcts.set_index('Instrument',inplace=True)



In the above call, I am requesting each instrument’s per cent Price change for Week, Month and Year to Date — and the 6m and 1yr period as well.

Putting It All Together for Our Initial ‘Historical’ Analysis Run

I can now go ahead and request my historical data and perform the Technical Analysis.

As well as interval data at the minute, hour, daily etc Eikon product can also provide tick data — i.e. individual trades as well as Bid//Ask changes. However, there are some differences in the data set for tick data compared to the other intervals:

  • Historical tick data will be indexed on the date+time of every tick — and therefore the index can vary across instruments
  • Intervalised data will be indexed according to the interval e.g. every minute, hour etc and therefore use the same index values across multiple instruments
  • I can get high, low prices for interval data (which is needed for the Stochastic TA) but not for tick data

As you can see, since the tick data is not interval based, it does not make sense from a TA point of view. For interval data, I can specify multiple RICs in the get_timeseries call and get back a single dataframe with the prices for all the RICs. However, to make the code easier to read, I will loop through the RICs and request each one individually rather than slightly increased subsequent complication involved if I have to use a single dataframe containing all the data.

I have split the following section of code into smaller chunks for ease of reading and annotations.

For each RIC code in our list, the first thing I do is use the get_timeseries function to request a subset of fields at the specified Interval for the previously calculated time period.

Java
 




xxxxxxxxxx
1


 
1
for ISIN, symb in myRICs.iterrows():
2
    myRIC=symb['RIC']
3
    data = ek.get_timeseries(myRIC,  
4
        fields = ['CLOSE','HIGH','LOW'],
5
        start_date=myStart,  
6
        end_date=myEnd,
7
        interval = myInterval)price = data['CLOSE']
8
    high = data['HIGH']
9
    low = data['LOW']basket[myRIC]=data # Save each instrument's raw data for later use



Next, I calculate values for Price Up, Down and no change movements for the analysis period. Then I call the various TA functions to calculate the indicators and generate the signals (& plot charts if enabled).

Java
 




xxxxxxxxxx
1
25


 
1
# Count Price Up, Down and No change
2
    upCnt = (price < price.shift(1)).value_counts().loc[True]
3
    downCnt = (price > price.shift(1)).value_counts().loc[True]
4
    
5
    try:
6
        ncCnt = (price == price.shift(1)).value_counts().loc[True]
7
    except KeyError as e:
8
        ncCnt = 0
9
    
10
    # Do the Simple Moving Average calcs
11
    smaSell,smaBuy,shortSMA,longSMA = 
12
        SMA(price,shortPeriod,longPeriod)
13
    if plotCharts:
14
        plotSMAs(myRIC,price,shortSMA,longSMA,smaSell,smaBuy)
15
            
16
    # Do the RSI calcs
17
    rsiSell,rsiBuy,rsi = RSI(price,shortPeriod)
18
    if plotCharts:
19
        plotRSI(myRIC,price,rsi)# Stochastic calcs
20
    stochSell,stochBuy,slowk,slowd = Stoch(price, high, low)
21
    if plotCharts:
22
        plotStoch(myRIC,price,slowk,slowd)
23
    
24
    # Get the Percent Change data for thisRIC
25
    pct = pcts.loc[myRIC]



Finally, I use the data and signals etc to generate a dataframe displaying a Summary Table of the various stats + any signals generated for our configured TA time period and interval.

Java
 




xxxxxxxxxx
1
46


 
1
# Now we build the summary table
2
    # starting with the non-trade signal related stuff
3
    nonSignalData = [myRIC,
4
                         pct['Company Common Name'],
5
                         ISIN,
6
                         pct['Close Price'],
7
                         myInterval,
8
                         upCnt,downCnt,ncCnt,
9
                         pct['WTD Price PCT Change'],
10
                         pct['MTD Price PCT Change'],
11
                         pct['YTD Price PCT Change'],
12
                         pct['6-month Price PCT Change'],
13
                         pct['1-year Price PCT Change']]
14
    
15
    # Now build the Signal buy & sell columns for each TA indicator
16
    sigTimeStamps = pd.concat([smaSell, smaBuy, stochSell, 
17
        stochBuy, rsiSell, rsiBuy],axis=1)
18
    sigTimeStamps.columns=['SMA Sell','SMA Buy','Stoch Sell',
19
        'Stoch Buy','RSI Sell','RSI Buy']
20
    signals = sigTimeStamps.loc[
21
        sigTimeStamps['SMA Sell'] | sigTimeStamps['Stoch Sell'] |
22
        sigTimeStamps['RSI Sell'] | sigTimeStamps['SMA Buy'] | 
23
        sigTimeStamps['Stoch Buy'] | sigTimeStamps['RSI Buy']
24
       ]# If any trade signals were generated for our time period
25
 
          
26
    # Add them to the summary table
27
    signalCnt = len(signals)
28
    if (signalCnt):
29
        first = True
30
        for index,row in signals.iterrows():
31
            sigDate = str(index)[0:timestampLen]
32
            signalList = [sigDate if item else "" for item in row]
33
            # First row to contain non-signal stuff, 
34
            # subsequent rows only trade signals
35
            tempRow = nonSignalData.copy() 
36
                if first else blankNonSignalCols.copy()
37
            tempRow.extend(signalList)
38
            s = pd.Series(tempRow, index=outputDF.columns)
39
            outputDF = outputDF.append(s,ignore_index=True)
40
            first=False
41
    else:  # No signals so just the non-signal related stuff
42
        tempRow = nonSignalData.copy()
43
        tempRow.extend(blankSignalCols)
44
        outputDF = outputDF.append(tempRow)outputDF.head(5)
45
 
          
46
outputDF.tail(5)



Once that is done, I simply display the head and tail of the Summary Table.

Before we get to the table, let us have a quick look at the charts I plotted and compare them to the subset of TA signals in the table displayed further down.

Here I have included

  • the SMA, RSI and Stochastic charts for the final RIC — III.L
  • the head of the outputDF dataframe - showing the first few trading signals for BAE Systems (BAES.L)
  • the tail of the outputDF dataframe which represents the final few signals for 3I Group (III.L).

Simple Moving Average chart for 3I Group PLC — III.L

Towards the end of the Simple Moving Average chart, you can see that the SMA14 crosses down through the SMA200 line which is a sell indicator — and if you refer to the tail end of the Summary table below you will see an SMA Sell indicator for the 5th of March 2020.

Relative Strength Index chart for 3I Group PLC — III.L

Here I am plotting the Close price in the upper chart and the RSI value in the lower chart. Just at the tail end, you can see that the RSI value crosses below the lower threshold twice around the end of Feb / start of March — which corresponds to the RSI Buy indicator on 2020–03–05 in the Summary table below

Stochastics chart for 3I Group PLC — III.L

Once again I am plotting the Close price in the upper chart and the Stochastics slowk and slowd lines in the lower chart. You can see that the slowk line crosses above the slowd line in the oversold region (below 20) on three occasions — i.e. buy indicators reflected in the summary table below with Stochastic Buy entries for 2020–02–28, 2020–03–03 and 2020–03–13.
Finally, just before the end of the chart, we see the slowk and slowd lines cross in the overbought region (above 80). This is reflected in the table below as a Stochastic Sell entry at 2020–03–27.

Historical Summary Table

After the initial Technical Analysis run using the historical data, I output a historical Summary table with some basic stats as well as the history of the Trade signals for the configured Interval and Time Period.

For the basic stats, I display the Percent change for various periods such as Week to Day, Month to Day, 6 months — to provide an indication of just how the particular stock has been trading over those periods. I also display the number of intervals where the price has gone up, down or no change as potentially useful reference points.

The rightmost columns of the table contain the Buy and Sell signals occurrences for the 3 TA methodologies — one row for each date/time that triggered a signal — you may occasionally see more than one signal in a single row. For example, whilst I was testing both RSI and Stoch indicated Buy signals on 11th October 2018 for the 3i Group (III.L) — concerning this, you may find the Reuters 3 yr chart for III interesting…

Head of the Summary table — showing the first few trading signals for BAE Systems (BAES.L)

Head of my output summary table for BAES.L

The tail of the Summary table — showing the most recent trading signals for 3I Group (III.L)



0
 Advanced issue found

 
Note how the final few signals for 3I group compare with the crossover points on the charts

Ongoing Technical Analysis

Now that I have the historical analysis out of the way, I will move onto the ongoing ‘real-time’ analysis. In other words, the code that I can keep running to perform the TA on an ongoing basis at the configured interval.

Java
 




xxxxxxxxxx
1
14


 
1
while True:
2
    gotoSleep(myInterval)
3
    for myRIC in listRICs:
4
        historical = basket[myRIC]
5
        latest = ek.get_timeseries(myRIC,fields = 
6
            ['CLOSE','HIGH','LOW'],
7
            count = 1, interval = myInterval)  
8
        # Delete earliest data point
9
        historical.drop(historical.index[0])
10
        # Append latest data point
11
        historical = historical.append(latest)
12
        runAllTA(myRIC, historical)
13
        # Udpate basket with latest values
14
        basket[myRIC] = historical



I put the script to sleep for our configured interval, and when it wakes I request the latest data points for each RIC.

The simplest approach would be to just make the above get_timeseries calls with a revised start_date and end_date parameters. However, this would be quite wasteful of resources and so I will request just the latest data point for my configured interval.

To do this, I make the call with count=1 (rather than Start/End times) to get just the latest data point. I then drop the earliest data point from our historical data points and append the latest one.

I can then invoke the Technical Analysis functions with the most recent data included.

Closing Summary

I could now leave this script running with my preferred interval and it will send messages to the chatroom, each time the Technical Analysis yields a Buy or Sell signal, like the ones shown below:



0
 Advanced issue found

 


0
 Advanced issue found

 


0
 Advanced issue found

 
Example Buy and Sell Signal in the Chatroom
Note: For the purpose of the above screen capture, I did a run with the interval set to ‘ minute’ — in an attempt to generate signals sooner rather than later.

For a trader who uses Simple Moving Average, Relative Strength Indices and/or Stochastic charts to help inform their trading decisions — this could mean they don’t need to actively sit there eyeballing charts all day long.

I hope you found this exercise useful and no doubt there are many possible improvements or refinements that could be made to both the code and the simple TA methodology I have used.
A few examples to consider are:

  • In the Summary table extract above I can see that on 2020–03–05, SMA based TA is indicating a Sell whereas the RSI based TA is indicating a Buy — which suggests that further refinements are required
  • An RSI related article I read, suggested that once the line crosses a threshold it may be better to wait till it crosses back in the opposite direction before generating a signal — so for example, if the crosses below %30, wait till it crosses back up above 30% before generating a Buy signal.
  • In my summary output table, I show the ‘% change’ for a week, month, year etc — which could be relevant for my ‘daily’ interval-based TA — but not so much if say I used an ‘hourly’ interval. It was suggested that a better approach could be to show ‘% change’ values for say, Hour t, Hour t+1, t+5, t+10 etc according to my chosen interval.
  • Another improvement could be to show up/down markers on the plot lines highlighting the crossover points.

As you can imagine, there is no shortage of material on Technical Analysis to be found on the internet.

NOTE: I do not endorse the above workflow as something that should be used for active trading — it is purely an educational example.

Downlaods

Source for this workflow on GitHub
Source for Messenger ChatBot example on GitHub

Topics:
artificial intelligence ,chatrooms ,matplotlib ,python ,stock market trading tips ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}