You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ThinkScript转Pine Script无交易生成问题排查求助

ThinkScript转Pine Script无交易信号问题排查与修正

问题背景

我正在将一个简单的ThinkScript策略转换为Pine Script。原策略在ThinkOrSwim的SPY标的5分钟图表上,过去几日会产生大量交易,但转换为Pine Script后,策略测试器无任何交易生成,且无语法或编译错误。策略使用request.security获取更高时间框架/聚合周期的数据,不确定是否为此问题根源。移除交易时段(RTH/InSession)逻辑后仅生成1笔交易,希望实现与ThinkOrSwim完全一致的交易表现,请求帮忙定位问题。

原ThinkScript代码

input agg = AggregationPeriod.HOUR; 
input TrendPeriods = 5;
input use_trading_hours = yes;
input spread = 0.15;

######################################################
# TIME
######################################################
def startTradingTime = if use_trading_hours == yes then 0930 else 0000;
def noMorePositions = if use_trading_hours == yes then 1530 else 0000;
def endTradingTime = if use_trading_hours == yes then 1600 else 0000;
def isTradingSession = if use_trading_hours == yes then SecondsFromTime(startTradingTime) >= 0 and SecondsTillTime(endTradingTime) > 0 else yes;
def positionsStillAllowed = if use_trading_hours == yes then SecondsTillTime(noMorePositions) > 0 else yes;

def h = high(period = agg); 
def l = low(period = agg); 
def c = close(period = agg); 
def hh = highest(h, trendPeriods); 
def ll = lowest(l, trendPeriods); 
def trend = if ((c - ll) / (hh - ll)) > .5  
            then 1 
            else 0; 

// Enter long (flip from short if necessary)
addorder(orderType.BUY_AUTO, trend[1] and isTradingSession and positionsStillAllowed and BarNumber()>1, tradesize=1, price=open[-1]+spread); 

// Exit long at EOD
addorder(orderType.SELL_TO_CLOSE, !isTradingSession[-1], tradesize=1, price=open-spread); 

// Enter short (flip from long if necessary)
addorder(orderType.SELL_AUTO, !trend[1] and isTradingSession and positionsStillAllowed and BarNumber()>1, tradesize=1, price=open[-1]-spread); 

// Exit short at EOD
addorder(orderType.BUY_TO_CLOSE, !isTradingSession[-1], tradesize=1, price=open+spread);

注:由于使用更高聚合周期,通过trend[1]获取已完全闭合的前一个trend值以避免重绘。

当前无交易的Pine Script代码

// © windowshopr

//@version=5
//=========================== IMPORTS AND STRATEGY ===========================//
strategy("My Strategy", shorttitle="My Strategy", 
     overlay=true, 
     initial_capital=1000, 
     pyramiding=1, 
     calc_on_order_fills=true, 
     calc_on_every_tick=true, 
     backtest_fill_limits_assumption=10, 
     default_qty_type=strategy.percent_of_equity, 
     default_qty_value=100, 
     currency=currency.NONE, 
     slippage=1, 
     commission_type=strategy.commission.cash_per_contract, 
     commission_value=0.01)



//=========================== INPUTS ===========================//
//========= STRATEGY
input_agg_period = input.timeframe(
     title = "Higher TimeFrame Aggregation Period", 
     defval = "60", 
     group = 'Strategy',
     tooltip = 'The higher timeframe to use with strategy')

input_trend_periods = input.int(
     title = "Trend Periods", 
     minval = 1, 
     defval = 5, 
     group = 'Strategy',
     tooltip = 'The number of bars/periods to use for calculating trend')



//=========================== STRATEGY ===========================//
var float h = request.security(syminfo.tickerid, input_agg_period, high)
var float l = request.security(syminfo.tickerid, input_agg_period, low)
var float c = request.security(syminfo.tickerid, input_agg_period, close)
var float hh = ta.highest(h, input_trend_periods)
var float ll = ta.lowest(l, input_trend_periods)
var int trend = ((c - ll) / (hh - ll)) > .5 ? 1 : 0

