<?xml version="1.0"?>
<!-- Copyright (C) 2019 Quaternion Risk Management -->
<!-- All Rights Reserved -->
<ScriptLibrary>
  <!-- Only officially supported ORE+ scripts go here -->

  <!-- Accumulator01

       Input Data:
       Strike                          option strike
       FixingAmount                    unleveraged amount paid on each fixing date
       LongShort                       1 for a long, -1 for a short position
       Underlying                      underlying index
       PayCcy                          payment currency
       StartDate                       start date (only needed if AmericanKO = 1)
       FixingDates                     fixing valuation dates
       SettlementDates                 settlement dates
       RangeUpperBounds                upper bounds of fixing ranges
       RangeLowerBounds                lower bounds of fixing ranges
       RangeLeverages                  leverage amounts of fixing ranges
       KnockOutLevel                   knock out level
       KnockOutType                    barrier type 3=DownOut, 4=UpOut
       AmericanKO                      1 for American KO style, -1 for European KO style
       GuaranteedFixings               the number of fixings guaranteed regardless of knockout level

       Result Data:
       value                            npv of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>Accumulator01</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
            REQUIRE KnockOutType == 3 OR KnockOutType == 4;
            NUMBER Payoff, fix, d, r, Alive, currentNotional, Factor, ThisPayout, Fixing[SIZE(FixingDates)];
            Alive = 1;
            FOR d IN (1, SIZE(FixingDates), 1) DO
                fix = Underlying(FixingDates[d]);
                Fixing[d] = fix;

                IF AmericanKO == 1 THEN
                  IF KnockOutType == 4 THEN
                    IF FixingDates[d] >= StartDate THEN
                       IF d == 1 OR FixingDates[d-1] <= StartDate THEN
                          Alive = Alive * (1 - ABOVEPROB(Underlying, StartDate, FixingDates[d], KnockOutLevel));
		       ELSE
                          Alive = Alive * (1 - ABOVEPROB(Underlying, FixingDates[d-1], FixingDates[d], KnockOutLevel));
		       END;
                    END;
                  ELSE
                    IF FixingDates[d] >= StartDate THEN
                       IF d == 1 OR FixingDates[d-1] <= StartDate THEN
                          Alive = Alive * (1 - BELOWPROB(Underlying, StartDate, FixingDates[d], KnockOutLevel));
		       ELSE
                          Alive = Alive * (1 - BELOWPROB(Underlying, FixingDates[d-1], FixingDates[d], KnockOutLevel));
		       END;
                    END;
                  END;
                ELSE
                  IF {KnockOutType == 4 AND fix >= KnockOutLevel} OR
                     {KnockOutType == 3 AND fix <= KnockOutLevel} THEN
                    Alive = 0;
                  END;
                END;

                IF d <= GuaranteedFixings THEN
                  Factor = 1;
                ELSE
                  Factor = Alive;
                END;

                FOR r IN (1, SIZE(RangeUpperBounds), 1) DO
                  IF fix > RangeLowerBounds[r] AND fix <= RangeUpperBounds[r] THEN
                    ThisPayout = RangeLeverages[r] * FixingAmount * (fix - Strike) * Factor;
                    IF d > GuaranteedFixings OR ThisPayout >= 0 THEN
                      Payoff = Payoff + LOGPAY(RangeLeverages[r] * FixingAmount * (fix - Strike) * Factor,
                                               FixingDates[d], SettlementDates[d], PayCcy);
                    END;
                  END;
                END;
            END;
            value = LongShort * Payoff;
            currentNotional = FixingAmount * Strike;
        ]]></Code>
      <NPV>value</NPV>
      <Results>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result>Alive</Result>
        <Result>Fixing</Result>
      </Results>
      <CalibrationSpec>
        <Calibration>
          <Index>Underlying</Index>
          <Strikes>
            <Strike>Strike</Strike>
            <Strike>KnockOutLevel</Strike>
          </Strikes>
        </Calibration>
      </CalibrationSpec>
    </Script>
  </Script>
  <!-- Accumulator02

       Input Data:
       Strike                          option strike
       FixingAmount                    number of shares per day (EQ), foreign amount (FX)
       LongShort                       1 for a long, -1 for a short position
       Underlying                      underlying index
       PayCcy                          payment currency
       ObservationDates                observation dates
       KnockOutSettlementDates         knock out settlement dates
       ObservationPeriodEndDates       end dates of observation periods
       last of these dates should be >= last observation date
       SettlementDates                 settlement dates
       RangeUpperBounds                upper bounds of fixing ranges
       RangeLowerBounds                lower bounds of fixing ranges
       RangeLeverages                  leverage amounts of fixing ranges
       Default Range                   default range index for guaranteed period
       KnockOutLevel                   knock out level
       KnockOutType                    barrier type 3=DownOut, 4=UpOut
       GuaranteedPeriodEndDate         end of guarateed period

       Result Data:
       value                           npv of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>Accumulator02</Name>
    <ProductTag>SingleAssetOptionBwd({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
            REQUIRE SIZE(ObservationDates) == SIZE(KnockOutSettlementDates);
            REQUIRE SIZE(ObservationPeriodEndDates) == SIZE(SettlementDates);
            REQUIRE SIZE(RangeUpperBounds) == SIZE(RangeLowerBounds);
            REQUIRE SIZE(RangeUpperBounds) == SIZE(RangeLeverages);
            REQUIRE ObservationPeriodEndDates[SIZE(ObservationPeriodEndDates)] >= ObservationDates[SIZE(ObservationDates)];
            NUMBER Payoff, fix, d, dd, KnockedOut, currentNotional, Days[SIZE(RangeUpperBounds)], knockOutDays, Fixing[SIZE(ObservationPeriodEndDates)];
            NUMBER currentPeriod, r;
            currentPeriod = 1;
            FOR d IN (1, SIZE(ObservationDates), 1) DO
              fix = Underlying(ObservationDates[d]);

              knockOutDays = max(DATEINDEX(GuaranteedPeriodEndDate, ObservationDates, GT) - 1 - d, 0);

              IF KnockedOut == 0 THEN
                IF {KnockOutType == 4 AND fix >= KnockOutLevel} OR
                   {KnockOutType == 3 AND fix <= KnockOutLevel} THEN
                   KnockedOut = 1;
                   Days[DefaultRange] = Days[DefaultRange] + knockOutDays;
                   FOR r IN (1, SIZE(RangeUpperBounds), 1) DO
                     value = value + PAY( LongShort * FixingAmount * RangeLeverages[r] * Days[r] * ( fix - Strike ),
                                          ObservationDates[d], KnockOutSettlementDates[d], PayCcy );
                   END;
                END;
              END;

              IF KnockedOut == 0 THEN
                FOR r IN (1, SIZE(RangeUpperBounds), 1) DO
                  IF fix > RangeLowerBounds[r] AND fix <= RangeUpperBounds[r] THEN
                    Days[r] = Days[r] + 1;
                  END;
                END;
                IF ObservationDates[d] >= ObservationPeriodEndDates[currentPeriod] THEN
                  FOR r IN (1, SIZE(RangeUpperBounds), 1) DO
                    value = value + LOGPAY( LongShort * FixingAmount * RangeLeverages[r] * Days[r] * ( fix - Strike ),
                                            ObservationDates[d], SettlementDates[currentPeriod], PayCcy );
                  END;
                END;
              END;
              IF ObservationDates[d] >= ObservationPeriodEndDates[currentPeriod] THEN
                Fixing[currentPeriod] = fix;
                currentPeriod = currentPeriod + 1;
                FOR r IN (1, SIZE(RangeUpperBounds), 1) DO
                  Days[r] = 0;
                END;
              END;
            END;
            currentNotional = FixingAmount *  Strike;
        ]]></Code>
      <NPV>value</NPV>
      <Results>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result>KnockedOut</Result>
        <Result>Fixing</Result>
      </Results>
      <CalibrationSpec>
        <Calibration>
          <Index>Underlying</Index>
          <Strikes>
            <Strike>Strike</Strike>
            <Strike>KnockOutLevel</Strike>
          </Strikes>
        </Calibration>
      </CalibrationSpec>
    </Script>
    <Script purpose="FD">
      <Code><![CDATA[
            REQUIRE SIZE(ObservationDates) == SIZE(KnockOutSettlementDates);
            REQUIRE SIZE(ObservationPeriodEndDates) == SIZE(SettlementDates);
            REQUIRE SIZE(RangeUpperBounds) == SIZE(RangeLowerBounds);
            REQUIRE SIZE(RangeUpperBounds) == SIZE(RangeLeverages);
            REQUIRE ObservationPeriodEndDates[SIZE(ObservationPeriodEndDates)] >= ObservationDates[SIZE(ObservationDates)];

            NUMBER currentPeriod, referencePayout, fix, d, r, dd, currentNotional, Fixing[SIZE(ObservationPeriodEndDates)];

            currentPeriod = DATEINDEX(ObservationDates[SIZE(ObservationDates)], ObservationPeriodEndDates, GEQ);
            referencePayout = PAY( LongShort * FixingAmount * ( Underlying(ObservationDates[SIZE(ObservationDates)]) - Strike ),
                                      ObservationDates[SIZE(ObservationDates)], SettlementDates[currentPeriod], PayCcy );
            value = 0 * referencePayout;

            FOR d IN (SIZE(ObservationDates), 1, -1) DO

              IF ObservationDates[d] >= TODAY THEN
                value = NPV(value, ObservationDates[d]);
                referencePayout = NPV(referencePayout, ObservationDates[d]);
              ELSE
                value = NPV(value, TODAY);
                referencePayout = NPV(referencePayout, TODAY);
              END;

              fix = Underlying(ObservationDates[d]);

              IF d > 1 AND currentPeriod > 0 AND ObservationDates[d-1] < ObservationPeriodEndDates[currentPeriod] THEN
                referencePayout = PAY( LongShort * FixingAmount * ( fix - Strike ), ObservationDates[d], SettlementDates[currentPeriod], PayCcy );
                Fixing[currentPeriod] = fix;
                currentPeriod = currentPeriod - 1;
              END;

              IF {KnockOutType == 4 AND fix >= KnockOutLevel} OR
                 {KnockOutType == 3 AND fix <= KnockOutLevel} THEN
                referencePayout = PAY( LongShort * FixingAmount * ( fix - Strike ), ObservationDates[d], KnockOutSettlementDates[d], PayCcy );
                value = referencePayout * RangeLeverages[DefaultRange] * max(DATEINDEX(GuaranteedPeriodEndDate, ObservationDates, GT) - 1 - d, 0);
              ELSE
                FOR r IN (1, SIZE(RangeUpperBounds), 1) DO
                  IF fix > RangeLowerBounds[r] AND fix <= RangeUpperBounds[r] THEN
                    value = value + RangeLeverages[r] * referencePayout;
                  END;
                END;
              END;
            END;
            currentNotional = FixingAmount *  Strike;
        ]]></Code>
      <NPV>value</NPV>
      <Results>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result>Fixing</Result>
      </Results>
      <CalibrationSpec>
        <Calibration>
          <Index>Underlying</Index>
          <Strikes>
            <Strike>Strike</Strike>
            <Strike>KnockOutLevel</Strike>
          </Strikes>
        </Calibration>
      </CalibrationSpec>
    </Script>
  </Script>
  <!-- Cash settled Window Barrier EQ, FX, COM Option

       Input Data:
       Expiry                          option expiry date
       Settlement                      option payoff settlement date
       ObservationDates                barrier monitoring dates
       Strike                          option strike
       BarrierLevel                    barrier level
       BarrierType                     1 for DownIn, 2 for UpIn, 3 for DownOut, 4 for UpOut
       PutCall                         1 for call, -1 for put
       LongShort                       1 for a long, -1 for a short position
       Quantity                        number of option contracts
       Underlying                      underlying index
       PayCcy                          payment currency
       PremiumPayDate                  option premium payment date
       PremiumAmount                   option premium amount
       PremiumCcy                      option premium currency

       Result Data:
       NPV                             npv of the option
       TriggerProbability              barrier trigger probability of the option
       ExerciseProbability             exercise probability of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>WindowBarrierOption</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
             REQUIRE BarrierType == 1 OR BarrierType == 2 OR BarrierType == 3 OR BarrierType == 4;

             NUMBER i, Payoff, TriggerProbability, ExerciseProbability, isUp, currentNotional;

             IF BarrierType == 1 OR BarrierType == 3 THEN
               TriggerProbability = BELOWPROB(Underlying, StartDate, EndDate, BarrierLevel);
             ELSE
               TriggerProbability = ABOVEPROB(Underlying, StartDate, EndDate, BarrierLevel);
             END;

             Payoff = Quantity * PutCall * (Underlying(Expiry) - Strike);
             IF Payoff > 0.0 THEN
               IF BarrierType == 1 OR BarrierType == 2 THEN
                 Option = PAY(Payoff * TriggerProbability, Expiry, Settlement, PayCcy);
                 ExerciseProbability = TriggerProbability;
               ELSE
                 Option = PAY(Payoff * (1 - TriggerProbability), Expiry, Settlement, PayCcy);
                 ExerciseProbability = (1 - TriggerProbability);
               END;
             END;

             Option = LongShort * (Option - PAY(PremiumAmount, PremiumPayDate, PremiumPayDate, PremiumCcy));
             currentNotional = Quantity * Strike;
      ]]></Code>
      <NPV>Option</NPV>
      <CalibrationSpec>
        <Calibration>
          <Index>Underlying</Index>
          <Strikes>
            <Strike>Strike</Strike>
            <Strike>BarrierLevel</Strike>
          </Strikes>
        </Calibration>
      </CalibrationSpec>
      <Results>
        <Result>TriggerProbability</Result>
        <Result>ExerciseProbability</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- TaRF

       Input Data:
       FixingAmount                    unleveraged amount paid on each fixing date
       LongShort                       1 for a long, -1 for a short position
       Underlying                      underlying index
       PayCcy                          payment currency
       FixingDates                     fixing valuation dates
       SettlementDates                 settlement dates
       RangeUpperBounds                upper bounds of fixing ranges
       RangeLowerBounds                lower bounds of fixing ranges
       RangeLeverages                  leverage amounts of fixing ranges
       RangeStrikes                    option strikes per range
       KnockOutProfitAmount            if > 0, trigger for accumulated profit
       KnockOutProfitEvents            if > 0, trigger for number of profit events
       TargetAmount                    target amount for TargetType = exact
       TargetType                      -1 for truncated, 0 for exact, 1 for full

       Result Data:
       value                           npv of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>TaRF</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
            REQUIRE FixingAmount > 0;
            REQUIRE LongShort == 1 OR LongShort == -1;
            REQUIRE SIZE(RangeUpperBounds) == SIZE(RangeLowerBounds);
            REQUIRE SIZE(RangeLowerBounds) == SIZE(RangeLeverages);
            REQUIRE SIZE(RangeLowerBounds) == SIZE(RangeStrikes);
            REQUIRE TargetType == -1 OR TargetType == 0 OR TargetType == 1;
            REQUIRE SIZE(FixingDates) == SIZE(SettlementDates);

            NUMBER Payoff, d, r, PnL, wasTriggered, AccProfit, Hits, currentNotional;
            NUMBER Fixing[SIZE(FixingDates)], Triggered[SIZE(FixingDates)];

            FOR r IN (1, SIZE(RangeUpperBounds), 1) DO
              REQUIRE RangeLowerBounds[r] <= RangeUpperBounds[r];
              REQUIRE RangeStrikes[r] >= 0;
            END;

            FOR d IN (1, SIZE(FixingDates), 1) DO
              Fixing[d] = Underlying(FixingDates[d]);
              IF wasTriggered != 1 THEN
                PnL = 0;
                FOR r IN (1, SIZE(RangeUpperBounds), 1) DO
                  IF Fixing[d] > RangeLowerBounds[r] AND Fixing[d] <= RangeUpperBounds[r] THEN
                    PnL = PnL + RangeLeverages[r] * FixingAmount * (Fixing[d] - RangeStrikes[r]);
                  END;
                END;

                IF PnL >= 0 THEN
                  AccProfit = AccProfit + PnL;
                  Hits = Hits + 1;
                END;

                IF {KnockOutProfitEvents > 0 AND Hits >= KnockOutProfitEvents} OR
                   {KnockOutProfitAmount > 0 AND AccProfit >= KnockOutProfitAmount} THEN
                  wasTriggered = 1;
                  Triggered[d] = 1;
                  IF TargetType == 0 THEN
                    Payoff = Payoff + LOGPAY(TargetAmount - (AccProfit - PnL), FixingDates[d], SettlementDates[d], PayCcy, 0, Cashflow);
                  END;
                  IF TargetType == 1 THEN
                    Payoff = Payoff + LOGPAY(PnL, FixingDates[d], SettlementDates[d], PayCcy, 0, Cashflow);
                  END;
                ELSE
                    Payoff = Payoff + LOGPAY(PnL, FixingDates[d], SettlementDates[d], PayCcy, 0, Cashflow);
                END;
              END;
            END;
            value = LongShort * Payoff;
            currentNotional = FixingAmount * RangeStrikes[1];
        ]]></Code>
      <NPV>value</NPV>
      <Results>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result>Fixing</Result>
        <Result>Triggered</Result>
      </Results>
    </Script>
  </Script>

  <!-- BASKET OPTIONS -->

  <!-- Vanilla Basket EQ, FX, COM Option

       Inputs:
       Expiry                          option expiry date
       Settlement                      option payoff settlement date
       Strike                          option strike
       PutCall                         1 for call, -1 for put
       LongShort                       1 for a long, -1 for a short position
       Notional                        multiplier, number of option contracts, etc.
       Underlyings                     vector of underlying indices
       Weights                         vector of underlying weights
       PayCcy                          payment currency

       Results:
       Option                          npv of the option
       ExerciseProbability             exercise probability of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>VanillaBasketOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE SIZE(Underlyings) == SIZE(Weights);

      NUMBER u, basketPrice, ExerciseProbability, Payoff, currentNotional;

      FOR u IN (1, SIZE(Underlyings), 1) DO
          basketPrice = basketPrice + Underlyings[u](Expiry) * Weights[u];
      END;

      Payoff = max(PutCall * (basketPrice - Strike), 0);

      Option = LongShort * Notional * PAY(Payoff, Expiry, Settlement, PayCcy);

      IF Payoff > 0 THEN
          ExerciseProbability = 1;
      END;
      currentNotional = Notional * Strike;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result>ExerciseProbability</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Asian Basket EQ, FX, COM Option

       Inputs:
       Expiry                          option expiry date
       Settlement                      option payoff settlement date
       Strike                          option strike
       PutCall                         1 for call, -1 for put
       LongShort                       1 for a long, -1 for a short position
       Notional                        multiplier, number of option contracts, etc.
       Underlyings                     vector of underlying indices
       Weights                         vector of underlying weights
       PayCcy                          payment currency
       ObservationDates                observation dates

       Results:
       Option                          npv of the option
       ExerciseProbability             exercise probability of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>AsianBasketOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE SIZE(Underlyings) == SIZE(Weights);

      NUMBER d, u, basketPrice, ExerciseProbability, Payoff;
      NUMBER currentNotional;

      FOR d IN (1, SIZE(ObservationDates), 1) DO
          FOR u IN (1, SIZE(Underlyings), 1) DO
              basketPrice = basketPrice + Underlyings[u](ObservationDates[d]) * Weights[u];
          END;
      END;

      basketPrice = basketPrice / SIZE(ObservationDates);

      Payoff = max(PutCall * (basketPrice - Strike), 0);

      Option = LongShort * Notional * PAY(Payoff, Expiry, Settlement, PayCcy);

      IF Payoff > 0 THEN
          ExerciseProbability = 1;
      END;

      currentNotional = Notional * Strike;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result>ExerciseProbability</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Average Strike Basket EQ, FX, COM Option

       Inputs:
       Expiry                          option expiry date
       Settlement                      option payoff settlement date
       PutCall                         1 for call, -1 for put
       LongShort                       1 for a long, -1 for a short position
       Notional                        multiplier, number of option contracts, etc.
       Underlyings                     vector of underlying indices
       Weights                         vector of underlying weights
       PayCcy                          payment currency
       ObservationDates                observation dates

       Results:
       Option                          npv of the option
       ExerciseProbability             exercise probability of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>AverageStrikeBasketOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE SIZE(Underlyings) == SIZE(Weights);

      NUMBER d, u, timeAverageBasketPrice, currentNotional;
      FOR d IN (1, SIZE(ObservationDates), 1) DO
          FOR u IN (1, SIZE(Underlyings), 1) DO
              timeAverageBasketPrice = timeAverageBasketPrice
                + Underlyings[u](ObservationDates[d]) * Weights[u];
          END;
      END;
      timeAverageBasketPrice = timeAverageBasketPrice / SIZE(ObservationDates);

      NUMBER expiryBasketPrice;
      FOR u IN (1, SIZE(Underlyings), 1) DO
         expiryBasketPrice = expiryBasketPrice + Underlyings[u](Expiry) * Weights[u];
      END;

      NUMBER Payoff;
      Payoff = max(PutCall * (expiryBasketPrice - timeAverageBasketPrice), 0);

      Option = LongShort * Notional * PAY(Payoff, Expiry, Settlement, PayCcy);

      NUMBER ExerciseProbability;
      IF Payoff > 0 THEN
          ExerciseProbability = 1;
      END;
      FOR u IN (1, SIZE(Underlyings), 1) DO
        currentNotional = currentNotional + Notional * Underlyings[u](ObservationDates[1]) * Weights[u];
      END;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result>ExerciseProbability</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Lookback Call Basket EQ, FX, COM Option

       Inputs:
       Expiry                          option expiry date
       Settlement                      option payoff settlement date
       LongShort                       1 for a long, -1 for a short position
       Notional                        multiplier, number of option contracts, etc.
       Underlyings                     vector of underlying indices
       Weights                         vector of underlying weights
       PayCcy                          payment currency
       ObservationDates                observation dates

       Results:
       Option                          npv of the option
       ExerciseProbability             exercise probability of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>LookbackCallBasketOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE SIZE(Underlyings) == SIZE(Weights);

      NUMBER d, u, basketPrice, minBasketPrice, currentNotional;
      FOR d IN (1, SIZE(ObservationDates), 1) DO
          basketPrice = 0;
          FOR u IN (1, SIZE(Underlyings), 1) DO
              basketPrice = basketPrice + Underlyings[u](ObservationDates[d]) * Weights[u];
          END;
          IF d == 1 THEN
              minBasketPrice = basketPrice;
          END;
          IF basketPrice < minBasketPrice THEN
              minBasketPrice = basketPrice;
          END;
      END;

      NUMBER expiryBasketPrice;
      FOR u IN (1, SIZE(Underlyings), 1) DO
         expiryBasketPrice = expiryBasketPrice + Underlyings[u](Expiry) * Weights[u];
      END;

      NUMBER Payoff;
      Payoff = max(expiryBasketPrice - minBasketPrice, 0);

      Option = LongShort * Notional * PAY(Payoff, Expiry, Settlement, PayCcy);

      NUMBER ExerciseProbability;
      IF Payoff > 0 THEN
          ExerciseProbability = 1;
      END;
      FOR u IN (1, SIZE(Underlyings), 1) DO
        currentNotional = currentNotional + Notional * Underlyings[u](ObservationDates[1]) * Weights[u];
      END;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result>ExerciseProbability</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Lookback Put Basket EQ, FX, COM Option

       Inputs:
       Expiry                          option expiry date
       Settlement                      option payoff settlement date
       LongShort                       1 for a long, -1 for a short position
       Notional                        multiplier, number of option contracts, etc.
       Underlyings                     vector of underlying indices
       Weights                         vector of underlying weights
       PayCcy                          payment currency
       ObservationDates                observation dates

       Results:
       Option                          npv of the option
       ExerciseProbability             exercise probability of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>LookbackPutBasketOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE SIZE(Underlyings) == SIZE(Weights);

      NUMBER d, u, basketPrice, maxBasketPrice, currentNotional;
      FOR d IN (1, SIZE(ObservationDates), 1) DO
          basketPrice = 0;
          FOR u IN (1, SIZE(Underlyings), 1) DO
              basketPrice = basketPrice + Underlyings[u](ObservationDates[d]) * Weights[u];
          END;
          IF d == 1 THEN
              maxBasketPrice = basketPrice;
          END;
          IF basketPrice > maxBasketPrice THEN
              maxBasketPrice = basketPrice;
          END;
      END;

      NUMBER expiryBasketPrice;
      FOR u IN (1, SIZE(Underlyings), 1) DO
         expiryBasketPrice = expiryBasketPrice + Underlyings[u](Expiry) * Weights[u];
      END;

      NUMBER Payoff;
      Payoff = max(maxBasketPrice - expiryBasketPrice, 0);

      Option = LongShort * Notional * PAY(Payoff, Expiry, Settlement, PayCcy);

      NUMBER ExerciseProbability;
      IF Payoff > 0 THEN
          ExerciseProbability = 1;
      END;
      FOR u IN (1, SIZE(Underlyings), 1) DO
        currentNotional = currentNotional + Notional * Underlyings[u](ObservationDates[1]) * Weights[u];
      END;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result>ExerciseProbability</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Worst Of Basket EQ, FX, COM Swap

       Inputs:
       LongShort                       1 for long, -1 for short
       Quantity                        equity notional amount, multiplier, number of option contracts, etc.
       InitialFixedRate                rate amount paid at the first settlement date, % in decimal form
       Underlyings                     vector of underlying indices
       InitialPrices                   the agreed initial price for each underlying
       DeterminationDates              Floating leg period dates, knock-out determination dates, fixed rate trigger
                                        determination dates, and the last date is a knock-in determination date
       SettlementDates                 Settlement dates for the corresponding determination dates, or dates on which the
                                        corresponding knock-out occurs or fixed/floating/knock-in payout/s are made
       KnockOutLevels                  barrier values for determining a knock-out trigger event, % in decimal form
       CouponTriggerLevels              barrier values for determining a fixed rate trigger event, % in decimal form
       KnockInLevel                    barrier values for determining a knock-in trigger event, % in decimal form
       CouponRate                      fixed leg rate
       AccumulatingCoupons             whether coupon amounts accumulate based on number of periods until next
                                        coupon event, 0 for False, 1 for True
       FloatingIndex                   floating leg underlying IR index
       FloatingSpread                  floating leg spread
       FloatingDayCountFraction        floating leg day count fraction
       FixingSchedule                  floating leg fixing dates/schedule derived from the DeterminationDates
       Strike                          strike price, as a % of the initial price in decimal form
       PayCcy                          currency of all settlement payments

       Results:
       Option                          npv of the option
  -->
  <Script>
    <Name>WorstOfBasketSwap</Name>
    <ProductTag>MultiAssetOptionAD({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE SIZE(Underlyings) == SIZE(InitialPrices);
        REQUIRE SIZE(SettlementDates) == SIZE(DeterminationDates);
        REQUIRE SIZE(KnockOutLevels) == SIZE(DeterminationDates) - 1;
        REQUIRE SIZE(CouponTriggerLevels) == SIZE(DeterminationDates) - 1;

        NUMBER alive, couponAccumulation, fixing, n, accrualFraction, indexInitial;
        NUMBER allAssetsTriggered, indexFinal, performance, worstPerformance, d, payoff, u;
        
        Option = Option + LOGPAY(LongShort * Quantity * InitialFixedRate,
                                 SettlementDates[1], SettlementDates[1], PayCcy, 0, InitialFixedAmount);

        alive = 1;
        couponAccumulation = 1;
        n = SIZE(DeterminationDates);

        FOR d IN (2, n, 1) DO
          fixing = FloatingIndex(FixingSchedule[d-1]) + FloatingSpread;
          accrualFraction = dcf(FloatingDayCountFraction, DeterminationDates[d-1], DeterminationDates[d]);
          Option = Option + LOGPAY(-1 * LongShort * Quantity * alive * fixing * accrualFraction,
                                   FixingSchedule[d-1], SettlementDates[d], PayCcy, 1, FloatingLeg);

          allAssetsTriggered = 1;
          FOR u IN (1, SIZE(Underlyings), 1) DO
            IF Underlyings[u](DeterminationDates[d]) < CouponTriggerLevels[d-1] * InitialPrices[u] THEN
              allAssetsTriggered = 0;
            END;
          END;
          Option = Option + LOGPAY(allAssetsTriggered * LongShort * Quantity * alive * CouponRate
                                   * couponAccumulation, SettlementDates[d], SettlementDates[d],
                                   PayCcy, 2, FixedCouponLeg);
          couponAccumulation = allAssetsTriggered + (1-allAssetsTriggered) * couponAccumulation;
          IF AccumulatingCoupons == 1 THEN
            couponAccumulation = couponAccumulation + (1-allAssetsTriggered)
          END;

          IF d == n THEN
            worstPerformance = 999999.9;
            FOR u IN (1, SIZE(Underlyings), 1) DO
              indexInitial = InitialPrices[u];
              indexFinal = Underlyings[u](DeterminationDates[n]);
              performance = indexFinal / indexInitial;

              IF performance < worstPerformance THEN
                worstPerformance = performance;
              END;
            END;       

            IF worstPerformance < min(Strike, KnockInLevel) THEN
              payoff = worstPerformance - Strike;
              Option = Option + LOGPAY(LongShort * Quantity * alive * payoff, DeterminationDates[n],
                                       SettlementDates[n], PayCcy, 3, EquityAmountPayoff);
            END;
          END;

          IF d != n THEN
            allAssetsTriggered = 1;
            FOR u IN (1, SIZE(Underlyings), 1) DO
              IF Underlyings[u](DeterminationDates[d]) < KnockOutLevels[d-1] * InitialPrices[u] THEN
                allAssetsTriggered = 0;
              END;
            END;
            alive = alive * (1 - allAssetsTriggered);
          END;
        END;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result rename="currentNotional">Quantity</Result>
      </Results>
    </Script>
  </Script>

  <!-- RAINBOW OPTIONS -->

  <!-- Best of Asset or Cash Rainbow EQ, FX, COM Option

       Inputs:
       Expiry                          option expiry date
       Settlement                      option payoff settlement date
       LongShort                       1 for a long, -1 for a short position
       Notional                        multiplier, number of option contracts, etc.
       Strike                          option strike
       Underlyings                     vector of underlying indices
       Weights                         vector of underlying weights
       PayCcy                          payment currency

       Results:
       Option                          npv of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
       expectedUnderlyingValue         vector of expected values of the underlyings at expiry
  -->
  <Script>
    <Name>BestOfAssetOrCashRainbowOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE SIZE(Underlyings) == SIZE(Weights);
      NUMBER u, thisPrice, bestPrice, Payoff, currentNotional;
      NUMBER expUnderValue[SIZE(Underlyings)];
      bestPrice = Strike;
      FOR u IN (1, SIZE(Underlyings), 1) DO
          expUnderValue[u] = Underlyings[u](Expiry);
          thisPrice = Underlyings[u](Expiry) * Weights[u];
          IF thisPrice > bestPrice THEN
              bestPrice = thisPrice;
          END;
      END;
      Option = LongShort * Notional * PAY(bestPrice, Expiry, Settlement, PayCcy);
      currentNotional = Notional * Strike;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result rename="expectedUnderlyingValue">expUnderValue</Result>
      </Results>
    </Script>
  </Script>
  <!-- Worst of Asset or Cash Rainbow EQ, FX, COM Option

       Inputs:
       Expiry                          option expiry date
       Settlement                      option payoff settlement date
       LongShort                       1 for a long, -1 for a short position
       Notional                        multiplier, number of option contracts, etc.
       Strike                          option strike
       Underlyings                     vector of underlying indices
       Weights                         vector of underlying weights
       PayCcy                          payment currency

       Results:
       Option                          npv of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
       expectedUnderlyingValue         vector of expected values of the underlyings at expiry
  -->
  <Script>
    <Name>WorstOfAssetOrCashRainbowOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE SIZE(Underlyings) == SIZE(Weights);
      NUMBER u, thisPrice, worstPrice, Payoff, currentNotional;
      NUMBER expUnderValue[SIZE(Underlyings)];
      worstPrice = Strike;
      FOR u IN (1, SIZE(Underlyings), 1) DO
          expUnderValue[u] = Underlyings[u](Expiry);
          thisPrice = Underlyings[u](Expiry) * Weights[u];
          IF thisPrice < worstPrice THEN
              worstPrice = thisPrice;
          END;
      END;
      Option = LongShort * Notional * PAY(worstPrice, Expiry, Settlement, PayCcy);
      currentNotional = Notional * Strike;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result rename="expectedUnderlyingValue">expUnderValue</Result>
      </Results>
    </Script>
  </Script>
  <!-- Max Rainbow EQ, FX, COM Option

       Inputs:
       Expiry                          option expiry date
       Settlement                      option payoff settlement date
       PutCall                         1 for call, -1 for put
       LongShort                       1 for a long, -1 for a short position
       Notional                        multiplier, number of option contracts, etc.
       Strike                          option strike
       Underlyings                     vector of underlying indices
       Weights                         vector of underlying weights
       PayCcy                          payment currency

       Results:
       Option                          npv of the option
       ExerciseProbability             exercise probability of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>MaxRainbowOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE SIZE(Underlyings) == SIZE(Weights);

      NUMBER u, thisPrice, maxPrice, Payoff, ExerciseProbability, currentNotional;
      NUMBER expUnderValue[SIZE(Underlyings)];
      maxPrice = 0;
      FOR u IN (1, SIZE(Underlyings), 1) DO
          expUnderValue[u] = Underlyings[u](Expiry);
          thisPrice = Underlyings[u](Expiry) * Weights[u];
          IF thisPrice > maxPrice THEN
              maxPrice = thisPrice;
          END;
      END;

      Payoff = max(PutCall * (maxPrice - Strike), 0);

      Option = LongShort * Notional * PAY(Payoff, Expiry, Settlement, PayCcy);

      IF Payoff > 0 THEN
          ExerciseProbability = 1;
      END;
      currentNotional = Notional * Strike;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result>ExerciseProbability</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result rename="expectedUnderlyingValue">expUnderValue</Result>
      </Results>
    </Script>
  </Script>
  <!-- Min Rainbow EQ, FX, COM Option

       Inputs:
       Expiry                          option expiry date
       Settlement                      option payoff settlement date
       PutCall                         1 for call, -1 for put
       LongShort                       1 for a long, -1 for a short position
       Notional                        multiplier, number of option contracts, etc.
       Strike                          option strike
       Underlyings                     vector of underlying indices
       Weights                         vector of underlying weights
       PayCcy                          payment currency

       Results:
       Option                          npv of the option
       ExerciseProbability             exercise probability of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>MinRainbowOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE SIZE(Underlyings) == SIZE(Weights);
      REQUIRE SIZE(Underlyings) > 0;

      NUMBER u, thisPrice, minPrice, Payoff, ExerciseProbability, currentNotional;
      NUMBER expUnderValue[SIZE(Underlyings)];
      minPrice = Underlyings[1](Expiry) * Weights[1];
      FOR u IN (1, SIZE(Underlyings), 1) DO
          expUnderValue[u] = Underlyings[u](Expiry);
          thisPrice = Underlyings[u](Expiry) * Weights[u];
          IF thisPrice < minPrice THEN
              minPrice = thisPrice;
          END;
      END;

      Payoff = max(PutCall * (minPrice - Strike), 0);

      Option = LongShort * Notional * PAY(Payoff, Expiry, Settlement, PayCcy);

      IF Payoff > 0 THEN
          ExerciseProbability = 1;
      END;
      currentNotional = Notional * Strike;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result>ExerciseProbability</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result rename="expectedUnderlyingValue">expUnderValue</Result>
      </Results>
    </Script>
  </Script>
  <!-- European Rainbow Call Spread EQ, FX, COM Option

       Inputs:
       Expiry                          option expiry date
       Settlement                      option payoff settlement date
       LongShort                       1 for a long, -1 for a short position
       Notional                        multiplier, number of option contracts, etc.
       Underlyings                     vector of underlying indices
       InitialStrikes                  vector of initial strikes
       Weights                         vector of weights for best, second best, ... , worst
       performing underlying
       Floor                           floor
       Cap                             cap
       PayCcy                          payment currency

       Results:
       Option                          npv of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>EuropeanRainbowCallSpreadOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE SIZE(Underlyings) == SIZE(Weights);
      NUMBER perf[SIZE(Underlyings)], return, u;
      FOR u IN (1, SIZE(Underlyings), 1) DO
        perf[u] = Underlyings[u](Expiry) / InitialStrikes[u];
      END;
      SORT (perf);
      FOR u IN (1, SIZE(Underlyings), 1) DO
        return = return + Weights[u] * perf[SIZE(Underlyings) + 1 - u];
      END;
      Option = LongShort * PAY( Notional * min( max( Floor, return - 1 ), Cap ), Expiry, Settlement, PayCcy );
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="currentNotional">Notional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Rainbow Call Spread EQ, FX, COM Barrier Option

       Inputs:
       Expiry                option expiry date
       Settlement            option payoff settlement date
       LongShort             1 for a long, -1 for a short position
       Notional              multiplier, number of option contracts, etc.
       Underlyings           vector of underlying indices
       InitialPrices         vector of initial prices
       Weights               vector of weights for best, second best, ... , worst
                               performing underlying
       Strike                option strike price
       Floor                 floor
       Cap                   cap
       Gearing               gearing/payoff multiplier
       BermudanBarrier       True for Bermudan observability, False for European
       BarrierLevel          KI barrier level, expressed as a percentage of the initial
                              prices in decimal form
       BarrierSchedule       if BermudanBarrier is true, the barrier observation schedule
       PayCcy                payment currency

       Results:
       Option                npv of the option
       currentNotional       current notional
       notionalCurrency      notional currency
  -->
  <Script>
    <Name>RainbowCallSpreadBarrierOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE SIZE(Underlyings) == SIZE(Weights);
      REQUIRE Floor <= Cap;
      NUMBER performance, perf[SIZE(Underlyings)], return, u, d, payoff, knockedIn;

      FOR u IN (1, SIZE(Underlyings), 1) DO
        perf[u] = Underlyings[u](Expiry) / InitialPrices[u];
      END;
      SORT (perf);
      
      FOR u IN (1, SIZE(Underlyings), 1) DO
        return = return + Weights[u] * perf[SIZE(Underlyings) + 1 - u];
      END;

      IF BermudanBarrier == 1 THEN
        FOR d IN (1, SIZE(BarrierSchedule), 1) DO
          IF knockedIn == 0 THEN
            FOR u IN (1, SIZE(Underlyings), 1) DO
              performance = Underlyings[u](BarrierSchedule[d]) / InitialPrices[u];
              IF performance <= BarrierLevel THEN
                knockedIn = 1;
              END;
            END;
          END;
        END;
      ELSE
        FOR u IN (1, SIZE(perf), 1) DO
          IF perf[u] <= BarrierLevel THEN
            knockedIn = 1;
          END;
        END;
      END;
      
      payoff = min( max( Floor, return - Strike ), Cap );
      Option = LongShort * PAY(Notional * Gearing * payoff * knockedIn,
                               Expiry, Settlement, PayCcy);
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="currentNotional">Notional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Asian Rainbow Call Spread EQ, FX, COM Option

       Inputs:
       Expiry                          option expiry date
       Settlement                      option payoff settlement date
       AveragingDates                  observation dates for calculating average final price
       LongShort                       1 for a long, -1 for a short position
       Notional                        multiplier, number of option contracts, etc.
       Underlyings                     vector of underlying indices
       InitialStrikes                  vector of initial strikes
       Weights                         vector of weights for best, second best, ... , worst
       performing underlying
       Floor                           floor
       Cap                             cap
       PayCcy                          payment currency

       Results:
       Option                          npv of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>AsianRainbowCallSpreadOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE SIZE(Underlyings) == SIZE(Weights);
      NUMBER perf[SIZE(Underlyings)], return, d, u;
      FOR u IN (1, SIZE(Underlyings), 1) DO
        FOR d IN (1, SIZE(AveragingDates), 1) DO
          perf[u] = perf[u] + Underlyings[u](AveragingDates[d]);
        END;
        perf[u] = perf[u] / SIZE(AveragingDates);
      END;
      SORT (perf);
      FOR u IN (1, SIZE(Underlyings), 1) DO
        return = return + Weights[u] * perf[SIZE(Underlyings) + 1 - u];
      END;
      Option = LongShort * PAY( Notional * min( max( Floor, return - 1 ), Cap ), Expiry, Settlement, PayCcy );
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="currentNotional">Notional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Worst Performance Rainbow EQ, FX, COM Option 01

       Inputs:
       LongShort          1 for Long, -1 for Short
       Underlyings        vector of underlying indices
       InitialPrices      the agreed initial price for each underlying index
       Premium            the option premium amount
       PremiumDate        the option premium date
       Quantity           multiplier, no. of option contracts, equity notional amount, etc.
       PayoffMultiplier   the factor to multiply the payoff by when the payoff is negative
       ObservationDate    the date on which the 'final' index values are determined
       SettlementDate     the option payoff settlement date
       PayCcy             the settlement/payment currency

       Results:
       Option             npv of the option
       notionalCurrency   notional currency
  -->
  <Script>
    <Name>WorstPerformanceRainbowOption01</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE SIZE(Underlyings) == SIZE(InitialPrices);
        REQUIRE ObservationDate <= SettlementDate;

        NUMBER u, indexInitial, indexFinal, performance;
        NUMBER worstPerformance, payoff, premium;

        FOR u IN (1, SIZE(Underlyings), 1) DO
          indexInitial = InitialPrices[u];
          indexFinal = Underlyings[u](ObservationDate);
          performance = indexFinal / indexInitial;

          IF {u == 1} OR {performance < worstPerformance} THEN
            worstPerformance = performance;
          END;
        END;

        payoff = LOGPAY(Quantity * (worstPerformance - 1), ObservationDate,
                        SettlementDate, PayCcy, 1, Payoff);

        IF worstPerformance < 1 THEN
          payoff = payoff * PayoffMultiplier;
        END;

        premium = LOGPAY(Premium, PremiumDate, PremiumDate, PayCcy, 0, Premium);

        Option = LongShort * (payoff - premium);
        ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result rename="currentNotional">Quantity</Result>
      </Results>
    </Script>
  </Script>
  <!-- Worst Performance Rainbow EQ, FX, COM Option 02

       Inputs:
       LongShort          1 for Long, -1 for Short             
       Underlyings        vector of underlying indices
       InitialPrices      the agreed initial price for each underlying index
       Premium            the total option premium amount
       PremiumDate        the option premium date
       Quantity           multiplier, no. of option contracts, equity notional amount, etc.
       PayoffMultiplier   the factor to multiply the payoff by when the worst performing asset
                          has a positive return over the period
       Floor              the maximum possible loss incurred by the option buyer when the worst
                          performing asset has a negative return over the period
       ObservationDate    the date on which the 'final' index values are determined
       SettlementDate     the option payoff settlement date
       PayCcy             the settlement/payment currency

       Results:
       Option             npv of the option
       notionalCurrency   notional currency
  -->
  <Script>
    <Name>WorstPerformanceRainbowOption02</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE SIZE(Underlyings) == SIZE(InitialPrices);
        REQUIRE ObservationDate <= SettlementDate;
        REQUIRE Floor <= 0;

        NUMBER u, initialPrice, finalPrice, performance;
        NUMBER worstPerformance, payoff, premium;

        FOR u IN (1, SIZE(Underlyings), 1) DO
          initialPrice = InitialPrices[u];
          finalPrice = Underlyings[u](ObservationDate);
          performance = finalPrice / initialPrice;
 
          IF {u == 1} OR {performance < worstPerformance} THEN
            worstPerformance = performance;
          END;
        END;

        IF worstPerformance > 1 THEN
          payoff = PayoffMultiplier * (worstPerformance - 1);
        ELSE
          IF worstPerformance < 1 THEN
            payoff = max(Floor, worstPerformance - 1);
          ELSE
            payoff = 0;
          END;
        END;

        payoff = Quantity * LOGPAY(payoff, ObservationDate, SettlementDate,
                                   PayCcy, 1, Payoff);
        premium = LOGPAY(Premium, PremiumDate, PremiumDate, PayCcy,
                         0, Premium);

        Option = LongShort * (payoff - premium);
        ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result rename="currentNotional">Quantity</Result>
      </Results>
    </Script>
  </Script>
  <!-- Worst Performance Rainbow EQ, FX, COM Option 03

       Inputs:
       LongShort          1 for Long, -1 for Short
       Underlyings        vector of underlying indices
       InitialPrices      the agreed initial price for each underlying index
       Premium            the option premium amount
       PremiumDate        the option premium date
       Strike             the option strike price, expressed as a percentage of the initial
                            prices in decimal form
       Quantity           multiplier, no. of option contracts, equity notional amount, etc.
       PayoffMultiplier   the factor to multiply the payoff by when the payoff is negative
       Cap                the maximum possible profit received by the option buyer when the
                            worst performing asset has a positive return over the period
       Floor              the maximum possible loss incurred by the option buyer when the
                            worst performing asset has a negative return over the period
       BermudanBarrier    True for Bermudan observability, False for European
       BarrierLevel       KI/KO barrier level, expressed as a percentage of the initial
                            prices in decimal form
       BarrierSchedule    if BermudanBarrier is true, the barrier observation schedule
       ObservationDate    the date on which the 'final' index values are determined
       SettlementDate     the option payoff settlement date
       PayCcy             the settlement/payment currency

       Results:
       Option             npv of the option
       notionalCurrency   notional currency

  This script is an extension of the WorstPerformanceRainbowOption01, with the following additions/modifications:
    - European and Bermudan KI/KO barrier
    - Add cap and floor features
    - Allow for varying strike

  -->
  <Script>
    <Name>WorstPerformanceRainbowOption03</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE SIZE(Underlyings) == SIZE(InitialPrices);
        REQUIRE ObservationDate <= SettlementDate;
        REQUIRE Floor <= Cap;

        NUMBER indexInitial, indexFinal, performance, d;
        NUMBER worstPerformance, payoff, premium, knockedIn, u;

        FOR u IN (1, SIZE(Underlyings), 1) DO
          indexInitial = InitialPrices[u];
          indexFinal = Underlyings[u](ObservationDate);
          performance = indexFinal / indexInitial;

          IF {u == 1} OR {performance < worstPerformance} THEN
            worstPerformance = performance;
          END;
        END;

        IF BermudanBarrier == 1 THEN
          FOR d IN (1, SIZE(BarrierSchedule), 1) DO
            IF knockedIn == 0 THEN
              FOR u IN (1, SIZE(Underlyings), 1) DO
                indexInitial = InitialPrices[u];
                indexFinal = Underlyings[u](BarrierSchedule[d]);
                performance = indexFinal / indexInitial;

                IF performance <= BarrierLevel THEN
                  knockedIn = 1;
                END;
              END;
            END;
          END;
        ELSE
          IF worstPerformance <= BarrierLevel THEN
            knockedIn = 1;
          END;
        END;

        payoff = min(Cap, max(Floor, worstPerformance - Strike));
        payoff = LOGPAY(Quantity * payoff * knockedIn, ObservationDate,
                        SettlementDate, PayCcy, 1, Payoff);

        IF worstPerformance < 1 THEN
          payoff = payoff * PayoffMultiplier;
        END;

        premium = LOGPAY(Premium, PremiumDate, PremiumDate,
                         PayCcy, 0, Premium);

        Option = LongShort * (payoff - premium);
        ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result rename="currentNotional">Quantity</Result>
      </Results>
    </Script>
  </Script>
   <!-- Worst Performance Rainbow EQ, FX, COM Option 04

       Inputs:
       LongShort          1 for Long, -1 for Short
       Underlyings        vector of underlying indices
       InitialPrices      the agreed initial price for each underlying index
       Premium            the option premium amount
       PremiumDate        the option premium date
       Strike             the option strike price, expressed as a percentage of the initial
                            prices in decimal form
       Quantity           multiplier, no. of option contracts, equity notional amount, etc.
       PayoffMultiplier   the factor to multiply the payoff by when no knock-in event has
                            occurred. The mutliplication is applied before the cap/floor
       Cap                the maximum possible profit received by the option buyer when no
                            knock-in event has occurred.
       Floor              the maximum possible loss incurred by the option buyer when no
                            knock-in event has occurred.
       BermudanBarrier    True for Bermudan observability, False for European
       BarrierLevel       KI/KO barrier level, expressed as a percentage of the initial
                            prices in decimal form
       BarrierSchedule    if BermudanBarrier is true, the barrier observation schedule
       ObservationDate    the date on which the 'final' index values are determined
       SettlementDate     the option payoff settlement date
       PayCcy             the settlement/payment currency

       Results:
       Option             npv of the option
       notionalCurrency   notional currency
  -->
  <Script>
    <Name>WorstPerformanceRainbowOption04</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE SIZE(Underlyings) == SIZE(InitialPrices);
        REQUIRE ObservationDate <= SettlementDate;
        REQUIRE Floor <= Cap;

        NUMBER indexInitial, indexFinal, performance, d;
        NUMBER worstPerformance, payoff, premium, knockedIn, u;

        FOR u IN (1, SIZE(Underlyings), 1) DO
          indexInitial = InitialPrices[u];
          indexFinal = Underlyings[u](ObservationDate);
          performance = indexFinal / indexInitial;

          IF {u == 1} OR {performance < worstPerformance} THEN
            worstPerformance = performance;
          END;
        END;

        IF BermudanBarrier == 1 THEN
          FOR d IN (1, SIZE(BarrierSchedule), 1) DO
            IF knockedIn == 0 THEN
              FOR u IN (1, SIZE(Underlyings), 1) DO
                indexInitial = InitialPrices[u];
                indexFinal = Underlyings[u](BarrierSchedule[d]);
                performance = indexFinal / indexInitial;

                IF performance <= BarrierLevel THEN
                  knockedIn = 1;
                END;
              END;
            END;
          END;
        ELSE
          IF worstPerformance <= BarrierLevel THEN
            knockedIn = 1;
          END;
        END;

        payoff = worstPerformance - Strike;
        IF knockedIn == 0 THEN
          payoff = min(Cap, max(Floor, PayoffMultiplier * payoff));
        END;

        payoff = LOGPAY(Quantity * payoff, ObservationDate,
                        SettlementDate, PayCcy, 1, Payoff);

        premium = LOGPAY(Premium, PremiumDate, PremiumDate,
                         PayCcy, 0, Premium);

        Option = LongShort * (payoff - premium);
        ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result rename="currentNotional">Quantity</Result>
      </Results>
    </Script>
  </Script>
  <!-- Worst Performance Rainbow EQ, FX, COM Option 05

      Inputs:
      LongShort         1 for Long, -1 for Short
      PutCall           1 for Call, -1 for Put
      Underlyings       vector of underlying indices
      InitialPrices     the agreed initial price for each underlying index
      Premium           the option premium amount
      PremiumDate       the option premium date
      Strike            the option strike price, expressed as a percentage of the initial
                          prices in decimal form
      Quantity          multiplier, no. of option contracts, equity notional amount, etc.
      BarrierType       1 for DownIn, 2 for UpIn, 3 for DownOut, 4 for UpOut
      BarrierLevel      KI/KO barrier level, expressed as a percentage of the initial
                          prices in decimal form
      ObservationDate   the date on which the 'final' index values are determined
      SettlementDate    the option payoff settlement date
      PayCcy            the settlement/payment currency

      Results:
      Option             npv of the option
      notionalCurrency   notional currency
  -->
  <Script>
    <Name>WorstPerformanceRainbowOption05</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE SIZE(Underlyings) == SIZE(InitialPrices);
        REQUIRE ObservationDate <= SettlementDate;
        REQUIRE BarrierType == 1 OR BarrierType == 2;

        NUMBER indexInitial, indexFinal, performance;
        NUMBER worstPerformance, payoff, premium, knockedIn, u;

        FOR u IN (1, SIZE(Underlyings), 1) DO
          indexInitial = InitialPrices[u];
          indexFinal = Underlyings[u](ObservationDate);
          performance = indexFinal / indexInitial;

          IF {u == 1} OR {performance < worstPerformance} THEN
            worstPerformance = performance;
          END;
        END;

        IF {{BarrierType == 1 OR BarrierType == 4}
              AND worstPerformance <= BarrierLevel}
        OR {{BarrierType == 2 OR BarrierType == 3}
              AND worstPerformance >= BarrierLevel} THEN
          knockedIn = 1;
        END;

        IF knockedIn == 0 THEN
          payoff = 0;
        ELSE
          payoff = max(0, PutCall * (worstPerformance - Strike));
        END;

        payoff = LOGPAY(Quantity * payoff, ObservationDate,
                        SettlementDate, PayCcy, 1, Payoff);

        premium = LOGPAY(Premium, PremiumDate, PremiumDate,
                         PayCcy, 0, Premium);

        Option = LongShort * (payoff - premium);
        ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result rename="currentNotional">Quantity</Result>
      </Results>
    </Script>
  </Script>

  <!-- EXOTIC VARIANCE DERIVATIVES -->

  <!-- Variance/Volatility Option - EQ, FX or COMM

      Inputs:
      LongShort             whether own party pays on the variance (or volatility) strike,
                              1 for Long (fixed payer), -1 for Short
      PutCall               the option type: 1 for call, -1 for put
      PremiumAmount         the total option premium amount
      PremiumDate           the option premium payment date
      Notional              the vega notional amount, in terms of the volatility
      VarianceReference     the variance strike used to scale the vega notional to obtain the variance amount
      Strike                the volatility strike price
      Underlying            underlying index
      ValuationSchedule     the realised variance (volatility) accrual schedule
      SquaredPayoff         whether the payoff is squared (variance) or not (volatility)
      SettlementDate        the settlement date of the option payoff
      PayCcy                the settlement currency      

      Results:
      RealisedVariance      the variance accrued over the life of the variance/volatility option
      ExerciseProbability   option exercise probability
      Option                NPV of the variance option
  -->
  <Script>
    <Name>VarianceOption</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {Notional >= 0} AND {Strike >= 0};

        NUMBER expectedN, realisedVariance, currPrice, currentNotional;
        NUMBER prevPrice, payoff, realisedVariation, strike, premium, d;

        FOR d IN (2, SIZE(ValuationSchedule), 1) DO
          currPrice = Underlying(ValuationSchedule[d]);
          prevPrice = Underlying(ValuationSchedule[d-1]);
          realisedVariance = realisedVariance + pow(ln(currPrice/prevPrice), 2);
        END;

        expectedN = SIZE(ValuationSchedule) - 1;
        realisedVariance = (252/expectedN) * realisedVariance;

        IF SquaredPayoff == 1 THEN
          realisedVariation = realisedVariance;
          currentNotional = pow(100, 2) * Notional / (2 * 100 * VarianceReference);
          strike = pow(Strike, 2);
        ELSE
          realisedVariation = sqrt(realisedVariance);
          currentNotional = 100 * Notional;
          strike = Strike;
        END;

        payoff = currentNotional * max(PutCall * (realisedVariation - strike), 0);

        NUMBER ExerciseProbability;
        IF payoff > 0 THEN
          ExerciseProbability = 1;
        END;

        premium = PAY(PremiumAmount, PremiumDate, SettlementDate, PayCcy);
        payoff = PAY(payoff, ValuationSchedule[SIZE(ValuationSchedule)],
                     SettlementDate, PayCcy);
        Option = LongShort * (payoff - premium);
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="RealisedVariance">realisedVariance</Result>
        <Result>ExerciseProbability</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>

  <!-- KI/KO Variance Swap (with Cap and/or Floor) - EQ, FX or COMM

      Inputs:
      LongShort             whether own party pays on the variance (or volatility) strike,
                              1 for Long (fixed payer), -1 for Short
      Strike                the volatility strike price
      Notional              the vega notional amount, in terms of the volatility
      Underlying            underlying index
      ValuationSchedule     the realised variance (volatility) accrual schedule
      SquaredPayoff         whether the payoff is squared (variance) or not (volatility)
      BarrierType           1 for DownIn, 2 for UpIn, 3 for DownOut, 4 for UpOut
      BarrierLevel          the agreed barrier level
      Cap                   variance/volatility cap expressed as a factor of the volatility strike price,
                              0 for no cap
      Floor                 variance/volatility floor expressed as a factor of the volatility strike price,
                              0 for no floor
      SettlementDate        the settlement date of the swap payoff
      PayCcy                the settlement currency      

      Results:
      RealisedVariance      the variance accrued over the life of the variance/volatility swap
      TriggerProbability    Probability of the barrier being reached during the life of the trade
      Swap                  NPV of the variance swap
  -->
  <Script>
    <Name>KIKOVarianceSwap</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {Notional >= 0} AND {Strike >= 0};
        REQUIRE {Cap >= 0} AND {Floor >= 0};

        NUMBER d, expectedN, realisedVariance, currPrice, prevPrice, currentNotional;
        NUMBER payoff, realisedVariation, strike, daysBeforeKO, alive, knockedIn;
        NUMBER cap, floor;

        alive = 1;
        FOR d IN (2, SIZE(ValuationSchedule), 1) DO
          IF alive == 1 THEN
            currPrice = Underlying(ValuationSchedule[d]);
            prevPrice = Underlying(ValuationSchedule[d-1]);
            realisedVariance = realisedVariance + pow(ln(currPrice/prevPrice), 2);

            IF BarrierType == 3 OR BarrierType == 4 THEN
              daysBeforeKO = daysBeforeKO + 1;
            END;

            IF {BarrierType == 3 AND currPrice <= BarrierLevel} OR
               {BarrierType == 4 AND currPrice >= BarrierLevel} THEN
              alive = 0;
            END;

            IF knockedIn == 0 THEN
              IF {BarrierType == 1 AND currPrice <= BarrierLevel} OR
                 {BarrierType == 2 AND currPrice >= BarrierLevel} THEN
                knockedIn = 1;
              END;
            END;
          END;
        END;

        expectedN = SIZE(ValuationSchedule) - 1;
        realisedVariance = (252/expectedN) * realisedVariance;

        IF SquaredPayoff == 1 THEN
          realisedVariation = realisedVariance;
          currentNotional = pow(100, 2) * Notional / (2 * 100 * Strike);
          strike = pow(Strike, 2);
        ELSE
          realisedVariation = sqrt(realisedVariance);
          currentNotional = 100 * Notional;
          strike = Strike;
        END;

        IF Floor > 0 THEN
          IF SquaredPayoff == 1 THEN
            floor = pow(Floor, 2);
          ELSE
            floor = Floor;
          END;
          realisedVariation = max(floor * strike, realisedVariation);
        END;
        IF Cap > 0 THEN
          IF SquaredPayoff == 1 THEN
            cap = pow(Cap, 2);
          ELSE
            cap = Cap;
          END;
          realisedVariation = min(cap * strike, realisedVariation);
        END;

        NUMBER TriggerProbability;
        IF {{BarrierType == 1 OR BarrierType == 2} AND {knockedIn == 1}} OR
           {{BarrierType == 3 OR BarrierType == 4} AND {alive == 0}} THEN
          TriggerProbability = 1;
        END;

        IF BarrierType == 1 OR BarrierType == 2 THEN
          daysBeforeKO = expectedN;
        ELSE
          knockedIn = 1;
        END;

        payoff = LongShort * knockedIn * (daysBeforeKO / expectedN)
                 * currentNotional * (realisedVariation - strike);

        Swap = PAY(payoff, ValuationSchedule[SIZE(ValuationSchedule)],
                   SettlementDate, PayCcy);
      ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result rename="RealisedVariance">realisedVariance</Result>
        <Result>TriggerProbability</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Corridor Variance Swap (with Cap and/or Floor) - EQ, FX or COMM

      Inputs:
      LongShort               whether own party pays on the variance strike,
                                1 for Long (fixed payer), -1 for Short
      Strike                  the volatility strike price
      Notional                the vega notional amount, in terms of the volatility
      Underlying              underlying index
      ValuationSchedule       the realised variance accrual schedule
      SquaredPayoff           whether the payoff is squared (variance) or not (volatility)
      UpperBarrierLevel       the agreed value for the upper barrier
      LowerBarrierLevel       the agreed value for the lower barrier
      CountBothObservations   whether to count previous price only (False) or also the 
                                current price (True) when evaluating variance accrual for
                                each observation date.
      AccrualAdjustment       whether the strike will be scaled relative to the
                                number of days when the underlying remained in the corridor
      Cap                     variance/volatility cap expressed as a factor of the adj volatility strike price,
                                0 for no cap
      Floor                   variance/volatility floor expressed as a factor of the adj volatility strike price,
                                0 for no floor
      SettlementDate          the settlement date of the swap payoff
      PayCcy                  the settlement currency      

      Results:
      RealisedVariance        the variance accrued over the life of the variance
      Swap                    NPV of the variance swap
  -->
  <Script>
    <Name>CorridorVarianceSwap</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {Notional >= 0} AND {Strike >= 0};
        REQUIRE UpperBarrierLevel >= LowerBarrierLevel;

        NUMBER d, accruedDays, realisedVariance, currPrice, prevPrice, payoff;
        NUMBER currentNotional, daysBeforeKO, realisedVariation;
        NUMBER expectedN, cap, floor, adjustedStrike, numberOfHits;
        NUMBER varianceNotional, vegaNotional, maturityDiscountFactor, varStrike;
        NUMBER effectiveCap, effectiveFloor, adjVarStrike, volStrike, adjVolStrike;

        FOR d IN (2, SIZE(ValuationSchedule), 1) DO
          currPrice = Underlying(ValuationSchedule[d]);
          prevPrice = Underlying(ValuationSchedule[d-1]);

          IF {CountBothObservations == 1 AND 
              currPrice >= LowerBarrierLevel AND currPrice <= UpperBarrierLevel AND
              prevPrice >= LowerBarrierLevel AND prevPrice <= UpperBarrierLevel} OR
             {CountBothObservations == -1 AND
              prevPrice >= LowerBarrierLevel AND prevPrice <= UpperBarrierLevel} THEN
                realisedVariance = realisedVariance + pow(ln(currPrice/prevPrice), 2);
                numberOfHits = numberOfHits + 1;
          END;
        END;

        expectedN = SIZE(ValuationSchedule) - 1;
        realisedVariance = (252/expectedN) * realisedVariance;

        IF AccrualAdjustment == -1 THEN
          accruedDays = expectedN;
        ELSE
          accruedDays = numberOfHits;
        END;

        varianceNotional = Notional / (2 * 100 * Strike);
        varStrike = pow(Strike, 2);
        adjVarStrike = (accruedDays / expectedN) * varStrike;
        vegaNotional = Notional;
        volStrike = Strike;
        adjVolStrike = sqrt(accruedDays / expectedN) * volStrike;

        IF SquaredPayoff == 1 THEN
          realisedVariation = realisedVariance;
          currentNotional = pow(100, 2) * varianceNotional;
          adjustedStrike = adjVarStrike;
        ELSE
          realisedVariation = sqrt(realisedVariance);
          currentNotional = 100 * vegaNotional;
          adjustedStrike = adjVolStrike;
        END;

        IF Floor > 0 THEN
          IF SquaredPayoff == 1 THEN
            floor = pow(Floor, 2);
          ELSE
            floor = Floor;
          END;
          effectiveFloor = floor * adjustedStrike;
          realisedVariation = max(effectiveFloor, realisedVariation);
        END;
        IF Cap > 0 THEN
          IF SquaredPayoff == 1 THEN
            cap = pow(Cap, 2);
          ELSE
            cap = Cap;
          END;
          effectiveCap = cap * adjustedStrike;
          realisedVariation = min(effectiveCap, realisedVariation);
        END;

        payoff = LongShort * currentNotional * (realisedVariation - adjustedStrike);

        Swap = PAY(payoff, ValuationSchedule[SIZE(ValuationSchedule)],
                   SettlementDate, PayCcy);

        maturityDiscountFactor = PAY(1.0, TODAY,
                                     ValuationSchedule[SIZE(ValuationSchedule)], PayCcy);
      ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result>SquaredPayoff</Result>
        <Result>AccrualAdjustment</Result>
        <Result rename="NumberOfHits">numberOfHits</Result>
        <Result rename="NumberOfDays">expectedN</Result>
        <Result rename="RealisedVariance">realisedVariance</Result>
        <Result rename="VarianceNotional">varianceNotional</Result>
        <Result rename="VegaNotional">vegaNotional</Result>
        <Result rename="VarianceStrike">varStrike</Result>
        <Result rename="AdjustedVarianceStrike">adjVarStrike</Result>
        <Result rename="VolatilityStrike">volStrike</Result>
        <Result rename="AdjustedVolatilityStrike">adjVolStrike</Result>
        <Result rename="CapFactor">cap</Result>
        <Result rename="FloorFactor">floor</Result>
        <Result rename="EffectiveCap">effectiveCap</Result>
        <Result rename="EffectiveFloor">effectiveFloor</Result>
        <Result rename="MaturityDiscountFactor">maturityDiscountFactor</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- KIKO Corridor Variance Swap (with Cap and/or Floor) - EQ, FX or COMM

      Inputs:
      LongShort                   whether own party pays on the variance strike,
                                    1 for Long (fixed payer), -1 for Short
      Strike                      the volatility strike price
      Notional                    the vega notional amount, in terms of the volatility
      Underlying                  underlying index
      ValuationSchedule           the realised variance accrual schedule
      CorridorUpperBarrierLevel   the agreed value for the upper barrier of the corridor
      CorridorLowerBarrierLevel   the agreed value for the lower barrier of the corridor
      KIKOBarrierType             1 for DownIn, 2 for UpIn, 3 for DownOut, 4 for UpOut
      KIKOBarrierLevel            the agreed KI/KO barrier level
      CountBothObservations       whether to count previous price only (False) or also the 
                                    current price (True) when evaluating variance accrual for
                                    each observation date.
      AccrualAdjustment           whether the strike will be scaled relative to the
                                    number of days when the underlying remained in the corridor
      Cap                         variance/volatility cap expressed as a factor of the adj volatility strike price,
                                    0 for no cap
      Floor                       variance/volatility floor expressed as a factor of the adj volatility strike price,
                                    0 for no floor
      SettlementSchedule          derived from the ValuationSchedule, based on settlement lag
                                    after either a knock-out, or after the last valuation date
      PayCcy                      the settlement currency      

      Results:
      RealisedVariance            the variance accrued over the life of the variance
      Swap                        NPV of the variance swap
  -->
  <Script>
    <Name>KIKOCorridorVarianceSwap</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {Notional >= 0} AND {Strike >= 0} AND {KIKOBarrierLevel > 0};
        REQUIRE CorridorUpperBarrierLevel >= CorridorLowerBarrierLevel;

        NUMBER n;
        n = SIZE(ValuationSchedule);

        NUMBER d, accruedDays, realisedVariance, currPrice, prevPrice;
        NUMBER calculated, currentNotional, payoff, strike, alive;
        NUMBER knockedIn, expectedN, cap, floor, adjustedStrike;
        NUMBER TriggerProbability;

        alive = 1;
        FOR d IN (2, n, 1) DO
          currPrice = Underlying(ValuationSchedule[d]);
          prevPrice = Underlying(ValuationSchedule[d-1]);

          IF alive == 1 THEN
            IF {CountBothObservations == 1 AND 
                currPrice >= CorridorLowerBarrierLevel AND currPrice <= CorridorUpperBarrierLevel AND
                prevPrice >= CorridorLowerBarrierLevel AND prevPrice <= CorridorUpperBarrierLevel} OR
               {CountBothObservations == -1 AND
                  prevPrice >= CorridorLowerBarrierLevel AND prevPrice <= CorridorUpperBarrierLevel} THEN
                  realisedVariance = realisedVariance + pow(ln(currPrice/prevPrice), 2);
                  accruedDays = accruedDays + 1;
            END;

            IF {KIKOBarrierType == 3 AND currPrice <= KIKOBarrierLevel} OR
               {KIKOBarrierType == 4 AND currPrice >= KIKOBarrierLevel} THEN
              alive = 0;
            END;
          END;

          IF knockedIn == 0 THEN
            IF {KIKOBarrierType == 1 AND currPrice <= KIKOBarrierLevel} OR
               {KIKOBarrierType == 2 AND currPrice >= KIKOBarrierLevel} THEN
              knockedIn = 1;
            END;
          END;

          IF {alive == 0 OR d == n} AND {calculated == 0} THEN
            calculated = 1;
            expectedN = n - 1;
            realisedVariance = (252/expectedN) * realisedVariance;

            IF AccrualAdjustment == -1 THEN
              accruedDays = expectedN;
            END;

            currentNotional = pow(100, 2) * Notional / (2 * 100 * Strike);
            strike = pow(Strike, 2);
            adjustedStrike = (accruedDays / expectedN) * strike;

            IF Floor > 0 THEN
              floor = pow(Floor, 2);
              realisedVariance = max(floor * adjustedStrike, realisedVariance);
            END;
            IF Cap > 0 THEN
              cap = pow(Cap, 2);
              realisedVariance = min(cap * adjustedStrike, realisedVariance);
            END;

            IF {{KIKOBarrierType == 1 OR KIKOBarrierType == 2} AND {knockedIn == 1}} OR
               {{KIKOBarrierType == 3 OR KIKOBarrierType == 4} AND {alive == 0}} THEN
              TriggerProbability = 1;
            END;

            IF KIKOBarrierType == 3 OR KIKOBarrierType == 4 THEN
              knockedIn = 1;
            END;

            payoff = LongShort * knockedIn * currentNotional
                      * (realisedVariance - adjustedStrike);

            Swap = PAY(payoff, ValuationSchedule[d], SettlementSchedule[d], PayCcy);
          END;
        END;
      ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result rename="RealisedVariance">realisedVariance</Result>
        <Result>TriggerProbability</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Conditional Variance Swap 01 (with Cap and/or Floor) - EQ, FX or COMM

      Inputs:
      LongShort               whether own party pays on the variance (or volatility) strike,
                                1 for Long (fixed payer), -1 for Short
      Strike                  the volatility strike level
      Notional                the vega notional amount, in terms of the volatility
      Underlying              underlying index
      ValuationSchedule       the realised variance (volatility) accrual schedule
      SquaredPayoff           whether the payoff is squared (variance) or not (volatility)
      BarrierType             1 for DownIn, 2 for UpIn, 3 for DownOut, 4 for UpOut
      BarrierLevel            the agreed barrier level
      Cap                     variance/volatility cap expressed as a factor of the adj volatility strike price,
                                0 for no cap
      Floor                   variance/volatility floor expressed as a factor of the adj volatility strike price,
                                0 for no floor
      CountBothObservations   whether to count previous price only (False) or also the 
                              current price (True) when evaluating variance accrual for
                              each observation date.
      AccrualAdjustment       whether the strike will be scaled relative to the
                                number of days when the underlying remained in the accrual region
      SettlementDate          the settlement date of the swap payoff
      PayCcy                  the settlement currency      

      Results:
      RealisedVariance        the variance accrued over the life of the variance/volatility swap
      Swap                    NPV of the variance swap
  -->
  <Script>
    <Name>ConditionalVarianceSwap01</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {Notional >= 0} AND {Strike > 0};

        NUMBER d, accruedDays, realisedVariance, currPrice, prevPrice, payoff;
        NUMBER currentNotional, daysBeforeKO, alive, realisedVariation;
        NUMBER knockedIn, expectedN, cap, floor, adjustedStrike, numberOfHits;
        NUMBER varianceNotional, vegaNotional, maturityDiscountFactor, varStrike;
        NUMBER effectiveCap, effectiveFloor, adjVarStrike, volStrike, adjVolStrike;

        FOR d IN (2, SIZE(ValuationSchedule), 1) DO
          currPrice = Underlying(ValuationSchedule[d]);
          prevPrice = Underlying(ValuationSchedule[d-1]);

          IF {CountBothObservations == 1 AND {
              {{BarrierType == 1 OR BarrierType == 4} AND
                currPrice <= BarrierLevel AND prevPrice <= BarrierLevel} OR
              {{BarrierType == 2 OR BarrierType == 3} AND
                currPrice >= BarrierLevel AND prevPrice >= BarrierLevel}}}
          OR {CountBothObservations == -1 AND {
              {{BarrierType == 1 OR BarrierType == 4} AND
                prevPrice <= BarrierLevel} OR 
              {{BarrierType == 2 OR BarrierType == 3} AND
                prevPrice >= BarrierLevel} }}
          THEN
            realisedVariance = realisedVariance + pow(ln(currPrice/prevPrice), 2);
            numberOfHits = numberOfHits + 1;
          END;
        END;

        expectedN = SIZE(ValuationSchedule) - 1;
        realisedVariance = (252/expectedN) * realisedVariance;

        IF AccrualAdjustment == -1 THEN
          accruedDays = expectedN;
        ELSE
          accruedDays = numberOfHits;
        END;

        varianceNotional = Notional / (2 * 100 * Strike);
        varStrike = pow(Strike, 2);
        adjVarStrike = (accruedDays / expectedN) * varStrike;
        vegaNotional = Notional;
        volStrike = Strike;
        adjVolStrike = sqrt(accruedDays / expectedN) * volStrike;

        IF SquaredPayoff == 1 THEN
          realisedVariation = realisedVariance;
          currentNotional = pow(100, 2) * varianceNotional;
          adjustedStrike = adjVarStrike;
        ELSE
          realisedVariation = sqrt(realisedVariance);
          currentNotional = 100 * vegaNotional;
          adjustedStrike = adjVolStrike;
        END;

        IF Floor > 0 THEN
          IF SquaredPayoff == 1 THEN
            floor = pow(Floor, 2);
          ELSE
            floor = Floor;
          END;
          effectiveFloor = floor * adjustedStrike;
          realisedVariation = max(effectiveFloor, realisedVariation);
        END;
        IF Cap > 0 THEN
          IF SquaredPayoff == 1 THEN
            cap = pow(Cap, 2);
          ELSE
            cap = Cap;
          END;
          effectiveCap = cap * adjustedStrike;
          realisedVariation = min(effectiveCap, realisedVariation);
        END;

        payoff = LongShort * currentNotional * (realisedVariation - adjustedStrike);

        Swap = PAY(payoff, ValuationSchedule[SIZE(ValuationSchedule)],
                   SettlementDate, PayCcy);

        maturityDiscountFactor = PAY(1.0, TODAY,
                                     ValuationSchedule[SIZE(ValuationSchedule)], PayCcy);
      ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result>SquaredPayoff</Result>
        <Result>AccrualAdjustment</Result>
        <Result rename="NumberOfHits">numberOfHits</Result>
        <Result rename="NumberOfDays">expectedN</Result>
        <Result rename="RealisedVariance">realisedVariance</Result>
        <Result rename="VarianceNotional">varianceNotional</Result>
        <Result rename="VegaNotional">vegaNotional</Result>
        <Result rename="VarianceStrike">varStrike</Result>
        <Result rename="AdjustedVarianceStrike">adjVarStrike</Result>
        <Result rename="VolatilityStrike">volStrike</Result>
        <Result rename="AdjustedVolatilityStrike">adjVolStrike</Result>
        <Result rename="CapFactor">cap</Result>
        <Result rename="FloorFactor">floor</Result>
        <Result rename="EffectiveCap">effectiveCap</Result>
        <Result rename="EffectiveFloor">effectiveFloor</Result>
        <Result rename="MaturityDiscountFactor">maturityDiscountFactor</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Conditional Variance Swap 02 (with Cap and/or Floor) - EQ, FX or COMM

      Inputs:
      LongShort               whether own party pays on the variance (or volatility) strike,
                                1 for Long (fixed payer), -1 for Short
      Strike                  the volatility strike level
      Notional                the vega notional amount, in terms of the volatility
      VarianceReference       the variance strike used to scale the vega notional to obtain the variance amount
      Underlying              underlying index
      ValuationSchedule       the realised variance (volatility) accrual schedule
      SquaredPayoff           whether the payoff is squared (variance) or not (volatility)
      BarrierType             1 for DownIn, 2 for UpIn, 3 for DownOut, 4 for UpOut
      BarrierLevel            the agreed barrier level
      Cap                     variance/volatility cap expressed as a factor of the adj volatility strike price,
                                0 for no cap
      Floor                   variance/volatility floor expressed as a factor of the adj volatility strike price,
                                0 for no floor
      CountBothObservations   whether to count previous price only (False) or also the 
                              current price (True) when evaluating variance accrual for
                              each observation date.
      AccrualAdjustment       whether the strike will be scaled relative to the
                                number of days when the underlying remained in the accrual region
      SettlementDate          the settlement date of the swap payoff
      PayCcy                  the settlement currency      

      Results:
      RealisedVariance        the variance accrued over the life of the variance/volatility swap
      Swap                    NPV of the variance swap
  -->
  <Script>
    <Name>ConditionalVarianceSwap02</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {Notional >= 0} AND {Strike > 0} AND {VarianceReference > 0};

        NUMBER d, accruedDays, realisedVariance, currPrice, prevPrice, payoff;
        NUMBER currentNotional, daysBeforeKO, alive, realisedVariation;
        NUMBER knockedIn, expectedN, cap, floor, adjustedStrike, numberOfHits;
        NUMBER varianceNotional, vegaNotional, maturityDiscountFactor, varStrike;
        NUMBER effectiveCap, effectiveFloor, adjVarStrike, volStrike, adjVolStrike;

        FOR d IN (2, SIZE(ValuationSchedule), 1) DO
          currPrice = Underlying(ValuationSchedule[d]);
          prevPrice = Underlying(ValuationSchedule[d-1]);

          IF {CountBothObservations == 1 AND {
              {{BarrierType == 1 OR BarrierType == 4} AND
                currPrice <= BarrierLevel AND prevPrice <= BarrierLevel} OR
              {{BarrierType == 2 OR BarrierType == 3} AND
                currPrice >= BarrierLevel AND prevPrice >= BarrierLevel}}}
          OR {CountBothObservations == -1 AND {
              {{BarrierType == 1 OR BarrierType == 4} AND
                prevPrice <= BarrierLevel} OR 
              {{BarrierType == 2 OR BarrierType == 3} AND
                prevPrice >= BarrierLevel} }}
          THEN
            realisedVariance = realisedVariance + pow(ln(currPrice/prevPrice), 2);
            numberOfHits = numberOfHits + 1;
          END;
        END;

        expectedN = SIZE(ValuationSchedule) - 1;
        realisedVariance = (252/expectedN) * realisedVariance;

        IF AccrualAdjustment == -1 THEN
          accruedDays = expectedN;
        ELSE
          accruedDays = numberOfHits;
        END;

        varianceNotional = Notional / (2 * 100 * VarianceReference);
        varStrike = pow(Strike, 2);
        adjVarStrike = (accruedDays / expectedN) * varStrike;
        vegaNotional = Notional;
        volStrike = Strike;
        adjVolStrike = sqrt(accruedDays / expectedN) * volStrike;

        IF SquaredPayoff == 1 THEN
          realisedVariation = realisedVariance;
          currentNotional = pow(100, 2) * varianceNotional;
          adjustedStrike = adjVarStrike;
        ELSE
          realisedVariation = sqrt(realisedVariance);
          currentNotional = 100 * vegaNotional;
          adjustedStrike = adjVolStrike;
        END;

        IF Floor > 0 THEN
          IF SquaredPayoff == 1 THEN
            floor = pow(Floor, 2);
          ELSE
            floor = Floor;
          END;
          effectiveFloor = floor * adjustedStrike;
          realisedVariation = max(effectiveFloor, realisedVariation);
        END;
        IF Cap > 0 THEN
          IF SquaredPayoff == 1 THEN
            cap = pow(Cap, 2);
          ELSE
            cap = Cap;
          END;
          effectiveCap = cap * adjustedStrike;
          realisedVariation = min(effectiveCap, realisedVariation);
        END;

        payoff = LongShort * currentNotional * (realisedVariation - adjustedStrike);

        Swap = PAY(payoff, ValuationSchedule[SIZE(ValuationSchedule)],
                   SettlementDate, PayCcy);

        maturityDiscountFactor = PAY(1.0, TODAY,
                                     ValuationSchedule[SIZE(ValuationSchedule)], PayCcy);
      ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result>SquaredPayoff</Result>
        <Result>AccrualAdjustment</Result>
        <Result rename="NumberOfHits">numberOfHits</Result>
        <Result rename="NumberOfDays">expectedN</Result>
        <Result rename="RealisedVariance">realisedVariance</Result>
        <Result rename="VarianceNotional">varianceNotional</Result>
        <Result rename="VegaNotional">vegaNotional</Result>
        <Result rename="VarianceStrike">varStrike</Result>
        <Result rename="AdjustedVarianceStrike">adjVarStrike</Result>
        <Result rename="VolatilityStrike">volStrike</Result>
        <Result rename="AdjustedVolatilityStrike">adjVolStrike</Result>
        <Result rename="CapFactor">cap</Result>
        <Result rename="FloorFactor">floor</Result>
        <Result rename="EffectiveCap">effectiveCap</Result>
        <Result rename="EffectiveFloor">effectiveFloor</Result>
        <Result rename="MaturityDiscountFactor">maturityDiscountFactor</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Pairwise Variance Swap (with Cap and/or Floor) - EQ, FX or COMM

      Inputs:
      LongShort                whether own party pays on the variance (or volatility) strike,
                                  1 for Long (fixed payer), -1 for Short
      UnderlyingStrikes        the volatility strike level for the underlyings
      UnderlyingNotionals      the vega notional amount for the underlyings, in terms of the volatility
      Underlyings              the index underlyings
      BasketNotional           the basket vega notional amount
      BasketStrike             the basket strike
      ValuationSchedule        the variance accrual schedule start dates
      LaggedValuationSchedule  the variance accrual schedule end dates
      AccrualLag               number of days' lag applied to the ValuationSchedule to derive
                                  the LaggedValuationSchedule
      PayoffLimit              the factor determining the maximum and minimum payoff possible under the trade
      Cap                      variance/volatility cap expressed as a factor of the volatility strike price,
                                  0 for no cap
      Floor                    variance/volatility floor expressed as a factor of the volatility strike price,
                                  0 for no floor
      SettlementDate           the settlement date of the swap payoff
      PayCcy                   the settlement currency
  
      Results:  
      RealisedVariance         the variance accrued over the life of the variance/volatility swap
      Swap                     NPV of the variance swap
  -->
  <Script>
    <Name>PairwiseVarianceSwap</Name>
    <ProductTag>MutiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {SIZE(Underlyings) == 2} AND {SIZE(UnderlyingStrikes) == 2};
        REQUIRE {SIZE(UnderlyingNotionals) == 2} AND {UnderlyingStrikes[1] >= 0};
        REQUIRE {UnderlyingStrikes[2] >= 0} AND {BasketStrike >= 0};
        REQUIRE {UnderlyingNotionals[1] >= 0} AND {UnderlyingNotionals[2] >= 0};
        REQUIRE {BasketNotional >= 0} AND {PayoffLimit > 0};
        REQUIRE {SIZE(ValuationSchedule) == SIZE(LaggedValuationSchedule)};

        NUMBER d, performance1, performance2, basketPerformance, realisedVariance1;
        NUMBER realisedVariance2, realisedVarianceBasket, currentNotional1, expectedN;
        NUMBER cap, currentNotional2, currentNotionalBasket, strike1, strike2, strikeBasket;
        NUMBER floor, equityAmount1, minPairEquityAmount, maxPairEquityAmount;
        NUMBER equityAmount2, equityAmountBasket, pairEquityAmount;

        FOR d IN (1, SIZE(ValuationSchedule)-1, 1) DO
          performance1 = ln(Underlyings[1](LaggedValuationSchedule[d]) /
                            Underlyings[1](ValuationSchedule[d]));
          performance2 = ln(Underlyings[2](LaggedValuationSchedule[d]) /
                            Underlyings[2](ValuationSchedule[d]));
          basketPerformance = (performance1 + performance2) / 2;
          
          realisedVariance1 = realisedVariance1 + pow(performance1, 2);
          realisedVariance2 = realisedVariance2 + pow(performance2, 2);
          realisedVarianceBasket = realisedVarianceBasket + pow(basketPerformance, 2);
        END;

        expectedN = SIZE(ValuationSchedule) - 1;
        realisedVariance1 = 252 / (expectedN * AccrualLag) * realisedVariance1;
        realisedVariance2 = 252 / (expectedN * AccrualLag) * realisedVariance2;
        realisedVarianceBasket = 252 / (expectedN * AccrualLag) * realisedVarianceBasket;

        currentNotional1 = pow(100, 2) * UnderlyingNotionals[1] /
                                          (2 * 100 * UnderlyingStrikes[1]);
        currentNotional2 = pow(100, 2) * UnderlyingNotionals[2] /
                                          (2 * 100 * UnderlyingStrikes[2]);
        currentNotionalBasket = pow(100, 2) * BasketNotional / (2 * 100 * BasketStrike);
        strike1 = pow(UnderlyingStrikes[1], 2);
        strike2 = pow(UnderlyingStrikes[2], 2);
        strikeBasket = pow(BasketStrike, 2);

        IF Floor > 0 THEN
          floor = pow(Floor, 2);
          realisedVariance1 = max(floor * strike1, realisedVariance1);
          realisedVariance2 = max(floor * strike2, realisedVariance2);
          realisedVarianceBasket = max(floor * strikeBasket, realisedVarianceBasket);
        END;
        IF Cap > 0 THEN
          cap = pow(Cap, 2);
          realisedVariance1 = min(cap * strike1, realisedVariance1);
          realisedVariance2 = min(cap * strike2, realisedVariance2);
          realisedVarianceBasket = min(cap * strikeBasket, realisedVarianceBasket);
        END;

        equityAmount1 = currentNotional1 * (realisedVariance1 - strike1);
        equityAmount2 = currentNotional2 * (realisedVariance2 - strike2);
        equityAmountBasket = currentNotionalBasket * (realisedVarianceBasket - strikeBasket);
        pairEquityAmount = equityAmount1 + equityAmount2 + equityAmountBasket;

        maxPairEquityAmount = PayoffLimit * (abs(UnderlyingNotionals[1]) +
                                             abs(UnderlyingNotionals[2]));
        minPairEquityAmount = -maxPairEquityAmount;

        pairEquityAmount = max(minPairEquityAmount, pairEquityAmount);
        pairEquityAmount = min(maxPairEquityAmount, pairEquityAmount);

        Swap = PAY(LongShort * pairEquityAmount, ValuationSchedule[SIZE(ValuationSchedule)],
                   SettlementDate, PayCcy);
      ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result rename="finalVariance_1">realisedVariance1</Result>
        <Result rename="finalVariance_2">realisedVariance2</Result>
        <Result rename="finalBasketVariance">realisedVarianceBasket</Result>
        <Result rename="varianceAmount1">currentNotional1</Result>
        <Result rename="varianceAmount2">currentNotional2</Result>
        <Result rename="basketVarianceAmount">currentNotionalBasket</Result>
        <Result rename="currentNotional">currentNotionalBasket</Result>
        <Result rename="equityAmount1">equityAmount1</Result>
        <Result rename="equityAmount2">equityAmount2</Result>
        <Result rename="equityAmountBasket">equityAmountBasket</Result>
        <Result rename="finalEquityAmount">pairEquityAmount</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Basket Variance Swap (with Cap and/or Floor) - EQ, FX or COMM

      Inputs:
      LongShort             whether own party pays on the variance (or volatility) strike,
                              1 for Long (fixed payer), -1 for Short
      Strike                the volatility strike price
      Notional              the vega notional amount, in terms of the volatility
      Underlyings           vector of underlying indices
      Weights               vector of underlying weights
      ValuationSchedule     the realised variance (volatility) accrual schedule
      SquaredPayoff         whether the payoff is squared (variance) or not (volatility)
      Cap                   variance/volatility cap expressed as a factor of the volatility strike price,
                              0 for no cap
      Floor                 variance/volatility floor expressed as a factor of the volatility strike price,
                              0 for no floor
      SettlementDate        the settlement date of the swap payoff
      PayCcy                the settlement currency

      Results:
      RealisedVariance      the variance accrued over the life of the variance/volatility swap
      Swap                  NPV of the variance swap
  -->
  <Script>
    <Name>BasketVarianceSwap</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {Notional >= 0} AND {Strike >= 0};
        REQUIRE {Cap >= 0} AND {Floor >= 0};

        NUMBER i, n, d, sumOfWeights;
        n = SIZE(Underlyings);

        FOR i IN (1, n, 1) DO
          sumOfWeights = sumOfWeights + Weights[i];
        END;
        REQUIRE sumOfWeights == 1;

        NUMBER expectedN, currPrice[n], prevPrice[n], maturityDiscountFactor;
        NUMBER volStrike, realisedVariance, basketVariation, realisedVariation;
        NUMBER varianceNotional, strike, cap, floor, currentNotional, payoff;
        NUMBER effectiveCap, effectiveFloor, varStrike, vegaNotional;

        FOR d IN (2, SIZE(ValuationSchedule), 1) DO
          basketVariation = 0;
          FOR i IN (1, n, 1) DO
            currPrice[i] = Underlyings[i](ValuationSchedule[d]);
            prevPrice[i] = Underlyings[i](ValuationSchedule[d-1]);
            basketVariation = basketVariation + Weights[i] * ln(currPrice[i]/prevPrice[i]);
          END;
          realisedVariance = realisedVariance + pow(basketVariation, 2);
        END;

        expectedN = SIZE(ValuationSchedule) - 1;
        realisedVariance = (252/expectedN) * realisedVariance;

        varianceNotional = Notional / (2 * 100 * Strike);
        varStrike = pow(Strike, 2);
        vegaNotional = Notional;
        volStrike = Strike;

        IF SquaredPayoff == 1 THEN
          realisedVariation = realisedVariance;
          currentNotional = pow(100, 2) * varianceNotional;
          strike = varStrike;
        ELSE
          realisedVariation = sqrt(realisedVariance);
          currentNotional = 100 * vegaNotional;
          strike = volStrike;
        END;

        IF Floor > 0 THEN
          IF SquaredPayoff == 1 THEN
            floor = pow(Floor, 2);
          ELSE
            floor = Floor;
          END;
          effectiveFloor = floor * strike;
          realisedVariation = max(effectiveFloor, realisedVariation);
        END;
        IF Cap > 0 THEN
          IF SquaredPayoff == 1 THEN
            cap = pow(Cap, 2);
          ELSE
            cap = Cap;
          END;
          effectiveCap = cap * strike;
          realisedVariation = min(effectiveCap, realisedVariation);
        END;

        payoff = LongShort * currentNotional * (realisedVariation - strike);

        Swap = PAY(payoff, ValuationSchedule[SIZE(ValuationSchedule)],
                   SettlementDate, PayCcy);

        maturityDiscountFactor = PAY(1.0, TODAY,
                                     ValuationSchedule[SIZE(ValuationSchedule)], PayCcy);
      ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result>SquaredPayoff</Result>
        <Result rename="NumberOfDays">expectedN</Result>
        <Result rename="RealisedVariance">realisedVariance</Result>
        <Result rename="VarianceNotional">varianceNotional</Result>
        <Result rename="VegaNotional">vegaNotional</Result>
        <Result rename="VarianceStrike">varStrike</Result>
        <Result rename="VolatilityStrike">volStrike</Result>
        <Result rename="CapFactor">cap</Result>
        <Result rename="FloorFactor">floor</Result>
        <Result rename="EffectiveCap">effectiveCap</Result>
        <Result rename="EffectiveFloor">effectiveFloor</Result>
        <Result rename="MaturityDiscountFactor">maturityDiscountFactor</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!--   Best Entry Option - EQ, FX, COMM

      Inputs:
        LongShort                whether own party pays on the variance (or volatility) strike,
                                    1 for Long (fixed payer), -1 for Short
        Strike                   the strike of the option, input as a percentage (of the value of Index Initial)
        ResetMinimum             the minimum value of Index Initial in the case of a Trigger Event, input as a percentage
                                  (of the value of Strike Index Level)
        TriggerLevel             the value of the index below which a Trigger Event occurs on any of the Strike Observation Dates
        Underlying               the underlying index
        Notional                 the notional amount of the option
        Multiplier               a multiplier applied to the payoff in the event that the value of the underlying is greater than 
                                  the value of percentage of the Initial Index as specified in the Strike on the Expiry Date
        ExpiryDate               the expiry date of the option
        StrikeDate               the date on which the Strike Index Level is observed
        Premium                  the premium paid for the option
        PremiumDate              the date on which the option premium is paid
        Cap                      the maximum value of the percentage increase in the underlying that is used to determine the 
                                  payoff in the event that the Multiplier is used
        SettlementDate           the settlement date of the option payoff
        PayCcy                   the settlement currency
        StrikeObservationDates   the dates on which the underlying index value is observed, used in the computation of Initial Index
    
        Results:  
        Option                   the NPV of the option
        initialIndex             the value of InitialIndex (see product catalogue)
        strikeIndexLevel         the value of the underlying index on the Strike Date
        strikeIndexObs           the lowest value of the underlying index taken over all Strike Observation Dates
        Cap                      the value of the Cap
        payoff                   the payoff of the option
        triggerEvent             the proportion of Trigger Events to total simulations
        resetMinimumValue        the Reset Minimum Value
  -->
  <Script>
    <Name>BestEntryOption</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        NUMBER payoff, initialIndex, triggerEvent, strikeIndexObs, strikeIndexLevel, d, resetMinValue;

        triggerEvent = 0;

        strikeIndexLevel = Underlying(StrikeDate);

        resetMinValue = strikeIndexLevel * ResetMinimum;

        strikeIndexObs = Underlying(StrikeObservationDates[1]);

        FOR d IN (1, SIZE(StrikeObservationDates), 1) DO
          IF Underlying(StrikeObservationDates[d]) < TriggerLevel * strikeIndexLevel THEN
            triggerEvent = 1;
            strikeIndexObs = min(strikeIndexObs, Underlying(StrikeObservationDates[d]));
          END;
        END;

        IF triggerEvent == 1 THEN
          initialIndex = max(ResetMinimum * strikeIndexLevel, strikeIndexObs);
        ELSE
          initialIndex = strikeIndexLevel;
        END;

        IF Underlying(ExpiryDate) > Strike * initialIndex THEN
          payoff = LongShort * Notional * Multiplier * min(Cap, max(0, (Underlying(ExpiryDate) - initialIndex)/initialIndex));
        ELSE
          payoff = -1* LongShort * Notional * (Strike * initialIndex - Underlying(ExpiryDate))/initialIndex;
        END;

        Option = PAY(payoff, ExpiryDate, SettlementDate, Currency) - PAY(Premium, PremiumDate, PremiumDate, Currency);

      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="initialIndex">initialIndex</Result>
        <Result rename="strikeIndexLevel">strikeIndexLevel</Result>
        <Result rename="lowestStrikeObs">strikeIndexObs</Result>
        <Result rename="Cap">Cap</Result>
        <Result rename="payoff">payoff</Result>
        <Result rename="triggerEvent">triggerEvent</Result>
        <Result rename="resetMinimumValue">ResetMinimum</Result>
      </Results>
    </Script>
  </Script>
  <!-- Gamma Swap - EQ

      Inputs:
      LongShort             whether own party pays on the variance (or volatility) strike,
                              1 for Long (fixed payer), -1 for Short
      Strike                the volatility strike price
      Notional              the vega notional amount, in terms of the volatility
      Underlying            underlying index
      ValuationSchedule     the realised variance (volatility) accrual schedule
      SettlementDate        the settlement date of the swap payoff
      PayCcy                the settlement currency      

      Results:
      RealisedVariance      the variance accrued over the life of the variance
      Swap                  NPV of the variance swap
  -->
  <Script>
    <Name>GammaSwap</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {Notional >= 0} AND {Strike >= 0};

        NUMBER d, expectedN, realisedVariance, currPrice, prevPrice, presPrice, currentNotional;
        NUMBER payoff, realisedVariation, strike;

        presPrice = Underlying(ValuationSchedule[1]);

        FOR d IN (2, SIZE(ValuationSchedule), 1) DO
          currPrice = Underlying(ValuationSchedule[d]);
          prevPrice = Underlying(ValuationSchedule[d-1]);
          realisedVariance = realisedVariance + (currPrice/presPrice) * pow(ln(currPrice/prevPrice), 2);
        END;

        expectedN = SIZE(ValuationSchedule) - 1;
        realisedVariance = (252/expectedN) * realisedVariance;

        realisedVariation = realisedVariance;
        currentNotional = pow(100, 2) * Notional / (2 * 100 * Strike);
        strike = pow(Strike, 2);

        payoff = LongShort * currentNotional * (realisedVariation - 
                    strike);

        Swap = PAY(payoff, ValuationSchedule[SIZE(ValuationSchedule)],
                   SettlementDate, PayCcy);
      ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result rename="RealisedVariance">realisedVariance</Result>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Variance Dispersion Swap (with Cap and/or Floor) - EQ, FX or COMM

      Inputs:
      LongShort             whether own party pays on the variance strike,
                            1 for Long (fixed payer), -1 for Short
      Underlyings1          underlying index (first term in the spread/difference)
      Weights1              vector of underlying weights for Underlyings1
      Strikes1              the volatility strike price for Underlyings1
      Spreads1              the spreads on the realised variance for Underlyings1
      Notionals1            the vega notional amount for Underlyings1, in terms of the volatility
      Caps1                 variance/volatility cap for Underlyings1 expressed as a factor of the adj volatility strike price,
                            0 for no cap
      Floors1               variance/volatility floor for Underlyings1 expressed as a factor of the adj volatility strike price,
                            0 for no floor
      Underlyings2          underlying index (second term in the spread/difference)
      Weights2              vector of underlying weights for Underlyings2
      Strikes2              the volatility strike price for Underlyings2
      Spreads2              the spreads on the realised variance for Underlyings2
      Notionals2            the vega notional amount for Underlyings2, in terms of the volatility
      Caps2                 variance/volatility cap for Underlyings2 expressed as a factor of the adj volatility strike price,
                            0 for no cap
      Floors2               variance/volatility floor for Underlyings2 expressed as a factor of the adj volatility strike price,
                            0 for no floor
      DividendAdjustment    whether or not to use ex-dividend share price for underlying price
      ValuationSchedule     the realised variance accrual schedule
      SettlementDate        the settlement date of the swap payoff
      PayCcy                the settlement currency      

      Results:
      RealisedVariance      the variance accrued over the life of the variance
      Swap                  NPV of the variance swap
  -->
  <Script>
    <Name>VarianceDispersionSwap</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {SIZE(Strikes1) == SIZE(Spreads1)} AND {SIZE(Spreads1) == SIZE(Notionals1)};
        REQUIRE {SIZE(Notionals1) == SIZE(Underlyings1)} AND {SIZE(Underlyings1) == SIZE(Caps1)};
        REQUIRE {SIZE(Caps1) == SIZE(Floors1)} AND {SIZE(Strikes2) == SIZE(Spreads2)};
        REQUIRE {SIZE(Spreads2) == SIZE(Notionals2)} AND {SIZE(Notionals2) == SIZE(Underlyings2)};
        REQUIRE {SIZE(Underlyings2) == SIZE(Caps2)} AND {SIZE(Caps2) == SIZE(Floors2)};

        NUMBER u, n1, n2;
        n1 = SIZE(Underlyings1);
        n2 = SIZE(Underlyings2);
        FOR u IN (1, n1, 1) DO
          REQUIRE {Strikes1[u] >= 0} AND {Caps1[u] >= 0} AND {Floors1[u] >= 0};
        END;
        FOR u IN (1, n2, 1) DO
          REQUIRE {Strikes2[u] >= 0} AND {Caps2[u] >= 0} AND {Floors2[u] >= 0};
        END;

        NUMBER D, d, expectedN, realisedVariance, currPrice, prevPrice;
        NUMBER currentNotional, floor, strike, payoff, payoff1, payoff2, cap;

        D = SIZE(ValuationSchedule);
        expectedN = D - 1;

        FOR u IN (1, n1, 1) DO
          FOR d IN (2, D, 1) DO
            currPrice = Underlyings1[u](ValuationSchedule[d]);
            prevPrice = Underlyings1[u](ValuationSchedule[d-1]);
            realisedVariance = realisedVariance + pow(ln(currPrice/prevPrice), 2);
          END;

          realisedVariance = (252/expectedN) * realisedVariance + Spreads1[u];
          currentNotional = pow(100, 2) * Notionals1[u] / (2 * 100 * Strikes1[u]);
          strike = pow(Strikes1[u], 2);

          IF Floors1[u] > 0 THEN
            floor = pow(Floors1[u], 2);
            realisedVariance = max(floor * strike, realisedVariance);
          END;
          IF Caps1[u] > 0 THEN
            cap = pow(Caps1[u], 2);
            realisedVariance = min(cap * strike, realisedVariance);
          END;
          payoff1 = payoff1 + currentNotional * (realisedVariance - strike) * Weights1[u];
        END;

        FOR u IN (1, n2, 1) DO
          FOR d IN (2, D, 1) DO
            currPrice = Underlyings2[u](ValuationSchedule[d]);
            prevPrice = Underlyings2[u](ValuationSchedule[d-1]);
            realisedVariance = realisedVariance + pow(ln(currPrice/prevPrice), 2);
          END;

          realisedVariance = (252/expectedN) * realisedVariance + Spreads2[u];
          currentNotional = pow(100, 2) * Notionals2[u] / (2 * 100 * Strikes2[u]);
          strike = pow(Strikes2[u], 2);

          IF Floors2[u] > 0 THEN
            floor = pow(Floors2[u], 2);
            realisedVariance = max(floor * strike, realisedVariance);
          END;
          IF Caps2[u] > 0 THEN
            cap = pow(Caps2[u], 2);
            realisedVariance = min(cap * strike, realisedVariance);
          END;
          payoff2 = payoff2 + currentNotional * (realisedVariance - strike) * Weights2[u];
        END;

        payoff = LongShort * (payoff1 - payoff2);

        Swap = PAY(payoff, ValuationSchedule[D], SettlementDate, PayCcy);

        NUMBER currentNotional1, currentNotional2;

        FOR u IN (1, n1, 1) DO
          currentNotional1 = currentNotional1 + (pow(100, 2) * Notionals1[u] / (2 * 100 * Strikes1[u]));
        END;
        FOR u IN (1, n2, 1) DO
          currentNotional2 = currentNotional2 + (pow(100, 2) * Notionals2[u] / (2 * 100 * Strikes2[u]));
        END;
      ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result rename="currentNotional">currentNotional1</Result>
        <Result>currentNotional2</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Corridor Variance Dispersion Swap (with Cap and/or Floor) - EQ, FX or COMM

      Inputs:
      LongShort               whether own party pays on the variance strike,
                              1 for Long (fixed payer), -1 for Short
      Weights                 vector of underlying weights
      Underlyings1            underlying index (first term in the spread/difference)
      Strikes1                the volatility strike price for Underlyings1
      Spreads1                the spreads on the realised variance for Underlyings1
      Notionals1              the vega notional amount for Underlyings1, in terms of the volatility
      Caps1                   variance/volatility cap for Underlyings1 expressed as a factor of the adj volatility strike price,
                              0 for no cap
      Floors1                 variance/volatility floor for Underlyings1 expressed as a factor of the adj volatility strike price,
                              0 for no floor
      Underlyings2            underlying index (second term in the spread/difference)
      Strikes2                the volatility strike price for Underlyings2
      Spreads2                the spreads on the realised variance for Underlyings2
      Notionals2              the vega notional amount for Underlyings2, in terms of the volatility
      Caps2                   variance/volatility cap for Underlyings2 expressed as a factor of the adj volatility strike price,
                              0 for no cap
      Floors2                 variance/volatility floor for Underlyings2 expressed as a factor of the adj volatility strike price,
                              0 for no floor
      UpperBarrierLevels      the agreed value for the upper barrier on each underlying in Underlyings1
      LowerBarrierLevels      the agreed value for the lower barrier on each underlying in Underlyings1
      CountBothObservations   whether to count previous price only (False) or also the 
                                current price (True) when evaluating variance accrual for
                                each observation date.
      AccrualAdjustment       whether the strike will be scaled relative to the
                                number of days when the underlying remained in the corridor
      DividendAdjustment      whether or not to use ex-dividend share price for underlying price
      ValuationSchedule       the realised variance accrual schedule
      SettlementDate          the settlement date of the swap payoff
      PayCcy                  the settlement currency      

      Results:
      RealisedVariance        the variance accrued over the life of the variance
      Swap                    NPV of the variance swap
  -->
  <Script>
    <Name>CorridorVarianceDispersionSwap</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {SIZE(Strikes1) == SIZE(Spreads1)} AND {SIZE(Spreads1) == SIZE(Notionals1)};
        REQUIRE {SIZE(Notionals1) == SIZE(Underlyings1)} AND {SIZE(Underlyings1) == SIZE(Caps1)};
        REQUIRE {SIZE(Caps1) == SIZE(Floors1)} AND {SIZE(Strikes2) == SIZE(Spreads2)};
        REQUIRE {SIZE(Spreads2) == SIZE(Notionals2)} AND {SIZE(Notionals2) == SIZE(Underlyings2)};
        REQUIRE {SIZE(Underlyings2) == SIZE(Caps2)} AND {SIZE(Caps2) == SIZE(Floors2)};
        REQUIRE SIZE(Underlyings1) == SIZE(Underlyings2);

        NUMBER u, N;
        N = SIZE(Underlyings1);
        FOR u IN (1, N, 1) DO
          REQUIRE {Strikes1[u] >= 0} AND {Caps1[u] >= 0} AND {Floors1[u] >= 0};
          REQUIRE {Strikes2[u] >= 0} AND {Caps2[u] >= 0} AND {Floors2[u] >= 0};
          REQUIRE UpperBarrierLevels[u] >= LowerBarrierLevels[u];
        END;

        NUMBER D, d, expectedN, realisedVariance1, realisedVariance2, currPrice1, prevPrice1, strike1;
        NUMBER currentNotional1, currentNotional2, floor1, strike2, payoff, payoff1, payoff2, cap1;
        NUMBER adjustedStrike1, adjustedStrike2, currPrice2, prevPrice2, floor2, cap2, accruedDays;

        D = SIZE(ValuationSchedule);
        expectedN = D - 1;

        FOR u IN (1, N, 1) DO
          FOR d IN (2, D, 1) DO
            currPrice1 = Underlyings1[u](ValuationSchedule[d]);
            prevPrice1 = Underlyings1[u](ValuationSchedule[d-1]);

            IF {CountBothObservations == 1 AND
                currPrice1 >= LowerBarrierLevels[u] AND currPrice1 <= UpperBarrierLevels[u] AND
                prevPrice1 >= LowerBarrierLevels[u] AND prevPrice1 <= UpperBarrierLevels[u]} OR
               {CountBothObservations == -1 AND
                prevPrice >= LowerBarrierLevels[u] AND prevPrice <= UpperBarrierLevels[u]} THEN
                  currPrice2 = Underlyings2[u](ValuationSchedule[d]);
                  prevPrice2 = Underlyings2[u](ValuationSchedule[d-1]);
                  realisedVariance1 = realisedVariance1 + pow(ln(currPrice1/prevPrice1), 2);
                  realisedVariance2 = realisedVariance2 + pow(ln(currPrice2/prevPrice2), 2);
                  accruedDays = accruedDays + 1;
            END;
          END;

          IF AccrualAdjustment == -1 THEN
            accruedDays = expectedN;
          END;

          realisedVariance1 = (252/expectedN) * realisedVariance1 + Spreads1[u];
          realisedVariance2 = (252/expectedN) * realisedVariance2 + Spreads2[u];

          currentNotional1 = pow(100, 2) * Notionals1[u] / (2 * 100 * Strikes1[u]);
          currentNotional2 = pow(100, 2) * Notionals2[u] / (2 * 100 * Strikes2[u]);

          strike1 = pow(Strikes1[u], 2);
          strike2 = pow(Strikes2[u], 2);

          adjustedStrike1 = (accruedDays / expectedN) * strike1;
          adjustedStrike2 = (accruedDays / expectedN) * strike2;

          IF Floors1[u] > 0 THEN
            floor1 = pow(Floors1[u], 2);
            realisedVariance1 = max(floor1 * adjustedStrike1, realisedVariance1);
          END;
          IF Floors2[u] > 0 THEN
            floor2 = pow(Floors2[u], 2);
            realisedVariance2 = max(floor2 * adjustedStrike2, realisedVariance2);
          END;
          IF Caps1[u] > 0 THEN
            cap1 = pow(Caps1[u], 2);
            realisedVariance1 = max(cap1 * adjustedStrike1, realisedVariance1);
          END;
          IF Caps2[u] > 0 THEN
            cap2 = pow(Caps2[u], 2);
            realisedVariance2 = max(cap2 * adjustedStrike2, realisedVariance2);
          END;

          payoff1 = currentNotional1 * (realisedVariance1 - adjustedStrike1);
          payoff2 = currentNotional2 * (realisedVariance2 - adjustedStrike2);
          payoff = payoff + LongShort * Weights[u] * (payoff1 - payoff2);
        END;

        Swap = PAY(payoff, ValuationSchedule[D], SettlementDate, PayCcy);

        currentNotional1 = 0;
        currentNotional2 = 0;
        FOR u IN (1, N, 1) DO
          currentNotional1 = currentNotional1 + (pow(100, 2) * Weights[u] * Notionals1[u] / (2 * 100 * Strikes1[u]));
          currentNotional2 = currentNotional2 + (pow(100, 2) * Weights[u] * Notionals2[u] / (2 * 100 * Strikes2[u]));
        END;
      ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result rename="currentNotional">currentNotional1</Result>
        <Result>currentNotional2</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- KO Corridor Variance Dispersion Swap (with Cap and/or Floor) - EQ, FX or COMM

      Inputs:
      LongShort                   whether own party pays on the variance strike,
                                    1 for Long (fixed payer), -1 for Short
      Weights                     vector of underlying weights
      Underlyings1                underlying index (first term in the spread/difference)
      Strikes1                    the volatility strike price for Underlyings1
      Spreads1                    the spreads on the realised variance for Underlyings1
      Notionals1                  the vega notional amount for Underlyings1, in terms of the volatility
      Caps1                       variance/volatility cap for Underlyings1 expressed as a factor of the adj volatility strike price,
                                    0 for no cap
      Floors1                     variance/volatility floor for Underlyings1 expressed as a factor of the adj volatility strike price,
                                    0 for no floor
      Underlyings2                underlying index (second term in the spread/difference)
      Strikes2                    the volatility strike price for Underlyings2
      Spreads2                    the spreads on the realised variance for Underlyings2
      Notionals2                  the vega notional amount for Underlyings2, in terms of the volatility
      Caps2                       variance/volatility cap for Underlyings2 expressed as a factor of the adj volatility strike price,
                                    0 for no cap
      Floors2                     variance/volatility floor for Underlyings2 expressed as a factor of the adj volatility strike price,
                                    0 for no floor
      CorridorUpperBarrierLevels  the agreed value for the upper barrier on each underlying in Underlyings1
      CorridorLowerBarrierLevels  the agreed value for the lower barrier on each underlying in Underlyings1
      KOUpperBarrierLevels        the agreed KO upper barrier levels for each underlying in Underlyings1
      KOLowerBarrierLevels        the agreed KO lower barrier levels for each underlying in Underlyings1
      CountBothObservations       whether to count previous price only (False) or also the 
                                    current price (True) when evaluating variance accrual for
                                    each observation date.
      AccrualAdjustment           whether the strike will be scaled relative to the
                                    number of days when the underlying remained in the corridor
      DividendAdjustment          whether or not to use ex-dividend share price for underlying price
      KnockOutSchedule            the KO observation schedule (usually from trade start date to final variance accrual date)
      VarianceAccrualStartDate    the date on which variance accrual/observation starts
      SettlementSchedule          the settlement date of the swap payoff, usually derived from the ObservationSchedule
      PayCcy                      the settlement currency      

      Results:
      RealisedVariance            the variance accrued over the life of the variance
      Swap                        NPV of the variance swap
  -->
  <Script>
    <Name>KOCorridorVarianceDispersionSwap</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {SIZE(Strikes1) == SIZE(Spreads1)} AND {SIZE(Spreads1) == SIZE(Notionals1)};
        REQUIRE {SIZE(Notionals1) == SIZE(Underlyings1)} AND {SIZE(Underlyings1) == SIZE(Caps1)};
        REQUIRE {SIZE(Caps1) == SIZE(Floors1)} AND {SIZE(Strikes2) == SIZE(Spreads2)};
        REQUIRE {SIZE(Spreads2) == SIZE(Notionals2)} AND {SIZE(Notionals2) == SIZE(Underlyings2)};
        REQUIRE {SIZE(Underlyings2) == SIZE(Caps2)} AND {SIZE(Caps2) == SIZE(Floors2)};
        REQUIRE SIZE(Underlyings1) == SIZE(Underlyings2);

        NUMBER u, N;
        N = SIZE(Underlyings1);
        FOR u IN (1, N, 1) DO
          REQUIRE {Strikes1[u] >= 0} AND {Caps1[u] >= 0} AND {Floors1[u] >= 0};
          REQUIRE {Strikes2[u] >= 0} AND {Caps2[u] >= 0} AND {Floors2[u] >= 0};
          REQUIRE CorridorUpperBarrierLevels[u] >= CorridorLowerBarrierLevels[u];
          REQUIRE KOUpperBarrierLevels[u] >= KOLowerBarrierLevels[u];
        END;

        NUMBER d, expectedN, realisedVariance1, realisedVariance2, currPrice1, prevPrice1, strike1;
        NUMBER currentNotional1, currentNotional2, floor1, strike2, payoff, payoff1, payoff2, cap1;
        NUMBER adjustedStrike1, adjustedStrike2, currPrice2, prevPrice2, floor2, cap2, accruedDays;
        NUMBER alive, calculated, TriggerProbability, Dk;

        Dk = SIZE(KnockOutSchedule);

        FOR d IN (1, Dk, 1) DO
          IF KnockOutSchedule[d] > VarianceAccrualStartDate THEN
            expectedN = expectedN + 1;
          END;
        END;

        alive = 1;
        FOR d IN (1, Dk, 1) DO
          IF alive == 1 THEN
            IF KnockOutSchedule[d] > VarianceAccrualStartDate THEN
              FOR u IN (1, N, 1) DO
                currPrice1 = Underlyings1[u](KnockOutSchedule[d]);
                prevPrice1 = Underlyings1[u](KnockOutSchedule[d-1]);

                IF {CountBothObservations == 1 AND
                    currPrice1 >= CorridorLowerBarrierLevels[u] AND currPrice1 <= CorridorUpperBarrierLevels[u] AND
                    prevPrice1 >= CorridorLowerBarrierLevels[u] AND prevPrice1 <= CorridorUpperBarrierLevels[u]} OR
                   {CountBothObservations == -1 AND
                    prevPrice >= CorridorLowerBarrierLevels[u] AND prevPrice <= CorridorUpperBarrierLevels[u]} THEN
                      currPrice2 = Underlyings2[u](KnockOutSchedule[d]);
                      prevPrice2 = Underlyings2[u](KnockOutSchedule[d-1]);
                      realisedVariance1 = realisedVariance1 + pow(ln(currPrice1/prevPrice1), 2);
                      realisedVariance2 = realisedVariance2 + pow(ln(currPrice2/prevPrice2), 2);
                      accruedDays = accruedDays + 1;
                END;
              END;
            END;

            FOR u IN (1, N, 1) DO
              currPrice1 = Underlyings1[u](KnockOutSchedule[d]);
              IF currPrice1 <= KOLowerBarrierLevels[u] OR currPrice1 >= KOUpperBarrierLevels[u] THEN
                alive = 0;
                TriggerProbability = 1;
              END;
            END;
          END;

          IF {alive == 0 OR d == Dk} AND {calculated == 0} THEN
            calculated = 1;

            realisedVariance1 = (252/expectedN) * realisedVariance1 + Spreads1[u];
            realisedVariance2 = (252/expectedN) * realisedVariance2 + Spreads2[u];

            IF AccrualAdjustment == -1 THEN
              accruedDays = expectedN;
            END;

            currentNotional1 = pow(100, 2) * Notionals1[u] / (2 * 100 * Strikes1[u]);
            currentNotional2 = pow(100, 2) * Notionals2[u] / (2 * 100 * Strikes2[u]);

            strike1 = pow(Strikes1[u], 2);
            strike2 = pow(Strikes2[u], 2);

            adjustedStrike1 = (accruedDays / expectedN) * strike1;
            adjustedStrike2 = (accruedDays / expectedN) * strike2;

            IF Floors1[u] > 0 THEN
              floor1 = pow(Floors1[u], 2);
              realisedVariance1 = max(floor1 * adjustedStrike1, realisedVariance1);
            END;
            IF Floors2[u] > 0 THEN
              floor2 = pow(Floors2[u], 2);
              realisedVariance2 = max(floor2 * adjustedStrike2, realisedVariance2);
            END;
            IF Caps1[u] > 0 THEN
              cap1 = pow(Caps1[u], 2);
              realisedVariance1 = max(cap1 * adjustedStrike1, realisedVariance1);
            END;
            IF Caps2[u] > 0 THEN
              cap2 = pow(Caps2[u], 2);
              realisedVariance2 = max(cap2 * adjustedStrike2, realisedVariance2);
            END;

            payoff1 = currentNotional1 * (realisedVariance1 - adjustedStrike1);
            payoff2 = currentNotional2 * (realisedVariance2 - adjustedStrike2);
            payoff = payoff + LongShort * Weights[u] * (payoff1 - payoff2);

            Swap = PAY(payoff, KnockOutSchedule[d], SettlementSchedule[d], PayCcy);
          END;
        END;

        currentNotional1 = 0;
        currentNotional2 = 0;
        FOR u IN (1, N, 1) DO
          currentNotional1 = currentNotional1 + (pow(100, 2) * Weights[u] * Notionals1[u] / (2 * 100 * Strikes1[u]));
          currentNotional2 = currentNotional2 + (pow(100, 2) * Weights[u] * Notionals2[u] / (2 * 100 * Strikes2[u]));
        END;
      ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result>TriggerProbability</Result>
        <Result rename="currentNotional">currentNotional1</Result>
        <Result>currentNotional2</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>

  <!-- OTHER -->

  <!-- Asian IR Cap / Floor

       Inputs:
       NotionalAmount                  the notional amount
       LongShort                       1 for a long, -1 for a short position
       Underlying                      the underlying index (EUR-CMS-10Y, EUR-EURIBOR-3M)
       NumberOfFixingsPerCoupon        number of fixings to average over per coupon
       FixingIndexStep                 number of dates to advance in fixing schedule for each coupon
       OptionType                      1 for cap, -1 for floor
       Strike                          the strike
       Gearing                         the gearing to mulitply the underlying fixing with
       Spread                          the spread added to the underlying fixing
       DayCountFraction                the floating rate day count fraction
       FixedAmount                     a fixed amount to pay by the option holder
       FixedAmountPayDate              the pay date of the fixed amount
       PayCcy                          the pay ccy
       AccrualSchedule                 the coupon accrual schedule
       FixingSchedule                  the averaging schedule

       Results:
       Option                          npv of the option
       currentNotional                 current notional
       notionalCurrency                notoinal currency
  -->
  <Script>
    <Name>AsianIrCapFloor</Name>
    <ProductTag>SingleUnderlyingIrOption</ProductTag>
    <Script>
      <Code><![CDATA[
      NUMBER f, fixing, p, l, noFixings;
      FOR p IN (1, SIZE(AccrualSchedule) - 1, 1) DO
         fixing = 0;
         noFixings = 0;
         FOR f IN (1, SIZE(FixingSchedule), 1) DO
            l =  dcf( FixingLagDc, FixingSchedule[f], AccrualSchedule[p+1] );
            IF l >= MinFixingLag AND l <= MaxFixingLag THEN
              fixing = fixing + Underlying(FixingSchedule[f]);
              noFixings = noFixings + 1;
           END;
         END;
         IF noFixings >= 1 THEN
           fixing = fixing / noFixings;
         END;
         Option = Option + LongShort * PAY( NotionalAmount * max( OptionType * (Gearing * fixing + Spread - Strike), 0 ) *
                                            dcf( DayCountFraction, AccrualSchedule[p], AccrualSchedule[p+1] ),
                                            AccrualSchedule[p+1], AccrualSchedule[p+1], PayCcy );
      END;
      Option = Option - LongShort * PAY( FixedAmount, FixedAmountPayDate, FixedAmountPayDate, PayCcy );
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="currentNotional">NotionalAmount</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
      <ScheduleCoarsening>
        <EligibleSchedule>FixingSchedule</EligibleSchedule>
      </ScheduleCoarsening>
    </Script>
  </Script>
  <!-- OutperformanceOption

       Inputs:
       Notional                        the notional amount
       LongShort                       1 for a long, -1 for a short position
       Underlying1                     the first underlying index (EUR-CMS-10Y, EUR-EURIBOR-3M)
       Underlying2                     the second underlying index (EUR-CMS-10Y, EUR-EURIBOR-3M)
       InitialPrice1                   the first index's initial price
       InitialPrice2                   the second index's initial price
       StrikeReturn                    the strike return
       PayCcy                          the pay ccy
       KnockIn                         if the trade has a knockIn condition, 1 for true, -1 for false
       KnockInPrice                    KnockIn price for second underlying
       KnockOut                        if the trade has a knockOut condition, 1 for true, -1 for false
       KnockOutPrice                   KnockOut price for second underlying
       OptionType                      1 if call, -1 if put

       Results:
       Option                          npv of the option
       ExerciseProbability             exercise probability of the trade
  -->
  <Script>
    <Name>OutperformanceOption</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
      NUMBER IndexReturn1, IndexReturn2, barrier, ExerciseProbability, active, Payoff;
      
      IndexReturn1 = Underlying1(Expiry) / InitialPrice1;
      IndexReturn2 = Underlying2(Expiry) / InitialPrice2;

      Payoff = max(OptionType * (IndexReturn1 - IndexReturn2 - StrikeReturn), 0);

      active = 1;
      barrier = Underlying2(Expiry);
      IF KnockIn == 1 THEN
        IF barrier <= KnockInPrice THEN 
            active = -1;
        END;
      END;

      IF KnockOut == 1 THEN
        IF active == 1 THEN
            IF barrier >= KnockOutPrice THEN
                active = -1;
            END;
        END;
      END;

      Option = 0;
      IF active == 1 THEN
        Option = LongShort * Notional * PAY(Payoff, Expiry, Settlement, PayCcy);
      END;

      IF Option > 0 THEN
          ExerciseProbability = 1;
      END;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result>ExerciseProbability</Result>
        <Result rename="currentNotional">Notional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Forward Volatility Agreement

        Inputs:
        FvaDate             the date when the underlying straddle option is exchanged
        OptionExpiry        the option expiry date
        PremiumDate         the option premium payment date
        Underlying          the underlying of the straddle option
        LongShort           the position in the underlying straddle option, Long or Short
        ImpliedVolStrike    the agreed volatility rate for calculating the option premium
        Quantity            the quantity of the underlying option, in terms of the base
                            currency for FX, and as the number of options exchanged for EQ
        PayCcy              the option settlement currency
        SettlementDate      the option exercise payment date

        Requirements:
        TODAY <= FvaDate        This instrument is priced with the assumption that the
                                  price of the straddle is not yet known. If
                                  TODAY > FvaDate, it would be better to price this
                                  option some other way, which assumes that the premium
                                  has already been determined (if not paid out).
        FvaDate <= PremiumDate  The premium amount is determined at the FvaDate. 
        FvaDate < OptionExpiry  Option's time to expiry must be positive, i.e. t > 0 in
                                  the black function

        Results:
        FVA                 the NPV of the Forward Volatility Agreement
  -->
  <Script>
    <Name>ForwardVolatilityAgreement</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE TODAY <= FvaDate;
        REQUIRE FvaDate <= PremiumDate;
        REQUIRE FvaDate < OptionExpiry;
        REQUIRE Quantity >= 0;
        REQUIRE ImpliedVolStrike > 0;

        NUMBER forwardStrike, fixedPremium, floatingPayoff;

        forwardStrike = Underlying(FvaDate, OptionExpiry);
        fixedPremium = black(1, FvaDate, OptionExpiry, forwardStrike, forwardStrike, ImpliedVolStrike) +
                       black(-1, FvaDate, OptionExpiry, forwardStrike, forwardStrike, ImpliedVolStrike);
        floatingPayoff = abs(forwardStrike - Underlying(OptionExpiry));

        FVA = LongShort * Quantity * (PAY(floatingPayoff, OptionExpiry, SettlementDate, PayCcy) -
                PAY(fixedPremium, FvaDate, PremiumDate, PayCcy));
        ]]>
      </Code>
      <NPV>FVA</NPV>
      <Results>
        <Result rename="currentNotional">Quantity</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- Correlation Swap

        Inputs:
        Amount                  the notional amount (per percent correlation)
        FixedRate               the agreed correlation rate for the fixed leg
        FixedRatePayer          whether own party pays the fixed leg, true or false
        Underlyings             the two underlying assets
        DeterminationDates      the schedule of determination/observation dates
        SettlementDate          the settlement date
        PayCcy                  the settlement currency

        Results:
        Swap                    the NPV of the Correlation Swap
  -->
  <Script>
    <Name>CorrelationSwap</Name>
    <ProductTag>MultiAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE FixedRate >= -1 AND FixedRate <= 1;
        REQUIRE Amount >= 0;
        REQUIRE DeterminationDates[SIZE(DeterminationDates)] <= SettlementDate;
        REQUIRE SIZE(Underlyings) == 2;

        NUMBER floatingRate, i, covXY, covXX, covYY, logReturnX, logReturnY;

        FOR i IN (2, SIZE(DeterminationDates), 1) DO
          logReturnX = ln(Underlyings[1](DeterminationDates[i])/Underlyings[1](DeterminationDates[i-1]));
          logReturnY = ln(Underlyings[2](DeterminationDates[i])/Underlyings[2](DeterminationDates[i-1]));
  
          covXY = covXY + (logReturnX * logReturnY);
          covXX = covXX + pow(logReturnX, 2);
          covYY = covYY + pow(logReturnY, 2);
        END;

        floatingRate = covXY / sqrt(covXX * covYY);

        Swap = FixedRatePayer * Amount * 100 * PAY((floatingRate - FixedRate),
                DeterminationDates[SIZE(DeterminationDates)], SettlementDate, PayCcy);
        ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result rename="currentNotional">Amount</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- CMS Volatility Swap

       Inputs:
       NotionalAmount                  the notional amount
       Underlyings                     vector of underlying CMS indices
       LongShort                       1 for a long, -1 for a short position
       Strike                          the strike of 100 notional
       PayCcy                          the settlement currency
       DayCountFraction                the floating rate day count fraction 
       Settlement                      payment date
       ResetSchedule                   the coupon accrual schedule 

       Results:
       Swap                            npv of the option
       currentNotional                 current notional
       notionalCurrency                notional currency
  -->
  <Script>
    <Name>ConstantMaturityVolatilitySwap</Name>
    <ProductTag>SingleUnderlyingIrOption</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE SIZE(Underlyings) <= 2;

        NUMBER i, u, fixings[SIZE(Underlyings)], fixing, spreadRate;

        FOR i IN (1, SIZE(ResetSchedule) - 1, 1) DO
          FOR u IN (1, SIZE(Underlyings), 1) DO
            fixings[u] = fixings[u] + pow(Underlyings[u](ResetSchedule[i+1]) - Underlyings[u](ResetSchedule[i]), 2);
          END;
        END;

        IF SIZE(Underlyings) == 1 THEN
          spreadRate = sqrt(fixings[1]);
        ELSE
          spreadRate = sqrt(fixings[1]) - sqrt(fixings[2]);
        END;

        Swap = LongShort * PAY(NotionalAmount * (15.8745 * (spreadRate / i) - Strike / 100) * 
             dcf(DayCountFraction, ResetSchedule[1], ResetSchedule[i+1]), Settlement, Settlement, PayCcy);
      ]]>
      </Code>
      <NPV>Swap</NPV>
      <Results>
        <Result rename="currentNotional">NotionalAmount</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <!-- CMS CapFloor with Barrier
  
        Inputs:
        Notional                the notional amount
        Strike                  the option cap/floor strike (as a decimal)
        PremiumAmount           the option premium amount (per option)
        PremiumCurrency         the currency of the premium
        PremiumDate             the premium pay date
        OptionExpiry            the option expiry date
        Quantity                the number of options exchanged
        OptionType              1 for call, -1 for put
        LongShort               1 for long, -1 for short
        CMSUnderlyings          vector of underlying CMS indices
        Gearing                 the gearing to mulitply the underlying fixing with
        Spread                  the spread added to the underlying fixing
        BarrierUnderlying       the asset on which the barrier level is determined
        BarrierLevel            the agreed barrier value for the BarrierUnderlying
        BarrierType             1 for DownIn, 2 for UpIn, 3 for DownOut, 4 for UpOut
        SettlementDate          the date when the option payoff is settled
        SettlementCurrency      the settlement currency

        Results:
        Option                  the NPV of the CMS CapFloor
  -->
  <Script>
    <Name>CMSCapFloorBarrier</Name>
    <ProductTag>IrHybrid({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE OptionExpiry <= SettlementDate AND Notional >= 0 AND Quantity >= 0;
        REQUIRE PremiumAmount >= 0 AND BarrierLevel >= 0 AND Gearing >= 0;
        REQUIRE BarrierType == 1 OR BarrierType == 2 OR BarrierType == 3 OR BarrierType == 4;
        REQUIRE SIZE(CMSUnderlyings) <= 2;
        
        NUMBER barrierFixing, cmsFixing, exercisePayoff, premium;

        barrierFixing = BarrierUnderlying(OptionExpiry);

        IF {{BarrierType == 1 OR BarrierType == 4} AND barrierFixing <= BarrierLevel}
        OR {{BarrierType == 2 OR BarrierType == 3} AND barrierFixing >= BarrierLevel} THEN
          IF SIZE(CMSUnderlyings) == 1 THEN
            cmsFixing = CMSUnderlyings[1](OptionExpiry);
          ELSE
            cmsFixing = CMSUnderlyings[1](OptionExpiry) - CMSUnderlyings[2](OptionExpiry);
          END;
          exercisePayoff = Notional * Quantity *
                            max(0, OptionType * ((Gearing * cmsFixing) + Spread - Strike));
        END;
        
        exercisePayoff = LOGPAY(exercisePayoff, OptionExpiry, SettlementDate,
                                SettlementCurrency, 1, ExercisePayoff);
        premium = LOGPAY(Quantity * PremiumAmount, PremiumDate, PremiumDate,
                         PremiumCurrency, 0, Premium);
        Option = LongShort * (exercisePayoff - premium);
        ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="currentNotional">Notional</Result>
        <Result rename="notionalCurrency">SettlementCurrency</Result>
      </Results>
    </Script>
  </Script>
  <!-- Fixed Strike Forward Starting Option
  
        Inputs:
        ForwardDate             the date when the option is exchanged (and premium is determined)
        PremiumDate             the option premium payment date
        OptionExpiry            the option expiry date
        DayCountFraction        the day counter for calculating the option premium
        Underlying              the underlying of the forward starting option
        UnderlyingDrift         the agreed underlying drift for calculating the premium
        DiscountRate            the agreed risk free rate for calculating the premium
        ImpliedVolatility       the agreed volatility rate for calculating the premium
        LongShort               the position in the forward starting option: 1 for long
        PutCall                 the option type: 1 for call, -1 for put
        Strike                  the strike price of the forward starting option
        Quantity                the number of options exchanged
        SettlementDate          the date when the option payoff is settled
        SettlementCurrency      the option settlement currency

        Results:
        Option                  the NPV of the Forward Starting Option
  -->
  <Script>
    <Name>FixedStrikeForwardStartingOption</Name>
    <ProductTag>SingleAssetOption{AssetClass}</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {TODAY <= ForwardDate} AND {ForwardDate <= PremiumDate};
        REQUIRE {PremiumDate < OptionExpiry} AND {OptionExpiry <= SettlementDate};
        REQUIRE {Strike > 0} AND {ImpliedVolatility > 0} AND {Quantity >= 0};

        NUMBER optionTerm, forwardFixing, premium, exercisePayoff;

        optionTerm = dcf(DayCountFraction, ForwardDate, OptionExpiry);
        forwardFixing = Underlying(ForwardDate) * exp(UnderlyingDrift * optionTerm);

        premium = black(PutCall, ForwardDate, OptionExpiry, Strike,forwardFixing,
                        ImpliedVolatility) * exp(-DiscountRate * optionTerm);
        
        premium = PAY(premium, ForwardDate, PremiumDate, SettlementCurrency);
        exercisePayoff = PAY(max(0, PutCall * (Underlying(OptionExpiry) - Strike)),
                          OptionExpiry, SettlementDate, SettlementCurrency);

        Option = LongShort * Quantity * (exercisePayoff - premium);
        ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="currentNotional">Quantity</Result>
        <Result rename="notionalCurrency">SettlementCurrency</Result>
      </Results>
    </Script>
  </Script>
  <!-- Floating Strike Forward Starting Option
  
        Inputs:
        ForwardDate             the date when the option is exchanged
        PremiumDate             the option premium payment date
        OptionExpiry            the option expiry date
        PremiumAmount           the total option premium amount
        PremiumCurrency         the currency of the option premium
        Underlying              the underlying of the forward starting option
        LongShort               the position in the forward starting option: 1 for long
        PutCall                 the option type: 1 for call, -1 for put
        Strike                  the strike price of the forward starting option,
                                as a percentage of the Underlying in decimal form
        Notional                the notional amount in terms of the SettlementCurrency
        SettlementDate          the date when the option payoff is settled
        SettlementCurrency      the option settlement currency

        Results:
        Option                  the NPV of the Forward Starting Option
  -->
  <Script>
    <Name>FloatingStrikeForwardStartingOption</Name>
    <ProductTag>SingleAssetOption{AssetClass}</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE TODAY <= ForwardDate;
        REQUIRE OptionExpiry <= SettlementDate;
        REQUIRE {Strike > 0} AND {Notional >= 0} AND {PremiumAmount > 0};

        NUMBER premium, indexInitial, indexFinal, strikePrice, exercisePayoff;

        premium = PAY(PremiumAmount, PremiumDate, PremiumDate, PremiumCurrency);
        indexInitial = Underlying(ForwardDate);
        indexFinal = Underlying(OptionExpiry);
        strikePrice = Strike * indexInitial;
        exercisePayoff = PAY(Notional * max(0, PutCall * (indexFinal - strikePrice) / indexInitial),
                          OptionExpiry, SettlementDate, SettlementCurrency);

        Option = LongShort * (exercisePayoff - premium);
        ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="currentNotional">Notional</Result>
        <Result rename="notionalCurrency">SettlementCurrency</Result>
      </Results>
    </Script>
  </Script>
  <!-- Forward Starting Swaption

        Inputs:
        DeterminationDate         the date when the fixed rate of the underlying swap is calculated and agreed upon
        LongShort                 the position in the underlying swaption, Long or Short
        SwaptionType              1 for receiver swaption, -1 for payer swaption, 0 for straddle swaption
        PremiumDate               the option premium payment date
        PremiumAmount             the option premium amount
        PremiumCurrency           the currency of the premium
        OptionExpiry              the swaption expiry date
        Underlying                the IR index of the underlying swap floating rate
        Notional                  the notional amount of the underlying swap
        PayCcy                    the currency of the underlying swap notional
        FixedDayCountFraction     the day count fraction for the fixed leg
        FixedSchedule             the payment schedule of the fixed leg        
        FloatingDayCountFraction  the day count fraction for the floating leg
        FloatingSchedule          the payment schedule of the floating leg
        FixingSchedule            the fixing schedule of the floating leg payments, derived from the FloatingSchedule

        Results:
        FSS                       the NPV of the Forward Starting Swaption
  -->
  <Script>
    <Name>ForwardStartingSwaption</Name>
    <ProductTag>SingleUnderlyingIrOption</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {TODAY <= DeterminationDate} AND {DeterminationDate <= OptionExpiry};
        REQUIRE {OptionExpiry <= FixedSchedule[1]} AND {OptionExpiry <= FloatingSchedule[1]};
        REQUIRE {PremiumAmount >= 0} AND {Notional >= 0};
        REQUIRE {SwaptionType == -1} OR {SwaptionType == 0} OR {SwaptionType == 1};
        
        NUMBER d, floatingAccrualFraction, forwardRate, numerator;
        NUMBER fixedAccrualFraction, denominator;
        NUMBER fairRate, floatingLegNpv, fixedLegNpv, exercisePayoff, totalExercisePayoff, premium;
        NUMBER floatingAccrualFractions[SIZE(FloatingSchedule)-1], fixedAccrualFractions[SIZE(FixedSchedule)-1];

        FOR d IN (2, SIZE(FloatingSchedule), 1) DO
          floatingAccrualFraction = dcf(FloatingDayCountFraction, FloatingSchedule[d-1], FloatingSchedule[d]);
          floatingAccrualFractions[d-1] = floatingAccrualFraction;
          forwardRate = Underlying(DeterminationDate, FixingSchedule[d-1]);
          numerator = numerator + PAY(forwardRate * floatingAccrualFraction, DeterminationDate, FloatingSchedule[d], PayCcy);
        END;

        FOR d IN (2, SIZE(FixedSchedule), 1) DO
          fixedAccrualFraction = dcf(FixedDayCountFraction, FixedSchedule[d-1], FixedSchedule[d]);
          fixedAccrualFractions[d-1] = fixedAccrualFraction;
          denominator = denominator + PAY(fixedAccrualFraction, DeterminationDate, FixedSchedule[d], PayCcy);
        END;

        fairRate = numerator / denominator;

        FOR d IN (2, SIZE(FloatingSchedule), 1) DO
          forwardRate = Underlying(OptionExpiry, FixingSchedule[d-1]);
          floatingLegNpv = floatingLegNpv + PAY(forwardRate * floatingAccrualFractions[d-1], OptionExpiry, FloatingSchedule[d], PayCcy);
        END;
        
        FOR d IN (2, SIZE(FixedSchedule), 1) DO
          fixedLegNpv = fixedLegNpv + PAY(fixedAccrualFractions[d-1], OptionExpiry, FixedSchedule[d-1], PayCcy);
        END;
        fixedLegNpv = fairRate * fixedLegNpv;

        IF SwaptionType == 0 THEN
          exercisePayoff = abs(fixedLegNpv - floatingLegNpv);
        ELSE
          exercisePayoff = SwaptionType * (fixedLegNpv - floatingLegNpv);
        END;

        totalExercisePayoff = LOGPAY(Notional * exercisePayoff, OptionExpiry, OptionExpiry, PayCcy, 1, ExercisePayoff);
        premium = LOGPAY(PremiumAmount, PremiumDate, PremiumDate, PremiumCurrency, 0, Premium);

        FSS = LongShort * (totalExercisePayoff - premium);
        ]]>
      </Code>
      <NPV>FSS</NPV>
      <Results>
        <Result rename="notionalCurrency">PayCcy</Result>
        <Result rename="currentNotional">Notional</Result>
      </Results>
    </Script>
  </Script>
  <!-- Floored Average CPI Zero Coupon Inflation Index Swap

        Inputs:
        Floor                     the floor of floating rate
        CPIIndex              	  the underlying CPI Inflation Index
        FixedRate                 the fixed leg rate 
        Notional                  the notional amount of the underlying swap
        PayCurrency               the currency of the underlying swap notional
        PayFixLeg                 the payer/receiver flag
        FixedDayCounter	          the day count fraction for the fixed leg
        FixedLegSchedule          the payment schedule of the fixed leg        
        FloatDayCounter           the day count fraction for the floating leg
        FloatLegSchedule          the payment schedule of the floating leg
		    ObservationSchedule	      the observation schedule with all observation dates for the averaging 
								  (for each floating coupon, it will average the CPI on all observation dates between coupon start and end date)
        FixingSchedule            the fixing schedule of the floating leg payments, derived from the observation schedule

        Results:
        FSS                       the NPV of the Floored Average CPI Zero Coupon Inflation Index Swap
  -->
  <Script>
    <Name>FlooredAverageCPIZCIIS</Name>
    <Script>
      <Code><![CDATA[
      NUMBER FixedLegNpv, FloatLegNpv;
	  NUMBER i, j, obs_start, obs_end, lpi, average_lpi;
	  NUMBER fixedCpnRate, fixedAmount, floatAmount;
	  
	  FOR i IN(2, SIZE(FixedLegSchedule), 1) DO
		fixedAmount = LOGPAY(-1 * PayFixLeg * (pow(1+FixedRate, dcf(FixedDayCounter, FixedLegSchedule[1], FixedLegSchedule[i]))-1) * Notional,
                           FixedLegSchedule[i], FixedLegSchedule[i], PayCurrency, 0, Interest, i);
		FixedLegNpv = FixedLegNpv + fixedAmount;
	  END;
	  
	  FOR i IN(2, SIZE(FloatLegSchedule), 1) DO
		  IF FloatLegSchedule[i] >= TODAY THEN
			obs_end = DATEINDEX(FloatLegSchedule[i], ObservationSchedule, GEQ);
			obs_start = DATEINDEX(FloatLegSchedule[i-1], ObservationSchedule, GT);
			average_lpi = 0;
			FOR j IN(obs_start, obs_end, 1) DO
				average_lpi = average_lpi + CPIIndex(FixingSchedule[j]);
			END;
			average_lpi = average_lpi / (obs_end-obs_start+1);
			lpi = max(average_lpi/BaseCPI, 1+Floor) - 1;
			floatAmount = LOGPAY(PayFixLeg *lpi * Notional * dcf(FloatDayCounter,FloatLegSchedule[i-1],FloatLegSchedule[i]),
							   FloatLegSchedule[i], FloatLegSchedule[i], PayCurrency, 1, Interest, SIZE(FixedLegSchedule)+i);
			FloatLegNpv = FloatLegNpv+floatAmount;
		  END;
	  END;
	  
	  Value = FloatLegNpv+FixedLegNpv;
      ]]></Code>
      <NPV>Value</NPV>
      <Results>
        <Result rename="currentNotional">Notional</Result>
        <Result rename="notionalCurrency">PayCurrency</Result>
      </Results>
    </Script>
  </Script>
  <!-- Moving Maximum YoY Inflation Index Swap
        Inputs:

        Notional                        the notional amount of the underlying swap
        PayCurrency                     the currency of the underlying swap notional
        IborLegDayCounter	              the day count fraction for the Ibor floating leg
        IborSpread                      the spread added to the Ibor fixing
        IborIndex              	        the underlying Ibor Index
        PayIborLeg                      the payer/receiver flag
        IborLegSchedule                 the payment schedule of the Ibor floating leg 
        IborLegFixingSchedule           the fixing schedule of the Ibor floating leg
        InflationLeg1_DayCounter        the day count fraction for the first inflation leg
        InflationLeg1_CPI               the underlying CPI index of the first inflation leg
        InflationLeg1_Schedule          the payment schedule of the first inflation leg
        InflationLeg1_Gearing           the gearing of the first inflation leg
        InflationLeg1_Floor             the floor rate of the first inflation leg
        InflationLeg1_InitialCPI        the initial base fixing for the first inflation coupon
        InflationLeg1_SubtractNotional  if true, the inflation rate is I_t / I_{t-1} - 1 otherwise I_t / I_{t-1}
        InflationLeg1_FixingSchedule    the fixing dates of the first inflation index
        InflationLeg2_DayCounter        the day count fraction for the second inflation leg
        InflationLeg2_CPI               the underlying CPI index of the second inflation leg
        InflationLeg2_Schedule          the payment schedule of the second inflation leg
        InflationLeg2_Gearing           the gearing of the second inflation leg
        InflationLeg2_Floor             the floor rate of the second inflation leg
        InflationLeg2_InitialCPI        the initial base fixing for the second inflation coupon
        InflationLeg2_SubtractNotional  if true, the inflation rate is I_t / I_{t-1} - 1 otherwise I_t / I_{t-1}
        InflationLeg2_FixingSchedule    the fixing dates of the second inflation index
        

        Results:
        FSS                       the NPV of the Moving Maximum YoY Inflation Index Swap
  -->
  <Script>
      <Name>MovingMaxYYIIS</Name>
      <Script>
        <Code><![CDATA[
        NUMBER i;
      NUMBER iborLegStartIdx, iborLegNPV, iborAmount;
      NUMBER infLeg1NPV, infLeg1Amount, infLeg1MaxIndexFixing, infLeg1Rate;
        NUMBER infLeg2NPV, infLeg2Amount, infLeg2MaxIndexFixing, infLeg2Rate;

      iborLegNPV = 0;
      
      FOR i IN(2, SIZE(IborLegSchedule), 1) DO
      iborAmount = LOGPAY(-1 * PayIborLeg * (IborIndex(IborLegFixingSchedule[i-1])+IborSpread) * Notional * dcf(IborLegDayCounter, IborLegSchedule[i-1], IborLegSchedule[i]),
                            IborLegSchedule[i], IborLegSchedule[i], PayCurrency, 0, Interest, i);
      iborLegNPV = iborLegNPV + iborAmount;
      END;
      
      infLeg1NPV = 0;
      infLeg1MaxIndexFixing = InflationLeg1_InitialCPI;
      FOR i IN(2, SIZE(InflationLeg1_Schedule), 1) DO
        IF i > 2 THEN 
          infLeg1MaxIndexFixing = max(infLeg1MaxIndexFixing,InflationLeg1_CPI(InflationLeg1_FixingSchedule[i-1]));
        END;
        infLeg1Rate = InflationLeg1_CPI(InflationLeg1_FixingSchedule[i])/infLeg1MaxIndexFixing;
        IF InflationLeg1_SubtractNotional == 1 THEN
          infLeg1Rate = infLeg1Rate - 1;
        END;
        infLeg1Rate = max(infLeg1Rate, InflationLeg1_Floor);
        infLeg1Amount = LOGPAY(PayIborLeg * infLeg1Rate * Notional * InflationLeg1_Gearing * dcf(InflationLeg1_DayCounter,InflationLeg1_Schedule[i-1],InflationLeg1_Schedule[i]),
                  InflationLeg1_Schedule[i], InflationLeg1_Schedule[i], PayCurrency, 1, Interest, SIZE(IborLegSchedule)+i);
        infLeg1NPV = infLeg1NPV+infLeg1Amount;
      END;
      
      infLeg2NPV = 0;
      infLeg2MaxIndexFixing = InflationLeg2_InitialCPI;
      FOR i IN(2, SIZE(InflationLeg2_Schedule), 1) DO
        IF i > 2 THEN 
          infLeg2MaxIndexFixing = max(infLeg2MaxIndexFixing, InflationLeg2_CPI(InflationLeg2_FixingSchedule[i-1]));
        END;
        infLeg2Rate = InflationLeg2_CPI(InflationLeg2_FixingSchedule[i])/infLeg2MaxIndexFixing;
        IF InflationLeg2_SubtractNotional == 1 THEN
          infLeg2Rate = infLeg2Rate - 1;
        END;
        infLeg2Rate = max(infLeg2Rate, InflationLeg2_Floor);
        infLeg2Amount = LOGPAY(PayIborLeg * infLeg2Rate * Notional * InflationLeg2_Gearing * dcf(InflationLeg2_DayCounter,InflationLeg2_Schedule[i-1],InflationLeg2_Schedule[i]),
                  InflationLeg2_Schedule[i], InflationLeg2_Schedule[i], PayCurrency, 2, Interest, SIZE(IborLegSchedule)+SIZE(InflationLeg1_Schedule)+i);
        infLeg2NPV = infLeg2NPV+infLeg2Amount;
      END;
      
      Value =  iborLegNPV + infLeg1NPV+infLeg2NPV;
        ]]></Code>
        <NPV>Value</NPV>
        <Results>
          <Result rename="currentNotional">Notional</Result>
          <Result rename="notionalCurrency">PayCurrency</Result>
        </Results>
      </Script>
    </Script>
