1-4移动均线交叉策略3

第一阶段、一个简单策略入门量化投资

1-4移动均线交叉策略3

上一文1-3移动均线交叉策略2中,我们得到的结果是令人失望的。但我们的探索还要继续。
我们知道,使用投资组合的方式进行分散投资是降低风险的好办法。尽管移动均线交叉策略的表现并不理想,我们还是在此策略基础上进行修改,添加采用投资组合进行投资的代码,重新进行回测。
修改后的代码,你只需提前设置你想要购买股票的公司代码列表,例如:

# the list of listed companies that we are concerned about
listed_company_list = ["AAPL","MSFT","GOOG","FB","TWTR","NFLX","AMZN","SNY","NTDOY","IBM","HPQ"]

假设初始资金仍为100万,在使用上面给出的投资组合的情况下,资产的变化情况如下图所示:
这里写图片描述
这时,我们策略的收益率为93.5%
平均年化收益率为9.138%
显然,使用投资组合后,收益进一步减少了,但是我们也清楚其中的积极意义,这样的策略分摊了风险。我们都知道,风险越大,收益越高的现象是普遍存在的,如何权衡呢?
于是我们看到,同样是均线交叉策略,使用和不使用投资组合两种情况,在回测后判断策略优劣时就已经针对风险与收益的权衡出现了问题。
因此,如何更合理的在回测时评价策略的优劣是一个需要探索的有意义工作,未完待续…


完整代码

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime

import stockdata_preProcess as preProcess



##### the first step: get the listed companies's stock data that we concerned about

# the time interval of the data we want to get(from start to end)
start = datetime.datetime(2010, 1, 1)
end = datetime.date.today()

# the list of listed companies that we are concerned about
listed_company_list = ["AAPL","MSFT","GOOG","FB","TWTR","NFLX","AMZN","SNY","NTDOY","IBM","HPQ"]
# *trouble*: I can't get "YHOO" 's data, and I have't find

# download data (we do not need to repeat this work)
#preProcess.downloadAndSaveData(listed_company_list, start, end)




# use moving average crossover strategy to build the trading signal dataframe
#   stocks: the data, e.g: [("AAPL", apple_adjust_data),("MSFT", microsoft_adjust_data),("GOOG", google_adjust_data)]
#   fast: the span of short-term moving average
#   slow: the span of long-term moving average
def ma_crossover_orders(stocks, fast, slow):
    fast_str = str(fast) + 'd'
    slow_str = str(slow) + 'd'
    ma_diff_str = fast_str + '-' + slow_str

    trades = pd.DataFrame({"Price": [], "Regime": [], "Signal": []})
    for s in stocks:
        s[1][fast_str] = np.round(s[1]["Close"].rolling(window = fast, center = False).mean(), 2)
        s[1][slow_str] = np.round(s[1]["Close"].rolling(window = slow, center = False).mean(), 2)
        s[1][ma_diff_str] = s[1][fast_str] - s[1][slow_str]

        s[1]["Regime"] = np.where(s[1][ma_diff_str] > 0, 1, 0)

        s[1]["Regime"] = np.where(s[1][ma_diff_str] < 0, -1, s[1]["Regime"])

        regime_orig = s[1].ix[-1, "Regime"]
        s[1].ix[-1, "Regime"] = 0
        s[1]["Signal"] = np.sign(s[1]["Regime"] - s[1]["Regime"].shift(1))

        s[1].ix[-1, "Regime"] = regime_orig

        signals = pd.concat([
            pd.DataFrame({"Price": s[1].loc[s[1]["Signal"] == 1, "Close"],
                         "Regime": s[1].loc[s[1]["Signal"] == 1, "Regime"],
                         "Signal": "Buy"}),
            pd.DataFrame({"Price": s[1].loc[s[1]["Signal"] == -1, "Close"],
                         "Regime": s[1].loc[s[1]["Signal"] == -1, "Regime"],
                         "Signal": "Sell"}),
        ])
        signals.index = pd.MultiIndex.from_product([signals.index, [s[0]]], names = ["Date", "Symbol"])
        trades = trades.append(signals)

    trades.sort_index(inplace = True)
    trades.index = pd.MultiIndex.from_tuples(trades.index, names = ["Date", "Symbol"])
    return trades





