22 KiB
Name
移动平均线交叉多周期策略Moving-Average-Crossover-Multi-Timeframe-Strategy
Author
ChaoZhang
Strategy Description
[trans]
概述
该策略是基于移动平均线的交叉系统,通过不同周期的移动平均线 golden交叉 和 death交叉来判断买入和卖出时机。策略同时结合趋势跟踪止损、止盈、止损等功能来锁定盈利和规避风险。
策略原理
该策略使用两组移动平均线,分别是快线和慢线。快线周期较短,代表短期趋势;慢线周期较长,代表长期趋势。当快线从下方向上突破慢线时,产生golden交叉,表示行情由熊转牛,做多;当快线从上方向下跌破慢线时,形成death交叉,表示行情由牛转熊,做空。
代码中,快线指标为ma1,慢线为ma2。ma1和ma2均可选择不同类型的移动平均线,如SMA、EMA等,并可设定不同的周期参数。ma1代表短期趋势,周期较短;ma2代表长期趋势,周期较长。
当ma1金叉ma2时,产生long信号,做多;当ma1死叉ma2时,产生short信号,做空。实际交易时,可结合趋势跟踪止损、止盈、止损等功能,来锁定盈利和规避风险。
优势分析
该策略具有以下优势:
-
策略思路简单清晰,容易理解和实现。
-
可灵活选择不同类型和参数的移动平均线,适用于不同市场环境。
-
多周期设计,同时捕捉短期和长期趋势,避免被错杀。
-
可自定义开仓条件,严格控制交易频率。
-
可设置止损、止盈条件,有效控制风险。
-
可添加趋势跟踪止损,实现盈利跟踪。
-
可优化参数,使策略更稳定可靠。
风险分析
该策略也存在以下风险:
-
双移动平均线交叉本身滞后,可能错过价格反转的最佳时机。
-
移动平均线周期设置不当,可能产生更多假信号。
-
突发事件造成快速反转,止损被击穿的风险。
-
趋势行情中,价格长期滞留在均线一侧的概率较大。
-
参数优化不当,可能过度优化到特定时间段。
对应风险管理措施:
-
结合其他指标过滤信号,规避假突破。
-
周期设置要遵循趋势交易原则,测试优化参数。
-
风险控制要审慎,止损位置设置合理。
-
需承担一定的耐心等待成本。
-
多种市场环境中测试参数健壮性。
优化方向
该策略可以从以下方面进行优化:
-
测试更多类型的移动平均线,如加权移动平均线等。
-
增加基于波动率的动态周期,根据市场变化调整均线参数。
-
策略入场条件可加选时间或基本面过滤,降低错误交易率。
-
出场条件可设定动态止盈止损,根据市场波动程度调整止损幅度。
-
可建立参数优化系统,实现策略的历史回测与参数调优。
-
增加机器学习算法,利用AI来优化参数和过滤信号。
总结
该移动平均线交叉多周期策略整体思路清晰易懂,通过快慢均线交叉来捕捉趋势,是一种较为经典的趋势跟踪策略。策略可以通过选取合适的参数、优化入场出场逻辑、严格风险控制来实现稳定的盈利。但用户需要承担一定的滞后 recognizes风险和等待时间成本。总体来说,该策略简单实用,值得进行参数优化和风险控制,以适应更多市场环境。
||
Overview
This strategy is based on moving average crossover system, using golden cross and death cross of moving averages in different timeframes to determine entry and exit points. It also combines features like trailing stop loss, take profit and stop loss to lock in profits and mitigate risks.
Strategy Logic
The strategy utilizes two sets of moving averages, fast MA and slow MA. The fast MA has a shorter period to capture short-term trend, while the slow MA has a longer period for long-term trend. When the fast MA crosses above the slow MA, a golden cross occurs, signaling an uptrend. When the fast MA crosses below the slow MA, a death cross occurs, signaling a downtrend.
In the code, the fast MA is ma1, slow MA is ma2. Both ma1 and ma2 can be different types like SMA, EMA, with customizable periods. ma1 represents short-term trend with shorter period, ma2 represents long-term trend with longer period.
When ma1 golden crosses ma2, a long signal is generated. When ma1 death crosses ma2, a short signal is generated. In actual trading, features like trailing stop loss, take profit and stop loss can be added to lock in profits and control risks.
Advantage Analysis
The strategy has the following advantages:
-
Simple and easy-to-understand logic.
-
Flexible in choosing different types and parameters of MAs for different market conditions.
-
Multi-timeframe design to capture both short and long term trends.
-
Customizable entry rules to strictly control trading frequency.
-
Configurable stop loss and take profit to effectively manage risks.
-
Trend trailing stop loss allows profit to run.
-
Optimizable parameters for more robustness.
Risk Analysis
The strategy also has the following risks:
-
Lagging issue of dual MA crossovers may miss best reversal timing.
-
Improper MA periods may generate more false signals.
-
Sudden reversals may hit stop loss.
-
Price may stay on one side of MA for extended periods in trending markets.
-
Over-optimization over fitted parameters.
Risk management measures:
-
Add filters to avoid false breakout signals.
-
Test and optimize MA periods based on trading principles.
-
Careful risk control and reasonable stop loss placement.
-
Accept necessary cost of patience.
-
Robustness test under different market conditions.
Optimization Directions
The strategy can be improved from the following aspects:
-
Test more types of MA, like weighted moving average.
-
Add dynamic periods based on volatility.
-
Add filters like time and fundamentals to entry rules.
-
Use adaptive stops that adjust to market volatility.
-
Build parameter optimization system for backtesting.
-
Incorporate machine learning to optimize parameters and filter signals.
Conclusion
In conclusion, this moving average crossover multi timeframe strategy has simple and clear logic to follow trends using fast and slow MA crossovers. With proper parameter selection, optimized entry/exit rules and risk control, it can achieve steady profits. However, users need to tolerate lagging risks and waiting time costs. Overall it is a simple, practical strategy worth optimizing and risk control for adapting to more market conditions.
[/trans]
Strategy Arguments
| Argument | Default | Description |
|---|---|---|
| v_input_1 | 2018 | Backtest Start Year |
| v_input_2 | true | Backtest Start Month |
| v_input_3 | true | Backtest Start Day |
| v_input_4 | 9999 | Backtest Stop Year |
| v_input_5 | 12 | Backtest Stop Month |
| v_input_6 | 31 | Backtest Stop Day |
| v_input_7 | 0 | --------Moving Average 1----------: ---- |
| v_input_8 | false | Use Different Resolution? |
| v_input_9 | 60 | Set Resolution Minutes |
| v_input_10 | 0 | MA: EMA |
| v_input_11_close | 0 | Source: close |
| v_input_12 | 15 | Period |
| v_input_13 | true | Least Squares (LSMA) Only - Offset Value |
| v_input_14 | 0.85 | Arnaud Legoux (ALMA) Only - Offset Value |
| v_input_15 | 6 | Arnaud Legoux (ALMA) Only - Sigma Value |
| v_input_16 | 0 | --------Moving Average 2----------: ---- |
| v_input_17 | false | Use Different Resolution? |
| v_input_18 | 60 | Set Resolution Minutes |
| v_input_19 | 0 | MA: EMA |
| v_input_20_close | 0 | Source: close |
| v_input_21 | 30 | Period |
| v_input_22 | true | Least Squares (LSMA) Only - Offset Value |
| v_input_23 | 0.85 | Arnaud Legoux (ALMA) Only - Offset Value |
| v_input_24 | 6 | Arnaud Legoux (ALMA) Only - Sigma Value |
| v_input_25 | false | Longs Only |
| v_input_26 | false | Shorts Only |
| v_input_27 | false | Flip the Opens |
| v_input_28 | true | Pyramiding less than |
| v_input_29 | false | Pyramiding equal to |
| v_input_30 | 1000000 | Pyramiding greater than |
| v_input_31 | false | Trailing Stop |
| v_input_32 | 1000 | Activate Trailing Stop Price (%). Divided by 100 (1 = 0.01%) |
| v_input_33 | 575 | Trailing Stop (%). Divided by 100 (1 = 0.01%) |
| v_input_34 | false | Take Profit |
| v_input_35 | 300 | Take Profit (%). Divided by 100 (1 = 0.01%) |
| v_input_36 | false | Stop Loss |
| v_input_37 | 575 | Stop Loss (%). Divided by 100 (1 = 0.01%) |
Source (PineScript)
/*backtest
start: 2023-09-08 00:00:00
end: 2023-10-08 00:00:00
period: 4h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/
//@version=3
// The majority of this script I took from the Autoview website. There are some typos in the original that I've fixed, some things I've added, things I will add, and I'm tired pulling my strategy code out and uploading this to pastebin for people.
// DISCLAIMER: I am not a financial advisor, this is not financial advice, do not use this code without first doing your own research, etc, etc, it's not my fault when you lose your house.
strategy("Moving Averages Cross - MTF - Strategy", "MA Cross", overlay=true, pyramiding=0, initial_capital=100000, currency=currency.USD, default_qty_type = strategy.percent_of_equity, default_qty_value = 100, commission_type=strategy.commission.percent, commission_value=0.1)
bgcolor ( color=black, transp=40, title='Blackground', editable=true)
///////////////////////////////////////////////
//* Backtesting Period Selector | Component *//
///////////////////////////////////////////////
//* https://www.tradingview.com/script/eCC1cvxQ-Backtesting-Period-Selector-Component *//
//* https://www.tradingview.com/u/pbergden/ *//
//* Modifications made *//
testStartYear = input(2018, "Backtest Start Year")
testStartMonth = input(1, "Backtest Start Month")
testStartDay = input(1, "Backtest Start Day")
testPeriodStart = timestamp(testStartYear,testStartMonth,testStartDay,00,00)
testStopYear = input(9999, "Backtest Stop Year")
testStopMonth = input(12, "Backtest Stop Month")
testStopDay = input(31, "Backtest Stop Day")
testPeriodStop = timestamp(testStopYear,testStopMonth,testStopDay,0,0)
testPeriod() => true
/////////////////////////////////////
//* Put your strategy logic below *//
/////////////////////////////////////
sp1 = input("----", title="--------Moving Average 1----------", options=["----"])
maUseRes1 = input(defval = false, title = "Use Different Resolution?")
//maReso1 = input(defval = "60", title = "Set Resolution", type = resolution)
maReso1 = input(defval='60', title = "Set Resolution Minutes")
maType1 = input("EMA", title="MA", options=["SMA", "EMA", "DEMA", "TEMA", "WMA", "VWMA", "SMMA", "Hull", "LSMA", "ALMA"])
maSource1 = input(defval = close, title = "Source")
maLength1 = input(defval = 15, title = "Period", minval = 1)
lsmaOffset1 = input(defval = 1, title = "Least Squares (LSMA) Only - Offset Value", minval = 0)
almaOffset1 = input(defval = 0.85, title = "Arnaud Legoux (ALMA) Only - Offset Value", minval = 0, step = 0.01)
almaSigma1 = input(defval = 6, title = "Arnaud Legoux (ALMA) Only - Sigma Value", minval = 0)
sp2 = input("----", title="--------Moving Average 2----------", options=["----"])
maUseRes2 = input(defval = false, title = "Use Different Resolution?")
//maReso2 = input(defval = "60", title = "Set Resolution", type = resolution)
maReso2 = input(defval='60', title = "Set Resolution Minutes")
maType2 = input("EMA", title="MA", options=["SMA", "EMA", "DEMA", "TEMA", "WMA", "VWMA", "SMMA", "Hull", "LSMA", "ALMA"])
maSource2 = input(defval = close, title = "Source")
maLength2 = input(defval = 30, title = "Period", minval = 1)
lsmaOffset2 = input(defval = 1, title = "Least Squares (LSMA) Only - Offset Value", minval = 0)
almaOffset2 = input(defval = 0.85, title = "Arnaud Legoux (ALMA) Only - Offset Value", minval = 0, step = 0.01)
almaSigma2 = input(defval = 6, title = "Arnaud Legoux (ALMA) Only - Sigma Value", minval = 0)
//Function from @JayRogers thank you man awesome work
variant(type, src, len, lsmaOffset, almaOffset, almaSigma) =>
v1 = sma(src, len) // Simple
v2 = ema(src, len) // Exponential
v3 = 2 * v2 - ema(v2, len) // Double Exponential
v4 = 3 * (v2 - ema(v2, len)) + ema(ema(v2, len), len) // Triple Exponential
v5 = wma(src, len) // Weighted
v6 = vwma(src, len) // Volume Weighted
v7 = na(v5[1]) ? sma(src, len) : (v5[1] * (len - 1) + src) / len // Smoothed
v8 = wma(2 * wma(src, len / 2) - wma(src, len), round(sqrt(len))) // Hull
v9 = linreg(src, len, lsmaOffset) // Least Squares
v10 = alma(src, len, almaOffset, almaSigma) // Arnaud Legoux
type=="EMA"?v2 : type=="DEMA"?v3 : type=="TEMA"?v4 : type=="WMA"?v5 : type=="VWMA"?v6 : type=="SMMA"?v7 : type=="Hull"?v8 : type=="LSMA"?v9 : type=="ALMA"?v10 : v1
//Different resolution function
reso(exp, res, use) => use ? security(tickerid, res, exp) : exp
ma1 = reso(variant(maType1, maSource1, maLength1, lsmaOffset1, almaOffset1, almaSigma1), maReso1, maUseRes1)
ma2 = reso(variant(maType2, maSource2, maLength2, lsmaOffset2, almaOffset2, almaSigma2), maReso2, maUseRes2)
plotma1 = plot(ma1, color=green, tranps=50, linewidth = 2 )
plotma2 = plot(ma2, color=red, tranps=50, linewidth = 2 )
// Long/Short Logic
longLogic = crossover(ma1,ma2) ? 1 : 0
shortLogic = crossunder(ma1,ma2) ? 1 : 0
//////////////////////////
//* Strategy Component *//
//////////////////////////
isLong = input(false, "Longs Only")
isShort = input(false, "Shorts Only")
isFlip = input(false, "Flip the Opens")
long = longLogic
short = shortLogic
if isFlip
long := shortLogic
short := longLogic
else
long := longLogic
short := shortLogic
if isLong
long := long
short := na
if isShort
long := na
short := short
////////////////////////////////
//======[ Signal Count ]======//
////////////////////////////////
sectionLongs = 0
sectionLongs := nz(sectionLongs[1])
sectionShorts = 0
sectionShorts := nz(sectionShorts[1])
if long
sectionLongs := sectionLongs + 1
sectionShorts := 0
if short
sectionLongs := 0
sectionShorts := sectionShorts + 1
//////////////////////////////
//======[ Pyramiding ]======//
//////////////////////////////
pyrl = input(1, "Pyramiding less than") // If your count is less than this number
pyre = input(0, "Pyramiding equal to") // If your count is equal to this number
pyrg = input(1000000, "Pyramiding greater than") // If your count is greater than this number
longCondition = long and sectionLongs <= pyrl or long and sectionLongs >= pyrg or long and sectionLongs == pyre ? 1 : 0
shortCondition = short and sectionShorts <= pyrl or short and sectionShorts >= pyrg or short and sectionShorts == pyre ? 1 : 0
////////////////////////////////
//======[ Entry Prices ]======//
////////////////////////////////
last_open_longCondition = na
last_open_shortCondition = na
last_open_longCondition := longCondition ? close : nz(last_open_longCondition[1])
last_open_shortCondition := shortCondition ? close : nz(last_open_shortCondition[1])
////////////////////////////////////
//======[ Open Order Count ]======//
////////////////////////////////////
sectionLongConditions = 0
sectionLongConditions := nz(sectionLongConditions[1])
sectionShortConditions = 0
sectionShortConditions := nz(sectionShortConditions[1])
if longCondition
sectionLongConditions := sectionLongConditions + 1
sectionShortConditions := 0
if shortCondition
sectionLongConditions := 0
sectionShortConditions := sectionShortConditions + 1
///////////////////////////////////////////////
//======[ Position Check (long/short) ]======//
///////////////////////////////////////////////
last_longCondition = na
last_shortCondition = na
last_longCondition := longCondition ? time : nz(last_longCondition[1])
last_shortCondition := shortCondition ? time : nz(last_shortCondition[1])
in_longCondition = last_longCondition > last_shortCondition
in_shortCondition = last_shortCondition > last_longCondition
/////////////////////////////////////
//======[ Position Averages ]======//
/////////////////////////////////////
totalLongs = 0.0
totalLongs := nz(totalLongs[1])
totalShorts = 0.0
totalShorts := nz(totalShorts[1])
averageLongs = 0.0
averageLongs := nz(averageLongs[1])
averageShorts = 0.0
averageShorts := nz(averageShorts[1])
if longCondition
totalLongs := totalLongs + last_open_longCondition
totalShorts := 0.0
if shortCondition
totalLongs := 0.0
totalShorts := totalShorts + last_open_shortCondition
averageLongs := totalLongs / sectionLongConditions
averageShorts := totalShorts / sectionShortConditions
/////////////////////////////////
//======[ Trailing Stop ]======//
/////////////////////////////////
isTS = input(false, "Trailing Stop")
tsi = input(1000, "Activate Trailing Stop Price (%). Divided by 100 (1 = 0.01%)") / 100
ts = input(575, "Trailing Stop (%). Divided by 100 (1 = 0.01%)") / 100
last_high = na
last_low = na
last_high_short = na
last_low_short = na
last_high := not in_longCondition ? na : in_longCondition and (na(last_high[1]) or high > nz(last_high[1])) ? high : nz(last_high[1])
last_high_short := not in_shortCondition ? na : in_shortCondition and (na(last_high[1]) or high > nz(last_high[1])) ? high : nz(last_high[1])
last_low := not in_shortCondition ? na : in_shortCondition and (na(last_low[1]) or low < nz(last_low[1])) ? low : nz(last_low[1])
last_low_short := not in_longCondition ? na : in_longCondition and (na(last_low[1]) or low < nz(last_low[1])) ? low : nz(last_low[1])
long_ts = isTS and not na(last_high) and low <= last_high - last_high / 100 * ts and longCondition == 0 and last_high >= averageLongs + averageLongs / 100 * tsi
short_ts = isTS and not na(last_low) and high >= last_low + last_low / 100 * ts and shortCondition == 0 and last_low <= averageShorts - averageShorts/ 100 * tsi
///////////////////////////////
//======[ Take Profit ]======//
///////////////////////////////
isTP = input(false, "Take Profit")
tp = input(300, "Take Profit (%). Divided by 100 (1 = 0.01%)") / 100
long_tp = isTP and close > averageLongs + averageLongs / 100 * tp and not longCondition
short_tp = isTP and close < averageShorts - averageShorts / 100 * tp and not shortCondition
/////////////////////////////
//======[ Stop Loss ]======//
/////////////////////////////
isSL = input(false, "Stop Loss")
sl = input(575, "Stop Loss (%). Divided by 100 (1 = 0.01%)") / 100
long_sl = isSL and close < averageLongs - averageLongs / 100 * sl and longCondition == 0
short_sl = isSL and close > averageShorts + averageShorts / 100 * sl and shortCondition == 0
/////////////////////////////////
//======[ Close Signals ]======//
/////////////////////////////////
longClose = long_tp or long_sl or long_ts ? 1 : 0
shortClose = short_tp or short_sl or short_ts ? 1: 0
///////////////////////////////
//======[ Plot Colors ]======//
///////////////////////////////
longCloseCol = na
shortCloseCol = na
longCloseCol := long_tp ? purple : long_sl ? maroon : long_ts ? blue : longCloseCol[1]
shortCloseCol := short_tp ? purple : short_sl ? maroon : short_ts ? blue : shortCloseCol[1]
tpColor = isTP and in_longCondition ? purple : isTP and in_shortCondition ? purple : white
slColor = isSL and in_longCondition ? red : isSL and in_shortCondition ? red : white
//////////////////////////////////
//======[ Strategy Plots ]======//
//////////////////////////////////
// Comment out these lines to use alerts
plot(isTS and in_longCondition ? averageLongs + averageLongs / 100 * tsi : na, "Long Trailing Activate", blue, style=3, linewidth=2)
plot(isTS and in_longCondition and last_high >= averageLongs + averageLongs / 100 * tsi ? last_high - last_high / 100 * ts : na, "Long Trailing", fuchsia, style=2, linewidth=3)
plot(isTS and in_shortCondition ? averageShorts - averageShorts/ 100 * tsi : na, "Short Trailing Activate", blue, style=3, linewidth=2)
plot(isTS and in_shortCondition and last_low <= averageShorts - averageShorts/ 100 * tsi ? last_low + last_low / 100 * ts : na, "Short Trailing", fuchsia, style=2, linewidth=3)
plot(isTP and in_longCondition and last_high < averageLongs + averageLongs / 100 * tp ? averageLongs + averageLongs / 100 * tp : na, "Long TP", tpColor, style=3, linewidth=2)
plot(isTP and in_shortCondition and last_low > averageShorts - averageShorts / 100 * tp ? averageShorts - averageShorts / 100 * tp : na, "Short TP", tpColor, style=3, linewidth=2)
plot(isSL and in_longCondition and last_low_short > averageLongs - averageLongs / 100 * sl ? averageLongs - averageLongs / 100 * sl : na, "Long SL", slColor, style=3, linewidth=2)
plot(isSL and in_shortCondition and last_high_short < averageShorts + averageShorts / 100 * sl ? averageShorts + averageShorts / 100 * sl : na, "Short SL", slColor, style=3, linewidth=2)
///////////////////////////////
//======[ Alert Plots ]======//
///////////////////////////////
// Uncomment to use Alerts, or the new Signal Plots, but not both
// Old Signal Plots
//plot(longCondition, "Long", green)
//plot(shortCondition, "Short", red)
//plot(longClose, "Long Close", longCloseCol)
//plot(shortClose, "Short Close", shortCloseCol)
// Uncomment for your alerts
//alertcondition(condition=longCondition, title="Long", message="")
//alertcondition(condition=shortCondition, title="Short", message="")
//alertcondition(condition=longClose, title="Long Close", message="")
//alertcondition(condition=shortClose, title="Short Close", message="")
///////////////////////////////////
//======[ Reset Variables ]======//
///////////////////////////////////
if longClose or not in_longCondition
averageLongs := 0
totalLongs := 0.0
sectionLongs := 0
sectionLongConditions := 0
if shortClose or not in_shortCondition
averageShorts := 0
totalShorts := 0.0
sectionShorts := 0
sectionShortConditions := 0
////////////////////////////////////////////
//======[ Strategy Entry and Exits ]======//
////////////////////////////////////////////
// Comment out to use alerts
if testPeriod()
strategy.entry("Long", 1, when=longCondition)
strategy.entry("Short", 0, when=shortCondition)
strategy.close("Long", when=longClose)
strategy.close("Short", when=shortClose)
Detail
https://www.fmz.com/strategy/428810
Last Modified
2023-10-09 16:41:04