Exemple #1
0
    def check_autoflow_limits(self):
        ccy = self.get_ccy()

        key_max = f'TradingParameters/CurrencyKeys/{ccy}/FXNDF/MaximumAmountAutoFlow'
        max_autoflow = get_or_raise(
            key_max, 'Erro ao consultar limite superior para notional')

        if max_autoflow is not None and self.amount > max_autoflow:
            msg = 'Notional acima do parâmetro e-sales'
            raise RejectException(msg)

        key_min = f'TradingParameters/CurrencyKeys/{ccy}/FXNDF/MinimumAmountAutoFlow'
        min_autoflow = get_or_raise(
            key_min, 'Erro ao consultar limite inferior para notional')
        if min_autoflow is not None and self.amount < min_autoflow:
            msg = 'Notional abaixo do parâmetro e-sales'
            raise RejectException(msg)

        key_max = f'FXSupplierControl/{ccy}/MaxQuantity'
        max_autoflow_supplier = get_or_raise(
            key_max,
            'Erro ao consultar limite superior para notinal (fx supplier)')

        if max_autoflow_supplier is not None and self.amount > max_autoflow_supplier:
            msg = 'Notional acima do parâmetro fx-supplier'
            raise RejectException(msg)
Exemple #2
0
    def check_dates_and_get_day_count(self):
        sett_date_brl = self.get_settlement_date(self.get_brl())
        sett_date_ccy = self.get_settlement_date(self.get_ccy())
        sett_grid_ccy = self.get_settlement_grid(self.get_ccy())

        if sett_date_brl not in sett_grid_ccy:
            dates_str = ' '.join(map(str, sett_grid_ccy))
            raise RejectException(f'Data de settlement inválida (datas válidas: {dates_str})')

        if sett_date_ccy not in sett_grid_ccy:
            dates_str = ' '.join(map(str, sett_grid_ccy))
            raise RejectException(f'Data de settlement inválida (datas válidas: {dates_str})')

        du_brl = sett_grid_ccy.index(sett_date_brl)
        du_ccy = sett_grid_ccy.index(sett_date_ccy)

        market_type = get_or_raise(f"LegalEntities/{self.cnpj}/FXMarketType", "Erro ao consultar tipo de mercado")

        if market_type is None:
            raise RejectException(f'Erro ao consultar tipo de mercado')
        elif str(market_type) == '2':  # cliente interbancário
            if du_brl != du_ccy:
                raise RejectException(f'Settlement descasado para operação no mercado secundário')

        return du_brl, du_ccy
Exemple #3
0
    def price(self):
        '''implementação do modelo de precificação de um instrumento SPOT'''
        brl = self.get_brl()
        ccy = self.get_ccy()

        today = self.get_today()
        sett_date_ccy = self.get_settlement_date(self.ccy)
        du_brl = self.du_brl
        du_ccy = self.du_ccy

        self.check_autoflow_limits(du_ccy)
        self.check_cutoff(du_ccy)

        dc = np.int64((sett_date_ccy - today) / np.timedelta64(1, 'D'))

        r_brl = self.get_settlement_rate(brl)
        r_ccy = self.get_settlement_rate(ccy)

        usd_future = self.get_usdbrl_future(self.get_bid_or_ask())
        casado = self.get_casado()
        usdbrl_quote = usd_future - casado

        s_cost = self.get_scost(ccy, self.get_bid_or_ask())
        s_cost_ccybrl = s_cost * ((1 + r_ccy) ** ((2 - dc) / 360.0)) / ((1 + r_brl) ** ((2 - du_brl) / 252.0))
        precision = self.get_currency_precision(ccy)
        s_cost_ccybrl = round(s_cost_ccybrl, precision)

        cnpj = self.get_cnpj()
        spread = self.get_spread(cnpj, ccy, self.get_side(), du_ccy)

        if self.get_side() == 'Buy':
            s_client = s_cost_ccybrl - float(spread)
        else:
            s_client = s_cost_ccybrl + float(spread)

        if s_client <= 0.0:
            raise RejectException('Notional abaixo do Parâmetro e-sales')

        if self.result:
            if not self.result.has_changed(s_client, precision):
                raise ResultHasntChanged()

        s_client = round(s_client, precision)
        self.result = PriceSpotBase.Result()
        self.result.quote = s_client
        self.result.spread = float(spread)
        self.result.settlement_ccy = du_ccy
        self.result.settlement_brl = du_brl
        self.result.amount_brl = round(self.get_amount() * s_client, 2)
        self.result.amount_usd = round(self.get_amount() * usdbrl_quote)
        self.result.revenue_brl = round(self.get_amount() * float(spread), 2)
        self.result.settlement_date_ccy = str(sett_date_ccy)
        self.result.settlement_date_brl = str(self.get_settlement_date(self.brl))
        self.result.usdbrl_quote = usdbrl_quote
        self.result.s_cost = s_cost
        self.result.precision = precision
        return self.result
