
这个策略的主要思想是利用突破带识别趋势方向,结合固定止损进行风险管理。策略首先计算一定周期内的最高价和最低价,形成突破带。当价格突破突破带时产生交易信号。此外,策略允许交易者设置固定止损金额。每次交易时,系统会根据固定止损金额反向计算交易数量,从而实现每单固定损失。
该策略主要由四个部分组成:仓位管理、突破带识别、止损设定和数量计算。
首先,策略要判断当前是否持有仓位。如果已持有仓位,则不产生新信号。
其次,策略会计算一定周期内的最高价和最低价,形成突破带。当价格从突破带内部突破到外部时,产生交易信号。具体来说,如果价格突破突破带上轨,产生做多信号;如果价格突破突破带下轨,产生做空信号。
此外,做多信号产生时,策略会将突破带中点设定为止损位。做空信号产生时,也会设定止损位。为了进行追踪止损,策略还会在持仓期间实时调整止损位。
最后,策略允许设定固定止损金额。当信号产生时,策略会计算止损点到当前价格的点数距离,再结合报价单位、汇率等因素,计算出止损点间的价格变动所代表的金额。然后根据固定止损额反向计算交易数量。
以上就是策略的主要原理。通过突破带识别趋势方向,并利用固定止损进行风险控制,这是该策略的核心思想。
这种突破带固定止损策略具有以下优势:
止损思想先进。策略采用固定止损金额而不是固定止损距离。这避免了不同品种间由于点值不同带来的无法固定风险的问题。从风险管理角度看,固定金额止损更先进。
数量计算合理。策略能够根据固定止损金额智能计算交易数量,使得每单损失可控,从而对风险敞口进行合理控制。
突破识别简单有效。突破带的识别方式简单直接,能够有效识别趋势方向。与仅突破某价格级别相比,这样的突破带识别可避免更多脱离趋势方向的假信号。
追踪止损增加获利。策略能够实时调整止损位置,进行追踪止损,帮助锁定更多利润。
适用范围广泛。策略适用于任何品种,只要设置好参数,就可以进行固定金额止损的风险控制,从而具有非常广泛的适用性。
代码结构清晰。策略代码结构合理清晰,各功能模块解耦良好,便于理解及后续优化。
尽管该策略具有上述优势,但仍存在一定风险需要注意:
突破形态质量无法判断。策略中无法判断突破的形态质量,可能产生一些低质量的信号。需要结合其它指标进行过滤。
固定止损可能过于机械。市场价格往往具有跳空行情的特点,固定止损可能会过于依赖规则,无法灵活调整。
无法限制交易频率。策略无法限制交易频率,可能会过于频繁出场。需要结合其它规则限制频率。
固定止损依赖参数设定。固定止损额的设定关乎整体敞口控制,需要根据资金规模、风险偏好等多方面进行合理设置。
突破方向可能产生错误信号。当价格出现震荡或回调时,可能会产生错误的突破信号。需要结合多重条件进行优化。
缺乏止盈设定。策略当前没有止盈机制,无法主动确定利润。这可能导致利润不理想。
针对上述风险,我们可以从以下几个方面进行优化:
添加指标进行形态判断,过滤信号质量。例如MACD、KD等。
结合突破强度指标评估突破质量。例如通过成交量变化判断突破强弱。
增加开仓频率限制。例如每天只交易一次或类似规则。
优化固定止损设定逻辑。例如根据特定阈值改为百分比止损等。
增加其它过滤条件。例如增强止损、价格波动率等。
添加止盈策略。例如靠近阻力位时止盈。
根据上述分析,该策略可以从以下几个方面进行优化:
增加过滤条件,提高信号质量。可以加入多种技术指标,判断趋势质量,避免不理想的突破信号。还可以判断突破强度。
优化止损策略,使之更灵活。可以在突破回调一定距离后改为比例止损。也可以根据波动率实时优化止损距离。
控制交易频率,避免过度交易。可以针对时间段或次数设置过滤条件,降低交易频率。
结合趋势判断指标,改进入场时机选择。例如优化为在趋势确认后再入场。
优化止盈策略,提高盈利能力。可以设定目标利润、移动止盈、波动止盈等方式。
优化风险参数设定。可以根据回测结果设定更优的参数组合,如固定止损金额、突破周期等。
改进代码结构,增强可扩展性。将信号生成、过滤、风控、盈利等模块进行进一步解耦。
测试更多品种套利空间。评估不同品种组合的套利优势。
通过以上多方面优化,可以进一步增强该突破止损策略的稳定性和盈利能力。同时也为未来扩展到更多策略组合搭建基础。
该策略整体思路合理,采用突破带识别趋势,并利用固定金额止损进行风险控制。这在风险管理上具有进步性。同时计算交易数量的思路也较为合理,能够对每单亏损进行控制。但策略可以通过多方面优化以提高信号质量、止损策略的灵活性、盈利水平等。如果结合趋势判断指标进行过滤,改进止盈方式,并严格控制交易频率,该策略的效果还具有很大提升空间。整体来说,该策略提供了一套可供学习的风险管理和数量计算方法,为进一步研究套利和多策略组合搭建了基础。
/*backtest
start: 2023-10-26 00:00:00
end: 2023-10-28 03:00:00
period: 10m
basePeriod: 1m
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/
//@version=4
//@author=Takazudo
strategy("Fixed price SL",
overlay=true,
default_qty_type=strategy.fixed,
initial_capital=0,
currency=currency.USD)
var COLOR_TRANSPARENT = color.new(#000000, 100)
var COLOR_ENTRY_BAND = color.new(#43A6F5, 30)
//============================================================================
// config
//============================================================================
// Money management
_g1 = 'Money management'
var config_riskPrice = input(100, minval=1, title="Risk price for each entry", group=_g1)
var config_depositCurrency = input(title="Deposit currency", type=input.string, defval="USD", options=["USD"], group=_g1)
// Entry strategy
_g2 = 'Entry strategy'
var config_entryBandBars = input(defval = 100, title = "Entry band bar count", minval=1, group=_g2)
// Backtesting range
_g3 = 'Backtesting range'
fromYear = input(defval = 2018, title = "From Year", minval = 1970, group=_g3)
fromMonth = input(defval = 1, title = "From Month", minval = 1, maxval = 12, group=_g3)
fromDay = input(defval = 1, title = "From Day", minval = 1, maxval = 31, group=_g3)
toYear = input(defval = 2020, title = "To Year", minval = 1970, group=_g3)
toMonth = input(defval = 12, title = "To Month", minval = 1, maxval = 12, group=_g3)
toDay = input(defval = 31, title = "To Day", minval = 1, maxval = 31, group=_g3)
//============================================================================
// exchange caliculations
//============================================================================
// mico pip size caliculation
// ex1: AUDCAD -> 0.0001
// ex2: USDJPY -> 0.01
f_calcMicroPipSize() =>
_base = syminfo.basecurrency
_quote = syminfo.currency
_result = 0.0001
if _quote == 'JPY'
_result := _result * 100
if _base == 'BTC'
_result := _result * 100
_result
// convert price to pips
f_convertPriceToPips(_price) =>
_microPipSize = f_calcMicroPipSize()
_price / _microPipSize
// caliculate exchange rate between deposit and quote currency
f_calcDepositExchangeSymbolId() =>
_result = ''
_deposit = config_depositCurrency
_quote = syminfo.currency
if (_deposit == 'USD') and (_quote == 'USD')
_result := na
if (_deposit == 'USD') and (_quote == 'AUD')
_result := 'OANDA:AUDUSD'
if (_deposit == 'EUR') and (_quote == 'USD')
_result := 'OANDA:EURUSD'
if (_deposit == 'USD') and (_quote == 'GBP')
_result := 'OANDA:GBPUSD'
if (_deposit == 'USD') and (_quote == 'NZD')
_result := 'OANDA:NZDUSD'
if (_deposit == 'USD') and (_quote == 'CAD')
_result := 'OANDA:USDCAD'
if (_deposit == 'USD') and (_quote == 'CHF')
_result := 'OANDA:USDCHF'
if (_deposit == 'USD') and (_quote == 'JPY')
_result := 'OANDA:USDJPY'
_result
// Let's say we need CAD to USD exchange
// However there's only "OANDA:USDCAD" symbol.
// Then we need to invert the exhchange rate.
// this function tells us whether we should invert the rate or not
f_calcShouldInvert() =>
_result = false
_deposit = config_depositCurrency
_quote = syminfo.currency
if (_deposit == 'USD') and (_quote == 'CAD')
_result := true
if (_deposit == 'USD') and (_quote == 'CHF')
_result := true
if (_deposit == 'USD') and (_quote == 'JPY')
_result := true
_result
// caliculate how much quantity should I buy or sell
f_calcQuantitiesForEntry(_depositExchangeRate, _slPips) =>
_microPipSize = f_calcMicroPipSize()
_priceForEachPipAsDeposit = _microPipSize * _depositExchangeRate
_losePriceOnSl = _priceForEachPipAsDeposit * _slPips
floor(config_riskPrice / _losePriceOnSl)
//============================================================================
// Quantity caliculation
//============================================================================
depositExchangeSymbolId = f_calcDepositExchangeSymbolId()
// caliculate deposit exchange rate
rate = security(depositExchangeSymbolId, timeframe.period, hl2)
shouldInvert = f_calcShouldInvert()
depositExchangeRate = if config_depositCurrency == syminfo.currency
// if USDUSD, no exchange of course
1
else
// else, USDCAD to CADUSD invert if we need
shouldInvert ? (1 / rate) : rate
//============================================================================
// Range Edge caliculation
//============================================================================
f_calcEntryBand_high() =>
_highest = max(open[3], close[3])
for i = 4 to (config_entryBandBars - 1)
_highest := max(_highest, open[i], close[i])
_highest
f_calcEntryBand_low() =>
_lowest = min(open[3], close[3])
for i = 4 to (config_entryBandBars - 1)
_lowest := min(_lowest, open[i], close[i])
_lowest
entryBand_high = f_calcEntryBand_high()
entryBand_low = f_calcEntryBand_low()
entryBand_height = entryBand_high - entryBand_low
plot(entryBand_high, color=COLOR_ENTRY_BAND, linewidth=1)
plot(entryBand_low, color=COLOR_ENTRY_BAND, linewidth=1)
rangeBreakDetected_long = entryBand_high < close
rangeBreakDetected_short = entryBand_low > close
shouldMakeEntryLong = (strategy.position_size == 0) and rangeBreakDetected_long
shouldMakeEntryShort = (strategy.position_size == 0) and rangeBreakDetected_short
//============================================================================
// SL & Quantity
//============================================================================
var sl_long = hl2
var sl_short = hl2
entryQty = 0
slPips = 0.0
// just show info bubble
f_showEntryInfo(_isLong) =>
_str =
'SL pips: ' + tostring(slPips) + '\n' +
'Qty: ' + tostring(entryQty)
_bandHeight = entryBand_high - entryBand_low
_y = _isLong ? (entryBand_low + _bandHeight * 1/4) : (entryBand_high - _bandHeight * 1/4)
_style = _isLong ? label.style_label_up : label.style_label_down
label.new(bar_index, _y, _str, size=size.large, style=_style)
if shouldMakeEntryLong
sl_long := (entryBand_high + entryBand_low) / 2
slPips := f_convertPriceToPips(close - sl_long)
entryQty := f_calcQuantitiesForEntry(depositExchangeRate, slPips)
if shouldMakeEntryShort
sl_short := (entryBand_high + entryBand_low) / 2
slPips := f_convertPriceToPips(sl_short - close)
entryQty := f_calcQuantitiesForEntry(depositExchangeRate, slPips)
// trailing SL
if strategy.position_size > 0
sl_long := max(sl_long, entryBand_low)
if strategy.position_size < 0
sl_short := min(sl_short, entryBand_high)
//============================================================================
// backtest duration
//============================================================================
// Calculate start/end date and time condition
startDate = timestamp(fromYear, fromMonth, fromDay, 00, 00)
finishDate = timestamp(toYear, toMonth, toDay, 00, 00)
//============================================================================
// make entries
//============================================================================
if (true)
if shouldMakeEntryLong
strategy.entry(id="Long", long=true, stop=close, qty=entryQty)
f_showEntryInfo(true)
if shouldMakeEntryShort
strategy.entry(id="Short", long=false, stop=close, qty=entryQty)
f_showEntryInfo(false)
strategy.exit('Long-SL/TP', 'Long', stop=sl_long)
strategy.exit('Short-SL/TP', 'Short', stop=sl_short)
//============================================================================
// plot misc
//============================================================================
sl = strategy.position_size > 0 ? sl_long :
strategy.position_size < 0 ? sl_short : na
plot(sl, color=color.red, style=plot.style_cross, linewidth=2, title="SL")
value_bgcolor = rangeBreakDetected_long ? color.green :
rangeBreakDetected_short ? color.red : COLOR_TRANSPARENT
bgcolor(value_bgcolor, transp=95)