approaches to calibrating a Heston model

I just started messing with chatGPT4. Here is the version it gives for the below as a python function.

import numpy as np
def heston_char_function(t, w, v_bar, v_0, s_0, r, lam, theta, sigma):
i_w = 1j * w

g = (lam - r * i_w) / (lam + r * i_w)

C = (((r * i_w - lam * theta) / sigma**2) * (1 - np.exp(-lam * t))
+ (theta / (2 * lam)) * ((lam - r * i_w) * t - 2 * np.log((1 - g * np.exp(-lam * t)) / (1 - g))))

D = ((1 / sigma**2) * (1 - np.exp(-lam * t)) * ((r * i_w - lam * theta) / lam - 1 / 2)
+ (1 / lam**2) * ((lam - r * i_w) * (1 - np.exp(-lam * t)) - 2 * (np.log(1 - g * np.exp(-lam * t)) - g * np.exp(-lam * t))))

phi_h = np.exp(C * v_bar + D * v_0 + i_w * np.log(s_0 * np.exp(r * t)))

return phi_h

# Example parameters
t = 1
w = 0.5
v_bar = 0.04
v_0 = 0.04
s_0 = 100
r = 0.03
lam = 2
theta = 0.04
sigma = 0.3

# Compute the Heston characteristic function
result = heston_char_function(t, w, v_bar, v_0, s_0, r, lam, theta, sigma)
print("Heston Characteristic Function: ", result)

No clue on any of that but it does give an answer in a jupyter notebook. I tried GPT4 on a more obscure javascript library that it hallucinated nonsense but got things right on the 3rd try with feedback from the output.

It is just lunacy that chatGPT3.5 is outdated already lol. GPT4 looks like it did much better on math related tests in the paper so hopefully programming takes a nice jump. Seems very hard to test this though.


The characteristic function of the Heston model, as modified according to Gatheral (2006), is given by:

φ_H(t; w) = exp[C(t,w)̅V + D(t,w)V₀ + iwln(S₀e^(rt))]

whereV ̅ is the long-term variance, V₀ is the initial variance, S₀ is the initial stock price, r is the risk-free rate, and w is the frequency variable. The coefficients C(t,w) and D(t,w) are given by:

C(t,w) = ((r i w - λ θ)/σ^2) * (1 - e^(-λ t)) + (θ/(2λ)) * ((λ - r i w) t - 2 ln((1 - g e^(-λ t))/(1 - g)))

and

D(t,w) = (1/σ^2) * (1 - e^(-λ t)) * ((r i w - λ θ)/λ - 1/2) + (1/λ^2) * {(λ - r i w)(1 - e^(-λ t)) - 2(ln(1 - g e^(-λ t)) - g e^(-λ t))}

where λ is the mean reversion rate, θ is the long-term variance, σ is the volatility of volatility, and g = (λ - r i w)/(λ + r i w) is the damping factor.
 
I just started messing with chatGPT4. Here is the version it gives for the below as a python function.

import numpy as np
def heston_char_function(t, w, v_bar, v_0, s_0, r, lam, theta, sigma):
i_w = 1j * w

g = (lam - r * i_w) / (lam + r * i_w)

C = (((r * i_w - lam * theta) / sigma**2) * (1 - np.exp(-lam * t))
+ (theta / (2 * lam)) * ((lam - r * i_w) * t - 2 * np.log((1 - g * np.exp(-lam * t)) / (1 - g))))

D = ((1 / sigma**2) * (1 - np.exp(-lam * t)) * ((r * i_w - lam * theta) / lam - 1 / 2)
+ (1 / lam**2) * ((lam - r * i_w) * (1 - np.exp(-lam * t)) - 2 * (np.log(1 - g * np.exp(-lam * t)) - g * np.exp(-lam * t))))

phi_h = np.exp(C * v_bar + D * v_0 + i_w * np.log(s_0 * np.exp(r * t)))

return phi_h

# Example parameters
t = 1
w = 0.5
v_bar = 0.04
v_0 = 0.04
s_0 = 100
r = 0.03
lam = 2
theta = 0.04
sigma = 0.3

# Compute the Heston characteristic function
result = heston_char_function(t, w, v_bar, v_0, s_0, r, lam, theta, sigma)
print("Heston Characteristic Function: ", result)

No clue on any of that but it does give an answer in a jupyter notebook. I tried GPT4 on a more obscure javascript library that it hallucinated nonsense but got things right on the 3rd try with feedback from the output.

It is just lunacy that chatGPT3.5 is outdated already lol. GPT4 looks like it did much better on math related tests in the paper so hopefully programming takes a nice jump. Seems very hard to test this though.

