R优化买卖
我需要找到一个优化问题的解决方案。在我的简化示例中,我预测了明年的价格。我的库存最多可以包含25种产品。我每个月既可以卖也可以买。我每月不能购买超过4种产品或销售超过8种产品。我想以低于销售的价格买进以获取利润。是否有可指示何时购买和何时出售的软件包/功能?目标是在保持既定条件的同时,在期末实现利润最大化,见下例。还提供了一种可能的手动解决方案。在实际应用中,会有其他条件,例如我需要在冬季保持一定的库存水平,或者最大买入/卖出取决于库存水平。例如,如果库存较高,则可以销售更多等R优化买卖,r,optimization,linear-programming,mixed-integer-programming,R,Optimization,Linear Programming,Mixed Integer Programming,我需要找到一个优化问题的解决方案。在我的简化示例中,我预测了明年的价格。我的库存最多可以包含25种产品。我每个月既可以卖也可以买。我每月不能购买超过4种产品或销售超过8种产品。我想以低于销售的价格买进以获取利润。是否有可指示何时购买和何时出售的软件包/功能?目标是在保持既定条件的同时,在期末实现利润最大化,见下例。还提供了一种可能的手动解决方案。在实际应用中,会有其他条件,例如我需要在冬季保持一定的库存水平,或者最大买入/卖出取决于库存水平。例如,如果库存较高,则可以销售更多等 library(
library(tidyverse)
library(lubridate)
df <- tibble(
date = ymd("2020-06-01") + months(0:11),
price = c(12, 11, 12, 13, 16, 17, 18, 17, 18, 16, 17, 13),
total_capacity = 25,
max_units_buy = 4,
max_units_sell = 8)
# date price total_capacity max_units_buy max_units_sell
# 1 2020-06-01 12 25 4 8
# 2 2020-07-01 11 25 4 8
# 3 2020-08-01 12 25 4 8
# 4 2020-09-01 13 25 4 8
# 5 2020-10-01 16 25 4 8
# 6 2020-11-01 17 25 4 8
# 7 2020-12-01 18 25 4 8
# 8 2021-01-01 17 25 4 8
# 9 2021-02-01 18 25 4 8
# 10 2021-03-01 16 25 4 8
# 11 2021-04-01 17 25 4 8
# 12 2021-05-01 13 25 4 8
df_manual_solution <- tibble(
date = ymd("2020-06-01") + months(0:11),
price = c(12, 11, 12, 13, 16, 17, 18, 17, 18, 16, 17, 13),
total_capacity = 25,
max_units_buy = 4,
max_units_sell = 8,
real_buy = c(4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 0, 0),
real_sell = c(0, 0, 0, 0, 0, 0, 8, 8, 8, 0, 4, 0),
inventory_level = cumsum(real_buy) - cumsum(real_sell),
profit_loss = cumsum(real_sell*price) - cumsum(real_buy*price))
# date price total_capacity max_units_buy max_units_sell real_buy real_sell inventory_level profit_loss
# 1 2020-06-01 12 25 4 8 4 0 4 -48
# 2 2020-07-01 11 25 4 8 4 0 8 -92
# 3 2020-08-01 12 25 4 8 4 0 12 -140
# 4 2020-09-01 13 25 4 8 4 0 16 -192
# 5 2020-10-01 16 25 4 8 4 0 20 -256
# 6 2020-11-01 17 25 4 8 4 0 24 -324
# 7 2020-12-01 18 25 4 8 0 8 16 -180
# 8 2021-01-01 17 25 4 8 0 8 8 -44
# 9 2021-02-01 18 25 4 8 0 8 0 100
# 10 2021-03-01 16 25 4 8 4 0 4 36
# 11 2021-04-01 17 25 4 8 0 4 0 104
# 12 2021-05-01 13 25 4 8 0 0 0 104
我相信这可以建模为一个小型的混合整数规划MIP模型 下面是一个使用CVXR的实现:
> 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
>
> # number of time periods
> NT <- length(price)
>
> # Decision variables
> inv = Variable(NT,integer=T)
> buy = Variable(NT,integer=T)
> sell = Variable(NT,integer=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,
+ inv >= 0, inv <= capacity,
+ buy >= 0, buy <= max_units_buy,
+ sell >= 0, sell <= max_units_sell))
> result <- solve(problem,verbose=T)
GLPK Simplex Optimizer, v4.47
84 rows, 36 columns, 119 non-zeros
* 0: obj = 0.000000000e+000 infeas = 0.000e+000 (12)
* 35: obj = -1.040000000e+002 infeas = 0.000e+000 (0)
OPTIMAL SOLUTION FOUND
GLPK Integer Optimizer, v4.47
84 rows, 36 columns, 119 non-zeros
36 integer variables, none of which are binary
Integer optimization begins...
+ 35: mip = not found yet >= -inf (1; 0)
+ 35: >>>>> -1.040000000e+002 >= -1.040000000e+002 0.0% (1; 0)
+ 35: mip = -1.040000000e+002 >= tree is empty 0.0% (0; 1)
INTEGER OPTIMAL SOLUTION FOUND
> cat("status:",result$status)
status: optimal
> cat("objective:",result$value)
objective: 104
> print(result$getValue(buy))
[,1]
[1,] 4
[2,] 4
[3,] 4
[4,] 4
[5,] 4
[6,] 0
[7,] 0
[8,] 4
[9,] 0
[10,] 4
[11,] 0
[12,] 0
> print(result$getValue(sell))
[,1]
[1,] 0
[2,] 0
[3,] 0
[4,] 0
[5,] 0
[6,] 8
[7,] 8
[8,] 0
[9,] 8
[10,] 0
[11,] 4
[12,] 0
> print(result$getValue(inv))
[,1]
[1,] 4
[2,] 8
[3,] 12
[4,] 16
[5,] 20
[6,] 12
[7,] 4
[8,] 8
[9,] 0
[10,] 4
[11,] 0
[12,] 0
>
增加了拥有初始库存的可能性,并创建了一个功能,以逐步进行优化,以考虑与库存水平相关的购买/出售
library(tidyverse)
library(lubridate)
library(CVXR)
init_fce <- function(.df_storage, .df_bounds, .type = "max"){
if(.type == "max"){
.df_storage$max_buy <- max(.df_bounds$max_buy)
.df_storage$max_sell <- max(.df_bounds$max_sell)
} else if(.type == "min"){
.df_storage$max_buy <- min(.df_bounds$max_buy)
.df_storage$max_sell <- min(.df_bounds$max_sell)
} else if(.type == "mean"){
.df_storage$max_buy <- mean(.df_bounds$max_buy)
.df_storage$max_sell <- mean(.df_bounds$max_sell)
}
.df_storage
}
optim_fce <- function(.df){
# Decision variables
m_inv_tot = Variable(nrow(.df), integer = T)
m_buy = Variable(nrow(.df), integer = T)
m_sell = Variable(nrow(.df), integer = T)
# Lag operator
m_L = cbind(rbind(0, diag(nrow(.df) - 1)), 0)
objetive <- Maximize(sum(.df$price*(m_sell-m_buy)))
constraints <- list(
m_inv_tot == m_L %*% m_inv_tot + .df$inv_init + m_buy - m_sell, # L %*% result$getValue(inv) + result$getValue(buy) - result$getValue(sell)
m_inv_tot >= 0, m_inv_tot <= .df$capacity,
m_buy >= 0, m_buy <= .df$max_buy,
m_sell >= 0, m_sell <= .df$max_sell
)
problem <- Problem(objetive, constraints)
result <- solve(problem) # , verbose=T
.df <- .df %>%
mutate(
buy = (result$getValue(m_buy) %>% as.vector()),
sell = (result$getValue(m_sell) %>% as.vector()),
inventory_real = (result$getValue(m_inv_tot) %>% as.vector())
)
.df
}
set_limits_fce <- function(.df_storage, .df_bounds){
.df_storage <- .df_storage %>%
select(-max_buy, -max_sell) %>%
mutate(capacity_usage_pct_prec = lag(inventory_real, default = inv_init[1])/capacity) %>%
crossing(.df_bounds %>% select(-segment)) %>%
filter(capacity_usage_pct_prec >= lbound, capacity_usage_pct_prec < ubound) %>%
mutate(
within_bounds = (buy <= max_buy) & (sell <= max_sell)
) %>%
select(-lbound, -ubound)
.df_storage
}
get_results <- function(.df_storage){
if( any(!.df_storage$within_bounds) ){
print("result not within bounds")
} else{
.df_storage$profit <- .df_storage$sell * .df_storage$price - .df_storage$buy * .df_storage$price
print(sum(.df_storage$profit))
}
.df_storage
}
A1_storage <- tibble(
date = ymd("2020-06-01") + months(0:11),
price = c(12, 11, 12, 13, 16, 17, 18, 17, 18, 16, 17, 13),
inv_init = c(3, rep(0, 11)),
capacity = 25
)
A2_bounds <- tibble(
segment = c("0%-30%", "30%-65%", "65%-70%", "70%-100%"),
lbound = c(0, 0.3, 0.65, 0.7),
ubound = c(0.3, 0.65, 0.7, 1),
max_buy = c(4,3,2,2),
max_sell = c(4,6,6,8)
)
B1_max <- init_fce(A1_storage, A2_bounds, .type = "max") %>%
optim_fce() %>%
set_limits_fce(.df_bounds = A2_bounds) %>%
get_results() %>%
optim_fce() %>%
set_limits_fce(.df_bounds = A2_bounds) %>%
get_results() %>%
optim_fce() %>%
set_limits_fce(.df_bounds = A2_bounds) %>%
get_results() %>%
optim_fce() %>%
set_limits_fce(.df_bounds = A2_bounds) %>%
get_results()
这看起来很有希望。您认为有可能添加更复杂的约束条件吗?例如,如果库存水平超过其总产能的50%,您可以像原始问题中那样销售8件。但是,如果低于50%,你只能卖出6个?是的。这是可能的。然而,这里有一个建模问题。通常,我们将买卖视为t期间发生的事情,我们称之为流动变量。inv是一个股票变量,在t期末进行计量。说销售和购买依赖于库存有点奇怪,我们在时间上倒退了。当然,我们可以对它进行建模,并将其作为联立方程组进行求解,所以我们可以做这些事情。但是,这在现实世界中可能没有意义。也许我们应该看看inv[t-1]来改变买入[t]和卖出[t]。我们一致认为inv[t-1]更适合买入[t]和卖出[t]。你知道我怎么能在代码中包含这样的条件吗?例如,如果库存[t-1]/容量>=0.5,则销售[t]=最大库存单位\u销售其他销售[t]=0.75*最大库存单位\u销售您需要一个二进制变量来为库存创建两个段。数学并不难,但我可以把它放在评论框里。非常感谢你的回答。我问了一个新问题,最大买入/卖出取决于这里的股票水平。欢迎加入:-