pine-scriptthinkscript

Simple ThinkScript Strategy to Pine Script - No Trades Placed


I'm converting a simple ThinkScript strategy to Pine Script. In ThinkScript, it opens many trades over the past few days (5-minute chart on SPY ticker), but when translated to Pine Script, there are no trades placed. Wondering if someone can spot the bug. Note, it uses request.security to get data from a higher timeframe/aggregation, not sure if that's the issue or not.

This is the 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);

Due to looking at higher aggregation/timeframe, I use trend[1] to get the previous, fully closed trend value to act on the current bar re: repainting.

This is my Pine Script so far that generates no trades in the Strategy Tester, yet shows no syntax or compile errors:

// © 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')

Note that if you comment out the RTH/InSession logic from the entry/exit conditions, it opens 1 trade, however I'm trying to get a 1:1 match from ThinkOrSwim, and it trades almost daily over the past few weeks. Can you spot the issue?


Solution

  • I've discovered that you CANNOT use the var float declarations if you want to use variable values on a global scope, var <type> is meant for local scopes.

    The updated code looks like this:

    //=========================== STRATEGY ===========================//
    h = request.security(syminfo.ticker, input_agg_period, high)
    l = request.security(syminfo.ticker, input_agg_period, low)
    c = request.security(syminfo.ticker, input_agg_period, close)
    hh = ta.highest(h, input_trend_periods)
    ll = ta.lowest(l, input_trend_periods)
    // If "trend" > 0.5, then true/uptrend, else false/downtrend
    trendSignal = (c - ll) / (hh - ll) > 0.5 ? 1 : 0
    // Update the bar colours for visual inspection
    barcolor(trendSignal[1] == 1 ? color.blue : color.red)
    // Define some potential entry conditions
    longCondition = trendSignal[1] == 1
    shortCondition = trendSignal[1] == 0