Back
Learning Hub/ Python for Algo Trading/ Lesson 4 Phase 1 of 6
Python for Algo Trading · Phase 1

Functions — Reusable Logic

Copy-pasting the same signal logic for every stock is a recipe for bugs. Functions let you write logic once and call it anywhere — the building block of every modular, professional algo trading system.

Lesson 4 of 9 ~40 min Intermediate Modular algo design
Lesson 4 of 9 — Functions44% complete
LESSON 04 · SECTION 1
What is a Function?
intermediate

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.

🧩
Define once, use many times
Write def signal(ltp, vwap): once; call it for every stock in your watchlist.
📥
Parameters & Arguments
Parameters are the function's inputs. Arguments are the actual values you pass when calling it.
📤
return statement
Sends a value back to the caller. A function without return returns None.
🔒
Local scope
Variables inside a function exist only within it. This prevents accidental side effects across your system.
first_function.pypython
# 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
Output
BUY
SELL
HOLD

Section 2
Default Parameters

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.

default_params.pypython
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
Output
17500
5100
17500

Section 3
Returning Multiple Values

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.

multi_return.pypython
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}")
Output
Signal    : BUY
Confidence: HIGH
Reason    : VWAP + RSI + Volume confirmed

Section 4
Risk Calculator Function

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.

risk_calculator.pypython
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}")
Output
NIFTY lots      : 5
Max risk        : ₹5,000
Risk per lot    : ₹10,000

BANKNIFTY lots  : 1
💡 DOCSTRINGS
The triple-quoted string right after 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.

Section 5
Combining Functions — Mini Signal Engine

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.

signal_engine.pypython
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)
Output
========================================
Symbol  : NIFTY25JUNFUT
Signal  : BUY @ ₹22485
SL      : ₹22335   |  Target: ₹22785
Lots    : 6
========================================
========================================
Symbol  : BANKNIFTY25JUNFUT
Signal  : SELL @ ₹48100
SL      : ₹48250   |  Target: ₹47800
Lots    : 6
========================================

Section 6
Lambda — Anonymous Functions

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_functions.pypython
# 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}%")
Output
1.58
INFY       +2.07%
RELIANCE   +2.26%
TCS        -2.05%

Section 7
Quick Quiz
QUESTION 1 OF 3
What keyword is used to send a value back from a function?
Ayield
Breturn
Coutput
Dsend
QUESTION 2 OF 3
In def calc_pnl(entry, ltp, lot_size=50), what is lot_size=50?
AA global variable
BA default parameter value
CA required argument
DA constant — cannot be changed
QUESTION 3 OF 3
A function def greet(): print("hello") with no return returns:
A"hello"
B0
CNone
DAn error

Section 8
Practice Exercises
EXERCISE 1 · EASY
P&L Function
Write a function 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.
Hint: P&L = (ltp - entry) × lots × lot_size
EXERCISE 2 · MEDIUM
RSI Signal Function
Write a function 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.
Hint: use ob and os as default parameters.
EXERCISE 3 · CHALLENGE
Full Trade Setup Generator
Write three functions: 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.
Hint: call one function from inside another — this is called function composition.
🚀 UP NEXT — LESSON 5
Functions are powerful — but they work on single values. What if you need to store a list of 50 prices or 200 candles? Lesson 5: Lists teaches you Python's most-used data structure — essential for candle arrays, trade history, and watchlists.
Prev