Building an Order Generator App using Angel One SmartAPI’s

Let’s say you are a positional investor who buys and holds securities for the long term after doing your due diligence. You very carefully come up with the allocation percentage to each stock, and now you want to invest 10,00,000 rupees into this basket of stocks but do not want to go through the pain of figuring out the Current Market Price (CMP) of each stock and finally calculating the quantity you will have to buy for each stock to maintain the asset allocation. Frankly, there are many tools out there like Google Sheets which can give you the CMP (15 mins delayed) via which you can calculate your quantity and place your orders manually. But what if we can automate this whole process and remove the pain points? Sounds exciting?

Check this video out of the app we will create in this article, where you can just upload a spreadsheet containing your asset allocation, and it will directly place all orders into your account.

Angel_Allocation_App

Trust us, this is very simple to build, and you will be done in minutes. We will utilize our previous articles’ learning on generating SmartAPI Key, order ticket generation, parallel processing, etc. Make sure you check out those articles for more understanding of Angel One’s SmartAPI. We will be building functions to do the following steps in this article:

  1. smartApi_login() will be used to log into your account and return an API Object used by the place_order function.
  2. get_ticker_file() will be used to fetch the ticker file from this link.
  3. tradingSymbol() will be used to generate the trading symbol accepted by SmartAPI; for example, for the NSE segment, the symbol for RELIANCE will be RELIANCE-EQ, and for the BSE segment, the symbol will be just RELIANCE.
  4. cmp() will be used to get the Current Market Price of the trading symbol mentioned above.
  5. shares_to_buy() will be used to calculate the number of shares to buy based on capital allocation and the current market price.
  6. generate_trade_list() will be used to generate the order param dictionary that the SmartAPI Object will accept to place trades on your account.
  7. place_order() will be used to place orders.

This article aims to show you how to build these functions and how they interact with each other in an overall view. We will not be show steps on how to build the frontend application in this article but will share the code in the end for you to review.

Python Implementation

1. Importing the Libraries and Creating a Login Function

Probably one of the simplest functions in this article and one we have covered multiple times in the past. This function will accept your apiKey, clientCode, and clientPassword to return an ApiObj (highlighted in yellow below)

import pandas as pd
import os, urllib.request, math, time
from smartapi import SmartConnect
import streamlit as st
import time
#you will know why other imports are being made slowly.

def smartApi_login(apiKey, clientCode, clientPassword):

    obj = SmartConnect(api_key = apiKey)
    data = obj.generateSession(clientCode, clientPassword)

    if data['message'] == 'SUCCESS':
        return obj

apiObj = smartApi_login('your_api_key', 'your_client_code', 'your_client_password')

2. Building a Function to Get Ticker File

The reason we need to get the latest ticker file is to ensure we are using the correct trading symbols to place our orders via the API. The trading symbols for NSE and BSE are different, and the ticker file will provide us that mapping. Very often, when you are creating that allocation sheet, you will not have access to the correct symbols, so it’s best to reference them via this file. Just for reference, below is the allocation sheet we have created; if we pass the Symbol column directly to the APIs, it will not work; we need to pass the correct symbol referenced in the Ticker File.

def get_ticker_file():
    '''
    A function to get all available instruments at Angel Broking
    This is primarily used to get the symbol tokenCode
    '''
    url = "https://margincalculator.angelbroking.com/OpenAPI_File/files/OpenAPIScripMaster.json"
    data = urllib.request.urlopen(url).read().decode()
    df = pd.read_json(data)

    return df

df = get_ticker_file()

 

3. Building a Function to Generate Trading Symbol

For most available common stocks on NSE, the SmartAPI Trading Symbol will generally end with -EQ; there might be a few cases where the symbols end with something other than -EQ, but that is not under the scope of this article and may be dealt with in more advanced articles. Like in the screenshot above, we have highlighted M&MFIN-BL, but M&MFIN also has a symbol M&MFIN-EQ available. So for NSE, we will just append a -EQ to every symbol we get.

