A function is a named, reusable block of code. You define it once with def
and call it as many times as needed. Functions accept inputs (parameters)
and return outputs (return values) — just like a trading engine:
give it price data, get back a signal.
def signal(ltp, vwap): once; call it for every stock in your watchlist.return statementreturn returns None.# Define a function def get_signal(ltp, vwap): if ltp > vwap: return "BUY" elif ltp < vwap: return "SELL" else: return "HOLD" # Call it for multiple stocks print(get_signal(22450, 22380)) # BUY print(get_signal(22100, 22380)) # SELL print(get_signal(22380, 22380)) # HOLD
BUY SELL HOLD
Default parameters give a fallback value if an argument is not passed. This is very useful for optional settings like lot size, slippage, or confidence threshold.
def calc_pnl(entry, ltp, lot_size=50, side="BUY"): if side == "BUY": pnl = (ltp - entry) * lot_size else: pnl = (entry - ltp) * lot_size return pnl # NIFTY default lot = 50 print(calc_pnl(22100, 22450)) # ₹17500 (default lot 50) print(calc_pnl(48200, 48540, lot_size=15)) # BANKNIFTY lot 15 print(calc_pnl(22450, 22100, side="SELL")) # Short trade
17500 5100 17500
Python functions can return multiple values as a tuple. This is extremely useful for signal functions that need to return a signal and its confidence or the stop-loss and target together.
def vwap_signal(ltp, vwap, rsi, volume_ratio): """Returns (signal, confidence, reason)""" if ltp > vwap and rsi < 65 and volume_ratio >= 1.2: return "BUY", "HIGH", "VWAP + RSI + Volume confirmed" elif ltp > vwap and rsi < 65: return "BUY", "MEDIUM", "VWAP confirmed, volume weak" elif ltp < vwap: return "SELL", "MEDIUM", "Price below VWAP" else: return "HOLD", "LOW", "Neutral zone" # Unpack the tuple signal, confidence, reason = vwap_signal(22485, 22380, 53, 1.4) print(f"Signal : {signal}") print(f"Confidence: {confidence}") print(f"Reason : {reason}")
Signal : BUY Confidence: HIGH Reason : VWAP + RSI + Volume confirmed
Proper position sizing is one of the most critical functions in any algo system. This function calculates lot size based on capital, risk per trade, and stop-loss distance.
def calc_position_size(capital, risk_pct, entry, stop_loss, lot_size): """ Returns the number of lots to trade based on risk %. capital : total trading capital (₹) risk_pct : max % of capital to risk per trade (e.g. 1.0 for 1%) entry : trade entry price stop_loss : stop-loss price lot_size : contract lot size (NIFTY=50, BANKNIFTY=15) """ risk_amount = capital * (risk_pct / 100) risk_per_lot = abs(entry - stop_loss) * lot_size if risk_per_lot == 0: return 0 lots = int(risk_amount / risk_per_lot) return max(lots, 1) # minimum 1 lot # NIFTY trade: capital ₹5L, 1% risk lots = calc_position_size( capital=500000, risk_pct=1.0, entry=22300, stop_loss=22100, lot_size=50 ) print(f"NIFTY lots : {lots}") print(f"Max risk : ₹{500000 * 0.01:,.0f}") print(f"Risk per lot : ₹{abs(22300-22100)*50:,}") # BANKNIFTY trade lots_bn = calc_position_size(500000, 1.0, 48200, 47900, 15) print(f"\nBANKNIFTY lots : {lots_bn}")
NIFTY lots : 5 Max risk : ₹5,000 Risk per lot : ₹10,000 BANKNIFTY lots : 1
def is called a docstring. It documents what the function does, its parameters and return value. Always write docstrings for trading functions — your future self will thank you.
Real trading systems are built by combining small, focused functions. Here we chain together our signal and risk functions into a mini trade setup generator.
def get_signal(ltp, vwap, rsi): if ltp > vwap and rsi < 65: return "BUY" if ltp < vwap and rsi > 35: return "SELL" return "HOLD" def calc_stops(entry, signal, sl_pts=150, tgt_pts=300): if signal == "BUY": return entry - sl_pts, entry + tgt_pts elif signal == "SELL": return entry + sl_pts, entry - tgt_pts return None, None def generate_trade_setup(symbol, ltp, vwap, rsi, capital=500000): signal = get_signal(ltp, vwap, rsi) if signal == "HOLD": print(f"{symbol}: No trade — HOLD") return sl, tgt = calc_stops(ltp, signal) lots = int((capital * 0.01) / (abs(ltp - sl) * 50)) print(f"\n{'='*40}") print(f"Symbol : {symbol}") print(f"Signal : {signal} @ ₹{ltp}") print(f"SL : ₹{sl} | Target: ₹{tgt}") print(f"Lots : {lots}") print(f"{'='*40}") generate_trade_setup("NIFTY25JUNFUT", 22485, 22380, 53) generate_trade_setup("BANKNIFTY25JUNFUT", 48100, 48300, 48)
======================================== Symbol : NIFTY25JUNFUT Signal : BUY @ ₹22485 SL : ₹22335 | Target: ₹22785 Lots : 6 ======================================== ======================================== Symbol : BANKNIFTY25JUNFUT Signal : SELL @ ₹48100 SL : ₹48250 | Target: ₹47800 Lots : 6 ========================================
A lambda is a tiny one-line function without a name. Use it for simple transformations — rounding prices, computing percentage change, or sorting a list by a field.
# Lambda syntax: lambda params: expression pct_change = lambda entry, ltp: round((ltp - entry) / entry * 100, 2) print(pct_change(22100, 22450)) # 1.58% # Sort watchlist by % change (descending) trades = [ {"sym": "RELIANCE", "entry": 2880, "ltp": 2945}, {"sym": "TCS", "entry": 3900, "ltp": 3820}, {"sym": "INFY", "entry": 1450, "ltp": 1480}, ] ranked = sorted(trades, key=lambda t: (t["ltp"] - t["entry"]) / t["entry"], reverse=True) for t in ranked: chg = pct_change(t["entry"], t["ltp"]) print(f"{t['sym']:10} {chg:+.2f}%")
1.58 INFY +2.07% RELIANCE +2.26% TCS -2.05%
def calc_pnl(entry, ltp, lot_size=50), what is lot_size=50?def greet(): print("hello") with no return returns:pnl(entry, ltp, lots, lot_size=50) that returns
the P&L for a NIFTY long trade. Test with entry=22100, ltp=22450, lots=3.
rsi_signal(rsi, ob=70, os=30) that returns
"OVERBOUGHT", "OVERSOLD", or "NEUTRAL". Use default parameters for overbought/oversold levels.
Test with RSI values 75, 25, and 50.
ob and os as default parameters.get_signal(ltp, vwap), get_sl_target(entry, signal, sl=150, tgt=300),
and trade_report(symbol, ltp, vwap) that calls the other two and prints
a formatted trade card with symbol, signal, SL, and target.