R optimisation max buy/sell dependent on stock level

风格不统一 提交于 2020-06-01 05:03:50

问题


I would like to find a solution to an optimisation problem. The aim is to maximise profit by buying for low price and selling for a higher one. There are constraints such as maximum stock level, and max buy/sell number of units. Moreover, sell and buy limits depend on the inventory levels. I have asked a similar question albeit without the last condition here R optimisation buy sell.

Here is an example:

price = c(12, 11, 12, 13, 16, 17, 18, 17, 18, 16, 17, 13)
capacity = 25
max_units_buy_30 = 4 # when inventory level is lower then 30% it is possible to buy 0 to 4 units
max_units_buy_65 = 3 # when inventory level is between 30% and 65% it is possible to buy 0 to 3 units
max_units_buy_100 = 2 # when inventory level is between 65% and 100% it is possible to buy 0 to 2 units
max_units_sell_30 = 4 # when inventory level is lower then 30% it is possible to sell 0 to 4 units
max_units_sell_70 = 6 # when inventory level is between 30% and 70% it is possible to sell 0 to 6 units
max_units_sell_100 = 8 # when inventory level is between 70% and 100% it is possible to sell 0 to 8 units

回答1:


There is a lot going on here.

  1. Description

There seems to be a problem in the description. "The max sell/price is dependent on the stock level." This seems to be wrong. From the data, it looks like the price is constant, but rather sell and buy limits depend on the inventory levels.

  1. Time

It is important to get the timing right. Usually, we look at buy and sell as things that happen during period t (we call them flow variables). inv is a stock variable, and is measured at the end of period t. To say that sell[t] and buy[t] depend on inv[t] is a bit strange (we are going backward in time). Of course, we can model it and solve it (we solve as simultaneous equations, so we can do these things). But, it may not make sense in the real world. Probably we should look at inv[t-1] in order to change buy[t] and sell[t].

  1. Segmenting inventory levels.

We need to split inventory levels into segments. We have the following segments:

0%-30%
30%-65%
65%-70%
70%-100%

we associate a binary variable with each segment:

inventory in [0%-30%]  <=> δ[1,t] = 1, all other zero
             [30%-65%]     δ[2,t] = 1 
             [65%-70%]     δ[3,t] = 1 
             [70%-100%]    δ[4,t] = 1 

Because we need to do this for all time periods, we slap on an extra index t. Warning: we will associate δ[k,t] with the inventory at the beginning of period t, i.e. inv[t-1]. We can link δ[k,t] to inv[t-1] by changing lower- and upper bounds depending on in which segment we are.

  1. Bounds on buy/sell

Similar to bounds on the inventory, we have the following upper bounds on buy and sell:

     segment     buy   sell
     0%-30%       4     4 
     30%-65%      3     6
     65%-70%      2     6
     70%-100%     2     8

The first step is to develop a mathematical model. There is too much going on here that we can immediately code things up. The mathematical model is our "design". So here we go:

With this, we can develop some R code. Here we use CVXR as a modeling tool and GLPK as an MIP solver.

> library(CVXR)
> 
> # data
> price = c(12, 11, 12, 13, 16, 17, 18, 17, 18, 16, 17, 13)
> capacity = 25
> max_units_buy = 4
> max_units_sell = 8
> 
> # capacity segments
> s <- c(0,0.3,0.65,0.7,1)
> 
> # corresponding lower and upper bounds
> invlb <- s[1:(length(s)-1)] * capacity
> invlb
[1]  0.00  7.50 16.25 17.50
> invub <- s[2:length(s)] * capacity
> invub
[1]  7.50 16.25 17.50 25.00
> 
> buyub <- c(4,3,2,2)
> sellub <- c(4,6,6,8)
> 
> # number of time periods
> NT <- length(price)
> NT
[1] 12
> 
> # number of capacity segments
> NS <- length(s)-1
> NS
[1] 4
> 
> # Decision variables
> inv = Variable(NT,integer=T)
> buy = Variable(NT,integer=T)
> sell = Variable(NT,integer=T)
> delta = Variable(NS,NT,boolean=T)
> 
> # Lag operator
> L = cbind(rbind(0,diag(NT-1)),0)
> 
> # optimization model
> problem <- Problem(Maximize(sum(price*(sell-buy))),
+                    list(inv == L %*% inv + buy - sell,
+                         sum_entries(delta,axis=2)==1, 
+                         L %*% inv >= t(delta) %*% invlb,
+                         L %*% inv <= t(delta) %*% invub,
+                         buy <= t(delta) %*% buyub,
+                         sell <= t(delta) %*% sellub,
+                         inv >= 0, inv <= capacity,
+                         buy >= 0, sell >= 0))
> result <- solve(problem,verbose=T)
GLPK Simplex Optimizer, v4.47
120 rows, 84 columns, 369 non-zeros
      0: obj =  0.000000000e+000  infeas = 1.200e+001 (24)
*    23: obj =  0.000000000e+000  infeas = 0.000e+000 (24)
*    85: obj = -9.875986758e+001  infeas = 0.000e+000 (2)
OPTIMAL SOLUTION FOUND
GLPK Integer Optimizer, v4.47
120 rows, 84 columns, 369 non-zeros
84 integer variables, 48 of which are binary
Integer optimization begins...
+    85: mip =     not found yet >=              -inf        (1; 0)
+   123: >>>>> -8.800000000e+001 >= -9.100000000e+001   3.4% (17; 0)
+   126: >>>>> -9.000000000e+001 >= -9.100000000e+001   1.1% (9; 11)
+   142: mip = -9.000000000e+001 >=     tree is empty   0.0% (0; 35)
INTEGER OPTIMAL SOLUTION FOUND
> cat("status:",result$status)
status: optimal
> cat("objective:",result$value)
objective: 90
> print(result$getValue(buy))
      [,1]
 [1,]    3
 [2,]    4
 [3,]    4
 [4,]    3
 [5,]    3
 [6,]    1
 [7,]    0
 [8,]    0
 [9,]    0
[10,]    4
[11,]    0
[12,]    0
> print(result$getValue(sell))
      [,1]
 [1,]    0
 [2,]    0
 [3,]    0
 [4,]    0
 [5,]    0
 [6,]    0
 [7,]    8
 [8,]    6
 [9,]    4
[10,]    0
[11,]    4
[12,]    0
> print(result$getValue(inv))
      [,1]
 [1,]    3
 [2,]    7
 [3,]   11
 [4,]   14
 [5,]   17
 [6,]   18
 [7,]   10
 [8,]    4
 [9,]    0
[10,]    4
[11,]    0
[12,]    0
> print(result$getValue(delta))
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,]    1    1    1    0    0    0    0    0    1     1     1     1
[2,]    0    0    0    1    1    0    0    1    0     0     0     0
[3,]    0    0    0    0    0    1    0    0    0     0     0     0
[4,]    0    0    0    0    0    0    1    0    0     0     0     0
> 

So, I think someone owes me a good bottle of cognac for this.



来源:https://stackoverflow.com/questions/61900256/r-optimisation-max-buy-sell-dependent-on-stock-level

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!