#!/user/bin/env python
# -*- coding: utf-8 -*-
"""
Copyright (C) 2024 Galaxy Technologies
Licensed under the Apache License, Version 2.0
"""
import numpy as np
from pricelib.common.pricing_engine_base import QuadEngine
from pricelib.common.date_handler import global_evaluation_date


class QuadPortfolioEngine(QuadEngine):
    """香草组合积分法定价引擎"""

    def calc_present_value(self, prod, t=None, spot=None, calc_greeks=False, s_step=0.001):
        """计算现值
        Args:
            prod: Product产品对象
            t: datetime.date，估值日; 如果是None，则使用全局估值日globalEvaluationDate
            spot: float，估值日标的价格，如果是None，则使用随机过程的当前价格
            calc_greeks: bool，是否计算delta和gamma，默认为False，即只计算理论估值
            s_step: float，价格网格步长（百分比）
        Returns:
            若只计算pv，返回float，现值 pv
            若计算pv、delta和gamma，返回np.ndarray [down_price, pv, up_price] ，其中 down_price 和 up_price 是 s±Δs 的理论估值
        """
        calculate_date = global_evaluation_date() if t is None else t
        tau = (prod.end_date - calculate_date).days / prod.annual_days.value
        if spot is None:
            spot = self.process.spot()

        if tau == 0:  # 如果估值日是到期日
            if calc_greeks:  # 计算理论估值、delta和gamma
                value = self._init_terminal_condition(prod, np.array([1 - s_step, 1, 1 + s_step]) * spot)
            else:  # 只计算理论估值, 直接计算终止条件
                value = self._init_terminal_condition(prod, np.array([spot]))[0]
            return value

        r = self.process.interest(tau)
        q = self.process.div(tau)
        vol = self.process.vol(tau, spot)
        self._check_method_params()

        # 设定期末价值
        self.init_grid(spot, vol, tau)
        v_vec = np.zeros(self.ln_s_vec.shape)
        for vanilla, position in prod.vanilla_list:
            v_vec += np.maximum(vanilla.callput.value * (np.exp(self.ln_s_vec) - vanilla.strike), 0) * position
        # 设置积分法engine参数
        self.set_quad_params(r=r, q=q, vol=vol)
        # 回溯计算
        if calc_greeks:  # 计算理论估值、delta和gamma
            up_price = self.fft_step_backward(np.array([np.log(spot * (1 + s_step))]), self.ln_s_vec, v_vec, tau)[0]
            pv = self.fft_step_backward(np.array([np.log(spot)]), self.ln_s_vec, v_vec, tau)[0]
            down_price = self.fft_step_backward(np.array([np.log(spot * (1 - s_step))]), self.ln_s_vec, v_vec, tau)[0]
            return np.array([down_price, pv, up_price])
        else:  # 只计算理论估值
            value = self.fft_step_backward(np.log(np.array([spot])), self.ln_s_vec, v_vec, tau)[0]
        return value

    @staticmethod
    def _init_terminal_condition(prod, s_vec):
        """初始化终止时的期权价值，在障碍价格内侧，默认设定未敲入，未敲出
        Args:
            prod: Product产品对象
            s_vec: np.ndarray, 网格的价格格点
        Returns:
            payoff: np.ndarray, 期末价值向量
        """
        value = np.zeros(len(s_vec))
        for vanilla, position in prod.vanilla_list:
            value += np.maximum(vanilla.callput.value * (s_vec - vanilla.strike), 0) * position
        return value