For the codes on BSE, since we are uploading the actual BSE Code, we need to find the correct symbol name. Let’s see an example below. Considering df is the output of the function get_ticker_file() above. So in the below example, for Apollo Tricoat, we would want APOLLOTRI to be returned as the tradingsymbol, which the SmartAPI will accept.

 

def tradingSymbol(exchange, symbol, angel_df):
    
    if exchange == 'NSE':
        new_symbol = str(symbol) + '-EQ'
        return new_symbol
    
    elif exchange == 'BSE':
        new_symbol = angel_df[angel_df['token'] == str(symbol)]['symbol'].iloc[0]
        return new_symbol

allocation_df = pd.read_excel('path/to/allocation.xlsx')
allocation_df['tradingsymbol'] = allocation_df.apply(lambda x: tradingSymbol(x['Exchange'], x['Symbol'], df), axis=1)

Now, let’s quickly test this function to see if this works; we will read our allocation spreadsheet into a new data frame called allocation_df

Looks good for now. Let’s move to the next function.

4. Building a Function to Get Current Market Price or Last Traded Price.

This is very simple to fetch via the SmartAPI endpoint ltpData; let's see a quick example by fetching the price of HDFCBANK. It needs three parameters to fetch data, i.e., exchange, tradingsymbol, and token (available from ticker file)

As you can see from the above, we already have the exchange and symbol column in our allocation data frame from the above steps; we also need the token to fetch the last traded price. Let’s code a quick function. In the case of BSE, we already have everything we need as the symbol is the token already.

def cmp(apiObj, exchange, symbol, ticker_df):
    try:
        if exchange == "NSE":
            new_symbol = str(symbol) + '-EQ'
            token = ticker_df[ticker_df['symbol'] == new_symbol]['token'].iloc[0]

            cmp = apiObj.ltpData(exchange, new_symbol, token)
            ltp = cmp['data']['ltp']

            return ltp

        elif exchange == "BSE":
            angel_symbol = angel_df[angel_df['token'] == str(symbol)]['symbol'].iloc[0]
            #assumption that symbol is a token for BSE
            cmp = apiObj.ltpData(exchange, angel_symbol, symbol)
            ltp = cmp['data']['ltp']
            return ltp
        
    except IndexError:
        return 'No Price'

allocation_df['cmp'] = allocation_df.apply(lambda x: cmp(apiObj, x['Exchange'], x['Symbol'], df), axis=1)

So, now you have the current market price of each stock in your allocation, the only thing now left is to calculate the number of shares to buy based on the allocation percentage and the CMP.

5. Building a Function to Calculate Number of Shares to Buy.

This is a simple one that just takes three inputs, the capital we would like to invest, the allocation percentage, and then the current market price. The investment amount can be variable and can be input in the frontend application. In the below example, let’s assume a sum of ten lakh rupees.

def shares_to_buy(investment, allocation, cmp):
    
    amt_to_invest = int(investment)*allocation
    shares_to_buy = math.floor(amt_to_invest/cmp)
    
    return shares_to_buy

allocation_df['quantity'] = allocation_df.apply(lambda x: shares(1000000, x['Allocation'], x['cmp']), axis=1)

That’s it; we now have everything we need to generate our orderparams dictionary and to place the orders using SmartAPI finally.

6. Building a Function to Generate Trade List

We will assume we want to buy ‘NORMAL’ orders for ‘DELIVERY’ with a ‘LIMIT’ price which will be valid for the ‘DAY’.

def generate_trade_list(trade_df, ticker_df):

    tradeList = []

    for index, rows in trade_df.iterrows():
        trade_dict = {"variety": 'NORMAL', "tradingsymbol" : str(rows['tradingsymbol']),
                    "symboltoken" : str(ticker_df[ticker_df['symbol'] == rows['tradingsymbol']]['token'].iloc[0]),
                    "transactiontype":'BUY', "exchange": str(rows['Exchange']),
                    "ordertype": 'LIMIT', "producttype": 'DELIVERY', "duration": 'DAY', 
                    "price": str(rows['cmp']), "quantity": str(rows['quantity']),
                    "triggerprice": '0'}

        tradeList.append(trade_dict)
        
    return tradeList

