Python股市数据分析教程——学会它，或可以实现半“智能”炒股 (Part 2)

交易策略

• 当短期均线越过长期均线时，交易金融资产。
• 当短期均线再一次越过长期均线时，结束交易。

apple['20d-50d'] = apple['20d'] - apple['50d']
apple.tail()

apple["Regime"] = np.where(apple['20d-50d'] > 0, 1, 0)
apple["Regime"] = np.where(apple['20d-50d'] < 0, -1, apple["Regime"])
apple.loc['2016-01-01':'2016-08-07',"Regime"].plot(ylim = (-2,2)).axhline(y = 0, color = "black", lw = 2)

apple["Regime"].plot(ylim = (-2,2)).axhline(y = 0, color = "black", lw = 2)

apple["Regime"].value_counts()
1       966
-1      663
0       50
Name: Regime, dtype: int64

st = sing(rt - rt-1)

st ∈ {-1, 0, 1}，其中-1表示"抛出"，1表示"买入"，0表示不采取任何措施，我们可以这样获取信号：

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

apple["Signal"].plot(ylim = (-2, 2))

apple["Signal"].value_counts()
0.0     1637
-1.0    21
1.0     20
Name: Signal, dtype: int64

apple.loc[apple["Signal"] == 1, "Close"]
Date
2010-03-16    224.449997
2010-06-18    274.070011
2010-09-20    283.230007
2011-05-12    346.569988
2011-07-14    357.770004
2011-12-28    402.640003
2012-06-25    570.770020
2013-05-17    433.260010
2013-07-31    452.529984
2013-10-16    501.110001
2014-03-26    539.779991
2014-04-25    571.939980
2014-08-18     99.160004
2014-10-28    106.739998
2015-02-05    119.940002
2015-04-28    130.559998
2015-10-27    114.550003
2016-03-11    102.260002
2016-07-01     95.889999
2016-07-25     97.339996
Name: Close, dtype: float64
apple.loc[apple["Signal"] == -1, "Close"]
Date
2010-06-11    253.509995
2010-07-22    259.020000
2011-03-30    348.630009
2011-03-31    348.510006
2011-05-27    337.409992
2011-11-17    377.410000
2012-05-09    569.180023
2012-10-17    644.610001
2013-06-26    398.069992
2013-10-03    483.409996
2014-01-28    506.499977
2014-04-22    531.700020
2014-06-11     93.860001
2014-10-17     97.669998
2015-01-05    106.250000
2015-04-16    126.169998
2015-06-25    127.500000
2015-12-18    106.029999
2016-05-05     93.239998
2016-07-08     96.680000
2016-09-01    106.730003
Name: Close, dtype: float64
apple_signals = pd.concat([
pd.DataFrame({"Price": apple.loc[apple["Signal"] == 1, "Close"],
"Regime": apple.loc[apple["Signal"] == 1, "Regime"],
pd.DataFrame({"Price": apple.loc[apple["Signal"] == -1, "Close"],
"Regime": apple.loc[apple["Signal"] == -1, "Regime"],
"Signal": "Sell"}),
])
apple_signals.sort_index(inplace = True)
apple_signals

apple_long_profits = pd.DataFrame({
apple_signals["Regime"] == 1, "Price"],
"Profit": pd.Series(apple_signals["Price"] - apple_signals["Price"].shift(1)).loc[
apple_signals.loc[(apple_signals["Signal"].shift(1) == "Buy") & (apple_signals["Regime"].shift(1) == 1)].index
].tolist(),
"End Date": apple_signals["Price"].loc[
apple_signals.loc[(apple_signals["Signal"].shift(1) == "Buy") & (apple_signals["Regime"].shift(1) == 1)].index
].index
})
apple_long_profits

pandas_candlestick_ohlc(apple, stick = 45, otherseries = ["20d", "50d", "200d"])

def ohlc_adj(dat):
return pd.DataFrame({"Open": dat["Open"] * dat["Adj Close"] / dat["Close"],
"High": dat["High"] * dat["Adj Close"] / dat["Close"],
"Low": dat["Low"] * dat["Adj Close"] / dat["Close"],

"Signal": "Sell"}),
])
].tolist(),
].index
})

