Initializing help system before first use

Folio - Insight example from 'Getting started'


Type: Portfolio optimization
Rating: 2 (easy-medium)
Description:
  • The files application.py, folio.vdl, foliocompare.vdl, and folioinsightxml.xml need to form an archive folioinsightxml.zip with the directory structure required by Insight in order to be loaded into Insight. This Insight app is described in Chapter 14 Embedding a Python model in an application of the guide 'Getting started with Xpress'.
File(s): application.py, folio.vdl, foliocompare.vdl, folioinsightxml.xml
Data file(s): shares.csv

application.py
# Xpress Insight Portfolio Optimization - Getting started example
# Copyright (c) 2025 Fair Isaac Corporation. All rights reserved.

import xpressinsight as xi
import xpress as xp
import pandas as pd
import sys

@xi.AppConfig(name="Portfolio Optimization", version=xi.AppVersion(1, 0, 0))
class InsightApp(xi.AppBase):
    # Input entities
    MaxHighRisk: xi.types.Param(default=1/3)
    MaxPerShare: xi.types.Param(default=0.3)
    MinNorthAmerica: xi.types.Param(default=0.5)
    ShareIds: xi.types.Index(dtype=xi.string, alias="Shares")

    # Input and result entities indexed over ShareIds
    Shares: xi.types.DataFrame(index="ShareIds", columns=[
        xi.types.Column("Return", dtype=xi.real, alias="Expected Return on Investment"),
        xi.types.Column("HighRisk", dtype=xi.boolean, alias="High-risk value"),
        xi.types.Column("NorthAmerica", dtype=xi.boolean, alias="Issued in North America"),
        xi.types.Column("fraction", dtype=xi.real, alias="Fraction used", manage=xi.Manage.RESULT)
    ])

    # Result entities
    TotalReturn: xi.types.Scalar(dtype=xi.real, alias="Total expected return on investment", manage=xi.Manage.RESULT)

    CtrSolIds: xi.types.Index(dtype=xi.string, alias="Constraints", manage=xi.Manage.RESULT)
    CtrSol: xi.types.DataFrame(index="CtrSolIds", columns=[
        xi.types.Column("Activity", dtype=xi.real, alias="Activity", manage=xi.Manage.RESULT),
        xi.types.Column("LowerLimit", dtype=xi.real, alias="Lower limit", manage=xi.Manage.RESULT),
        xi.types.Column("UpperLimit", dtype=xi.real, alias="Upper limit", manage=xi.Manage.RESULT),
    ])
    
    
    CtrSumIds: xi.types.Index(dtype=xi.string, alias="Constraints", manage=xi.Manage.RESULT)
    CtrSummary: xi.types.Series(index="CtrSumIds", dtype=xi.real, manage=xi.Manage.RESULT)

    # Constant class attribute
    DATAFILE = "shares.csv"

    @xi.ExecModeLoad(descr="Load input data and initialize all input entities.")
    def load(self):
        print("Loading data.")
        self.Shares = pd.read_csv(InsightApp.DATAFILE, index_col=['ShareIds'])
        self.ShareIds = self.Shares.index
        print("Loading finished.")

    @xi.ExecModeRun(descr="Solve problem and initialize all result entities.")
    def run(self):
        print('Starting optimization.')

        # Create Xpress problem and variables
        p = xp.problem("portfolio")
        self.Shares['fractionVar'] = pd.Series(p.addVariables(self.ShareIds, vartype=xp.continuous, name='fractionVar'))

        # Objective: expected total return
        objective = xp.Sum(self.Shares.Return * self.Shares.fractionVar)
        p.setObjective(objective, sense=xp.maximize)

        # Limit the percentage of high-risk values
        limit_high_risk = \
            xp.Sum(self.Shares.loc[self.Shares.HighRisk, 'fractionVar']) \
            <= self.MaxHighRisk
        p.addConstraint(limit_high_risk)

        # Minimum amount of North-American values
        limit_north_america = \
            xp.Sum(self.Shares.loc[self.Shares.NorthAmerica, 'fractionVar']) \
            >= self.MinNorthAmerica
        p.addConstraint(limit_north_america)

        # Spend all the capital
        p.addConstraint(xp.Sum(self.Shares.fractionVar) == 1)

        # Upper bounds on the investment per share
        p.addConstraint(share.fractionVar <= self.MaxPerShare for share_id, share in self.Shares.iterrows())

        # Solve optimization problem
        p.optimize()

        # Save results and key indicator values for GUI display
        self.Shares["fraction"] = pd.Series(p.getSolution(), index=self.ShareIds)
        self.TotalReturn = p.attributes.objval

        # Programmatic logic to compute evaluation metrics for constraints
        self.CtrSol = pd.DataFrame(columns=["Activity", "LowerLimit", "UpperLimit"])
        self.CtrSol.loc["Limit high risk shares"] = {
            "Activity": sum(self.Shares.loc[self.Shares.HighRisk, 'fraction']),
            "LowerLimit": 0,
            "UpperLimit": self.MaxHighRisk}

        self.CtrSol.loc["Limit North-American"] = {
            "Activity": sum(self.Shares.loc[self.Shares.NorthAmerica, 'fraction']),
            "LowerLimit": self.MinNorthAmerica,
            "UpperLimit": 1}

        for s in self.ShareIds:
            if self.Shares.loc[s, 'fraction'] > 0:
                self.CtrSol.loc[f"Limit per value: {s}"] = {
                    "Activity": self.Shares.loc[s, 'fraction'],
                    "LowerLimit": 0,
                    "UpperLimit": self.MaxPerShare}

        self.CtrSolIds = self.CtrSol.index
        
        self.CtrSummary = pd.Series()
        self.CtrSummary.loc["Largest position"] = max(self.Shares.fraction)
        self.CtrSummary.loc["Total high risk shares"] = sum(self.Shares.loc[self.Shares.HighRisk, 'fraction'])
        self.CtrSummary.loc["Total North-American"] = sum(self.Shares.loc[self.Shares.NorthAmerica, 'fraction'])
        self.CtrSummary.loc["Total return"] = p.attributes.objval
        
        self.CtrSumIds = self.CtrSummary.index
        
        print('Optimization finished.')


