Building your algo setup in a day

By: Sumit Raj

About Me:

  • Techie | Author | Trainer | Mentor | Startups
  • Working as Principal Engineer at Unacademy
  • Authored internationally published book "Building Chatbots with Python", translated in Portuguese, Korean & Chinese.
  • Avid Quora writer: Over 1.6 million views so far.
  • Still learning to trade.

Problem

  • Not being able to take quick action
  • Emotions play
  • Strategies needs more time
  • Seamless execution
  • Cost and Productivity

Challenge

  • I know the strategy
  • I have manually backtested it or got it backtested.
  • I can write the pseudo code, may be using Chartink or TradingView using an interface
  • But I don't know how to automate or I can't sit all day in front of the system

Serverless Solution

What is serverless?

  • Serverless architectures doesn't mean there are no servers.
  • 3rd-party "Backend as a Service" (BaaS) services or "Functions as a Service" (FaaS) platform
  • Removes the need for a traditional always-on server like EC2 or VMs.
  • No out of memory error unless you already know how much you needed.

The Strategy

Backtested results on StockMock

Rule 1:

Short  ( ATM + 0.5 % CE   and    ATM - 0.5 % PE )  with 30% SL

Rule 2:

Entry at 9:19 AM and exit at 15:00pm

Rule 3:

If SL triggers for anyone of the leg (CE or PE) THEN

For that particular leg (CE or PE) re-execute logic ( i.e short ATM + 0.5  % CE or  ATM - 0.5 %PE ) the next minute.​

Re-execute can be done max for 2 times each leg (CE or PE ) if SL Triggers.

Rule 4:

Let's dive into code

Rule 1: Part - I

def get_ltp(symbol):
    url = f"https://api.kite.trade/quote/ltp?i={symbol}"
    response = requests.request("GET", url, headers=headers, data={}).json()
    ltp =  response["data"][symbol]["last_price"]
    return ltp

def get_atm_strikes(strike_list, symbol):
    """
        Gets the ATM+0.5% strike for CE and ATM-0.5% strike for PE
    """
    ltp = get_ltp(symbol)
    atm_strike_ce = closest(strike_list, ltp * 1.005) #ATM + 0.5% for CE
    atm_strike_pe = closest(strike_list, ltp * 0.995) #ATM + 0.5% for PE
    data = [
            {"strike_price": atm_strike_ce, "type": "CE"},
            {"strike_price": atm_strike_pe, "type": "PE"}
        ]
    return data
sl = ltp * 1.3
#Place SL-M order
order_obj = place_order(tradingsymbol, qty, "BUY", order_type='SL-M',
                        trigger_price=sl)
order_id = order_obj['data']['order_id']
orders.append({'inst_type': inst_type, 'tradingsymbol': tradingsymbol, 'qty': qty,
               'order_id': order_id, 'sl': sl, 'sl_hit': False})

Rule 1: Part - II

Rule 2

if datetime.time(9, 19) < current_time.time() < datetime.time(15, 0): #Rule 2

Rule 3

def is_sl_hit(order_id):
#     if order_id == "382270196720590":
#         return True
#     else:
#         return False
    return order_status(order_id) == 'COMPLETE'

Rule 4

sl_hit_thresold = 2 #Rule 4

count_of_sl = len(get_data({"inst_type": it, "sl_hit": True}))

if count_of_sl < sl_hit_thresold:
    sl_not_hit = get_data({"inst_type": it, "sl_hit": False}, {"order_id": 1})
    order_id = sl_not_hit[0]['order_id']

Utility Methods

def get_data(query={}, project={"_id": 0}):
    data = db['tradelog'].find(query, project)
    return list(data)

def update_data(query, data):
    data.update({"modified_at": dt.now()})
    newvalues = { "$set": data }
    x = db['tradelog'].update_one(query, newvalues)

def insert_orders(orders):
    print(orders, "orders list")
    x = db['tradelog'].insert_many(orders)
    return x.inserted_ids
def find_entry():
    #Rule 1
    inst = requests.get(instruments + '/NFO', headers=headers).text
    df_fno = pd.read_csv(StringIO(inst))
    df_options = df_fno[df_fno.segment == 'NFO-OPT']

    df_bnf_only = df_options[(df_options.name.isin(['BANKNIFTY']))]
    df_bnf_only = df_bnf_only[['instrument_token', 'tradingsymbol', 'expiry', 'strike', 
                               'instrument_type', 'name']]

    df = get_and_process_data(df_bnf_only, expiry_date)
    atm_strikes_with_type = get_atm_strikes(df['strike'], f"NFO:{index_symbol}")
    tradingsymbols = get_tradingsymbols(df, atm_strikes_with_type)
    return tradingsymbols
def exit_all_positions():
    print("Exiting all positions at after cut off time")
    sl_not_hit_by_3pm = get_data({"sl_hit": False}, {"order_id": 1,
                                                     "tradingsymbol": 1, "qty": 1})

    for t in sl_not_hit_by_3pm:
        print(t['tradingsymbol'], t['qty'], "BUY")
        place_order(t['tradingsymbol'], t['qty'], "BUY")

Utility Methods

def order_status(order_id):
    url = f"https://api.kite.trade/orders/{order_id}"
    payload={}

    response = requests.request("GET", url, headers=headers, data=payload)
    return response.json()['data']['status'] #list of orders

Cost Perspective

  • 1M free requests per month and 400,000 GB-seconds of compute time per month for AWS Lambda
Price
Requests $0.20 per 1M requests
Duration $0.0000166667 for every GB-second

Average trading days: 21 * 6 hour/day = 126 hours
Average runtime: 800 ms ~1sec
126 hours= 126*3600 = 4,53,600 seconds

(126*3600*128)/1024 = 56,700 GB-seconds

 

56,700 GB-s - 400000 free tier GB-s = -343,300.00 GB-s

 

Lambda costs - With Free Tier (monthly): 0.00 USD

Let's do the maths

Number of Requests (per month) Price (per million)
First 300 million $1.00
300+ million $0.90
  • Amazon API Gateway free tier includes one million API calls per month for up to 12 months.
  • 5GB Data (ingestion, archive storage, and data scanned by Logs Insights queries)
  • $1.00 per million events

Productivity and Maintenance

Perspective

  • Setup cronjob with click of a button
  • Logging is a piece of cake
  • Search logs for history of your executions
  • Stopping execution using Throttle
  • Setup once and use forever

Why choose Serverless Lambda over EC2?

  • Highly reduced operational cost
  • Less complex to manage compared to VMs
  • Faster development time and productivity
  • Near zero maintenance

Questions?

Thank you.

                               https://t.me/algotradingwithsumit

                                  @sumit12dec

+91 959 028 3524

   sumit786raj@gmail.com

Telegram:

Twitter:

Phone:

Email:

StockMock Podcast

By sumit12dec

StockMock Podcast

  • 157