text: """
    Name     : c9_23_efficient_based_on_sortino_ratio.py
    Book     : Python for Finance (2nd ed.)
    Publisher: Packt Publishing Ltd. 
    Author   : Yuxing Yan
    Date     : 6/6/2017
    email    : yany@canisius.edu
               paulyxy@hotmail.com
  """
  import scipy as sp
  import numpy as np
  import pandas as pd
  from scipy.optimize import fmin
  from matplotlib.finance import quotes_historical_yahoo_ochl as getData
  #
  # Step 1: input area
  ticker=('IBM','WMT','C')   # tickers
  begdate=(1990,1,1)         # beginning date 
  enddate=(2012,12,31)       # ending date
  rf=0.0003                  # annual risk-free rate
  #
  # Step 2: define a few functions
  # function 1: 
  def ret_annual(ticker,begdate,enddte):
      x=getData(ticker,begdate,enddate,asobject=True,adjusted=True)
      logret =sp.log(x.aclose[1:]/x.aclose[:-1])
      date=[]
      d0=x.date
      for i in range(0,sp.size(logret)):
          date.append(d0[i].strftime("%Y"))
      y=pd.DataFrame(logret,date,columns=[ticker])
      return sp.exp(y.groupby(y.index).sum())-1
  
  # function 2: estimate LPSD
  def LPSD_f(returns, Rf):
      y=returns[returns-Rf<0]  
      m=len(y)
      total=0.0
      for i in sp.arange(m):
          total+=(y[i]-Rf)**2
      return total/(m-1)
  
  # function 3: estimate Sortino
  def sortino(R,w):
      mean_return=sp.mean(R,axis=0)
      ret = sp.array(mean_return)
      LPSD=LPSD_f(R,rf)
      return (sp.dot(w,ret) - rf)/LPSD
  
  # function 4: for given n-1 weights, return a negative sharpe ratio
  def negative_sortino_n_minus_1_stock(w):
      w2=sp.append(w,1-sum(w))
      return -sortino(R,w2)        # using a return matrix here!!!!!!
  
  # Step 3: generate a return matrix (annul return)
  n=len(ticker)              # number of stocks
  x2=ret_annual(ticker[0],begdate,enddate) 
  for i in range(1,n):
      x_=ret_annual(ticker[i],begdate,enddate) 
      x2=pd.merge(x2,x_,left_index=True,right_index=True)
  
  # using scipy array format 
  R = sp.array(x2)
  print('Efficient porfolio (mean-variance) :ticker used')
  print(ticker)
  print('Sortino ratio for an equal-weighted portfolio')
  equal_w=sp.ones(n, dtype=float) * 1.0 /n 
  print(equal_w)
  print(sortino(R,equal_w))
  # for n stocks, we could only choose n-1 weights
  w0= sp.ones(n-1, dtype=float) * 1.0 /n 
  w1 = fmin(negative_sortino_n_minus_1_stock,w0)
  final_w = sp.append(w1, 1 - sum(w1))
  final_sortino = sortino(R,final_w)
  print ('Optimal weights are ')
  print (final_w)
  print ('final Sortino ratio is ')
  print(final_sortino)
  