Exemple #4
0
    def check_cutoff(self, settlement):
        market_type = get_or_raise(f"LegalEntities/{self.cnpj}/FXMarketType", "Erro ao consultar tipo de mercado")

        key = 'SpotConfig/CutOffTimes/'
        if market_type and str(market_type) == '2':  # cliente interbancário
            cutoff = get_or_raise(f"{key}Secundary/Any/d{settlement}", "Erro ao consultar cutoff")
        else:
            ccy = self.get_ccy()
            cutoff = get_or_raise(f"{key}Primary/{ccy}/d{settlement}", f"Erro ao consultar cutoff (ccy)")

        if cutoff == '-':
            raise RejectException('Transação acima do horário de corte')

        hh_cutoff, mm_cutoff = map(int, cutoff.split(':'))
        now = get_local_time().time()  # dt.datetime.now().time()

        if now.hour > hh_cutoff or (now.hour == hh_cutoff and now.minute > mm_cutoff):
            raise RejectException('Transação acima do horário de corte')
Exemple #5
0
    def check_max_days_to_maturity(self, days):
        ccy = self.get_ccy()
        key = f'TradingParameters/CurrencyKeys/{ccy}/FXNDF/UpperLimitDays2Maturity'
        limit = get_or_raise(
            key, f'Erro ao consultar limite máximo para vencimento ({ccy})')

        if days > limit:
            msg = 'Vencimento acima do parâmetro e-sales (moeda)'
            raise RejectException(msg)

        key = f'TradingParameters/CounterpartyKeys/{self.cnpj}/FXNDF/UpperLimitDays2Maturity'
        limit = get_or_raise(
            key,
            'Erro ao consultar limite máximo para vencimento (contraparte)')

        if days > limit:
            msg = 'Vencimento acima do parâmetro e-sales (contraparte)'
            raise RejectException(msg)
Exemple #6
0
    def get_spread(self, cnpj, ccy, dc):
        def get_bucket(buckets):
            bucket = None
            for index in range(0, len(buckets)):
                if int(buckets[index]) >= dc:
                    bucket = index
                    break

            return bucket

        buckets = databus.get(
            f'ClientSpreads/CounterpartySpreads/{cnpj}/FXNDF/Buckets')
        spreads = databus.get(
            f'ClientSpreads/CounterpartySpreads/{cnpj}/FXNDF/Spreads/{ccy}/BUYSELL'
        )
        bucket = get_bucket(buckets) if buckets is not None else None
        counterparty_valid_spread = True

        if None in (bucket, spreads) or (spreads and spreads[bucket] is None):
            counterparty_valid_spread = False

        if not counterparty_valid_spread:
            group_name = databus.get(
                f'LegalEntitiesRelationships/Groups_Spreads_FXNDF_Memberships/{cnpj}'
            )
            if group_name is None or group_name == "":
                raise RejectException(f'Spread não cadastrado para CNPJ/Grupo')

            buckets = databus.get(
                f'ClientSpreads/GroupSpreads/{group_name}/FXNDF/Buckets')
            spreads = databus.get(
                f'ClientSpreads/GroupSpreads/{group_name}/FXNDF/Spreads/{ccy}/BUYSELL'
            )

            bucket = get_bucket(buckets) if buckets is not None else None
            if None in (bucket, spreads) or (spreads
                                             and spreads[bucket] is None):
                raise RejectException('Spread não cadastrado para CNPJ/Grupo')

        return spreads[bucket]
