Monday, April 21, 2014

thinkscript included: sdi_seasonalstgy - p&l on 10k invested seasonally and held short term

the seasonal strategy shows you the profit and loss of investing $10,000 on the same trading day of the year as today going back up to 20 years. here's the image of this strategy at work:
spy with sdi_seasonalstgy, shows the p&l of $10k invested on the same trading day of the year and held for 10 trading days.
floatingPL, a standard tos chart study, shows the running total of the profit and loss of the strategy, which  purchases spy at the open on the same trading day of the year going back 10 years, the extent of the chart. 9 trades were taken resulting in a total profit of $626.719 or about $70 per trade, net, after commissions.

right clicking on the chart will drop down a menu with the show reports selection which, when clicked, produces the following:

strategy report for seasonal purchases of spy showing 6 wins and 3 losses.

here's the thinkscript:

# sdi_seasonalStgy:
#hint:Strategy to evaluate buying and holding on the current trading day of the year for a fixed number of trading days. rev:1.3.0
# author: allen everhart
input dollarsPerTrade = 10000;
# date: 20april2014
# revision 1.3.0 24jan2015
#    - previously, the stats label omitted the first trade from the calculation when it coincided with the first bar. now this is fixed.
#    - display the standard deviation of gains and losses as a +/- number.
# revision 1.2.0 24may2014
#    - display number of trades evaluated.
#    - display average net gain or loss in chart label.
#    - default to using next trading day from current as target_td to reduce the influence of open positions.
#    - added input for specific target trading days.
# revision 1.1.0 10may2014
#    - display winning percentage as a chart label.
#    - commission input is now a floating point number instead of integer.
# revision 1.0.1 30apr2014
#    - previously held trade 1 bar too long.
#    - previously would not take trade on first bar.
#    - previously would not take trade before first new-year on chart.
# copylefts reserved. This is free software. That means you are free
# to use or modify it for your own usage but not for resale. 
# Help me get the word out about my blog by keeping this header
# in place. 

#hint dollarsPerTrade: trades vary in share size in order to keep the invested dollars constant to create a fair comparison to other products. rev: 1.3.0
input targetTradingDay=0;
#hint targetTradingDay: controls the trading day used for seasonal evaluation. a value of zero evaluates the current trading date. 
input commission = 5.0;
#hint commission: dollar value of commission per trade i.e. this number is multiplied by 2 (to account for a round trip) and divided by the number of shares traded and added to the purchase price in order to slip the entry price to account for the effect of commissions on the p&l.
def shareSize = Round(dollarsPerTrade / close, 0) ;
def commissionPerShare = 2 * commission / shareSize;
input holdLength = 10;
#hint holdLength: the number of bars to hold the trade. trades start and end on the open of a bar.

def tdytd = CountTradingDays(GetYear() * 10000 + 101, GetYYYYMMDD());

def agFactor = AggregationPeriod.DAY / GetAggregationPeriod();
def barsytd = Floor(tdytd * agFactor);
#plot bytd=barsytd;

def tradedYYYYMMDD = if !IsNaN(close) then GetYYYYMMDD() else Double.NaN;
def mrbarytd = Floor( agFactor * CountTradingDays( HighestAll(GetYear()) * 10000 + 101, HighestAll(tradedYYYYMMDD)));
def target_td=
    if targetTradingDay==0 then mrbarytd+1
    else targetTradingDay

def buySig = barsytd[-1] == target_td;
def sellSig = barsytd >= target_td + holdLength - 1;
def buyPrice=open[-1];
def sellPrice=open[-1];
AddOrder(OrderType.BUY_TO_OPEN, buySig, price = buyPrice + commissionPerShare, tradeSize = shareSize, name = Concat("bto on td# ", barsytd[-1])  );
AddOrder(OrderType.SELL_TO_CLOSE, sellSig, name = concat("stc after ", CONCAT(holdLength," td's")));

#### funkey addorder for first bar since addorder can only addorder's starting on bar#2
AddOrder(OrderType.BUY_TO_OPEN, BarNumber() == 1 && barsytd == mrbarytd, price = open + commissionPerShare, tradeSize = shareSize, name = Concat("bto on td# ", barsytd));

def winacc= 
    if buySig && !isnan(open[-holdLength-1]) &&open[-1]+commissionPerShare<open[-holdLength-1] then winacc[1]+1
    else winacc[1]

def wins=if isnaN(winacc) then wins[1] else winacc;
def lossNumacc= 
    if buySig && !isnan(open[-holdLength-1]) &&open[-1]+commissionPerShare>=open[-holdLength-1] then lossNumacc[1]+1
    else lossNumacc[1]

def lossNum=if isnaN(lossNumacc) then lossNum[1] else lossNumacc;
def gainacc= 
    if buySig && !isnan(sellPrice[-holdLength]) && buyPrice+commissionPerShare<sellPrice[-holdLength] then 
    else gainacc[1]

def gains=if isnaN(gainacc) then gains[1] else gainacc;

def lossesacc= 
    if buySig && !isnan(sellPrice[-holdLength]) &&buyPrice+commissionPerShare>sellPrice[-holdLength] then 
    else lossesacc[1]

def losses=if isnaN(lossesacc) then losses[1] else lossesacc;
def avgpnl=(gains+losses)/(wins+lossNum);
def deltaGain=gainacc-gainacc[1];
def deltaLoss=lossesacc-lossesacc[1];

def bn= if isnan(close) then bn [1] else barNumber();
def vsqracc= compoundValue(1,
    if isnan(deltaGain[-1]) && !isnaN(deltaGain) then
        fold i=0 to bn with vsa=0 do
            if getValue(deltaGain,i)-getvalue(deltaLoss,i)!=0 then
                vsa+sqr( avgpnl-getValue(deltaGain,i)-getvalue(deltaLoss,i))
            else vsa
    else vsqracc[1]

def glsd = sqrt(vsqracc/(wins+lossNum));

plot labelColor=double.NaN;
    concat( "wins: ", 
    concat( round(100*wins/(wins+lossNum),1),
    concat( "% of ",
    concat( wins+lossNum,
    concat( ", avg g/l: $",
    concat( round( avgpnl,1),
    concat( " +/- ", round(glsd,0))

1 comment: