function [PARAMS, ERROR_PRICES, ERROR_VOL] = calibrate_Heston(S0, r)

% Calibrate Heston Model using Carr-Madan algorithm for pricing

% epsilon = PARAMS(1) - vol-of-vol
% k       = PARAMS(2) - mean reversion speed
% rho     = PARAMS(3) - correlation
% theta   = PARAMS(4) - mean reversion value
% V0      = PARAMS(5) - starting value of the variance


%% Input

% add paths
addpath('/Users/alessandromoneta/Desktop/Politecnico/CORSI/Computational Finance/Pricing Library/Models')

% options' data
Prices_table = readtable('Example_Data.xlsx', 'Range', 'E1:G23');

mkt_prices = Prices_table.Price;
strikes    = Prices_table.Strike;
maturities = Prices_table.TimeToMaturity;


% plot ──────────────────────────────────────

red   = [204/255, 0/255, 0/255];
blue  = [21/255, 110/255, 235/255];
green = [40/255, 161/255, 0/255];

figure
set(gcf, 'Color', 'w', 'Name', 'Market Prices', 'NumberTitle', 'off')
hold on
grid on

plot(strikes, mkt_prices, ...
     'Color', red, ...
     'Linewidth', 1.2, ...
     'Marker', '.', 'Markersize', 12, 'MarkerEdgeColor', red)

xlabel('Strike : K')
ylabel('Prices')
title('Options'' prices')

%{
% put-call parity
mkt_prices(1:13) = mkt_prices(1:13) + S0 - strikes(1:13) .* exp( - r * maturities(1:13) );


% plot ──────────────────────────────────────

figure
set(gcf, 'Color', 'w', 'Name', 'Market Prices', 'NumberTitle', 'off')
hold on
grid on

plot(strikes(1:13), mkt_prices(1:13), ...
     'Color', blue, ...
     'Linewidth', 1.2, ...
     'Marker', '.', 'Markersize', 12, 'MarkerEdgeColor', blue)
plot(strikes(14:end), mkt_prices(14:end), ...
     'Color', green, ...
     'Linewidth', 1.2, ...
     'Marker', '.', 'Markersize', 12, 'MarkerEdgeColor', green);

xlabel('Strike : K')
ylabel('Prices')
title('Options'' prices')
%}


%% Calibration

% minimization parameters
LB = [eps; eps;    -1;  eps; eps];
x0 = [0.2; 0.5; -0.25; 0.15; 0.2]; % epsilon, k, rho, theta, V0
UB = [  1;   3;     1;    1;   1];

% specify options for lsqnonlin
options = optimset('Display', 'off', ...
                   'PlotFcns', 'my_optimplotfval');
%{
options = optimset('Display', 'off', ...
                   'PlotFcns', 'my_optimplotfval'), ..
                   'TolFun', 1e-6, ...
                   'MaxIter', 1e6, ...
                   'MaxFunEvals', 1e6);
%}

% ┌----- PRICES -----------------------------------------------------------------------------------------------------------┐
%{
% parameters calibration
PARAMS = ...
    lsqnonlin(@(p) abs(arrayfun(@(i) callPrice_FFT_Heston(S0, maturities(i), r, strikes(i), p) ...
                                      - mkt_prices(i), [1:length(strikes)]')), x0, LB, UB, options);
%}
% └------------------------------------------------------------------------------------------------------------------------┘

% ┌----- VOLATILITY -------------------------------------------------------------------------------------------------------┐

% parameters calibration
PARAMS = ...
    lsqnonlin(@(p) abs(arrayfun(@(i) blsimpv(S0, strikes(i), r, maturities(i), callPrice_FFT_Heston(S0, maturities(i), r, strikes(i), p)) ...
                                      - blsimpv(S0, strikes(i), r, maturities(i), mkt_prices(i)), [1:length(strikes)]')), x0, LB, UB, options);

% └------------------------------------------------------------------------------------------------------------------------┘

% compute the calibrated model prices
prices = arrayfun(@(i) callPrice_FFT_Heston(S0, maturities(i), r, strikes(i), PARAMS), [1:length(strikes)]');

% compute implied volatilities
mkt_vol = blsimpv(S0, strikes, r, maturities, mkt_prices);
model_vol = blsimpv(S0, strikes, r, maturities, prices);

% calibration errors
ERROR_PRICES = sum(abs(prices - mkt_prices));
ERROR_VOL = sum(abs(model_vol - mkt_vol));


% ┌----- PLOTS ------------------------------------------------------------------------------------------------------------┐

% plot market prices VS model prices
figure
set(gcf, 'Color', 'w', 'Name', 'Market Prices VS Model Prices', 'NumberTitle', 'off')

plot(strikes, mkt_prices, 'sm', 'Markersize', 7, 'Linewidth', 1);
hold on
plot(strikes, prices, '+b', 'Markersize', 7, 'Linewidth', 1);

% display absolute errors on graph
text(strikes - 3, min(mkt_prices, prices) - 1.5, num2str(abs(mkt_prices - prices)), 'FontSize', 6); % error for each point
t = text(min(strikes), mkt_prices(end), ['\bf Total Error : ', num2str(ERROR_PRICES)]);             % total error
t.FontSize = 13;

legend('Market prices', 'Calibrated Bates prices')
xlabel('Strike : K')
ylabel('Prices')
title('Market Prices VS Model Prices')
grid on


% plot market volatility VS model volatility
figure
set(gcf, 'Color', 'w', 'Name', 'Market Implied Volatility VS Model Implied Volatility', 'NumberTitle', 'off')

plot(strikes, mkt_vol, 'sm', 'Markersize', 7, 'Linewidth', 1);
hold on
plot(strikes, model_vol, '+b', 'Markersize', 7, 'Linewidth', 1);

% display absolute errors on graph
text(strikes - 3, min(mkt_vol, model_vol) - 0.005, num2str(abs(mkt_vol - model_vol)), 'FontSize', 6); % error for each point
t = text(min(strikes), mkt_vol(end), ['\bf Total Error : ', num2str(ERROR_VOL)]);                     % total error
t.FontSize = 13;

legend('Market Implied Volatility', 'Bates Implied Volatility')
xlabel('Strike : K')
ylabel('Implied Volatility')
title('Market Implied Volatility VS Model Implied Volatility')
grid on

% └------------------------------------------------------------------------------------------------------------------------┘


end

