#!/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.utilities.enums import StatusType, ExerciseType
from pricelib.common.date_handler import CN_CALENDAR, AnnualDays
from .autocallable_base import AutocallableBase


class EuropeanSnowball(AutocallableBase):
    """欧式雪球
       保本(strike_lower=s0)、绝对票息(trigger=True)的欧式雪球，可以作为DCN、凤凰的派息拆分的子结构(敲入价=派息线)，用PDE/QUAD定价"""

    def __init__(self, s0, barrier_out, barrier_in, coupon_out, trigger=False, strike_lower=None, margin_lvl=1,
                 coupon_div=None, lock_term=3, parti_in=1, obs_dates=None, pay_dates=None,
                 engine=None, status=StatusType.NoTouch, maturity=None, start_date=None, end_date=None,
                 trade_calendar=CN_CALENDAR, annual_days=AnnualDays.N365, t_step_per_year=243,
                 stoch_process=None, s=None, r=None, q=None, vol=None, engine_method=None):
        """继承自动赎回基类AutocallableBase的参数，详见AutocallableBase的__init__方法
        Args: 以下为区别于经典雪球的参数
            trigger: bool，是否为触发器，即敲出票息是否年化，True为绝对值，False为百分比
            strike_lower: float，敲入发生后(熊市价差)的低行权价，即保底边界，越高提供的保护越多，值为0时不保底
            margin_lvl: float，保证金比例，默认为1，即无杠杆。
                                不追保雪球一般将保证金比例设为最大可能亏损，例如75敲入，保证金比例25%，即为4倍杠杆
            parti_in: float，敲入后参与率，限损
        """
        super().__init__(in_obs_type=ExerciseType.European,  # 属性：到期观察敲入(欧式)
                         s0=s0, maturity=maturity, start_date=start_date, end_date=end_date, lock_term=lock_term,
                         trade_calendar=trade_calendar, obs_dates=obs_dates, pay_dates=pay_dates, status=status,
                         annual_days=annual_days, parti_in=parti_in, margin_lvl=margin_lvl,
                         t_step_per_year=t_step_per_year, engine=engine, stoch_process=stoch_process,
                         s=s, r=r, q=q, vol=vol, trigger=trigger, engine_method=engine_method)

        len_obs_dates = len(self.obs_dates.date_schedule) if isinstance(self.obs_dates.date_schedule,
                                                                        (list, np.ndarray)) else 1
        if isinstance(barrier_out, (int, float, np.int32, np.float64)):
            self.barrier_out = np.ones(len_obs_dates) * barrier_out
        elif isinstance(barrier_out, (list, np.ndarray)):
            self.barrier_out = barrier_out
        else:
            raise ValueError(f'敲出障碍价类型为{type(barrier_out)}，仅支持int/float/list/np.ndarray，请检查')
        if isinstance(barrier_in, (int, float, np.int32, np.float64)):
            self.barrier_in = barrier_in
        else:
            raise ValueError(f'敲入障碍价类型为{type(barrier_in)}，仅支持int/float，请检查')
        if isinstance(coupon_out, (int, float, np.int32, np.float64)):
            self.coupon_out = np.ones(len_obs_dates) * coupon_out
        elif isinstance(coupon_out, (list, np.ndarray)):
            self.coupon_out = coupon_out
        else:
            raise ValueError(f'敲出票息类型为{type(coupon_out)}，仅支持int/float/list/np.ndarray，请检查')
        self.coupon_div = coupon_div if coupon_div is not None else self.coupon_out[-1]  # 红利票息
        self.parti_out = 0  # 没有敲出上涨参与率
        self.strike_upper = s0  # 敲入后高行权价，默认s0
        self.strike_lower = strike_lower if strike_lower is not None else 0  # 敲入后低行权价（保底价）
        # 欧式雪球继承了父类的反算票息方法，但是只支持红利票息=敲出票息=常数的情形

    def __repr__(self):
        """返回期权的描述"""
        return "欧式雪球"