pandas_candlestick_ohlc(apple_adj, stick = 45, otherseries = ["20d", "50d", "200d"])

apple_adj_long_profits

• 在任何交易中，仅投资所有投资总额的10%。
• 如果损失超过交易金额的20%，则退出仓位。

• 股票交易以100股为单位。
• 我们的止损规则包含在股价下跌至一定程度时将股票抛出的指令。因此，我们需要检查这一期间的低价是否已经足够得低，以至于触发止损指令。实际上，除非我们买入了看跌期权，否则我们无法保证以设置的止损价格抛出股票，但为简单起见，我们将这个价格作为抛出价。
• 每一笔交易都需要向经纪人支付一笔佣金，这部分费用应该计算在内。但在这里我们不这样做。

tradeperiods = pd.DataFrame({"Start": apple_adj_long_profits.index,
apple_adj_long_profits

cash = 1000000
apple_backtest = pd.DataFrame({"Start Port. Value": [],
"End Port. Value": [],
"End Date": [],
"Shares": [],
"Share Price": [],
"Profit per Share": [],
"Total Profit": [],
"Stop-Loss Triggered": []})
port_value = .1
batch = 100
stoploss = .2
batches = np.floor(cash * port_value) // np.ceil(batch * row["Price"]) # Maximum number of batches of stocks invested in
trade_val = batches * batch * row["Price"]
if row["Low"] < (1 - stoploss) * row["Price"]:   # Account for the stop-loss
share_profit = np.round((1 - stoploss) * row["Price"], 2)
stop_trig = True
else:
share_profit = row["Profit"]
stop_trig = False
profit = share_profit * batches * batch

apple_backtest = apple_backtest.append(pd.DataFrame({
"Start Port. Value": cash,
"End Port. Value": cash + profit,
"End Date": row["End Date"],
"Shares": batch * batches,
"Share Price": row["Price"],
"Profit per Share": share_profit,
"Total Profit": profit,
"Stop-Loss Triggered": stop_trig
}, index = [index]))
cash = max(0, cash + profit)

apple_backtest

apple_backtest["End Port. Value"].plot()

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"],
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"])

def backtest(signals, cash, port_value = .1, batch = 100):
SYMBOL = 1
portfolio = dict()
port_prices = dict()

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

for index, row in signals.iterrows():
shares = portfolio.setdefault(index[SYMBOL], 0)
batches = 0
cash_change = row["Price"] * shares
portfolio[index[SYMBOL]] = 0

old_price = port_prices.setdefault(index[SYMBOL], row["Price"])
portfolio_val = 0
for key, val in portfolio.items():
portfolio_val += val * port_prices[key]

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"]
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"],
"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

microsoft = web.DataReader("MSFT", "yahoo", start, end)
netflix = web.DataReader("NFLX", "yahoo", start, end)
amazon = web.DataReader("AMZN", "yahoo", start, end)
yahoo = web.DataReader("YHOO", "yahoo", start, end)
sony = web.DataReader("SNY", "yahoo", start, end)
nintendo = web.DataReader("NTDOY", "yahoo", start, end)
ibm = web.DataReader("IBM", "yahoo", start, end)
hp = web.DataReader("HPQ", "yahoo", start, end)
signals = ma_crossover_orders([("AAPL", ohlc_adj(apple)),
fast = 20, slow = 50)
signals
bk = backtest(signals, 1000000)
bk
bk["Portfolio Value"].groupby(level = 0).apply(lambda x: x[-1]).plot()

基准测试

spyder = web.DataReader("SPY", "yahoo", start, end)
spyder.iloc[[0,-1],:]

batches = 1000000 // np.ceil(100 * spyder.ix[0,"Adj Close"]) # Maximum number of batches of stocks invested in
trade_val = batches * batch * spyder.ix[0,"Adj Close"] # How much money is used to buy SPY
final_val = batches * batch * spyder.ix[-1,"Adj Close"] + (1000000 - trade_val) # Final value of the portfolio
final_val
2180977.0
ax_bench = (spyder["Adj Close"] / spyder.ix[0, "Adj Close"]).plot(label = "SPY")
ax_bench = (bk["Portfolio Value"].groupby(level = 0).apply(lambda x: x[-1]) / 1000000).plot(ax = ax_bench, label = "Portfolio")
ax_bench.legend(ax_bench.get_lines(), [l.get_label() for l in ax_bench.get_lines()], loc = 'best')
ax_bench

• 当每月收盘价高于十月均线时，买入SPY基金。
• 当十月均线的动量为正时，买入SPY基金。（动量是移动平均过程中的第一个差值，即MOtq = MAtq - MAt-1q。）

（我最早在这里知道了这些策略。）普遍的经验仍然成立：对于一个包含大量活跃交易的复杂交易系统，如果一个涉及指数基金且不进行频繁交易的简单策略击败了它，那么不要使用这个复杂系统。实际上，这个要求很难满足。

问题

问题2

1. 当移动均线相差固定金额时，触发交易
2. 当移动均线相差一定数值的（滚动）标准差时，触发交易，标准差根据如下公式定义:

（pandas确实提供了计算滚动标准差的方法。）对于后者，如果移动均线相差p x SDtn，交易信号将被释放。

问题3

|
3月前
|

【python】Python航空公司客户价值数据分析（代码+论文）【独一无二】
【python】Python航空公司客户价值数据分析（代码+论文）【独一无二】
474 1
|
3月前
|

Python跳水：探索数据分析的深渊
Python跳水：探索数据分析的深渊
31 0
|
3月前
|

【Python】Python商业公司贸易业务数据分析可视化（数据+源码）【独一无二】
【Python】Python商业公司贸易业务数据分析可视化（数据+源码）【独一无二】
63 0
|
3月前
|

Python数据挖掘项目实战——自动售货机销售数据分析
Python数据挖掘项目实战——自动售货机销售数据分析
175 0
|
3月前
|

「一行分析」利用12000条招聘数据分析Python学习方向和就业方向
「一行分析」利用12000条招聘数据分析Python学习方向和就业方向
45 0
|
3月前
|

Python在体育分析中的应用：从数据到洞察
【4月更文挑战第12天】Python在体育数据分析中扮演重要角色，利用其强大的数据处理（如Pandas, NumPy）和可视化工具（Matplotlib, Seaborn），以及机器学习库（Scikit-learn, TensorFlow），可提升球队表现和训练效率。基本流程包括数据获取、预处理、探索、模型选择与训练、评估优化及结果可视化。通过球员表现、球队战术分析和赛事预测等案例，展示了Python在体育领域的广泛应用。要精通Python体育数据分析，需持续学习和实践。
106 2
|
8月前
|

Python数据挖掘实用案例——自动售货机销售数据分析与应用（二）
Python数据挖掘实用案例——自动售货机销售数据分析与应用（二）
663 0
|
3月前
|

【python】Python国内GDP经济总量数据分析可视化(源码+报告)【独一无二】
【python】Python国内GDP经济总量数据分析可视化(源码+报告)【独一无二】
250 0
|
3月前
|

Python爬取哈尔滨旅游爆火视频数据并进行可视化分析
Python爬取哈尔滨旅游爆火视频数据并进行可视化分析
124 0
|
8月前
|

Python数据挖掘实用案例——自动售货机销售数据分析与应用（一）
Python数据挖掘实用案例——自动售货机销售数据分析与应用
504 0