# do the backtest
#   signals: the dataframe recording the trading signal
#   cash: the initial cash flow
#   port_value: the largest proportion of single transaction to total assets
#   batch: the smallest unit of the number of shares traded
def backtest(signals, cash, port_value = .1, batch = 100):
    SYMBOL = 1
    portfolio = dict()    # denote: all the stock asset allocation
    port_prices = dict()  # denote: all the stock price correspond to the asset allocation

    results = pd.DataFrame({"Start Cash": [],
                            "End Cash": [],
                            "Portfolio Value": [],
                            "Type": [],
                            "Shares": [],
                            "Share Price": [],
                            "Trade Value": [],
                            "Profit per Share": [],
                            "Total Profit": []})

    for index, row in signals.iterrows():
        # index[SYMBOL] denote the listed company's name, e.g APPL
        shares = portfolio.setdefault(index[SYMBOL], 0)
        trade_val = 0
        batches = 0

        # step 1 : sell current stock(if we hold current stock)
        # if shares>0, means we already hold the stock of the company
        # so it is a sell signal here, we sell all the shares held now
        cash_change = row["Price"] * shares
        portfolio[index[SYMBOL]] = 0
        old_price = port_prices.setdefault(index[SYMBOL], row["Price"]) # get the price when we buy the stock before

        # step 2 : compute portfolio's value( the value after sell current stock)
        portfolio_val = 0
        for key, val in portfolio.items():
            portfolio_val += val * port_prices[key]

        # step 3 : buy current stock( if it is a buy singnal here )
        if row["Signal"] == "Buy" and row["Regime"] == 1:
            batches = np.floor((portfolio_val + cash) * port_value) // np.ceil(batch * row["Price"])
            trade_val = batches * batch * row["Price"]
            cash_change -= trade_val
            portfolio[index[SYMBOL]] = batches * batch
            port_prices[index[SYMBOL]] = row["Price"]
            old_price = row["Price"]
        elif row["Signal"] == "Sell" and row["Regime"] == -1:
            pass

        pprofit = row["Price"] - old_price

        results = results.append(pd.DataFrame({
                "Start Cash": cash,
                "End Cash": cash + cash_change,
                "Portfolio Value": cash + cash_change + portfolio_val + trade_val,
                "Type": row["Signal"],
                "Shares": batch * batches,
                "Share Price": row["Price"],
                "Trade Value": abs(cash_change),
                "Profit per Share": pprofit,
                "Total Profit": batches * batch * pprofit
            }, index = [index]))
        cash += cash_change

    results.sort_index(inplace = True)
    results.index = pd.MultiIndex.from_tuples(results.index, names = ["Date", "Symbol"])

    return results




##### get the data we save in .csv file and then return the repaired data to the user
DataSetList = preProcess.repairAndGetData(listed_company_list)



##### use moving average crossover strategy to build the trading signal dataframe
# build the data format requied by the function ma_crossover_orders
# ( combine the stock name and the corresponding data )
stock_NameDataTuple_List = []
for i in range(len(listed_company_list)):
    cur_stock = DataSetList[i];
    cur_company = listed_company_list[i]
    stock_NameDataTuple_List.append((cur_company,cur_stock))

signals = ma_crossover_orders(stock_NameDataTuple_List, fast = 20, slow = 50)
# these codes are the same as below: (use Ctrl+/ to batch annotation code)
# apple = DataSetList[0]
# microsoft = DataSetList[1]
# google = DataSetList[2]
# facebook = DataSetList[3]
# twitter = DataSetList[4]
# netflix = DataSetList[5]
# amazon = DataSetList[6]
# sony = DataSetList[7]
# nintendo = DataSetList[8]
# ibm = DataSetList[9]
# hp = DataSetList[10]
# signals = ma_crossover_orders([("AAPL", preProcess.ohlc_adjust(apple)),
#                               ("MSFT",  preProcess.ohlc_adjust(microsoft)),
#                               ("GOOG",  preProcess.ohlc_adjust(google)),
#                               ("FB",    preProcess.ohlc_adjust(facebook)),
#                               ("TWTR",  preProcess.ohlc_adjust(twitter)),
#                               ("NFLX",  preProcess.ohlc_adjust(netflix)),
#                               ("AMZN",  preProcess.ohlc_adjust(amazon)),
#                               ("SNY",   preProcess.ohlc_adjust(sony)),
#                               ("NTDOY", preProcess.ohlc_adjust(nintendo)),
#                               ("IBM",   preProcess.ohlc_adjust(ibm)),
#                               ("HPQ",   preProcess.ohlc_adjust(hp))],
#                             fast = 20, slow = 50)
print(signals)



##### do the back test
bk = backtest(signals, 1000000)
print(bk)



##### show the changes in portfolio value
#bk["Portfolio Value"].groupby(level = 0).apply(lambda x: x[-1]).plot()
portfolio_ValueList = bk["Portfolio Value"].groupby(level = 0).apply(lambda x: x[-1])
portfolio_ValueList.plot()
#print(portfolio_ValueList)



##### compute annualized rate of return
initial_value = portfolio_ValueList[0]
deadline_value = portfolio_ValueList[-1]

initial_date = portfolio_ValueList.index[0]
deadline_date = portfolio_ValueList.index[-1]
holding_interval = (deadline_date - initial_date).days / 365

AnnualReturnRate = ( pow(deadline_value/initial_value,1/holding_interval) - 1 )*100
print('平均年化收益率: ',AnnualReturnRate,"%")
print((deadline_value-initial_value)/initial_value)


plt.show()

猜你喜欢

转载自blog.csdn.net/u011583927/article/details/78237335
1-4
今日推荐