trade_list = generate_trade_list(allocation_df, df)

6. Building a Function to Place Orders

Now, we have the trade list generated in an appropriate format in all the details; we can pass on those details to the ApiObj we created in the first step to place orders in the account.

def place_order(apiObj, trade_dict):

    obj = apiObj
    orderParams = trade_dict
    try:
        orderID = obj.placeOrder(orderParams)
        print(f"Trade Placed for Symbol: {orderParams['tradingsymbol']} with OrderID: {orderID}")

    except Exception as e:
        time.sleep(1)
        try:
            orderID = obj.placeOrder(orderParams)
            print(f"Trade Placed for Symbol: {orderParams['tradingsymbol']} with OrderID: {orderID}")
        except Exception as e:
            print(e)

And we are Done! We hope you enjoyed this overall exercise of how you can build your own app; we have shown you the backend, the below code will also integrate the frontend. To run this, please ensure you have streamlit installed on your system. If not, you can do pip install streamlit to install it.

Save the below code in a .py file and ensure to enter your apikey, clientcode, and password in the code and run it using streamlit run filename.py

import pandas as pd
import os, urllib.request, math
from datetime import datetime
from smartapi import SmartConnect
import streamlit as st
import time

@st.cache()
def get_ticker_file():
    '''
    A function to get all available instruments at Angel Broking
    This is primarily used to get the symbol tokenCode
    '''
    url = "https://margincalculator.angelbroking.com/OpenAPI_File/files/OpenAPIScripMaster.json"
    data = urllib.request.urlopen(url).read().decode()
    df = pd.read_json(data)

    return df

def smartApi_login(apiKey, clientCode, clientPassword):

    assert (isinstance(apiKey, str) is True), "apiKey input should be str"
    assert (isinstance(clientCode, str) is True), "clientCode input should be str"
    assert (isinstance(clientPassword, str) is True), "clientPassword input should be str"

    obj = SmartConnect(api_key = apiKey)
    data = obj.generateSession(clientCode, clientPassword)

    if data['message'] == 'SUCCESS':
        return obj

def cmp(apiObj, exchange, symbol, angel_df):
    try:
        if exchange == "NSE":
            new_symbol = str(symbol) + '-EQ'
            token = angel_df[angel_df['symbol'] == new_symbol]['token'].iloc[0]

            cmp = apiObj.ltpData(exchange, new_symbol, token)
            ltp = cmp['data']['ltp']

            return ltp

        elif exchange == "BSE":
            angel_symbol = angel_df[angel_df['token'] == str(symbol)]['symbol'].iloc[0]
            #assumption that symbol is a token for BSE
            cmp = apiObj.ltpData(exchange, angel_symbol, symbol)
            ltp = cmp['data']['ltp']
            return ltp
        
    except IndexError:
        return 'No Price'

def shares(investment, allocation, cmp):
    
    amt_to_invest = int(investment)*allocation
    shares_to_buy = math.floor(amt_to_invest/cmp)
    
    return shares_to_buy

def tradingSymbol(exchange, symbol, angel_df):
    
    if exchange == 'NSE':
        new_symbol = str(symbol) + '-EQ'
        return new_symbol
    
    elif exchange == 'BSE':
        new_symbol = angel_df[angel_df['token'] == str(symbol)]['symbol'].iloc[0]
        return new_symbol

def generate_trade_list(trade_df, angel_stocks_df):

    tradeList = []

    for index, rows in trade_df.iterrows():
        trade_dict = {"variety": 'NORMAL', "tradingsymbol" : str(rows['tradingsymbol']),
                    "symboltoken" : str(angel_stocks_df[angel_stocks_df['symbol'] == rows['tradingsymbol']]['token'].iloc[0]),
                    "transactiontype":'BUY', "exchange": str(rows['Exchange']),
                    "ordertype": 'LIMIT', "producttype": 'DELIVERY', "duration": 'DAY', 
                    "price": str(rows['cmp']), "quantity": str(rows['quantity']),
                    "triggerprice": '0'}

        tradeList.append(trade_dict)
        
    return tradeList

