sub OHLCStudy { @UAIndicatorLabels = ('csinum', 'Date', 'Open', 'High', 'Low', 'Close', 'Vol'); #Show the 7 fields on spread sheet $UAIndicatorTitle ='OHLCStudy'; #Supply spread sheet title # @Close(0) is the most current date on file & @Close(N) is the close of the Nth day before the current day. return ($Csinum, $Date[0], $Open[0], $High[0], $Low[0], $Close[0], $Vol[0]); #Supply all data for current day. } # sub ClosePrice { # @UAIndicatorLabels = ('Close'); $UAIndicatorTitle = 'Close'; # Identify spread sheet column title to be displayed my($a) = @_; # Define past day # relative to current day # of 0 return $Close[$a]; # Supply actual closing price of $a days into the past. } # $a defines the relative day number index sub RSI { my($size)=@_; #The @_ string given in "Col Insrt" transfers(14) into $size variable. my($au, $ad, $d, $i); #Identify the variables if($size<=0) { #Look at the RSI period and set it to 14 if it was set improperly $size = 14 } $au = $ad = 0; #initialize up and down days accumulators for($i=1;$i<=$size;$i++) #Loop thru $size days & count the up and down price differentials { $d = $Close[$i-1]-$Close[$i]; #compute difference between tomorrow's close and today close. if($d<0) #When a negative $d is a down day is observed, { $ad = $ad -$d; #accumulate the down differential ($d) into $ad } else #When a positive or zero ($d) is observed, { $au = $au +$d; #accumulate the up differential ($d) into $au. } } # end of 'for' stmt. $ad & $au ready 4 processing RSI result: # if($ad < 1e-6) { #when ad is less than 10**-6 (effectively 0) if($au < 1e-6) { #when both ad and au are effectively zero return indx of 50 return 50; } else { return 100; #Come here when ad=0 & au non-zero } } return(100-100/(1+($au/$ad))); #Come here when ad is positive. } sub NumDays { # This function delivers the intgr # of days of data available for processing any field such as: int($#Date+1); # Date or Open or High, etc. It refers back to the OHLCStudy to get the vector length. # Date is a globally defined array, and "$#Date" says give me the last index for that array. # Since an arrays length is one more than the maximum index, the array length is $#Date+1. # To invoke NumDays, and store Numdays in a variable, say $i, simply write: $i = NumDays() # $i will then hold the number of days in the $#Date vector defined thru the OHLCStudy above. } # End of NumDays study. sub Max #This routine searches an array (list of args) to find the largest value { my(@t) = @_; #of a variable length list. #Move the input array,@_, to the @t array my($biggest,$i); # define the variables 'biggest' and the index variable '$i' $biggest = $t[$#t]; #Set $biggest to the last one in the list. for($i=$#t-1;$i>=0;$i--) { #now search thru the list frm the arg be4 last to the frst if($t[$i]>$biggest) { $biggest = $t[$i] } #to find all args which are bigger. } #The index runs from 0 to N-1 for an N argument list. return $biggest #return the biggest value of all. } sub Min{ #This routine searches an array (list of args) to find the largest value my(@t) = @_; #of a variable length list. #Move the input array,@_, to the @t array my($smallest,$i); # define the variables 'biggest' and the index variable '$i' $smallest = $t[$#t]; #Set $biggest to the last one in the list. for($i=$#t-1;$i>=0;$i--) { #now search thru the list frm the arg be4 last to the frst if($t[$i]<$smallest) { $smallest = $t[$i] } #to find all args which are bigger. } #The index runs from 0 to N-1 for an N argument list. return $smallest #return the biggest value of all. } sub Max0{ #This subroutine is designed for precisely two arguments. # #To invoke this routine say: $Min = Max0($a,$b) $Min would then = min of $a & $b my($a,$b)=@_; #Define the two input arguments $a, and $b. @_ is an array. if($a>$b) { return $a; #Return $a if it is the lesser. } else { return $b; #Otherwise return $b as the lesser. } } sub Min0{ #This subroutine is designed for precisely two arguments. # #To invoke this routine say: $Min = Max0($a,$b) $Min would then = min of $a & $b my($a,$b)=@_; #Define the two input arguments $a, and $b. @_ is an array. if($a<$b) { return $a; #Return $a if it is the lesser. } else { return $b; #Otherwise return $b as the lesser. } } sub BrkOut { #BrkOut invokes breakout logic on all inpt lkng for a $a day channel # It reports the nxt signal, the exit stop, and the % return per day of data. my($a) = @_; #define sole input to BrkOut my($ISignal,$BSorSS,$PcntReturn); my($NDayChan,$MthHigh,$MthLow,$SumProfit,$PosPrice,$NDayChannel,$VarChannel,$SS,$BS); my($N,$M,$iHi,$iLo,$NewPos,$DaysOnFile,$perlIndex); $DaysOnFile = NumDays(); #Compute the number of days on file #print "DaysOnFile=$DaysOnFile a=$a\n"; # $NDayChan = $a; #Define the channel width in days $SumProfit = 0; #Initialize the variables $PosPrice = 0; $ISignal = 0; #Always begin from a neutral position $NDayChannel = Max0(2,$NDayChan); for($N=$NDayChan+1;$N<=$DaysOnFile;$N++) #DO 100 N = 1,DaysOnFile { $VarChannel = Min0($NDayChannel,$N); $MthHigh = 0; $MthLow = 999999999; #if($VarChannel<$NDayChannel+1) { next; } #print "N=$N ISignal=$ISignal\n"; if($ISignal eq 0) { #IF for ($M=1;$M<=$VarChannel;$M++) { #DO 13 #This is the DO 13 loop $perlIndex = $DaysOnFile-$N + $M; $iHi = $High[$perlIndex]; $iLo = $Low [$perlIndex]; $MthHigh = &Max0($MthHigh,$iHi); $MthLow = &Min0($MthLow ,$iLo); } #Continue; #End of the Do 13 loop $NthClose = $Close[$DaysOnFile-$N]; #print "NthClose=$NthClose MthHigh=$MthHigh MthLow=$MthLow\n"; if($NthClose>($MthHigh+$MthLow)/2) { #If $ISignal = 1; $PosPrice = $NthClose; $SS = $MthLow; $BSorSS = $SS; $BS = 0; } else { $ISignal = -1; $PosPrice = $NthClose; $BS = $MthHigh; $BSorSS = $BS; $SS = 0; } } else { $MthHigh = 0; $MthLow = 999999999; for ($M=1;$M<=$VarChannel;$M++) { #DO 101 #This is the DO 101 loop $perlIndex = $DaysOnFile-$N + $M; $iHi = $High[$perlIndex]; $iLo = $Low [$perlIndex]; $MthHigh = &Max0($MthHigh,$iHi); $MthLow = &Min0($MthLow ,$iLo); } #101 Continue #End of the Do 101 loop $perlIndex = $DaysOnFile-$N; if($ISignal eq 1) { #If $SS = Max0($SS,$MthLow); $BSorSS = $SS; if($Low[$perlIndex]<$SS) { #if $NewPos = Min0($SS,$Open[$perlIndex]); $SumProfit = $SumProfit + $NewPos - $PosPrice; $PosPrice = $NewPos; $BS = $MthHigh; $BSorSS = $BS; $SS = 0; $ISignal = -1; } #endif } #Endif else { #If $BS = Min0($BS,$MthHigh); $BSorSS = $BS; if($High[$perlIndex]>$BS) { #if $NewPos = Max0($BS,$Open[$perlIndex]); $SumProfit = $SumProfit - $NewPos + $PosPrice; $PosPrice = $NewPos; $SS = $MthLow; $BSorSS = $SS; $BS = 0; $ISignal = 1; } #endif } #Endif } #Endif } # Label100: # 100 Continue if($ISignal>0) { #If $SumProfit = $SumProfit + $Close[0] - $PosPrice; } if($ISignal>0) { $SumProfit = $SumProfit + $PosPrice - $Close[0]; } #print "ISignal=$ISignal BSorSS=$BSorSS\n"; return -$ISignal * $BSorSS; } sub BOPcnRet { #BOPcnRet also invokes breakout logic on all inpt lkng for a $a day channel # it cmpts the signals, and retrns the Buy/Sell stp,&the ave% retrn for ea items ntire hist. my($a) = @_; #define sole input (the # of days of channel width) to BOPcnRet my($ISignal,$BSorSS,$PcntReturn); my($NDayChan,$MthHigh,$MthLow,$SumProfit,$PosPrice,$NDayChannel,$VarChannel,$SS,$BS); my($N,$M,$iHi,$iLo,$NewPos,$DaysOnFile,$perlIndex,$NthClose); # $PcnRet variable added my($PcnRet,$NmbrTrades); $DaysOnFile = NumDays(); #The BOPcnRet perl script was converted from a Fortran program. #print "DaysOnFile=$DaysOnFile a=$a\n"; # Initialize the variables. $NDayChan = $a; $SumProfit = 0; $PosPrice = 0; $ISignal = 0; $NmbrTrades = 0; $PcnRet = 0; #initialize the cumulative % return variable $NDayChannel = Max0(2,$NDayChan); #force the channel width to be at least two days for($N=$NDayChan+1;$N<=$DaysOnFile;$N++) #This was main Fortran loop for all file entrys: { #Define the for loop and process the $N th day of the current stock analyzed $VarChannel = Min0($NDayChannel,$N); $MthHigh = 0; $MthLow = 999999999; #if($VarChannel<$NDayChannel+1) { next; } #print "N=$N ISignal=$ISignal\n"; if($ISignal eq 0) { #IF for ($M=1;$M<=$VarChannel;$M++) #Look thru $VarChannel pst dys of file & capture hi&low { #DO 13 #as MthHigh and MthLow. This is the DO 13 loop $perlIndex = $DaysOnFile-$N + $M; #which determins the initial market direction only $iHi = $High[$perlIndex]; #when $ISignal is 0 $iLo = $Low [$perlIndex]; $MthHigh = &Max0($MthHigh,$iHi); $MthLow = &Min0($MthLow ,$iLo); } #Continue; #End of the Do 13 loop $NthClose = $Close[$DaysOnFile-$N]; #Get the $NthClose&see if init pstn shd B lng or shrt #print "NthClose=$NthClose MthHigh=$MthHigh MthLow=$MthLow\n"; if($NthClose>($MthHigh+$MthLow)/2) { #If $ISignal = 1; $PosPrice = $NthClose; #PosPrice is the initial $SS = $MthLow; #starting position $BSorSS = $SS; #calc when ISignal is zero. $BS = 0; } else { $ISignal = -1; $PosPrice = $NthClose; $BS = $MthHigh; $BSorSS = $BS; $SS = 0; } } else { # Come here when ISignal is nz, when a non-neutral position is in force $MthHigh = 0; #on the 3rd or 4th day. Initialize the MthHigh and MthLow $MthLow = 999999999; for ($M=1;$M<=$VarChannel;$M++) { #DO 101 #This DO 101 loop $perlIndex = $DaysOnFile-$N + $M; #captures the $iHi = $High[$perlIndex]; #highest hi and lowest low $iLo = $Low [$perlIndex]; #as $MthHigh and $MthLow $MthHigh = &Max0($MthHigh,$iHi); $MthLow = &Min0($MthLow ,$iLo); } #101 Continue #End of the Do 101 loop $perlIndex = $DaysOnFile-$N; #a $perlIndex is a # from 0 if($ISignal eq 1) #to the # of Days on File - 1 { #If $SS = Max0($SS,$MthLow); #The sell stop is allowed to go $BSorSS = $SS; #up so long as the market is moving up if($Low[$perlIndex]<$SS) { #if $NewPos = Min0($SS,$Open[$perlIndex]); #if the open falls below the reversal $SumProfit = $SumProfit + $NewPos - $PosPrice; #the revrsl will b the worst of two $PcnRet = $PcnRet + 100.*($NewPos-$PosPrice)/$PosPrice; #Sum the % returns ### $PosPrice = $NewPos; #The above stmt sums the % returns as $BS = $MthHigh; #trades are logged. $BSorSS = $BS; #save the new buy stop $SS = 0; $ISignal = -1; #show the reverse signal. $NmbrTrades= $NmbrTrades + 1; #increment the $NmbrTrades } #endif } #Endif else { #If $BS = Min0($BS,$MthHigh); $BSorSS = $BS; if($High[$perlIndex]>$BS) { #if #Reverse the current position $NewPos = Max0($BS,$Open[$perlIndex]); #from short to long $SumProfit = $SumProfit - $NewPos + $PosPrice; $PcnRet = $PcnRet + 100.*($PosPrice-$NewPos)/$PosPrice; #Sum the % returns ### $PosPrice = $NewPos; $SS = $MthLow; $BSorSS = $SS; $BS = 0; $ISignal = 1; $NmbrTrades = $NmbrTrades + 1; #replace NmbrTrades with NmbrTrades + 1 } #endif } #Endif } #Endif } # Label100: # 100 Continue if($ISignal>0) #Branch from above when all days for { #If #current stock are processed $SumProfit = $SumProfit + $Close[0] - $PosPrice; #add in the curnt position equity $PcnRet = $PcnRet + 100.*($Close[0]-$PosPrice)/$PosPrice; #Sum the % returns ### $NmbrTrades = $NmbrTrades + 1; #Artificially bump # of trades to close up loop } if($ISignal>0) { $SumProfit = $SumProfit + $PosPrice - $Close[0]; $PcnRet = $PcnRet + 100.*($PosPrice - $Close[0])/$PosPrice; #Sum the % returns ### $NmbrTrades = $NmbrTrades + 1; } #print "ISignal =$ISignal BSorSS=$BSorSS\n"; @UAIndicatorLabels = ('stop', '%Retrn', '#Trades'); #Add titles 2 spread sheet Columns $UAIndicatorTitle = 'BreakOut for channel of '.$a; #Show title-The . b4 $a ads $a's val 2 titl. return (-$ISignal*$BSorSS,$PcnRet,$NmbrTrades); #return the three values separated by commas } #This curly bracket ends the BOPcnRet subr. # # sub BOPcnRetOpt { #BOPcnRetOpt also invokes breakout logic on all inpt lkng for a $a day channel # it cmpts the signals, and retrns the Buy/Sell stp,&the ave% retrn for ea items ntire hist. # This version uses a global to all stocks variable & returns another result ($BestChanSiz) my($a) = @_; #define sole input (the # of days of channel width) to BOPcnRet my($ISignal,$BSorSS,$PcntReturn); my($NDayChan,$MthHigh,$MthLow,$SumProfit,$PosPrice,$NDayChannel,$VarChannel,$SS,$BS); my($N,$M,$iHi,$iLo,$NewPos,$DaysOnFile,$perlIndex,$NthClose); # $PcnRet variable added my($PcnRet,$NmbrTrades); $DaysOnFile = NumDays(); #The BOPcnRet perl script was converted from a Fortran program. #print "DaysOnFile=$DaysOnFile a=$a\n"; # Initialize the variables. $NDayChan = $a; $SumProfit = 0; $PosPrice = 0; $ISignal = 0; $NmbrTrades = 0; $PcnRet = 0; #initialize the cumulative % return variable $NDayChannel = Max0(2,$NDayChan); #force the channel width to be at least two days for($N=$NDayChan+1;$N<=$DaysOnFile;$N++) #This was main Fortran loop for all file entrys: { #Define the for loop and process the $N th day of the current stock analyzed $VarChannel = Min0($NDayChannel,$N); $MthHigh = 0; $MthLow = 999999999; #if($VarChannel<$NDayChannel+1) { next; } #print "N=$N ISignal=$ISignal\n"; if($ISignal eq 0) { #IF for ($M=1;$M<=$VarChannel;$M++) #Look thru $VarChannel pst dys of file & capture hi&low { #DO 13 #as MthHigh and MthLow. This is the DO 13 loop $perlIndex = $DaysOnFile-$N + $M; #which determins the initial market direction only $iHi = $High[$perlIndex]; #when $ISignal is 0 $iLo = $Low [$perlIndex]; $MthHigh = &Max0($MthHigh,$iHi); $MthLow = &Min0($MthLow ,$iLo); } #Continue; #End of the Do 13 loop $NthClose = $Close[$DaysOnFile-$N]; #Get the $NthClose&see if init pstn shd B lng or shrt #print "NthClose=$NthClose MthHigh=$MthHigh MthLow=$MthLow\n"; if($NthClose>($MthHigh+$MthLow)/2) { #If $ISignal = 1; $PosPrice = $NthClose; #PosPrice is the initial $SS = $MthLow; #starting position $BSorSS = $SS; #calc when ISignal is zero. $BS = 0; } else { $ISignal = -1; $PosPrice = $NthClose; $BS = $MthHigh; $BSorSS = $BS; $SS = 0; } } else { # Come here when ISignal is nz, when a non-neutral position is in force $MthHigh = 0; #on the 3rd or 4th day. Initialize the MthHigh and MthLow $MthLow = 999999999; for ($M=1;$M<=$VarChannel;$M++) { #DO 101 #This DO 101 loop $perlIndex = $DaysOnFile-$N + $M; #captures the $iHi = $High[$perlIndex]; #highest hi and lowest low $iLo = $Low [$perlIndex]; #as $MthHigh and $MthLow $MthHigh = &Max0($MthHigh,$iHi); $MthLow = &Min0($MthLow ,$iLo); } #101 Continue #End of the Do 101 loop $perlIndex = $DaysOnFile-$N; #a $perlIndex is a # from 0 if($ISignal eq 1) #to the # of Days on File - 1 { #If $SS = Max0($SS,$MthLow); #The sell stop is allowed to go $BSorSS = $SS; #up so long as the market is moving up if($Low[$perlIndex]<$SS) { #if $NewPos = Min0($SS,$Open[$perlIndex]); #if the open falls below the reversal $SumProfit = $SumProfit + $NewPos - $PosPrice; #the revrsl will b the worst of two $PcnRet = $PcnRet + 100.*($NewPos-$PosPrice)/$PosPrice; #Sum the % returns ### $PosPrice = $NewPos; #The above stmt sums the % returns as $BS = $MthHigh; #trades are logged. $BSorSS = $BS; #save the new buy stop $SS = 0; $ISignal = -1; #show the reverse signal. $NmbrTrades= $NmbrTrades + 1; #increment the $NmbrTrades } #endif } #Endif else { #If $BS = Min0($BS,$MthHigh); $BSorSS = $BS; if($High[$perlIndex]>$BS) { #if #Reverse the current position $NewPos = Max0($BS,$Open[$perlIndex]); #from short to long $SumProfit = $SumProfit - $NewPos + $PosPrice; $PcnRet = $PcnRet + 100.*($PosPrice-$NewPos)/$PosPrice; #Sum the % returns ### $PosPrice = $NewPos; $SS = $MthLow; $BSorSS = $SS; $BS = 0; $ISignal = 1; $NmbrTrades = $NmbrTrades + 1; #replace NmbrTrades with NmbrTrades + 1 } #endif } #Endif } #Endif } # Label100: # 100 Continue if($ISignal>0) #Branch from above when all days for { #If #current stock are processed $SumProfit = $SumProfit + $Close[0] - $PosPrice; #add in the curnt position equity $PcnRet = $PcnRet + 100.*($Close[0]-$PosPrice)/$PosPrice; #Sum the % returns ### $NmbrTrades = $NmbrTrades + 1; #Artificially bump # of trades to close up loop } if($ISignal>0) { $SumProfit = $SumProfit + $PosPrice - $Close[0]; $PcnRet = $PcnRet + 100.*($PosPrice - $Close[0])/$PosPrice; #Sum the % returns ### $NmbrTrades = $NmbrTrades + 1; } #print "ISignal =$ISignal BSorSS=$BSorSS\n"; @UAIndicatorLabels = ('stop', '%Retrn', '#Trades'); #Add titles 2 spread sheet Columns $UAIndicatorTitle = 'BreakOut for channel of '.$a; #Show title-The . b4 $a ads $a's val 2 titl. return (-$ISignal*$BSorSS,$PcnRet,$NmbrTrades); #return the three values separated by commas } #This curly bracket ends the BOPcnRet subr. # #