"""
Copyright 2019 Goldman Sachs.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
"""

import datetime
import datetime as dt
import json
import os
from typing import Union, Dict
from unittest import mock

import numpy as np
import pandas as pd
import pytest
import pytz
from numpy.testing import assert_equal
from pandas.testing import assert_series_equal
from pandas.tseries.offsets import CustomBusinessDay
from pytz import timezone
from testfixtures import Replacer
from testfixtures.mock import Mock

import gs_quant.timeseries.measures as tm
import gs_quant.timeseries.measures_rates as tm_rates
from gs_quant.api.gs.assets import AssetType, GsTemporalXRef, GsAssetApi, GsIdType, IdList, GsAsset
from gs_quant.api.gs.data import GsDataApi, MarketDataResponseFrame
from gs_quant.api.gs.data import QueryType
from gs_quant.data.core import DataContext
from gs_quant.errors import MqError, MqValueError, MqTypeError
from gs_quant.json_encoder import JSONEncoder
from gs_quant.markets.baskets import Basket as CustomBasket
from gs_quant.markets.index import Index
from gs_quant.markets.securities import AssetClass, Cross, Currency, SecurityMaster, Stock, \
    Swap, CommodityNaturalGasHub, CommodityEUNaturalGasHub, AssetIdentifier, CommodityPowerAggregatedNodes, \
    FutureMarket, DefaultSwap
from gs_quant.session import GsSession, Environment, OAuth2Session
from gs_quant.target.common import XRef, PricingLocation, Currency as CurrEnum
from gs_quant.test.timeseries.utils import mock_request
from gs_quant.timeseries import Returns, ExtendedSeries, BenchmarkType

_index = [pd.Timestamp('2019-01-01')]
_index2 = [pd.Timestamp('2019-08-02')]
_test_datasets = ('TEST_DATASET',)
_test_datasets2 = ('TEST_DATASET2',)
_test_datasets_rt = ('TEST_DATASET_RT',)


def mock_empty_market_data_response():
    df = MarketDataResponseFrame()
    df.dataset_ids = ()
    return df


def map_identifiers_default_mocker(input_type: Union[GsIdType, str],
                                   output_type: Union[GsIdType, str],
                                   ids: IdList,
                                   as_of: dt.datetime = None,
                                   multimap: bool = False,
                                   limit: int = None,
                                   **kwargs
                                   ) -> dict:
    if "USD-LIBOR-BBA" in ids:
        return {"USD-LIBOR-BBA": "MAPDB7QNB2TZVQ0E"}
    elif "EUR-EURIBOR-TELERATE" in ids:
        return {"EUR-EURIBOR-TELERATE": "MAJNQPFGN1EBDHAE"}
    elif "GBP-LIBOR-BBA" in ids:
        return {"GBP-LIBOR-BBA": "MAFYB8Z4R1377A19"}
    elif "JPY-LIBOR-BBA" in ids:
        return {"JPY-LIBOR-BBA": "MABMVE27EM8YZK33"}
    elif "EUR OIS" in ids:
        return {"EUR OIS": "MARFAGXDQRWM07Y2"}


def map_identifiers_ois_mocker(input_type: Union[GsIdType, str],
                               output_type: Union[GsIdType, str],
                               ids: IdList,
                               as_of: dt.datetime = None,
                               multimap: bool = False,
                               limit: int = None,
                               **kwargs
                               ) -> dict:
    if "USD OIS" in ids:
        return {"USD OIS": "MA29GN9VZE0WK56V"}
    elif "EUR OIS" in ids:
        return {"EUR OIS": "MARFAGXDQRWM07Y2"}
    elif "SEK OIS" in ids:
        return {"SEK OIS": "MARSF2Y8GV5GHXYZ"}
    elif "AUD OIS" in ids:
        return {"AUD OIS": "MA2XT17D0P77A84G"}
    elif "JPY OIS" in ids:
        return {"JPY OIS": "MAY3XC3279QN7SHW"}
    elif "GBP OIS" in ids:
        return {"GBP OIS": "MAGP76C36BX2Q0YA"}
    elif "NOK OIS" in ids:
        return {"NOK OIS": "MAS7EYBPYZ4SQ4GG"}
    elif "CAD OIS" in ids:
        return {"CAD OIS": "MAX229TKVEZMZ4WT"}
    elif "NZD OIS" in ids:
        return {"NZD OIS": "MA48BHGWY71R4SAJ"}
    elif "CHF OIS" in ids:
        return {"CHF OIS": "MAK0CRRF9DPMECSJ"}


def map_identifiers_swap_rate_mocker(input_type: Union[GsIdType, str],
                                     output_type: Union[GsIdType, str],
                                     ids: IdList,
                                     as_of: dt.datetime = None,
                                     multimap: bool = False,
                                     limit: int = None,
                                     **kwargs
                                     ) -> dict:
    if "USD-3m" in ids:
        return {"USD-3m": "MAAXGV0GZTW4GFNC"}
    elif "EUR-6m" in ids:
        return {"EUR-6m": "MA5WM2QWRVMYKDK0"}
    elif "KRW" in ids:
        return {"KRW": 'MAJ6SEQH3GT0GA2Z'}


def map_identifiers_inflation_mocker(input_type: Union[GsIdType, str],
                                     output_type: Union[GsIdType, str],
                                     ids: IdList,
                                     as_of: dt.datetime = None,
                                     multimap: bool = False,
                                     limit: int = None,
                                     **kwargs
                                     ) -> dict:
    if "CPI-UKRPI" in ids:
        return {"CPI-UKRPI": "MAQ7ND0MBP2AVVQW"}
    elif "CPI-CPXTEMU" in ids:
        return {"CPI-CPXTEMU": "MAK1FHKH5P5GJSHH"}


def map_identifiers_cross_basis_mocker(input_type: Union[GsIdType, str],
                                       output_type: Union[GsIdType, str],
                                       ids: IdList,
                                       as_of: dt.datetime = None,
                                       multimap: bool = False,
                                       limit: int = None,
                                       **kwargs
                                       ) -> dict:
    if "USD-3m/JPY-3m" in ids:
        return {"USD-3m/JPY-3m": "MA99N6C1KF9078NM"}
    elif "EUR-3m/USD-3m" in ids:
        return {"EUR-3m/USD-3m": "MAXPKTXW2D4X6MFQ"}
    elif "GBP-3m/USD-3m" in ids:
        return {"GBP-3m/USD-3m": "MA8BZHQV3W32V63B"}


def test_currency_to_default_ois_asset(mocker):
    mocker.patch.object(GsSession.__class__, 'default_value',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(GsSession.current, '_get', side_effect=mock_request)
    mocker.patch.object(GsAssetApi, 'map_identifiers', side_effect=map_identifiers_ois_mocker)

    asset_id_list = ["MAJNQPFGN1EBDHAE"]
    correct_mapping = ["MARFAGXDQRWM07Y2"]
    with tm.PricingContext(dt.date.today()):
        for i in range(len(asset_id_list)):
            correct_id = tm.currency_to_default_ois_asset(asset_id_list[i])
            assert correct_id == correct_mapping[i]

        # Test that the same id is returned when a TypeError is raised
        mocker.patch.object(GsAssetApi, 'map_identifiers', side_effect=TypeError('Test'))
        assert tm.currency_to_default_ois_asset('MAJNQPFGN1EBDHAE') == 'MAJNQPFGN1EBDHAE'


def test_currency_to_default_benchmark_rate(mocker):
    mocker.patch.object(GsSession.__class__, 'default_value',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(GsSession.current, '_get', side_effect=mock_request)
    mocker.patch.object(GsAssetApi, 'map_identifiers', side_effect=map_identifiers_default_mocker)

    asset_id_list = ["MAZ7RWC904JYHYPS", "MAJNQPFGN1EBDHAE", "MA66CZBQJST05XKG", "MAK1FHKH5P5GJSHH", "MA4J1YB8XZP2BPT8",
                     "MA4B66MW5E27U8P32SB"]
    correct_mapping = ["MAPDB7QNB2TZVQ0E", "MAJNQPFGN1EBDHAE", "MAFYB8Z4R1377A19", "MABMVE27EM8YZK33",
                       "MA4J1YB8XZP2BPT8", "MA4B66MW5E27U8P32SB"]
    with tm.PricingContext(dt.date.today()):
        for i in range(len(asset_id_list)):
            correct_id = tm.currency_to_default_benchmark_rate(asset_id_list[i])
            assert correct_id == correct_mapping[i]


def test_currency_to_default_swap_rate_asset(mocker):
    mocker.patch.object(GsSession.__class__, 'default_value',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(GsSession.current, '_get', side_effect=mock_request)
    mocker.patch.object(GsAssetApi, 'map_identifiers', side_effect=map_identifiers_swap_rate_mocker)

    asset_id_list = ['MAZ7RWC904JYHYPS', 'MAJNQPFGN1EBDHAE', 'MAJ6SEQH3GT0GA2Z']
    correct_mapping = ['MAAXGV0GZTW4GFNC', 'MA5WM2QWRVMYKDK0', 'MAJ6SEQH3GT0GA2Z']
    with tm.PricingContext(dt.date.today()):
        for i in range(len(asset_id_list)):
            correct_id = tm.currency_to_default_swap_rate_asset(asset_id_list[i])
            assert correct_id == correct_mapping[i]


def test_currency_to_inflation_benchmark_rate(mocker):
    mocker.patch.object(GsSession.__class__, 'default_value',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(GsSession.current, '_get', side_effect=mock_request)
    mocker.patch.object(GsAssetApi, 'map_identifiers', side_effect=map_identifiers_inflation_mocker)

    asset_id_list = ["MA66CZBQJST05XKG", "MAK1FHKH5P5GJSHH", "MA4J1YB8XZP2BPT8"]
    correct_mapping = ["MAQ7ND0MBP2AVVQW", "MAK1FHKH5P5GJSHH", "MA4J1YB8XZP2BPT8"]
    with tm.PricingContext(dt.date.today()):
        for i in range(len(asset_id_list)):
            correct_id = tm.currency_to_inflation_benchmark_rate(asset_id_list[i])
            assert correct_id == correct_mapping[i]

        # Test that the same id is returned when a TypeError is raised
        mocker.patch.object(GsAssetApi, 'map_identifiers', side_effect=TypeError('Test'))
        assert tm.currency_to_inflation_benchmark_rate('MA66CZBQJST05XKG') == 'MA66CZBQJST05XKG'


def test_cross_to_basis(mocker):
    mocker.patch.object(GsSession.__class__, 'default_value',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(GsSession.current, '_get', side_effect=mock_request)
    mocker.patch.object(GsAssetApi, 'map_identifiers', side_effect=map_identifiers_cross_basis_mocker)

    asset_id_list = ["MAYJPCVVF2RWXCES", "MA4B66MW5E27U8P32SB", "nobbid"]
    correct_mapping = ["MA99N6C1KF9078NM", "MA4B66MW5E27U8P32SB", "nobbid"]
    with tm.PricingContext(dt.date.today()):
        for i in range(len(asset_id_list)):
            correct_id = tm.cross_to_basis(asset_id_list[i])
            assert correct_id == correct_mapping[i]

        # Test that the same id is returned when a TypeError is raised
        mocker.patch.object(GsAssetApi, 'map_identifiers', side_effect=TypeError('Test'))
        assert tm.cross_to_basis('MAYJPCVVF2RWXCES') == 'MAYJPCVVF2RWXCES'


def test_currency_to_tdapi_swap_rate_asset(mocker):
    replace = Replacer()
    mocker.patch.object(GsSession.__class__, 'current',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(GsSession.current, '_get', side_effect=mock_request)
    mocker.patch.object(SecurityMaster, 'get_asset', side_effect=mock_request)
    bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    with tm.PricingContext(dt.date.today()):
        asset = Currency('MA25DW5ZGC1BSC8Y', 'NOK')
        bbid_mock.return_value = 'NOK'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        asset = Currency('MAZ7RWC904JYHYPS', 'USD')
        bbid_mock.return_value = 'USD'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MAFRSWPAF5QPNTP2' == correct_id
        bbid_mock.return_value = 'CHF'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MAW25BGQJH9P6DPT' == correct_id
        bbid_mock.return_value = 'EUR'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MAA9MVX15AJNQCVG' == correct_id
        bbid_mock.return_value = 'GBP'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MA6QCAP9B7ABS9HA' == correct_id
        bbid_mock.return_value = 'JPY'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MAEE219J5ZP0ZKRK' == correct_id
        bbid_mock.return_value = 'SEK'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MAETMVTPNP3199A5' == correct_id

        bbid_mock.return_value = 'HKD'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MABRNGY8XRFVC36N' == correct_id
        bbid_mock.return_value = 'NZD'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MAH16NHE1HBN0FBZ' == correct_id
        bbid_mock.return_value = 'AUD'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MAY8147CRK0ZP53B' == correct_id
        bbid_mock.return_value = 'CAD'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MANJ8SS88WJ6N28Q' == correct_id
        bbid_mock.return_value = 'KRW'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MAP55AXG5SQVS6C5' == correct_id

        bbid_mock.return_value = 'INR'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MA20JHJXN1PD5HGE' == correct_id

        bbid_mock.return_value = 'CNY'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MA4K1D8HH2R0RQY5' == correct_id

        bbid_mock.return_value = 'SGD'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MA5CQFHYBPH9E5BS' == correct_id

        bbid_mock.return_value = 'DKK'
        correct_id = tm_rates._currency_to_tdapi_swap_rate_asset(asset)
        assert 'MAF131NKWVRESFYA' == correct_id

        asset = Currency('MA890', 'PLN')
        bbid_mock.return_value = 'PLN'
        assert 'MA890' == tm_rates._currency_to_tdapi_swap_rate_asset(asset)
    replace.restore()


def test_currency_to_tdapi_basis_swap_rate_asset(mocker):
    replace = Replacer()
    mocker.patch.object(GsSession.__class__, 'current',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(GsSession.current, '_get', side_effect=mock_request)
    mocker.patch.object(SecurityMaster, 'get_asset', side_effect=mock_request)
    bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    with tm.PricingContext(dt.date.today()):
        asset = Currency('MA890', 'EGP')
        bbid_mock.return_value = 'EGP'
        assert 'MA890' == tm_rates._currency_to_tdapi_basis_swap_rate_asset(asset)
        asset = Currency('MAZ7RWC904JYHYPS', 'USD')
        bbid_mock.return_value = 'USD'
        correct_id = tm_rates._currency_to_tdapi_basis_swap_rate_asset(asset)
        assert 'MAQB1PGEJFCET3GG' == correct_id
        bbid_mock.return_value = 'EUR'
        correct_id = tm_rates._currency_to_tdapi_basis_swap_rate_asset(asset)
        assert 'MAGRG2VT11GQ2RQ9' == correct_id
        bbid_mock.return_value = 'GBP'
        correct_id = tm_rates._currency_to_tdapi_basis_swap_rate_asset(asset)
        assert 'MAHCYNB3V75JC5Q8' == correct_id
        bbid_mock.return_value = 'JPY'
        correct_id = tm_rates._currency_to_tdapi_basis_swap_rate_asset(asset)
        assert 'MAXVRBEZCJVH0C4V' == correct_id
        bbid_mock.return_value = 'AUD'
        correct_id = tm_rates._currency_to_tdapi_basis_swap_rate_asset(asset)
        assert 'MAY8H7HCNZ85FJKM' == correct_id
        bbid_mock.return_value = 'NZD'
        correct_id = tm_rates._currency_to_tdapi_basis_swap_rate_asset(asset)
        assert 'MAWK15C0P3SM6C7Q' == correct_id
        bbid_mock.return_value = 'CHF'
        correct_id = tm_rates._currency_to_tdapi_basis_swap_rate_asset(asset)
        assert 'MA7ZHB9T0PF1SB96' == correct_id
        bbid_mock.return_value = 'DKK'
        correct_id = tm_rates._currency_to_tdapi_basis_swap_rate_asset(asset)
        assert 'MA2164KK5DMYA561' == correct_id
        bbid_mock.return_value = 'NOK'
        correct_id = tm_rates._currency_to_tdapi_basis_swap_rate_asset(asset)
        assert 'MAPXC5YBPZJZXYMZ' == correct_id
        bbid_mock.return_value = 'SEK'
        correct_id = tm_rates._currency_to_tdapi_basis_swap_rate_asset(asset)
        assert 'MAS2NJCYHDHP8P0X' == correct_id
        bbid_mock.return_value = 'CAD'
        correct_id = tm_rates._currency_to_tdapi_basis_swap_rate_asset(asset)
        assert 'MARVD2E65AWEXXBA' == correct_id
        replace.restore()


def test_check_clearing_house():
    assert tm_rates._ClearingHouse.LCH == tm_rates._check_clearing_house('lch')
    assert tm_rates._ClearingHouse.CME == tm_rates._check_clearing_house(tm_rates._ClearingHouse.CME)
    assert tm_rates._ClearingHouse.LCH == tm_rates._check_clearing_house(None)
    invalid_ch = ['NYSE']
    for ch in invalid_ch:
        with pytest.raises(MqError):
            tm_rates._check_clearing_house(ch)


def test_get_swap_csa_terms():
    euribor_index = tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['EUR'][BenchmarkType.EURIBOR.value]
    usd_libor_index = tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['USD'][BenchmarkType.LIBOR.value]
    fed_funds_index = tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['USD'][BenchmarkType.Fed_Funds.value]
    estr_index = tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['EUR'][BenchmarkType.EUROSTR.value]
    assert dict(csaTerms='USD-1') == tm_rates._get_swap_csa_terms('USD', fed_funds_index)
    assert dict(csaTerms='EUR-EuroSTR') == tm_rates._get_swap_csa_terms('EUR', estr_index)
    assert {} == tm_rates._get_swap_csa_terms('EUR', euribor_index)
    assert {} == tm_rates._get_swap_csa_terms('USD', usd_libor_index)


def test_get_basis_swap_csa_terms():
    euribor_index = tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['EUR'][BenchmarkType.EURIBOR.value]
    usd_libor_index = tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['USD'][BenchmarkType.LIBOR.value]
    fed_funds_index = tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['USD'][BenchmarkType.Fed_Funds.value]
    sofr_index = tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['USD'][BenchmarkType.SOFR.value]
    estr_index = tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['EUR'][BenchmarkType.EUROSTR.value]
    eonia_index = tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['EUR'][BenchmarkType.EONIA.value]
    assert dict(csaTerms='USD-1') == tm_rates._get_basis_swap_csa_terms('USD', fed_funds_index, sofr_index)
    assert dict(csaTerms='EUR-EuroSTR') == tm_rates._get_basis_swap_csa_terms('EUR', estr_index, eonia_index)
    assert {} == tm_rates._get_basis_swap_csa_terms('EUR', eonia_index, euribor_index)
    assert {} == tm_rates._get_basis_swap_csa_terms('USD', fed_funds_index, usd_libor_index)


def test_match_floating_tenors():
    swap_args = dict(asset_parameters_payer_rate_option=tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['USD']['LIBOR'],
                     asset_parameters_payer_designated_maturity='12m',
                     asset_parameters_receiver_rate_option=tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['USD']['SOFR'],
                     asset_parameters_receiver_designated_maturity='1y')

    assert '1y' == tm_rates._match_floating_tenors(swap_args)['asset_parameters_receiver_designated_maturity']

    swap_args = dict(asset_parameters_payer_rate_option=tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['USD']['SOFR'],
                     asset_parameters_payer_designated_maturity='1y',
                     asset_parameters_receiver_rate_option=tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['USD']['LIBOR'],
                     asset_parameters_receiver_designated_maturity='12m')
    assert '1y' == tm_rates._match_floating_tenors(swap_args)['asset_parameters_payer_designated_maturity']

    swap_args = dict(asset_parameters_payer_rate_option=tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['GBP']['SONIA'],
                     asset_parameters_payer_designated_maturity='1y',
                     asset_parameters_receiver_rate_option=tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['GBP']['LIBOR'],
                     asset_parameters_receiver_designated_maturity='3m')
    assert '3m' == tm_rates._match_floating_tenors(swap_args)['asset_parameters_payer_designated_maturity']

    swap_args = dict(asset_parameters_payer_rate_option=tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['GBP']['LIBOR'],
                     asset_parameters_payer_designated_maturity='3m',
                     asset_parameters_receiver_rate_option=tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['GBP']['SONIA'],
                     asset_parameters_receiver_designated_maturity='1y')
    assert '3m' == tm_rates._match_floating_tenors(swap_args)['asset_parameters_receiver_designated_maturity']

    swap_args = dict(asset_parameters_payer_rate_option=tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['USD']['LIBOR'],
                     asset_parameters_payer_designated_maturity='3m',
                     asset_parameters_receiver_rate_option=tm_rates.CURRENCY_TO_SWAP_RATE_BENCHMARK['USD']['LIBOR'],
                     asset_parameters_receiver_designated_maturity='6m')

    assert swap_args == tm_rates._match_floating_tenors(swap_args)


def test_get_term_struct_date(mocker):
    today = datetime.datetime.today()
    biz_day = CustomBusinessDay()
    assert today == tm_rates._get_term_struct_date(tenor=today, index=today, business_day=biz_day)
    date_index = datetime.datetime(2020, 7, 31, 0, 0)
    assert date_index == tm_rates._get_term_struct_date(tenor='2020-07-31', index=date_index, business_day=biz_day)
    assert date_index == tm_rates._get_term_struct_date(tenor='0b', index=date_index, business_day=biz_day)
    assert datetime.datetime(2021, 7, 30, 0, 0) == tm_rates._get_term_struct_date(tenor='1y', index=date_index,
                                                                                  business_day=biz_day)


def test_cross_stored_direction_for_fx_vol(mocker):
    mocker.patch.object(GsSession.__class__, 'default_value',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(GsSession.current, '_get', side_effect=mock_request)
    mocker.patch.object(GsSession.current, '_post', side_effect=mock_request)
    asset_id_list = ["MAYJPCVVF2RWXCES", "MATGYV0J9MPX534Z"]
    correct_mapping = ["MATGYV0J9MPX534Z", "MATGYV0J9MPX534Z"]
    with tm.PricingContext(dt.date.today()):
        for i in range(len(asset_id_list)):
            correct_id = tm.cross_stored_direction_for_fx_vol(asset_id_list[i])
            assert correct_id == correct_mapping[i]


def test_cross_to_usd_based_cross_for_fx_forecast(mocker):
    mocker.patch.object(GsSession.__class__, 'default_value',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(GsSession.current, '_get', side_effect=mock_request)
    mocker.patch.object(GsSession.current, '_post', side_effect=mock_request)
    asset_id_list = ["MAYJPCVVF2RWXCES", "MATGYV0J9MPX534Z"]
    correct_mapping = ["MATGYV0J9MPX534Z", "MATGYV0J9MPX534Z"]
    with tm.PricingContext(dt.date.today()):
        for i in range(len(asset_id_list)):
            correct_id = tm.cross_to_usd_based_cross(asset_id_list[i])
            assert correct_id == correct_mapping[i]


def test_cross_to_used_based_cross(mocker):
    mocker.patch.object(GsSession.__class__, 'default_value',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(GsSession.current, '_get', side_effect=mock_request)
    mocker.patch.object(GsSession.current, '_post', side_effect=mock_request)
    mocker.patch.object(SecurityMaster, 'get_asset', side_effect=TypeError('unsupported'))

    replace = Replacer()
    bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    bbid_mock.return_value = 'HELLO'

    assert 'FUN' == tm.cross_to_usd_based_cross(Cross('FUN', 'EURUSD'))
    replace.restore()


def test_cross_stored_direction(mocker):
    mocker.patch.object(GsSession.__class__, 'default_value',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(GsSession.current, '_get', side_effect=mock_request)
    mocker.patch.object(GsSession.current, '_post', side_effect=mock_request)
    mocker.patch.object(SecurityMaster, 'get_asset', side_effect=TypeError('unsupported'))

    replace = Replacer()
    bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    bbid_mock.return_value = 'HELLO'

    assert 'FUN' == tm.cross_stored_direction_for_fx_vol(Cross('FUN', 'EURUSD'))
    replace.restore()


def test_get_tdapi_rates_assets(mocker):
    mock_asset_1 = GsAsset(asset_class='Rates', id='MAW25BGQJH9P6DPT', type_='Swap', name='Test_asset')
    mock_asset_2 = GsAsset(asset_class='Rates', id='MAA9MVX15AJNQCVG', type_='Swap', name='Test_asset')
    mock_asset_3 = GsAsset(asset_class='Rates', id='MANQHVYC30AZFT7R', type_='BasisSwap', name='Test_asset')

    replace = Replacer()
    assets = replace('gs_quant.timeseries.measures.GsAssetApi.get_many_assets', Mock())
    assets.return_value = [mock_asset_1]
    assert 'MAW25BGQJH9P6DPT' == tm_rates._get_tdapi_rates_assets()
    replace.restore()

    assets = replace('gs_quant.timeseries.measures.GsAssetApi.get_many_assets', Mock())
    assets.return_value = [mock_asset_1, mock_asset_2]
    kwargs = dict(asset_parameters_termination_date='10y', asset_parameters_effective_date='0b')
    with pytest.raises(MqValueError):
        tm_rates._get_tdapi_rates_assets(**kwargs)
    replace.restore()

    assets = replace('gs_quant.timeseries.measures.GsAssetApi.get_many_assets', Mock())
    assets.return_value = []
    with pytest.raises(MqValueError):
        tm_rates._get_tdapi_rates_assets()
    replace.restore()

    assets = replace('gs_quant.timeseries.measures.GsAssetApi.get_many_assets', Mock())
    assets.return_value = [mock_asset_1, mock_asset_2]
    kwargs = dict()
    assert ['MAW25BGQJH9P6DPT', 'MAA9MVX15AJNQCVG'] == tm_rates._get_tdapi_rates_assets(**kwargs)
    replace.restore()

    #   test case will test matching sofr maturity with libor leg and flipping legs to get right asset
    kwargs = dict(type='BasisSwap', asset_parameters_termination_date='10y',
                  asset_parameters_payer_rate_option=BenchmarkType.LIBOR,
                  asset_parameters_payer_designated_maturity='3m',
                  asset_parameters_receiver_rate_option=BenchmarkType.SOFR,
                  asset_parameters_receiver_designated_maturity='1y',
                  asset_parameters_clearing_house='lch', asset_parameters_effective_date='Spot',
                  asset_parameters_notional_currency='USD',
                  pricing_location='NYC')

    assets = replace('gs_quant.timeseries.measures.GsAssetApi.get_many_assets', Mock())
    assets.return_value = [mock_asset_3]
    assert 'MANQHVYC30AZFT7R' == tm_rates._get_tdapi_rates_assets(**kwargs)
    replace.restore()


def test_get_swap_leg_defaults():
    result_dict = dict(currency=CurrEnum.JPY, benchmark_type='JPY-LIBOR-BBA', floating_rate_tenor='6m',
                       pricing_location=PricingLocation.TKO)
    defaults = tm_rates._get_swap_leg_defaults(CurrEnum.JPY)
    assert result_dict == defaults

    result_dict = dict(currency=CurrEnum.USD, benchmark_type='USD-LIBOR-BBA', floating_rate_tenor='3m',
                       pricing_location=PricingLocation.NYC)
    defaults = tm_rates._get_swap_leg_defaults(CurrEnum.USD)
    assert result_dict == defaults

    result_dict = dict(currency=CurrEnum.EUR, benchmark_type='EUR-EURIBOR-TELERATE', floating_rate_tenor='6m',
                       pricing_location=PricingLocation.LDN)
    defaults = tm_rates._get_swap_leg_defaults(CurrEnum.EUR)
    assert result_dict == defaults

    result_dict = dict(currency=CurrEnum.SEK, benchmark_type='SEK-STIBOR-SIDE', floating_rate_tenor='6m',
                       pricing_location=PricingLocation.LDN)
    defaults = tm_rates._get_swap_leg_defaults(CurrEnum.SEK)
    assert result_dict == defaults


def test_check_forward_tenor():
    valid_tenors = [datetime.date(2020, 1, 1), '1y', 'imm2', 'frb2', '1m', '0b']
    for tenor in valid_tenors:
        assert tenor == tm_rates._check_forward_tenor(tenor)

    invalid_tenors = ['5yr', 'imm5', 'frb0']
    for tenor in invalid_tenors:
        with pytest.raises(MqError):
            tm_rates._check_forward_tenor(tenor)


def mock_commod(_cls, _q, ignore_errors=False):
    d = {
        'price': [30, 30, 30, 30, 35.929686, 35.636039, 27.307498, 23.23177, 19.020833, 18.827291, 17.823749, 17.393958,
                  17.824999, 20.307603, 24.311249, 25.160103, 25.245728, 25.736873, 28.425206, 28.779789, 30.519996,
                  34.896348, 33.966973, 33.95489, 33.686348, 34.840307, 32.674163, 30.261665, 30, 30, 30]
    }
    df = MarketDataResponseFrame(data=d, index=pd.date_range('2019-05-01', periods=31, freq='H', tz=timezone('UTC')))
    df.dataset_ids = _test_datasets
    return df


def mock_commod_dup(_cls, _q, ignore_errors=False):
    d = {'price': [35.929686, 35]}
    idx = pd.date_range('2019-05-01', periods=1, freq='H', tz=timezone('UTC'))
    df = MarketDataResponseFrame(data=d, index=idx.repeat(2))
    df.dataset_ids = _test_datasets
    return df


def mock_forward_price(_cls, _q, ignore_errors=False):
    d = {
        'forwardPrice': [
            22.0039,
            24.8436,
            24.8436,
            11.9882,
            14.0188,
            11.6311,
            18.9234,
            21.3654,
            21.3654,
        ],
        'quantityBucket': [
            "PEAK",
            "PEAK",
            "PEAK",
            "7X8",
            "7X8",
            "7X8",
            "2X16H",
            "2X16H",
            "2X16H",

        ],
        'contract': [
            "J20",
            "K20",
            "M20",
            "J20",
            "K20",
            "M20",
            "J20",
            "K20",
            "M20",
        ]

    }
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2019, 1, 2)] * 9))
    df.dataset_ids = _test_datasets
    return df


def mock_implied_volatility_elec():
    d = {
        'impliedVolatility': [
            0.3424,
            0.3624,
            0.4424,
            0.4424,
            0.4224,
            0.5324,
            0.3224,
            0.3324,
            0.3924,
        ],
        'quantityBucket': [
            "PEAK",
            "PEAK",
            "PEAK",
            "7X8",
            "7X8",
            "7X8",
            "2X16H",
            "2X16H",
            "2X16H",

        ],
        'contract': [
            "J20",
            "K20",
            "M20",
            "J20",
            "K20",
            "M20",
            "J20",
            "K20",
            "M20",
        ]

    }
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2019, 1, 2)] * 9))
    df.dataset_ids = _test_datasets
    return df


def mock_fair_price(_cls, _q, ignore_errors=False):
    d = {
        'fairPrice': [
            2.880,
            2.844,
            2.726,
        ],
        'contract': [
            "F21",
            "G21",
            "H21",
        ]
    }
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2019, 1, 2)] * 3))
    df.dataset_ids = _test_datasets
    return df


