Markets: A Rigorous Course

Exercise · m01_costs_tax.py

What a trade really costs in India

Every trade pays a stack of charges: brokerage, STT, exchange transaction charges, SEBI turnover fee, stamp duty, and GST on some of those. Beginners ignore them; they quietly decide whether an intraday strategy can ever work.

The rates are passed in as a dict so your code never hard-codes numbers that SEBI or the government will change. Shape of rates (see notes.md 1.7 for current values and where to verify them):

rates = { "brokerage": {seg: {"pct": 0.0003, "cap": 20.0} or {"flat": 20.0}}, "stt": {seg: {"buy": 0.001, "sell": 0.001}}, "exchange": {seg: 0.0000297}, # fraction of turnover, both sides "sebi_per_crore": 10.0, # rupees per 1 crore of turnover, both sides "stamp": {seg: 0.00015}, # fraction of BUY value only "gst": 0.18, # on brokerage + exchange + SEBI fee } seg is one of: "delivery", "intraday", "futures", "options". For options, buy_value and sell_value are PREMIUM values (premium x qty).

The test file uses ILLUSTRATIVE rates (pre-April-2026 STT); the notes give the current ones. Check: cd exercises && .venv/bin/python -m pytest modules/M01-market-structure/tests

Tests (5)

  • test_brokerage_rules
  • test_delivery_round_trip
  • test_intraday_and_options
  • test_breakeven_move
  • test_capital_gains

Theory: M01 notes. Run with Ctrl/⌘+Enter.

not run yet

Not saved: sign up to keep your code
"""What a trade really costs in India.

Every trade pays a stack of charges: brokerage, STT, exchange transaction
charges, SEBI turnover fee, stamp duty, and GST on some of those. Beginners
ignore them; they quietly decide whether an intraday strategy can ever work.

The rates are passed in as a dict so your code never hard-codes numbers that
SEBI or the government will change. Shape of `rates` (see notes.md §1.7 for
current values and where to verify them):

rates = {
    "brokerage": {seg: {"pct": 0.0003, "cap": 20.0}  or  {"flat": 20.0}},
    "stt":       {seg: {"buy": 0.001, "sell": 0.001}},
    "exchange":  {seg: 0.0000297},          # fraction of turnover, both sides
    "sebi_per_crore": 10.0,                 # rupees per 1 crore of turnover, both sides
    "stamp":     {seg: 0.00015},            # fraction of BUY value only
    "gst": 0.18,                            # on brokerage + exchange + SEBI fee
}
seg is one of: "delivery", "intraday", "futures", "options".
For options, buy_value and sell_value are PREMIUM values (premium x qty).

The test file uses ILLUSTRATIVE rates (pre-April-2026 STT); the notes give
the current ones. Check:
    cd exercises && .venv/bin/python -m pytest modules/M01-market-structure/tests
"""

CRORE = 1e7


def brokerage_one_side(value: float, spec: dict) -> float:
    """Brokerage for one executed order of `value` rupees.

    - {"flat": F}            -> F (0 if value == 0)
    - {"pct": p, "cap": C}   -> min(p * value, C); a cap of 0 means "no cap" only if pct is 0... keep it simple:
                                if cap > 0 return min(p*value, cap) else return p*value
    """
    raise NotImplementedError


def trade_costs(buy_value: float, sell_value: float, segment: str, rates: dict) -> dict:
    """All charges for a round trip (one buy order + one sell order).

    Return a dict with keys: brokerage, stt, exchange, sebi, stamp, gst, total.
    """
    raise NotImplementedError


def breakeven_move(price: float, qty: float, segment: str, rates: dict) -> float:
    """For a LONG bought at `price`, how many rupees per unit must price rise
    before the round trip makes zero net profit?

    Solve s*qty - price*qty - total_costs(price*qty, s*qty) = 0 for s and
    return s - price. Costs depend on s, so use a root finder
    (scipy.optimize.brentq) or iterate to a fixed point.
    """
    raise NotImplementedError


def capital_gains_tax(gain: float, holding_days: int, rates: dict) -> float:
    """Tax on a gain from listed equity shares (delivery).

    rates = {"stcg": 0.20, "ltcg": 0.125, "ltcg_exempt": 125000, "lt_days": 365}
    - holding_days > lt_days  -> long-term: tax = ltcg * max(0, gain - ltcg_exempt)
    - otherwise               -> short-term: tax = stcg * gain
    - a loss (gain <= 0) pays 0 here (set-off rules are in notes.md §1.8)
    Ignore cess and surcharge.
    """
    raise NotImplementedError

No runs yet

The stub's functions raise NotImplementedError until you write them. Fill in one, press Run tests, and watch its tests turn green.

Reference solution

Available after your first run, with an account approved for code runs.

Create a free account

Reading is open to everyone. An account keeps your progress, answers, notes and code drafts, and gives you the study plan, the journals and the simulator. Running code needs an account approved for it.