Overall Statistics Total Trades 667 Average Win 0.25% Average Loss -0.28% Compounding Annual Return 7.418% Drawdown 23.500% Expectancy 0.507 Net Profit 119.759% Sharpe Ratio 0.806 Loss Rate 19% Win Rate 81% Profit-Loss Ratio 0.87 Alpha 0.13 Beta -4.203 Annual Standard Deviation 0.076 Annual Variance 0.006 Information Ratio 0.592 Tracking Error 0.076 Treynor Ratio -0.015 Total Fees \$689.27
```from math import ceil,floor,isnan
from datetime import datetime
import pandas as pd
import numpy as np
from scipy.optimize import minimize

class AssetAllocationAlgorithm(QCAlgorithm):

def Initialize(self):
self.SetStartDate(2007, 01, 01)  #Set Start Date
self.SetEndDate(2018, 01, 01)    #Set End Date
self.SetCash(100000)            #Set Strategy Cash

tickers = ["IEF", "TLT", "SPY", "EFA", "EEM", "JPXN", "XLK"]
self.symbols = []
for i in tickers:
for syl in self.symbols:

self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.AfterMarketOpen("SPY"), Action(self.Rebalancing))

def OnData(self, data):
if data.ContainsKey("SPY"):
for syl in self.symbols:

def Rebalancing(self):

data = {}
for syl in self.symbols:
data[syl] = [float(i.Close) for i in syl.window]
df_price = pd.DataFrame(data,columns=data.keys())
daily_return = (df_price / df_price.shift(1)).dropna()

a = PortfolioOptimization(daily_return, 0, len(data))
opt_weight = a.opt_portfolio()

if isnan(sum(opt_weight)): return
self.Log(str(opt_weight))

for i in range(len(data)):
self.SetHoldings(df_price.columns[i], opt_weight[i])

# equally weighted
# self.SetHoldings(self.symbols[i], 1.0/len(data))

class PortfolioOptimization(object):

import numpy as np
import pandas as pd

def __init__(self, df_return, risk_free_rate, num_assets):

self.daily_return = df_return
self.risk_free_rate = risk_free_rate
self.n = num_assets # numbers of risk assets in portfolio
self.target_vol = 0.05

def annual_port_return(self, weights):
# calculate the annual return of portfolio
return np.sum(self.daily_return.mean() * weights) * 252

def annual_port_vol(self, weights):
# calculate the annual volatility of portfolio
return np.sqrt(np.dot(weights.T, np.dot(self.daily_return.cov() * 252, weights)))

def min_func(self, weights):

# method 1: maximize sharp ratio
return - self.annual_port_return(weights) / self.annual_port_vol(weights)

# # method 2: maximize the return with target volatility
# return - self.annual_port_return(weights) / self.target_vol

def opt_portfolio(self):
# maximize the sharpe ratio to find the optimal weights
cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
bnds = tuple((0, 1) for x in range(2)) + tuple((0, 0.25) for x in range(self.n - 2))
opt = minimize(self.min_func,                               # object function
np.array(self.n * [1. / self.n]),            # initial value
method='SLSQP',                              # optimization method
bounds=bnds,                                 # bounds for variables
constraints=cons)                            # constraint conditions

opt_weights = opt['x']

return opt_weights                        ```