On 23 Dec 2008, I wrote:
> Recently, there was discussion of OHLC and Candlestick charting of
> stock market data.  Rather than reinvent the wheel, I thought I'd ask
> first: has anyone created J code that will convert stock market data
> into a point & figure chart and that they'd be willing to share? 

There being no responses with such code, I went ahead and wrote an 
explicit verb that creates an array containing P&F chart data (see 
bottom of this post).  Admittedly, it's not very J-ish at all, but it 
does seem to work.  Anyway, before I can have a finalized version (I 
probably should prepend an initial column containing the price values 
for each box), I need the answers to some questions:

(1) How do I pass *six* arguments to the verb?  (nBox, nRevBoxes, 
nMaxColumns, nMaxRows, nShowDate, datafilename--the latter would be 
passed to the readcsv line below) (depending on whether nChartLow and 
nChartHigh are derived from the data or from user input, these might 
possibly be additional arguments, for a total of *eight* arguments)  
How would any of these multiple arguments be made "optional" (i.e., 
they could be present or not)?

(2) How can the resulting array be "plotted" (or converted into some 
sort of resizable diagram/chart/whatever) in a way that shows all the 
(perhaps tiny) X's, O's, and date digits?  It would be nice, too, to 
impose a gridwork surrounding the array elements.

Harvey


NB. #################################################################

NB. pf.ijs - standard "Point & Figure" charting algorithm with optional 
dating added
NB. by Harvey E. Hahn -- 23-25,29-31 Jan; 1,4,6-7 Feb 2009

NB. Initial datatype prefixes used to keep names straight:
NB.    "b" for boxed
NB.    "s" for string (literal)
NB.    "n" for numeric

require 'files'

pfchart=: 3 : 0

NB. constants (should be input by user instead?):
nBox=. 150    NB. size of charting box (related to nMaxRows below))
nRevBoxes=. 3    NB. number of boxes needed for reversal
nMaxColumns=. 125
nMaxRows=. 50    NB. this can affect value of "nBox" if calculated by 
formula
nShowDate=. 1    NB. flag to display dates or not

NB. initialize variables:
nBoxHigh=. 0
nBoxLow=. 0
nHigh=. 0
nLow=. 0
nClose=. 0
nNewHigh=. 0
nNewLow=. 0
nCurrCol=. 0
nFirst=. 1    NB. flag is turned "off" (false) after first entry
nXCol=. 1
nOCol=. 0
nColType=. nXCol
sLastMonthPlotted=. ''
sLastYearPlotted=. ''
sYr=. ''
sMn=. ''
nMon=. 0
sMonth=. ''
nRowOffset=. 0

if. (1 = nShowDate) do.
  nMaxRows=. nMaxRows + 4  NB. 4 extra rows needed for 4 vertical 
digits of year
  nRowOffset=. 4
end.

sChart=. (nMaxRows,nMaxColumns) $ ' '    NB. chart array