<!-- Irregular YoY Inflation Index Swap
        Inputs:

        Notional                        the notional amount of the underlying swap
        PayCurrency                     the currency of the underlying swap notional
        IborLegDayCounter	              the day count fraction for the Ibor floating leg
        IborSpread                      the spread added to the Ibor fixing
        IborIndex              	        the underlying Ibor Index
        PayIborLeg                      the payer/receiver flag
        IborLegSchedule                 the payment schedule of the Ibor floating leg 
        IborLegFixingSchedule           the fixing schedule of the Ibor floating leg
        InflationLeg1_DayCounter        the day count fraction for the first inflation leg
        InflationLeg1_CPI               the underlying CPI index of the first inflation leg
        InflationLeg1_Schedule          the payment schedule of the first inflation leg
        InflationLeg1_Gearing           the gearing of the first inflation leg
        InflationLeg1_Floor             the floor rate of the first inflation leg
        InflationLeg1_InitialCPI        the initial base fixing for the first inflation coupon
        InflationLeg1_SubtractNotional  if true, the inflation rate is I_t / I_{t-1} - 1 otherwise I_t / I_{t-1}
        InflationLeg1_FixingSchedule    the fixing dates of the first inflation index
        InflationLeg2_DayCounter        the day count fraction for the second inflation leg
        InflationLeg2_CPI               the underlying CPI index of the second inflation leg
        InflationLeg2_Schedule          the payment schedule of the second inflation leg
        InflationLeg2_Gearing           the gearing of the second inflation leg
        InflationLeg2_Floor             the floor rate of the second inflation leg
        InflationLeg2_InitialCPI        the initial base fixing for the second inflation coupon
        InflationLeg2_SubtractNotional  if true, the inflation rate is I_t / I_{t-1} - 1 otherwise I_t / I_{t-1}
        InflationLeg2_FixingSchedule    the fixing dates of the second inflation index
        

        Results:
        FSS                       the NPV of the Moving Maximum YoY Inflation Index Swap
  -->