def mock_eu_natgas_forward_price(_cls, _q, ignore_errors=False):
    d = {'forwardPrice': [15.65], 'contract': ["H21"]}
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2021, 1, 1)]))
    df.dataset_ids = _test_datasets
    return df


def mock_natgas_forward_price(_cls, _q, ignore_errors=False):
    d = {
        'forwardPrice': [
            2.880,
            2.844,
            2.726,
        ],
        'contract': [
            "F21",
            "G21",
            "H21",
        ]
    }
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2019, 1, 2)] * 3))
    df.dataset_ids = _test_datasets
    return df


def mock_natgas_implied_volatility(_cls, _q, ignore_errors=False):
    d = {
        'impliedVolatility': [
            2.880,
            2.844,
            2.726,
        ],
        'contract': [
            "F21",
            "G21",
            "H21",
        ]
    }
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2019, 1, 2)] * 3))
    df.dataset_ids = _test_datasets
    return df


def mock_fair_price_swap(_cls, _q, ignore_errors=False):
    d = {'fairPrice': [2.880]}
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2019, 1, 2)]))
    df.dataset_ids = _test_datasets
    return df


def mock_implied_volatility(_cls, _q, ignore_errors=False):
    d = {
        'impliedVolatility': [
            2.880,
            2.844,
            2.726,
        ],
        'contract': [
            "F21",
            "G21",
            "H21",
        ]
    }
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2019, 1, 2)] * 3))
    df.dataset_ids = _test_datasets
    return df


def mock_missing_bucket_forward_price(_cls, _q, ignore_errors=False):
    d = {
        'forwardPrice': [
            22.0039,
            24.8436,
            24.8436,
            11.9882,
            14.0188,
            18.9234,
            21.3654,
            21.3654,
        ],
        'quantityBucket': [
            "PEAK",
            "PEAK",
            "PEAK",
            "7X8",
            "7X8",
            "2X16H",
            "2X16H",
            "2X16H",

        ],
        'contract': [
            "J20",
            "K20",
            "M20",
            "J20",
            "K20",
            "J20",
            "K20",
            "M20",
        ]

    }
    return pd.DataFrame(data=d, index=pd.to_datetime([datetime.date(2019, 1, 2)] * 8))


def mock_missing_bucket_implied_volatility():
    d = {
        'impliedVolatility': [
            0.3424,
            0.3624,
            0.4424,
            0.4424,
            0.4224,
            0.3224,
            0.3324,
            0.3924,
        ],
        'quantityBucket': [
            "PEAK",
            "PEAK",
            "PEAK",
            "7X8",
            "7X8",
            "2X16H",
            "2X16H",
            "2X16H",

        ],
        'contract': [
            "J20",
            "K20",
            "M20",
            "J20",
            "K20",
            "J20",
            "K20",
            "M20",
        ]

    }
    return pd.DataFrame(data=d, index=pd.to_datetime([datetime.date(2019, 1, 2)] * 8))


def mock_fx_vol(_cls, asset_ids=None, query_type=None, where=None, source=None, real_time=None, request_id=None):
    d = {
        'strikeReference': ['delta', 'spot', 'forward'],
        'relativeStrike': [25, 100, 100],
        'impliedVolatility': [5, 1, 2],
        'forecast': [1.1, 1.1, 1.1]
    }
    if DataContext.current.end_date >= datetime.date.today():
        periods = 4
        d['impliedVolatility'].append(3)
        d['strikeReference'].append('delta')
        d['relativeStrike'].append(100)
        d['forecast'].append(1.1)
    else:
        periods = 3
    df = MarketDataResponseFrame(data=d, index=pd.date_range('2019-01-01', periods=periods, freq='D'))
    df.dataset_ids = _test_datasets
    df.index.freq = None
    return df


def mock_fx_spot_fwd_3m(*args, **kwargs):
    d = pd.DataFrame({
        'spot': [1.18250, 1.18566, 1.18511],
        'forwardPoint': [0.00234, 0.00234, 0.00235],
        'tenor': ['3m', '3m', '3m'],
        'date': [pd.Timestamp(2020, 9, 2), pd.Timestamp(2020, 9, 3), pd.Timestamp(2020, 9, 4)],
        'settlementDate': [pd.Timestamp('2020-12-04'), pd.Timestamp('2020-12-08'), pd.Timestamp('2020-12-08')]
    })
    d = d.set_index('date')
    df = MarketDataResponseFrame(d)
    df.dataset_ids = _test_datasets
    return df


def mock_fx_spot_fwd_2y(*args, **kwargs):
    d = pd.DataFrame({
        'spot': [1.18250, 1.18566, 1.18511],
        'forwardPoint': [0.02009, 0.02015, 0.02064],
        'tenor': ['2y', '2y', '2y'],
        'date': [pd.Timestamp(2020, 9, 2), pd.Timestamp(2020, 9, 3), pd.Timestamp(2020, 9, 4)],
        'settlementDate': [pd.Timestamp('2020-12-04'), pd.Timestamp('2020-12-08'), pd.Timestamp('2020-12-08')]
    })
    d = d.set_index('date')
    df = MarketDataResponseFrame(d)
    df.dataset_ids = _test_datasets
    return df


def mock_fx_spot_fwd_3m_rt(*args, **kwargs):
    d = pd.DataFrame({
        'spot': [1.18250, 1.18566, 1.18511],
        'forwardPoint': [0.00234, 0.00234, 0.00235],
        'tenor': ['3m', '3m', '3m'],
        'time': [pd.Timestamp(2020, 9, 2), pd.Timestamp(2020, 9, 3), pd.Timestamp(2020, 9, 4)],
        'settlementDate': [pd.Timestamp('2020-12-04'), pd.Timestamp('2020-12-08'), pd.Timestamp('2020-12-08')]
    })
    d = d.set_index('time')
    df = MarketDataResponseFrame(d)
    df.dataset_ids = _test_datasets
    return df


def mock_fx_spot_fwd_2y_rt(*args, **kwargs):
    d = pd.DataFrame({
        'spot': [1.18250, 1.18566, 1.18511],
        'forwardPoint': [0.02009, 0.02015, 0.02064],
        'tenor': ['2y', '2y', '2y'],
        'time': [pd.Timestamp(2020, 9, 2), pd.Timestamp(2020, 9, 3), pd.Timestamp(2020, 9, 4)],
        'settlementDate': [pd.Timestamp('2020-12-04'), pd.Timestamp('2020-12-08'), pd.Timestamp('2020-12-08')]
    })
    d = d.set_index('time')
    df = MarketDataResponseFrame(d)
    df.dataset_ids = _test_datasets
    return df


def mock_fx_correlation(*args, **kwargs):
    d = pd.DataFrame({
        'impliedVolatility': [7.943208, 8.042599, 7.875325, 8.304180, 8.353483, 8.268724, 8.267971, 8.395843, 8.355239],
        'bbid': ['EURUSD', 'EURUSD', 'EURUSD', 'EURJPY', 'EURJPY', 'EURJPY', 'USDJPY', 'USDJPY', 'USDJPY'],
        'assetId': ['MAA0NE9QX2ABETG6', 'MAA0NE9QX2ABETG6', 'MAA0NE9QX2ABETG6', 'MAYPHS80JRWDJ8RC', 'MAYPHS80JRWDJ8RC',
                    'MAYPHS80JRWDJ8RC', 'MATGYV0J9MPX534Z', 'MATGYV0J9MPX534Z', 'MATGYV0J9MPX534Z'],
        'relativeStrike': [0, 0, 0, 0, 0, 0, 0, 0, 0],
        'strikeReference': ['delta', 'delta', 'delta', 'delta', 'delta', 'delta', 'delta', 'delta', 'delta'],
        'tenor': ['3m', '3m', '3m', '3m', '3m', '3m', '3m', '3m', '3m'],
        'date': [pd.Timestamp(2020, 9, 2), pd.Timestamp(2020, 9, 3), pd.Timestamp(2020, 9, 4),
                 pd.Timestamp(2020, 9, 2), pd.Timestamp(2020, 9, 3), pd.Timestamp(2020, 9, 4),
                 pd.Timestamp(2020, 9, 2), pd.Timestamp(2020, 9, 3), pd.Timestamp(2020, 9, 4)]
    })
    d = d.set_index('date')
    df = MarketDataResponseFrame(d)
    df.dataset_ids = _test_datasets
    return df


def mock_fx_forecast(_cls, _q, ignore_errors=False):
    d = {
        'fxForecast': [1.1, 1.1, 1.1]
    }
    df = MarketDataResponseFrame(data=d, index=_index * 3)
    df.dataset_ids = _test_datasets
    return df


def mock_fx_delta(_cls, _q, ignore_errors=False):
    d = {
        'relativeStrike': [25, -25, 0],
        'impliedVolatility': [1, 5, 2],
        'forecast': [1.1, 1.1, 1.1],
        'forwardPoint': [1, 1.1, 1.2]
    }
    df = MarketDataResponseFrame(data=d, index=_index * 3)
    df.dataset_ids = _test_datasets
    return df


def mock_fx_empty(_cls, _q, ignore_errors=False):
    d = {
        'strikeReference': [],
        'relativeStrike': [],
        'impliedVolatility': []
    }
    df = MarketDataResponseFrame(data=d, index=[])
    df.dataset_ids = _test_datasets
    return df


def mock_fx_switch(_cls, _q, _n, ignore_errors=False):
    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_fx_empty)
    replace.restore()
    return Cross('MA1889', 'ABC/XYZ')


def mock_curr(_cls, _q, ignore_errors=False):
    d = {
        'swapAnnuity': [1, 2, 3],
        'swapRate': [1, 2, 3],
        'basisSwapRate': [1, 2, 3],
        'swaptionVol': [1, 2, 3],
        'atmFwdRate': [1, 2, 3],
        'midcurveVol': [1, 2, 3],
        'capFloorVol': [1, 2, 3],
        'spreadOptionVol': [1, 2, 3],
        'inflationSwapRate': [1, 2, 3],
        'midcurveAtmFwdRate': [1, 2, 3],
        'capFloorAtmFwdRate': [1, 2, 3],
        'spreadOptionAtmFwdRate': [1, 2, 3],
        'strike': [0.25, 0.5, 0.75]
    }
    df = MarketDataResponseFrame(data=d, index=_index * 3)
    df.dataset_ids = _test_datasets
    return df


def mock_cross(_cls, _q, ignore_errors=False):
    d = {
        'basis': [1, 2, 3],
    }
    df = MarketDataResponseFrame(data=d, index=_index * 3)
    df.dataset_ids = _test_datasets
    return df


def mock_eq(_cls, _q, ignore_errors=False):
    d = {
        'relativeStrike': [0.75, 0.25, 0.5],
        'impliedVolatility': [5, 1, 2],
        'impliedCorrelation': [5, 1, 2],
        'realizedCorrelation': [3.14, 2.71828, 1.44],
        'averageImpliedVolatility': [5, 1, 2],
        'averageImpliedVariance': [5, 1, 2],
        'averageRealizedVolatility': [5, 1, 2],
        'impliedVolatilityByDeltaStrike': [5, 1, 2],
        'optionPremium': [5, 1, 2],
        'absoluteStrike': [5, 1, 2],
        'fundamentalMetric': [5, 1, 2]
    }
    df = MarketDataResponseFrame(data=d, index=_index * 3)
    df.dataset_ids = _test_datasets
    return df


def mock_eq_vol(_cls, asset_ids=None, query_type=None, where=None, source=None, real_time=None, request_id=None):
    d = {
        'impliedVolatility': [5, 1, 2],
    }
    if DataContext.current.end_date >= datetime.date.today():
        end = datetime.datetime.now(pytz.UTC).date()
        periods = 4
        d['impliedVolatility'].append(3)
    else:
        end = datetime.datetime.now(pytz.UTC).date() - datetime.timedelta(days=1)
        periods = 3
    df = MarketDataResponseFrame(data=d, index=pd.date_range(end=end, periods=periods, freq='D'))
    df.dataset_ids = _test_datasets
    df.index.freq = None
    return df


def mock_eq_vol_last_empty(_cls, asset_ids=None, query_type=None, where=None, source=None, real_time=None,
                           request_id=None):
    d = {
        'impliedVolatility': [5, 1, 2],
    }
    end = datetime.date.today() - datetime.timedelta(days=1)
    df = MarketDataResponseFrame(data=d, index=pd.date_range(end=end, periods=3, freq='D'))
    df.dataset_ids = _test_datasets
    return df


def mock_eq_norm(_cls, _q, ignore_errors=False):
    d = {
        'relativeStrike': [-4.0, 4.0, 0],
        'impliedVolatility': [5, 1, 2]
    }
    df = MarketDataResponseFrame(data=d, index=_index * 3)
    df.dataset_ids = _test_datasets
    return df


def mock_eq_spot(_cls, _q, ignore_errors=False):
    d = {
        'relativeStrike': [0.75, 1.25, 1.0],
        'impliedVolatility': [5, 1, 2]
    }
    df = MarketDataResponseFrame(data=d, index=_index * 3)
    df.dataset_ids = _test_datasets
    return df


def mock_inc(_cls, _q, ignore_errors=False):
    d = {
        'relativeStrike': [0.25, 0.75],
        'impliedVolatility': [5, 1]
    }
    df = MarketDataResponseFrame(data=d, index=_index * 2)
    df.dataset_ids = _test_datasets
    return df


def mock_esg(_cls, _q, ignore_errors=False):
    d = {
        "esNumericScore": [2, 4, 6],
        "esNumericPercentile": [81.2, 75.4, 65.7],
        "esPolicyScore": [2, 4, 6],
        "esPolicyPercentile": [81.2, 75.4, 65.7],
        "esScore": [2, 4, 6],
        "esPercentile": [81.2, 75.4, 65.7],
        "esProductImpactScore": [2, 4, 6],
        "esProductImpactPercentile": [81.2, 75.4, 65.7],
        "gScore": [2, 4, 6],
        "gPercentile": [81.2, 75.4, 65.7],
        "esMomentumScore": [2, 4, 6],
        "esMomentumPercentile": [81.2, 75.4, 65.7],
        "gRegionalScore": [2, 4, 6],
        "gRegionalPercentile": [81.2, 75.4, 65.7],
        "controversyScore": [2, 4, 6],
        "controversyPercentile": [81.2, 75.4, 65.7],
        "esDisclosurePercentage": [49.2, 55.7, 98.4]
    }
    df = MarketDataResponseFrame(data=d, index=_index * 3)
    df.dataset_ids = _test_datasets
    return df


def mock_index_positions_data(
        asset_id,
        start_date,
        end_date,
        fields=None,
        position_type=None
):
    return [
        {'underlyingAssetId': 'MA3',
         'netWeight': 0.1,
         'positionType': 'close',
         'assetId': 'MA890',
         'positionDate': '2020-01-01'
         },
        {'underlyingAssetId': 'MA1',
         'netWeight': 0.6,
         'positionType': 'close',
         'assetId': 'MA890',
         'positionDate': '2020-01-01'
         },
        {'underlyingAssetId': 'MA2',
         'netWeight': 0.3,
         'positionType': 'close',
         'assetId': 'MA890',
         'positionDate': '2020-01-01'
         }
    ]


def mock_rating(_cls, _q, ignore_errors=False):
    d = {
        'rating': ['Buy', 'Sell', 'Buy', 'Neutral'],
        'convictionList': [1, 0, 0, 0]
    }
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2020, 8, 13), datetime.date(2020, 8, 14),
                                                               datetime.date(2020, 8, 17), datetime.date(2020, 8, 18)]))
    df.dataset_ids = _test_datasets
    return df


def mock_gsdeer_gsfeer(_cls, assetId, start_date):
    d = {
        'gsdeer': [1, 1.2, 1.1],
        'gsfeer': [2, 1.8, 1.9],
        'year': [2000, 2010, 2020],
        'quarter': ['Q1', 'Q2', 'Q3']
    }
    df = MarketDataResponseFrame(data=d, index=_index * 3)
    return df


def mock_factor_profile(_cls, _q, ignore_errors=False):
    d = {
        'growthScore': [0.238, 0.234, 0.234, 0.230],
        'financialReturnsScore': [0.982, 0.982, 0.982, 0.982],
        'multipleScore': [0.204, 0.192, 0.190, 0.190],
        'integratedScore': [0.672, 0.676, 0.676, 0.674]
    }
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2020, 8, 13), datetime.date(2020, 8, 14),
                                                               datetime.date(2020, 8, 17), datetime.date(2020, 8, 18)]))
    df.dataset_ids = _test_datasets
    return df


def mock_commodity_forecast(_cls, _q, ignore_errors=False):
    d = {
        'forecastPeriod': ['3m', '3m', '3m', '3m'],
        'forecastType': ['spotReturn', 'spotReturn', 'spotReturn', 'spotReturn'],
        'commodityForecast': [1700, 1400, 1500, 1600]
    }
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2020, 8, 13), datetime.date(2020, 8, 14),
                                                               datetime.date(2020, 8, 17), datetime.date(2020, 8, 18)]))
    df.dataset_ids = _test_datasets
    return df


def mock_cds_spread(_cls, _q, ignore_errors=False):
    d = {
        "spreadAt100": [0.000836],
        "spreadAt250": [0.000436],
        "spreadAt500": [0.00036],
    }
    df = MarketDataResponseFrame(d, index=_index2)
    return df


