Finding Arbitrage Portfolios with Linear Programming in R
Summary
The document shows how to formulate a search for arbitrage as an optimization problem using asset prices and state-contingent payoffs. A linear program can minimize portfolio cost subject to nonnegative payoffs in every state. A negative-cost solution signals a portfolio that pays to establish while avoiding losses; a zero-cost portfolio with nonnegative payoffs and at least one positive payoff is also an arbitrage. The question’s original setup listed constraints without an objective, and unconstrained positions can make the problem unbounded.
An R example uses a linear programming solver, then adds bounds on short positions to obtain a finite solution. The proceeds from the negative-cost portfolio are invested in an asset to produce a zero-cost portfolio with positive payoffs. A second example maximizes average state returns with a penalty for negative returns. These examples illustrate the formulation, but numerical penalty methods do not guarantee arbitrage constraints are satisfied exactly, and the results depend on the supplied prices, payoffs, and position bounds.
Key ideas
- Arbitrage detection needs an objective as well as constraints on state payoffs.
- A negative-cost portfolio with nonnegative payoffs across states indicates an arbitrage opportunity.
- A zero-cost portfolio must have at least one positive payoff to qualify as arbitrage.
- Unbounded positions can make the linear program unbounded, so practical bounds may be needed.
- A penalty-based numerical search can illustrate candidate portfolios but may not enforce constraints exactly.
Tags
Full text
# Setting up arbitrage strategy in R
# Setting up arbitrage strategy in R
I am trying to construct an arbitrage portfolio $\textbf{x}$ such that $S^T\textbf{x} = 0$ and $A\textbf{x} \geq \textbf{0}$, where $A$ is the payoff matrix at $t=1$ and $S$ is the price at $t=0$. I was not able to do it manually, so I tried using functions contained in the limSolve and lpSolve packages in R with no success. I am not sure how to code it up myself either. Any help or hints on how to proceed would be much appreciated. Thanks!
## Answer by Enrico Schumann (score 3, accepted)
https://quant.stackexchange.com/a/58046
A test for arbitrage opportunities with an LP is to minimize the cost of setting up the portfolio, subject to the restriction that the portfolio loses money in no state of the world. (Note that in your formulation you are missing the actual objective; you only list constraints.) If you find a portfolio that has a negative cost (i.e. you get paid for holding it), but you never lose money, you have found an arbitrage portfolio. Or if find a portfolio with zero cost, but no possibility of losses and at least one positive payoff, you have found an arbitrage opportunity. If you find one arbitrage portfolio, then without constraints you'll typically find infinitely many. That should be intuitive: if you have a portfolio of zero cost, but only non-negative payoffs, you can multiply all weights by some constant and still have an arbitrage portfolio. Also, without constraints, an arbitrage portfolio with negative cost will be unbounded.
Doing this in R:
```
A <- matrix(c(2, 1, 0, 3, 1,
1, 1, 1, 2, 1,
0, 1, 2, 1, 0), byrow = TRUE, nrow = 3)
S <- c(1, 1, 1, 2, 1/3)
library("Rglpk")
bounds <- list(lower = list(ind = 1:5, val = rep(-Inf, 5)))
lp.sol <- Rglpk_solve_LP(S,
mat = A,
dir = rep(">=", 3),
rhs = c(0, 0, 0),
bounds = bounds,
control = list(canonicalize_status = FALSE,
verbose = TRUE))
## [....]
## LP HAS UNBOUNDED PRIMAL SOLUTION
```
That's not overly helpful because it only tells you that there is an arbitrage opportunity. So we add constraints: a negative position may not exceed -1.
```
bounds <- list(lower = list(ind = 1:5, val = rep(-1, 5)))
lp.sol <- Rglpk_solve_LP(S,
mat = A,
dir = rep(">=", 3),
rhs = c(0, 0, 0),
bounds = bounds)
sum(lp.sol$solution*S)
## [1] -1
A %*% lp.sol$solution
## [,1]
## [1,] 0
## [2,] 3
## [3,] 0
```
Now you have a negative-cost portfolio (i.e. you receive 1 for setting up the portfolio). To make it zero cost, you invest those proceeds into one asset:
```
x <- lp.sol$solution
x[2] <- x[2] + 1/S[2]
sum(x*S)
## [1] 0
A %*% x
## [,1]
## [1,] 1
## [2,] 4
## [3,] 1
```
Now you have a zero-cost portfolio with strictly positive payoffs.
Alternatively, you could use another numerical solver to directly solve the optimization model. Here is an example. (Disclosure: I am the maintainer of packages NMOF and neighbours.) It's more convenient to work with returns:
```
R <- t(t(A)/S) - 1
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 0 -1 0.5 2
## [2,] 0 0 0 0.0 2
## [3,] -1 0 1 -0.5 -1
library("NMOF") ## https://github.com/enricoschumann/NMOF
library("neighbours") ## https://github.com/enricoschumann/neighbours
```
Now we directly maximize the average payoff, say. (The implementation I use minimizes, so I multiply by -1.)
```
max_payoff <- function(x, R, S)
-sum(R %*% x) + ## => maximize average payoff
-10*sum(pmin(R %*% x, 0)) ## => penalty for negative state returns
nb <- neighbourfun(-1, 5, length = 5, stepsize = 5/100)
ta.sol <- LSopt(max_payoff,
list(neighbour = nb,
x0 = rep(0, length(S)),
nI = 5000),
R = R, S = S)
round(ta.sol$xbest, 3) ## the portfolio
## [1] -1.00 -1.00 0.75 -1.00 2.25
round(R %*% ta.sol$xbest, 1) ## the state returns
## [,1]
## [1,] 2.2
## [2,] 4.5
## [3,] 0.0
```
The portfolio in shares:
```
x <- round(ta.sol$xbest/S, 3)
sum(x*S)
## [1] 0
A %*% x
## [,1]
## [1,] 2.25
## [2,] 4.50
## [3,] 0.00
```Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.