NB. read in (Yahoo) market data file and cull out date/high/low/close 
values:
bMktData=. readcsv (jpath '~user\data\DJI-r-pf.csv')
bDate=.  0 {"1 bMktData   NB. column 0 is date (format: yyyy-mm-dd)
bHigh=.  2 {"1 bMktData   NB. column 2 is high
bLow=.   3 {"1 bMktData   NB. column 3 is low
bClose=. 4 {"1 bMktData   NB. column 4 is close

NB. two ways chart range might be done --
NB.   (1) look for max and min of prices (nBox = (max-min)/boxsize),
NB. or
NB.   (2) take first price and calculate range of 50% higher
NB.       and 50% lower

NB. for testing purposes (DJIA 2007-2008):
nChartLow=. 7000
nChartHigh=. 14500    NB. midpoint = 10750, nBox = 150

for_i. i.((#bMktData)-1) do.

  sYr=. 4 {. > i { bDate
  sMn=. 2 {. 5 }. > i { bDate
  nMon=. ". sMn
  if. (9 < nMon) do.
    sMonth=. (nMon-10) { 'abc'
  else.
    sMonth=. ": 1 } sMn
  end.
  nHigh=. (". > i { bHigh) - nChartLow
  nLow=. (". > i { bLow) - nChartLow
  nClose=. (". > i { bClose) - nChartLow

  if. (nChartHigh < (". > i { bHigh)) +. (nChartLow > (". > i { bLow)) 
do.
    sChart=. |. sChart
    smoutput 'Data for ',(> i { bDate),' exceeded chart range'
    return.
  end.

  NB. (initialize starting point of chart in the first column)
  NB. is this the first entry?
  if. (1 = nFirst) do.

    nFirst=. 0    NB. turn off flag to guarantee this is done only 
once:

    if. nClose >= ((nHigh - nLow) % 2) do.
      nColType=. nXCol
      nBoxHigh=. <. (nLow % nBox)
      nNewHigh=. <. (nHigh % nBox)
      for_b. i.((nNewHigh-nBoxHigh)+1) do.
        sChart=. 'X' (< (nBoxHigh+b+nRowOffset),nCurrCol) } sChart
      end.
    else.
      nColType=. nOCol
      nBoxLow=. <. (nHigh % nBox)
      nNewLow=. <. (nLow % nBox)
      for_b. i.((nBoxLow-nNewLow)+1) do.
        sChart=. 'O' (< ((nBoxLow-b)+nRowOffset),nCurrCol) } sChart
      end.
    end.
    if. 1 = nShowDate do.
      if. (0 = (sLastYearPlotted -: sYr)) do.
        for_c. i._4 do.
          sChart=. (c{sYr) (< (3-c),nCurrCol) } sChart
        end.
        sLastYearPlotted=. sYr
      end.
    end.

  else.    NB. the following is the normal (i.e., non-first) procedure:

    NB. convert prices to boxes:
    nNewHigh=. <. (nHigh % nBox)
    nNewLow=. <. (nLow % nBox)

    NB. continue upward direction?
    if. (nXCol = nColType) do.

      if. (1 <: (nNewHigh-nBoxHigh)) do.

        for_b. (1+i.(nNewHigh-nBoxHigh)) do.
          sChart=. 'X' (< (nBoxHigh+b+nRowOffset),nCurrCol) } sChart
        end.
        if. 1 = nShowDate do.
          if. (0 = (sLastMonthPlotted -: sMonth)) do.
            sChart=. sMonth (< (nBoxHigh+1+nRowOffset),nCurrCol) } 
sChart
            sLastMonthPlotted=. sMonth
            if. 1 = ".sMonth do.
              if. (0 = (sLastYearPlotted -: sYr)) do.
                for_c. i._4 do.
                  sChart=. (c{sYr) (< (3-c),nCurrCol) } sChart
                end.
                sLastYearPlotted=. sYr
              end.
            end.
          end.
        end.
        nBoxHigh=. nNewHigh
        nBoxLow=. nBoxHigh - 1    NB. for drawing purposes, 1 box below 
highest 'X'

      NB. no new high, so test for downside reversal:
      elseif. (nRevBoxes <: (nBoxHigh - nNewLow) ) do.

        if. (nCurrCol < nMaxColumns-1) do.
          nCurrCol=. nCurrCol + 1
        else.
          sChart=. |. sChart
          return.
        end.
        nColType=. nOCol
        for_b. i.((nBoxLow-nNewLow)+1) do.
          sChart=. 'O' (< ((nBoxLow-b)+nRowOffset),nCurrCol) } sChart
        end.
        if. 1 = nShowDate do.
          if. (0 = (sLastMonthPlotted -: sMonth)) do.
            sChart=. sMonth (< ((nBoxLow-2)+nRowOffset),nCurrCol) } 
sChart
            sLastMonthPlotted=. sMonth
            if. 1 = ".sMonth do.
              if. (0 = (sLastYearPlotted -: sYr)) do.
                for_c. i._4 do.
                  sChart=. (c{sYr) (< (3-c),nCurrCol) } sChart
                end.
                sLastYearPlotted=. sYr
              end.
            end.
          end.
        end.
        nBoxLow=. nNewLow
        nBoxHigh=. nBoxLow + 1    NB. for drawing purposes, 1 box above 
lowest 'O'

      end.

    NB. continue downward direction?
    elseif. (nOCol = nColType) do.

      if. (1 <: (nBoxLow-nNewLow)) do.

        for_b. (1+i.(nBoxLow-nNewLow)) do.
          sChart=. 'O' (< ((nBoxLow-b)+nRowOffset),nCurrCol) } sChart
        end.
        if. 1 = nShowDate do.
          if. (0 = (sLastMonthPlotted -: sMonth)) do.
            sChart=. sMonth (< ((nBoxLow-1)+nRowOffset),nCurrCol) } 
sChart
            sLastMonthPlotted=. sMonth
            if. 1 = ".sMonth do.
              if. (0 = (sLastYearPlotted -: sYr)) do.
                for_c. i._4 do.
                  sChart=. (c{sYr) (< (3-c),nCurrCol) } sChart
                end.
                sLastYearPlotted=. sYr
              end.
            end.
          end.
        end.
        nBoxLow=. nNewLow
        nBoxHigh=. nBoxLow + 1    NB. for drawing purposes, 1 box above 
lowest 'O'

      NB. no new low, so test for upside reversal:
      elseif. (nRevBoxes <: (nNewHigh - nBoxLow) ) do.

        if. (nCurrCol < nMaxColumns-1) do.
          nCurrCol=. nCurrCol + 1
        else.
          sChart=. |. sChart
          return.
        end.
        nColType=. nXCol
        for_b. i.((nNewHigh-nBoxHigh)+1) do.
          sChart=. 'X' (< (nBoxHigh+b+nRowOffset),nCurrCol) } sChart
        end.
        if. 1 = nShowDate do.
          if. (0 = (sLastMonthPlotted -: sMonth)) do.
            sChart=. sMonth (< (nBoxHigh+2+nRowOffset),nCurrCol) } 
sChart
            sLastMonthPlotted=. sMonth
            if. 1 = ".sMonth do.
              if. (0 = (sLastYearPlotted -: sYr)) do.
                for_c. i._4 do.
                  sChart=. (c{sYr) (< (3-c),nCurrCol) } sChart
                end.
                sLastYearPlotted=. sYr
              end.
            end.
          end.
        end.
        nBoxHigh=. nNewHigh
        nBoxLow=. nBoxHigh - 1    NB. for drawing purposes, 1 box below 
highest 'X'

      end.

    end.

  end.

end.  NB. "for" loop

NB. flip chart array so that smallest coords are at lower left
NB. rather than at upper left:
sChart=. |. sChart
)

NB. #################################################################

----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to