该策略运用均线系统判断当前趋势方向,根据趋势方向做多做空。当均线上升时判断为看涨置信度较高,做多;当均线下降时判断为看跌置信度较高,做空。该策略主要通过均线系统来判断市场走势方向,属于趋势跟随型策略。
计算一定周期(默认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)