if __name__ == "__main__":
    app = xi.create_app(InsightApp)
    sys.exit(app.call_exec_modes(["LOAD", "RUN"]))

folio.vdl
<!--
  Xpress Insight - Portfolio optimization example from 'Getting Started' guide 
  Copyright (c) 2025 Fair Isaac Corporation. All rights reserved.
-->
<vdl version="5">
    <!-- Optional: number formatting function -->
    <script>
        function formatRender(data) {
            return insight.Formatter.formatNumber(data, '0.#%');
        }
    </script>
    <vdl-page class="compact">
        <!-- 'vdl' and 'vdl-page' tags must always be present -->
        <!-- 'header' element: container for any vdl elements that are not part
             of the page layout -->
        <vdl-header>
            <vdl-action-group name="runModel">
                <vdl-action-execute></vdl-action-execute>
            </vdl-action-group>
        </vdl-header>
        <!-- Structural element 'section': print header text for a section -->
        <vdl-section heading="Configuration">
            <vdl-row>
                <vdl-column size="5">
                    <vdl-form>
                        <vdl-field label="Maximum investment into high-risk values" size="3" label-size="9" parameter="MaxHighRisk" type="number"></vdl-field>
                        <vdl-field label="Maximum investment per share" type="number" size="3" label-size="9" parameter="MaxPerShare"></vdl-field>
                        <vdl-field label="Minimum investment into North-American values" type="number" size="3" label-size="9" parameter="MinNorthAmerica"></vdl-field>
                    </vdl-form>
                </vdl-column>
                <vdl-column size="4">
                    <vdl-table>
                        <vdl-table-column entity="Shares_Return" editable="true"></vdl-table-column>
                    </vdl-table>
                </vdl-column>
                <vdl-column size="3">
                    <vdl-form>
                        <!-- 'Run' button to launch optimization -->
                        <vdl-button vdl-event="click:actions.runModel" label="Run optimization"></vdl-button>
                    </vdl-form>
                </vdl-column>
            </vdl-row>
        </vdl-section>
        <vdl-section heading="Results">
            <!-- Display inline text element with objective value -->
            <vdl-row>
                <vdl-column><span vdl-text="= 'Total expected return: £' +
            insight.Formatter.formatNumber(scenario.entities.TotalReturn.value, '##.00')"></span></vdl-column>
            </vdl-row>
            <vdl-row>
                <vdl-column size="4" heading="Portfolio composition">
                    <vdl-table>
                        <vdl-table-column entity="Shares_fraction" render="=formatRender"></vdl-table-column>
                    </vdl-table>
                </vdl-column>
                <vdl-column size="8" heading="Evaluation of constraints">
                    <vdl-table>
                        <vdl-table-column entity="CtrSol_Activity"></vdl-table-column>
                        <vdl-table-column entity="CtrSol_LowerLimit"></vdl-table-column>
                        <vdl-table-column entity="CtrSol_UpperLimit"></vdl-table-column>
                    </vdl-table>
                </vdl-column>
            </vdl-row>
            <vdl-row>
                <vdl-column size="4">
                    <vdl-chart style="width:500px;">
                        <!-- Filtering out 0-valued entries -->
                        <script>
                            function getFracData(frac, shares) {
                                if (!shares.length || !(frac && frac.toObjectArray && frac.toObjectArray().length)) {
                                    return [];
                                }
                                var result = shares
                                    .filter(function(share) {
                                        return frac(share.value).value > 0;
                                    })
                                    .map(function(share) {
                                        return [share.label, frac(share.value).value];
                                    });
                                return result;
                            }
                        </script>
                        <vdl-chart-series type="pie" data="=getFracData(scenario.entities.Shares_fraction, scenario.entities.ShareIds)"></vdl-chart-series>
                        <!-- Simple pie chart:-->
                        <!--    <vdl-chart-series entity="frac" type="pie"></vdl-chart-series>--></vdl-chart>
                </vdl-column>
                <vdl-column size="8">
                    <vdl-chart modifier="=function(config) {config.layout.xaxis.tickangle = 5; return config; }" style="width:900px;">
                        <vdl-chart-series entity="CtrSol_Activity" type="line"></vdl-chart-series>
                        <vdl-chart-series entity="CtrSol_LowerLimit" type="line"></vdl-chart-series>
                        <vdl-chart-series entity="CtrSol_UpperLimit" type="line"></vdl-chart-series>
                        <vdl-chart-axis axis="x" type="category"></vdl-chart-axis>
                    </vdl-chart>
                </vdl-column>
            </vdl-row>
        </vdl-section>
    </vdl-page>