<Script>
    <Name>IrregularYYIIS</Name>
    <Script>
      <Code><![CDATA[
            NUMBER i;
      NUMBER iborLegStartIdx, iborLegNPV, iborAmount;
      NUMBER infLeg1NPV, infLeg1Amount, infLeg1MaxIndexFixing, infLeg1Rate;
        NUMBER infLeg2NPV, infLeg2Amount, infLeg2MaxIndexFixing, infLeg2Rate;

      iborLegNPV = 0;
      
      FOR i IN(2, SIZE(IborLegSchedule), 1) DO
      iborAmount = LOGPAY(-1 * PayIborLeg * (IborIndex(IborLegFixingSchedule[i-1])+IborSpread) * Notional * dcf(IborLegDayCounter, IborLegSchedule[i-1], IborLegSchedule[i]),
                            IborLegSchedule[i], IborLegSchedule[i], PayCurrency, 0, Interest, i);
      iborLegNPV = iborLegNPV + iborAmount;
      END;
      
      infLeg1NPV = 0;
      infLeg1MaxIndexFixing = InflationLeg1_InitialCPI;
      FOR i IN(2, SIZE(InflationLeg1_Schedule), 1) DO
        IF i > 2 THEN 
          infLeg1MaxIndexFixing = InflationLeg1_CPI(InflationLeg1_FixingSchedule[i-1]);
        END;
        infLeg1Rate = InflationLeg1_CPI(InflationLeg1_FixingSchedule[i])/infLeg1MaxIndexFixing;
        IF InflationLeg1_SubtractNotional == 1 THEN
          infLeg1Rate = infLeg1Rate - 1;
        END;
        infLeg1Rate = max(infLeg1Rate, InflationLeg1_Floor);
        infLeg1Amount = LOGPAY(PayIborLeg * infLeg1Rate * Notional * InflationLeg1_Gearing * dcf(InflationLeg1_DayCounter,InflationLeg1_Schedule[i-1],InflationLeg1_Schedule[i]),
                  InflationLeg1_Schedule[i], InflationLeg1_Schedule[i], PayCurrency, 1, Interest, SIZE(IborLegSchedule)+i);
        infLeg1NPV = infLeg1NPV+infLeg1Amount;
      END;
      
      infLeg2NPV = 0;
      infLeg2MaxIndexFixing = InflationLeg2_InitialCPI;
      FOR i IN(2, SIZE(InflationLeg2_Schedule), 1) DO
        IF i > 2 THEN 
          infLeg2MaxIndexFixing = InflationLeg2_CPI(InflationLeg2_FixingSchedule[i-1]);
        END;
        infLeg2Rate = InflationLeg2_CPI(InflationLeg2_FixingSchedule[i])/infLeg2MaxIndexFixing;
        IF InflationLeg2_SubtractNotional == 1 THEN
          infLeg2Rate = infLeg2Rate - 1;
        END;
        infLeg2Rate = max(infLeg2Rate, InflationLeg2_Floor);
        infLeg2Amount = LOGPAY(PayIborLeg * infLeg2Rate * Notional * InflationLeg2_Gearing * dcf(InflationLeg2_DayCounter,InflationLeg2_Schedule[i-1],InflationLeg2_Schedule[i]),
                  InflationLeg2_Schedule[i], InflationLeg2_Schedule[i], PayCurrency, 2, Interest, SIZE(IborLegSchedule)+SIZE(InflationLeg1_Schedule)+i);
        infLeg2NPV = infLeg2NPV+infLeg2Amount;
      END;
      
      Value =  iborLegNPV + infLeg1NPV+infLeg2NPV;
      ]]></Code>
      <NPV>Value</NPV>
      <Results>
        <Result rename="currentNotional">Notional</Result>
        <Result rename="notionalCurrency">PayCurrency</Result>
      </Results>
    </Script>
  </Script>

  <!-- European Option with Volatility Barrier

      Inputs:

      LongShort               whether own party pays on the FX strike

      CallCcy                 call currency
      PutCcy                  put currency
      CallNotional            call currency notional amount
      PutNotional             put currency notional amount
      Strike                  strike price
      SettlementDate          the settlement date of the option payoff 
      Premium                 the total option premium amount
      PremiumDate             the option premium date
      BarrierLevel            the volatility barrier level
      BarrierType             the type of barrier event; 1 for DownIn or 2 for UpIn
      ValuationSchedule       the realised variance accrual schedule
      Expiry                  the expiry date
      Underlying              the underlying (FX, EQ, COM) index     

      Results:
      RealisedVariance        the variance accrued during the barrier event
      TriggerProbability      probability of the barrier being reached during the life of the trade
      Option                  NPV of the option
  -->
  <Script>
    <Name>VolatilityBarrierOption</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE {CallNotional >= 0}  AND {PutNotional >= 0};
        REQUIRE {BarrierLevel >= 0}  AND {Premium >= 0};

        NUMBER d, realisedVariance, realisedVolatility, currPrice, pay;
        NUMBER prevPrice, payoff, strike, TriggerProbability, knockedIn, premium, expectedN;

        strike = CallNotional/PutNotional;

        FOR d IN (2, SIZE(ValuationSchedule), 1) DO
          currPrice = Underlying(ValuationSchedule[d]);
          prevPrice = Underlying(ValuationSchedule[d-1]);

          realisedVariance = realisedVariance + pow(ln(currPrice/prevPrice), 2);

        END;

        expectedN = SIZE(ValuationSchedule) - 1;
        realisedVolatility = 100 * sqrt((252/expectedN)) * sqrt(realisedVariance);

        IF {BarrierType == 1 AND realisedVolatility <= BarrierLevel} OR 
           {BarrierType == 2 AND realisedVolatility >= BarrierLevel} THEN
              TriggerProbability = 1;
              knockedIn = 1;
              payoff = LongShort * knockedIn * max((strike - Underlying(Expiry)), 0) * PutNotional;
        END;

        premium = PAY(Premium, Expiry, PremiumDate, PayCcy);

        pay = PAY(payoff, Expiry, SettlementDate, PayCcy);

        Option = pay - premium;

      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="RealisedVariance">realisedVariance</Result>
        <Result>TriggerProbability</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>

    <!-- Dual European Binary option with Volatility Barrier

      Inputs:

      LongShort               whether own party pays on the strike
      SettlementAmount        the settlement amount
      SettlementDate          the settlement date of the option payoff 
      Premium                 the total option premium amount
      PremiumDate             the option premium date
      VolBarrierLevel         the realized volatility barrier level
      VolBarrierType          the realized volatility barrier type; 1 for Down-and-in, 2 for Up-and-in
      BarrierLevel            the underlying's barrier level
      BarrierDate             the date of the underlying's barrier event
      BarrierType             the type of underlying barrier; 1 for Down-and-in, 2 for Up-and-in
      ValuationSchedule       the realized variance accrual schedule
      Expiry                  the expiry date
      Underlying              the underlying FX/EQ/COM index

      Results:
      RealisedVariance        the variance accrued during the barrier event
      TriggerProbability      probability of the barrier being reached during the life of the trade
      NPV                     NPV of the option
  -->
  <Script>
    <Name>DualEuroBinaryOption</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[

        NUMBER d, realisedVariance, realisedVolatility, currPrice, pay;
        NUMBER prevPrice, payoff, strike, TriggerProbability, knockedIn, premium, expectedN;

        FOR d IN (2, SIZE(VolSchedule), 1) DO
          currPrice = Underlying(VolSchedule[d]);
          prevPrice = Underlying(VolSchedule[d-1]);

          realisedVariance = realisedVariance + pow(ln(currPrice/prevPrice), 2);

        END;

        expectedN = SIZE(VolSchedule) - 1;
        realisedVolatility = 100 * sqrt((252/expectedN)) * sqrt(realisedVariance);

        IF BarrierType == 1 AND VolBarrierType == 1 THEN
          IF {realisedVolatility <= VolBarrierLevel AND Underlying(BarrierDate) <= BarrierLevel} THEN
            TriggerProbability = 1;
            payoff = SettlementAmount;
          END;
        END;

        IF BarrierType == 2 AND VolBarrierType == 1 THEN
          IF {realisedVolatility <= VolBarrierLevel AND Underlying(BarrierDate) >= BarrierLevel} THEN
            TriggerProbability = 1;
            payoff = SettlementAmount;
          END;
        END;

        IF BarrierType == 1 AND VolBarrierType == 2 THEN
          IF {realisedVolatility >= VolBarrierLevel AND Underlying(BarrierDate) <= BarrierLevel} THEN
            TriggerProbability = 1;
            payoff = SettlementAmount;
          END;
        END;

        IF BarrierType == 2 AND VolBarrierType == 2 THEN
          IF {realisedVolatility >= VolBarrierLevel AND Underlying(BarrierDate) >= BarrierLevel} THEN
            TriggerProbability = 1;
            payoff = SettlementAmount;
          END;
        END;

        premium = PAY(Premium, Expiry, PremiumDate, PayCcy);

        pay = PAY(payoff, Expiry, SettlementDate, PayCcy);

        Option = pay - premium;

      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result rename="RealisedVariance">realisedVariance</Result>
        <Result>TriggerProbability</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>

  <!-- Generic Barrier Option

       Inputs:

       PayoffType                           0 = Vanilla max(0, S-K), 1 = Cash or Nothing
       TransatlanticBarrierType             0 = none, 1 = DonwIn, 2 = UpIn, 3 = DownOut, 4 = UpOut
       TransatlanticBarrierLevel            transatlantic barrier level
       LongShort                            long / short indicator, +1 for long, -1 for short position
       PutCall                              put / call indicator, +1 for call, -1 for put
       Quantity                             quantity for PayoffType = 0 (Vanilla)
       Strike                               the strike for PayoffType = 0 (Vanilla)
       Amount                               amount for PayoffType = 1 (Cash or Nothing)
       PayCurrency                          payment currency
       ExpiryDate                           option expiry date
       SettlementDate                       option settlement date
       Underlying                           the underlying index
       BarrierTypes                         array of barrier types 1 = DonwIn, 2 = UpIn, 3= DownOut, 4 = UpOut
       BarrierLevels                        array of barrier levels
       BarrierRebates                       array of barrier rebates (only for out, must all be zero if at least on KI barrier is defined)
       BarrierRebateCurrencies              array of barrier rebate currencies
       BarrierRebatePayTimes                0 = atHit, 1 = atExpiry (only for out)
       BarrierRebate                        barrier rebate, if at least one KI barrier is defined (should be zero otherwise, use BarrierRebates then)
       BarrierRebateCurrency                barrier rebate currency
       TransatlanticBarrierRebate           transatlantic barrier rebate
       TransatlanticBarrierRebateCurrency   transatlantic barrier rebate currency
       KikoType                             1 = KoAlways, 2 = KoBeforeKi, 3 = KoAfterKi, should be 1 for pure KO or pure KI barrier configs
       BarrierMonitoringDates               list of barrier monitoring dates

       Results:

       Value                                NPV of barrier option
       currentNotional                      current notional
       notionalCurrency                     current notional currency
  -->

  <Script>
    <Name>GenericBarrierOption</Name>
    <ProductTag>SingleAssetOptionBwd({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        REQUIRE PayoffType == 0 OR PayoffType == 1;
        REQUIRE TransatlanticBarrierType >= 0 AND TransatlanticBarrierType <= 4;
        REQUIRE SIZE(BarrierTypes) == SIZE(BarrierLevels);
        REQUIRE SIZE(BarrierTypes) == SIZE(BarrierRebates);
        REQUIRE SIZE(BarrierTypes) == SIZE(BarrierRebateCurrencies);
        REQUIRE SIZE(BarrierTypes) == SIZE(BarrierRebatePayTimes);

        NUMBER KnockedIn, KnockedOut, Active, rebate, TransatlanticActive;
        NUMBER U, i, d, currentNotional;

        FOR d IN (1, SIZE(BarrierMonitoringDates), 1) DO

          U = Underlying(BarrierMonitoringDates[d]);

          FOR i IN (1, SIZE(BarrierTypes), 1) DO

            IF {BarrierTypes[i] == 1 AND U <= BarrierLevels[i]} OR
               {BarrierTypes[i] == 2 AND U >= BarrierLevels[i]} THEN
	       IF KnockedOut == 0 THEN
                 KnockedIn = 1;
	       END;
            END;

            IF {BarrierTypes[i] == 3 AND U < BarrierLevels[i]} OR
               {BarrierTypes[i] == 4 AND U > BarrierLevels[i]} THEN
               IF KikoType == 1 OR { KikoType == 2 AND KnockedIn == 0 } OR { KikoType == 3 AND KnockedIn == 1 } THEN
                 IF KnockedOut == 0 THEN
                   IF BarrierRebatePayTimes[i] == 0 THEN
                     rebate = PAY( LongShort * BarrierRebates[i], BarrierMonitoringDates[d], BarrierMonitoringDates[d], BarrierRebateCurrencies[i] );
                   ELSE
                     rebate = PAY( LongShort * BarrierRebates[i], BarrierMonitoringDates[d], SettlementDate, BarrierRebateCurrencies[i] );
                   END;
                 END;
                 KnockedOut = 1;
               END;
            END;

          END;

        END;

        Active = 1;
        FOR i IN (1, SIZE(BarrierTypes),1) DO
          IF BarrierTypes[i] == 1 OR BarrierTypes[i] == 2 THEN
            Active = 0;
          END;
        END;

        Active = max(Active, KnockedIn) * (1 - KnockedOut);

	IF BarrierRebate != 0 THEN
	  rebate = (1 - Active) * PAY( LongShort * BarrierRebate, SettlementDate, SettlementDate, BarrierRebateCurrency );
	END;

	TransatlanticActive = 1;
        IF { TransatlanticBarrierType == 1 AND Underlying(ExpiryDate) >= TransatlanticBarrierLevel  } OR
           { TransatlanticBarrierType == 2 AND Underlying(ExpiryDate) <= TransatlanticBarrierLevel  } OR
           { TransatlanticBarrierType == 3 AND Underlying(ExpiryDate) < TransatlanticBarrierLevel } OR
           { TransatlanticBarrierType == 4 AND Underlying(ExpiryDate) > TransatlanticBarrierLevel } THEN
          TransatlanticActive = 0;
        END;

	rebate = rebate + Active * (1 - TransatlanticActive) * PAY( TransatlanticBarrierRebate, SettlementDate, SettlementDate, TransatlanticBarrierRebateCurrency );

        IF PayoffType == 0 THEN
	  value = Active * TransatlanticActive * PAY( LongShort * Quantity * max(0, PutCall * (Underlying(ExpiryDate) - Strike)), ExpiryDate, SettlementDate, PayCurrency ) +
                  rebate;
	ELSE
	  value = Active * TransatlanticActive * PAY( LongShort * Amount, ExpiryDate, SettlementDate, PayCurrency ) +
                  rebate;
	END;

        IF PayoffType == 0 THEN
          currentNotional = Quantity * Strike;
        ELSE
          currentNotional = Amount;
        END;
        ]]></Code>
      <NPV>value</NPV>
      <Results>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCurrency</Result>
	<Result>Active</Result>
	<Result>TransatlanticActive</Result>
      </Results>
      <CalibrationSpec>
        <Calibration>
          <Index>Underlying</Index>
          <Strikes>
            <Strike>Strike</Strike>
            <Strike>BarrierLevels</Strike>
          </Strikes>
        </Calibration>
      </CalibrationSpec>
    </Script>
    <Script purpose="FD">
      <Code><![CDATA[
        REQUIRE PayoffType == 0 OR PayoffType == 1;
        REQUIRE TransatlanticBarrierType >= 0 AND TransatlanticBarrierType <= 4;
        REQUIRE SIZE(BarrierTypes) == SIZE(BarrierLevels);
        REQUIRE SIZE(BarrierTypes) == SIZE(BarrierLevels);
        REQUIRE SIZE(BarrierTypes) == SIZE(BarrierRebates);
        REQUIRE SIZE(BarrierTypes) == SIZE(BarrierRebateCurrencies);
        REQUIRE SIZE(BarrierTypes) == SIZE(BarrierRebatePayTimes);

        NUMBER V, V_V, V_NA, V_KI, V_KO, V_KIKO, V_KOKI;
        NUMBER R, R_V, R_NA, R_KI, R_KO, R_KIKO, R_KOKI, rebate;
        NUMBER U, i, d, currentNotional;

        IF PayoffType == 0 THEN
          V = PAY( LongShort * Quantity * max(0, PutCall * (Underlying(ExpiryDate) - Strike)), ExpiryDate, SettlementDate, PayCurrency );
        ELSE
          V = PAY( LongShort * Amount, ExpiryDate, SettlementDate, PayCurrency );
        END;

        IF { TransatlanticBarrierType == 1 AND Underlying(ExpiryDate) >= TransatlanticBarrierLevel  } OR
           { TransatlanticBarrierType == 2 AND Underlying(ExpiryDate) <= TransatlanticBarrierLevel  } OR
           { TransatlanticBarrierType == 3 AND Underlying(ExpiryDate) < TransatlanticBarrierLevel } OR
           { TransatlanticBarrierType == 4 AND Underlying(ExpiryDate) > TransatlanticBarrierLevel } THEN
          V = PAY( LongShort * TransatlanticBarrierRebate, ExpiryDate, SettlementDate, TransatlanticBarrierRebateCurrency );
        END;

        V_V = V;
        V_NA = V;
        V_KI = V * 0;
        V_KO = V * 0;
        V_KIKO = V * 0;
        V_KOKI = V * 0;

        R = PAY( LongShort * BarrierRebate, ExpiryDate, SettlementDate, BarrierRebateCurrency);
        R_V = R;
        R_NA = R;
        R_KI = R * 0;
        R_KO = R * 0;
        R_KIKO = R * 0;
        R_KOKI = R * 0;

	rebate = R * 0;

        FOR i IN (1, SIZE(BarrierTypes), 1) DO
          IF BarrierTypes[i] == 1 OR BarrierTypes[i] == 2 THEN
            V_V = V_V * 0;
	    R_V = R_V * 0;
          END;
        END;

        FOR d IN (SIZE(BarrierMonitoringDates), 1, -1) DO

          IF BarrierMonitoringDates[d] >= TODAY THEN
            V_V = NPV(V_V, BarrierMonitoringDates[d]);
            V_NA = NPV(V_NA, BarrierMonitoringDates[d]);
            V_KI = NPV(V_KI, BarrierMonitoringDates[d]);
            V_KO = NPV(V_KO, BarrierMonitoringDates[d]);
            V_KIKO = NPV(V_KIKO, BarrierMonitoringDates[d]);
            V_KOKI = NPV(V_KOKI, BarrierMonitoringDates[d]);
            R_V = NPV(R_V, BarrierMonitoringDates[d]);
            R_NA = NPV(R_NA, BarrierMonitoringDates[d]);
            R_KI = NPV(R_KI, BarrierMonitoringDates[d]);
            R_KO = NPV(R_KO, BarrierMonitoringDates[d]);
            R_KIKO = NPV(R_KIKO, BarrierMonitoringDates[d]);
            R_KOKI = NPV(R_KOKI, BarrierMonitoringDates[d]);
	    rebate = NPV(rebate, BarrierMonitoringDates[d]);
          END;

          U = Underlying(BarrierMonitoringDates[d]);

          FOR i IN (1, SIZE(BarrierTypes), 1) DO

            IF {BarrierTypes[i] == 1 AND U <= BarrierLevels[i]} OR
               {BarrierTypes[i] == 2 AND U >= BarrierLevels[i]} THEN
              V_KIKO = V_KO + V_KIKO + V_KOKI;
              V_KOKI = V_KOKI * 0;
              V_KI = V_NA + V_KI;
              V_KO = V_KO * 0;
              V_NA = V_NA * 0;
              V_V = V_KI;
              IF KikoType == 2 THEN
                V_V = V_V + V_KIKO;
              END;
              R_KIKO = R_KO + R_KIKO + R_KOKI;
              R_KOKI = R_KOKI * 0;
              R_KI = R_NA + R_KI;
              R_KO = R_KO * 0;
              R_NA = R_NA * 0;
              R_V = R_KI;
              IF KikoType == 2 THEN
                R_V = R_V + R_KIKO;
              END;
            END;

            IF {BarrierTypes[i] == 3 AND U < BarrierLevels[i]} OR
               {BarrierTypes[i] == 4 AND U > BarrierLevels[i]} THEN
              V_KOKI = V_KI + V_KOKI + V_KIKO;
              V_KIKO = V_KIKO * 0;
              V_KO = V_NA + V_KO;
              V_KI = V_KI * 0;
              V_NA = V_NA * 0;
              IF KikoType == 1 OR KikoType == 2 THEN
                V_V = V_V * 0;
              END;
              R_KOKI = R_KI + R_KOKI + R_KIKO;
              R_KIKO = R_KIKO * 0;
              R_KO = R_NA + R_KO;
              R_KI = R_KI * 0;
              R_NA = R_NA * 0;
              IF KikoType == 1 OR KikoType == 2 THEN
                R_V = R_V * 0;
              END;
              IF BarrierRebatePayTimes[i] == 0 THEN
                rebate = PAY( LongShort * BarrierRebates[i], BarrierMonitoringDates[d], BarrierMonitoringDates[d], BarrierRebateCurrencies[i] );
              ELSE
                rebate = PAY( LongShort * BarrierRebates[i], BarrierMonitoringDates[d], SettlementDate, BarrierRebateCurrencies[i] );
              END;
            END;

          END;

        END;

	rebate = rebate + ( PAY( LongShort * BarrierRebate, TODAY, SettlementDate, BarrierRebateCurrency ) - R_V );
        value = V_V + rebate;

        IF PayoffType == 0 THEN
          currentNotional = Quantity * Strike;
        ELSE
          currentNotional = Amount;
        END;
        ]]></Code>
      <NPV>value</NPV>
      <Results>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCurrency</Result>
      </Results>
      <CalibrationSpec>
        <Calibration>
          <Index>Underlying</Index>
          <Strikes>
            <Strike>Strike</Strike>
            <Strike>BarrierLevels</Strike>
          </Strikes>
        </Calibration>
      </CalibrationSpec>
    </Script>
  </Script>
  <!-- Asset Linked Cliquet Option (FX Crossed) - EQ, FX or COMM

      Inputs:
      Nominal               the notional amount
      LongShort             whether own party is paying on the value of the basket (long) and
                              receiving on the value of the linked CMS underlying or receiving
                              on the value of the basket (short) and paying on the value of the
                              linked CMS underlying
      PayCurrency           the settlement currency
      ValuationDates        the dates when the basket is valued and cashflows are determined
      PaymentDates          corresponding dates for each date in ValuationDates
      Underlyings           basket underlyings
      FXConversions         FX indices to be used for determining FX cross factors
      Weights               weights to be applied to each underlying in Underlyings
      LinkedUnderlying      (CMS) underlying
      PayStrike             spread amount on the LinkedUnderlying fixing paid by option buyer,
                              or strike on the value of the basket underlying
      RecStrike             spread amount on the LinkedUnderlying fixing paid by option seller,
                              or strike on the value of the basket underlying

      Results:
      Option                NPV of the cliquet option
  -->
  <Script>
    <Name>AssetLinkedCliquetOption</Name>
    <ProductTag>IrHybrid({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        NUMBER B, r, i, j, currentNotional, value, sumOfWeights;
        FOR j IN (1, SIZE(Weights), 1) DO
          REQUIRE abs(Weights[j]) <= 1;
          sumOfWeights = sumOfWeights + Weights[j];
        END;
        REQUIRE sumOfWeights == 1;

        FOR i IN (2, SIZE(ValuationDates), 1) DO
          B = 0;
          FOR j IN (1, SIZE(Underlyings), 1) DO
            r = Underlyings[j](ValuationDates[i]) / Underlyings[j](ValuationDates[i-1])
                * FXConversions[j](ValuationDates[i-1]) / FXConversions[j](ValuationDates[i]);
            B = B + Weights[j] * r;
          END;

          value = value + PAY( Nominal * 
                               (max( RecStrike + LinkedUnderlying(ValuationDates[i-1]) - B, 0) -
                                max( B - PayStrike - LinkedUnderlying(ValuationDates[i-1]), 0)),
                               ValuationDates[i], PaymentDates[i], PayCurrency );
        END;

        Option = LongShort * value;
        currentNotional = Nominal;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCurrency</Result>
      </Results>
    </Script>
  </Script>
  <!-- Ladder Lock-In Option

      Inputs:
      LongShort             own party position
      Quantity              the notional amount
      PutCall               +1 for Call, -1 for Put
      PremiumAmount         the total option premium amount
      PremiumDate           the option premium pay date
      Underlying            the underlying FX/EQ/COM index
      LockInLevels          ladder lock-in price levels
      ObservationSchedule   observation schedule for determining price lock-ins
      Strike                the option strike price
      SettlementDate        the settlement date of the option payoff 
      PayCcy                the settlement currency

      Results:
      Option                npv of the option
      currentNotional       current notional
      notionalCurrency      notional currency
  -->
  <Script>
    <Name>LadderLockInOption</Name>
    <ProductTag>SingleAssetOption({AssetClass})</ProductTag>
    <Script>
      <Code><![CDATA[
        NUMBER sortedLevels[SIZE(LockInLevels)], price;
        NUMBER payoff, premium, lockIn, d, l, lastLockIn;

        FOR l IN (1, SIZE(LockInLevels), 1) DO
          sortedLevels[l] = LockInLevels[l];
        END;
        SORT(sortedLevels);

        IF PutCall == 1 THEN
          REQUIRE Strike <= sortedLevels[1];
        ELSE
          REQUIRE Strike >= sortedLevel[SIZE(sortedLevels)];
        END;

        lastLockIn = Strike;

        FOR d IN (1, SIZE(ObservationSchedule), 1) DO
          price = Underlying(ObservationSchedule[d]);

          FOR l IN (1, SIZE(sortedLevels), 1) DO
            IF PutCall*price >= PutCall*sortedLevels[l] THEN
              lockIn = sortedLevels[l];
              IF PutCall*lockIn >= PutCall*lastLockIn THEN
                lastLockIn = lockIn;
                payoff = PAY(Quantity * PutCall * (lastLockIn - Strike),
                             ObservationSchedule[d],
                             SettlementDate, PayCcy);
              END;
            END;
          END;
        END;

        premium = PAY(Premium, PremiumDate, PremiumDate, PayCcy);

        Option = LongShort * (payoff - premium);

        NUMBER currentNotional;
        currentNotional = Quantity * Strike;
      ]]>
      </Code>
      <NPV>Option</NPV>
      <Results>
        <Result>currentNotional</Result>
        <Result rename="notionalCurrency">PayCcy</Result>
      </Results>
    </Script>
  </Script>
  <Script>
    <Name>LapseHedgeSwap</Name>
    <ProductTag>Model_SingleUnderlyingIrOption</ProductTag>
    <Script>
      <Code><![CDATA[
      REQUIRE {SIZE(ValuationDates) == SIZE(IMS)};
      REQUIRE {SIZE(ValuationDates) == SIZE(PHFV)};
      REQUIRE {SIZE(ValuationDates) == SIZE(Penalty)};
      REQUIRE {SIZE(ExerciseDates) == SIZE(ExitPrice)};
      REQUIRE {SIZE(ExerciseDates) == SIZE(ExitFee)};
      NUMBER underlyingNPV, upfrontNPV, callNPVReceiver, callNPVPayer;
      NUMBER estimatedReceiverCallPayoff, estimatedPayerCallPayoff;
      NUMBER dN[SIZE(ValuationDates)], dM[SIZE(ExerciseDates)], famc[SIZE(ValuationDates)], lapsRiskHedgeAmount[SIZE(ValuationDates)];
      NUMBER lastValuationDateIndex, lastKnownDeltaNIndex, payDateIndex;
      NUMBER i, j, k;
      NUMBER legNo;
      legNo = 1;
      lastValuationDateIndex  = SIZE(ValuationDates)-1;
      underlyingNPV = 0;
      callNPVReceiver = 0;
      callNPVPayer = 0;
      upfrontNPV = 0;
      IF SIZE(InitialExchangeDate) == 1 AND InitialExchangeDate[1] > TODAY THEN
        upfrontNPV = LOGPAY(Notional * InitialExchangeFee, InitialExchangeDate[1],InitialExchangeDate[1], SettlementCurrency, legNo, InitialExchange);
      END;
      IF SIZE(deltaN) < SIZE(ValuationDates) THEN
        FOR i IN (1, SIZE(ValuationDates), 1) DO
          IF i <= SIZE(deltaN) THEN
            dN[i] = deltaN[i];
          ELSE
            dN[i] = deltaN[SIZE(deltaN)];
          END;
        END;
      ELSE
        dN = deltaN;
      END;
      FOR i IN (1, SIZE(ValuationDates)-1, 1) DO
        famc[i] = 0;
        FOR j IN (i + 1, SIZE(ValuationDates), 1) DO
          famc[i] = famc[i] + Tau * Fee * IMS[j] * PHFV[j] * (1.0 + Tau * IRS) / pow((1 + IRS + IRSSpread), dcf(Paydaycounter, ValuationDates[i], ValuationDates[j]));
        END;
        lapsRiskHedgeAmount[i] = Notional * dN[i] * LapseHedgePercentage * (famc[i]-Penalty[i]*PHFV[i+1]);
      END;
      FOR i IN (SIZE(ExerciseDates), 1, -1) DO
        IF ExerciseDates[i] > TODAY THEN
          dM[i] = 0;
          lastKnownDeltaNIndex = DATEINDEX(ExerciseDates[i], ValuationDates, GT);
          REQUIRE {lastKnownDeltaNIndex <= SIZE(ValuationDates)};
          IF lastKnownDeltaNIndex > 1 THEN
            FOR j IN (1, lastKnownDeltaNIndex - 1, 1) DO
              dM[i] = dM[i] + dN[j];
            END;
          END;
          FOR j IN (lastValuationDateIndex, 1, -1) DO
            payDateIndex = DATEINDEX(ValuationDates[j], PaymentDates, GEQ);
            REQUIRE {payDateIndex <= SIZE(PaymentDates)};
            IF PaymentDates[payDateIndex] > ExerciseDates[i] THEN
              underlyingNPV = underlyingNPV + LOGPAY(max(lapsRiskHedgeAmount[j],0), ValuationDates[j], PaymentDates[payDateIndex], SettlementCurrency, legNo, HedgeAmount);
              lastValuationDateIndex = lastValuationDateIndex - 1; 
            END;
          END;
          
          estimatedReceiverCallPayoff = Notional * (1-dM[i]) * ExitPrice[i] - NPV(underlyingNPV, ExerciseDates[i]);
          IF estimatedReceiverCallPayoff > 0 AND estimatedReceiverCallPayoff > NPV(callNPVReceiver, ExerciseDates[i], estimatedReceiverCallPayoff > 0) THEN
            callNPVReceiver = Notional * (1-dM[i]) * ExitPrice[i] - underlyingNPV;
            callNPVPayer = 0;
          END; 

          estimatedPayerCallPayoff = NPV(underlyingNPV, ExerciseDates[i]) - Notional * ((1-dM[i]) * ExitPrice[i] + ExitFee[i]);
          IF estimatedPayerCallPayoff > 0 AND estimatedPayerCallPayoff > NPV(callNPVPayer, ExerciseDates[i], estimatedPayerCallPayoff > 0) THEN
            callNPVPayer = underlyingNPV - Notional * ((1-dM[i]) * ExitPrice[i] + ExitFee[i]);
            callNPVReceiver = 0;
          END; 
          
        END;
      END;
      FOR j IN (lastValuationDateIndex, 1, -1) DO
        payDateIndex = DATEINDEX(ValuationDates[j], PaymentDates, GEQ);
        REQUIRE {payDateIndex <= SIZE(PaymentDates)};
        IF PaymentDates[payDateIndex] > TODAY THEN
          underlyingNPV = underlyingNPV + LOGPAY(max(lapsRiskHedgeAmount[j], 0), ValuationDates[j], PaymentDates[payDateIndex], SettlementCurrency, legNo, HedgeAmount);
        END;
      END;

      npv = Payer * (upfrontNPV - underlyingNPV - callNPVReceiver + callNPVPayer);
      ]]>
      </Code>
      <NPV>npv</NPV>
      <Results>
        <Result rename="underlyingNPV">underlyingNPV</Result>
        <Result rename="optionNPVReceiver">callNPVReceiver</Result>
        <Result rename="optionNPVPayer">callNPVPayer</Result>
        <Result rename="deltaN">dN</Result>
        <Result rename="deltaM">dM</Result>
        <Result rename="FAMC">famc</Result>
        <Result rename="Penalty">Penalty</Result>
        <Result rename="PHFV">PHFV</Result>
        <Result rename="IMS">IMS</Result>
        <Result rename="IRS">IRS</Result>
        <Result rename="IRS_Spread">IRSSpread</Result>
        <Result rename="PayerSwap">Payer</Result>
        <Result rename="InitialExchangeNPV">upfrontNPV</Result>
        <Result rename="lapsRiskHedgeAmount">lapsRiskHedgeAmount</Result>
        <Result rename="valuationDates">ValuationDates</Result>
        <Result rename="paymentDates">PaymentDates</Result>
        <Result rename="exerciseDates">ExerciseDates</Result>
      </Results>
    </Script>
  </Script>
  <Script>
    <Name>LPISwap</Name>
    <Script>
      <Code><![CDATA[
      NUMBER FixedLegNpv, FloatLegNpv;
      NUMBER i,j;
      NUMBER zeroCouponRate;
      NUMBER notional;
      NUMBER forwardCPI[SIZE(FloatLegSchedule)];
      NUMBER LPI[SIZE(FloatLegSchedule)-1];
      NUMBER floatingRate[SIZE(FloatLegSchedule)];
      NUMBER capPrice[SIZE(FloatLegSchedule)-1];
      NUMBER floorPrice[SIZE(FloatLegSchedule)-1];
      NUMBER cappedFlooredRate[SIZE(FloatLegSchedule)-1];
      NUMBER floatAmount[SIZE(FloatLegSchedule)];
      NUMBER FixedAmount[SIZE(FixedLegSchedule)-1];
      NUMBER FixedRate[SIZE(FixedLegSchedule)-1];
      NUMBER FloatNotionals[SIZE(FloatLegSchedule)-1];
      NUMBER NumberOfInfPayments;
      REQUIRE SIZE(FixedLegSchedule) >= 2;
      REQUIRE SIZE(FloatLegSchedule) >= 2;
      
      FOR i IN(2, SIZE(FixedLegSchedule), 1) DO
        FixedRate[i-1] = pow(1+ZeroCouponRate, dcf(FixedDayCounter, FixedLegSchedule[1], FixedLegSchedule[i]))-1.0;
        IF FixedLegSchedule[i] > TODAY THEN
          
          notional = FixedLegNotionals[1];
          IF SIZE(FixedLegNotionals)>1 THEN
            notional = FixedLegNotionals[i-1];
          END;
          FixedAmount[i-1] = -1 * PayFixLeg * FixedRate[i-1] * notional;
          FixedLegNpv = FixedLegNpv +LOGPAY(FixedAmount[i-1],
                              TODAY, FixedLegSchedule[i], PayCurrency, 1, ZeroCoupon);
        END;
      END;
      
      forwardCPI[1] = CPIIndex(FixingSchedule[1]);
      floatingRate[1] = 1.0;
      FOR i IN(2, SIZE(FloatLegSchedule), 1) DO
        IF FloatLegSchedule[i] >= FloatLegFirstPaymentDate THEN
          NumberOfInfPayments = NumberOfInfPayments + 1;
          IF SIZE(FloatLegNotional) == 1 THEN
            FloatNotionals[i-1] = FloatLegNotional[1];
          ELSE
            FloatNotionals[i-1] = FloatLegNotional[NumberOfInfPayments];
          END;
        END;
        forwardCPI[i] = CPIIndex(FixingSchedule[i]);
        LPI[i-1] = forwardCPI[i] / forwardCPI[i-1] - 1.0;
        capPrice[i-1] = max(0.0, LPI[i-1] - Cap);
        floorPrice[i-1] = max(0.0, Floor - LPI[i-1]);
        cappedFlooredRate[i-1] = LPI[i-1] - capPrice[i-1] + floorPrice[i-1];
        floatingRate[i] = floatingRate[i-1] * (cappedFlooredRate[i-1] + 1.0);
        IF FloatLegSchedule[i] > TODAY AND FloatLegSchedule[i] >= FloatLegFirstPaymentDate THEN
          floatAmount[i] = PayFixLeg * (floatingRate[i] - 1.0) * FloatNotionals[i-1];
          
          FloatLegNpv = FloatLegNpv+LOGPAY(floatAmount[i],
            FloatLegSchedule[i], FloatLegSchedule[i], PayCurrency, 2, Inflation);
        END;
      END;
      
      Value = FloatLegNpv+FixedLegNpv;
      ]]></Code>
      <NPV>Value</NPV>
      <Results>
        <Result rename="fixedSchedule">FixedLegSchedule</Result>
        <Result rename="fixedLegNotionals">FixedLegNotionals</Result>
        <Result rename="fixedZeroCouponRate">ZeroCouponRate</Result>
        <Result rename="fixedFixedRate">FixedRate</Result>
        <Result rename="fixedLegCashflows">FixedAmount</Result>
        <Result rename="FixedLegNpv">FixedLegNpv</Result>
        <Result rename="infLegFloatLegSchedule">FloatLegSchedule</Result>
        <Result rename="infLegFloatLegFixingSchedule">FixingSchedule</Result>
        <Result rename="infLegNotionals">FloatLegNotional</Result>
        <Result rename="infLegCPIFixings">forwardCPI</Result>
        <Result rename="infLegLPIRates">LPI</Result>
        <Result rename="infLegcapletPrice">capPrice</Result>
        <Result rename="infLegfloorletPrice">floorPrice</Result>
        <Result rename="infLegcappedFlooredRate">cappedFlooredRate</Result>
        <Result rename="infLegFloatingRate">floatingRate</Result>
        <Result rename="infLegFloatingCashflows">floatAmount</Result>
        <Result rename="infLegNpv">FloatLegNpv</Result>
        <Result rename="infLegCashflowNotionals">FloatNotionals</Result>
      </Results>
    </Script>
  </Script>
</ScriptLibrary>
