import mosek
def sharpe(task, G, rT, w0, rf):
  # Computes the largest Sharpe ratio (assuming no short-selling), 
  #   minimize      f
  #   subject to  [ (r-e*rf)'  0   0      0      ] [ y ]   [ 1 ] 
  #               [    G       0  -I      0      ]*[ f ] = [ 0 ] 
  #               [    e'      0   0   -sum(w0)  ] [ g ]   [ 0 ]
  #                                                [ t ]
  #               t >= 0,  y >= 0,  ||g||_2 <= f
  n = len(rT);  p = 0;  nvars = n+1+len(G)+1;  inf = 0.0
  
  task.putobjsense(mosek.objsense.minimize)
  task.append(mosek.accmode.var, nvars)      # y, f, g and t
  task.append(mosek.accmode.con, 1+len(G)+1) # constraints
  task.putcj(n,1.0)
  
  task.putavec(mosek.accmode.con, 0, range(n), [i-rf for i in rT]) 
  task.putbound(mosek.accmode.con, 0, mosek.boundkey.fx, 1.0, 1.0)
  
  for row in G:
    Gidx = [v[0] for v in row];  Gval = [v[1] for v in row]
    task.putavec(mosek.accmode.con, 1+p, Gidx+[n+1+p], Gval+[-1.0])
    task.putbound(mosek.accmode.con, 1+p, mosek.boundkey.fx, 0.0, 0.0)
    p += 1

  task.putavec(mosek.accmode.con, 1+p, range(n) + [n+1+p], 
                                       [1.0]*n  + [-sum(w0)])
  task.putbound(mosek.accmode.con, 1+p, mosek.boundkey.fx, 0.0, 0.0)

  task.putboundlist(mosek.accmode.var, range(n+1+p+1),
      [mosek.boundkey.lo]*n+[mosek.boundkey.fr]*(p+1)+[mosek.boundkey.lo], 
      [0]*n + [-inf]*(p+1) + [0], # lower bounds
      [inf]*nvars)                # upper bounds

  task.appendcone(mosek.conetype.quad, 0.0, range(n, n+1+p))

  task.optimize();  xx = [0.0]*nvars
  task.getsolutionslice(mosek.soltype.itr, mosek.solitem.xx, 0, nvars, xx)
  y = xx[0:n];  f = xx[n:n+1];  g = xx[n+1:n+1+p];  t = xx[n+1+p:nvars]

  x    = [y[i]/t[0]-w0[i] for i in range(n)]
  risk = f[0]/t[0]
  ret  = sum([rT[i]*(x[i]+w0[i]) for i in range(n)])
  return [x,risk,ret]