</vdl>

foliocompare.vdl
<!--
  Xpress Insight - Portfolio optimization example from 'Getting Started' guide 
  Copyright (c) 2025 Fair Isaac Corporation. All rights reserved.
-->
<vdl version="5">
    <vdl-page>    <!-- 'vdl' and 'vdl-page' must always be present -->
        <!-- Structural element 'section': print header text for a section -->
        <vdl-section heading="Comparison of scenario results">
            <vdl-row>
                <vdl-column>
                    <p><span vdl-text="Viewing "></span><span vdl-text="=scenarios.length"></span><span vdl-text=" scenario(s)."></span></p>
                </vdl-column>
            </vdl-row>
            <vdl-row>
                <!-- Display the solution values of constraints -->
                <vdl-column heading="Constraints and objective" size="6">
                    <vdl-table>
                        <!--<vdl-table-column set="CTRS"></vdl-table-column>-->
                        <vdl-table-column entity="CtrSummary" heading="='Results ' + s.props.name" vdl-repeat="=s,i in scenarios" scenario="=i"></vdl-table-column>
                    </vdl-table>
                </vdl-column>
		<!-- Display the 'frac' solution values -->
                <vdl-column heading="Portfolio composition" size="6">
                    <vdl-table>
                        <vdl-table-column entity="Shares_fraction" heading="=s.props.name" vdl-repeat="=s,i in scenarios" scenario="=i"></vdl-table-column>
                    </vdl-table>
                </vdl-column>

            </vdl-row>
            <vdl-row>
                <!-- Display the objective values in a bar chart -->
                <vdl-column heading="Expected total return" size="6">
                    <vdl-chart>
                        <vdl-preload entities="TotalReturn" scenarios="all"></vdl-preload>
                        <script>
                            function getData(scenarios) {
                                return scenarios.map(function(s) {
                                    return [s.props().name, s.entities.TotalReturn().value];
                                });
                            }
                        </script>
                        <vdl-chart-series data="=getData(scenarios)"></vdl-chart-series>
<!-- Alternative formulation using a new array 'TotalReturn'        
          <vdl-preload entities="TotalReturn" scenarios="all"></vdl-preload>
          <script>
          function getData(scenarios) {
            return scenarios.map(function (s) {
              return [s.props().name, s.entities.TotalReturn().value];
            });
          }
          </script>
          <vdl-chart-series data="=getData(scenarios)"></vdl-chart-series>
-->
                    </vdl-chart>
                </vdl-column>
            </vdl-row>
        </vdl-section>
    </vdl-page>
</vdl>

folioinsightxml.xml
<?xml version="1.0" encoding="UTF-8"?>
<model-companion xmlns="http://www.fico.com/xpress/optimization-modeler/model-companion" version="5.0">

  <client>
    <view-group title="Main">
      <vdl-view title="Portfolio data" path="folio.vdl" />
      <vdl-view title="Scenario comparison" default="false" path="foliocompare.vdl" />
    </view-group>
  </client>

</model-companion>

© 2001-2025 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.