you can probably test it on some reference prices at
https://financepress.com/2019/02/15/heston-model-reference-prices/

gpt-4 seems much better, it knows that the W function is the Newton flow of the exponential function, I got it to verify this even though i couldnt find a paper explicitly stating it:

https://math.stackexchange.com/ques...n-the-newton-flow-of-the-exponential-function

and I updated my paper with the closed-form formula it helped me derive.. this is a publishable result but i probably wont mess with it
 

Attachments

there are multitudes of papers on how to compute the option prices once you have the characteristic function, they all involve the inverse Fourier transform in one form of the other because by definition the probability density is the inverse Fourier transform of the characteristic function and the characteristic function is the Fourier transform of the density
, see https://en.wikipedia.org/wiki/Carr–Madan_formula
 
there are multitudes of papers on how to compute the option prices once you have the characteristic function, they all involve the inverse Fourier transform in one form of the other because by definition the probability density is the inverse Fourier transform of the characteristic function and the characteristic function is the Fourier transform of the density
, see https://en.wikipedia.org/wiki/Carr–Madan_formula

Since you brought up the Fourier Transform:


What does option pricing have to do with converting a periodic function in the time domain into the frequency domain?
 
What does option pricing have to do with converting a periodic function in the time domain into the frequency domain?

the function doesn't have to be periodic. Was that a rhetorical question? videos is not my favorite format for learning and understanding, i always learn math by pontificating on theorems in black and white and doing thought experiments or code experiments or just working out the calculus.. its a bit of a rush each time you figure out how some shit works .. the guys explanation ends up being long winded and ironically he had to use fourier transforms implicitly in the operation of the DSP devices he used to produce the video and also similiar to hear the sound on the computer from the data stream
 
Section 3.1 of Perfect hedging in rough Heston models: Generalized rough Heston models as limit of nearly unstable Hawkes processes

In this section, the authors explore the connection between generalized rough Heston models and nearly unstable Hawkes processes. They explain that a microscopic price model based on two-dimensional Hawkes processes converges to a rough Heston log-price with constant mean-reversion after suitable rescaling. However, this method has limitations when it comes to computing prices and hedging portfolios using classical Fourier inversion methods.

To address this issue, the authors propose an alternative approach similar to the one presented in another study. They consider a sequence of one-dimensional Hawkes processes with intensity given by:

λTt = µT + ∫(from 0 to t) aT * ϕ(t - s) * dNsT

Here, µT and aT are positive constants with aT < 1, and ϕ is an integrable function. The authors show that, under certain conditions on the parameters, the intensity process λTt asymptotically behaves as the variance process of a rough Heston model with constant mean-reversion and an initial variance equal to zero.

To obtain a time-dependent mean-reversion level and a non-zero starting value in the limit, the authors draw inspiration from another study, which suggests that a time-dependent µT is a way to modify some parameters in the limit. This approach helps to better understand the relationship between generalized rough Heston models and nearly unstable Hawkes processes, and ultimately aids in managing financial risks more effectively.
 
package bonanzai.instruments.options.vix;



import java.util.ArrayList;

importjava.util.Collections;

import java.util.List;



import com.ib.client.ClientSocket;

import com.ib.client.TagValue;

importcom.ib.client.TickType;

import com.ib.contracts.Contract;

import com.ib.contracts.ContractDetails;



publicclassVIXHedging

