#!/usr/bin/python
"""
This program will genereate the Mean-CVaR program in free MPS format

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
import sys
from optparse import OptionParser

def main():
    usage = """
    usage: python generate_mps.py [option] returns_file
    If returns_file is not provided, it will try to read from stdin.
    Examples: 
    python generate_mps.py -N 50000 -r 0.05 returns.dat > meancvar.mps
    python generate_mps.py -r 0.02 -e 0.01 < returns.dat > meancvar.mps
    cat returns.dat | python generate_mps.py -r 0.1 -n  > meancvar.mps
    gzip -cd returns.dat.gz | python generate_mps.py -r 0.03  > meancvar.mps
    """
    version = "%prog 1.0"
    parser = OptionParser(usage=usage, version=version)
    parser.add_option("--samplesize", "-N", dest="N", action="store",
            help="Number of samples, will get all if N > number of returns sample",
            default=0, type = 'int')
    parser.add_option("--requiredreturn", "-r", dest="rr", action="store",
            help="required return", default=None, type = 'string')
    parser.add_option("--epsilon", "-e", dest="epsilon", action="store",
            help="epsilon of CVaR", default="0.05", type = 'string')
    parser.add_option("--probability", "-p", dest="probability", action="store",
            help="file of probability of return, default 1/N", 
            default=None, type = 'string')
    parser.add_option("--noshortsell", "-n", dest="noshortsell", action="store_true",
            help="if shortsell is not allowed", default=False)

    (option, arg) = parser.parse_args()

    # check with the option
    if option.rr is None:
        parser.error("please input the required return (-R)")
    if len(arg) > 1:
        parser.error("please input only one return file")
    try:
        option.rr = float(option.rr)
    except:
        parser.error("please check the required return (-R)")
    try:
        option.epsilon = float(option.epsilon)
    except:
        parser.error("please check the epsilon (-e)")

    if len(arg) == 1:
        # get the sample returns data
        fr = open(arg[0], 'r')
    else:
        fr = sys.stdin
    returns = []
    for index, i in enumerate(fr):
        if option.N == 0 or index < option.N:
            try:
                sample = map(float, i.split())
                returns.append(sample)
            except:
                parser.error("please check the sample returns file at line " +
                        str(index + 1))
        else:
            break
    fr.close()

    # initialization
    M = len(returns[0]) # number of assets
    N = len(returns)

    # save the probability
    if option.probability is not None:
        try:
            probfile = open(option.probability, 'r')
        except:
            parser.error("please check the probablity file (-p)")
        prob = []
        for index, i in enumerate(probfile):
            if option.N == 0 or index < option.N:
                try:
                    tmpprob = float(i)
                    prob.append(tmpprob)
                except:
                    parser.error("please check the probability file at line " +
                            str(index + 1))
        # normalize
        tmpsum = sum(prob)
        prob = [i / tmpsum for i in prob]
    else:
        prob = [1.0 / N for i in range(N)]

    # check size of probability and returns
    if len(prob) != len(returns):
        parser.error("please check the size of probability and returns")

    # find mean return
    meanreturn = [sum(map(lambda x, y: x * y, [returns[i][j] for i in range(N)], prob)) 
            for j in range(M)]

    # generate the MPS file
    tab = "    "
    print "NAME", "MEANCVAR"
    print "ROWS"
    print tab + tab.join(["N", "CVAR"])
    print tab + tab.join(["L", "COST"])
    for i in range(N):
        print tab + tab.join(["G", "CV" + str(i)])
    print tab + tab.join(["E", "RETURN"])
    print tab + tab.join(["E", "TOTALWEIGHT"])
    
    print "COLUMNS"
    for i in range(N):
        print tab + tab.join(["A" + str(i), "CV" + str(i), "1", "COST",
            str(prob[i] / option.epsilon)])
    for j in range(M):
        for i in range(N):
            print tab + tab.join(["W" + str(j), "CV" + str(i),
                str(returns[i][j])])
        print tab + tab.join(["W" + str(j), "TOTALWEIGHT", "1", "RETURN",
                str(meanreturn[j])])
    print tab + tab.join(["C", "COST", "-1", "CVAR", "1"])
    print tab + tab.join(["TAU", "COST", "1"])
    for i in range(N):
        print tab + tab.join(["TAU", "CV" + str(i), "1"])

    print "RHS"
    print tab + tab.join(["RHS1", "TOTALWEIGHT", "1"])
    print tab + tab.join(["RHS1", "RETURN", str(option.rr)])

    print "BOUNDS"
    print tab + tab.join(["FR", "B1", "TAU"])
    print tab + tab.join(["FR", "B1", "C"])
    if not option.noshortsell:
        for j in range(M):
            print tab + tab.join(["FR", "B1", "W" + str(j)])
    print "ENDATA"

if __name__ == "__main__":
    main()


