#!/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.date_handler import global_evaluation_date
from pricelib.common.pricing_engine_base import McEngine


class MCRangeAccuralEngine(McEngine):
    """区间累计 Monte Carlo 模拟定价引擎"""

    def calc_present_value(self, prod, t=None, spot=None):
        """计算现值
        Args:
            prod: Product产品对象
            t: datetime.date，估值日; 如果是None，则使用全局估值日globalEvaluationDate
            spot: float，估值日标的价格，如果是None，则使用随机过程的当前价格
        Returns: float，现值
                 如果输入spot = np.ndarray一维价格向量，t=终止日，则返回盈亏测算向量，即每个价格对应的现值，而不是平均值
        """
        calculate_date = global_evaluation_date() if t is None else t
        _maturity = (prod.end_date - calculate_date).days / prod.annual_days.value
        _maturity_business_days = prod.trade_calendar.business_days_between(calculate_date, prod.end_date)

        if spot is None:
            spot = self.process.spot()
        # 当估值日就是到期日时，直接用spot计算payoff，无需随机数矩阵
        if calculate_date == prod.end_date:
            if isinstance(spot, (list, np.ndarray)):  # spot是一个np.ndarray一维价格向量，返回盈亏测算向量，即每个价格对应的现值，而不是平均值
                paths = np.array([spot, ])
            else:  # spot是一个数值，计算期末payoff
                paths = np.array([[spot, ]])
        else:
            if spot != self.process.spot():
                self.reset_paths_flag()  # 重置路径标志位，重新生成路径
            paths = self.path_generator(n_step=_maturity_business_days, spot=spot,
                                        t_step_per_year=prod.t_step_per_year).copy()
        _n_path = paths.shape[1]

        # 敲出与行权日期(paths[1:]计算时排除已确定的估值日价格，估值之前，该日的状态应该已经随着价格的变化，更新到了status中)
        n_coupon = np.sum((paths[1:] < prod.strike_upper) & (paths[1:] > prod.strike_lower), axis=0) + prod.status
        # 支付 = 名义本金 * 年化收益 * 计息天数/365 * 位于区间内的观察日天数/观察日总天数
        payoff = (prod.s0 * prod.payment * _maturity * (n_coupon / _maturity_business_days)
                  * self.process.interest.disc_factor(_maturity))

        if (calculate_date == prod.end_date) and isinstance(spot, np.ndarray):
            return payoff  # 终止日, spot是价格向量, 则返回盈亏测算向量，即每个价格对应的现值，而不是平均值
        else:
            return np.mean(payoff)
