# USD Swap curve prices do not line up with inputs

As mentioned in the title, i'm having trouble with pricing USD swaps in quantlib.

I wanted to take some inputs (Prices of swaps in the market) and be able to construct a yield term structure that allows me to price say a 1y1y swap. However, it seems that that when constructing a yield curve and then pricing a swap, my prices do not line with with the exact instruments im using and im wondering if how i can reconcile the two such that the model outputs == inputs for the tenors below

import QuantLib as ql
today = ql.Date().todaysDate()
calendar = ql.UnitedStates()
helpers = ql.RateHelperVector()

index = ql.IborIndex('MyIndex',
ql.Period('3m'),
2,
ql.USDCurrency(),
ql.UnitedStates(),
ql.ModifiedFollowing,
True,
ql.Thirty360())

helpers.append( ql.DepositRateHelper(0.01, index) )

swaps = [
('1Y', 0.015),
('2Y', 0.02),
('3Y', 0.025)
]
for tenor, rate in swaps:
swapIndex = ql.UsdLiborSwapIsdaFixAm(ql.Period(tenor))
helpers.append( ql.SwapRateHelper(rate, swapIndex) )

curve = ql.PiecewiseSplineCubicDiscount(spot, helpers, ql.Thirty360())
yts = ql.YieldTermStructureHandle(curve)
engine = ql.DiscountingSwapEngine(yts)

index = ql.IborIndex('MyIndex',
ql.Period('3m'),
2,
ql.USDCurrency(),
ql.UnitedStates(),
ql.ModifiedFollowing,
True,
ql.Thirty360(),
yts)

print("maturity, market, model")
for tenor, rate in swaps:
swap = ql.MakeVanillaSwap(ql.Period(tenor), index, 0.01, ql.Period('0D'), pricingEngine=engine)
print(f"{tenor}, {rate:.6f}, {swap.fairRate():.6f}")

maturity market model
1Y 0.015000 0.014831
2Y 0.020000 0.019809
3Y 0.025000 0.024755

I updated your example to show what needs to change so that the fair rates on the manually created swaps match the input quotes. The main thing is that the manually created swaps need to match the rate helpers that are created exactly. A few things in your setup meant that this was not happening:

1. The helpers created with the ql.SwapRateHelper(rate, swapIndex) call where the swapIndex is ql.UsdLiborSwapIsdaFixAm use ql.USDLibor(ql.Period('3m')) as the floating leg index. You can see this here in the underlying C++ code. This does not match the index that you create manually for your swap e.g. different day counter and fixing calendar. So, I just used ql.USDLibor directly.
2. I added a few additional parameters to the call to ql.MakeVanillaSwap so that it matches the call that occurs in the swap rate helper constructor under the hood in the C++ code i.e. here and then here.
import QuantLib as Ql
ql = Ql

# Dates
today = ql.Date().todaysDate()
ql.Settings.instance().evaluationDate = today

# Helper container
helpers = ql.RateHelperVector()

# Deposit helper
index = ql.USDLibor(ql.Period('3m'))
helpers.append(ql.DepositRateHelper(0.01, index))

# Swap helpers
swaps = [
('1Y', 0.015),
('2Y', 0.02),
('3Y', 0.025)
]
for tenor, rate in swaps:
swapIndex = ql.UsdLiborSwapIsdaFixAm(ql.Period(tenor))
helpers.append(ql.SwapRateHelper(rate, swapIndex))

# Yield term structure
curve = ql.PiecewiseSplineCubicDiscount(today, helpers, ql.Thirty360())
yts = ql.YieldTermStructureHandle(curve)

# Link index to discount curve
index = index.clone(yts)

# Create engine with yield term structure
engine = ql.DiscountingSwapEngine(yts)

# Check the swaps reprice
print("maturity |  market  |   model")
for tenor, rate in swaps:
swap = ql.MakeVanillaSwap(ql.Period(tenor),
index, 0.01,
ql.Period('0D'),
fixedLegTenor=ql.Period('6M'),
fixedLegDayCount=ql.Thirty360(),
fixedLegCalendar=ql.UnitedStates(),
floatingLegCalendar=ql.UnitedStates(),
pricingEngine=engine)

print(f"   {tenor}    | {rate:.6f} | {swap.fairRate():.6f}")


The output from this is now:

maturity |  market  |   model
1Y    | 0.015000 | 0.015000
2Y    | 0.020000 | 0.020000
3Y    | 0.025000 | 0.025000