该策略运用均线系统判断当前趋势方向,根据趋势方向做多做空。当均线上升时判断为看涨置信度较高,做多;当均线下降时判断为看跌置信度较高,做空。该策略主要通过均线系统来判断市场走势方向,属于趋势跟随型策略。
计算一定周期(默认400周期)的加权移动平均线vwma作为均线指标。
判断均线vwma是否上升,如果上升则设置看多信号uptrend;如果下降则设置看空信号downtrend。
当uptrend为真时,做多;当downtrend为真时,平仓做空。
计算每根K线的策略收益率bar_pnl和买持收益率bar_bh。
根据季度和年度断点,计算每个季度和年度的策略收益率quarter_pnl和年度收益率year_pnl以及相应的买持收益率quarter_bh和year_bh。
在表格中展示每年度每个季度的策略收益率和买持收益率。
该策略主要依靠均线判断市场趋势方向,具有以下优势:
操作简单,通过均线指标判断市场走势,容易理解掌握。
回撤控制能力较强,跟随趋势操作,能够有效控制非趋势市的损失。
可配置参数较少,主要调整均线周期,容易测试优化。
采用表格直观展示收益情况,一目了然。
收益表格中添加买持收益进行对比,可以明确策略增量收益。
可灵活设置表格位置,方便组合其他策略使用。
该策略也存在一些风险:
Bulk market风险,在长期持续的牛市中,相比买持策略可能收益略低。可以适当调整均线周期来优化。
震荡行情下 whipsaw风险较大。可考虑增加过滤条件,如突破前高点等,来减少反复 transactions。
均线系统对曲线拟合性不佳,可能错过趋势转折点。可以试验不同类型均线指标。
未考虑止损退出机制,存在大幅回撤风险。可以设置动态止损或考虑降低仓位。
表格优化方面,可考虑添加 sharpe ratio,最大回撤等风险指标。
该策略可以从以下几个方面进行优化:
优化均线参数,调整均线周期适应不同市场环境。
增加过滤条件,如突破前高点等,以减少 whipsaw。
尝试不同类型均线,如加权移动均线,双指数移动均线等。
加入止损机制,可以设置动态止损或考虑降低仓位。
丰富表格内容,添加 sharpe ratio,最大回撤等指标。
结合其他指标,如MACD,Bollinger Bands等判断趋势。
优化仓位管理,根据市场情况动态调整仓位。
测试不同标的运行效果,寻找最佳适用范围。
该均线交易策略整体较为简单直接,通过均线判断趋势操作,回撤控制能力较强,适合跟随趋势型交易者。优化空间还很大,可从均线系统、止损机制、仓位管理等方面进行优化,使策略更适应复杂市场环境。表格设计展示了策略与买持收益比较,直观展示策略增量价值。该策略有效框架和表格展示思路,对于量化交易者具有一定的借鉴作用。
/*backtest
start: 2022-10-23 00:00:00
end: 2023-10-29 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Dannnnnnny
//@version=4
strategy(title="Quarterly Returns in Strategies vs Buy & Hold", initial_capital= 1000, overlay=true,default_qty_type = strategy.percent_of_equity, default_qty_value = 100, commission_type = strategy.commission.percent, commission_value = 0.1)
maLength= input(400)
wma= vwma(hl2,maLength)
uptrend= rising(wma, 5)
downtrend= falling(wma,5)
plot(wma)
if uptrend
strategy.entry("Buy", strategy.long)
else
strategy.close("Buy")//
///////////////////
// QUARTERLY TABLE //
enableQuarterlyTable = input(title="Enable Quarterly Return table", type=input.bool, defval=false)
enableCompareWithMarket = input(title="Compare with Market Benchmark", type=input.bool, defval=false)
table_position = input(title="Table Position", type=input.string, defval='bottom_right', options=['bottom_right','bottom_left','top_right', 'top_left'])
precision = 2
new_quarter = ceil(month(time)/3) != ceil(month(time[1])/3)
new_year = year(time) != year(time[1])
eq = strategy.equity
bar_pnl = eq / eq[1] - 1
bar_bh = (close-close[1])/close[1]
cur_quarter_pnl = 0.0
cur_year_pnl = 0.0
cur_quarter_bh = 0.0
cur_year_bh = 0.0
// Current Quarterly P&L
cur_quarter_pnl := new_quarter ? 0.0 :
(1 + cur_quarter_pnl[1]) * (1 + bar_pnl) - 1
cur_quarter_bh := new_quarter ? 0.0 :
(1 + cur_quarter_bh[1]) * (1 + bar_bh) - 1
// Current Yearly P&L
cur_year_pnl := new_year ? 0.0 :
(1 + cur_year_pnl[1]) * (1 + bar_pnl) - 1
cur_year_bh := new_year ? 0.0 :
(1 + cur_year_bh[1]) * (1 + bar_bh) - 1
// Arrays to store Yearly and Quarterly P&Ls
var quarter_pnl = array.new_float(0)
var quarter_time = array.new_int(0)
var quarter_bh = array.new_float(0)
var year_pnl = array.new_float(0)
var year_time = array.new_int(0)
var year_bh = array.new_float(0)
end_time = false
end_time:= time_close + (time_close - time_close[1]) > timenow or barstate.islastconfirmedhistory
if (not na(cur_quarter_pnl[1]) and (new_quarter or end_time))
if (end_time[1])
array.pop(quarter_pnl)
array.pop(quarter_time)
array.push(quarter_pnl , cur_quarter_pnl[1])
array.push(quarter_time, time[1])
array.push(quarter_bh , cur_quarter_bh[1])
if (not na(cur_year_pnl[1]) and (new_year or end_time))
if (end_time[1])
array.pop(year_pnl)
array.pop(year_time)
array.push(year_pnl , cur_year_pnl[1])
array.push(year_time, time[1])
array.push(year_bh , cur_year_bh[1])
// Quarterly P&L Table
var quarterly_table = table(na)
getCellColor(pnl, bh) =>
if pnl > 0
if bh < 0 or pnl > 2 * bh
color.new(color.green, transp = 20)
else if pnl > bh
color.new(color.green, transp = 50)
else
color.new(color.green, transp = 80)
else
if bh > 0 or pnl < 2 * bh
color.new(color.red, transp = 20)
else if pnl < bh
color.new(color.red, transp = 50)
else
color.new(color.red, transp = 80)
if (end_time and enableQuarterlyTable)
quarterly_table := table.new(table_position, columns = 14, rows = array.size(year_pnl) + 1, border_width = 1)
table.cell(quarterly_table, 0, 0, "", bgcolor = #cccccc)
table.cell(quarterly_table, 1, 0, "Q1", bgcolor = #cccccc)
table.cell(quarterly_table, 2, 0, "Q2", bgcolor = #cccccc)
table.cell(quarterly_table, 3, 0, "Q3", bgcolor = #cccccc)
table.cell(quarterly_table, 4, 0, "Q4", bgcolor = #cccccc)
table.cell(quarterly_table, 5, 0, "Year", bgcolor = #999999)
for yi = 0 to array.size(year_pnl) - 1
table.cell(quarterly_table, 0, yi + 1, tostring(year(array.get(year_time, yi))), bgcolor = #cccccc)
y_color = getCellColor(array.get(year_pnl, yi), array.get(year_bh, yi))
table.cell(quarterly_table, 5, yi + 1, enableCompareWithMarket ? tostring(round(array.get(year_pnl, yi) * 100, precision)) + " (" + tostring(round(array.get(year_bh, yi) * 100, precision)) + ")" : tostring(round(array.get(year_pnl, yi) * 100, precision)), bgcolor = y_color, text_color=#bfbfbf)
for mi = 0 to array.size(quarter_time) - 1
m_row = year(array.get(quarter_time, mi)) - year(array.get(year_time, 0)) + 1
m_col = ceil(month(array.get(quarter_time, mi)) / 3)
m_color = getCellColor(array.get(quarter_pnl, mi), array.get(quarter_bh, mi))
table.cell(quarterly_table, m_col, m_row, enableCompareWithMarket ? tostring(round(array.get(quarter_pnl, mi) * 100, precision)) + " (" + tostring(round(array.get(quarter_bh, mi) * 100,precision)) +")" : tostring(round(array.get(quarter_pnl, mi) * 100, precision)), bgcolor = m_color, text_color=#bfbfbf)