{



// Define constants for contract multipliers

privatestaticfinalintVIX_CONTRACT_MULTIPLIER = 1000;

privatestaticfinalintSPX_CONTRACT_MULTIPLIER = 100;



// Define constant for gamma adjustment

privatestaticfinaldoubleGAMMA_ADJUSTMENT_CONSTANT = 1.5;



publicstaticvoidmain(String[] args)

{



// Set up connection to TWS

ClientSocketclient = new ClientSocket(null);

client.connect("localhost", 7496, 0);



// Define VIX and SPX contracts

ContractvixContract = new Contract();

vixContract.symbol("VIX");

vixContract.secType("FUT");

vixContract.exchange("CFE");

vixContract.currency("USD");

vixContract.expirationDate = "20230315";



ContractspxContract = new Contract();

spxContract.symbol("SPX");

spxContract.secType("OPT");

spxContract.exchange("SMART");

spxContract.currency("USD");

spxContract.expirationDate = "20230315";

spxContract.multiplier(Integer.toString(SPX_CONTRACT_MULTIPLIER));



// Define range of strikes to consider

doubleminStrike = 1000.0;
package bonanzai.instruments.options.vix;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.ib.client.ClientSocket;
import com.ib.client.TagValue;
import com.ib.client.TickType;
import com.ib.contracts.Contract;
import com.ib.contracts.ContractDetails;

public class VIXHedging
{

// Define constants for contract multipliers
private static final int VIX_CONTRACT_MULTIPLIER = 1000;
private static final int SPX_CONTRACT_MULTIPLIER = 100;

// Define constant for gamma adjustment
private static final double GAMMA_ADJUSTMENT_CONSTANT = 1.5;

public static void main(String[] args)
{

// Set up connection to TWS
ClientSocket client = new ClientSocket(null);
client.connect("localhost", 7496, 0);

// Define VIX and SPX contracts
Contract vixContract = new Contract();
vixContract.symbol("VIX");
vixContract.secType("FUT");
vixContract.exchange("CFE");
vixContract.currency("USD");
vixContract.expirationDate = "20230315";

Contract spxContract = new Contract();
spxContract.symbol("SPX");
spxContract.secType("OPT");
spxContract.exchange("SMART");
spxContract.currency("USD");
spxContract.expirationDate = "20230315";
spxContract.multiplier(Integer.toString(SPX_CONTRACT_MULTIPLIER));

// Define range of strikes to consider
double minStrike = 1000.0;
double maxStrike = 5000.0;
double strikeIncrement = 50.0;

// Define list to store SPX options and their deltas
List<OptionDelta> spxOptions = new ArrayList<>();

double T = 7; // 7 days til expation

// Loop over strikes to retrieve SPX option deltas
for (double strike = minStrike; strike <= maxStrike; strike += strikeIncrement)
{
spxContract.strikePrice = strike;
spxContract.right = "C"; // assume all calls for simplicity

// Retrieve contract details to get delta
List<ContractDetails> contractDetails = new ArrayList<>();
client.reqContractDetails(0, spxContract);
assert false : "todo, if i surive";
double delta = contractDetails.get(0).getGreeks().getDelta();

// Create OptionDelta object to store strike and delta
OptionDelta optionDelta = new OptionDelta(strike,
delta,
Double.NaN,
T);

// Add OptionDelta object to list
spxOptions.add(optionDelta);
}

// Retrieve VIX option deltas using VIX pricing model
List<Double> vixOptionDeltas = getVIXOptionDeltas();

// Calculate hedge ratios for each SPX option
List<HedgeRatio> hedgeRatios = new ArrayList<>();
for (int i = 0; i < spxOptions.size(); i++)
{
OptionDelta spxOption = spxOptions.get(i);
double deltaVIX = vixOptionDeltas.get(i);
double hedgeRatio = (deltaVIX * VIX_CONTRACT_MULTIPLIER)
/ (spxOption.getDelta() * SPX_CONTRACT_MULTIPLIER);
client.reqMarketData(0, spxContract, "SPX", false, new ArrayList<TagValue>());
assert false : "todo, dont blow up";
double gamma = Double.NaN;
double gammaAdjustment = 1 + 0.5 * spxOption.getGamma()
* Math.pow(spxOption.getStrike() / GAMMA_ADJUSTMENT_CONSTANT, 2)
/ (Math.pow(gamma, 2)
* spxOption.getTimeToMaturity());
hedgeRatios.add(new HedgeRatio(spxOption.getStrike(),
hedgeRatio * gammaAdjustment));
}
// Print out hedge ratios
for (HedgeRatio hedgeRatio : hedgeRatios)
{
System.out.println("Strike: " + hedgeRatio.getStrike() + ", Ratio: " + hedgeRatio.getRatio());
}

// Disconnect from TWS
client.disconnect();
}

private static List<Double> getVIXOptionDeltas()
{
// TODO: Implement function to calculate VIX option deltas using VIX pricing
// model
return null;
}

public static class OptionDelta
{
private final double strike;
private final double delta;
private final double gamma;
private final double timeToMaturity;

public OptionDelta(double strike, double delta, double gamma, double timeToMaturity)
{
this.strike = strike;
this.delta = delta;
this.gamma = gamma;
this.timeToMaturity = timeToMaturity;
}

public double getStrike()
{
return strike;
}

public double getDelta()
{
return delta;
}

public double getGamma()
{
return gamma;
}

public double getTimeToMaturity()
{
return timeToMaturity;
}
}

public static class HedgeRatio
{
private final double strike;
private final double ratio;

public HedgeRatio(double strike, double ratio)
{
this.strike = strike;
this.ratio = ratio;
}

public double getStrike()
{
return strike;
}

public double getRatio()
{
return ratio;
}
}
}
doublemaxStrike = 5000.0;

doublestrikeIncrement = 50.0;



// Define list to store SPX options and their deltas

List<OptionDelta> spxOptions = new ArrayList<>();



doubleT = 7; // 7 days tilexpation



// Loop over strikes to retrieve SPX option deltas

for (doublestrike = minStrike; strike <= maxStrike; strike += strikeIncrement)
package bonanzai.instruments.options.vix;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.ib.client.ClientSocket;
import com.ib.client.TagValue;
import com.ib.client.TickType;
import com.ib.contracts.Contract;
import com.ib.contracts.ContractDetails;

public class VIXHedging
{

// Define constants for contract multipliers
private static final int VIX_CONTRACT_MULTIPLIER = 1000;
private static final int SPX_CONTRACT_MULTIPLIER = 100;

// Define constant for gamma adjustment
private static final double GAMMA_ADJUSTMENT_CONSTANT = 1.5;

public static void main(String[] args)
{

// Set up connection to TWS
ClientSocket client = new ClientSocket(null);
client.connect("localhost", 7496, 0);

// Define VIX and SPX contracts
Contract vixContract = new Contract();
vixContract.symbol("VIX");
vixContract.secType("FUT");
vixContract.exchange("CFE");
vixContract.currency("USD");
vixContract.expirationDate = "20230315";

Contract spxContract = new Contract();
spxContract.symbol("SPX");
spxContract.secType("OPT");
spxContract.exchange("SMART");
spxContract.currency("USD");
spxContract.expirationDate = "20230315";
spxContract.multiplier(Integer.toString(SPX_CONTRACT_MULTIPLIER));

// Define range of strikes to consider
double minStrike = 1000.0;
double maxStrike = 5000.0;
double strikeIncrement = 50.0;

// Define list to store SPX options and their deltas
List<OptionDelta> spxOptions = new ArrayList<>();

double T = 7; // 7 days til expation

// Loop over strikes to retrieve SPX option deltas
for (double strike = minStrike; strike <= maxStrike; strike += strikeIncrement)
{
spxContract.strikePrice = strike;
spxContract.right = "C"; // assume all calls for simplicity

// Retrieve contract details to get delta
List<ContractDetails> contractDetails = new ArrayList<>();
client.reqContractDetails(0, spxContract);
assert false : "todo, if i surive";
double delta = contractDetails.get(0).getGreeks().getDelta();

// Create OptionDelta object to store strike and delta
OptionDelta optionDelta = new OptionDelta(strike,
delta,
Double.NaN,
T);

// Add OptionDelta object to list
spxOptions.add(optionDelta);
}

// Retrieve VIX option deltas using VIX pricing model
List<Double> vixOptionDeltas = getVIXOptionDeltas();

// Calculate hedge ratios for each SPX option
List<HedgeRatio> hedgeRatios = new ArrayList<>();
for (int i = 0; i < spxOptions.size(); i++)
{
OptionDelta spxOption = spxOptions.get(i);
double deltaVIX = vixOptionDeltas.get(i);
double hedgeRatio = (deltaVIX * VIX_CONTRACT_MULTIPLIER)
/ (spxOption.getDelta() * SPX_CONTRACT_MULTIPLIER);
client.reqMarketData(0, spxContract, "SPX", false, new ArrayList<TagValue>());
assert false : "todo, dont blow up";
double gamma = Double.NaN;
double gammaAdjustment = 1 + 0.5 * spxOption.getGamma()
* Math.pow(spxOption.getStrike() / GAMMA_ADJUSTMENT_CONSTANT, 2)
/ (Math.pow(gamma, 2)
* spxOption.getTimeToMaturity());
hedgeRatios.add(new HedgeRatio(spxOption.getStrike(),
hedgeRatio * gammaAdjustment));
}
// Print out hedge ratios
for (HedgeRatio hedgeRatio : hedgeRatios)
{
System.out.println("Strike: " + hedgeRatio.getStrike() + ", Ratio: " + hedgeRatio.getRatio());
}

// Disconnect from TWS
client.disconnect();
}

private static List<Double> getVIXOptionDeltas()
{
// TODO: Implement function to calculate VIX option deltas using VIX pricing
// model
return null;
}

public static class OptionDelta
{
private final double strike;
private final double delta;
private final double gamma;
private final double timeToMaturity;

public OptionDelta(double strike, double delta, double gamma, double timeToMaturity)
{
this.strike = strike;
this.delta = delta;
this.gamma = gamma;
this.timeToMaturity = timeToMaturity;
}

public double getStrike()
{
return strike;
}

public double getDelta()
{
return delta;
}

public double getGamma()
{
return gamma;
}

public double getTimeToMaturity()
{
return timeToMaturity;
}
}

public static class HedgeRatio
{
private final double strike;
private final double ratio;

public HedgeRatio(double strike, double ratio)
{
this.strike = strike;
this.ratio = ratio;
}

public double getStrike()
{
return strike;
}

public double getRatio()
{
return ratio;
}
}
}
{

spxContract.strikePrice = strike;

spxContract.right = "C"; // assume all calls for simplicity



// Retrieve contract details to get delta

List<ContractDetails> contractDetails = new ArrayList<>();

client.reqContractDetails(0, spxContract);

assertfalse : "todo, if i surive";

doubledelta = contractDetails.get(0).getGreeks().getDelta();



// Create OptionDelta object to store strike and delta

OptionDeltaoptionDelta = new OptionDelta(strike,

delta,

Double.NaN,

T);



// Add OptionDelta object to list

spxOptions.add(optionDelta);

}



// Retrieve VIX option deltas using VIX pricing model

List<Double> vixOptionDeltas = getVIXOptionDeltas();



// Calculate hedge ratios for each SPX option

List<HedgeRatio> hedgeRatios = new ArrayList<>();

for (inti = 0; i < spxOptions.size(); i++)

{

OptionDeltaspxOption = spxOptions.get(i);

doubledeltaVIX = vixOptionDeltas.get(i);

doublehedgeRatio = (deltaVIX * VIX_CONTRACT_MULTIPLIER)

/ (spxOption.getDelta() * SPX_CONTRACT_MULTIPLIER);

client.reqMarketData(0, spxContract, "SPX", false, new ArrayList<TagValue>());

assertfalse : "todo, dont blow up";

doublegamma = Double.NaN;

doublegammaAdjustment = 1 + 0.5 * spxOption.getGamma()

* Math.pow(spxOption.getStrike() / GAMMA_ADJUSTMENT_CONSTANT, 2)

/ (Math.pow(gamma, 2)

* spxOption.getTimeToMaturity());

hedgeRatios.add(new HedgeRatio(spxOption.getStrike(),

hedgeRatio * gammaAdjustment));

}

// Print out hedge ratios

for (HedgeRatiohedgeRatio : hedgeRatios)

{

System.out.println("Strike: " + hedgeRatio.getStrike() + ", Ratio: " + hedgeRatio.getRatio());

}



// Disconnect from TWS

client.disconnect();

}



privatestaticList<Double> getVIXOptionDeltas()

{

// TODO: Implement function to calculate VIX option deltas using VIX pricing

// model

returnnull;

}



publicstaticclassOptionDelta

{

privatefinaldoublestrike;

privatefinaldoubledelta;

privatefinaldoublegamma;

privatefinaldoubletimeToMaturity;



publicOptionDelta(doublestrike, doubledelta, doublegamma, doubletimeToMaturity)

{

this.strike = strike;

this.delta = delta;

this.gamma = gamma;

this.timeToMaturity = timeToMaturity;

}



publicdoublegetStrike()

{

returnstrike;

}



publicdoublegetDelta()

{

returndelta;

}



publicdoublegetGamma()

{

returngamma;

}



publicdoublegetTimeToMaturity()

{

returntimeToMaturity;

}

}



publicstaticclassHedgeRatio

{

privatefinaldoublestrike;

privatefinaldoubleratio;



publicHedgeRatio(doublestrike, doubleratio)

{

this.strike = strike;

this.ratio = ratio;

}



publicdoublegetStrike()

{

returnstrike;

}



publicdoublegetRatio()

{

returnratio;

}

}

}
 
That is really cool. It hadn't really occurred to me that was outputting a price hah.
I would actually rather not get it in my head that I could actually trade options with this. This is really a perfect thread of why I don't trade options. It is crazy to me that people trade options without knowing all this.

chatGPT4 seems like it can get to a working answer much better even if the first pass is wrong. 3.5 felt like it tended to go off the rails if the first answer was wrong. I read in the released paper though it still only gets about 75% on leet code easy questions, 25% on medium and helpless on leet code hard questions.

you can probably test it on some reference prices at
https://financepress.com/2019/02/15/heston-model-reference-prices/

gpt-4 seems much better, it knows that the W function is the Newton flow of the exponential function, I got it to verify this even though i couldnt find a paper explicitly stating it:

https://math.stackexchange.com/ques...n-the-newton-flow-of-the-exponential-function

and I updated my paper with the closed-form formula it helped me derive.. this is a publishable result but i probably wont mess with it
 
Back
Top