longCondition = trend[1] == 1
shortCondition = trend[1] == 0



//=========================== TIME MANAGEMENT ===========================//
// InSession() returns 'true' when the current bar happens inside
// the specified session, corrected for the given time zone (optional).
// Returns 'false' when the bar doesn't happen in that time period,
// or when the chart's time frame is 1 day or higher. 
InSession(sessionTime, sessionTimeZone=syminfo.timezone) =>
    not na(time(timeframe.period, sessionTime, sessionTimeZone))
session  = input.session("0730-1400", title="Session")
timeZone = input.string("GMT-6", title="Time Zone")

var bool inRTH = InSession(session, timeZone) 



//=========================== TRADE MANAGEMENT ===========================//
bool openLongPosition = longCondition and inRTH
bool openShortPosition = shortCondition and inRTH

bool closeLongPosition = shortCondition or not inRTH
bool closeShortPosition = longCondition or not inRTH

bool longIsActive = openLongPosition or strategy.position_size > 0 and not closeLongPosition
bool shortIsActive = openShortPosition or strategy.position_size < 0 and not closeShortPosition

// the open signals when not already into a position
bool validOpenLongPosition = openLongPosition and not (strategy.position_size > 0)
bool validOpenShortPosition = openShortPosition and not (strategy.position_size < 0)

// Order Exits
if closeLongPosition and longIsActive
    strategy.close(id = 'Long Entry', comment = 'Close Long', alert_message = 'Long: Closed at market price')
// close on trend reversal
if closeShortPosition and shortIsActive
    strategy.close(id = 'Short Entry', comment = 'Close Short', alert_message = 'Short: Closed at market price')

// Order Entries
if validOpenLongPosition
    strategy.entry(id = 'Long Entry', direction = strategy.long, alert_message = 'Long(' + syminfo.ticker + '): Started')

// getting into SHORT position
if validOpenShortPosition
    strategy.entry(id = 'Short Entry', direction = strategy.short, alert_message = 'Short(' + syminfo.ticker + '): Started')

核心问题定位与修正方案

1. 变量初始化错误(var关键字滥用)

所有和更高时间框架数据相关的变量(h、l、c、hh、ll、trend)都被错误地加上了var关键字。var会让变量仅在脚本首次运行时初始化,后续不再更新,导致趋势计算一直停留在初始值,完全失效。必须移除所有这些变量前的var。

2. 更高时间框架数据获取逻辑错误

原策略用trend[1]取已闭合的前一根高时间框架K线数据,避免重绘。Pine Script中request.security默认会返回当前未闭合的高时间框架数据,需要添加参数确保获取已闭合数据:

[h, l, c] = request.security(syminfo.tickerid, input_agg_period, [high, low, close], barmerge.gaps_on, barmerge.lookahead_on=false)

用解构赋值一次性获取三个值,同时关闭前瞻(lookahead),确保数据无重绘。

3. 时间逻辑未对齐原策略

原策略区分三个时间规则:

  • isTradingSession:当前是否在交易时段(0930-1600)
  • positionsStillAllowed:是否还允许开新仓(1530前)
  • 出场逻辑用!isTradingSession[-1](前一根K线已离开交易时段)

你当前的时段设置是0730-1400,和原策略的美股交易时段完全不符,需修正时间规则,还原原逻辑:

// 还原原策略的时间逻辑
input use_trading_hours = input.bool(title="Use Trading Hours", defval=true, group="Time")
input spread = input.float(title="Spread", defval=0.15, group="Strategy")

startTradingTime = use_trading_hours ? 0930 : 0000
noMorePositions = use_trading_hours ? 1530 : 0000
endTradingTime = use_trading_hours ? 1600 : 0000

isTradingSession = use_trading_hours ? (ta.secondssince(time("0930")) >= 0 and ta.secondstill(time("1600")) > 0) : true
positionsStillAllowed = use_trading_hours ? (ta.secondstill(time("1530")) > 0) : true

