0

I am currently trying to create a model where the results of the objective function when applied to all rows in the problem will be used for recommendation purposes.

    !********************************************************************************
! 1. Import Modules
! 2. This part is used to define result format and directories
!********************************************************************************

model "savings portfolio"
    uses "mmxprs", "mmsheet"

parameters
    CAPITAL = 100000000
    INPUTFILE = "C:/Users/Admin/Downloads/201031_SAVINGS_ACCOUNTS3_v1.0.xlsx"! 3. File directory for data file
    RESULTFILE = "C:/Users/Admin/Downloads/201031_SAVINGRESULTS3_v1.0.xlsx"! 4. File directory for result file
end-parameters

!********************************************************************************
! 5. Declare variable types
! 6. Variable types as well as the mpvar must be declared first before importing data
!********************************************************************************
declarations
    INDEX: set of integer
    PRODUCTNAME: array(INDEX) of string
    MONTH: array(INDEX) of integer
    MONTH2: array(INDEX) of integer
    MININVEST: array(INDEX) of integer
    MININVEST2: array(INDEX) of integer
    PRINCIPAL: array(INDEX) of mpvar  ! 7. mpvar refers to the variable that is to be optimized
    PRINCIPAL2: array(INDEX) of mpvar
    INTEREST: array(INDEX) of real
    INTEREST2: array(INDEX) of real
end-declarations


!********************************************************************************
! 8. "XPRS_VERBOSE" settings: Enable to view problem statistics
!********************************************************************************
setparam("XPRS_VERBOSE", true)

!********************************************************************************
! 9. Read data
!********************************************************************************
initializations from "mmsheet.xlsx:"+INPUTFILE
    [PRODUCTNAME,INTEREST,MONTH,MININVEST,INTEREST2,MONTH2,MININVEST2] as "[Sheet1$A2:H501]" !10.Select data from excel sheet excluding headers, must include index column
end-initializations

!********************************************************************************
! 11. Calculating YEAR
!********************************************************************************
forall(i in INDEX) YEAR(i):=MONTH(i)/12
forall(i in INDEX) YEAR2(i):=MONTH2(i)/12

!********************************************************************************
! 12. Objective Function
!********************************************************************************
SUMDIFF:=sum(i in INDEX) (PRINCIPAL(i)*(1+(INTEREST(i)/MONTH(i)))^(MONTH(i)*getsol(YEAR(i))) - PRINCIPAL2(i)*(1+(INTEREST2(i)/MONTH2(i)))^(MONTH2(i)*getsol(YEAR(i))))

!********************************************************************************
! 13. Constraints
!********************************************************************************
sum(i in INDEX) PRINCIPAL(i) = CAPITAL
sum(i in INDEX) PRINCIPAL2(i) = CAPITAL
forall(i in INDEX) PRINCIPAL(i) >= MININVEST(i)
forall(i in INDEX) PRINCIPAL2(i) >= MININVEST2(i)
SUMDIFF >= 0

!********************************************************************************
! 14. Optimization method
!********************************************************************************
maximize(SUMDIFF)

!********************************************************************************
! 15. Calculate variables
!********************************************************************************
forall(i in INDEX) PRINSTANDIFF(i):=(getsol(PRINCIPAL(i))*(1+(INTEREST(i)/MONTH(i)))^(MONTH(i)*getsol(YEAR(i)))) - (getsol(PRINCIPAL2(i))*(1+(INTEREST2(i)/MONTH2(i)))^(MONTH2(i)*getsol(YEAR(i))))
forall(i in INDEX) INVESTMENT1(i):=getsol(PRINCIPAL(i))
forall(i in INDEX) INVESTMENT2(i):=getsol(PRINCIPAL2(i))


initializations to "mmsheet.xlsx:"+RESULTFILE
    [PRODUCTNAME,INVESTMENT1,INVESTMENT2,PRINSTANDIFF] as "grow;[Sheet1$A2:E2]"
end-initializations

end-model

The purpose of this model is to use the values in PRINSTANDIFF to decide which option to invest in. If the resulting value is positive, Option 1 (INVESTMENT1) is the better option whereas if the value is negative, Option 2 (INVESTMENT2) is the better option.

My question is, is there a way to control the number of positive values and negative values I can get from the model via constraints? I would ideally like to keep the number of positives and negatives at a minimum ratio of 6:4 and a maximum ratio of 7:3.

Daniel Junglas
  • 5,830
  • 1
  • 5
  • 22
  • You could introduce binary helper variables in the model that are 1 if the PRINTSTANDIFF value is positive and 0 if it is negative (what happens if the value is exactly 0?). With these variables you formulate the desired constraints. – Daniel Junglas Feb 04 '21 at 09:04

1 Answers1

0

Elaborating on my comment and turning it into an answer: Tasks like the one you describe can be achieved by indicator constraints.

The idea is to introduce binary helper variables that are 1 if an expression is positive and 0 if it is negative:

declarations
    SIGN: array(INDEX) of mpvar
end-declarations
forall(i in INDEX) SIGN(i) is_binary

and then connect those with the sign of an expression via indicator constraints

forall(i in INDEX) indicator(1, SIGN(i), EXPR(i) >= 0)
forall(i in INDEX) indicator(-1, SIGN(i), EXPR(i) <= 0)

With this, SIGN(i) is 1 if EXPR(i) is non-negative and SIGN(i) is 0 if EXPR(i) is non-positive.

Now you can write the number of positive expressions as sum(i in INDEX) SIGN(i) and the number of negative expressions as sum(i in INDEX) (1 - SIGN(i)). Thus a ratio on the two can be enforced via

4 * sum(i in INDEX) SIGN(i) >= 6 * sum(i in INDEX)(1 - SIGN(i)) ! minimum ratio: sum_of_positive/sum_of_negative >= 6/4
3 * sum(i in INDEX) SIGN(i) <= 7 * sum(i in INDEX)(1 - SIGN(i)) ! maximum ratio: sum_of_positive/sum_of_negative <= 7/3

The only problem left is to define EXPR(i) appropriately as an expression in mpvars. This may be an issue here since it seems to involve some non-linearities.

Daniel Junglas
  • 5,830
  • 1
  • 5
  • 22