Closed masanorihirano closed 3 years ago
The fundamental problem was different.
At first, I missed the detailed implementation of this discrete approximation, but, now I have read the reference and confirmed it was implemented geometrically.
The actual problem was the price has been too small to be approximated to 0.0 as a result of the computation.
In the code I wrote above, log(-104.7081) was calculated as 0.0 in the line of the following link. https://github.com/pfnet-research/pfhedge/blob/main/pfhedge/stochastic/heston.py#L118
Now, I am being a little confused to think about how to handle this error. Practically, I should change the initial price (this is not fundamental fix), fix pfhedge, or other...?
The cause of the too-small spot seems to be an extremely large variance generated by generate_cir
.
I'll inspect the cause of the large variance.
https://github.com/pfnet-research/pfhedge/blob/9434de4c75d44c2c646e09f24a029b0f1687f3cb/pfhedge/stochastic/heston.py#L85
These plots are spot and variance for which log(spot)
dooms to nan.
As shown in the plot, v1 = variance[:, i_step + 1]
is extremely large for i_step=8
and sok2 * v1
is dominant in the following expression of log_spot[:, i_spot=9]
. Since k2 = -0.35 < 0
is negative, log_spot[:, i_spot=9]
becomes largely negative and so spot
goes extremely small.
Just an FYI thing, with torch.set_default_dtype(torch.float64)
the code ran without RuntimeError. (it does not fix the issue essentially though...)
update: retracted
Even if we use torch.set_default_dtype(torch.float64)
, the same issue happen:
import torch
from pfhedge.instruments import BrownianStock
from pfhedge.instruments import HestonStock
from pfhedge.instruments import EuropeanOption
from pfhedge.nn import Hedger, MultiLayerPerceptron
torch.autograd.set_detect_anomaly(True)
torch.set_default_dtype(torch.float64)
def main():
torch.manual_seed(0)
# stock = BrownianStock(cost=1e-4, volatility=0.2)
stock = HestonStock()
derivative = EuropeanOption(stock)
model = MultiLayerPerceptron()
hedger = Hedger(
model, inputs=["log_moneyness", "expiry_time", "prev_hedge"])
hedger.fit(derivative, n_epochs=200)
price = hedger.price(derivative)
print(price)
if __name__ == '__main__':
main()
The fundamental issue occurred here: https://github.com/pfnet-research/pfhedge/blob/9434de4c75d44c2c646e09f24a029b0f1687f3cb/pfhedge/stochastic/cir.py#L97-L102
According to the reference, these seem correct.
However, when the u
is too near to 1, 1-u
becomes almost 0.
Then, next_1
was sed the logarithm of it, thus this process causes a little bit bigger numbers.
However, because u
is drawn from a uniform distribution, I think this issue cannot be avoided.
There's a bug here.
Here the first tensor is of size (n_paths,)
while the second is (,)
.
I wrongly assumed it's equivalent to the following expression,
s2 = (
v * (tensor_sigma ** 2) * exp * (1 - exp) / tensor_kappa
+ tensor_theta * (tensor_sigma ** 2) * ((1 - exp) ** 2) / (2 * tensor_kappa)
)
But the results are different. The first tensor is unintendedly summed over the paths.
import torch
a = torch.full((5,), 3)
b = torch.tensor(2)
result = sum(a, b)
expect = a + b
print(result)
# tensor(17)
print(expect)
# tensor([5, 5, 5, 5, 5])
Consequently s^2
is O(n_paths)
times greater than the correct value and eventually pinv ~ 1 / beta
is O(n_paths)
times greater.
I don't think ((1 - p) / (1 - u + EPSILON)).log()
is the cause because it has EPSILON
keeping log at most log((1 - 0.95) / EPSILON) ~ 15
, which is not that seriously large (cancelled by small 1/beta
anyway).
If I substitute sum(...)
with the correct expression ... + ...
, the issue is resolved (See "fix" below).
May I patch this to pfhedge (it'll be after tomorrow though), or you want to?
update: typo corrected
I also found the same issue as #184. Thus, I'll make a PR including the fix.
I mean, the same issue as #184 in CIR.
Thank you so much, @masanorihirano !!
Resolved by #186
@masanorihirano Resolved with the latest release, 0.7.5. Again, thank you so much for the really quick and pertinent fix!!
The current implementation of the Heston process can make price time series including non-positive prices because of the discrete approximation.
https://github.com/pfnet-research/pfhedge/blob/main/pfhedge/stochastic/heston.py#L103
For example, we can face this issue with this code:
In this code,
-inf
nan
because of the invalid inputCurrent implementation use the discreate approximation of the following equations: %20+%20\sigma%20\sqrt{V(t)}%20dW_2(t))
However, according to the following calculation, the first equation can be in a different form.
This transformation enables us to implement the Heston process geometrically and avoid the current issue.