4. 订单逻辑还原原策略

原策略用BUY_AUTO/SELL_AUTO自动反向平仓,且指定了入场/出场价格(包含spread),需用strategy.entry的limit参数还原价格逻辑,同时用strategy.close实现EOD平仓:

// 入场信号(需满足已闭合趋势、交易时段、允许开仓、非首根K线)
longEntryCond = trend[1] == 1 and isTradingSession and positionsStillAllowed and bar_index > 0
shortEntryCond = trend[1] == 0 and isTradingSession and positionsStillAllowed and bar_index > 0

// 入场订单(带spread的限价单)
if longEntryCond
    strategy.entry("Long", strategy.long, limit=open[1]+spread)
if shortEntryCond
    strategy.entry("Short", strategy.short, limit=open[1]-spread)

// EOD平仓(前一根K线已离开交易时段)
eodCloseCond = not isTradingSession[1]
if eodCloseCond
    strategy.close("Long", limit=open-spread)
    strategy.close("Short", limit=open+spread)

修正后的完整Pine Script代码

//@version=5
strategy("My Strategy", shorttitle="My Strategy", 
     overlay=true, 
     initial_capital=1000, 
     pyramiding=1, 
     calc_on_order_fills=true, 
     calc_on_every_tick=true, 
     backtest_fill_limits_assumption=10, 
     default_qty_type=strategy.percent_of_equity, 
     default_qty_value=100, 
     currency=currency.NONE, 
     slippage=1, 
     commission_type=strategy.commission.cash_per_contract, 
     commission_value=0.01)

//=========================== INPUTS ===========================//
input_agg_period = input.timeframe(title="Higher TimeFrame Aggregation Period", defval="60", group='Strategy')
input_trend_periods = input.int(title="Trend Periods", minval=1, defval=5, group='Strategy')
use_trading_hours = input.bool(title="Use Trading Hours", defval=true, group="Time")
spread = input.float(title="Spread", defval=0.15, group="Strategy")

//=========================== TIME MANAGEMENT ===========================//
startTradingTime = use_trading_hours ? 0930 : 0000
noMorePositions = use_trading_hours ? 1530 : 0000
endTradingTime = use_trading_hours ? 1600 : 0000

isTradingSession = use_trading_hours ? (ta.secondssince(time("0930")) >= 0 and ta.secondstill(time("1600")) > 0) : true
positionsStillAllowed = use_trading_hours ? (ta.secondstill(time("1530")) > 0) : true

//=========================== STRATEGY ===========================//
// 获取已闭合的高时间框架数据,避免重绘
[h, l, c] = request.security(syminfo.tickerid, input_agg_period, [high, low, close], barmerge.gaps_on, barmerge.lookahead_on=false)
hh = ta.highest(h, input_trend_periods)
ll = ta.lowest(l, input_trend_periods)
trend = ((c - ll) / (hh - ll)) > 0.5 ? 1 : 0

//=========================== TRADE MANAGEMENT ===========================//
// 入场条件:用前一根高时间框架的趋势值,避免重绘
longEntryCond = trend[1] == 1 and isTradingSession and positionsStillAllowed and bar_index > 0
shortEntryCond = trend[1] == 0 and isTradingSession and positionsStillAllowed and bar_index > 0

// 入场订单(带spread的限价单)
if longEntryCond
    strategy.entry("Long", strategy.long, limit=open[1]+spread, comment="Long Entry")
if shortEntryCond
    strategy.entry("Short", strategy.short, limit=open[1]-spread, comment="Short Entry")

// EOD平仓条件:前一根K线已离开交易时段
eodCloseCond = not isTradingSession[1]
if eodCloseCond
    strategy.close("Long", limit=open-spread, comment="Close Long EOD")
    strategy.close("Short", limit=open+spread, comment="Close Short EOD")

内容的提问来源于stack exchange,提问作者wildcat89

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 01:53:09