from bs4 import BeautifulSoup
import datetime
import json
import pandas as pd
import re
import requests

from .error import no_site_error, not_200_response
from .search import search_funds, token_investment_strategy
from .utils import random_user_agent

from .security import Security



class Funds(Security):
    """
    Main class to access data about funds and etf, inherit from Security class

    Args:
        term (str): text to find a fund, can be a name, part of a name or the isin of the funds
        country (str) : text for code ISO 3166-1 alpha-2 of country, should be '' for etf
        pageSize (int): number of funds to return
        itemRange (int) : index of funds to return (must be inferior to PageSize)
        proxies = (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"}

    Examples:
        >>> Funds('0P0000712R', "ca", 9, 0)
        >>> Funds('bond', "uk", 25, 2)

    Raises:
        TypeError: raised whenever the parameter type is not the type expected
        ValueError : raised whenever the parameter is not valid or no fund found

    """

    def __init__(self, term = None, country: str = "", pageSize : int =1, itemRange: int = 0, filters: dict = {}, proxies: dict = {}):
        
        super().__init__(term=term,asset_type='fund',country=country,pageSize=pageSize,itemRange=itemRange,filters=filters,proxies=proxies)
        
        

    def allocationMap(self):
        """    
        This function retrieves the asset allocation of the funds, index and category.

        Returns:
            dict with allocation map

        Examples:
            >>> Funds("myria", "fr").allocationMap()

        """
        return self.GetData("process/asset/v2")

    def allocationWeighting(self):
        """
        This function retrieves the Growth/Blend/Value and market capitalizaton allocation size of the funds.

        Returns:
            dict with allocation

        Examples:
            >>> Funds("myria", "fr").allocationWeighting()
        
        """
        return self.GetData("process/weighting")

    def analystRating(self):
        """
        This function retrieves the rating of the funds

        Returns:
            list of dict with ratings

            >>> Funds("RMAGX", "us").analystRating()

        """
        

        return self.GetData("parent/analystRating")

    def analystRatingTopFunds(self):
        """
        This function retrieves the rating Top funds

        Returns:
            dict with ratings

            >>> Funds("RMAGX", "us").analystRatingTopFunds()
            
        """
        
        return self.GetData("parent/analystRating/topfunds")


    def analystRatingTopFundsUpDown(self):
        """
        This function retrieves the rating funds Up Down

        Returns:
            dict with ratings

            >>> Funds("RMAGX", "us").analystRatingTopFundsUpDown()
            
        """

        return self.GetData("parent/analystRating/topfundsUpDown")


    def AnnualPerformance(self, cat):
        """
        This function retrieves the annual performance of the funds, index, category or the annual rank of the funds.

        Args:
            cat (str) : possible values are category, funds, index, rank    
        Returns:
            dict annual performance or rank

        Raises:
            ValueError : raised whenever parameter cat is not category, funds, index, or rank

        Examples:
            >>> Funds("myria", "fr").AnnualPerformance("category")
            >>> Funds("myria", "fr").AnnualPerformance("funds")
            >>> Funds("myria", "fr").AnnualPerformance("index")
            >>> Funds("myria", "fr").AnnualPerformance("rank")

        """
        no_site_error(self.code,self.name,self.country,self.site)

        cat_row = {'funds' : 0,'category' : 1, 'index' : 2, 'rank' : 3}
        if cat not in cat_row:
            raise ValueError(f"cat parameter must take one of the following value : { ', '.join(cat_row.keys())}")

        result = {}
        #headers random agent
        headers = {'user-agent' : random_user_agent()}
        #page 1 - performance
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=1"
        
        response = requests.get(url, headers=headers, proxies=self.proxies)
        not_200_response(url,response)
        soup = BeautifulSoup(response.text, 'html.parser')
        #label are dates
        regex = re.compile('.*heading number')
        label_list = soup.find(id='returnsCalenderYearDiv').find_all('td', {"class": regex})
        #funds performance, category performance, index performance, rank in category
        regex = re.compile('.*value number')
        #values
        value_list = soup.find(id='returnsCalenderYearDiv').find_all('td', {"class": regex})

        regex = re.compile('-|\/')
        #first col is nothing
        for i in range(1, len(label_list)):
            label = label_list[i].text
            #if today

            if regex.search(label):
                label = 'current'
            #add category to label
            if label:
                
                result[f'{cat}_annual_performance_{label}'] = value_list[i+(cat_row[cat])*(len(label_list)-1)-1].text

        return result

    def benchmark(self):
        """
        This function retrieves the benchmark name of the funds.

        Returns:
            str benchmark name

        Examples:
            >>> Funds("myria", "fr").benchmark()

        """
        return self.referenceIndex("benchmark")

    def carbonMetrics(self):
        """
        This function retrieves the carbon metrics of the funds.

        Returns:
            dict with carbon metrics

        Examples:
            >>> Funds("myria", "fr").carbonMetrics()

        """

        return self.GetData("esg/carbonMetrics")

    def category(self):
        """
        This function retrieves the category name of the funds.

        Returns:
            str category name

        Examples:
            >>> Funds("myria", "fr").category()

        """
        return self.referenceIndex("category")

    def categoryAnnualPerformance(self):
        """
        This function retrieves the annual performance of the category.
  
        Returns:
            dict annual performance of the category


        Examples:
            >>> Funds("myria", "fr").categoryAnnualPerformance()

        """
        return self.AnnualPerformance('category')

    def categoryCumulativePerformance(self):
        """
        This function retrieves the cumulative performance of the category.
  
        Returns:
            dict cumulative performance of the category

        Examples:
            >>> Funds("myria", "fr").categoryCumulativePerformance()

        """
        
        return self.CumulativePerformance('category')

    def contact(self):
        """
        This function retrieves information about the asset manager.
  
        Returns:
            dict contact

        Examples:
            >>> Funds("myria", "fr").contact()

        """
        no_site_error(self.code,self.name,self.country,self.site)
        result = {}
        #headers random agent
        headers = {'user-agent' : random_user_agent()}
        #page 1 - performance
        #page 4 - info about found
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=4"
        response = requests.get(url, headers=headers,proxies=self.proxies)
        not_200_response(url,response)
        soup = BeautifulSoup(response.text, 'html.parser')
        #label
        label_list = soup.find(id='managementManagementDiv').find_all('td', {"class": "col1 label"})
        #value
        value_list = soup.find(id='managementManagementDiv').find_all('td', {"class": "col2 value number"})
        for i in range(0, len(value_list)):
            label = label_list[i].text
            
            result[label] = value_list[i].text

        return result

    def costIllustration(self):
        """
        This function retrieves the cost of the funds.
  
        Returns:
            dict cost of funds

        Examples:
            >>> Funds("FOUSA00E5P", "us").costIllustration()

        """
        return self.GetData("price/costIllustration")

    def couponRange(self):
        """
        This function retrieves the coupon of the funds, index and category.
  
        Returns:
            dict coupon

        Examples:
            >>> Funds("rmagx", "us").couponRange()

        """
        return self.GetData("process/couponRange")


    def creditQuality(self):
        """
        This function retrieves the credit notation of the funds, index and category.
  
        Returns:
            dict credit notation

        Examples:
            >>> Funds("rmagx", "us").creditQuality()

        """
        return self.GetData("portfolio/creditQuality")

    def dataPoint(self, field, currency ='EUR'):
        """
        This function retrieves infos about funds such as name, performance, risk metrics...

        Args:
        field (str or list) : field to find
        currency (str) : currency in 3 letters

        Returns:
            list of dict funds infos

        Example:
            >>> Funds("myria", "fr").dataPoint(['largestSector', 'Name', 'ongoingCharge'])
            >>> Funds("myria", "fr").dataPoint('SharpeM36')

        """
        return search_funds(self.code, field,self.country,10,currency, proxies=self.proxies)

    def distribution(self, period = "annual"):
        """
        This function retrieves the coupon distributed by the funds.

        Args:
            period (str) : annual or latest
        
        Raises:
            ValueError whenever the pariod parameter is not annual or latest

        Returns:
            dict distribution of coupon

        Example:
            >>> Funds("rmagx", "us").distribution("annual")
            
        """


        period_choice = ["annual", "latest"]
        if period not in period_choice:
            raise ValueError(f'period parameter can only take one of the values: {", ".join(period_choice)}')

        return self.GetData(f"distribution/{period}")



    def CumulativePerformance(self, cat):
        """
        This function retrieves the cumulative performance of funds, index and category.

        Args:
            cat (str) : possible values are category, funds, index

        Returns:
            dict cumulative performance

        Examples:
            >>> Funds("myria", "fr").CumulativePerformance("funds")
            >>> Funds("myria", "fr").CumulativePerformance("index")
            >>> Funds("myria", "fr").CumulativePerformance("category")

        """
        no_site_error(self.code,self.name,self.country,self.site)
        cat_row = {'funds' : 2,'category' : 3, 'index' : 4}
        if cat not in cat_row:
            raise ValueError(f"cat parameter must take one of the following value : { ', '.join(cat_row.keys())}")
        result = {}

        #headers random agent
        headers = {'user-agent' : random_user_agent()}
        #page 1 - performance
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=1"
        
        response = requests.get(url, headers=headers, proxies=self.proxies)
        not_200_response(url,response)
        soup = BeautifulSoup(response.text, 'html.parser')
        cumulative_performance_date = soup.find(id='returnsTrailingDiv').find('td', {"class": "titleBarNote"}).text
        result['cumulative_performance_date'] = cumulative_performance_date 
        #days
        regex = re.compile('.*label')
        label_list = soup.find(id='returnsTrailingDiv').find_all('td', {"class": regex})

        #cumulative performance cat
        regex = re.compile(f'.*col{str(cat_row[cat])} value number')
        value_list = soup.find(id='returnsTrailingDiv').find_all('td', {"class": regex})
        #loop on label
        for i in range(0, len(label_list)):
            #label
            label = label_list[i].text
            #perf funds
            result[f'{cat}_cumulative_performance_{label}'] = re.sub('[^0-9,-\.]','',value_list[i].text)
            
        return result

    def equityStyle(self):
        """
        This function retrieves the equity style of the funds and category.

        Returns:
            dict equity style

        Examples:
            >>> Funds("myria", "fr").equityStyle()

        """
        return self.GetData("process/stockStyle/v2")
    
    def equityStyleBoxHistory(self):
        """
        This function retrieves the equity style history of the funds

        Returns:
            dict equity style history

        Examples:
            >>> Funds("myria", "fr").equityStyleBoxHistory()

        """
        return self.GetData("process/equityStyleBoxHistory")

    def esgData(self):
        """
        This function retrieves ESG data of the funds and category

        Returns:
            dict ESG data

        Examples:
            >>> Funds("myria", "fr").esgData()

        """

        return self.GetData("esg/v1")

    def factorProfile(self):
        """
        This function retrieves the factor profile of the funds, index and category

        Returns:
            dict factor profile

        Examples:
            >>> Funds("myria", "fr").factorProfile()

        """
        return self.GetData("factorProfile")

    def feeLevel(self):
        """        
        This function retrieves the fees of the fund.

        Returns:
            dict fees

        Examples:
            >>> Funds("rmagx", "us").feeLevel()

        """
        return self.GetData("price/feeLevel")

    def fees(self):
        """        
        This function retrieves the fees of the fund (by scraping pages);

        Returns:
            dict fees

        Examples:
            >>> Funds("myria", "fr").fees()

        """
        no_site_error(self.code,self.name,self.country,self.site)
        result = {}
        #headers random agent
        headers = {'user-agent' : random_user_agent()}
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=5"
        response = requests.get(url, headers=headers, proxies=self.proxies)
        not_200_response(url,response)
        soup = BeautifulSoup(response.text, 'html.parser')
        if soup.find(id='managementFeesDiv') == None:
            return {}
        #label
        label_list =soup.find(id='managementFeesDiv').find_all('td', {"class": "label"})
        #value
        value_list = soup.find(id='managementFeesDiv').find_all('td', {"class": "value number"}) + soup.find(id='managementFeesDiv').find_all('td', {"class": "value number jdpa"})
        for i in range(0, len(value_list)):
            label = label_list[i].text
            result[label] = re.sub('(\\n +)|(\\n)','',value_list[i].text)

        return result

    def financialMetrics(self):
        """        
        This function retrieves the final metrics of the funds and category.

        Returns:
            dict financial metrics

        Examples:
            >>> Funds("rmagx", "us").financialMetrics()

        """
        return self.GetData("process/financialMetrics")


    def fixedIncomeStyle(self):
        """        
        This function retrieves the fixed income style of the funds and category.

        Returns:
            dict fixed income style

        Examples:
            >>> Funds("rmagx", "us").fixedIncomeStyle()

        """

        return self.GetData("process/fixedIncomeStyle")

    def fixedincomeStyleBoxHistory(self):
        """        
        This function retrieves the fixed income style history of the funds.

        Returns:
            dict fixed income style

        Examples:
            >>> Funds("rmagx", "us").fixedincomeStyleBoxHistory()

        """
        return self.GetData("process/fixedincomeStyleBoxHistory")

    def fundsAnnualPerformance(self):
        """        
        This function retrieves the annual performance of the funds.

        Returns:
            dict funds annual performance 

        Examples:
            >>> Funds("myria", "fr").fundsAnnualPerformance()

        """
        return self.AnnualPerformance('funds')

    def fundsAnnualRank(self):
        """        
        This function retrieves the annual rank of the funds in percentile.

        Returns:
            dict funds annual rank

        Examples:
            >>> Funds("myria", "fr").fundsAnnualRank()

        """
        return self.AnnualPerformance('rank')

    def fundsCumulativePerformance(self):
        """        
        This function retrieves the cumulative performance of the funds.

        Returns:
            dict funds cumulative performance

        Examples:
            >>> Funds("myria", "fr").fundsCumulativePerformance()

        """
        return self.CumulativePerformance('funds')

    def fundsQuarterlyPerformance(self):
        """        
        This function retrieves the quarterly performance of the funds.

        Returns:
            dict funds quarterly performance

        Examples:
            >>> Funds("myria", "fr").fundsCumulativePerformance()

        """
        no_site_error(self.code,self.name,self.country,self.site)
        result = {}
        #headers random agent
        headers = {'user-agent' : random_user_agent()}
        #page 1 - performance
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=1"
        
        response = requests.get(url, headers=headers, proxies=self.proxies)
        not_200_response(url,response)
        soup = BeautifulSoup(response.text, 'html.parser')
        quarterly_performance_date = soup.find(id='returnsTrailingDiv').find('td', {"class": "titleBarNote"}).text
        result['quarterly_performance_date'] = quarterly_performance_date 

        #quarter label
        regex = re.compile('.*heading number')
        quarter_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex})
        #year label
        regex = re.compile('.*label')
        year_list =soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex})
        #1st Quarter 
        regex = re.compile('.*col2 value number')
        quarter_1_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex})
        #2nd Quarter
        regex = re.compile('.*col3 value number')
        quarter_2_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex})
        #3rd Quarter
        regex = re.compile('.*col4 value number')
        quarter_3_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex})
        #4th Quarter
        regex = re.compile('.*col5 value number')
        quarter_4_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex})
        #loop on year
        for i in range(0, len(year_list)):
            label = 'performance_%s_' % (year_list[i].text)
            result[label + 'quarter_1'] = quarter_1_list[i].text
            result[label + 'quarter_2'] = quarter_2_list[i].text
            result[label + 'quarter_3'] = quarter_3_list[i].text
            result[label + 'quarter_4'] = quarter_4_list[i].text
        return result

    def graphData(self):
        """        
        This function retrieves historical data of the funds.

        Returns:
            dict historical data

        Examples:
            >>> Funds("myria", "fr").graphData()
   
        """

        return self.GetData("parent/graphData")

    def historicalData(self):
        """
        This function retrieves the historical price of the funds, index and category

        Returns:
            dict with historical data

        Examples:
            >>> Funds("myria", "fr").historicalData()

        """
        return self.GetData("performance/v3", url_suffixe='')



    def historicalExpenses(self):
        """        
        This function retrieves historical expenses of the funds.

        Returns:
            dict historical expenses

        Examples:
            >>> Funds("rmagx", "us").historicalExpenses()

        """
        if self.asset_type == 'etf':
            return {}
        return self.GetData("price/historicalExpenses")

    def holdings(self, holdingType: str = 'all'):
        """        
        This function retrieves holdings of the funds.

        Args:
            holdingType (str) : paramater to select the kind of holdings; all, bond, equity or other

        Returns:
            pandas DataFrame holdings

        Raises:
            ValueError whenever the parameter is not all, bond, equity or other

        Examples:
            >>> Funds("myria", "fr").holdings("all")
            >>> Funds("myria", "fr").holdings("bond")
            >>> Funds("myria", "fr").holdings("equity")
            >>> Funds("myria", "fr").holdings("other")

        """
        holdingType_to_holdingPage = {"all" : "all", "bond" : "boldHoldingPage","equity" : "equityHoldingPage", "other": "otherHoldingPage"}
        if holdingType not in holdingType_to_holdingPage:
            raise ValueError(f'parameter holdingType must take one of the following value : {", ".join(holdingType_to_holdingPage.keys())}  ')
    
        if holdingType == "all":
            return pd.DataFrame(self.position()["equityHoldingPage"]["holdingList"] + 
            self.position()["boldHoldingPage"]["holdingList"] + self.position()["otherHoldingPage"]["holdingList"] )
        else:
           return pd.DataFrame(self.position()[holdingType_to_holdingPage[holdingType]]["holdingList"])

    def indexAnnualPerformance(self):
        """
        This function retrieves the annual performance of the index.
  
        Returns:
            dict annual performance of the index


        Examples:
            >>> Funds("myria", "fr").indexAnnualPerformance()

        """
        return self.AnnualPerformance('index')

    def indexCumulativePerformance(self):
        """
        This function retrieves the cumulative performance of the index.
  
        Returns:
            dict cumulative performance of the index


        Examples:
            >>> Funds("myria", "fr").indexCumulativePerformance()

        """
        
        return self.CumulativePerformance('index')

    def investmentStrategy(self):
        """
        This function retrieves the investment strategy.
  
        Returns:
            dict investment strategy

        Examples:
            >>> Funds("LU0823421689").investmentStrategy()

        """
        bearer_token = token_investment_strategy()
        #header with bearer token
        headers = {
                    'authorization': f'Bearer {bearer_token}',
                    }
        return self.GetData("morningstarTake/investmentStrategy",headers=headers)
        
    def marketCapitalization(self):
        """
        This function retrieves the marketCapitalization breakdown of the funds, category and index.
  
        Returns:
            dict market capitalization

        Examples:
            >>> Funds("myria", "fr").marketCapitalization()

        """
        return self.GetData("process/marketCap")


    def maturitySchedule(self):
        """
        This function retrieves the maturity breakdown of the funds and category.
  
        Returns:
            dict maturity

        Examples:
            >>> Funds("rmagx", "us").maturitySchedule()

        """
        return self.GetData("process/maturitySchedule")


    def maxDrawDown(self, year = 3):
        """
        This function retrieves the max drawdown of the funds, index and category.
  
        Args:
            year (int) : period of calculation in year

        Returns:
            dict max drawdown

        Raises:
            TypeError whenever the parameter year is not an integer

        Examples:
            >>> Funds("myria", "fr").maxDrawDown()

        """

        if not isinstance(year, int):
            raise TypeError('year parameter should be an integer')


        return self.GetData("performance/marketVolatilityMeasure", params = {"year": year})

    def morningstarAnalyst(self):
        """
        This function retrieves the raiting of MorningStar analyst.
  
        Returns:
            dict rating

        Examples:
            >>> Funds("rmagx", "us").morningstarAnalyst()
   
        """

        return self.GetData("morningstarAnalyst")

    def multiLevelFixedIncomeData(self, primary = "superEffectiveDuration", secondary = "superSector.weight"):
        """
        This function retrieves the exposures of fixed income

        Args:
            primary (str) : can be superEffectiveDuration, superYieldToWorst, creditQuality
            secondary (str) : can be superSector.weight, region.weight, creditQuality.weight

        Returns:
            dict exposures

        Raises:
            ValueError whenever the primary and seconday paramater is not a value expected

        Examples:
            >>> Funds("rmagx", "us").multiLevelFixedIncomeData()

        """

        primary_choice = ["superEffectiveDuration","superYieldToWorst", "creditQuality"]
        secondary_choice = ["superSector.weight", "region.weight", "creditQuality.weight"]

        if primary not in primary_choice:
            raise ValueError(f'primary parameter can only take one of the values: {", ".join(primary_choice)}')
        
        if secondary not in secondary_choice:
            raise ValueError(f'secondary parameter can only take one of the values: {", ".join(secondary_choice)}')

        if primary == "creditQuality" and secondary == "creditQuality.weight":
            raise ValueError(f'primary and secondary parameters cannot be both credit quality')

        return self.GetData("multiLevelFixedIncomeData", params = {"primary": primary,"secondary": secondary})

    def nav(self,start_date,end_date,frequency="daily"):
        """
        This function retrieves the NAV of the funds

        Args:
            start_date (datetime) : start date to get nav
            end_date (datetime) : end date to get nav
            frequency (str) : can be daily, weekly, monthly

        Returns:
            list of dict with nav

            >>> Funds("RMAGX", "us").nav(datetime.datetime.today()- datetime.timedelta(30),datetime.datetime.today())

        Raises:
            TypeError: raised whenever the parameter type is not the type expected
            ValueError : raised whenever the parameter is not valid or no funds found

        """

        return self.TimeSeries(["nav","totalReturn"],
                                start_date=start_date,end_date=end_date,frequency=frequency)
            
        



    def objectiveInvestment(self):
        """        
        This function retrieves the objective of investment of the fund (by scraping pages);

        Returns:
            str objective investment

        Examples:
            >>> Funds("myria", "fr").objectiveInvestment()

        """
        
        no_site_error(self.code,self.name,self.country,self.site)
        #headers random agent
        headers = {'user-agent' : random_user_agent()}
        #Page 1 - overview
        #url page overview
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}"
        #get HTML page overview
        response = requests.get(url, headers=headers, proxies=self.proxies)
        #if page not found

        not_200_response(url,response)
        #html page as soup
        soup = BeautifulSoup(response.text, 'html.parser')
        #investment objective funds
        return soup.find(id='overviewObjectiveDiv').find('td', {"class": "value text"}).text

    def otherFee(self):
        """
        This function retrieves the other fee of the etf

        Returns:
            dict fees

        Examples:
            >>> Funds("American Century Foc Dynmc Gr ETF").otherFee()
            
            {'expenseWaiver': False, 'expenseReimbursement': None, 'expirationDate': None, 'expenseWaivers': None}
                    
        """
        return self.GetData("price/otherFee")


    def ownershipZone(self):
        """        
        This function retrieves ownershipZone of the funds, index and category.

        Returns:
            dict ownershipZone

        Examples:
            >>> Funds("myria", "fr").ownershipZone()

        """

        return self.GetData("process/ownershipZone")

    def parentMstarRating(self):

        """
        This function retrieves the raiting of parent by MorningStar analyst.
  
        Returns:
            list of dict rating

        Examples:
            >>> Funds("rmagx", "us").parentMstarRating()

        """

        return self.GetData("parent/parentMstarRating")

    def parentSummary(self):
        """
        This function retrieves info about the parent.
  
        Returns:
            dict parent info

        Examples:
            >>> Funds("rmagx", "us").parentSummary()

        """
        return self.GetData("parent/parentSummary")

    def people(self):
        """
        This function retrieves info about people who works in the company.
  
        Returns:
            dict people info

        Examples:
            >>> Funds("rmagx", "us").people()

        """
        return self.GetData("people")

    def position(self):
        """
        This function retrieves the hodings of the funds.
  
        Returns:
            dict holdings

        Examples:
            >>> Funds("myria", "fr").position()
  
        """

        return self.GetData("portfolio/holding/v2", params = {"premiumNum" : 10000, "freeNum" : 10000})

    def proxyVotingManagement(self):
        """
        This function retrieves the vote of management.
  
        Returns:
            dict vote

        Examples:
            >>> Funds("rmagx", "us").proxyVotingManagement()

        """
        return self.GetData("people/proxyVoting/management")
    

    def proxyVotingShareHolder(self):
        """
        This function retrieves the vote of shareholders.
  
        Returns:
            dict vote

        Examples:
            >>> Funds("rmagx", "us").proxyVotingShareHolder()

        """
        return self.GetData("people/proxyVoting/shareHolder")

    def productInvolvement(self):
        """
        This function retrieves the involvement of the funds
  
        Returns:
            dict involvement

        Examples:
            >>> Funds("myria", "fr").proxyVotingShareHolder()

        """

        return self.GetData("esg/productInvolvement")


    def referenceIndex(self, index):
        """
        This function retrieves the name of the category or the benchmark

        Args:
            index (str) : possible values are benchmark, category     

        Returns:
            str category or benchmark

        Raises:
            ValueErrror whenever the index parameter is not category of benchmark

        Examples:
            >>> Funds("myria", "fr").referenceIndex("category")
            >>> Funds("myria", "fr").referenceIndex("benchmark")

        """
        no_site_error(self.code,self.name,self.country,self.site)

        index_row = {'benchmark' : 0,'category' : 1}
        if index not in index_row:
            raise ValueError(f"index parameter must take one of the following value : { ', '.join(index_row.keys())}")

                #headers random agent
        headers = {'user-agent' : random_user_agent()}
        #Page 1 - overview
        #url page overview
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}"
        #get HTML page overview
        response = requests.get(url, headers=headers, proxies=self.proxies)
        #if page not found
        
        not_200_response(url,response)

        #html page as soup
        soup = BeautifulSoup(response.text, 'html.parser')
        benchmark_soup = soup.find(id='overviewBenchmarkDiv2Cols').find_all('td', {"class": "value text"})
        return benchmark_soup[index_row[index]].text

    def regionalSector(self):
        """
        This function retrieves the breakdown of the funds, category and index by region
  
        Returns:
            dict regional breakdown

        Examples:
            >>> Funds("myria", "fr").regionalSector()

        """
        return self.GetData("portfolio/regionalSector")

    def regionalSectorIncludeCountries(self):
        """
        This function retrieves the breakdown of the funds, category and index by region and country
  
        Returns:
            dict regional breakdown

        Examples:
            >>> Funds("myria", "fr").regionalSectorIncludeCountries()

        """
        return self.GetData("portfolio/regionalSectorIncludeCountries")



    def riskReturnScatterplot(self):
        """
        This function retrieves the return and standard deviation of the funds and category
  
        Returns:
            dict risk return

        Examples:
            >>> Funds("rmagx", "us").riskReturnScatterplot()

        """
        return self.GetData("performance/riskReturnScatterplot")

    def riskReturnSummary(self):
        """
        This function retrieves the return and risk summary of the funds compare to the category
  
        Returns:
            dict risk return

        Examples:
            >>> Funds("rmagx", "us").riskReturnSummary()

        """

        return self.GetData("performance/riskReturnSummary")

    def riskVolatility(self):
        """
        This function retrieves the alpha, beta, R², volatility and Sharpe ratio of the funds, category and index.

        Returns:
            dict econometrics

        Examples:
            >>> Funds("rmagx", "us").riskVolatility()

        """
        return self.GetData("performance/riskVolatility")

    def salesFees(self):
        """
        This function retrieves the sales fees of the funds

        Returns:
            dict fees

        Examples:
            >>> Funds("myria", "fr").salesFees()
        
        """
        if self.asset_type == 'etf':
            return {}
        return self.GetData("price/salesFees")

    def sector(self):
        """
        This function retrieves the sector breakdown of the funds, category and index
  
        Returns:
            dict sector breakdown

        Examples:
            >>> Funds("myria", "fr").sector()

        """
        return self.GetData("portfolio/v2/sector")

    def starRatingFundAsc(self):
        """
        This function retrieves the MorningStar rating of the funds of the company by ascending order
  
        Returns:
            dict rating

        Examples:
            >>> Funds("myria", "fr").starRatingFundAsc()
 
        """
        
        return self.GetData("parent/mstarRating/StarRatingFundAsc")

    def starRatingFundDesc(self):
        """
        This function retrieves the MorningStar rating of the funds of the company by descending order
  
        Returns:
            dict rating

        Examples:
            >>> Funds("myria", "fr").starRatingFundDesc()

        """
        
        return self.GetData("parent/mstarRating/StarRatingFundDesc")

    def taxes(self):
        """
        This function retrieves the other fee of the etf

        Returns:
            dict taxes

        Examples:
            >>> Funds("American Century Foc Dynmc Gr ETF").taxes()

        """
        return self.GetData("price/taxes")

    def trailingReturn(self, duration ='daily'):
        """
        This function retrieves the trailing return of the funds of the company.

        Args:
        duration (str) : frequency of return can be daily, monthly or quarterly
        
        Returns:
            dict trailing return

        Raises:
            ValueError whenever the parameter duration is not daily, monthly or quarterly

        Example:
            >>> Funds("myria", "fr").trailingReturn("daily")

        """

        duration_choice = ["daily", "monthly", "quarterly"]
        if duration not in duration_choice:
            raise ValueError(f'duration parameter can only take one of the values: {", ".join(duration_choice)}')


        return self.GetData("trailingReturn/v2",{"duration" : duration})