Developing the Performance Report

      Developing the Performance Report


        Article summary

        Developing the Performance Report

        In this example we look at how to create the below Performance report for Assette Capital. It illustrates performance returns for different performance periods for a given account against a benchmark.

        The Component Inventory analysis corresponding to the above requirement is illustrated below.

        A screenshot of a computer  Description automatically generated
        A screenshot of a computer  Description automatically generated

        Data Blocks

        To extract the required data, use multiple dependency blocks for this performance object as shown below to handle the necessary business logic. Note that the interface block settings are the same as the previous interface blocks developed for the previous data objects.

        Interface Blocks:

        • DEMO_SnowFlakePreCalPerformanceDetailsExtract: Use to extract Pre-Calculated Portfolio Performance

        • DEMO_SnowFlakePreCalBenchmarkPerformanceExtract: Use to extract Pre-Calculated Benchmark Performance

        • DEMO_SnowflakeAccountBenchmarkPerformance: Use to extract benchmark performance for account specific periods such as inception to date, fiscal year to date etc.

        • DEMO_SnowFlakeBenchmarkAssociationDetailsExtractWithoutRank: Use to extract all benchmarks which associated to given Portfolio.

        • DEMO_AccountbyPurpose: Use to switch account depending on Purpose and ShowComposite data setting. This block already explained under Attribution Object.

        • DEMO_GetRepCompositeAccountbyProduct: Use to get Performance and Non-Performance (Rep) Accounts from Product Master for given Portfolio Account.

        • DEMO_SnowFlakePortfolioGeneralInformationDetailsExtract: Use to extract Portfolio General Information to populate required output variables.

        • DEMO_SnowFlakeBenchmarkGeneralInformationDetailsExtract: Use to extract Benchmark General Information to populate required output variables.

        • DEMO_GetFiscalYearEndExtract: Use to get Fiscal Year for given Portfolio from Attribute Master.

        Environment Blocks:

        • PythonEnvForDateCalculation: Use to import Date calculation related to python Libraries.

        • CalculationEnv: Use to import Panda and NumPy Libraries.

        Calculation Blocks:

        • ast_period_code_map

        • ast_fn_GetValidPeriods

        Transformation Blocks:

        • DEMO_GetFiscalYearEnd: Use to convert Fiscal Year which extract using ‘DEMO_GetFiscalYearEndExtract ‘into required date time format for calculation.

        • DEMO_Pre Calculated Performance Transform: Use to process data to transfer required performance output, using all interface blocks and Calculations blocks.


        Definition of “DEMO_Pre Calculated Performance Transform”

        object_blueprint = {
        
                        "ENTITYCODE": "",
        
                        "HISTORYDATE": "",
        
                        "CURRENCYCODE": "",
        
                        "CURRENCY": "",
        
                        "PERFORMANCECATEGORY": "",
        
                        "PERFORMANCECATEGORYNAME": "",
        
                        "PERFORMANCETYPE": "",
        
                        "PERFORMANCEINCEPTIONDATE": "",
        
                        "QUARTERTODATE": "",
        
                        "YEARTODATE": "",
        
                        "PRIORQUARTER1": "",
        
                        "PRIORQUARTER2": "",
        
                        "PRIORQUARTER3": "",
        
                        "PRIORQUARTER4": "",
        
                        "PRIORCALENDARYEAR1": "",
        
                        "PRIORCALENDARYEAR2": "",
        
                        "PRIORCALENDARYEAR3": "",
        
                        "PRIORCALENDARYEAR4": "",
        
                        "PRIORCALENDARYEAR5": "",
        
                        "PRIORCALENDARYEAR6": "",
        
                        "PRIORCALENDARYEAR7": "",
        
                        "PRIORCALENDARYEAR8": "",
        
                        "PRIORCALENDARYEAR9": "",
        
                        "PRIORCALENDARYEAR10": "",
        
                        "MONTH1": "",
        
                        "MONTHS3": "",
        
                        "MONTHS6": "",
        
                        "MONTHS9": "",
        
                        "YEAR1": "",
        
                        "YEAR2CUMULATIVE": "",
        
                        "YEAR3CUMULATIVE": "",
        
                        "YEAR4CUMULATIVE": "",
        
                        "YEAR5CUMULATIVE": "",
        
                        "YEAR6CUMULATIVE": "",
        
                        "YEAR7CUMULATIVE": "",
        
                        "YEAR8CUMULATIVE": "",
        
                        "YEAR9CUMULATIVE": "",
        
                        "YEAR10CUMULATIVE": "",
        
                        "YEAR12CUMULATIVE": "",
        
                        "YEAR15CUMULATIVE": "",
        
                        "YEAR20CUMULATIVE": "",
        
                        "YEAR25CUMULATIVE": None,
        
                        "YEAR30CUMULATIVE": None,
        
                        "INCEPTIONTODATECUMULATIVE": "",
        
                        "YEAR2ANNUALIZED": "",
        
                        "YEAR3ANNUALIZED": "",
        
                        "YEAR4ANNUALIZED": "",
        
                        "YEAR5ANNUALIZED": "",
        
                        "YEAR6ANNUALIZED": "",
        
                        "YEAR7ANNUALIZED": "",
        
                        "YEAR8ANNUALIZED": "",
        
                        "YEAR9ANNUALIZED": "",
        
                        "YEAR10ANNUALIZED": "",
        
                        "YEAR12ANNUALIZED": "",
        
                        "YEAR15ANNUALIZED": "",
        
                        "YEAR20ANNUALIZED": "",
        
                        "YEAR25ANNUALIZED": None,
        
                        "YEAR30ANNUALIZED": None,
        
                        "INCEPTIONTODATEANNUALIZED": "",
        
                        "FISCALYEARTODATE": None,
        
                        "PRIORFISCALQUARTER1": None,
        
                        "PRIORFISCALQUARTER2": None,
        
                        "PRIORFISCALQUARTER3": None,
        
                        "PRIORFISCALQUARTER4": None,
        
                        "PRIORFISCALYEAR1": None,
        
                        "PRIORFISCALYEAR2": None,
        
                        "PRIORFISCALYEAR3": None,
        
                        "PRIORFISCALYEAR4": None,
        
                        "PRIORFISCALYEAR5": None,
        
                        "PRIORFISCALYEAR6": None,
        
                        "PRIORFISCALYEAR7": None,
        
                        "PRIORFISCALYEAR8": None,
        
                        "PRIORFISCALYEAR9": None,
        
                        "PRIORFISCALYEAR10": None,
        
                        "MONTHTODATE": "",
        
                        "PRIORMONTH1": None,
        
                        "PRIORMONTH2": None,
        
                        "PRIORMONTH3": None,
        
                        "MOSTRECENTCALENDARYEAR": None
        
                            }
        
        portfolio_perf_params = {
        
            "AccountCode":params["AccountCode"],
        
            "AsofDate": params["AsofDate"],
        
            "CurrencyCode": params["CurrencyCode"]
        
        }
        
        portfolio_benchmark_association_params = {
        
            "AccountCode": params["AccountCode"]
        
        }
        
        benchmark_perf_params = {
        
            "BenchmarkCode":"",
        
            "AsofDate": params["AsofDate"],
        
            "CurrencyCode": params ["CurrencyCode"]
        
        }
        
        date_pattern = ""
        
        if "DatePattern" in params.keys():
        
            date_pattern = params["DatePattern"].strip()
        
        if len(date_pattern) == 0:
        
            date_pattern = '%Y-%m-%d'
        
        output_list = []
        
        # Read PreCalculated Performance Details extraction block to retrieve Performance data
        
        PerformanceData = read("DEMO_SnowFlakePreCalPerformanceDetailsExtract", params=portfolio_perf_params)['data']
        
        if len(PerformanceData) > 0:
        
            portfolio_performance_df = pd.DataFrame(PerformanceData)
        
            #Renaming PORTFOLIOCODE as ENTITYCODE
        
            portfolio_performance_df = portfolio_performance_df.rename(columns={"PORTFOLIOCODE": "ENTITYCODE"})
        
            performance_inception_date = portfolio_performance_df.loc[0, "PERFORMANCEINCEPTIONDATE"]
        
            # response["data"] = portfolio_performance_df.to_dict(orient='records')
        
            # Read Benchmark Association Details extraction block to retrieve Benchmark Association
        
            portfolio_benchmark_association = read("DEMO_SnowFlakeBenchmarkAssociationDetailsExtractWithoutRank", params=portfolio_benchmark_association_params)['data']
        
            # Read PreCalculated Account Benchmark Performance extraction block
        
            portfolio_benchmark_performance_df = pd.DataFrame(read("DEMO_SnowflakeAccountBenchmarkPerformance", params=portfolio_perf_params)['data'])
        
            portfolio_benchmark_performance_df["PERFORMANCECATEGORYNAME"] = ""
        
            benchmark_perf_df = pd.DataFrame
        
            benchmark_performance = pd.DataFrame
        
            if len(portfolio_benchmark_association)> 0:
        
                for benchmark_index, benchmark in enumerate(portfolio_benchmark_association):
        
                    #Get the benchmark code that associated with the potfolio.
        
                    benchmark_perf_params["BenchmarkCode"] = benchmark["BENCHMARKCODE"]
        
                    # Read PreCalculated Benchmark Performance extraction block to retrieve Benchmark Performance data
        
                    benchmark_performance = pd.DataFrame(read("DEMO_SnowFlakePreCalBenchmarkPerformanceExtract", benchmark_perf_params)['data'])
        
                    benchmark_rank = "Index " + str(benchmark["RANK"])
        
                   
        
                    #updating PERFORMANCECATEGORYNAME field in the portfolio_benchmark_performance_df
        
                    portfolio_benchmark_performance_df.loc[portfolio_benchmark_performance_df['BENCHMARKCODE'] == benchmark["BENCHMARKCODE"], "PERFORMANCECATEGORYNAME"] = benchmark_rank
        
                    if not benchmark_performance.empty:
        
                        benchmark_performance["PERFORMANCECATEGORYNAME"] = benchmark_rank
        
                        if benchmark_index == 0:
        
                            benchmark_perf_df = benchmark_performance
        
                        else:
        
                            benchmark_perf_df = pd.concat([benchmark_perf_df, benchmark_performance])
        
                benchmark_perf_df = benchmark_perf_df.reset_index(drop=True)
        
                benchmark_perf_df["PERFORMANCECATEGORY"] = "Benchmark"
        
                benchmark_perf_df["PERFORMANCETYPE"] = np.nan
        
                benchmark_perf_df = benchmark_perf_df.rename(columns={"BENCHMARKCODE": "ENTITYCODE"})
        
                portfolio_benchmark_performance_df["PERFORMANCECATEGORY"] = "Benchmark"
        
                portfolio_benchmark_performance_df["PERFORMANCETYPE"] = np.nan
        
                portfolio_benchmark_performance_df = portfolio_benchmark_performance_df.rename(columns={"BENCHMARKCODE": "ENTITYCODE"})
        
                portfolio_benchmark_performance_df = portfolio_benchmark_performance_df.drop(["PORTFOLIOCODE", "HISTORYDATE",
        
                "PERFORMANCECATEGORYNAME", "PERFORMANCECATEGORY", "PERFORMANCETYPE", "CURRENCYCODE", "CURRENCY"], axis=1)
        
                #Merge both benchmark and Account benchmark data frame and create final result frame
        
                final_result_df = pd.merge(benchmark_perf_df, portfolio_benchmark_performance_df, on="ENTITYCODE")
        
                final_result_df["PERFORMANCEINCEPTIONDATE"] = performance_inception_date
        
                column_list = list(object_blueprint.keys())
        
                # Extract the columns that only specified in the blueprint
        
                final_result_df = final_result_df[column_list]
        
                # response["text"] = column_list
        
                # response["values"] = final_result_df.to_dict(orient='records')
        
                portfolio_performance_df = portfolio_performance_df[column_list]
        
                if params["SuppressNotApplicablePeriods"].lower() == "yes":
        
                    # Drop the column which has missing value
        
                    portfolio_performance_df = portfolio_performance_df.dropna(axis=1, how='all')
        
                period_code_map = read("ast_period_code_map")
        
                period_list = ""
        
                for period_code in period_code_map.keys():
        
                    if len(period_code_map[period_code]) > 0:
        
                        period_list += period_code + ","
        
                period_list = period_list[:-1]
        
                valid_period_params = {"AsofDate": str(params["AsofDate"]), "InceptionDate": str(performance_inception_date),
        
                                        "FiscalYearEnd": str(params["FiscalYearEnd"]),
        
                                        "SuppressNotApplicablePeriods": params["SuppressNotApplicablePeriods"].lower(),
        
                                        "SuppressDuplicatePeriods": params["SuppressDuplicatePeriods"].lower(),
        
                                        "PeriodList":period_list,"DatePattern":date_pattern}
        
                valid_period_result = pd.DataFrame(read("ast_fn_GetValidPeriods", valid_period_params)['data'])
        
                valid_period_list = valid_period_result['period'].unique()
        
                # get the period column names sent by the client
        
                valid_period_columns = []
        
                for valid_period in valid_period_list:
        
                    valid_period_columns.append(period_code_map[valid_period])
        
                meta_data_columns = ["ENTITYCODE", "HISTORYDATE", "CURRENCYCODE", "CURRENCY", "PERFORMANCECATEGORY",
        
                                "PERFORMANCECATEGORYNAME", "PERFORMANCETYPE", "PERFORMANCEINCEPTIONDATE"]
        
                final_frame_columns = meta_data_columns + valid_period_columns
        
                for column_item in final_result_df.columns:
        
                    if column_item not in final_frame_columns:
        
                        final_result_df = final_result_df.drop(columns=[column_item])
        
                for column_item in portfolio_performance_df.columns:
        
                    if column_item not in final_frame_columns:
        
                        portfolio_performance_df = portfolio_performance_df.drop(columns=[column_item])
        
                if params["SuppressNullPeriods"].lower() == 'yes':
        
                    portfolio_performance_df = portfolio_performance_df.dropna(axis=1, how='all')
        
                    for column_item in final_result_df.columns:
        
                        if column_item not in portfolio_performance_df:
        
                            final_result_df = final_result_df.drop(columns=[column_item])
        
                final_result_df = pd.concat([final_result_df, portfolio_performance_df])
        
                output_list = final_result_df.to_dict(orient='records')
        
        response["data"] = output_list

        Details of Calculations Blocks

        1. ast_period_code_map Block

        A screenshot of a chat  Description automatically generated

        This block is used to map the Performance period with the period name which received from client data source side.

        Definition of ast_period_code_map Block

        {
        
              "QTD": "QUARTERTODATE",
        
              "YTD": "YEARTODATE",
        
              "MRQ": "",
        
              "MRY": "MOSTRECENTCALENDARYEAR",
        
              "PQ1": "PRIORQUARTER1",
        
              "PQ2": "PRIORQUARTER2",
        
              "PQ3": "PRIORQUARTER3",
        
              "PQ4": "PRIORQUARTER4",
        
              "PY1": "PRIORCALENDARYEAR1",
        
              "PY2": "PRIORCALENDARYEAR2",
        
              "PY3": "PRIORCALENDARYEAR3",
        
              "PY4": "PRIORCALENDARYEAR4",
        
              "PY5": "PRIORCALENDARYEAR5",
        
              "PY6": "PRIORCALENDARYEAR6",
        
              "PY7": "PRIORCALENDARYEAR7",
        
              "PY8": "PRIORCALENDARYEAR8",
        
              "PY9": "PRIORCALENDARYEAR9",
        
              "PY10": "PRIORCALENDARYEAR10",
        
              "1MT": "MONTH1",
        
              "3MT": "MONTHS3",
        
              "6MT": "MONTHS6",
        
              "9MT": "MONTHS9",
        
              "12MT": "YEAR1",
        
              "2YC": "YEAR2CUMULATIVE",
        
              "3YC": "YEAR3CUMULATIVE",
        
              "4YC": "YEAR4CUMULATIVE",
        
              "5YC": "YEAR5CUMULATIVE",
        
              "6YC": "YEAR6CUMULATIVE",
        
              "7YC": "YEAR7CUMULATIVE",
        
              "8YC": "YEAR8CUMULATIVE",
        
              "9YC": "YEAR9CUMULATIVE",
        
              "10YC": "YEAR10CUMULATIVE",
        
              "12YC": "YEAR12CUMULATIVE",
        
              "15YC": "YEAR15CUMULATIVE",
        
              "20YC": "YEAR20CUMULATIVE",
        
              "25YC": "YEAR25CUMULATIVE",
        
              "30YC": "YEAR30CUMULATIVE",
        
              "ITD": "INCEPTIONTODATECUMULATIVE",
        
              "2YA": "YEAR2ANNUALIZED",
        
              "3YA": "YEAR3ANNUALIZED",
        
              "4YA": "YEAR4ANNUALIZED",
        
              "5YA": "YEAR5ANNUALIZED",
        
              "6YA": "YEAR6ANNUALIZED",
        
              "7YA": "YEAR7ANNUALIZED",
        
              "8YA": "YEAR8ANNUALIZED",
        
              "9YA": "YEAR9ANNUALIZED",
        
              "10YA": "YEAR10ANNUALIZED",
        
              "12YA": "YEAR12ANNUALIZED",
        
              "15YA": "YEAR15ANNUALIZED",
        
              "20YA": "YEAR20ANNUALIZED",
        
              "25YA": "YEAR25ANNUALIZED",
        
              "30YA": "YEAR30ANNUALIZED",
        
              "ITDA": "INCEPTIONTODATEANNUALIZED",
        
              "FYTD": "FISCALYEARTODATE",
        
              "MRFQ": "",
        
              "PFQ1": "PRIORFISCALQUARTER1",
        
              "PFQ2": "PRIORFISCALQUARTER2",
        
              "PFQ3": "PRIORFISCALQUARTER3",
        
              "PFQ4": "PRIORFISCALQUARTER4",
        
              "PFY1": "PRIORFISCALYEAR1",
        
              "PFY2": "PRIORFISCALYEAR2",
        
              "PFY3": "PRIORFISCALYEAR3",
        
              "PFY4": "PRIORFISCALYEAR4",
        
              "PFY5": "PRIORFISCALYEAR5",
        
              "PFY6": "PRIORFISCALYEAR6",
        
              "PFY7": "PRIORFISCALYEAR7",
        
              "PFY8": "PRIORFISCALYEAR8",
        
              "PFY9": "PRIORFISCALYEAR9",
        
              "PFY10": "PRIORFISCALYEAR10",
        
              "MTD": "MONTHTODATE"
        
        }

        2. ast_fn_GetValidPeriods: This function will return the begin, end dates for a list of periods. The function can also be used to eliminate duplicate periods based on AsofDate or inapplicable periods based on an inception date.

        A white line on a white surface  Description automatically generated

        Definition of ast_fn_GetValidPeriods

        suppress_not_applicable = params["SuppressNotApplicablePeriods"]
        
        suppress_duplicate_periods = params["SuppressDuplicatePeriods"]
        
        period_list = params["PeriodList"].split(",")
        
        date_pattern = ""
        
        if "DatePattern" in params.keys():
        
            date_pattern = params["DatePattern"].strip()
        
        if len(date_pattern) == 0:
        
            date_pattern = '%Y-%m-%d'
        
        period_func_params = {"AsofDate": params["AsofDate"], "PeriodCode": "", "InceptionDate": params["InceptionDate"], "FiscalYearEnd": params["FiscalYearEnd"],"DatePattern": date_pattern}
        
        inception_date = datetime.datetime.strptime(params["InceptionDate"],'%Y-%m-%d')
        
        period_frame = pd.DataFrame(columns=["period", "begin_date", "end_date"])
        
        period_index = 0
        
        for period_code in period_list:
        
            period_func_params["PeriodCode"] = period_code
        
            period_begin_end_dates = read("ast_fn_GetBeginEndDates", period_func_params)["data"]
        
            begin_date = datetime.datetime.strptime(period_begin_end_dates[0]["begin_date"], '%Y-%m-%d')
        
            end_date = datetime.datetime.strptime(period_begin_end_dates[0]["end_date"], '%Y-%m-%d')
        
            period_frame.at[period_index, "period"] = period_code
        
            period_frame.at[period_index, "begin_date"] = begin_date
        
            period_frame.at[period_index, "end_date"] = end_date
        
            period_index += 1
        
        if suppress_not_applicable == "yes":
        
            period_frame = period_frame.loc[period_frame['begin_date'] >= inception_date]
        
        if suppress_duplicate_periods == "yes":
        
            # period_frame = period_frame.drop_duplicates(["begin_date","end_date"])
        
            rank_mapping = {"MTD": 1, "QTD": 2, "YTD": 3, "12MT": 4}
        
            period_frame["RANK"] = period_frame["period"].map(rank_mapping)
        
            period_frame["RANK"].fillna("", inplace=True)
        
            selected_periods = ["QTD", "MTD", "YTD", "12MT"]
        
            filtered_period_frame = period_frame[period_frame["period"].isin(selected_periods)]
        
            period_frame = period_frame[~period_frame["period"].isin(selected_periods)]
        
            filtered_period_frame.sort_values(by=["RANK", "begin_date"], ascending=[True, True], inplace=True)
        
            filtered_period_frame.drop_duplicates(subset=["begin_date", "end_date"], keep="first", inplace=True)
        
            period_frame = pd.concat([filtered_period_frame, period_frame], ignore_index=True)
        
            period_frame = period_frame.drop(columns=["RANK"])
        
        period_frame['begin_date'] = period_frame['begin_date'].astype(str)
        
        period_frame['end_date'] = period_frame['end_date'].astype(str)
        
        # print(period_frame)
        
        output_list = period_frame.to_dict(orient='records')
        
        response['data'] = output_list
        
        # response['data'] = period_func_params

        Data Object

        To create the ‘Performance’ Data Object we can use the Data Block ‘DEMO_Performance’

        • Click on ‘Create New’ under ‘Data Objects’ tab from Developer Center

        • Select the options as shown below and hit ‘Create”.

        • Give an appropriate name.

        • Set your Data Block

        • Set the Data Settings one by one

        • Set the Column name one by one as needed.

        Note: We need to create an extra column called ‘Name’ and the instructions are given below.

        • Column ‘Name’ setup is shown below.

        Dynamic Value – conditions above

        If - PERFORMANCECATEGORY = "Asset Class" and PERFORMANCECATEGORYNAME = "Total Portfolio" and PERFORMANCETYPE = "Portfolio Gross" Then - {{PortfolioName}} (Gross)

        Else If - PERFORMANCECATEGORY = "Asset Class" and PERFORMANCECATEGORYNAME = "Total Portfolio" and PERFORMANCETYPE = "Portfolio Net" Then - {{PortfolioName}} (Net)

        Else If - PERFORMANCECATEGORY = "Benchmark" and PERFORMANCECATEGORYNAME = "Index 1" Then - {{Index1}}

        Else If - PERFORMANCECATEGORY = "Benchmark" and PERFORMANCECATEGORYNAME = "Index 2" Then - {{Index2}}

        Else If - PERFORMANCECATEGORY = "Excess Return" and PERFORMANCECATEGORYNAME = "Index 1" Then - Excess Index 1

        Else If - PERFORMANCECATEGORY = "Excess Return" and PERFORMANCECATEGORYNAME = "Index 2" Then - Excess Index 2

        Else - {{value}}

        • Set up the column ‘Since Inception’ – to show with dynamic date.

        • Go to Row Type and set the rows with conditions as shown below.

        • Conditions as below

        If - PERFORMANCECATEGORY = "Asset Class" and PERFORMANCECATEGORYNAME = "Total Portfolio" and PERFORMANCETYPE = "Portfolio Gross" Then - Portfolio Gross

        Else If - PERFORMANCECATEGORY = "Asset Class" and PERFORMANCECATEGORYNAME = "Total Portfolio" and PERFORMANCETYPE = "Portfolio Net" Then - Portfolio Net

        Else If - PERFORMANCECATEGORY = "Benchmark" and PERFORMANCECATEGORYNAME = "Index 1" Then - Index 1

        Else If - PERFORMANCECATEGORY = "Benchmark" and PERFORMANCECATEGORYNAME = "Index 2" Then – Index 1

        Else If - PERFORMANCECATEGORY = "Excess Return" and PERFORMANCECATEGORYNAME = "Index 1" Then - Excess Index 1

        Else If - PERFORMANCECATEGORY = "Excess Return" and PERFORMANCECATEGORYNAME = "Index 2" Then - Excess Index 2

        • Go to Legend and set the Title column to ‘Name’

        Authoring Smart page (Performance - Table)

        • Insert a native PowerPoint table as shown below (10 Columns by 6 Rows table structure)

        • Open Fabrication panel as shown below.

        • Set the Columns, Rows and Data Settings as shown below.

        Authoring Smart page (Performance - Chart)

        • Insert a bar Chart

        • Set the Series and Categories

        • Set the Data settings.