+
+

Stamp Duty Land Tax#

+

Stamp Duty Land Tax (SDLT) is a tax imposed on the purchase and rental of properties or land with values over a certain threshold in the UK. It applies to both residential and non-residential (commercial) properties.

+

Stamp Duty Land Tax parameters can be found in policyengine_uk/parameters/gov/hmrc/stamp_duty and logic in policyengine_uk/variables/gov/hmrc/stamp_duty_land_tax.py.

+
+

Legislation#

+

The regulation of SDLT is defined in part 4 of the Finance Act 2003.

+
+
+

Rates#

+

The rates of SDLT depend on various factors, including the purchase price of the property, property type, and whether it is an additional property or a purchase by a first-time buyer. Generally, SDLT rates are higher for properties or land with a purchase price exceeding a specific threshold, often referred to as “stamp duty bands” or “stamp duty brackets.” The rules and thresholds for SDLT may change over time due to government policy adjustments. First-time buyers may be eligible for reduced SDLT rates if the purchase price of the property is not more than £500,000.

+

Here we show current rates and thresholds for SDLT under different situations in 2023 below:

+
  • Residential
  • +
      +
    • Rent

      +

      Up to £125,000: 0%

      +

      Over £125,000: 1%

      +
    • +
    • Purchase

      +
        +
      • Main property

        +
          +
        • First-time

          +

          Up to £300,000: 0%

          +

          £300,000 to £500,000: 5%

          +

          Over £500,000: Ineligible for relief, follow standard rates

          +
        • +
        • Subsequent (standard rates):

          +

          Up to £125,000: 0%

          +

          £125,000 to £250,000: 2%

          +

          £250,000 to £925,000: 5%

          +

          £925,000 to £1,500,000: 10%

          +

          Over £1,500,000: 12%

          +
        • +
        +
      • +
      • Additional property

        +

        An additional 3% on top of the standard rates for each band. However, a secondary property or land with a purchase price or market value less than £40,000 will be exempt from SDLT.

        +

        Less than £40,000: 0%

        +

        £40,000 to £125,000: 3%

        +

        £125,000 to £250,000: 5%

        +

        £250,000 to £925,000: 8%

        +

        £925,000 to £1,500,000: 13%

        +

        Over £1,500,000: 15%

        +
      • +
      +
    • +
    +
  • Non-residential
  • +
      +
    • Rent

      +

      Up to £150,000: 0%

      +

      £150,000 to £5,000,000: 1%

      +

      Over £5,000,000: 2%

      +
    • +
    • Purchase

      +

      Up to £150,000: 0%

      +

      £150,000 to £250,000: 2%

      +

      Over £250,000: 5%

      +
    • +
    +
    +
    + + +Hide code cell source + +
    +
    import pandas as pd
    +import plotly.express as px
    +from policyengine_core.charts import format_fig
    +
    +residential_rent = pd.DataFrame({
    +    "Threshold": [0, 125000, 5200000],
    +    "Rate": [0, 0.01, 0.01],
    +    "Label": ["Residential rent"]*3
    +})
    +residential_purchase_main_first = pd.DataFrame({
    +    "Threshold": [0, 300000, 500000, 925000, 1500000, 5200000], 
    +    "Rate": [0, 0.05, 0.05, 0.1, 0.12, 0.12], 
    +    "Label": ["Residential purchase main (first home)"]*6   
    +})
    +residential_purchase_main_standard = pd.DataFrame({
    +    "Threshold": [0, 125000, 250000, 925000, 1500000, 5200000],
    +    "Rate": [0, 0.02, 0.05, 0.1, 0.12, 0.12],
    +    "Label": ["Residential purchase main standard"]*6
    +})
    +residential_purchase_additional = pd.DataFrame({
    +    "Threshold": [0, 40000, 125000, 250000, 925000, 1500000, 5200000], 
    +    "Rate": [0, 0.03, 0.05, 0.08, 0.13, 0.15, 0.15], 
    +    "Label": ["Residential purchase additional"]*7
    +})
    +non_residential_rent = pd.DataFrame({
    +    "Threshold": [0, 150000, 5000000, 5200000],
    +    "Rate": [0, 0.01, 0.02, 0.02],
    +    "Label": ["Non-residential rent"]*4
    +})
    +non_residential_purchase = pd.DataFrame({
    +    "Threshold": [0, 150000, 250000, 5200000],
    +    "Rate": [0, 0.02, 0.05, 0.05],
    +    "Label": ["Non-residential purchase"]*4
    +})
    +df_list = [residential_rent, residential_purchase_main_first, residential_purchase_main_standard, residential_purchase_additional,
    +           non_residential_rent, non_residential_purchase]
    +df = pd.concat(df_list)
    +# plot
    +fig = px.line(
    +    df,
    +    x="Threshold",
    +    y="Rate",
    +    color="Label",
    +    title = "Stamp duty land tax rates over property price thresholds"
    +).update_layout(
    +    yaxis_tickformat=",.0%",
    +    xaxis_tickprefix="£",
    +    legend=dict(
    +        orientation="h",
    +        yanchor="bottom",
    +        y=-0.3,
    +        xanchor="right",
    +        x=0.9,
    +        title=''
    +    )
    +).update_traces(
    +    line_shape="hv"
    +)
    +fig = format_fig(fig)
    +fig
    +
    +
    +
    +
    +
    +
    +
    +
    +

    The first home discount only applies for homes priced under £500,000. When simulating the marginal effect of SDLT, we find a cliff effect for first-time buyers.

    +
    +
    + + +Hide code cell source + +
    +
    from policyengine_uk import Simulation
    +
    +sim = Simulation(
    +    situation=dict(
    +        households=dict(
    +            household=dict(
    +                main_residential_property_purchased_is_first_home=True,
    +                members=["person"]
    +            )
    +        ),
    +        people=dict(
    +            person=dict(
    +                age=30,
    +            )
    +        ),
    +        axes=[[
    +            dict(
    +                name="main_residential_property_purchased",
    +                min=0,
    +                max=5_000_000,
    +                count=1_000,
    +            )
    +        ]]
    +    )
    +)
    +
    +import pandas as pd
    +
    +stamp_duty = sim.calculate("stamp_duty_land_tax")
    +home_price = sim.calculate("main_residential_property_purchased")
    +
    +marginal_rate = (stamp_duty[1:] - stamp_duty[:-1]) / (home_price[1:] - home_price[:-1])
    +home_price = home_price[:-1]
    +
    +df = pd.DataFrame({
    +    "Home price": home_price,
    +    "Marginal SDLT rate": marginal_rate,
    +    "SDLT": stamp_duty[:-1]
    +})
    +
    +import plotly.express as px
    +from plotly.subplots import make_subplots
    +
    +# left shows marginal rate, right shows total SDLT
    +
    +fig = make_subplots(rows=1, cols=2, subplot_titles=("Marginal SDLT rate", "Total SDLT"))
    +
    +fig.add_trace(
    +    px.line(
    +        df,
    +        x="Home price",
    +        y="Marginal SDLT rate",
    +        title = "Marginal SDLT rate"
    +    ).update_traces(
    +        line_shape="hv"
    +    ).data[0],
    +    row=1, col=1
    +).update_xaxes(
    +    row=1, col=1,
    +    title_text="Price",
    +    tickprefix="£"
    +).update_yaxes(
    +    row=1, col=1,
    +    tickformat=",.0%"
    +)
    +
    +fig.add_trace(
    +    px.line(
    +        df,
    +        x="Home price",
    +        y="SDLT",
    +        title = "Total SDLT"
    +    ).update_traces(
    +        line_shape="hv"
    +    ).data[0],
    +    row=1, col=2
    +).update_xaxes(
    +    row=1, col=2,
    +    title_text="Price",
    +    tickprefix="£"
    +).update_yaxes(
    +    row=1, col=2,
    +    tickprefix="£"
    +)
    +
    +fig = format_fig(fig)
    +fig
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    SDLT Statistics for 2019 and 2020#

    +

    HM Revenue & Customs has collected statistics for revenues generated from Stamp Duty Land Tax (SDLT). The SDLT statistics were collected separately for residential and non-residental properties, and for each property type, HMRC divided the data into two categories: corporate and households. The graph below shows SDLT statistics for 2019 and 2020:

    +
    +
    + + +Hide code cell source + +
    +
    from policyengine_uk.system import system
    +from policyengine_core.charts import format_fig
    +sdlt = system.parameters.gov.hmrc.stamp_duty.statistics
    +stats = {
    +    "Year": [],
    +    "Revenue (£m)": [],
    +    "Type": []
    +}
    +# residential corporate
    +for parameter in sdlt.residential.corporate.revenue.values_list:
    +    stats["Year"].append(parameter.instant_str[:4])
    +    stats["Revenue (£m)"].append(parameter.value/1000000)
    +    stats["Type"].append("Residental corporate")
    +# residential household
    +for parameter in sdlt.residential.household.revenue.values_list:
    +    stats["Year"].append(parameter.instant_str[:4])
    +    stats["Revenue (£m)"].append(parameter.value/1000000)
    +    stats["Type"].append("Residental household")
    +# non-residential corporate
    +for parameter in sdlt.non_residential.corporate.revenue.values_list:
    +    stats["Year"].append(parameter.instant_str[:4])
    +    stats["Revenue (£m)"].append(parameter.value/1000000)
    +    stats["Type"].append("Non-residental corporate")
    +# non-residential household
    +for parameter in sdlt.non_residential.household.revenue.values_list:
    +    stats["Year"].append(parameter.instant_str[:4])
    +    stats["Revenue (£m)"].append(parameter.value/1000000)
    +    stats["Type"].append("Non-residental household")
    +
    +df = pd.DataFrame(stats)
    +df.sort_values("Year", inplace=True)
    +fig = px.bar(
    +    df,
    +    x="Type",
    +    y="Revenue (£m)",
    +    color="Year",
    +    barmode="group",
    +    text_auto=True,
    +    title="SDLT statistics for 2019 and 2020",
    +    height=500
    +).update_traces(
    +    textposition="outside"
    +).update_layout(
    +    xaxis_title="Property type"
    +)
    +fig = format_fig(fig)
    +fig
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + +