Skip to content

Instantly share code, notes, and snippets.

@wuuconix
Created July 15, 2025 12:40
Show Gist options
  • Select an option

  • Save wuuconix/8042807072605dd7a8563eebf85cdbc1 to your computer and use it in GitHub Desktop.

Select an option

Save wuuconix/8042807072605dd7a8563eebf85cdbc1 to your computer and use it in GitHub Desktop.
1分钟实体突破
//@version=5
strategy("1分钟实体突破-精确计时版", overlay=true, process_orders_on_close=true)
// === 参数设置 ===
length_bb = input.int(20, "布林带周期")
mult = input.float(2.0, "布林带倍数")
ma_length = input.int(50, "均线周期")
use_trend_filter = input.bool(true, "趋势过滤")
trade_duration = input.int(10, "持仓时间(分钟)", minval=1)
// === 指标计算 ===
basis = ta.sma(close, length_bb)
dev = mult * ta.stdev(close, length_bb)
upper = basis + dev
lower = basis - dev
ma = ta.sma(close, ma_length)
// === 信号条件 ===
// 实体突破定义:收盘价突破(忽略上下影线)
body_break_upper = close > upper and open > upper[1] // 当前实体完全突破上轨
body_break_lower = close < lower and open < lower[1] // 当前实体完全突破下轨
trend_up = close > ma
trend_down = close < ma
// === 交易逻辑 ===
var int entry_bar_index = na // 记录入场K线索引
var int entry_time = na // 精确入场时间戳
// 多单条件(K线收盘后检查)
if (body_break_upper and (not use_trend_filter or trend_up) and strategy.position_size <= 0)
strategy.entry("Long", strategy.long)
alert("Long " + str.tostring(close), alert.freq_once_per_bar_close)
entry_bar_index := bar_index
entry_time := time
// 空单条件(K线收盘后检查)
if (body_break_lower and (not use_trend_filter or trend_down) and strategy.position_size >= 0)
strategy.entry("Short", strategy.short)
alert("Short " + str.tostring(close), alert.freq_once_per_bar_close)
entry_bar_index := bar_index
entry_time := time
// === 平仓逻辑 ===
// 方式1:精确时间控制(推荐)
// if (not na(entry_time) and (time - entry_time) >= trade_duration*60*1000)
// strategy.close_all()
// entry_time := na
// entry_bar_index := na
// 方式2:K线数量控制(备选)
// if (not na(entry_bar_index) and (bar_index - entry_bar_index) >= trade_duration
// strategy.close_all()
// entry_bar_index := na
// === 可视化 ===
plot(basis, "中线", color=color.orange)
plot(upper, "上轨", color=color.green)
plot(lower, "下轨", color=color.red)
plot(ma, "均线", color=color.blue)
// 标记入场点
// plotshape(body_break_upper and strategy.position_size <= 0, "↑突破", shape.triangleup, location.belowbar, color.green, size=size.small)
// plotshape(body_break_lower and strategy.position_size >= 0, "↓突破", shape.triangledown, location.abovebar, color.red, size=size.small)
@wuuconix
Copy link
Author

胜率统计优化

//@version=5
strategy("1分钟实体突破-精确计时版", overlay=true, process_orders_on_close=true)

// === 参数设置 ===
length_bb = input.int(20, "布林带周期")
mult = input.float(2.0, "布林带倍数")
ma_length = input.int(50, "均线周期")
use_trend_filter = input.bool(true, "趋势过滤")
trade_duration = input.int(10, "持仓时间(分钟)", minval=1)
stats_period = input.string("1天", "统计时段", options=["1小时", "4小时", "6小时", "12小时", "1天", "3天", "7天"])

// === 指标计算 ===
basis = ta.sma(close, length_bb)
dev = mult * ta.stdev(close, length_bb)
upper = basis + dev
lower = basis - dev
ma = ta.sma(close, ma_length)

// === 信号条件 ===
// 实体突破定义:收盘价突破(忽略上下影线)
body_break_upper = close > upper and open > upper[1]  // 当前实体完全突破上轨
body_break_lower = close < lower and open < lower[1]  // 当前实体完全突破下轨
trend_up = close > ma
trend_down = close < ma

// === 交易逻辑 ===
var int entry_bar_index = na  // 记录入场K线索引
var int entry_time = na       // 精确入场时间戳

// 多单条件(K线收盘后检查)
if (body_break_upper and (not use_trend_filter or trend_up))
    // strategy.entry("Long", strategy.long)
    alert("Long " + str.tostring(close), alert.freq_once_per_bar_close)
    entry_bar_index := bar_index
    entry_time := time