Exemple #7
0
    def get_spread(self, cnpj, ccy, side, du_ccy):
        side = side.upper()  # side foi colocado em upper no barramento.
        spreads = databus.get(f'ClientSpreads/CounterpartySpreads/{cnpj}/FXSPOT/{ccy}/{side}')

        if spreads is None or spreads[du_ccy] is None:
            group_name = databus.get(f'LegalEntitiesRelationships/Groups_Spreads_FXSPOT_Memberships/{cnpj}')
            if group_name is None or group_name == "":
                raise RejectException('Spread não cadastrado para CNPJ/Grupo')

            spreads = databus.get(f'ClientSpreads/GroupSpreads/{group_name}/FXSPOT/{ccy}/{side}')
            raise_if_none(spreads, 'Spread não cadastrado para CNPJ/Grupo')

            spread = spreads[du_ccy]
            raise_if_none(spread, 'Spread não cadastrado para CNPJ/Grupo')
        else:
            spread = spreads[du_ccy]

        return spread
Exemple #8
0
def get_or_raise(key, message):
    value = databus.get(key)
    if value is None:
        raise RejectException(message)

    return value
Exemple #9
0
    def price(self):
        self.check_autoflow_limits()

        brl = self.get_brl()
        ccy = self.get_ccy()
        today = self.get_today()
        fixing = self.get_fixing()
        maturity = self.get_maturity()
        amount = self.get_amount()
        holidays_brl = get_holidays(brl)
        d_n = count_business_days(fixing, maturity, holidays_brl)
        maturity_adjusted = get_maturity_adjusted(maturity,
                                                  d_n,
                                                  holidays=holidays_brl)
        dc = count_days(today, maturity_adjusted)
        du = count_business_days(today,
                                 maturity_adjusted,
                                 holidays=holidays_brl)

        self.check_max_days_to_maturity(dc)

        ccy_discount_curve_name = self.get_discount_curve_name(ccy)
        brl_discount_curve_name = self.get_discount_curve_name(brl)

        ccy_curve_dates = self.get_discount_curve_dates(
            ccy_discount_curve_name)
        brl_curve_dates = self.get_discount_curve_dates(
            brl_discount_curve_name)

        side = self.get_side()

        if side == 'Buy':
            ys_brl = self.get_discount_curve_values(brl_discount_curve_name,
                                                    'bid')
            ys_ccy = self.get_discount_curve_values(ccy_discount_curve_name,
                                                    'offer')
        else:
            ys_brl = self.get_discount_curve_values(brl_discount_curve_name,
                                                    'offer')
            ys_ccy = self.get_discount_curve_values(ccy_discount_curve_name,
                                                    'bid')

        dus_brl = list(
            map(lambda x: count_business_days(today, x, holidays=holidays_brl),
                brl_curve_dates))
        dcs_ccy = list(map(lambda x: count_days(today, x), ccy_curve_dates))

        y_brl = round(interpolation(dus_brl, ys_brl, du, 252), 6)
        y_ccy = interpolation(dcs_ccy, ys_ccy, dc, 360)
        y_ccy = round(((1 + y_ccy)**(dc / 360.0) - 1) * (360.0 / dc), 6)

        precision = self.get_currency_precision(ccy)

        s_cost = self.get_scost(ccy, 'Ask' if side != 'Buy' else 'Bid')
        s_cost = round(s_cost, precision)

        f_cost = s_cost * ((1 + y_brl)**(du / 252.0)) / (1 + y_ccy *
                                                         (dc / 360.0))
        f_cost = round(f_cost, precision)

        lower_limit_revenue = self.get_lower_limit_revenue(ccy)
        fmin = (lower_limit_revenue / amount) * ((1 + y_brl)**(du / 252))

        spread = self.get_spread(self.get_cnpj(), ccy, dc)
        fop = max(fmin, spread)  # TODO: add ceiling
        signal = -1 if side == 'Sell' else 1
        quote = f_cost - (fop * signal)
        y_ccy_custo = ((s_cost / quote) *
                       (1 + y_brl)**(du / 252.0) - 1) * (360.0 / dc)
        quote = round(quote, precision)

        if quote <= 0.0:
            raise RejectException(
                'Notional abaixo do parâmetro e-sales (receita)')

        if side == 'Buy':
            s_client = y_ccy_custo - y_ccy
            revenue = amount * abs(quote - f_cost) / (
                (1 + y_brl)**(du / 252.0))
        else:
            s_client = y_ccy - y_ccy_custo
            revenue = amount * abs(f_cost - quote) / (
                (1 + y_brl)**(du / 252.0))

        y_ccy_custo = round(y_ccy_custo, 6)
        s_client = round(s_client, 6)

        if self.result:
            if not self.result.has_changed(quote, precision, revenue):
                raise ResultHasntChanged()

        # cálculo do valor presente, se robo esta vendendo, valor deve ser negativo;
        present_value_brl = round(
            s_cost * (amount / (1 + y_ccy * (dc / 360.0))) * signal, 2)
        present_value_ccy = round(present_value_brl / s_cost, precision)

        # TODO: apagar esse trecho de código. O propósito desse trecho abaixo é para conseguir realizar
        # alguns testes no BV.
        if os.getenv('DATA_CONSULTA_PFE'):
            data_consulta_dt = dt.datetime.strptime(
                os.getenv('DATA_CONSULTA_PFE'), '%Y-%m-%d')
            log_info(f'data da consulta pdf = {data_consulta_dt}')
        else:
            data_consulta_dt = self.get_today().astype(dt.datetime)

        pfe_inst = PotentialFutureExposure()

        try:
            pfe_success = pfe_inst.execute(ccy=self.get_ccy(),
                                           side=self.get_side(),
                                           prazo_consumo=dc,
                                           data_consulta=data_consulta_dt)
        except Exception:
            raise  # TODO: tratar erro de maneira adequada

        f_pfe = pfe_success.value  # prep_and_get_pfe(self, dc)
        brl_risk = f_pfe * abs(present_value_brl) + revenue

        spread_risk = (1 + revenue / brl_risk)**(360.0 / dc) - 1
        spread_notional = (1 + revenue / abs(present_value_brl))**(360.0 /
                                                                   dc) - 1

        self.result = PriceNDFBase.Result()
        self.result.spread = fop
        self.result.pre_brl = y_brl
        self.result.cupom_ccy = y_ccy
        self.result.fop = fop
        self.result.fmin = fmin
        self.result.quote = quote
        self.result.value_brl = round(quote * amount, 2)
        self.result.revenue_brl = revenue
        self.result.present_value_brl = present_value_brl
        self.result.present_value_ccy = present_value_ccy
        self.result.brl_risk = brl_risk
        self.result.spread_risk = spread_risk
        self.result.spread_notional = spread_notional
        self.result.dc = dc
        self.result.du = du
        self.result.dn = d_n  # business days entre fixing e settlement.
        self.result.s_cost = s_cost
        self.result.f_cost = f_cost
        self.result.s_client = s_client
        self.result.forward_points = round(quote - s_cost, precision)
        self.result.y_brl = y_brl
        self.result.y_ccy = y_ccy
        self.result.y_ccy_client = y_ccy_custo
        self.result.f_pfe = f_pfe
        self.result.side = side
        self.result.adjusted_maturity = str(maturity_adjusted)
        self.result.precision = precision
        return self.result