Skip to content

Instantly share code, notes, and snippets.

@wuuconix
Created July 15, 2025 12:40
Show Gist options
  • Save wuuconix/8042807072605dd7a8563eebf85cdbc1 to your computer and use it in GitHub Desktop.
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

允许多比Long和Short,不下单,只标记。

//@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))
    // 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

// === 平仓逻辑 ===
// 方式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 (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)

@wuuconix
Copy link
Author

计算胜率:

//@version=5
indicator("事件合约胜率统计 (近1天修正版)", overlay=true)

length_bb = input.int(20, "布林带周期")
mult = input.float(2.0, "布林带倍数")
ma_length = input.int(50, "均线周期")
use_trend_filter = input.bool(true, "趋势过滤")
hold_minutes = 10

time_window_ms = 7 * 24 * 60 * 60 * 1000  // 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

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)

// === 数组
var float[] entry_prices = array.new_float()
var int[] entry_bar_indices = array.new_int()
var string[] entry_types = array.new_string()
var int[] entry_times = array.new_int()
var int[] remove_indexes = array.new_int()

var int closed_signals = 0
var int win_count = 0

// === 新信号记录
if long_signal or short_signal
    array.push(entry_prices, close)
    array.push(entry_bar_indices, bar_index + hold_minutes)
    array.push(entry_types, long_signal ? "Long" : "Short")
    array.push(entry_times, time)

// === 统计
if array.size(entry_prices) > 0
    for i = 0 to array.size(entry_prices) - 1
        target_bar_index = array.get(entry_bar_indices, i)
        signal_time = array.get(entry_times, i)

        if bar_index >= target_bar_index
            in_period = (timenow - signal_time) <= time_window_ms

            if in_period
                closed_signals := closed_signals + 1
                entry_price = array.get(entry_prices, i)
                entry_type = array.get(entry_types, i)

                if entry_type == "Long" and close > entry_price
                    win_count := win_count + 1
                if entry_type == "Short" and close < entry_price
                    win_count := win_count + 1

            array.push(remove_indexes, i)

// === 倒序删除
while array.size(remove_indexes) > 0
    j = array.size(remove_indexes) - 1
    remove_index = array.get(remove_indexes, j)
    array.remove(entry_prices, remove_index)
    array.remove(entry_bar_indices, remove_index)
    array.remove(entry_types, remove_index)
    array.remove(entry_times, remove_index)
    array.remove(remove_indexes, j)

// === 胜率
win_rate = closed_signals > 0 ? (win_count / closed_signals) * 100 : na

// === 图表标记
plotshape(long_signal, title="Long", location=location.belowbar, color=color.green, style=shape.triangleup, size=size.small)
plotshape(short_signal, title="Short", location=location.abovebar, color=color.red, style=shape.triangledown, size=size.small)

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

// === 胜率标签
var label win_label = na
if barstate.islast and not na(win_rate)
    label.delete(win_label)
    win_label := label.new(bar_index, high, "近1天胜率: " + str.tostring(win_rate, "#.##") + "%", style=label.style_label_down, color=color.new(color.blue, 0), textcolor=color.white)

@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