def test_skew():
    replace = Replacer()

    mock_spx = Index('MA890', AssetClass.Equity, 'SPX')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    actual = tm.skew(mock_spx, '1m', tm.SkewReference.DELTA, 25)
    assert_series_equal(pd.Series([2.0], index=_index, name='impliedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    actual = tm.skew(mock_spx, '1m', tm.SkewReference.DELTA, 25, tm.NormalizationMode.NORMALIZED)
    assert_series_equal(pd.Series([2.0], index=_index, name='impliedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    actual = tm.skew(mock_spx, '1m', tm.SkewReference.DELTA, 25, tm.NormalizationMode.OUTRIGHT)
    assert_series_equal(pd.Series([4], index=_index, name='impliedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq_norm)
    actual = tm.skew(mock_spx, '1m', tm.SkewReference.NORMALIZED, 4)
    assert_series_equal(pd.Series([2.0], index=_index, name='impliedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq_spot)
    actual = tm.skew(mock_spx, '1m', tm.SkewReference.SPOT, 25)
    assert_series_equal(pd.Series([2.0], index=_index, name='impliedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    mock.return_value = mock_empty_market_data_response()
    actual = tm.skew(mock_spx, '1m', tm.SkewReference.SPOT, 25)
    assert actual.empty

    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_inc)
    with pytest.raises(MqError):
        tm.skew(mock_spx, '1m', tm.SkewReference.DELTA, 25)
    replace.restore()

    with pytest.raises(MqError):
        tm.skew(mock_spx, '1m', None, 25)


def test_skew_fx():
    replace = Replacer()
    cross = Cross('MAA0NE9QX2ABETG6', 'USD/EUR')
    xrefs = replace('gs_quant.timeseries.measures.GsAssetApi.get_asset_xrefs', Mock())
    xrefs.return_value = [GsTemporalXRef(dt.date(2019, 1, 1), dt.date(2952, 12, 31), XRef(bbid='EURUSD', ))]
    replace('gs_quant.markets.securities.SecurityMaster.get_asset', Mock()).return_value = cross
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_fx_delta)
    mock = cross

    actual = tm.skew(mock, '1m', tm.SkewReference.DELTA, 25)
    assert_series_equal(pd.Series([4], index=_index, name='impliedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    actual = tm.skew(mock, '1m', tm.SkewReference.DELTA, 25, tm.NormalizationMode.NORMALIZED)
    assert_series_equal(pd.Series([2.0], index=_index, name='impliedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    actual = tm.skew(mock, '1m', tm.SkewReference.DELTA, 25, tm.NormalizationMode.OUTRIGHT)
    assert_series_equal(pd.Series([4], index=_index, name='impliedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    with pytest.raises(MqError):
        tm.skew(mock, '1m', tm.SkewReference.DELTA, 25, real_time=True)
    with pytest.raises(MqError):
        tm.skew(mock, '1m', tm.SkewReference.SPOT, 25)
    with pytest.raises(MqError):
        tm.skew(mock, '1m', tm.SkewReference.FORWARD, 25)
    with pytest.raises(MqError):
        tm.skew(mock, '1m', tm.SkewReference.NORMALIZED, 25)
    with pytest.raises(MqError):
        tm.skew(mock, '1m', None, 25)

    replace.restore()


def test_implied_vol():
    replace = Replacer()
    mock_spx = Index('MA890', AssetClass.Equity, 'SPX')
    replace('gs_quant.timeseries.measures.get_historical_and_last_for_measure', mock_eq_vol)
    idx = pd.date_range(end=datetime.datetime.now(pytz.UTC).date(), periods=4, freq='D')
    idx.freq = None

    actual = tm.implied_volatility(mock_spx, '1m', tm.VolReference.DELTA_CALL, 25)
    assert_series_equal(pd.Series([5, 1, 2, 3], index=idx, name='impliedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.implied_volatility(mock_spx, '1m', tm.VolReference.DELTA_PUT, 75)
    assert_series_equal(pd.Series([5, 1, 2, 3], index=idx, name='impliedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(MqError):
        tm.implied_volatility(mock_spx, '1m', tm.VolReference.DELTA_NEUTRAL)
    with pytest.raises(MqError):
        tm.implied_volatility(mock_spx, '1m', tm.VolReference.DELTA_CALL)
    with DataContext(datetime.date(2020, 1, 11), datetime.date(2021, 1, 11)):
        actual = tm.implied_volatility(mock_spx, '1m', tm.VolReference.DELTA_CALL, 25)
        actual.index.freq = None
        assert_series_equal(pd.Series([5, 1, 2], index=idx[:-1], name='impliedVolatility'), pd.Series(actual))
    replace.restore()


def test_merge_dataframes():
    a = pd.DataFrame({'a': [1, 2, 3]})
    b = pd.DataFrame({'a': [1, 5, 6]})
    c = tm.merge_dataframes([a, b])
    assert not len(c.dataset_ids)
    assert len(c) == 5
    c = tm.merge_dataframes([a, None])
    assert_series_equal(a['a'], c['a'])
    assert tm.merge_dataframes(None).empty


def test_get_last_for_measure():
    replace = Replacer()
    mock_market = replace('gs_quant.timeseries.measures._market_data_timed', Mock())
    mock_market.return_value = pd.DataFrame([1], index=pd.date_range('2020-01-01', periods=1, freq='D', tz=pytz.UTC))

    a = tm.get_last_for_measure(['blah'], QueryType.IMPLIED_VOLATILITY, {})
    assert len(a) == 1

    mock_market.return_value = pd.DataFrame()
    a = tm.get_last_for_measure(['blah'], QueryType.IMPLIED_VOLATILITY, {})
    assert a is None

    mock_market.side_effect = [MqValueError()]
    a = tm.get_last_for_measure(['blah'], QueryType.IMPLIED_VOLATILITY, {})
    assert a is None
    replace.restore()


def test_ignore_errors():
    with mock.patch.object(GsSession.current, '_post') as mocker:
        mocker.return_value = {
            'requestId': 'rq1234',
            'responses': [
                {
                    'queryResponse': [
                        {
                            'dataSetIds': ['DS1'],
                            'errorMessages': ['this failed']
                        }
                    ]
                }
            ]
        }
        last_data = tm.get_last_for_measure(['blah'], QueryType.IMPLIED_VOLATILITY, {}, ignore_errors=True)
        assert last_data is None


def test_tenor_month_to_year():
    assert tm._tenor_month_to_year('1y') == '1y'
    assert tm._tenor_month_to_year('11m') == '11m'
    assert tm._tenor_month_to_year('24m') == '2y'


def test_implied_vol_no_last():
    replace = Replacer()
    mock_spx = Index('MA890', AssetClass.Equity, 'SPX')
    idx = pd.date_range(end=datetime.date.today() - datetime.timedelta(days=1), periods=3, freq='D')

    replace('gs_quant.timeseries.measures.get_historical_and_last_for_measure', mock_eq_vol_last_empty)
    actual = tm.implied_volatility(mock_spx, '1m', tm.VolReference.DELTA_CALL, 25)
    assert_series_equal(pd.Series([5, 1, 2], index=idx, name='impliedVolatility'), pd.Series(actual))
    actual = tm.implied_volatility(mock_spx, '1m', tm.VolReference.DELTA_PUT, 75)
    assert_series_equal(pd.Series([5, 1, 2], index=idx, name='impliedVolatility'), pd.Series(actual))
    replace.restore()


def test_implied_vol_fx():
    replace = Replacer()

    mock = Cross('MAA0NE9QX2ABETG6', 'USD/EUR')
    xrefs = replace('gs_quant.timeseries.measures.GsAssetApi.get_asset_xrefs', Mock())
    xrefs.return_value = [GsTemporalXRef(dt.date(2019, 1, 1), dt.date(2952, 12, 31), XRef(bbid='EURUSD', ))]
    replace('gs_quant.markets.securities.SecurityMaster.get_asset', Mock()).return_value = mock

    # for different delta strikes
    replace('gs_quant.timeseries.measures.get_historical_and_last_for_measure', mock_fx_vol)
    actual = tm.implied_volatility(mock, '1m', tm.VolReference.DELTA_CALL, 25)
    expected = pd.Series([5, 1, 2, 3], index=pd.date_range('2019-01-01', periods=4, freq='D'), name='impliedVolatility')
    expected.index.freq = None
    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.implied_volatility(mock, '1m', tm.VolReference.DELTA_PUT, 25)
    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.implied_volatility(mock, '1m', tm.VolReference.DELTA_NEUTRAL)
    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.implied_volatility(mock, '1m', tm.VolReference.FORWARD, 100)
    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.implied_volatility(mock, '1m', tm.VolReference.SPOT, 100)
    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    # NORMALIZED not supported
    with pytest.raises(MqError):
        tm.implied_volatility(mock, '1m', tm.VolReference.DELTA_CALL)
    with pytest.raises(MqError):
        tm.implied_volatility(mock, '1m', tm.VolReference.NORMALIZED, 25)
    with pytest.raises(MqError):
        tm.implied_volatility(mock, '1m', tm.VolReference.SPOT, 25)
    with pytest.raises(MqError):
        tm.implied_volatility(mock, '1m', tm.VolReference.FORWARD, 25)
    replace.restore()


def test_fx_forecast():
    replace = Replacer()
    mock = Cross('MAA0NE9QX2ABETG6', 'USD/EUR')
    xrefs = replace('gs_quant.timeseries.measures.GsAssetApi.get_asset_xrefs', Mock())
    xrefs.return_value = [GsTemporalXRef(dt.date(2019, 1, 1), dt.date(2952, 12, 31), XRef(bbid='EURUSD', ))]
    replace('gs_quant.markets.securities.SecurityMaster.get_asset', Mock()).return_value = mock
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_fx_forecast)

    actual = tm.fx_forecast(mock, '12m')
    assert_series_equal(pd.Series([1.1, 1.1, 1.1], index=_index * 3, name='fxForecast'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.fx_forecast(mock, '3m')
    assert_series_equal(pd.Series([1.1, 1.1, 1.1], index=_index * 3, name='fxForecast'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.fx_forecast(mock, '3m', real_time=True)
    replace.restore()


def test_fx_forecast_inverse():
    replace = Replacer()
    get_cross = replace('gs_quant.timeseries.measures.cross_to_usd_based_cross', Mock())
    get_cross.return_value = "MATGYV0J9MPX534Z"
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_fx_forecast)

    mock = Cross("MAYJPCVVF2RWXCES", 'USD/JPY')
    actual = tm.fx_forecast(mock, '3m')
    assert_series_equal(pd.Series([1 / 1.1, 1 / 1.1, 1 / 1.1], index=_index * 3, name='fxForecast'),
                        pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    replace.restore()


def test_vol_smile():
    replace = Replacer()
    mock_spx = Index('MA890', AssetClass.Equity, 'SPX')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    actual = tm.vol_smile(mock_spx, '1m', tm.VolSmileReference.FORWARD, '5d')
    assert_series_equal(pd.Series([5, 1, 2], index=[0.75, 0.25, 0.5], name='vol_smile'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    assert actual.name == 'vol_smile'
    actual = tm.vol_smile(mock_spx, '1m', tm.VolSmileReference.SPOT, '5d')
    assert_series_equal(pd.Series([5, 1, 2], index=[0.75, 0.25, 0.5], name='vol_smile'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    assert actual.name == 'vol_smile'

    market_mock = replace('gs_quant.timeseries.measures.get_df_with_retries', Mock())
    market_mock.return_value = mock_empty_market_data_response()
    actual = tm.vol_smile(mock_spx, '1m', tm.VolSmileReference.SPOT, '1d')
    assert actual.empty
    assert actual.dataset_ids == ()
    market_mock.assert_called_once()
    with pytest.raises(NotImplementedError):
        tm.vol_smile(mock_spx, '1m', tm.VolSmileReference.SPOT, '1d', real_time=True)
    replace.restore()


def test_impl_corr():
    replace = Replacer()
    mock_spx = Index('MA890', AssetClass.Equity, 'SPX')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    actual = tm.implied_correlation(mock_spx, '1m', tm.EdrDataReference.DELTA_CALL, 25)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='impliedCorrelation'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.implied_correlation(mock_spx, '1m', tm.EdrDataReference.DELTA_PUT, 75)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='impliedCorrelation'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.implied_correlation(..., '1m', tm.EdrDataReference.DELTA_PUT, 75, real_time=True)
    with pytest.raises(MqError):
        tm.implied_correlation(..., '1m', tm.EdrDataReference.DELTA_CALL, 50, '')
    replace.restore()


def test_impl_corr_n(mocker):
    spx = Index('MA4B66MW5E27U8P32SB', AssetClass.Equity, 'SPX')
    with pytest.raises(MqValueError):
        tm.implied_correlation(spx, '1m', tm.EdrDataReference.DELTA_CALL, 0.5,
                               composition_date=datetime.date.today())
    with pytest.raises(MqValueError):
        tm.implied_correlation(spx, '1m', tm.EdrDataReference.DELTA_CALL, 0.5, 200)

    resources = os.path.join(os.path.dirname(__file__), '..', 'resources')
    i_vol = pd.read_csv(os.path.join(resources, 'SPX_50_icorr_in.csv'))
    i_vol.index = pd.to_datetime(i_vol['date'])
    weights = pd.read_csv(os.path.join(resources, 'SPX_50_weights.csv'))
    weights.set_index('underlyingAssetId', inplace=True)

    replace = Replacer()
    market_data = replace('gs_quant.timeseries.econometrics.GsDataApi.get_market_data', Mock())
    market_data.return_value = i_vol
    constituents = replace('gs_quant.timeseries.measures._get_index_constituent_weights', Mock())
    constituents.return_value = weights
    last_mock = replace('gs_quant.timeseries.measures.get_last_for_measure', Mock())
    last_mock.return_value = None

    mocker.patch.object(GsSession.__class__, 'default_value',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(OAuth2Session, '_authenticate', return_value=None)

    expected = pd.read_csv(os.path.join(resources, 'SPX_50_icorr_out.csv'))
    expected.index = pd.to_datetime(expected['date'])
    expected = expected['value']
    actual = tm.implied_correlation(spx, '1m', tm.EdrDataReference.DELTA_CALL, 0.5, 50, datetime.date(2020, 8, 31),
                                    source='PlotTool')
    expected = ExtendedSeries(expected)
    assert_series_equal(actual, expected, check_names=False)

    mask = i_vol.index < '2020-08-31'
    assert not all(mask)
    market_data.return_value = i_vol[mask]
    last_mock.return_value = i_vol[~mask]
    actual = tm.implied_correlation(spx, '1m', tm.EdrDataReference.DELTA_CALL, 0.5, 50, datetime.date(2020, 8, 31),
                                    source='PlotTool')
    assert_series_equal(actual, expected, check_names=False)

    market_data.return_value = pd.DataFrame()
    last_mock.return_value = pd.DataFrame()
    actual = tm.implied_correlation(spx, '1m', tm.EdrDataReference.DELTA_CALL, 0.5, 50, datetime.date(2020, 8, 31),
                                    source='PlotTool')
    assert actual.empty

    replace.restore()


def test_implied_corr_basket():
    replace = Replacer()

    dates = pd.DatetimeIndex([dt.date(2021, 1, 1), dt.date(2021, 1, 2), dt.date(2021, 1, 3), dt.date(2021, 1, 4),
                              dt.date(2021, 1, 5), dt.date(2021, 1, 6)])
    x = pd.DataFrame({'impliedVolatility': [0.1, 0.11, 0.12, 0.13, 0.14, 0.15]}, index=dates)
    x['assetId'] = 'MA4B66MW5E27U9VBB94'
    y = pd.DataFrame({'impliedVolatility': [0.2, 0.21, 0.22, 0.23, 0.24, 0.25]}, index=dates)
    y['assetId'] = 'MA4B66MW5E27UAL9SUX'
    z = pd.DataFrame({'impliedVolatility': [0.13, 0.21, 0.3, 0.31, 0.23, 0.24]}, index=dates)
    z['assetId'] = 'MA4B66MW5E27U8P32SB'
    implied_vol = x.append(y).append(z)

    x = pd.DataFrame({'spot': [100.0, 101, 103.02, 100.9596, 100.9596, 102.978792]}, index=dates)
    x['assetId'] = 'MA4B66MW5E27U9VBB94'
    y = pd.DataFrame({'spot': [100.0, 100, 100, 100, 100, 100]}, index=dates)
    y['assetId'] = 'MA4B66MW5E27UAL9SUX'
    spot_data = x.append(y)

    mock_data = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    mock_data.side_effect = [implied_vol, spot_data]

    mock_asset = replace('gs_quant.timeseries.backtesting.GsAssetApi.get_many_assets_data', Mock())
    mock_asset.return_value = [{'id': 'MA4B66MW5E27U9VBB94', 'bbid': 'AAPL UW'},
                               {'id': 'MA4B66MW5E27UAL9SUX', 'bbid': 'MSFT UW'}]

    spx = Index('MA4B66MW5E27U8P32SB', AssetClass.Equity, 'SPX')
    a_basket = tm.Basket(['AAPL UW', 'MSFT UW'], [0.1, 0.9])
    mock_data = replace('gs_quant.timeseries.get_historical_and_last_for_measure', Mock())
    mock_data.return_value = spot_data
    actual = tm.implied_correlation_with_basket(spx, '1y', tm.EdrDataReference.DELTA_PUT, 25, a_basket)
    expected = pd.Series([-433.33333, 198.60510, 1065.90909, 986.28763, 100.0, 100.0], index=dates)
    expected = ExtendedSeries(expected)
    assert_series_equal(actual, expected)

    with pytest.raises(NotImplementedError):
        tm.implied_correlation_with_basket(spx, '1y', tm.EdrDataReference.DELTA_PUT, 25, a_basket, real_time=True)

    replace.restore()


def test_realized_corr_basket():
    replace = Replacer()

    dates = pd.DatetimeIndex([dt.date(2021, 1, 1), dt.date(2021, 1, 2), dt.date(2021, 1, 3), dt.date(2021, 1, 4),
                              dt.date(2021, 1, 5), dt.date(2021, 1, 6)])
    x = pd.DataFrame({'spot': [100.0, 101, 103.02, 100.9596, 100.9596, 102.978792]}, index=dates)
    x['assetId'] = 'MA4B66MW5E27U9VBB94'
    y = pd.DataFrame({'spot': [100.0, 99.5, 100.1, 101, 100.7, 100.6]}, index=dates)
    y['assetId'] = 'MA4B66MW5E27UAL9SUX'
    constituents_spot = x.append(y)

    index_spot = pd.DataFrame({'spot': [100.0, 101, 102, 103, 103.3, 104]}, index=dates)
    index_spot['assetId'] = 'MA4B66MW5E27U8P32SB'

    mock_data = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    mock_data.side_effect = [index_spot, constituents_spot]

    mock_asset = replace('gs_quant.timeseries.backtesting.GsAssetApi.get_many_assets_data', Mock())
    mock_asset.return_value = [{'id': 'MA4B66MW5E27U9VBB94', 'bbid': 'AAPL UW'},
                               {'id': 'MA4B66MW5E27UAL9SUX', 'bbid': 'MSFT UW'}]
    mock_data = replace('gs_quant.timeseries.get_historical_and_last_for_measure', Mock())
    mock_data.return_value = constituents_spot

    spx = Index('MA4B66MW5E27U8P32SB', AssetClass.Equity, 'SPX')
    a_basket = tm.Basket(['AAPL UW', 'MSFT UW'], [0.1, 0.9])
    actual = tm.realized_correlation_with_basket(spx, '2d', a_basket)
    expected = pd.Series([np.nan, np.nan, -501.344109, -108.318770, -168.132382, 109.044958], index=dates)
    expected = ExtendedSeries(expected)
    assert_series_equal(actual, expected)

    with pytest.raises(NotImplementedError):
        tm.realized_correlation_with_basket(spx, '2d', a_basket, real_time=True)

    replace.restore()


def test_real_corr():
    spx = Index('MA4B66MW5E27U8P32SB', AssetClass.Equity, 'SPX')
    with pytest.raises(NotImplementedError):
        tm.realized_correlation(spx, '1m', real_time=True)

    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    actual = tm.realized_correlation(spx, '1m')
    assert_series_equal(pd.Series([3.14, 2.71828, 1.44], index=_index * 3), pd.Series(actual), check_names=False)
    assert actual.dataset_ids == _test_datasets
    replace.restore()


def test_real_corr_missing():
    spx = Index('MA4B66MW5E27U8P32SB', AssetClass.Equity, 'SPX')
    d = {
        'assetId': ['MA4B66MW5E27U8P32SB'] * 3,
        'spot': [3000, 3100, 3050],
    }
    df = MarketDataResponseFrame(data=d, index=pd.date_range('2020-08-01', periods=3, freq='D'))

    resources = os.path.join(os.path.dirname(__file__), '..', 'resources')
    weights = pd.read_csv(os.path.join(resources, 'SPX_50_weights.csv'))
    weights.set_index('underlyingAssetId', inplace=True)

    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', lambda *args, **kwargs: df)
    constituents = replace('gs_quant.timeseries.measures._get_index_constituent_weights', Mock())
    constituents.return_value = weights
    last_mock = replace('gs_quant.timeseries.measures.get_last_for_measure', Mock())
    last_mock.return_value = None

    with pytest.raises(MqValueError):
        tm.realized_correlation(spx, '1m', 50)
    replace.restore()


def test_real_corr_n():
    spx = Index('MA4B66MW5E27U8P32SB', AssetClass.Equity, 'SPX')
    with pytest.raises(MqValueError):
        tm.realized_correlation(spx, '1m', composition_date=datetime.date.today())
    with pytest.raises(MqValueError):
        tm.realized_correlation(spx, '1m', 200)

    mock_asset = GsAsset(asset_class='Equity', id='MA1234567890', type_='Custom Basket', name='test')
    mock_basket = CustomBasket(gs_asset=mock_asset, _finish_init=False)

    # Test baskets must have top n
    with pytest.raises(MqValueError):
        tm.realized_correlation(mock_basket, '1m', composition_date=datetime.date.today())

    mock_asset = GsAsset(asset_class='Equity', id='MA1234567890', type_='Custom Basket', name='test')
    mock_basket = CustomBasket(gs_asset=mock_asset, _finish_init=False)

    # Test baskets must have top n
    with pytest.raises(MqValueError):
        tm.realized_correlation(mock_basket, '1m', composition_date=None)

    resources = os.path.join(os.path.dirname(__file__), '..', 'resources')
    r_vol = pd.read_csv(os.path.join(resources, 'SPX_50_rcorr_in.csv'))
    r_vol.index = pd.to_datetime(r_vol['date'])
    weights = pd.read_csv(os.path.join(resources, 'SPX_50_weights.csv'))
    weights.set_index('underlyingAssetId', inplace=True)

    replace = Replacer()
    market_data = replace('gs_quant.timeseries.econometrics.GsDataApi.get_market_data', Mock())
    market_data.return_value = r_vol
    constituents = replace('gs_quant.timeseries.measures._get_index_constituent_weights', Mock())
    constituents.return_value = weights
    last_mock = replace('gs_quant.timeseries.measures.get_last_for_measure', Mock())
    last_mock.return_value = None

    expected = pd.read_csv(os.path.join(resources, 'SPX_50_rcorr_out.csv'))
    expected.index = pd.to_datetime(expected['date'])
    expected = ExtendedSeries(expected['value'])
    actual = tm.realized_correlation(spx, '1m', 50, datetime.date(2020, 8, 31), source='PlotTool')
    assert_series_equal(actual, expected, check_names=False)

    mask = r_vol.index < '2020-08-31'
    assert not all(mask)
    market_data.return_value = r_vol[mask]
    last_mock.return_value = r_vol[~mask]
    actual = tm.realized_correlation(spx, '1m', 50, datetime.date(2020, 8, 31), source='PlotTool')
    assert_series_equal(actual, expected, check_names=False)

    replace.restore()


def test_cds_implied_vol():
    replace = Replacer()
    mock_cds = Index('MA890', AssetClass.Equity, 'CDS')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    actual = tm.cds_implied_volatility(mock_cds, '1m', '5y', tm.CdsVolReference.DELTA_CALL, 10)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='impliedVolatilityByDeltaStrike'),
                        pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.cds_implied_volatility(mock_cds, '1m', '5y', tm.CdsVolReference.FORWARD, 100)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='impliedVolatilityByDeltaStrike'),
                        pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.cds_implied_volatility(..., '1m', '5y', tm.CdsVolReference.DELTA_PUT, 75, real_time=True)
    replace.restore()


def test_implied_vol_credit():
    replace = Replacer()
    mock_cds = Index('MA890', AssetClass.Equity, 'CDS')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    actual = tm.implied_volatility_credit(mock_cds, '1m', tm.CdsVolReference.DELTA_CALL, 10)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='impliedVolatilityByDeltaStrike'),
                        pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.implied_volatility_credit(mock_cds, '1m', tm.CdsVolReference.DELTA_PUT, 10)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='impliedVolatilityByDeltaStrike'),
                        pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.implied_volatility_credit(mock_cds, '1m', tm.CdsVolReference.FORWARD, 100)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='impliedVolatilityByDeltaStrike'),
                        pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.implied_volatility_credit(..., '1m', tm.CdsVolReference.DELTA_PUT, 75, real_time=True)
    with pytest.raises(NotImplementedError):
        tm.implied_volatility_credit(..., '1m', "", 75)
    replace.restore()


def test_absolute_strike_credit():
    replace = Replacer()
    mock_cds = Index('MA890', AssetClass.Equity, 'CDS')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    actual = tm.absolute_strike_credit(mock_cds, '1m', tm.CdsVolReference.DELTA_CALL, 10)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='absoluteStrike'),
                        pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.absolute_strike_credit(mock_cds, '1m', tm.CdsVolReference.DELTA_PUT, 10)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='absoluteStrike'),
                        pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.absolute_strike_credit(mock_cds, '1m', tm.CdsVolReference.FORWARD, 100)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='absoluteStrike'),
                        pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.absolute_strike_credit(..., '1m', tm.CdsVolReference.DELTA_PUT, 75, real_time=True)
    with pytest.raises(NotImplementedError):
        tm.absolute_strike_credit(..., '1m', "", 75)
    replace.restore()


def test_option_premium_credit():
    replace = Replacer()
    mock_cds = Index('MA890', AssetClass.Equity, 'CDS')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    actual = tm.option_premium_credit(mock_cds, '1m', tm.CdsVolReference.DELTA_CALL, 10)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='optionPremium'),
                        pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.option_premium_credit(mock_cds, '1m', tm.CdsVolReference.DELTA_PUT, 10)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='optionPremium'),
                        pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    actual = tm.option_premium_credit(mock_cds, '1m', tm.CdsVolReference.FORWARD, 100)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='optionPremium'),
                        pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.option_premium_credit(..., '1m', tm.CdsVolReference.DELTA_PUT, 75, real_time=True)
    with pytest.raises(NotImplementedError):
        tm.option_premium_credit(..., '1m', "", 75)
    replace.restore()


def test_cds_spreads():
    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_cds_spread)
    cds_asset = DefaultSwap('MAD6V3NP8ZB7HZTY', 'Lockheed_C_PFA_2y_USD')
    actual = tm.cds_spread(cds_asset, 100)
    assert_series_equal(pd.Series([0.000836], index=_index2, name='spreadAt100'), pd.Series(actual))
    actual = tm.cds_spread(cds_asset, 250)
    assert_series_equal(pd.Series([0.000436], index=_index2, name='spreadAt250'), pd.Series(actual))
    actual = tm.cds_spread(cds_asset, 500)
    assert_series_equal(pd.Series([0.00036], index=_index2, name='spreadAt500'), pd.Series(actual))
    with pytest.raises(NotImplementedError):
        tm.cds_spread(cds_asset, 200)
    with pytest.raises(NotImplementedError):
        tm.cds_spread(cds_asset, 200, real_time=True)
    replace.restore()


def test_avg_impl_vol(mocker):
    replace = Replacer()
    mock_spx = Index('MA890', AssetClass.Equity, 'SPX')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    last_mock = replace('gs_quant.timeseries.measures.get_last_for_measure', Mock())
    last_mock.return_value = None

    actual = tm.average_implied_volatility(mock_spx, '1m', tm.EdrDataReference.DELTA_CALL, 25)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='averageImpliedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    actual = tm.average_implied_volatility(mock_spx, '1m', tm.EdrDataReference.DELTA_PUT, 75)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='averageImpliedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    replace.restore()

    df1 = pd.DataFrame(data={'impliedVolatility': [1, 2, 3], 'assetId': ['MA1', 'MA1', 'MA1']},
                       index=pd.date_range(start='2020-01-01', periods=3))
    df2 = pd.DataFrame(data={'impliedVolatility': [2, 3, 4], 'assetId': ['MA2', 'MA2', 'MA2']},
                       index=pd.date_range(start='2020-01-01', periods=3))
    df3 = pd.DataFrame(data={'impliedVolatility': [2, 5], 'assetId': ['MA3', 'MA3']},
                       index=pd.date_range(start='2020-01-01', periods=2))

    replace('gs_quant.api.gs.indices.GsIndexApi.get_positions_data', mock_index_positions_data)
    market_data_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    mock_implied_vol = MarketDataResponseFrame(pd.concat([df1, df2, df3], join='inner'))
    mock_implied_vol.dataset_ids = _test_datasets
    market_data_mock.return_value = mock_implied_vol
    last_mock = replace('gs_quant.timeseries.measures.get_last_for_measure', Mock())
    last_mock.return_value = None

    mocker.patch.object(GsSession.__class__, 'default_value',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    mocker.patch.object(OAuth2Session, '_authenticate', return_value=None)

    actual = tm.average_implied_volatility(mock_spx, '1m', tm.EdrDataReference.DELTA_CALL, 25, 3, '1d')
    expected = pd.Series([1.4, 2.6, 3.33333], index=pd.date_range(start='2020-01-01', periods=3))
    expected.index.freq = None
    assert_series_equal(expected, pd.Series(actual), check_names=False)
    assert actual.dataset_ids == _test_datasets

    last_mock.return_value = pd.DataFrame(data={'impliedVolatility': [2], 'assetId': ['MA3']},
                                          index=pd.date_range(start='2020-01-03', periods=1))
    actual = tm.average_implied_volatility(mock_spx, '1m', tm.EdrDataReference.DELTA_CALL, 25, 3, '1d')
    expected = pd.Series([1.4, 2.6, 3.2], index=pd.date_range(start='2020-01-01', periods=3))
    expected.index.freq = None
    assert_series_equal(expected, pd.Series(actual), check_names=False)

    with pytest.raises(NotImplementedError):
        tm.average_implied_volatility(..., '1m', tm.EdrDataReference.DELTA_PUT, 75, real_time=True)

    with pytest.raises(MqValueError):
        tm.average_implied_volatility(mock_spx, '1m', tm.EdrDataReference.DELTA_PUT, 75, top_n_of_index=None,
                                      composition_date='1d')

    with pytest.raises(NotImplementedError):
        tm.average_implied_volatility(mock_spx, '1m', tm.EdrDataReference.DELTA_PUT, 75, top_n_of_index=101)

    replace.restore()


def test_avg_realized_vol():
    replace = Replacer()

    mock_spx = Index('MA890', AssetClass.Equity, 'SPX')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)

    actual = tm.average_realized_volatility(mock_spx, '1m')
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='averageRealizedVolatility'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    replace.restore()

    df1 = pd.DataFrame(data={'spot': [1, 2, 3], 'assetId': ['MA1', 'MA1', 'MA1']},
                       index=pd.date_range(start='2020-01-01', periods=3))
    df2 = pd.DataFrame(data={'spot': [2, 3, 4], 'assetId': ['MA2', 'MA2', 'MA2']},
                       index=pd.date_range(start='2020-01-01', periods=3))
    df3 = pd.DataFrame(data={'spot': [2, 2, 2], 'assetId': ['MA3', 'MA3', 'MA3']},
                       index=pd.date_range(start='2020-01-01', periods=3))
    mock_spot = MarketDataResponseFrame(pd.concat([df1, df2, df3], join='inner'))
    mock_spot.dataset_ids = _test_datasets

    replace('gs_quant.api.gs.indices.GsIndexApi.get_positions_data', mock_index_positions_data)
    last_mock = replace('gs_quant.timeseries.measures.get_last_for_measure', Mock())
    last_mock.return_value = None
    market_data_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_data_mock.return_value = mock_spot

    actual = tm.average_realized_volatility(mock_spx, '2d', Returns.SIMPLE, 3, '1d')
    expected = pd.Series([392.874026], index=pd.date_range(start='2020-01-03', periods=1),
                         name='averageRealizedVolatility')
    expected.index.freq = None
    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    dfl = pd.DataFrame(data={'spot': [5, 5, 5], 'assetId': ['MA1', 'MA2', 'MA3']},
                       index=[pd.to_datetime('2020-01-04')] * 3)
    last_mock.return_value = dfl
    actual = tm.average_realized_volatility(mock_spx, '2d', Returns.SIMPLE, 3, '1d')
    expected = pd.Series([392.874026, 308.686734], index=pd.to_datetime(['2020-01-03', '2020-01-04']),
                         name='averageRealizedVolatility')
    assert_series_equal(expected, pd.Series(actual))

    last_mock.return_value = None
    df4 = pd.DataFrame(data={'spot': [2, 2], 'assetId': ['MA3', 'MA3']},
                       index=pd.date_range(start='2020-01-01', periods=2))
    mock_spot_2 = MarketDataResponseFrame(pd.concat([df1, df2, df4], join='inner'))
    market_data_mock.return_value = mock_spot_2
    actual = tm.average_realized_volatility(mock_spx, '2d', Returns.SIMPLE, 3, '1d')
    assert actual.dropna().empty

    with pytest.raises(NotImplementedError):
        tm.average_realized_volatility(mock_spx, '1w', real_time=True)

    with pytest.raises(MqValueError):
        tm.average_realized_volatility(mock_spx, '1w', composition_date='1d')

    with pytest.raises(NotImplementedError):
        tm.average_realized_volatility(mock_spx, '1w', Returns.SIMPLE)

    with pytest.raises(NotImplementedError):
        tm.average_realized_volatility(mock_spx, '1w', Returns.SIMPLE, 201)

    replace.restore()

    empty_positions_data_mock = replace('gs_quant.api.gs.indices.GsIndexApi.get_positions_data', Mock())
    empty_positions_data_mock.return_value = []
    with pytest.raises(MqValueError):
        tm.average_realized_volatility(mock_spx, '1w', Returns.SIMPLE, 5)

    replace.restore()


def test_avg_impl_var():
    replace = Replacer()
    mock_spx = Index('MA890', AssetClass.Equity, 'SPX')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    actual = tm.average_implied_variance(mock_spx, '1m', tm.EdrDataReference.DELTA_CALL, 25)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='averageImpliedVariance'), pd.Series(actual))
    actual = tm.average_implied_variance(mock_spx, '1m', tm.EdrDataReference.DELTA_PUT, 75)
    assert actual.dataset_ids == _test_datasets
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='averageImpliedVariance'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.average_implied_variance(..., '1m', tm.EdrDataReference.DELTA_PUT, 75, real_time=True)
    replace.restore()


def test_basis_swap_spread(mocker):
    replace = Replacer()
    args = dict(swap_tenor='10y', spread_benchmark_type=None, spread_tenor=None,
                reference_benchmark_type=None, reference_tenor=None, forward_tenor='0b', real_time=False)

    mock_nok = Currency('MA891', 'EGP')
    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'EGP'
    args['asset'] = mock_nok
    with pytest.raises(NotImplementedError):
        tm_rates.basis_swap_spread(**args)

    mock_usd = Currency('MAZ7RWC904JYHYPS', 'USD')
    args['asset'] = mock_usd
    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'USD'
    with pytest.raises(NotImplementedError):
        tm_rates.basis_swap_spread(..., '1y', real_time=True)

    args['swap_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_spread(**args)
    args['swap_tenor'] = '6y'

    args['spread_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_spread(**args)
    args['spread_tenor'] = '3m'

    args['reference_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_spread(**args)
    args['reference_tenor'] = '6m'

    args['forward_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_spread(**args)
    args['forward_tenor'] = None

    args['spread_benchmark_type'] = BenchmarkType.STIBOR
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_spread(**args)
    args['spread_benchmark_type'] = BenchmarkType.LIBOR

    args['reference_benchmark_type'] = 'libor_3m'
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_spread(**args)
    args['reference_benchmark_type'] = BenchmarkType.LIBOR

    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'USD'
    identifiers = replace('gs_quant.timeseries.measures_rates._get_tdapi_rates_assets', Mock())
    identifiers.return_value = {'MAQB1PGEJFCET3GG'}
    mocker.patch.object(GsDataApi, 'get_market_data', return_value=mock_curr(None, None))
    actual = tm_rates.basis_swap_spread(**args)
    expected = tm.ExtendedSeries([1, 2, 3], index=_index * 3, name='basisSwapRate')
    expected.dataset_ids = _test_datasets
    assert_series_equal(expected, actual)
    assert actual.dataset_ids == expected.dataset_ids

    args['reference_benchmark_type'] = BenchmarkType.SOFR
    args['reference_tenor'] = '1y'
    args['reference_benchmark_type'] = BenchmarkType.LIBOR
    args['reference_tenor'] = '3m'

    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'USD'
    identifiers = replace('gs_quant.timeseries.measures_rates._get_tdapi_rates_assets', Mock())
    identifiers.return_value = {'MA06ATQ9CM0DCZFC'}
    mocker.patch.object(GsDataApi, 'get_market_data', return_value=mock_curr(None, None))
    args['location'] = PricingLocation.NYC
    actual = tm_rates.basis_swap_spread(**args)
    expected = tm.ExtendedSeries([1, 2, 3], index=_index * 3, name='basisSwapRate')
    expected.dataset_ids = _test_datasets
    assert_series_equal(expected, actual)
    assert actual.dataset_ids == expected.dataset_ids

    replace.restore()


def test_swap_rate(mocker):
    replace = Replacer()
    args = dict(swap_tenor='10y', benchmark_type=None, floating_rate_tenor=None, forward_tenor='0b', real_time=False)

    mock_usd = Currency('MAZ7RWC904JYHYPS', 'USD')
    args['asset'] = mock_usd
    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'USD'

    args['swap_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.swap_rate(**args)
    args['swap_tenor'] = '10y'

    args['floating_rate_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.swap_rate(**args)
    args['floating_rate_tenor'] = '1y'

    args['forward_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.swap_rate(**args)
    args['forward_tenor'] = None

    args['benchmark_type'] = BenchmarkType.STIBOR
    with pytest.raises(MqValueError):
        tm_rates.swap_rate(**args)

    args['benchmark_type'] = 'sonia'
    with pytest.raises(MqValueError):
        tm_rates.swap_rate(**args)
    args['benchmark_type'] = 'fed_funds'

    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'USD'
    identifiers = replace('gs_quant.timeseries.measures_rates._get_tdapi_rates_assets', Mock())
    identifiers.return_value = {'MAZ7RWC904JYHYPS'}
    mocker.patch.object(GsDataApi, 'get_market_data', return_value=mock_curr(None, None))
    actual = tm_rates.swap_rate(**args)
    expected = tm.ExtendedSeries([1, 2, 3], index=_index * 3, name='swapRate')
    expected.dataset_ids = _test_datasets
    assert_series_equal(expected, actual)
    assert actual.dataset_ids == _test_datasets

    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'EUR'
    identifiers = replace('gs_quant.timeseries.measures_rates._get_tdapi_rates_assets', Mock())
    identifiers.return_value = {'MAJNQPFGN1EBDHAE'}
    mocker.patch.object(GsDataApi, 'get_market_data', return_value=mock_curr(None, None))
    args['asset'] = Currency('MAJNQPFGN1EBDHAE', 'EUR')
    args['benchmark_type'] = 'estr'
    args['location'] = PricingLocation.LDN
    actual = tm_rates.swap_rate(**args)
    expected = tm.ExtendedSeries([1, 2, 3], index=_index * 3, name='swapRate')
    expected.dataset_ids = _test_datasets
    assert_series_equal(expected, actual)
    assert actual.dataset_ids == _test_datasets
    replace.restore()


def test_swap_annuity(mocker):
    replace = Replacer()
    args = dict(swap_tenor='10y', benchmark_type=None, floating_rate_tenor=None, forward_tenor='0b', real_time=False)

    mock_nok = Currency('MA891', 'PLN')
    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'PLN'
    args['asset'] = mock_nok
    with pytest.raises(NotImplementedError):
        tm_rates.swap_annuity(**args)

    mock_usd = Currency('MAZ7RWC904JYHYPS', 'USD')
    args['asset'] = mock_usd
    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'USD'
    with pytest.raises(NotImplementedError):
        tm_rates.swap_annuity(..., '1y', real_time=True)

    args['swap_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.swap_annuity(**args)
    args['swap_tenor'] = '10y'

    args['floating_rate_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.swap_annuity(**args)
    args['floating_rate_tenor'] = '1y'

    args['forward_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.swap_annuity(**args)
    args['forward_tenor'] = None

    args['benchmark_type'] = BenchmarkType.STIBOR
    with pytest.raises(MqValueError):
        tm_rates.swap_annuity(**args)
    args['benchmark_type'] = BenchmarkType.SOFR

    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'USD'
    identifiers = replace('gs_quant.timeseries.measures_rates._get_tdapi_rates_assets', Mock())
    identifiers.return_value = {'MAZ7RWC904JYHYPS'}
    mocker.patch.object(GsDataApi, 'get_market_data', return_value=mock_curr(None, None))
    actual = tm_rates.swap_annuity(**args)
    expected = abs(tm.ExtendedSeries([1.0, 2.0, 3.0], index=_index * 3, name='swapAnnuity') * 1e4 / 1e8)
    expected.dataset_ids = _test_datasets
    assert_series_equal(expected, actual)
    assert actual.dataset_ids == expected.dataset_ids
    replace.restore()


def test_swap_term_structure():
    replace = Replacer()
    args = dict(benchmark_type=None, floating_rate_tenor=None, tenor_type=tm_rates._SwapTenorType.FORWARD_TENOR,
                tenor='0b', real_time=False)

    mock_nok = Currency('MA891', 'PLN')
    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'PLN'
    args['asset'] = mock_nok
    with pytest.raises(NotImplementedError):
        tm_rates.swap_term_structure(**args)

    mock_usd = Currency('MAZ7RWC904JYHYPS', 'USD')
    args['asset'] = mock_usd
    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'USD'
    with pytest.raises(NotImplementedError):
        tm_rates.swap_term_structure(..., '1y', real_time=True)

    args['floating_rate_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.swap_term_structure(**args)
    args['floating_rate_tenor'] = '3m'

    args['tenor_type'] = 'expiry'
    with pytest.raises(MqValueError):
        tm_rates.swap_term_structure(**args)
    args['tenor_type'] = None

    args['tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.swap_term_structure(**args)
    args['tenor'] = None

    args['benchmark_type'] = BenchmarkType.STIBOR
    with pytest.raises(MqValueError):
        tm_rates.swap_term_structure(**args)
    args['benchmark_type'] = BenchmarkType.LIBOR

    bd_mock = replace('gs_quant.data.dataset.Dataset.get_data', Mock())
    bd_mock.return_value = pd.DataFrame(data=dict(date="2020-04-10", exchange="NYC", description="Good Friday"),
                                        index=[pd.Timestamp('2020-04-10')])
    args['pricing_date'] = datetime.date(2020, 4, 10)
    with pytest.raises(MqValueError):
        tm_rates.swap_term_structure(**args)
    args['pricing_date'] = None

    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'USD'
    identifiers_empty = replace('gs_quant.timeseries.measures.GsAssetApi.get_many_assets', Mock())
    identifiers_empty.return_value = {}
    with pytest.raises(MqValueError):
        tm_rates.swap_term_structure(**args)

    identifiers = replace('gs_quant.timeseries.measures.GsAssetApi.get_many_assets', Mock())
    mock_asset = Currency('USD', name='USD')
    mock_asset.id = 'MAEMPCXQG3T716EX'
    mock_asset.exchange = 'OTC'
    identifiers.return_value = [mock_asset]

    d = {
        'terminationTenor': ['1y', '2y', '3y', '4y'], 'swapRate': [1, 2, 3, 4],
        'assetId': ['MAEMPCXQG3T716EX', 'MAFRSWPAF5QPNTP2', 'MA88BXZ3TCTXTFW1', 'MAC4KAG9B9ZAZHFT']
    }

    pricing_date_mock = replace('gs_quant.timeseries.measures_rates._range_from_pricing_date', Mock())
    pricing_date_mock.return_value = [datetime.date(2019, 1, 1), datetime.date(2019, 1, 1)]
    bd_mock.return_value = pd.DataFrame()
    market_data_mock = replace('gs_quant.timeseries.measures_rates._market_data_timed', Mock())

    market_data_mock.return_value = pd.DataFrame()
    df = pd.DataFrame(data=d, index=_index * 4)
    assert tm_rates.swap_term_structure(**args).empty

    market_data_mock.return_value = df
    with DataContext('2019-01-01', '2025-01-01'):
        actual = tm_rates.swap_term_structure(**args)
    actual.dataset_ids = _test_datasets
    expected = tm.ExtendedSeries([1, 2, 3, 4], index=pd.to_datetime(['2020-01-01', '2021-01-01', '2021-12-31',
                                                                     '2022-12-30']))
    expected.dataset_ids = _test_datasets
    assert_series_equal(expected, actual, check_names=False)
    assert actual.dataset_ids == expected.dataset_ids

    df = pd.DataFrame(data={'effectiveTenor': ['1y'], 'swapRate': [1], 'assetId': ['MAEMPCXQG3T716EX']}, index=_index)
    market_data_mock.return_value = df
    args['tenor_type'] = 'swap_tenor'
    args['tenor'] = '5y'
    args['location'] = PricingLocation.NYC
    with DataContext('2019-01-01', '2025-01-01'):
        actual = tm_rates.swap_term_structure(**args)
    actual.dataset_ids = _test_datasets
    expected = tm.ExtendedSeries([1], index=pd.to_datetime(['2020-01-01']))
    expected.dataset_ids = _test_datasets
    assert_series_equal(expected, actual, check_names=False)
    assert actual.dataset_ids == expected.dataset_ids

    d = {
        'effectiveTenor': ['1y', '2y', '3y', '4y'], 'swapRate': [1, 2, 3, 4],
        'assetId': ['MAEMPCXQG3T716EX', 'MAFRSWPAF5QPNTP2', 'MA88BXZ3TCTXTFW1', 'MAC4KAG9B9ZAZHFT']
    }

    df = pd.DataFrame(data=d, index=_index * 4)
    market_data_mock.return_value = df
    args['tenor_type'] = 'swap_tenor'

    args['tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.swap_term_structure(**args)
    args['tenor'] = '5y'

    market_data_mock.return_value = pd.DataFrame()
    df = pd.DataFrame(data=d, index=_index * 4)
    assert tm_rates.swap_term_structure(**args).empty

    market_data_mock.return_value = df
    with DataContext('2019-01-01', '2025-01-01'):
        actual = tm_rates.swap_term_structure(**args)
    actual.dataset_ids = _test_datasets
    expected = tm.ExtendedSeries([1, 2, 3, 4], index=pd.to_datetime(['2020-01-01', '2021-01-01', '2021-12-31',
                                                                     '2022-12-30']))
    expected.dataset_ids = _test_datasets
    assert_series_equal(expected, actual, check_names=False)
    assert actual.dataset_ids == expected.dataset_ids
    replace.restore()


def test_basis_swap_term_structure():
    replace = Replacer()
    range_mock = replace('gs_quant.timeseries.measures_rates._range_from_pricing_date', Mock())
    range_mock.return_value = [datetime.date(2019, 1, 1), datetime.date(2019, 1, 1)]

    args = dict(spread_benchmark_type=None, spread_tenor=None,
                reference_benchmark_type=None, reference_tenor=None, tenor_type=tm_rates._SwapTenorType.FORWARD_TENOR,
                tenor='0b', real_time=False)

    mock_nok = Currency('MA891', 'EGP')
    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'EGP'
    args['asset'] = mock_nok
    with pytest.raises(NotImplementedError):
        tm_rates.basis_swap_term_structure(**args)

    mock_usd = Currency('MAZ7RWC904JYHYPS', 'USD')
    args['asset'] = mock_usd
    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'USD'
    with pytest.raises(NotImplementedError):
        tm_rates.basis_swap_term_structure(..., '1y', real_time=True)

    args['spread_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_term_structure(**args)
    args['spread_tenor'] = '3m'

    args['reference_tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_term_structure(**args)
    args['reference_tenor'] = '6m'

    args['tenor_type'] = 'expiry'
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_term_structure(**args)
    args['tenor_type'] = 'forward_tenor'

    args['tenor'] = '5yr'
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_term_structure(**args)
    args['tenor'] = None

    args['spread_benchmark_type'] = BenchmarkType.STIBOR
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_term_structure(**args)
    args['spread_benchmark_type'] = BenchmarkType.LIBOR

    args['reference_benchmark_type'] = BenchmarkType.STIBOR
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_term_structure(**args)
    args['reference_benchmark_type'] = BenchmarkType.LIBOR

    bd_mock = replace('gs_quant.data.dataset.Dataset.get_data', Mock())
    bd_mock.return_value = pd.DataFrame(data=dict(date="2020-04-10", exchange="NYC", description="Good Friday"),
                                        index=[pd.Timestamp('2020-04-10')])
    args['pricing_date'] = datetime.date(2020, 4, 10)
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_term_structure(**args)
    args['pricing_date'] = None

    xrefs = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    xrefs.return_value = 'USD'
    identifiers_empty = replace('gs_quant.timeseries.measures.GsAssetApi.get_many_assets', Mock())
    identifiers_empty.return_value = {}
    with pytest.raises(MqValueError):
        tm_rates.basis_swap_term_structure(**args)

    identifiers = replace('gs_quant.timeseries.measures.GsAssetApi.get_many_assets', Mock())
    mock_asset = Currency('USD', name='USD')
    mock_asset.id = 'MAEMPCXQG3T716EX'
    mock_asset.exchange = 'OTC'
    identifiers.return_value = [mock_asset]

    d = {
        'terminationTenor': ['1y', '2y', '3y', '4y'], 'basisSwapRate': [1, 2, 3, 4],
        'assetId': ['MAEMPCXQG3T716EX', 'MAFRSWPAF5QPNTP2', 'MA88BXZ3TCTXTFW1', 'MAC4KAG9B9ZAZHFT']
    }

    pricing_date_mock = replace('gs_quant.timeseries.measures_rates._range_from_pricing_date', Mock())
    pricing_date_mock.return_value = [datetime.date(2019, 1, 1), datetime.date(2019, 1, 1)]
    bd_mock.return_value = pd.DataFrame()
    market_data_mock = replace('gs_quant.timeseries.measures_rates._market_data_timed', Mock())

    market_data_mock.return_value = pd.DataFrame()
    assert tm_rates.basis_swap_term_structure(**args).empty

    df = pd.DataFrame(data=d, index=_index * 4)
    market_data_mock.return_value = df
    with DataContext('2019-01-01', '2025-01-01'):
        actual = tm_rates.basis_swap_term_structure(**args)
    actual.dataset_ids = _test_datasets
    expected = tm.ExtendedSeries([1, 2, 3, 4], index=pd.to_datetime(['2020-01-01', '2021-01-01', '2021-12-31',
                                                                     '2022-12-30']))
    expected.dataset_ids = _test_datasets
    assert_series_equal(expected, actual, check_names=False)
    assert actual.dataset_ids == expected.dataset_ids

    d = {
        'effectiveTenor': ['1y', '2y', '3y', '4y'], 'basisSwapRate': [1, 2, 3, 4],
        'assetId': ['MAEMPCXQG3T716EX', 'MAFRSWPAF5QPNTP2', 'MA88BXZ3TCTXTFW1', 'MAC4KAG9B9ZAZHFT']
    }
    bd_mock.return_value = pd.DataFrame()
    market_data_mock = replace('gs_quant.timeseries.measures_rates._market_data_timed', Mock())

    df = pd.DataFrame(data=d, index=_index * 4)
    market_data_mock.return_value = df
    args['tenor_type'] = tm_rates._SwapTenorType.SWAP_TENOR
    args['tenor'] = '5y'
    with DataContext('2019-01-01', '2025-01-01'):
        actual = tm_rates.basis_swap_term_structure(**args)
    actual.dataset_ids = _test_datasets
    expected = tm.ExtendedSeries([1, 2, 3, 4], index=pd.to_datetime(['2020-01-01', '2021-01-01', '2021-12-31',
                                                                     '2022-12-30']))
    expected.dataset_ids = _test_datasets
    assert_series_equal(expected, actual, check_names=False)
    assert actual.dataset_ids == expected.dataset_ids

    df = pd.DataFrame(data={'effectiveTenor': ['1y'], 'basisSwapRate': [1], 'assetId': ['MAEMPCXQG3T716EX']},
                      index=_index)
    market_data_mock.return_value = df
    with DataContext('2019-01-01', '2025-01-01'):
        actual = tm_rates.basis_swap_term_structure(**args)
    actual.dataset_ids = _test_datasets
    expected = tm.ExtendedSeries([1], index=pd.to_datetime(['2020-01-01']))
    expected.dataset_ids = _test_datasets
    assert_series_equal(expected, actual, check_names=False)
    assert actual.dataset_ids == expected.dataset_ids
    replace.restore()


def test_cap_floor_vol():
    replace = Replacer()
    mock_usd = Currency('MA890', 'USD')
    xrefs = replace('gs_quant.timeseries.measures.GsAssetApi.get_asset_xrefs', Mock())
    xrefs.return_value = [GsTemporalXRef(dt.date(2019, 1, 1), dt.date(2952, 12, 31), XRef(bbid='USD', ))]
    identifiers = replace('gs_quant.timeseries.measures.GsAssetApi.map_identifiers', Mock())
    identifiers.return_value = {'USD-LIBOR-BBA': 'MA123'}
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_curr)
    actual = tm.cap_floor_vol(mock_usd, '5y', 50)
    assert_series_equal(pd.Series([1, 2, 3], index=_index * 3, name='capFloorVol'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.cap_floor_vol(..., '5y', 50, real_time=True)
    replace.restore()


def test_cap_floor_atm_fwd_rate():
    replace = Replacer()
    mock_usd = Currency('MA890', 'USD')
    xrefs = replace('gs_quant.timeseries.measures.GsAssetApi.get_asset_xrefs', Mock())
    xrefs.return_value = [GsTemporalXRef(dt.date(2019, 1, 1), dt.date(2952, 12, 31), XRef(bbid='USD', ))]
    identifiers = replace('gs_quant.timeseries.measures.GsAssetApi.map_identifiers', Mock())
    identifiers.return_value = {'USD-LIBOR-BBA': 'MA123'}
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_curr)
    actual = tm.cap_floor_atm_fwd_rate(mock_usd, '5y')
    assert_series_equal(pd.Series([1, 2, 3], index=_index * 3, name='capFloorAtmFwdRate'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.cap_floor_atm_fwd_rate(..., '5y', real_time=True)
    replace.restore()


def test_spread_option_vol():
    replace = Replacer()
    mock_usd = Currency('MA890', 'USD')
    xrefs = replace('gs_quant.timeseries.measures.GsAssetApi.get_asset_xrefs', Mock())
    xrefs.return_value = [GsTemporalXRef(dt.date(2019, 1, 1), dt.date(2952, 12, 31), XRef(bbid='USD', ))]
    identifiers = replace('gs_quant.timeseries.measures.GsAssetApi.map_identifiers', Mock())
    identifiers.return_value = {'USD-LIBOR-BBA': 'MA123'}
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_curr)
    actual = tm.spread_option_vol(mock_usd, '3m', '10y', '5y', 50)
    assert_series_equal(pd.Series([1, 2, 3], index=_index * 3, name='spreadOptionVol'), pd.Series(actual))

    # Check null/no identifier cases
    identifiers.return_value = {None: 'MA123'}
    actual = tm.spread_option_vol(mock_usd, '3m', '10y', '5y', 50)
    assert_series_equal(pd.Series([1, 2, 3], index=_index * 3, name='spreadOptionVol'), pd.Series(actual))
    identifiers.return_value = {}
    with pytest.raises(MqValueError):
        tm.spread_option_vol(mock_usd, '3m', '10y', '5y', 50)

    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.spread_option_vol(..., '3m', '10y', '5y', 50, real_time=True)
    replace.restore()


def test_spread_option_atm_fwd_rate():
    replace = Replacer()
    mock_usd = Currency('MA890', 'USD')
    xrefs = replace('gs_quant.timeseries.measures.GsAssetApi.get_asset_xrefs', Mock())
    xrefs.return_value = [GsTemporalXRef(dt.date(2019, 1, 1), dt.date(2952, 12, 31), XRef(bbid='USD', ))]
    identifiers = replace('gs_quant.timeseries.measures.GsAssetApi.map_identifiers', Mock())
    identifiers.return_value = {'USD-LIBOR-BBA': 'MA123'}
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_curr)
    actual = tm.spread_option_atm_fwd_rate(mock_usd, '3m', '10y', '5y')
    assert_series_equal(pd.Series([1, 2, 3], index=_index * 3, name='spreadOptionAtmFwdRate'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.spread_option_atm_fwd_rate(..., '3m', '10y', '5y', real_time=True)
    replace.restore()


def test_zc_inflation_swap_rate():
    replace = Replacer()
    mock_gbp = Currency('MA890', 'GBP')
    xrefs = replace('gs_quant.timeseries.measures.GsAssetApi.get_asset_xrefs', Mock())
    xrefs.return_value = [GsTemporalXRef(dt.date(2019, 1, 1), dt.date(2952, 12, 31), XRef(bbid='GBP', ))]
    identifiers = replace('gs_quant.timeseries.measures.GsAssetApi.map_identifiers', Mock())
    identifiers.return_value = {'CPI-UKRPI': 'MA123'}
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_curr)
    actual = tm.zc_inflation_swap_rate(mock_gbp, '1y')
    assert_series_equal(pd.Series([1, 2, 3], index=_index * 3, name='inflationSwapRate'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.zc_inflation_swap_rate(..., '1y', real_time=True)
    replace.restore()


def test_basis():
    replace = Replacer()
    mock_jpyusd = Cross('MA890', 'USD/JPY')
    xrefs = replace('gs_quant.timeseries.measures.GsAssetApi.get_asset_xrefs', Mock())
    xrefs.return_value = [GsTemporalXRef(dt.date(2019, 1, 1), dt.date(2952, 12, 31), XRef(bbid='JPYUSD', ))]
    identifiers = replace('gs_quant.timeseries.measures.GsAssetApi.map_identifiers', Mock())
    identifiers.return_value = {'USD-3m/JPY-3m': 'MA123'}
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_cross)
    actual = tm.basis(mock_jpyusd, '1y')
    assert_series_equal(pd.Series([1, 2, 3], index=_index * 3, name='basis'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.basis(..., '1y', real_time=True)
    replace.restore()


def test_td():
    cases = {'3d': pd.DateOffset(days=3), '9w': pd.DateOffset(weeks=9), '2m': pd.DateOffset(months=2),
             '10y': pd.DateOffset(years=10)
             }
    for k, v in cases.items():
        actual = tm._to_offset(k)
        assert v == actual, f'expected {v}, got actual {actual}'

    with pytest.raises(ValueError):
        tm._to_offset('5z')


def test_pricing_range():
    import datetime

    given = datetime.date(2019, 4, 20)
    s, e = tm._range_from_pricing_date('NYSE', given)
    assert s == e == given

    class MockDate(datetime.date):
        @classmethod
        def today(cls):
            return cls(2019, 5, 25)

    # mock
    replace = Replacer()
    cbd = replace('gs_quant.timeseries.measures._get_custom_bd', Mock())
    cbd.return_value = pd.tseries.offsets.BusinessDay()
    today = replace('gs_quant.timeseries.measures.pd.Timestamp.today', Mock())
    today.return_value = pd.Timestamp(2019, 5, 25)
    gold = datetime.date
    datetime.date = MockDate

    # cases
    s, e = tm._range_from_pricing_date('ANY')
    assert s == pd.Timestamp(2019, 5, 24)
    assert e == pd.Timestamp(2019, 5, 24)

    s, e = tm._range_from_pricing_date('ANY', '3m')
    assert s == pd.Timestamp(2019, 2, 22)
    assert e == pd.Timestamp(2019, 2, 24)

    s, e = tm._range_from_pricing_date('ANY', '3b')
    assert s == e == pd.Timestamp(2019, 5, 22)

    # restore
    datetime.date = gold
    replace.restore()


def test_var_swap_tenors():
    session = GsSession.get(Environment.DEV, token='faux')

    replace = Replacer()
    get_mock = replace('gs_quant.session.GsSession._get', Mock())
    get_mock.return_value = {
        'data': [
            {
                'dataField': 'varSwap',
                'filteredFields': [
                    {
                        'field': 'tenor',
                        'values': ['abc', 'xyc']
                    }
                ]
            }
        ]
    }

    with session:
        actual = tm._var_swap_tenors(Index('MAXXX', AssetClass.Equity, 'XXX'))
    assert actual == ['abc', 'xyc']

    get_mock.return_value = {
        'data': []
    }
    with pytest.raises(MqError):
        with session:
            tm._var_swap_tenors(Index('MAXXX', AssetClass.Equity, 'XXX'))
    replace.restore()


def test_forward_var_term():
    idx = pd.DatetimeIndex([datetime.date(2020, 4, 1), datetime.date(2020, 4, 2)] * 6)
    data = {
        'varSwap': [1.1, 1, 2.1, 2, 3.1, 3, 4.1, 4, 5.1, 5, 6.1, 6],
        'tenor': ['1w', '1w', '1m', '1m', '5w', '5w', '2m', '2m', '3m', '3m', '5m', '5m']
    }
    out = MarketDataResponseFrame(data=data, index=idx)
    out.dataset_ids = _test_datasets

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = out

    # Equity
    expected = pd.Series([np.nan, 5.29150, 6.55744], name='forwardVarTerm',
                         index=pd.DatetimeIndex(['2020-05-01', '2020-06-02', '2020-07-02'], name='expirationDate'))
    with DataContext('2020-01-01', '2020-07-31'):
        actual = tm.forward_var_term(Index('MA123', AssetClass.Equity, '123'), datetime.date(2020, 4, 2))
    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    market_mock.assert_called_once()

    # FX
    expected_fx = pd.Series([np.nan, 5.29150, 6.55744, 7.24569], name='forwardVarTerm',
                            index=pd.DatetimeIndex(['2020-05-01', '2020-06-02', '2020-07-02', '2020-09-02'],
                                                   name='expirationDate'))

    with DataContext('2020-01-01', '2020-09-02'):
        actual_fx = tm.forward_var_term(Cross('ABCDE', 'EURUSD'))
    assert_series_equal(expected_fx, pd.Series(actual_fx))
    assert actual_fx.dataset_ids == _test_datasets

    # no data
    market_mock.reset_mock()
    market_mock.return_value = mock_empty_market_data_response()
    actual = tm.forward_var_term(Index('MA123', AssetClass.Equity, '123'))
    assert actual.empty

    # real-time
    with pytest.raises(NotImplementedError):
        tm.forward_var_term(..., real_time=True)

    replace.restore()


def _mock_var_swap_data(_cls, q, ignore_errors=False):
    queries = q.get('queries', [])
    if len(queries) > 0 and 'Last' in queries[0]['measures']:
        return MarketDataResponseFrame({'varSwap': [4]}, index=[pd.Timestamp('2019-01-04T12:00:00Z')])

    idx = pd.date_range(start="2019-01-01", periods=3, freq="D")
    data = {
        'varSwap': [1, 2, 3]
    }
    out = MarketDataResponseFrame(data=data, index=idx)
    out.dataset_ids = _test_datasets
    return out


def test_var_swap():
    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', _mock_var_swap_data)

    expected = pd.Series([1, 2, 3, 4], name='varSwap', index=pd.date_range("2019-01-01", periods=4, freq="D"))
    expected.index.freq = None
    actual = tm.var_swap(Index('MA123', AssetClass.Equity, '123'), '1m')
    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = mock_empty_market_data_response()
    actual = tm.var_swap(Index('MA123', AssetClass.Equity, '123'), '1m')
    assert actual.empty
    replace.restore()


def _mock_var_swap_fwd(_cls, q, ignore_errors=False):
    queries = q.get('queries', [])
    if len(queries) > 0 and 'Last' in queries[0]['measures']:
        return MarketDataResponseFrame({'varSwap': [4, 4.5], 'tenor': ['1y', '13m']},
                                       index=[pd.Timestamp('2019-01-04T12:00:00Z')] * 2)

    idx = pd.date_range(start="2019-01-01", periods=3, freq="D")
    d1 = {
        'varSwap': [1, 2, 3],
        'tenor': ['1y'] * 3
    }
    d2 = {
        'varSwap': [1.5, 2.5, 3.5],
        'tenor': ['13m'] * 3
    }
    df1 = MarketDataResponseFrame(data=d1, index=idx)
    df2 = MarketDataResponseFrame(data=d2, index=idx)
    out = pd.concat([df1, df2])
    out.dataset_ids = _test_datasets
    return out


def _mock_var_swap_1t(_cls, q, ignore_errors=False):
    queries = q.get('queries', [])
    if len(queries) > 0 and 'Last' in queries[0]['measures']:
        return MarketDataResponseFrame({'varSwap': [4, 4.5], 'tenor': ['1y', '13m']},
                                       index=[pd.Timestamp('2019-01-04T12:00:00Z')])

    idx = pd.date_range(start="2019-01-01", periods=3, freq="D")
    d1 = {
        'varSwap': [1, 2, 3],
        'tenor': ['1y'] * 3
    }
    df1 = MarketDataResponseFrame(data=d1, index=idx)
    df1.dataset_ids = _test_datasets
    return df1


def test_var_swap_fwd():
    # bad input
    with pytest.raises(MqError):
        tm.var_swap(Index('MA123', AssetClass.Equity, '123'), '1m', 500)

    # regular
    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', _mock_var_swap_fwd)

    tenors_mock = replace('gs_quant.timeseries.measures._var_swap_tenors', Mock())
    tenors_mock.return_value = ['1m', '1y', '13m']

    expected = pd.Series([4.1533, 5.7663, 7.1589, 8.4410], name='varSwap',
                         index=pd.date_range(start="2019-01-01", periods=4, freq="D"))
    expected.index.freq = None
    actual = tm.var_swap(Index('MA123', AssetClass.Equity, '123'), '1m', '1y')
    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    # no data
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = mock_empty_market_data_response()
    actual = tm.var_swap(Index('MA123', AssetClass.Equity, '123'), '1m', '1y')
    assert actual.empty
    assert actual.dataset_ids == ()

    # no data for a tenor
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', _mock_var_swap_1t)
    actual = tm.var_swap(Index('MA123', AssetClass.Equity, '123'), '1m', '1y')
    assert actual.empty
    assert actual.dataset_ids == ()

    # no such tenors
    tenors_mock.return_value = []
    actual = tm.var_swap(Index('MA123', AssetClass.Equity, '123'), '1m', '1y')
    assert actual.empty
    assert actual.dataset_ids == ()

    # finish
    replace.restore()


def _var_term_typical():
    assert DataContext.current_is_set
    data = {
        'tenor': ['1w', '2w', '1y', '2y'],
        'varSwap': [1, 2, 3, 4]
    }
    out = MarketDataResponseFrame(data=data, index=pd.DatetimeIndex(['2018-01-01'] * 4))
    out.dataset_ids = _test_datasets

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.side_effect = [pd.DataFrame(), out]

    actual = tm.var_term(Index('MA123', AssetClass.Equity, '123'))
    idx = pd.DatetimeIndex(['2018-01-08', '2018-01-15', '2019-01-01', '2020-01-01'], name='varSwap')
    expected = pd.Series([1, 2, 3, 4], name='varSwap', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]

    if expected.empty:
        assert actual.empty
    else:
        assert_series_equal(expected, pd.Series(actual), check_names=False)
        assert actual.dataset_ids == _test_datasets

    replace.restore()

    return actual


def _var_term_empty():
    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = mock_empty_market_data_response()

    actual = tm.var_term(Index('MAXYZ', AssetClass.Equity, 'XYZ'))
    assert actual.empty
    assert actual.dataset_ids == ()
    replace.restore()


def _var_term_fwd():
    idx = pd.date_range('2018-01-01', periods=2, freq='D')

    def mock_var_swap(_asset, tenor, _forward_start_date, **_kwargs):
        if tenor == '1m':
            series = tm.ExtendedSeries([1, 2], idx, name='varSwap')
            series.dataset_ids = _test_datasets
        elif tenor == '2m':
            series = tm.ExtendedSeries([3, 4], idx, name='varSwap')
            series.dataset_ids = _test_datasets
        else:
            series = tm.ExtendedSeries(dtype=float)
            series.dataset_ids = ()
        return series

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.var_swap', Mock())
    market_mock.side_effect = mock_var_swap
    tenors_mock = replace('gs_quant.timeseries.measures._var_swap_tenors', Mock())
    tenors_mock.return_value = ['1m', '2m', '3m']

    actual = tm.var_term(Index('MA123', AssetClass.Equity, '123'), forward_start_date='1m')
    idx = pd.DatetimeIndex(['2018-02-02', '2018-03-02'], name='varSwap')
    expected = pd.Series([2, 4], name='varSwap', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]

    if expected.empty:
        assert actual.empty
    else:
        assert_series_equal(expected, pd.Series(actual), check_names=False)
        assert actual.dataset_ids == _test_datasets
    market_mock.assert_called()

    replace.restore()
    return actual


def _var_term_latest():
    assert DataContext.current_is_set
    data = {
        'tenor': ['1w', '2w', '1y', '2y', '1w', '2w', '1y', '2y'],
        'varSwap': [10, 20, 30, 40, 1, 2, 3, 4]
    }
    out = MarketDataResponseFrame(data=data, index=pd.date_range("2018-01-01", periods=8, freq="min"))
    out.dataset_ids = _test_datasets

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.side_effect = [out.iloc[-1:], out]

    actual = tm.var_term(Index('MA123', AssetClass.Equity, '123'))
    idx = pd.DatetimeIndex(['2018-01-08', '2018-01-15', '2019-01-01', '2020-01-01'], name='expirationDate')
    expected = pd.Series([1, 2, 3, 4], name='varSwap', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]

    if expected.empty:
        assert actual.empty
    else:
        assert_series_equal(expected, pd.Series(actual))
        assert actual.dataset_ids == _test_datasets

    replace.restore()


def test_var_term():
    with DataContext('2018-01-01', '2019-01-01'):
        _var_term_typical()
        _var_term_empty()
        _var_term_fwd()
        _var_term_latest()
    with DataContext('2019-01-01', '2019-07-04'):
        _var_term_fwd()
    with DataContext('2018-01-16', '2018-12-31'):
        out = _var_term_typical()
        assert out.empty
        assert out.dataset_ids == _test_datasets
    with pytest.raises(MqError):
        tm.var_term(..., pricing_date=300)
    with pytest.raises(NotImplementedError):
        tm.var_term(..., real_time=True)


def _mock_forward_helper():
    idx = pd.DatetimeIndex([datetime.date(2020, 5, 1), datetime.date(2020, 5, 2)] * 4)
    data = {
        'impliedVolatility': [2.1, 2, 3.1, 3, 4.1, 4, 5.1, 5],
        'tenor': ['1m', '1m', '2m', '2m', '3m', '3m', '4m', '4m']
    }
    out = MarketDataResponseFrame(data=data, index=idx)
    out.dataset_ids = _test_datasets
    return out


def _mock_forward_vol_data(_cls, q, ignore_errors=False):
    queries = q.get('queries', [])
    if len(queries) > 0 and 'Last' in queries[0]['measures']:
        return MarketDataResponseFrame()

    return _mock_forward_helper()


def _mock_forward_vol_data_with_last(_cls, q, ignore_errors=False):
    queries = q.get('queries', [])
    if len(queries) > 0 and 'Last' in queries[0]['measures']:
        idx = [pd.Timestamp('2020-05-03T12:00:00Z')] * 4
        data = {
            'impliedVolatility': [2, 3, 4, 5],
            'tenor': ['1m', '2m', '3m', '4m']
        }
        ids = _test_datasets_rt
        out = MarketDataResponseFrame(data=data, index=idx)
        out.dataset_ids = ids
        return out

    return _mock_forward_helper()


def _mock_forward_vol_data_error(_cls, q, ignore_errors=False):
    queries = q.get('queries', [])
    if len(queries) > 0 and 'Last' in queries[0]['measures']:
        raise MqValueError('something happened')

    return _mock_forward_helper()


def test_forward_vol():
    idx = pd.DatetimeIndex([datetime.date(2020, 5, 1), datetime.date(2020, 5, 2)] * 4)
    data = {
        'impliedVolatility': [2.1, 2, 3.1, 3, 4.1, 4, 5.1, 5],
        'tenor': ['1m', '1m', '2m', '2m', '3m', '3m', '4m', '4m']
    }
    out = MarketDataResponseFrame(data=data, index=idx)
    out.dataset_ids = _test_datasets

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.side_effect = _mock_forward_vol_data

    # Equity
    expected = pd.Series([5.58659, 5.47723], name='forwardVol',
                         index=pd.to_datetime(['2020-05-01', '2020-05-02']))
    with DataContext('2020-01-01', '2020-09-01'):
        actual = tm.forward_vol(Index('MA123', AssetClass.Equity, '123'), '1m', '2m', tm.VolReference.SPOT, 100)
    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    market_mock.assert_called_once()

    # FX
    cross_mock = replace('gs_quant.timeseries.measures.cross_stored_direction_for_fx_vol', Mock())
    cross_mock.return_value = 'EURUSD'

    with DataContext('2020-01-01', '2020-09-01'):
        actual_fx = tm.forward_vol(Cross('ABCDE', 'EURUSD'), '1m', '2m', tm.VolReference.SPOT, 100)
    assert_series_equal(expected, pd.Series(actual_fx))
    assert actual_fx.dataset_ids == _test_datasets

    # EQ with last
    market_mock.reset_mock()
    market_mock.side_effect = _mock_forward_vol_data_with_last
    expected = pd.Series([5.58659, 5.47723, 5.47723], name='forwardVol',
                         index=pd.to_datetime(['2020-05-01', '2020-05-02', '2020-05-03']))
    actual = tm.forward_vol(Index('MA123', AssetClass.Equity, '123'), '1m', '2m', tm.VolReference.SPOT, 100)
    assert_series_equal(expected, pd.Series(actual))
    for t in _test_datasets + _test_datasets_rt:
        assert t in actual.dataset_ids
    assert market_mock.call_count == 2

    # EQ with exception on last
    market_mock.reset_mock()
    market_mock.side_effect = _mock_forward_vol_data_error
    expected = pd.Series([5.58659, 5.47723], name='forwardVol',
                         index=pd.to_datetime(['2020-05-01', '2020-05-02']))
    actual = tm.forward_vol(Index('MA123', AssetClass.Equity, '123'), '1m', '2m', tm.VolReference.SPOT, 100)
    assert_series_equal(expected, pd.Series(actual))
    assert market_mock.call_count == 2

    # no data
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = mock_empty_market_data_response()
    actual = tm.forward_vol(Index('MA123', AssetClass.Equity, '123'), '1m', '2m', tm.VolReference.SPOT, 100)
    assert actual.empty

    # no data for required tenor
    market_mock.reset_mock()
    market_mock.return_value = MarketDataResponseFrame(data={'impliedVolatility': [2.1, 3.1, 5.1],
                                                             'tenor': ['1m', '2m', '4m']},
                                                       index=[datetime.date(2020, 5, 1)] * 3)
    with DataContext('2020-01-01', '2020-09-01'):
        actual = tm.forward_vol(Index('MA123', AssetClass.Equity, '123'), '1m', '2m', tm.VolReference.SPOT, 100)
    assert actual.empty

    # real-time
    with pytest.raises(NotImplementedError):
        tm.forward_vol(..., '1m', '2m', tm.VolReference.SPOT, 100, real_time=True)

    replace.restore()


def test_forward_vol_term():
    idx = pd.DatetimeIndex([datetime.date(2020, 4, 1), datetime.date(2020, 4, 2)] * 6)
    data = {
        'impliedVolatility': [1.1, 1, 2.1, 2, 3.1, 3, 4.1, 4, 5.1, 5, 6.1, 6],
        'tenor': ['1w', '1w', '1m', '1m', '5w', '5w', '2m', '2m', '3m', '3m', '5m', '5m']
    }
    out = MarketDataResponseFrame(data=data, index=idx)
    out.dataset_ids = _test_datasets

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = out

    # Equity
    expected = pd.Series([np.nan, 5.29150, 6.55744], name='forwardVolTerm',
                         index=pd.DatetimeIndex(['2020-05-01', '2020-06-02', '2020-07-02'], name='expirationDate'))
    with DataContext('2020-01-01', '2020-07-31'):
        actual = tm.forward_vol_term(Index('MA123', AssetClass.Equity, '123'), tm.VolReference.SPOT, 100,
                                     datetime.date(2020, 4, 2))
    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    market_mock.assert_called_once()

    # FX
    cross_mock = replace('gs_quant.timeseries.measures.cross_stored_direction_for_fx_vol', Mock())
    cross_mock.return_value = 'EURUSD'
    expected_fx = pd.Series([np.nan, 5.29150, 6.55744, 7.24569], name='forwardVolTerm',
                            index=pd.DatetimeIndex(['2020-05-01', '2020-06-02', '2020-07-02', '2020-09-02'],
                                                   name='expirationDate'))

    with DataContext('2020-01-01', '2020-09-02'):
        actual_fx = tm.forward_vol_term(Cross('ABCDE', 'EURUSD'), tm.VolReference.SPOT, 100)
    assert_series_equal(expected_fx, pd.Series(actual_fx))
    assert actual_fx.dataset_ids == _test_datasets

    # no data
    market_mock.reset_mock()
    market_mock.return_value = mock_empty_market_data_response()
    actual = tm.forward_vol_term(Index('MA123', AssetClass.Equity, '123'), tm.VolReference.SPOT, 100)
    assert actual.empty

    # real-time
    with pytest.raises(NotImplementedError):
        tm.forward_vol_term(..., tm.VolReference.SPOT, 100, real_time=True)

    replace.restore()


def test_get_latest_term_structure_data():
    # Test latest_term_structure_data where no data is returned
    replace = Replacer()
    data = {
        'tenor': ['1w', '2w', '1y', '2y'],
        'impliedVolatility': [1, 2, 3, 4]
    }
    out = MarketDataResponseFrame(data=data, index=pd.DatetimeIndex(['2018-01-01'] * 4))
    out.dataset_ids = _test_datasets
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.side_effect = [out, pd.DataFrame()]

    # test that the function runs without error when nothing is returned by second market_data_timed call
    tm._get_latest_term_structure_data('MA123', tm.QueryType.IMPLIED_VOLATILITY, {}, None, '', '')


def _vol_term_typical(reference, value):
    assert DataContext.current_is_set
    data = {
        'tenor': ['1w', '2w', '1y', '2y'],
        'impliedVolatility': [1, 2, 3, 4]
    }
    out = MarketDataResponseFrame(data=data, index=pd.DatetimeIndex(['2018-01-01'] * 4))
    out.dataset_ids = _test_datasets

    data_expiry = {
        'expirationDate': ['2018-01-06', '2018-01-08'],
        'impliedVolatilityByExpiration': [1.2, 1.1]
    }
    out_expiry = MarketDataResponseFrame(data=data_expiry, index=pd.DatetimeIndex(['2018-01-01'] * 2))
    out_expiry.dataset_ids = _test_datasets2

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.side_effect = [pd.DataFrame(), pd.DataFrame(), out, out_expiry]

    actual = tm.vol_term(Index('MA123', AssetClass.Equity, '123'), reference, value)
    idx = pd.DatetimeIndex(['2018-01-06', '2018-01-08', '2018-01-15', '2019-01-01', '2020-01-01'],
                           name='expirationDate')
    expected = pd.Series([1.2, 1, 2, 3, 4], name='impliedVolatility', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]

    if expected.empty:
        assert actual.empty
    else:
        assert_series_equal(expected, pd.Series(actual))
        assert set(actual.dataset_ids) == set(_test_datasets + _test_datasets2)

    replace.restore()
    return actual


def _skew_term_typical(reference, value, normalization_mode=None):
    assert DataContext.current_is_set
    data = {
        'tenor': ['10y', '10y', '10y', '13m', '13m', '13m'],
        'impliedVolatility': [5, 10, 15, 50, 55, 60],
        'relativeStrike': [0.25, 0.5, 0.75, 0.25, 0.5, 0.75]
    }
    out = MarketDataResponseFrame(data=data, index=pd.DatetimeIndex(['2022-01-11'] * 6))
    out.dataset_ids = _test_datasets

    data_expiry = {
        'expirationDate': ['2022-01-12', '2022-01-12', '2022-01-12'],
        'impliedVolatilityByExpiration': [1000, 1500, 2000],
        'relativeStrike': [0.25, 0.5, 0.75]
    }
    out_expiry = MarketDataResponseFrame(data=data_expiry, index=pd.DatetimeIndex(['2022-01-11'] * 3))
    out_expiry.dataset_ids = _test_datasets2

    replace = Replacer()
    gltsd_mock = replace('gs_quant.api.utils.ThreadPoolManager.run_async', Mock())
    gltsd_mock.side_effect = [[pd.DataFrame(), pd.DataFrame()], [out, out_expiry]]
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.side_effect = [out, out_expiry]

    actual = tm.skew_term(Index('MA123', AssetClass.Equity, '123'), reference, value,
                          normalization_mode=normalization_mode)
    idx = pd.DatetimeIndex(['2022-01-12', '2023-02-13', '2032-01-12'])

    if normalization_mode is None:
        normalization_mode = tm.NormalizationMode.NORMALIZED

    expected = pd.Series([0.666667, 0.181818, 1], name='impliedVolatility', index=idx) \
        if normalization_mode == tm.NormalizationMode.NORMALIZED \
        else pd.Series([1000, 10, 10], name='impliedVolatility', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]
    if expected.empty:
        assert actual.empty
    else:
        assert_series_equal(expected, pd.Series(actual))
        assert set(actual.dataset_ids) == set(_test_datasets + _test_datasets2)

    replace.restore()
    return actual


def _skew_term_typical_fx(reference, value, normalization_mode=None):
    assert DataContext.current_is_set
    data = {
        'tenor': ['10y', '10y', '10y', '13m', '13m', '13m'],
        'impliedVolatility': [5, 10, 15, 50, 55, 60],
        'relativeStrike': [0.25, 0.5, 0.75, 0.25, 0.5, 0.75]
    }
    out = MarketDataResponseFrame(data=data, index=pd.DatetimeIndex(['2022-01-11'] * 6))
    out.dataset_ids = _test_datasets

    data_expiry = {
        'expirationDate': ['2022-01-12', '2022-01-12', '2022-01-12'],
        'impliedVolatilityByExpiration': [1000, 1500, 2000],
        'relativeStrike': [0.25, 0.5, 0.75]
    }
    out_expiry = MarketDataResponseFrame(data=data_expiry, index=pd.DatetimeIndex(['2022-01-11'] * 3))
    out_expiry.dataset_ids = _test_datasets2

    replace = Replacer()
    gltsd_mock = replace('gs_quant.api.utils.ThreadPoolManager.run_async', Mock())
    gltsd_mock.side_effect = [[pd.DataFrame(), pd.DataFrame()], [out, out_expiry]]
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    fx_asset_mock = replace('gs_quant.timeseries.measures.cross_stored_direction_for_fx_vol', Mock())
    fx_asset_mock.return_value = 'MA123'
    market_mock.side_effect = [out, out_expiry]

    actual = tm.skew_term(Index('MA123', AssetClass.FX, '123'), reference, value,
                          normalization_mode=normalization_mode)
    idx = pd.DatetimeIndex(['2022-01-12', '2023-02-13', '2032-01-12'])

    if normalization_mode is None:
        normalization_mode = tm.NormalizationMode.OUTRIGHT

    expected = pd.Series([0.666667, 0.181818, 1], name='impliedVolatility', index=idx) \
        if normalization_mode == tm.NormalizationMode.NORMALIZED \
        else pd.Series([1000, 10, 10], name='impliedVolatility', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]
    if expected.empty:
        assert actual.empty
    else:
        assert_series_equal(expected, pd.Series(actual))
        assert set(actual.dataset_ids) == set(_test_datasets + _test_datasets2)

    replace.restore()
    return actual


def _vol_term_empty():
    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = pd.DataFrame()

    mock = replace('gs_quant.timeseries.measures.get_df_with_retries', Mock())
    mock.return_value = MarketDataResponseFrame()

    actual = tm.vol_term(Index('MAXYZ', AssetClass.Equity, 'XYZ'), tm.VolReference.DELTA_CALL, 777)
    assert actual.empty
    assert actual.dataset_ids == ()
    replace.restore()


def _skew_term_empty():
    replace = Replacer()
    market_mock = replace('gs_quant.api.utils.ThreadPoolManager.run_async', Mock())
    market_mock.side_effect = [[pd.DataFrame(), pd.DataFrame()], [pd.DataFrame(), pd.DataFrame()]]

    actual = tm.skew_term(Index('MAXYZ', AssetClass.Equity, 'XYZ'), tm.SkewReference.DELTA, 777)
    assert actual.empty
    assert actual.dataset_ids == ()
    replace.restore()


def _vol_term_latest():
    assert DataContext.current_is_set
    data = {
        'tenor': ['1w', '2w', '1y', '2y', '1w', '2w', '1y', '2y'],
        'impliedVolatility': [10, 20, 30, 40, 1, 2, 3, 4]
    }
    out = MarketDataResponseFrame(data=data, index=pd.date_range("2018-01-01", periods=8, freq="min"))
    out.dataset_ids = _test_datasets

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.side_effect = [out.iloc[-1:], out, pd.DataFrame(), pd.DataFrame()]

    actual = tm.vol_term(Index('MA123', AssetClass.Equity, '123'), tm.VolReference.SPOT, 100)
    idx = pd.DatetimeIndex(['2018-01-08', '2018-01-15', '2019-01-01', '2020-01-01'], name='expirationDate')
    expected = pd.Series([1, 2, 3, 4], name='impliedVolatility', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]

    if expected.empty:
        assert actual.empty
    else:
        assert_series_equal(expected, pd.Series(actual))
        assert actual.dataset_ids == _test_datasets

    replace.restore()
    return actual


def test_vol_term():
    with DataContext('2018-01-01', '2019-01-01'):
        _vol_term_typical(tm.VolReference.SPOT, 100)
        _vol_term_typical(tm.VolReference.NORMALIZED, 4)
        _vol_term_typical(tm.VolReference.DELTA_PUT, 50)
        _vol_term_empty()
        _vol_term_latest()
    with DataContext('2018-01-16', '2018-12-31'):
        out = _vol_term_typical(tm.VolReference.SPOT, 100)
        assert out.empty
        assert set(out.dataset_ids) == set(_test_datasets + _test_datasets2)
    with pytest.raises(NotImplementedError):
        tm.vol_term(..., tm.VolReference.SPOT, 100, real_time=True)
    with pytest.raises(MqError):
        tm.vol_term(Index('MA123', AssetClass.Equity, '123'), tm.VolReference.DELTA_NEUTRAL, 0)
    with DataContext('2020-01-01', '2021-01-01'):
        with pytest.raises(MqError, match='forward looking date range'):
            tm.vol_term(Index('MA123', AssetClass.Equity, '123'), tm.VolReference.SPOT, 100, source='plottool')
    with DataContext(datetime.date.today(), datetime.date.today()):
        with pytest.raises(MqError, match='forward looking date range'):
            tm.vol_term(Index('MA123', AssetClass.Equity, '123'), tm.VolReference.SPOT, 100, source='plottool')


def test__get_skew_strikes():
    # Test FX
    asset = Index('MA123', AssetClass.FX, '123')
    skew_strikes, buf = tm._get_skew_strikes(asset, tm.SkewReference.DELTA, 25)
    assert skew_strikes == [-25, 25, 0] and buf == 1
    with pytest.raises(MqValueError):
        tm._get_skew_strikes(asset, tm.SkewReference.NORMALIZED, 50)

    # Test skew reference types
    asset = Index('MA123', AssetClass.Equity, '123')
    skew_strikes, _ = tm._get_skew_strikes(asset, tm.SkewReference.DELTA, 25)
    assert skew_strikes == [0.75, 0.25, 0.50]
    skew_strikes, _ = tm._get_skew_strikes(asset, tm.SkewReference.NORMALIZED, 25)
    assert skew_strikes == [-25, 25, 0]
    skew_strikes, _ = tm._get_skew_strikes(asset, tm.SkewReference.SPOT, 25)
    assert skew_strikes == [0.75, 1.25, 1.0]
    with pytest.raises(MqTypeError):
        tm._get_skew_strikes(asset, None, 50)


def test__skew():
    df = pd.DataFrame({'relativeStrike': [0.25, 0.5, 0.75, 0.25, 0.5, 0.75], 'iv': [5, 10, 15, 100, 150, 200]},
                      index=['2021-01-01'] * 3 + ['2021-02-01'] * 3)
    q_strikes = [0.75, 0.25, 0.5]
    res = tm._skew(df, 'relativeStrike', 'iv', q_strikes, tm.NormalizationMode.OUTRIGHT)
    expected = tm.ExtendedSeries(pd.Series([10, 100], name='iv',
                                           index=pd.to_datetime(['2021-01-01', '2021-02-01'])))
    assert_series_equal(res, expected)

    res = tm._skew(df, 'relativeStrike', 'iv', q_strikes, tm.NormalizationMode.NORMALIZED)
    expected = tm.ExtendedSeries(pd.Series([1.0, 0.6666666666666666], name='iv',
                                           index=pd.to_datetime(['2021-01-01', '2021-02-01'])))
    assert_series_equal(res, expected)
    with pytest.raises(MqValueError):
        tm._skew(df.loc[df['relativeStrike'].isin([0.25, 0.5])], 'relativeStrike', 'iv', q_strikes,
                 tm.NormalizationMode.NORMALIZED)


def _skew_term_no_data():
    replace = Replacer()
    market_mock = replace('gs_quant.api.utils.ThreadPoolManager.run_async', Mock())
    market_mock.side_effect = [[pd.DataFrame(), pd.DataFrame()], [pd.DataFrame(), MarketDataResponseFrame()]]

    actual = tm.skew_term(Index('MAXYZ', AssetClass.Equity, 'XYZ'), tm.SkewReference.DELTA, 777)
    assert actual.empty
    assert actual.dataset_ids == ()
    replace.restore()


def test__skew_term_fetcher():
    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures._market_data_timed', Mock())
    market_mock.side_effect = [MarketDataResponseFrame(), MqValueError(), MqValueError]
    actual = tm._skew_fetcher('ABC123', tm.QueryType.IMPLIED_VOLATILITY, {}, 'any', True, allow_exception=False)
    assert isinstance(actual, MarketDataResponseFrame) and actual.empty

    actual = tm._skew_fetcher('ABC123', tm.QueryType.IMPLIED_VOLATILITY, {}, 'any', True, allow_exception=True)
    assert isinstance(actual, MarketDataResponseFrame) and actual.empty

    with pytest.raises(MqValueError):
        tm._skew_fetcher('ABC123', tm.QueryType.IMPLIED_VOLATILITY, {}, 'any', True, allow_exception=False)
    replace.restore()


def test_skew_term():
    with DataContext('2018-01-01', '2040-01-01'):
        _skew_term_typical(tm.SkewReference.DELTA, 25)
        _skew_term_empty()
        _skew_term_no_data()
    with DataContext('2018-01-16', '2018-12-31'):
        out = _skew_term_typical(tm.SkewReference.DELTA, 25)
        assert out.empty
        assert set(out.dataset_ids) == set(_test_datasets + _test_datasets2)
    with DataContext('2018-01-16', '2018-12-31'):
        out = _skew_term_typical_fx(tm.SkewReference.DELTA, 25)
        assert out.empty
    with pytest.raises(NotImplementedError):
        tm.skew_term(..., tm.SkewReference.SPOT, 100, real_time=True)
    with DataContext('2020-01-01', '2021-01-01'):
        with pytest.raises(MqError, match='forward looking date range'):
            tm.skew_term(Index('MA123', AssetClass.Equity, '123'), tm.SkewReference.SPOT, 100, source='plottool')
    with DataContext(datetime.date.today(), datetime.date.today()):
        with pytest.raises(MqError, match='forward looking date range'):
            tm.skew_term(Index('MA123', AssetClass.Equity, '123'), tm.SkewReference.SPOT, 100, source='plottool')


def _vol_term_fx(reference, value):
    assert DataContext.current_is_set
    data = {
        'tenor': ['1w', '2w', '1y', '2y'],
        'impliedVolatility': [1, 2, 3, 4]
    }
    out = MarketDataResponseFrame(data=data, index=pd.DatetimeIndex(['2018-01-01'] * 4))
    out.dataset_ids = _test_datasets

    data_expiry = {
        'expirationDate': ['2018-01-06', '2018-01-08'],
        'impliedVolatilityByExpiration': [1.2, 1.1]
    }
    out_expiry = MarketDataResponseFrame(data=data_expiry, index=pd.DatetimeIndex(['2018-01-01'] * 2))
    out_expiry.dataset_ids = _test_datasets2

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.side_effect = [out, out_expiry]
    cross_mock = replace('gs_quant.timeseries.measures.cross_stored_direction_for_fx_vol', Mock())
    cross_mock.return_value = 'EURUSD'

    actual = tm.vol_term(Cross('ABCDE', 'EURUSD'), reference, value)
    idx = pd.DatetimeIndex(['2018-01-06', '2018-01-08', '2018-01-15', '2019-01-01', '2020-01-01'],
                           name='expirationDate')
    expected = pd.Series([1.2, 1, 2, 3, 4], name='impliedVolatility', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]

    if expected.empty:
        assert actual.empty
    else:
        assert_series_equal(expected, pd.Series(actual))
        assert set(actual.dataset_ids) == set(_test_datasets + _test_datasets2)

    replace.restore()
    return actual


def _vol_term_fx_no_expiry(reference, value):
    assert DataContext.current_is_set
    data = {
        'tenor': ['1w', '2w', '1y', '2y'],
        'impliedVolatility': [1, 2, 3, 4]
    }
    out = MarketDataResponseFrame(data=data, index=pd.DatetimeIndex(['2018-01-01'] * 4))
    out.dataset_ids = _test_datasets

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.side_effect = [out, MqValueError('a')]
    cross_mock = replace('gs_quant.timeseries.measures.cross_stored_direction_for_fx_vol', Mock())
    cross_mock.return_value = 'EURUSD'

    actual = tm.vol_term(Cross('ABCDE', 'EURUSD'), reference, value)
    idx = pd.DatetimeIndex(['2018-01-08', '2018-01-15', '2019-01-01'],
                           name='expirationDate')
    expected = pd.Series([1, 2, 3], name='impliedVolatility', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]

    if expected.empty:
        assert actual.empty
    else:
        assert_series_equal(expected, pd.Series(actual))
        assert actual.dataset_ids == _test_datasets

    replace.restore()
    return actual


def test_vol_term_fx():
    with pytest.raises(MqError):
        tm.vol_term(Cross('MABLUE', 'BLUE'), tm.VolReference.SPOT, 50)
    with pytest.raises(MqError):
        tm.vol_term(Cross('MABLUE', 'BLUE'), tm.VolReference.NORMALIZED, 1)
    with pytest.raises(MqError):
        tm.vol_term(Cross('MABLUE', 'BLUE'), tm.VolReference.DELTA_NEUTRAL, 1)
    with DataContext('2018-01-01', '2019-01-01'):
        _vol_term_fx(tm.VolReference.DELTA_CALL, 50)
    with DataContext('2018-01-01', '2019-01-01'):
        _vol_term_fx(tm.VolReference.DELTA_PUT, 50)
    with DataContext('2018-01-01', '2019-01-01'):
        _vol_term_fx_no_expiry(tm.VolReference.DELTA_PUT, 50)


def _fwd_term_typical():
    assert DataContext.current_is_set
    data = {
        'tenor': ['1w', '2w', '1y', '2y'],
        'forward': [1, 2, 3, 4]
    }
    out = MarketDataResponseFrame(data=data, index=pd.DatetimeIndex(['2018-01-01'] * 4))
    out.dataset_ids = _test_datasets

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = out

    actual = tm.fwd_term(Index('MA123', AssetClass.Equity, '123'))
    idx = pd.DatetimeIndex(['2018-01-08', '2018-01-15', '2019-01-01', '2020-01-01'], name='expirationDate')
    expected = pd.Series([1, 2, 3, 4], name='forward', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]

    if expected.empty:
        assert actual.empty
    else:
        assert_series_equal(expected, pd.Series(actual))
        assert actual.dataset_ids == _test_datasets
    market_mock.assert_called_once()

    replace.restore()
    return actual


def _fx_fwd_term_typical():
    assert DataContext.current_is_set
    fwd_data = {
        'tenor': ['1w', '2w', '1y', '2y'],
        'forwardPoint': [1, 2, 3, 4]
    }
    out = MarketDataResponseFrame(data=fwd_data, index=pd.DatetimeIndex(['2018-01-01'] * 4))
    out.dataset_ids = _test_datasets

    spot_data = {'spot': [100], 'assetId': 'MA123'}
    spot = MarketDataResponseFrame(data=spot_data, index=pd.DatetimeIndex(['2018-01-01']))

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = out

    fx_asset_mock = replace('gs_quant.timeseries.measures.cross_stored_direction_for_fx_vol', Mock())
    fx_asset_mock.return_value = 'MA123'

    thread_mock = replace('gs_quant.api.utils.ThreadPoolManager.run_async', Mock())
    thread_mock.return_value = [out, spot]

    actual = tm.fx_fwd_term(Index('MA123', AssetClass.FX, '123'))
    idx = pd.DatetimeIndex(['2018-01-08', '2018-01-15', '2019-01-01', '2020-01-01'], name='expirationDate')
    expected = pd.Series([101, 102, 103, 104], name='forwardPoint', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]
    assert_series_equal(pd.Series(actual), expected)
    assert actual.dataset_ids == _test_datasets

    actual = tm.fx_fwd_term(Index('MA123', AssetClass.FX, '123'), fwd_type=tm.FXForwardType.POINTS)
    idx = pd.DatetimeIndex(['2018-01-08', '2018-01-15', '2019-01-01', '2020-01-01'], name='expirationDate')
    expected = pd.Series([1, 2, 3, 4], name='forwardPoint', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]
    assert_series_equal(pd.Series(actual), expected)
    assert actual.dataset_ids == _test_datasets

    with pytest.raises(NotImplementedError):
        tm.fx_fwd_term(..., real_time=True)

    replace.restore()


def _fwd_term_empty():
    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = mock_empty_market_data_response()

    actual = tm.fwd_term(Index('MAXYZ', AssetClass.Equity, 'XYZ'))
    assert actual.empty
    assert actual.dataset_ids == ()
    market_mock.assert_called_once()

    replace.restore()


def _fx_fwd_term_empty():
    replace = Replacer()
    fx_asset_mock = replace('gs_quant.timeseries.measures.cross_stored_direction_for_fx_vol', Mock())
    fx_asset_mock.return_value = 'MA123'

    thread_mock = replace('gs_quant.api.utils.ThreadPoolManager.run_async', Mock())
    thread_mock.return_value = [pd.DataFrame(), pd.DataFrame()]

    actual = tm.fx_fwd_term(Index('MAXYZ', AssetClass.FX, 'XYZ'))
    assert actual.empty
    assert actual.dataset_ids == ()
    replace.restore()


def _fx_fwd_term():
    with DataContext('2018-01-01', '2019-01-01'):
        _fx_fwd_term_typical()
        _fx_fwd_term_empty()


def _fwd_term_equity():
    with DataContext('2018-01-01', '2019-01-01'):
        _fwd_term_typical()
        _fwd_term_empty()
    with DataContext('2018-01-16', '2018-12-31'):
        out = _fwd_term_typical()
        assert out.empty
        assert out.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.fwd_term(..., real_time=True)


def test_fwd_term():
    _fx_fwd_term()
    _fwd_term_equity()


def _carry_term_typical():
    assert DataContext.current_is_set
    fwd_data = {
        'tenor': ['1w', '2w', '1y', '2y'],
        'forwardPoint': [1, 2, 3, 4]
    }
    out = MarketDataResponseFrame(data=fwd_data, index=pd.DatetimeIndex(['2018-01-01'] * 4))
    out.dataset_ids = _test_datasets

    spot_data = {'spot': [100], 'assetId': 'MA123'}
    spot = MarketDataResponseFrame(data=spot_data, index=pd.DatetimeIndex(['2018-01-01']))

    replace = Replacer()
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = out

    fx_asset_mock = replace('gs_quant.timeseries.measures.cross_stored_direction_for_fx_vol', Mock())
    fx_asset_mock.return_value = 'MA123'

    thread_mock = replace('gs_quant.api.utils.ThreadPoolManager.run_async', Mock())
    thread_mock.return_value = [out, spot]

    actual = tm.carry_term(Index('MA123', AssetClass.FX, '123'), annualized=tm.FXSpotCarry.DAILY)
    idx = pd.DatetimeIndex(['2018-01-08', '2018-01-15', '2019-01-01', '2020-01-01'], name='expirationDate')
    expected = pd.Series([0.01, 0.02, 0.03, 0.04], name='carry', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]

    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    actual = tm.carry_term(Index('MA123', AssetClass.FX, '123'))
    idx = pd.DatetimeIndex(['2018-01-08', '2018-01-15', '2019-01-01', '2020-01-01'], name='expirationDate')
    expected = pd.Series([0.001667, 0.004714, 0.036105, 0.05621], name='carry', index=idx)
    expected = expected.loc[DataContext.current.start_date: DataContext.current.end_date]

    assert_series_equal(expected, pd.Series(actual), check_exact=False, check_less_precise=3)
    assert actual.dataset_ids == _test_datasets

    with pytest.raises(NotImplementedError):
        tm.carry_term(Index('MA123', AssetClass.FX, '123'), real_time=True)

    thread_mock.return_value = [pd.DataFrame(), pd.DataFrame()]
    actual = tm.carry_term(Index('MA123', AssetClass.FX, '123'))
    assert actual.empty
    replace.restore()


def test_carry_term():
    with DataContext('2018-01-01', '2019-01-01'):
        _carry_term_typical()


def test_measure_request_safe():
    def error(raise_error):
        if raise_error:
            raise MqValueError()

    with pytest.raises(MqValueError):
        tm.measure_request_safe('error', Index('MA123', AssetClass.Equity, '123'), error, None, True)
    tm.measure_request_safe('error', Index('MA123', AssetClass.Equity, '123'), error, None, False)


def test_bucketize_price():
    target = {
        '7x24': [27.323461],
        'offpeak': [26.004816],
        'peak': [27.982783],
        '7x8': [26.004816],
        '2x16h': [],
        'monthly': [],
        'CAISO 7x24': [26.953743375],
        'CAISO peak': [29.547952562499997],
        'MISO 7x24': [27.076390749999998],
        'MISO offpeak': [25.263605624999997],
    }

    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_commod)
    mock_pjm = Index('MA001', AssetClass.Commod, 'PJM')
    mock_caiso = Index('MA002', AssetClass.Commod, 'CAISO')
    mock_miso = Index('MA003', AssetClass.Commod, 'MISO')

    with DataContext(datetime.date(2019, 5, 1), datetime.date(2019, 5, 1)):
        bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
        bbid_mock.return_value = 'MISO'

        actual = tm.bucketize_price(mock_miso, 'LMP', bucket='7x24')
        assert_series_equal(pd.Series(target['MISO 7x24'],
                                      index=[datetime.date(2019, 5, 1)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.bucketize_price(mock_miso, 'LMP', bucket='offpeak')
        assert_series_equal(pd.Series(target['MISO offpeak'],
                                      index=[datetime.date(2019, 5, 1)],
                                      name='price'),
                            pd.Series(actual))

        bbid_mock.return_value = 'CAISO'

        actual = tm.bucketize_price(mock_caiso, 'LMP', bucket='7x24')
        assert_series_equal(pd.Series(target['CAISO 7x24'],
                                      index=[datetime.date(2019, 5, 1)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.bucketize_price(mock_caiso, 'LMP', bucket='peak')
        assert_series_equal(pd.Series(target['CAISO peak'],
                                      index=[datetime.date(2019, 5, 1)],
                                      name='price'),
                            pd.Series(actual))

        bbid_mock.return_value = 'PJM'

        actual = tm.bucketize_price(mock_pjm, 'LMP', bucket='7x24')
        assert_series_equal(pd.Series(target['7x24'],
                                      index=[datetime.date(2019, 5, 1)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.bucketize_price(mock_pjm, 'LMP', bucket='offpeak')
        assert_series_equal(pd.Series(target['offpeak'],
                                      index=[datetime.date(2019, 5, 1)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.bucketize_price(mock_pjm, 'LMP', bucket='peak')
        assert_series_equal(pd.Series(target['peak'],
                                      index=[datetime.date(2019, 5, 1)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.bucketize_price(mock_pjm, 'LMP', bucket='7x8')
        assert_series_equal(pd.Series(target['7x8'],
                                      index=[datetime.date(2019, 5, 1)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.bucketize_price(mock_pjm, 'LMP', bucket='2x16h')
        assert_series_equal(pd.Series(target['2x16h'],
                                      index=[],
                                      dtype=float,
                                      name='price'),
                            pd.Series(actual))

        actual = tm.bucketize_price(mock_pjm, 'LMP', granularity='m', bucket='7X24')
        assert_series_equal(pd.Series(target['monthly'],
                                      index=[],
                                      dtype=float,
                                      name='price'),
                            pd.Series(actual))

        with pytest.raises(ValueError):
            tm.bucketize_price(mock_pjm, 'LMP', bucket='7X24', real_time=True)

        with pytest.raises(ValueError):
            tm.bucketize_price(mock_pjm, 'LMP', bucket='weekday')

        with pytest.raises(ValueError):
            tm.bucketize_price(mock_caiso, 'LMP', bucket='weekday')

        with pytest.raises(ValueError):
            tm.bucketize_price(mock_pjm, 'LMP', granularity='yearly')

    replace.restore()

    # No market data
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = mock_empty_market_data_response()
    with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
        bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
        bbid_mock.return_value = 'MISO'
        actual = tm.bucketize_price(mock_miso, 'LMP', bucket='7x24')
        assert_series_equal(pd.Series(dtype='float64'), pd.Series(actual))

    # Duplicate data in dataset
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_commod_dup)
    with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
        bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
        bbid_mock.return_value = 'MISO'
        with pytest.raises(ValueError):
            tm.bucketize_price(mock_miso, 'LMP', bucket='peak')

    replace.restore()


def test_forward_price():
    # US Power
    target = {
        '7x24': [19.46101],
        'peak': [23.86745],
        'J20 7x24': [18.11768888888889],
        'J20-K20 7x24': [19.283921311475414],
        'J20-K20 offpeak': [15.82870707070707],
        'J20-K20 7x8': [13.020144262295084],
    }
    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_forward_price)
    mock_spp = Index('MA001', AssetClass.Commod, 'SPP')

    with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
        bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
        bbid_mock.return_value = 'SPP'

        # Should return empty series as mark for '7x8' bucket is missing
        actual = tm.forward_price(mock_spp,
                                  price_method='LMP',
                                  contract_range='2Q20',
                                  bucket='7x24'
                                  )
        assert_series_equal(pd.Series(target['7x24'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.forward_price(mock_spp,
                                  price_method='LMP',
                                  contract_range='J20',
                                  bucket='7x24'
                                  )
        assert_series_equal(pd.Series(target['J20 7x24'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.forward_price(mock_spp,
                                  price_method='LMP',
                                  contract_range='2Q20',
                                  bucket='PEAK'
                                  )
        assert_series_equal(pd.Series(target['peak'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.forward_price(mock_spp,
                                  price_method='LMP',
                                  contract_range='J20-K20',
                                  bucket='7x24'
                                  )
        assert_series_equal(pd.Series(target['J20-K20 7x24'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.forward_price(mock_spp,
                                  price_method='LMP',
                                  contract_range='J20-K20',
                                  bucket='offpeak'
                                  )
        assert_series_equal(pd.Series(target['J20-K20 offpeak'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.forward_price(mock_spp,
                                  price_method='LMP',
                                  contract_range='J20-K20',
                                  bucket='7x8'
                                  )
        assert_series_equal(pd.Series(target['J20-K20 7x8'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.forward_price(mock_spp,
                                  price_method='lmp',
                                  contract_range='2Q20',
                                  bucket='7x24'
                                  )
        assert_series_equal(pd.Series(target['7x24'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual)
                            )

        with pytest.raises(ValueError):
            tm.forward_price(mock_spp,
                             price_method='LMP',
                             contract_range='5Q20',
                             bucket='PEAK'
                             )

        with pytest.raises(ValueError):
            tm.forward_price(mock_spp,
                             price_method='LMP',
                             contract_range='Invalid',
                             bucket='PEAK'
                             )

        with pytest.raises(ValueError):
            tm.forward_price(mock_spp,
                             price_method='LMP',
                             contract_range='3H20',
                             bucket='7x24'
                             )

        with pytest.raises(ValueError):
            tm.forward_price(mock_spp,
                             price_method='LMP',
                             contract_range='F20-I20',
                             bucket='7x24'
                             )

        with pytest.raises(ValueError):
            tm.forward_price(mock_spp,
                             price_method='LMP',
                             contract_range='2H20',
                             bucket='7x24',
                             real_time=True
                             )

        replace.restore()

        replace = Replacer()
        replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_missing_bucket_forward_price)
        bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
        bbid_mock.return_value = 'SPP'

        actual = tm.forward_price(mock_spp,
                                  price_method='LMP',
                                  contract_range='2Q20',
                                  bucket='7x24'
                                  )

        assert_series_equal(pd.Series(dtype=float), pd.Series(actual), check_names=False)

        actual = tm.forward_price(mock_spp,
                                  price_method='LMP',
                                  contract_range='2Q20',
                                  bucket='PEAK'
                                  )
        assert_series_equal(pd.Series(target['peak'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm.forward_price(mock_spp,
                                  price_method='LMP',
                                  contract_range='J20-K20',
                                  bucket='7x24'
                                  )
        assert_series_equal(pd.Series(target['J20-K20 7x24'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual))
        replace.restore()

        # No market data
        market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
        market_mock.return_value = mock_empty_market_data_response()
        bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
        bbid_mock.return_value = 'SPP'
        with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
            actual = tm.forward_price(mock_spp,
                                      price_method='LMP',
                                      contract_range='2Q20',
                                      bucket='7x24'
                                      )
            assert_series_equal(pd.Series(dtype='float64'), pd.Series(actual))
        replace.restore()

        # Query for asset with no bbid
        replace = Replacer()
        replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_forward_price)
        mock_ice = CommodityPowerAggregatedNodes('MA001', 'ICE Mid C')

        with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
            bbId_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
            bbId_mock.return_value = None
            ISO_mock = replace('gs_quant.timeseries.measures.Asset.get_entity', Mock())
            ISO_mock.return_value = {'parameters': {'ISO': 'PJM'}}

            # Should work as other usecases where assetId has bbid
            actual = tm.forward_price(mock_ice,
                                      price_method='LMP',
                                      contract_range='2Q20',
                                      bucket='7x24'
                                      )
            assert_series_equal(pd.Series(target['7x24'],
                                          index=[datetime.date(2019, 1, 2)],
                                          name='price'),
                                pd.Series(actual))
        replace.restore()


def test_implied_volatility_elec():
    # US Power
    target = {
        '7x24': [0.403352],
        'peak': [0.383025],
        'J20 7x24': [0.372178],
        'J20-K20 7x24': [0.3737661202185792],
        'J20-K20 offpeak': [0.3922989898989899],
        'J20-K20 7x8': [0.4322360655737705],
    }
    replace = Replacer()
    mock_get_data = replace('gs_quant.data.dataset.Dataset.get_data', Mock())
    mock_get_data.return_value = mock_implied_volatility_elec()
    mock_spp = Index('MA001', AssetClass.Commod, 'SPP')

    with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
        bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
        bbid_mock.return_value = 'SPP'

        # Should return empty series as mark for '7x8' bucket is missing
        actual = tm.implied_volatility_elec(mock_spp,
                                            price_method='LMP',
                                            contract_range='2Q20',
                                            bucket='7x24'
                                            )
        assert_series_equal(pd.Series(target['7x24'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual)
                            )

        actual = tm.implied_volatility_elec(mock_spp,
                                            price_method='LMP',
                                            contract_range='J20',
                                            bucket='7x24'
                                            )
        assert_series_equal(pd.Series(target['J20 7x24'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual)
                            )

        actual = tm.implied_volatility_elec(mock_spp,
                                            price_method='LMP',
                                            contract_range='2Q20',
                                            bucket='PEAK'
                                            )
        assert_series_equal(pd.Series(target['peak'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual)
                            )

        actual = tm.implied_volatility_elec(mock_spp,
                                            price_method='LMP',
                                            contract_range='J20-K20',
                                            bucket='7x24'
                                            )
        assert_series_equal(pd.Series(target['J20-K20 7x24'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual)
                            )

        actual = tm.implied_volatility_elec(mock_spp,
                                            price_method='LMP',
                                            contract_range='J20-K20',
                                            bucket='offpeak'
                                            )
        assert_series_equal(pd.Series(target['J20-K20 offpeak'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual)
                            )

        actual = tm.implied_volatility_elec(mock_spp,
                                            price_method='LMP',
                                            contract_range='J20-K20',
                                            bucket='7x8'
                                            )
        assert_series_equal(pd.Series(target['J20-K20 7x8'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual)
                            )

        actual = tm.implied_volatility_elec(mock_spp,
                                            price_method='lmp',
                                            contract_range='2Q20',
                                            bucket='7x24'
                                            )
        assert_series_equal(pd.Series(target['7x24'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual)
                            )

        with pytest.raises(ValueError):
            tm.implied_volatility_elec(mock_spp,
                                       price_method='LMP',
                                       contract_range='5Q20',
                                       bucket='PEAK'
                                       )

        with pytest.raises(ValueError):
            tm.implied_volatility_elec(mock_spp,
                                       price_method='LMP',
                                       contract_range='Invalid',
                                       bucket='PEAK'
                                       )

        with pytest.raises(ValueError):
            tm.implied_volatility_elec(mock_spp,
                                       price_method='LMP',
                                       contract_range='3H20',
                                       bucket='7x24'
                                       )

        with pytest.raises(ValueError):
            tm.implied_volatility_elec(mock_spp,
                                       price_method='LMP',
                                       contract_range='F20-I20',
                                       bucket='7x24'
                                       )

        with pytest.raises(ValueError):
            tm.implied_volatility_elec(mock_spp,
                                       price_method='LMP',
                                       contract_range='2H20',
                                       bucket='7x24',
                                       real_time=True
                                       )

        replace.restore()

        replace = Replacer()
        mock_get_data = replace('gs_quant.data.dataset.Dataset.get_data', Mock())
        mock_get_data.return_value = mock_missing_bucket_implied_volatility()
        bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
        bbid_mock.return_value = 'SPP'

        actual = tm.implied_volatility_elec(mock_spp,
                                            price_method='LMP',
                                            contract_range='2Q20',
                                            bucket='7x24'
                                            )

        assert_series_equal(pd.Series(dtype=float), pd.Series(actual), check_names=False)

        actual = tm.implied_volatility_elec(mock_spp,
                                            price_method='LMP',
                                            contract_range='2Q20',
                                            bucket='PEAK'
                                            )
        assert_series_equal(pd.Series(target['peak'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual)
                            )

        actual = tm.implied_volatility_elec(mock_spp,
                                            price_method='LMP',
                                            contract_range='J20-K20',
                                            bucket='7x24'
                                            )
        assert_series_equal(pd.Series(target['J20-K20 7x24'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual)
                            )
        replace.restore()

        # No market data
        mock_get_data = replace('gs_quant.data.dataset.Dataset.get_data', Mock())
        mock_get_data.return_value = mock_empty_market_data_response()
        bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
        bbid_mock.return_value = 'SPP'
        with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
            actual = tm.implied_volatility_elec(mock_spp,
                                                price_method='LMP',
                                                contract_range='2Q20',
                                                bucket='7x24'
                                                )
            assert_series_equal(pd.Series(dtype='float64'), pd.Series(actual))
        replace.restore()


def test_forward_price_ng():
    # Tests for US NG assets
    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_natgas_forward_price)
    mock = CommodityNaturalGasHub('MA001', 'AGT')

    with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
        actual = pd.Series(tm.forward_price_ng(mock,
                                               price_method='GDD',
                                               contract_range='F21'))
        expected = pd.Series([2.880], index=[datetime.date(2019, 1, 2)], name='price')
        assert_series_equal(expected, actual)

        actual = pd.Series(tm.forward_price_ng(mock,
                                               price_method='GDD',
                                               contract_range='F21-G21'))
        expected = pd.Series([2.8629152542372878], index=[datetime.date(2019, 1, 2)], name='price')
        assert_series_equal(expected, actual)

        with pytest.raises(ValueError):
            tm.forward_price_ng(mock,
                                price_method='GDD',
                                contract_range='F21-I21')

        with pytest.raises(ValueError):
            tm.forward_price_ng(mock,
                                price_method='GDD',
                                contract_range='I21')

        with pytest.raises(MqTypeError):
            wrong_mock = Index('MA001', AssetClass.Commod, 'SPP')
            tm.forward_price_ng(wrong_mock,
                                price_method='GDD',
                                contract_range='I21')
        with pytest.raises(ValueError):
            tm.forward_price_ng(mock,
                                price_method='GDD',
                                contract_range='I21',
                                real_time=True)

    # No market data
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = mock_empty_market_data_response()
    with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
        actual = tm.forward_price_ng(mock,
                                     price_method='LMP',
                                     contract_range='2Q20')
        assert_series_equal(pd.Series(dtype='float64'),
                            pd.Series(actual)
                            )
    replace.restore()

    # Tests for EU NG assets
    mock_EU_asset = CommodityEUNaturalGasHub('MA001', 'TTF')
    mock_EU_swap_asset = Swap('MA002', AssetClass.Commod, 'Swap NatGas TTF')
    mock_EU_swap_asset.id = "Mock_Id"
    assets = replace('gs_quant.timeseries.measures.GsAssetApi.get_many_assets', Mock())
    assets.return_value = [mock_EU_swap_asset]

    # Test for forward price fetch using instrument id
    with DataContext(datetime.date(2021, 1, 1), datetime.date(2021, 1, 1)):
        market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eu_natgas_forward_price)
        expected = pd.Series([15.65], name='price', index=[datetime.date(2021, 1, 1)])
        actual = tm.forward_price_ng(mock_EU_asset, contract_range='H21')
        assert_series_equal(expected, pd.Series(actual))

    # Test for empty market response
    with DataContext(datetime.date(2021, 1, 1), datetime.date(2021, 1, 1)):
        market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
        market_mock.return_value = mock_empty_market_data_response()
        actual = tm.forward_price_ng(mock_EU_asset, contract_range='H21', price_method='USD')
        assert actual.empty

    # Test for no instruments found
    with DataContext(datetime.date(2021, 1, 1), datetime.date(2021, 1, 1)):
        assets.return_value = []
        market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
        market_mock.return_value = mock_empty_market_data_response()
        actual = tm.forward_price_ng(mock_EU_asset, contract_range='H21')
        assert actual.empty

    replace.restore()


def test_implied_volatility_ng():
    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_natgas_implied_volatility)
    mock = CommodityNaturalGasHub('MA001', 'AGT')

    with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
        actual = pd.Series(tm.implied_volatility_ng(mock,
                                                    price_method='GDD',
                                                    contract_range='F21'))
        expected = pd.Series([2.880], index=[datetime.date(2019, 1, 2)], name='price')
        assert_series_equal(expected, actual)

        actual = pd.Series(tm.implied_volatility_ng(mock,
                                                    price_method='GDD',
                                                    contract_range='F21-G21'))
        expected = pd.Series([2.8629152542372878], index=[datetime.date(2019, 1, 2)], name='price')
        assert_series_equal(expected, actual)

        with pytest.raises(ValueError):
            tm.implied_volatility_ng(mock,
                                     price_method='GDD',
                                     contract_range='F21-I21')

        with pytest.raises(ValueError):
            tm.implied_volatility_ng(mock,
                                     price_method='GDD',
                                     contract_range='I21')

        with pytest.raises(ValueError):
            tm.implied_volatility_ng(mock,
                                     price_method='GDD',
                                     contract_range='I21',
                                     real_time=True)

    # No market data
    market_mock = replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', Mock())
    market_mock.return_value = mock_empty_market_data_response()
    with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
        actual = tm.implied_volatility_ng(mock,
                                          price_method='LMP',
                                          contract_range='2Q20')
        assert_series_equal(pd.Series(dtype='float64'),
                            pd.Series(actual)
                            )
    replace.restore()


def test_get_iso_data():
    tz_map = {'MISO': 'US/Central', 'CAISO': 'US/Pacific'}
    for key in tz_map:
        assert (tm._get_iso_data(key)[0] == tz_map[key])


def test_string_to_date_interval():
    assert (tm._string_to_date_interval("K20")['start_date'] == datetime.date(2020, 5, 1))
    assert (tm._string_to_date_interval("K20")['end_date'] == datetime.date(2020, 5, 31))

    assert (tm._string_to_date_interval("k20")['start_date'] == datetime.date(2020, 5, 1))
    assert (tm._string_to_date_interval("k20")['end_date'] == datetime.date(2020, 5, 31))

    assert (tm._string_to_date_interval("Cal22")['start_date'] == datetime.date(2022, 1, 1))
    assert (tm._string_to_date_interval("Cal22")['end_date'] == datetime.date(2022, 12, 31))

    assert (tm._string_to_date_interval("Cal2012")['start_date'] == datetime.date(2012, 1, 1))
    assert (tm._string_to_date_interval("Cal2012")['end_date'] == datetime.date(2012, 12, 31))

    assert (tm._string_to_date_interval("Cal53")['start_date'] == datetime.date(1953, 1, 1))
    assert (tm._string_to_date_interval("Cal53")['end_date'] == datetime.date(1953, 12, 31))

    assert (tm._string_to_date_interval("2010")['start_date'] == datetime.date(2010, 1, 1))
    assert (tm._string_to_date_interval("2010")['end_date'] == datetime.date(2010, 12, 31))

    assert (tm._string_to_date_interval("3Q20")['start_date'] == datetime.date(2020, 7, 1))
    assert (tm._string_to_date_interval("3Q20")['end_date'] == datetime.date(2020, 9, 30))

    assert (tm._string_to_date_interval("2h2021")['start_date'] == datetime.date(2021, 7, 1))
    assert (tm._string_to_date_interval("2h2021")['end_date'] == datetime.date(2021, 12, 31))

    assert (tm._string_to_date_interval("3q20")['start_date'] == datetime.date(2020, 7, 1))
    assert (tm._string_to_date_interval("3q20")['end_date'] == datetime.date(2020, 9, 30))

    assert (tm._string_to_date_interval("2H2021")['start_date'] == datetime.date(2021, 7, 1))
    assert (tm._string_to_date_interval("2H2021")['end_date'] == datetime.date(2021, 12, 31))

    assert (tm._string_to_date_interval("Mar2021")['start_date'] == datetime.date(2021, 3, 1))
    assert (tm._string_to_date_interval("Mar2021")['end_date'] == datetime.date(2021, 3, 31))

    assert (tm._string_to_date_interval("March2021")['start_date'] == datetime.date(2021, 3, 1))
    assert (tm._string_to_date_interval("March2021")['end_date'] == datetime.date(2021, 3, 31))

    assert (tm._string_to_date_interval("5Q20") == "Invalid Quarter")

    assert (tm._string_to_date_interval("HH2021") == "Invalid num")

    assert (tm._string_to_date_interval("3H2021") == "Invalid Half Year")

    assert (tm._string_to_date_interval("Cal2a") == "Invalid year")
    assert (tm._string_to_date_interval("Marc201") == "Invalid date code")

    assert (tm._string_to_date_interval("M1a2021") == "Invalid date code")

    assert (tm._string_to_date_interval("Marcha2021") == "Invalid date code")

    assert (tm._string_to_date_interval("I20") == "Invalid month")

    assert (tm._string_to_date_interval("20") == "Unknown date code")


def test_implied_vol_commod():
    target = {
        'F21': [2.880],
        'F21-H21': [2.815756],
    }
    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_implied_volatility)
    mock = Index('MA001', AssetClass.Commod, 'Option NG Exchange')

    with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
        actual = tm.implied_volatility(mock,
                                       tenor='F21-H21')
        assert_series_equal(pd.Series(target['F21-H21'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual))
    replace.restore()


def test_fair_price():
    target = {
        'F21': [2.880],
        'F21-H21': [2.815756],
    }
    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_fair_price)
    mock = Index('MA001', AssetClass.Commod, 'Swap NG Exchange')
    mock2 = Swap('MA002', AssetClass.Commod, 'Swap Oil')

    with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
        actual = tm.fair_price(mock,
                               tenor='F21')
        assert_series_equal(pd.Series(target['F21'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual))
    with pytest.raises(ValueError):
        tm.fair_price(mock,
                      tenor=None)
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_fair_price_swap)
    with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
        actual = tm.fair_price(mock2)

        assert_series_equal(pd.Series([2.880],
                                      index=[pd.Timestamp('2019-01-02')],
                                      name='fairPrice'),
                            pd.Series(actual),
                            )

    replace.restore()


def test_weighted_average_valuation_curve_for_calendar_strip():
    target = {
        'F21': [2.880],
        'F21-H21': [2.815756],
    }
    replace = Replacer()
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_fair_price)
    mock = Index('MA001', AssetClass.Commod, 'Swap NG Exchange')

    with DataContext(datetime.date(2019, 1, 2), datetime.date(2019, 1, 2)):
        actual = tm._weighted_average_valuation_curve_for_calendar_strip(mock,
                                                                         contract_range='F21',
                                                                         query_type=QueryType.FAIR_PRICE,
                                                                         measure_field='fairPrice'
                                                                         )
        assert_series_equal(pd.Series(target['F21'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual))

        actual = tm._weighted_average_valuation_curve_for_calendar_strip(mock,
                                                                         contract_range='F21-H21',
                                                                         query_type=QueryType.FAIR_PRICE,
                                                                         measure_field='fairPrice'
                                                                         )
        assert_series_equal(pd.Series(target['F21-H21'],
                                      index=[datetime.date(2019, 1, 2)],
                                      name='price'),
                            pd.Series(actual))

        with pytest.raises(ValueError):
            tm._weighted_average_valuation_curve_for_calendar_strip(mock,
                                                                    contract_range='Invalid',
                                                                    query_type=QueryType.FAIR_PRICE,
                                                                    measure_field='fairPrice'
                                                                    )
        with pytest.raises(ValueError):
            tm._weighted_average_valuation_curve_for_calendar_strip(mock,
                                                                    contract_range='F20-I20',
                                                                    query_type=QueryType.FAIR_PRICE,
                                                                    measure_field='fairPrice'
                                                                    )
        with pytest.raises(ValueError):
            tm._weighted_average_valuation_curve_for_calendar_strip(mock,
                                                                    contract_range='3H20',
                                                                    query_type=QueryType.PRICE,
                                                                    measure_field='fairPrice'
                                                                    )

    replace.restore()


def test_fundamental_metrics():
    replace = Replacer()
    mock_spx = Index('MA890', AssetClass.Equity, 'SPX')
    mock_rb = CustomBasket(GsAsset(AssetClass.Equity, AssetType.Research_Basket, 'GSTEST01'))
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_eq)
    period = '1y'
    direction = tm.FundamentalMetricPeriodDirection.FORWARD

    actual = tm.dividend_yield(mock_spx, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.dividend_yield(..., period, direction, real_time=True)

    actual = tm.earnings_per_share(mock_spx, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.earnings_per_share(..., period, direction, real_time=True)

    actual = tm.earnings_per_share_positive(mock_spx, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.earnings_per_share_positive(..., period, direction, real_time=True)

    actual = tm.net_debt_to_ebitda(mock_spx, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.net_debt_to_ebitda(..., period, direction, real_time=True)

    actual = tm.price_to_book(mock_spx, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.price_to_book(..., period, direction, real_time=True)

    actual = tm.price_to_cash(mock_spx, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.price_to_cash(..., period, direction, real_time=True)

    actual = tm.price_to_earnings(mock_spx, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.price_to_earnings(..., period, direction, real_time=True)

    actual = tm.price_to_earnings_positive(mock_spx, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.price_to_earnings_positive(..., period, direction, real_time=True)

    actual = tm.price_to_sales(mock_spx, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.price_to_sales(..., period, direction, real_time=True)

    actual = tm.return_on_equity(mock_spx, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.return_on_equity(..., period, direction, real_time=True)

    actual = tm.sales_per_share(mock_spx, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.sales_per_share(..., period, direction, real_time=True)

    actual = tm.current_constituents_dividend_yield(mock_rb, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.current_constituents_dividend_yield(..., period, direction, real_time=True)

    actual = tm.current_constituents_earnings_per_share(mock_rb, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.current_constituents_earnings_per_share(..., period, direction, real_time=True)

    actual = tm.current_constituents_earnings_per_share_positive(mock_rb, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.current_constituents_earnings_per_share_positive(..., period, direction, real_time=True)

    actual = tm.current_constituents_net_debt_to_ebitda(mock_rb, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.current_constituents_net_debt_to_ebitda(..., period, direction, real_time=True)

    actual = tm.current_constituents_price_to_book(mock_rb, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.current_constituents_price_to_book(..., period, direction, real_time=True)

    actual = tm.current_constituents_price_to_cash(mock_rb, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.current_constituents_price_to_cash(..., period, direction, real_time=True)

    actual = tm.current_constituents_price_to_earnings(mock_rb, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.current_constituents_price_to_earnings(..., period, direction, real_time=True)

    actual = tm.current_constituents_price_to_earnings_positive(mock_rb, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.current_constituents_price_to_earnings_positive(..., period, direction, real_time=True)

    actual = tm.current_constituents_price_to_sales(mock_rb, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.current_constituents_price_to_sales(..., period, direction, real_time=True)

    actual = tm.current_constituents_return_on_equity(mock_rb, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.current_constituents_return_on_equity(..., period, direction, real_time=True)

    actual = tm.current_constituents_sales_per_share(mock_rb, period, direction)
    assert_series_equal(pd.Series([5, 1, 2], index=_index * 3, name='fundamentalMetric'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets
    with pytest.raises(NotImplementedError):
        tm.current_constituents_sales_per_share(..., period, direction, real_time=True)

    replace.restore()


def test_realized_volatility():
    from gs_quant.timeseries.econometrics import volatility, Returns
    from gs_quant.timeseries.statistics import generate_series

    random = generate_series(100).rename('spot')
    window = 10
    type_ = Returns.SIMPLE

    replace = Replacer()
    mock_data = replace('gs_quant.timeseries.measures.get_historical_and_last_for_measure', Mock())
    return_value = MarketDataResponseFrame(random)
    return_value.dataset_ids = _test_datasets
    mock_data.return_value = return_value

    expected = volatility(random, window, type_)
    actual = tm.realized_volatility(Cross('MA123', 'ABCXYZ'), window, type_)
    assert_series_equal(expected, pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    replace.restore()


def test_esg_headline_metric():
    replace = Replacer()

    mock_aapl = Stock('MA4B66MW5E27U9VBB94', 'AAPL')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_esg)
    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.ENVIRONMENTAL_SOCIAL_NUMERIC_SCORE)
    assert_series_equal(pd.Series([2, 4, 6], index=_index * 3, name='esNumericScore'), pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.ENVIRONMENTAL_SOCIAL_POLICY_SCORE)
    assert_series_equal(pd.Series([2, 4, 6], index=_index * 3, name='esPolicyScore'), pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.ENVIRONMENTAL_SOCIAL_AGGREGATE_SCORE)
    assert_series_equal(pd.Series([2, 4, 6], index=_index * 3, name='esScore'), pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.ENVIRONMENTAL_SOCIAL_PRODUCT_IMPACT_SCORE)
    assert_series_equal(pd.Series([2, 4, 6], index=_index * 3, name='esProductImpactScore'), pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.GOVERNANCE_AGGREGATE_SCORE)
    assert_series_equal(pd.Series([2, 4, 6], index=_index * 3, name='gScore'), pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.ENVIRONMENTAL_SOCIAL_MOMENTUM_SCORE)
    assert_series_equal(pd.Series([2, 4, 6], index=_index * 3, name='esMomentumScore'), pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.GOVERNANCE_REGIONAL_SCORE)
    assert_series_equal(pd.Series([2, 4, 6], index=_index * 3, name='gRegionalScore'), pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.CONTROVERSY_SCORE)
    assert_series_equal(pd.Series([2, 4, 6], index=_index * 3, name='controversyScore'), pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.ENVIRONMENTAL_SOCIAL_NUMERIC_PERCENTILE)
    assert_series_equal(pd.Series([81.2, 75.4, 65.7], index=_index * 3, name='esNumericPercentile'), pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.ENVIRONMENTAL_SOCIAL_POLICY_PERCENTILE)
    assert_series_equal(pd.Series([81.2, 75.4, 65.7], index=_index * 3, name='esPolicyPercentile'), pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.ENVIRONMENTAL_SOCIAL_AGGREGATE_PERCENTILE)
    assert_series_equal(pd.Series([81.2, 75.4, 65.7], index=_index * 3, name='esPercentile'), pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.ENVIRONMENTAL_SOCIAL_PRODUCT_IMPACT_PERCENTILE)
    assert_series_equal(pd.Series([81.2, 75.4, 65.7], index=_index * 3, name='esProductImpactPercentile'),
                        pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.GOVERNANCE_AGGREGATE_PERCENTILE)
    assert_series_equal(pd.Series([81.2, 75.4, 65.7], index=_index * 3, name='gPercentile'),
                        pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.ENVIRONMENTAL_SOCIAL_MOMENTUM_PERCENTILE)
    assert_series_equal(pd.Series([81.2, 75.4, 65.7], index=_index * 3, name='esMomentumPercentile'),
                        pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.GOVERNANCE_REGIONAL_PERCENTILE)
    assert_series_equal(pd.Series([81.2, 75.4, 65.7], index=_index * 3, name='gRegionalPercentile'),
                        pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.CONTROVERSY_PERCENTILE)
    assert_series_equal(pd.Series([81.2, 75.4, 65.7], index=_index * 3, name='controversyPercentile'),
                        pd.Series(actual))

    actual = tm.esg_headline_metric(mock_aapl, tm.EsgMetric.ENVIRONMENTAL_SOCIAL_DISCLOSURE)
    assert_series_equal(pd.Series([49.2, 55.7, 98.4], index=_index * 3, name='esDisclosurePercentage'),
                        pd.Series(actual))

    with pytest.raises(NotImplementedError):
        tm.esg_headline_metric(mock_aapl, tm.EsgMetric.ENVIRONMENTAL_SOCIAL_NUMERIC_SCORE, real_time=True)

    replace.restore()


def test_rating():
    replace = Replacer()

    mock_aapl = Stock('MA4B66MW5E27U9VBB94', 'AAPL')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_rating)
    actual = tm.rating(mock_aapl, tm._RatingMetric.RATING)
    assert_series_equal(pd.Series([1, -1, 1, 0], index=pd.to_datetime([datetime.date(2020, 8, 13),
                                                                       datetime.date(2020, 8, 14),
                                                                       datetime.date(2020, 8, 17),
                                                                       datetime.date(2020, 8, 18)]),
                                  name='rating'), pd.Series(actual))

    actual = tm.rating(mock_aapl, tm._RatingMetric.CONVICTION_LIST)
    assert_series_equal(pd.Series([1, 0, 0, 0], index=pd.to_datetime([datetime.date(2020, 8, 13),
                                                                      datetime.date(2020, 8, 14),
                                                                      datetime.date(2020, 8, 17),
                                                                      datetime.date(2020, 8, 18)]),
                                  name='convictionList'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    with pytest.raises(NotImplementedError):
        tm.rating(mock_aapl, tm._RatingMetric.RATING, real_time=True)
    replace.restore()


def test_fair_value(mocker):
    mocker.patch.object(GsSession.__class__, 'default_value',
                        return_value=GsSession.get(Environment.QA, 'client_id', 'secret'))
    replace = Replacer()
    mock_usdeur = Cross('MAQB05GD31BA5HWV', 'USDEUR')
    mock_eurusd = Cross('MAA0NE9QX2ABETG6', "EURUSD")
    xrefs = replace('gs_quant.timeseries.measures.GsAssetApi.get_asset_xrefs', Mock())
    xrefs.return_value = [GsTemporalXRef(dt.date(2019, 1, 1), dt.date(2952, 12, 31), XRef(bbid='EURUSD', ))]
    asset = replace('gs_quant.markets.securities.SecurityMaster.get_asset', Mock())
    asset.return_value = mock_usdeur
    replace('gs_quant.timeseries.measures.Dataset.get_data', mock_gsdeer_gsfeer)

    index = [dt.date(2000, 1, 1), dt.date(2010, 4, 1), dt.date(2020, 7, 1)]
    actual = tm.fair_value(mock_usdeur,
                           tm.EquilibriumExchangeRateMetric.GSDEER)
    assert_series_equal(pd.Series([1, 1.2, 1.1], index=index, name='gsdeer'),
                        pd.Series(actual))

    actual = tm.fair_value(mock_usdeur,
                           tm.EquilibriumExchangeRateMetric.GSFEER)
    assert_series_equal(pd.Series([2, 1.8, 1.9], index=index, name='gsfeer'),
                        pd.Series(actual))

    actual = tm.fair_value(mock_eurusd,
                           tm.EquilibriumExchangeRateMetric.GSDEER)
    assert_series_equal(pd.Series([1 / 1, 1 / 1.2, 1 / 1.1], index=index, name='gsdeer'),
                        pd.Series(actual))

    actual = tm.fair_value(mock_eurusd,
                           tm.EquilibriumExchangeRateMetric.GSFEER)
    assert_series_equal(pd.Series([1 / 2, 1 / 1.8, 1 / 1.9], index=index, name='gsfeer'),
                        pd.Series(actual))
    with pytest.raises(NotImplementedError):
        tm.fair_value(mock_usdeur,
                      tm.EquilibriumExchangeRateMetric.GSDEER,
                      real_time=True)
    replace.restore()


def test_factor_profile():
    replace = Replacer()

    mock_aapl = Stock('MA4B66MW5E27U9VBB94', 'AAPL')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_factor_profile)
    actual = tm.factor_profile(mock_aapl, tm._FactorProfileMetric.GROWTH_SCORE)
    assert_series_equal(pd.Series([0.238, 0.234, 0.234, 0.230], index=pd.to_datetime([datetime.date(2020, 8, 13),
                                                                                      datetime.date(2020, 8, 14),
                                                                                      datetime.date(2020, 8, 17),
                                                                                      datetime.date(2020, 8, 18)]),
                                  name='growthScore'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    actual = tm.factor_profile(mock_aapl, tm._FactorProfileMetric.FINANCIAL_RETURNS_SCORE)
    assert_series_equal(pd.Series([0.982, 0.982, 0.982, 0.982], index=pd.to_datetime([datetime.date(2020, 8, 13),
                                                                                      datetime.date(2020, 8, 14),
                                                                                      datetime.date(2020, 8, 17),
                                                                                      datetime.date(2020, 8, 18)]),
                                  name='financialReturnsScore'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    actual = tm.factor_profile(mock_aapl, tm._FactorProfileMetric.MULTIPLE_SCORE)
    assert_series_equal(pd.Series([0.204, 0.192, 0.190, 0.190], index=pd.to_datetime([datetime.date(2020, 8, 13),
                                                                                      datetime.date(2020, 8, 14),
                                                                                      datetime.date(2020, 8, 17),
                                                                                      datetime.date(2020, 8, 18)]),
                                  name='multipleScore'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    actual = tm.factor_profile(mock_aapl, tm._FactorProfileMetric.INTEGRATED_SCORE)
    assert_series_equal(pd.Series([0.672, 0.676, 0.676, 0.674], index=pd.to_datetime([datetime.date(2020, 8, 13),
                                                                                      datetime.date(2020, 8, 14),
                                                                                      datetime.date(2020, 8, 17),
                                                                                      datetime.date(2020, 8, 18)]),
                                  name='integratedScore'), pd.Series(actual))
    assert actual.dataset_ids == _test_datasets

    with pytest.raises(NotImplementedError):
        tm.factor_profile(mock_aapl, tm._FactorProfileMetric.GROWTH_SCORE, real_time=True)

    replace.restore()


def test_commodity_forecast():
    replace = Replacer()

    mock_spgcsb = Index('MA74Y70Z4D4TBX9H', 'SPGCSB', 'GSCI Sugar')
    replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_commodity_forecast)
    actual = tm.commodity_forecast(mock_spgcsb, '3m',
                                   tm._CommodityForecastType.SPOT_RETURN)
    assert_series_equal(pd.Series([1700, 1400, 1500, 1600], index=pd.to_datetime([datetime.date(2020, 8, 13),
                                                                                  datetime.date(2020, 8, 14),
                                                                                  datetime.date(2020, 8, 17),
                                                                                  datetime.date(2020, 8, 18)]),
                                  name='commodityForecast'), pd.Series(actual))

    with pytest.raises(NotImplementedError):
        tm.commodity_forecast(mock_spgcsb, '3m',
                              tm._CommodityForecastType.SPOT_RETURN, real_time=True)
    replace.restore()


def test_spot_carry():
    replace = Replacer()
    mock = Cross('MAA0NE9QX2ABETG6', 'USD/EUR')
    assets = replace('gs_quant.timeseries.measures.cross_stored_direction_for_fx_vol', Mock())
    assets.return_value = mock.get_marquee_id()
    df = pd.DataFrame({
        '3m': [0.001978858350951374, 0.0019735843327766817, 0.00198293829264794],
        '2y': [0.016989429175475686, 0.016994753976688093, 0.017416104834150414],
        '3m_ann': [0.007660096842392416, 0.007400941247912555, 0.0075142924774027195],
        'date': [pd.Timestamp('2020-09-02'), pd.Timestamp('2020-09-03'), pd.Timestamp('2020-09-04')],
        'settlementDate': [pd.Timestamp('2020-12-04'), pd.Timestamp('2020-12-08'), pd.Timestamp('2020-12-08')]
    })
    df = df.set_index('date')

    with DataContext(dt.date(2020, 9, 2), dt.date(2020, 9, 4)):
        # tenors in terms of months
        replace('gs_quant.data.dataset.Dataset.get_data', mock_fx_spot_fwd_3m)
        actual_3m = tm.spot_carry(mock, '3m')
        assert_series_equal(df['3m'], pd.Series(actual_3m, name='3m'))

        # annualized
        actual_3m_ann = tm.spot_carry(mock, '3m', tm.FXSpotCarry.ANNUALIZED)
        assert_series_equal(df['3m_ann'], pd.Series(actual_3m_ann, name='3m_ann'))

        with pytest.raises(MqError):
            tm.spot_carry(mock, '13m')

        # tenors in terms of years
        replace('gs_quant.data.dataset.Dataset.get_data', mock_fx_spot_fwd_2y)
        actual_2y = tm.spot_carry(mock, '2y')
        assert_series_equal(df['2y'], pd.Series(actual_2y, name='2y'))

    with DataContext(pd.Timestamp('2020-09-02'), pd.Timestamp('2020-09-04')):
        df.index.names = ['time']

        replace('gs_quant.data.dataset.Dataset.get_data', mock_fx_spot_fwd_3m_rt)
        actual_3m = tm.spot_carry(mock, '3m', real_time=True)
        assert_series_equal(df['3m'], pd.Series(actual_3m, name='3m'))

        replace('gs_quant.data.dataset.Dataset.get_data', mock_fx_spot_fwd_2y_rt)
        actual_2y = tm.spot_carry(mock, '2y', real_time=True)
        assert_series_equal(df['2y'], pd.Series(actual_2y, name='2y'))

    replace.restore()


def test_fx_implied_correlation():
    replace = Replacer()

    e1 = {'last_updated_time': '2021-02-03T15:11:06.409Z', 'owner_id': 'qwerty',
          'classifications': {}, 'description': 'Europe Euro to United States Dollar',
          'last_updated_by_id': 'qwerty', 'id': 'MAA0NE9QX2ABETG6', 'listed': True,
          'created_time': '2017-06-16T13:59:23.617Z', 'exchange': '', 'parameters': {}, 'type': 'Cross',
          'entitlements': {'edit': ['guid:qwerty'],
                           'view': ['internal', 'guid:qwerty', 'external'],
                           'admin': ['guid:qwerty']}, 'name': 'EURUSD',
          'region': 'Global', 'styles': [], 'short_name': 'EURUSD',
          'identifiers': [{'type': 'SECNAME', 'value': 'USD/EUR'}, {'type': 'BID', 'value': 'EURUSD'},
                          {'type': 'MDAPI', 'value': 'USD/EUR'}, {'type': 'CROSS', 'value': 'USD/EUR'}],
          'rank': 1.0, 'asset_class': 'FX',
          'xref': {'bbid': 'EURUSD', 'cross': 'USD/EUR', 'secName': 'USD/EUR', 'mdapi': 'USD/EUR'},
          'currency': 'USD', 'tags': ['USDEUR', 'EURUSD', 'Execution'],
          'created_by_id': 'qwerty'}
    e2 = {'last_updated_time': '2021-02-03T15:05:20.993Z', 'owner_id': 'azerty',
          'classifications': {}, 'description': 'United States Dollar to Europe Euro',
          'last_updated_by_id': 'azerty', 'id': 'MAQB05GD31BA5HWV', 'listed': True,
          'created_time': '2017-06-16T13:52:21.396Z', 'exchange': '', 'parameters': {}, 'type': 'Cross',
          'entitlements': {'edit': ['guid:azerty'],
                           'view': ['internal', 'guid:azerty', 'external'],
                           'admin': ['guid:azerty']}, 'name': 'USDEUR', 'region': 'Global',
          'styles': [], 'short_name': 'USDEUR', 'rank': 1.0,
          'asset_class': 'FX', 'xref': {'bbid': 'USDEUR', 'cross': 'EUR/USD', 'secName': 'EUR/USD', 'mdapi': 'EUR/USD'},
          'currency': 'EUR', 'tags': ['EURUSD', 'USDEUR'], 'created_by_id': 'azerty'}

    EURUSD = Cross('MAA0NE9QX2ABETG6', 'USD/EUR', entity=e1)
    USDEUR = Cross('MAQB05GD31BA5HWV', 'EUR/USD', entity=e2)
    USDJPY = Cross('MATGYV0J9MPX534Z', 'JPY/USD')
    JPYUSD = Cross('MAYJPCVVF2RWXCES', 'USD/JPY')
    EURGBP = Cross('MA3AMDKY4YJ83G1E', 'GBP/EUR')
    EURJPY = Cross('MAYPHS80JRWDJ8RC', 'JPY/EUR')
    JPYEUR = Cross('MAWVKC8Q6405ZWNE', 'EUR/JPY')
    bbid_to_asset_dict = {'EURUSD': EURUSD, 'USDEUR': USDEUR, 'USDJPY': USDJPY, 'JPYUSD': JPYUSD,
                          'EURJPY': EURJPY, 'JPYEUR': JPYEUR, 'EURGBP': EURGBP}
    mqid_to_asset_dict = {'MAA0NE9QX2ABETG6': EURUSD, 'MAQB05GD31BA5HWV': USDEUR, 'MATGYV0J9MPX534Z': USDJPY,
                          'MAYJPCVVF2RWXCES': JPYUSD, 'MAYPHS80JRWDJ8RC': EURJPY, 'MAWVKC8Q6405ZWNE': JPYEUR,
                          'MA3AMDKY4YJ83G1E': EURGBP}
    df = pd.DataFrame({
        'EURUSD USDJPY': [-0.47579170795443204, -0.4842168280011238, -0.48220935681227195],
        'USDEUR USDJPY': [0.47579175, 0.48421678, 0.48220939],
        'EURUSD JPYUSD': [0.47579175, 0.48421678, 0.48220939],
        'USDEUR JPYUSD': [-0.47579175, -0.48421678, -0.48220939],
        'USDJPY EURUSD': [-0.47579175, -0.48421678, -0.48220939],
        'date': [pd.Timestamp('2020-09-02'), pd.Timestamp('2020-09-03'), pd.Timestamp('2020-09-04')]
    })
    df = df.set_index('date')

    def mock_get_asset(cls, asset_id, id_type: dict) -> Cross:
        if id_type == AssetIdentifier.BLOOMBERG_ID:
            return bbid_to_asset_dict[asset_id]
        else:
            return mqid_to_asset_dict[asset_id]

    def cross_to_bbid(asset, id_type) -> str:
        if asset == EURUSD:
            return 'EURUSD'
        elif asset == USDEUR:
            return 'USDEUR'
        elif asset == USDJPY:
            return 'USDJPY'
        elif asset == JPYUSD:
            return 'JPYUSD'
        elif asset == EURJPY:
            return 'EURJPY'
        elif asset == JPYEUR:
            return 'JPYEUR'
        elif asset == EURGBP:
            return 'EURGBP'

    replace('gs_quant.markets.securities.SecurityMaster.get_asset', mock_get_asset)
    replace('gs_quant.markets.securities.Asset.get_identifier', cross_to_bbid)
    replace('gs_quant.timeseries.measures._market_data_timed', mock_fx_correlation)

    with DataContext(dt.date(2020, 9, 2), dt.date(2020, 9, 4)):
        # supported
        q_1 = tm.fx_implied_correlation(EURUSD, USDJPY, '3m')
        q_2 = tm.fx_implied_correlation(USDEUR, USDJPY, '3m')
        q_3 = tm.fx_implied_correlation(EURUSD, JPYUSD, '3m')
        q_4 = tm.fx_implied_correlation(USDEUR, JPYUSD, '3m')
        q_5 = tm.fx_implied_correlation(USDJPY, EURUSD, '3m')
        assert_series_equal(df['EURUSD USDJPY'], pd.Series(q_1, name='EURUSD USDJPY'))
        assert_series_equal(df['USDEUR USDJPY'], pd.Series(q_2, name='USDEUR USDJPY'))
        assert_series_equal(df['EURUSD JPYUSD'], pd.Series(q_3, name='EURUSD JPYUSD'))
        assert_series_equal(df['USDEUR JPYUSD'], pd.Series(q_4, name='USDEUR JPYUSD'))
        assert_series_equal(df['USDJPY EURUSD'], pd.Series(q_5, name='USDJPY EURUSD'))

        replace('gs_quant.timeseries.measures._market_data_timed', lambda q, request_id=None: pd.DataFrame())
        assert tm.fx_implied_correlation(USDJPY, EURUSD, '3m').empty

        # not supported
        with pytest.raises(NotImplementedError):
            tm.fx_implied_correlation(EURUSD, EURGBP, '3m', real_time=True)
        with pytest.raises(MqError):
            tm.fx_implied_correlation(USDJPY, EURGBP, '3m', real_time=False)
        with pytest.raises(MqError):
            mock_spx = Index('MA890', AssetClass.Equity, 'SPX')
            tm.fx_implied_correlation(USDJPY, mock_spx, '3m', real_time=False)

    replace.restore()


def mock_forward_curve_peak(_cls, _q, ignore_errors=False):
    d = {
        'forwardPrice': [40.05],
        'quantityBucket': ["PEAK"],
        'contract': ["U20"]
    }
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2020, 8, 20)]))
    df.dataset_ids = _test_datasets
    return df


def mock_forward_curve_peak_holiday(_cls, _q, ignore_errors=False):
    d = {
        'forwardPrice': [26.567302],
        'quantityBucket': ["PEAK"],
        'contract': ["U20"]
    }
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2020, 9, 4)]))
    df.dataset_ids = _test_datasets
    return df


def mock_forward_curve_offpeak(_cls, _q, ignore_errors=False):
    d = {
        'forwardPrice': [30.9692, 44.9868],
        'quantityBucket': ["7X8", "SUH1X16"],
        'contract': ["U20", "U20"]
    }
    df = MarketDataResponseFrame(data=d, index=pd.to_datetime([datetime.date(2020, 8, 20)] * 2))
    df.dataset_ids = _test_datasets
    return df


def mock_empty_forward_curve(_cls, _q, ignore_errors=False):
    df = MarketDataResponseFrame()
    df.dataset_ids = ()
    return df


def test_forward_curve():
    # Set output and mock attributes for test
    target = {
        'Peak_CAISO': [40.05],
        'OffPeak_CAISO': [34.4736],
        'Peak_CAISO_holiday': [26.567302]
    }
    replace = Replacer()
    mock_CAISO = Index('MA001', AssetClass.Commod, 'CAISO')
    bbid_mock = replace('gs_quant.timeseries.measures.Asset.get_identifier', Mock())
    bbid_mock.return_value = 'CAISO'
    mock_get_data = replace('gs_quant.data.dataset.Dataset.get_data_last', Mock())

    with DataContext(datetime.date(2020, 8, 20), datetime.date(2020, 9, 20)):
        # Test for term structure for bucket: peak
        replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_forward_curve_peak)
        mock_get_data.return_value = pd.DataFrame(data=[0], index=[datetime.date(2020, 8, 20)])
        actual = tm.forward_curve(mock_CAISO, bucket='Peak', market_date='20200820')
        assert_series_equal(pd.Series(target['Peak_CAISO'], index=[datetime.date(2020, 9, 1)], name='forwardPrice'),
                            pd.Series(actual))

        # Test for holiday date query, which would fetch value for prev date
        replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_forward_curve_peak_holiday)
        mock_get_data.return_value = pd.DataFrame(data=[0], index=[datetime.date(2020, 9, 4)])
        actual = tm.forward_curve(mock_CAISO, bucket='Peak', market_date='20200907')
        assert_series_equal(pd.Series(target['Peak_CAISO_holiday'], index=[datetime.date(2020, 9, 1)],
                                      name='forwardPrice'), pd.Series(actual))

        # Test for term structure for bucket: Off peak, which requires wtd avg computation
        replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_forward_curve_offpeak)
        mock_get_data.return_value = pd.DataFrame(data=[0], index=[datetime.date(2020, 8, 25)])
        actual = tm.forward_curve(mock_CAISO, bucket='OffPeak', market_date='20200825')
        assert_series_equal(pd.Series(target['OffPeak_CAISO'], index=[datetime.date(2020, 9, 1)], name='forwardPrice'),
                            pd.Series(actual))

        # Test for an empty data query result
        replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_empty_forward_curve)
        mock_get_data.return_value = pd.DataFrame(data=[0], index=[datetime.date(2020, 8, 25)])
        actual = tm.forward_curve(mock_CAISO, bucket='OffPeak', market_date='20200825')
        assert actual.empty

        # Test for intra day frequency error
        with pytest.raises(MqValueError):
            tm.forward_curve(mock_CAISO, bucket='7x24', market_date='20200820', real_time=True)

        # Test for future date query
        with pytest.raises(MqValueError):
            tm.forward_curve(mock_CAISO, bucket='7x24', market_date='20400820')

        # Test for weekend date query
        with pytest.raises(MqValueError):
            tm.forward_curve(mock_CAISO, bucket='7x24', market_date='20200822')

        # Test for term structure for market date beyond end date which is an invalid scenario
        with pytest.raises(MqValueError):
            tm.forward_curve(mock_CAISO, bucket='OffPeak', market_date='20201001')

        # Test for empty market date in parameters, will fail as end date for this test is in past
        with pytest.raises(MqValueError):
            actual = tm.forward_curve(mock_CAISO, bucket='Peak')

        # Test for invalid market_date data type
        with pytest.raises(MqTypeError):
            actual = tm.forward_curve(mock_CAISO, bucket='Peak', market_date=20201001)

        # Test for invalid market_date string format
        with pytest.raises(MqValueError):
            actual = tm.forward_curve(mock_CAISO, bucket='Peak', market_date='9, Jan 2020')

        # Test for default date always returning a weekday
        replace('gs_quant.timeseries.measures.GsDataApi.get_market_data', mock_forward_curve_peak)
        mock_todate = replace('pandas.Timestamp.today', Mock())
        mock_todate.return_value = pd.Timestamp('2020-08-22')
        mock_get_data.return_value = pd.DataFrame(data=[0], index=[datetime.date(2020, 8, 20)])
        actual = tm.forward_curve(mock_CAISO, bucket='Peak')
        assert_series_equal(pd.Series(target['Peak_CAISO'], index=[datetime.date(2020, 9, 1)], name='forwardPrice'),
                            pd.Series(actual))

    replace.restore()


def test_eu_ng_hub_to_swap():
    # Test for Id provider to return an instrument
    replace = Replacer()
    mock_EU_asset = CommodityEUNaturalGasHub('MA001', 'TTF')
    mock_EU_swap_asset = Swap('MA002', AssetClass.Commod, 'Swap NatGas TTF')
    mock_EU_swap_asset.id = 'MA002'
    assets = replace('gs_quant.timeseries.measures.GsAssetApi.get_many_assets', Mock())
    assets.return_value = [mock_EU_swap_asset]
    actual = tm.eu_ng_hub_to_swap(mock_EU_asset)
    assert_equal(actual, 'MA002')

    # Test for Id provider when no instrument found, returns original asset id
    mock_EU_asset = CommodityEUNaturalGasHub('MA001', 'TTF')
    assets = replace('gs_quant.timeseries.measures.GsAssetApi.get_many_assets', Mock())
    assets.return_value = []
    actual = tm.eu_ng_hub_to_swap(mock_EU_asset)
    assert_equal(actual, 'MA001')
    replace.restore()


def test_settlement_price():
    # Tests for settlement price function
    replace = Replacer()

    with DataContext(datetime.date(2021, 6, 2), datetime.date(2021, 6, 2)):
        # Test for EEX Asset
        Asset_Mock = FutureMarket('MA001', AssetClass.Commod, 'DEBM')
        EEX_mock = replace('gs_quant.timeseries.measures.Asset.get_entity', Mock())
        EEX_mock.return_value = {'parameters': {'exchange': 'EEX', 'productGroup': 'PowerFutures'}}
        EEX_ds = replace('gs_quant.data.dataset.Dataset.get_data', Mock())
        EEX_ds.return_value = pd.DataFrame(data=dict(settlementPrice=20.20, contract='K21'),
                                           index=[datetime.date(2021, 6, 2)])
        actual = pd.Series(tm.settlement_price(Asset_Mock, contract='K21'))
        expected = pd.Series([20.20], index=[datetime.date(2021, 6, 2)], name='settlementPrice')
        assert_series_equal(expected, actual)

        # Test for CarbonCredit Asset
        Asset_Mock = FutureMarket('MA001', AssetClass.Commod, 'RGGI V19')
        CC_mock = replace('gs_quant.timeseries.measures.Asset.get_entity', Mock())
        CC_mock.return_value = {'parameters': {'exchange': 'ICE', 'productGroup': 'Physical Environment'}}
        CC_ds = replace('gs_quant.data.dataset.Dataset.get_data', Mock())
        CC_ds.return_value = pd.DataFrame(data=dict(settlementPrice=21.21, contract='K21'),
                                          index=[datetime.date(2021, 6, 2)])
        actual = pd.Series(tm.settlement_price(Asset_Mock, contract='K21'))
        expected = pd.Series([21.21], index=[datetime.date(2021, 6, 2)], name='settlementPrice')
        assert_series_equal(expected, actual)

        # Test for ICE Power Asset
        Asset_Mock = FutureMarket('MA001', AssetClass.Commod, 'GAB')
        CC_mock = replace('gs_quant.timeseries.measures.Asset.get_entity', Mock())
        CC_mock.return_value = {'parameters': {'exchange': 'ICE', 'productGroup': 'PowerFutures'}}
        CC_ds = replace('gs_quant.data.dataset.Dataset.get_data', Mock())
        CC_ds.return_value = pd.DataFrame(data=dict(settlementPrice=22.22, contract='K21'),
                                          index=[datetime.date(2021, 6, 2)])
        actual = pd.Series(tm.settlement_price(Asset_Mock, contract='K21'))
        expected = pd.Series([22.22], index=[datetime.date(2021, 6, 2)], name='settlementPrice')
        assert_series_equal(expected, actual)

        # Test for NASDAQ Power Asset
        Asset_Mock = FutureMarket('MA001', AssetClass.Commod, 'ENOFUTBL')
        CC_mock = replace('gs_quant.timeseries.measures.Asset.get_entity', Mock())
        CC_mock.return_value = {'parameters': {'exchange': 'NASDAQ', 'productGroup': 'PowerFutures'}}
        CC_ds = replace('gs_quant.data.dataset.Dataset.get_data', Mock())
        CC_ds.return_value = pd.DataFrame(data=dict(settlementPrice=23.23, contract='K21'),
                                          index=[datetime.date(2021, 6, 2)])
        actual = pd.Series(tm.settlement_price(Asset_Mock, contract='K21'))
        expected = pd.Series([23.23], index=[datetime.date(2021, 6, 2)], name='settlementPrice')
        assert_series_equal(expected, actual)

        # Test for empty  result
        CC_ds.return_value = pd.DataFrame()
        actual = pd.Series(tm.settlement_price(Asset_Mock, contract='K21'))
        expected = pd.Series(dtype=float)
        assert_series_equal(expected, actual)

        # Test for asset with no exchange info
        Asset_Mock = FutureMarket('MA001', AssetClass.Commod, 'XYZ')
        empty_mock = replace('gs_quant.timeseries.measures.Asset.get_entity', Mock())
        empty_mock.return_value = {'parameters': {}}

        with pytest.raises(MqTypeError):
            tm.settlement_price(Asset_Mock, contract='F21')

        # Test for asset with any only some asset params filled
        Asset_Mock = FutureMarket('MA001', AssetClass.Commod, 'XYZ')
        empty_mock = replace('gs_quant.timeseries.measures.Asset.get_entity', Mock())
        empty_mock.return_value = {'parameters': {'exchange': 'OTHER_EXCHANGE'}}

        with pytest.raises(MqTypeError):
            tm.settlement_price(Asset_Mock, contract='F21')

        # Test for asset with any other exchange, currently not covered
        Asset_Mock = FutureMarket('MA001', AssetClass.Commod, 'XYZ')
        empty_mock = replace('gs_quant.timeseries.measures.Asset.get_entity', Mock())
        empty_mock.return_value = {'parameters': {'exchange': 'OTHER_EXCHANGE', 'productGroup': 'OTHER_PRODUCT'}}

        with pytest.raises(MqTypeError):
            tm.settlement_price(Asset_Mock, contract='F21')

        # Test for real time data setting on query
        with pytest.raises(MqValueError):
            tm.settlement_price(Asset_Mock, contract='F21', real_time=True)

    replace.restore()


def test_hloc_prices():
    mock_spx = Index('MA890', AssetClass.Equity, 'SPX')
    with pytest.raises(MqValueError):
        tm.hloc_prices(mock_spx, real_time=True)

    replace = Replacer()
    empty_df = replace('gs_quant.timeseries.measures.Asset.get_hloc_prices', Mock())
    empty_df.return_value = pd.DataFrame()
    with DataContext(datetime.date(2021, 6, 2), datetime.date(2021, 6, 2)):
        tm.hloc_prices(mock_spx, real_time=False)

    replace.restore()


def test_thematic_model_exposure():
    mock_asset = GsAsset(asset_class='Equity', id='MA1234567890', type_='Custom Basket', name='test')
    mock_basket = CustomBasket(gs_asset=mock_asset, _finish_init=False)
    with pytest.raises(MqValueError):
        tm.thematic_model_exposure(mock_basket, 'TICKER', real_time=True)

    replace = Replacer()

    empty_df = replace('gs_quant.timeseries.measures.PositionedEntity.get_thematic_exposure', Mock())
    empty_df.return_value = pd.DataFrame()
    with DataContext(datetime.date(2021, 6, 2), datetime.date(2021, 6, 2)):
        tm.thematic_model_exposure(mock_basket, 'TICKER')

    replace.restore()


def test_thematic_model_beta():
    mock_asset = GsAsset(asset_class='Equity', id='MA1234567890', type_='Custom Basket', name='test')
    mock_basket = CustomBasket(gs_asset=mock_asset, _finish_init=False)
    with pytest.raises(MqValueError):
        tm.thematic_model_beta(mock_basket, 'TICKER', real_time=True)

    replace = Replacer()

    empty_df = replace('gs_quant.timeseries.measures.PositionedEntity.get_thematic_beta', Mock())
    empty_df.return_value = pd.DataFrame()
    with DataContext(datetime.date(2021, 6, 2), datetime.date(2021, 6, 2)):
        tm.thematic_model_beta(mock_basket, 'TICKER')

    replace.restore()


def test_thematic_model_beta_single_stock():
    mock_asset = GsAsset(asset_class='Equity', id='MA4B66MW5E27U9VBB94', type_='Single Stock', name='test')
    mock_asset_entity: Dict = json.loads(json.dumps(mock_asset.as_dict(), cls=JSONEncoder))
    mock_stock = Stock(mock_asset.id, mock_asset.name, mock_asset.exchange, mock_asset.currency,
                       entity=mock_asset_entity)
    with pytest.raises(MqValueError):
        tm.thematic_model_beta(mock_stock, 'TICKER', real_time=True)

    replace = Replacer()

    empty_df = replace('gs_quant.timeseries.measures.Stock.get_thematic_beta', Mock())
    empty_df.return_value = pd.DataFrame()
    with DataContext(datetime.date(2021, 6, 2), datetime.date(2021, 6, 2)):
        tm.thematic_model_beta(mock_stock, 'TICKER')

    replace.restore()


def test_retail_interest_agg():
    mock_spx = Index('MA890', AssetClass.Equity, 'SPX', entity={'type': 'Index', 'underlying_asset_ids': []})

    with pytest.raises(NotImplementedError):
        tm.retail_interest_agg(..., data_source=tm.UnderlyingSourceCategory.ALL,
                               sector=tm.GICSSector.ALL, real_time=True)
    with pytest.raises(MqError):
        tm.retail_interest_agg(mock_spx)

    mock_spx.entity['underlying_asset_ids'] = ['MA4B66MW5E27U9VBB6A', 'MA4B66MW5E27U9VBB86', 'MA4B66MW5E27U9VBB8U']

    replace = Replacer()
    positions_df = replace('gs_quant.timeseries.measures.PositionedEntity.get_positions_data', Mock())
    positions_df.return_value = {
        'id': ['MA4B66MW5E27UALNBLL', 'MA4B66MW5E27UALNBLL'],
        'assetClassificationsGicsSector': ['Energy', 'Health Care'],
        'positionDate': ['2012-12-20', '2012-12-20']
    }

    retail_df = pd.DataFrame(
        data={
            'shares': [10.0, 10.0],
            'notional': [100.0, 100.0],
            'underlyingSourceCategory': ['All', 'All'],
            'impliedRetailShares': [5.0, 3.0],
            'impliedRetailNotional': [60.0, 30.0],
            'impliedRetailPctShares': [50.0, 30.0],
            'impliedRetailPctNotional': [60.0, 30.0],
            'assetId': ['MA4B66MW5E27UALNBLL', 'MA4B66MW5E27UALNBLL'],
        },
        index=[pd.Timestamp('2021-12-20'), pd.Timestamp('2021-12-20')])
    retail_df_energy = pd.DataFrame(
        data={
            'shares': [10.0],
            'notional': [100.0],
            'underlyingSourceCategory': ['All'],
            'impliedRetailShares': [5.0],
            'impliedRetailNotional': [60.0],
            'impliedRetailPctShares': [50.0],
            'impliedRetailPctNotional': [60.0],
            'assetId': ['MA4B66MW5E27UALNBLL'],
        },
        index=[pd.Timestamp('2021-12-20')])
    mock_retail = replace('gs_quant.data.dataset.Dataset.get_data', Mock())
    mock_retail.return_value = retail_df
    expected_df = pd.DataFrame(
        data={'shares': [20.0], 'impliedRetailPctShares': [40.0], 'impliedRetailPctNotional': [60.0]},
        index=[pd.Timestamp('2021-12-20')])

    assert_series_equal(ExtendedSeries(expected_df['shares']),
                        tm.retail_interest_agg(mock_spx,
                                               tm.RetailMeasures.SHARES,
                                               tm.UnderlyingSourceCategory.ALL,
                                               tm.GICSSector.ALL))
    assert_series_equal(ExtendedSeries(expected_df['impliedRetailPctShares']),
                        tm.retail_interest_agg(mock_spx,
                                               tm.RetailMeasures.RETAIL_PCT_SHARES,
                                               tm.UnderlyingSourceCategory.ALL,
                                               tm.GICSSector.ALL))
    mock_retail.return_value = retail_df_energy
    assert_series_equal(ExtendedSeries(expected_df['impliedRetailPctNotional']),
                        tm.retail_interest_agg(mock_spx,
                                               tm.RetailMeasures.RETAIL_PCT_NOTIONAL,
                                               tm.UnderlyingSourceCategory.ALL,
                                               tm.GICSSector.ENERGY))
    mock_retail.return_value = pd.DataFrame(dtype=float)
    assert_series_equal(ExtendedSeries(dtype=float),
                        tm.retail_interest_agg(mock_spx,
                                               tm.RetailMeasures.RETAIL_PCT_NOTIONAL,
                                               tm.UnderlyingSourceCategory.ALL,
                                               tm.GICSSector.ENERGY))
    replace.restore()


if __name__ == '__main__':
    pytest.main(args=["test_measures.py"])