// 空单条件(K线收盘后检查)
if (body_break_lower and (not use_trend_filter or trend_down))
    // strategy.entry("Short", strategy.short)
    alert("Short  " + str.tostring(close), alert.freq_once_per_bar_close)
    entry_bar_index := bar_index
    entry_time := time

// === 可视化 ===
plot(basis, "中线", color=color.orange)
plot(upper, "上轨", color=color.green)
plot(lower, "下轨", color=color.red)
plot(ma, "均线", color=color.blue)

// 标记入场点
plotshape(body_break_upper and (not use_trend_filter or trend_up), "↑突破", shape.triangleup, location.belowbar, color.green, size=size.small)
plotshape(body_break_lower and (not use_trend_filter or trend_down), "↓突破", shape.triangledown, location.abovebar, color.red, size=size.small)


// === 币安10分钟事件合约胜率统计 ===
var array<float> all_signal_prices = array.new<float>()
var array<string> all_signal_types = array.new<string>()
var array<int> all_signal_times = array.new<int>()
var array<bool> all_signal_results = array.new<bool>()
var array<float> pending_prices = array.new<float>()
var array<string> pending_types = array.new<string>()
var array<int> pending_bars = array.new<int>()
var array<int> pending_times = array.new<int>()

// 获取统计时间窗口(毫秒)
get_time_window() =>
    switch stats_period
        "1小时" => 1 * 60 * 60 * 1000
        "4小时" => 4 * 60 * 60 * 1000
        "6小时" => 6 * 60 * 60 * 1000
        "12小时" => 12 * 60 * 60 * 1000
        "1天" => 24 * 60 * 60 * 1000
        "3天" => 3 * 24 * 60 * 60 * 1000
        "7天" => 7 * 24 * 60 * 60 * 1000

// 定义信号
long_signal = body_break_upper and (not use_trend_filter or trend_up)
short_signal = body_break_lower and (not use_trend_filter or trend_down)

// 记录新信号
if long_signal
    array.push(pending_prices, close)
    array.push(pending_types, "LONG")
    array.push(pending_bars, bar_index)
    array.push(pending_times, math.round(time))

if short_signal
    array.push(pending_prices, close)
    array.push(pending_types, "SHORT")
    array.push(pending_bars, bar_index)
    array.push(pending_times, math.round(time))

// 检查10分钟后的结果
if array.size(pending_bars) > 0
    for i = array.size(pending_bars) - 1 to 0
        signal_bar = array.get(pending_bars, i)
        if bar_index >= signal_bar + 10  // 10分钟后
            signal_price = array.get(pending_prices, i)
            signal_type = array.get(pending_types, i)
            signal_time = array.get(pending_times, i)
            
            // 判断胜负
            is_win = false
            if signal_type == "LONG"
                is_win := close > signal_price
            else if signal_type == "SHORT"
                is_win := close < signal_price
            
            // 保存到历史记录
            array.push(all_signal_prices, signal_price)
            array.push(all_signal_types, signal_type)
            array.push(all_signal_times, signal_time)
            array.push(all_signal_results, is_win)
            
            // 删除已处理的信号
            array.remove(pending_prices, i)
            array.remove(pending_types, i)
            array.remove(pending_bars, i)
            array.remove(pending_times, i)

// 统计指定时段内的胜率
time_window = get_time_window()
current_time = math.round(time)
total_count = 0
win_count = 0

if array.size(all_signal_times) > 0
    for i = 0 to array.size(all_signal_times) - 1
        signal_time = array.get(all_signal_times, i)
        signal_result = array.get(all_signal_results, i)
        
        // 检查是否在统计时间窗口内
        in_window = (current_time - signal_time <= time_window)
        
        if in_window
            total_count += 1
            if signal_result
                win_count += 1

// 计算胜率
win_rate = total_count > 0 ? (win_count * 100.0 / total_count) : 0.0

// 显示胜率
var label info_label = na
if barstate.islast
    label.delete(info_label)
    period_text = stats_period
    info_text = period_text + "胜率: " + str.tostring(win_rate, "#.##") + "%\n胜:" + str.tostring(win_count) + " 总:" + str.tostring(total_count)
    info_label := label.new(bar_index, high, info_text, 
                           style=label.style_label_down, 
                           color=color.new(color.blue, 20), 
                           textcolor=color.white, 
                           size=size.normal)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment