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?
Telegram:

Twitter:
Phone:
Email:



StockMock Podcast
By sumit12dec
StockMock Podcast
- 157