def place_order(apiObj, trade_dict):

    obj = apiObj
    orderParams = trade_dict

    try:
        orderID = obj.placeOrder(orderParams)
        placeholder1.success(f"Trade Placed for Symbol: {orderParams['tradingsymbol']} with OrderID: {orderID}")

    except Exception as e:
        time.sleep(1)
        try:
            orderID = obj.placeOrder(orderParams)
            placeholder1.success(f"Trade Placed for Symbol: {orderParams['tradingsymbol']} with OrderID: {orderID}")
        except Exception as e:
            print(e)

if __name__ == '__main__':

    st.title("Angel One Asset Allocation App")
    allocation_df = pd.DataFrame()

    with st.form(key = 'angel_one_allocation'):

        uploaded_file = st.file_uploader("Upload your Capital Allocation File", type = ['xlsx'])
        capital_deployed = st.text_input("Capital Deployed", value = "100000")
        submitbutton = st.form_submit_button("Submit")

        if submitbutton:
            allocation_df = pd.read_excel(uploaded_file)
            st.write(f" You are deploying Capital {capital_deployed} across {len(allocation_df)} stocks.")
            placeholder1 = st.empty()
            placeholder1.info("Getting the Latest Ticker File from Angel One")
            angel_ticker_df = get_ticker_file()
            placeholder1.info("Angel One Ticker File Downloaded. Now Initiating Login")
            apiObj = smartApi_login('your_api_key', 'your_client_code', 'your_password')
            placeholder1.success("Login Into Account SuccessFul")
            placeholder1.info("Getting the Current Market Price of All Stocks Mentioned in Uploaded File")
            allocation_df['cmp'] = allocation_df.apply(lambda row: cmp(apiObj, row['Exchange'], row['Symbol'], angel_ticker_df), axis = 1)
            placeholder1.info("Calculating the Quantity to be bought for each stock based on capital and current price.")
            allocation_df['quantity'] = allocation_df.apply(lambda row: shares(capital_deployed, row['Allocation'], row['cmp']), axis = 1)
            allocation_df['tradingsymbol'] = allocation_df.apply(lambda row: tradingSymbol(row['Exchange'], row['Symbol'], angel_ticker_df), axis = 1)

            placeholder1.info("Generating the List of Orders to be Placed")
            trade_list = generate_trade_list(allocation_df, angel_ticker_df)

            for trades in trade_list:
                place_order(apiObj, trades)

            placeholder1.success("All Orders Successfully placed, check your account for more details.")

    if allocation_df.empty == False:
        allocation_df = allocation_df[['Company Name', 'Exchange', 'tradingsymbol', 'Allocation', 'cmp', 'quantity']]
        st.table(allocation_df)

We hope you enjoyed the article; let us know if you face any technical challenges in the comments. Have a good day!

‘Investments in securities market are subject to market risks, read all the related documents carefully before investing.’

Angel Broking Limited (formerly known as Angel Broking Private Limited), Registered Office: G-1, Ackruti Trade Center, Road No. 7, MIDC, Andheri (E), Mumbai – 400 093. Tel: (022)42319600 .Fax: (022) 42319607, CIN: L67120MH1996PLC101709, SEBI Regn. No.: INZ000161534-BSE Cash/F&O/CD (Member ID: 612), NSE Cash/F&O/CD (Member ID: 12798), MSEI Cash/F&O/CD (Member ID: 10500), MCX Commodity Derivatives (Member ID: 12685) and NCDEX Commodity Derivatives (Member ID: 220), CDSL Regn. No.: IN-DP-384-2018, PMS Regn. No.: INP000001546, Research Analyst SEBI Regn. No.: INH000000164, Investment Adviser SEBI Regn. No.: INA000008172, AMFI Regn. No.: ARN–77404, PFRDA Registration No.19092018. Compliance officer: Ms.Richa Ghosh, Tel: (022) 39413940 Email: compliance@angelbroking.com

Related Articles

Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x