R优化买卖

R优化买卖,r,optimization,linear-programming,mixed-integer-programming,R,Optimization,Linear Programming,Mixed Integer Programming,我需要找到一个优化问题的解决方案。在我的简化示例中,我预测了明年的价格。我的库存最多可以包含25种产品。我每个月既可以卖也可以买。我每月不能购买超过4种产品或销售超过8种产品。我想以低于销售的价格买进以获取利润。是否有可指示何时购买和何时出售的软件包/功能?目标是在保持既定条件的同时,在期末实现利润最大化,见下例。还提供了一种可能的手动解决方案。在实际应用中,会有其他条件,例如我需要在冬季保持一定的库存水平,或者最大买入/卖出取决于库存水平。例如,如果库存较高,则可以销售更多等 library(

我需要找到一个优化问题的解决方案。在我的简化示例中,我预测了明年的价格。我的库存最多可以包含25种产品。我每个月既可以卖也可以买。我每月不能购买超过4种产品或销售超过8种产品。我想以低于销售的价格买进以获取利润。是否有可指示何时购买和何时出售的软件包/功能?目标是在保持既定条件的同时,在期末实现利润最大化,见下例。还提供了一种可能的手动解决方案。在实际应用中,会有其他条件,例如我需要在冬季保持一定的库存水平,或者最大买入/卖出取决于库存水平。例如,如果库存较高,则可以销售更多等

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销售您需要一个二进制变量来为库存创建两个段。数学并不难,但我可以把它放在评论框里。非常感谢你的回答。我问了一个新问题,最大买入/卖出取决于这里的股票水平。欢迎加入:-