Some code for creating a "point-and-figure" chart from Dow Jones price data for 2007 and 2008.
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:
NB. bMktData=. readcsv (jpath '~user\data\DJI-r-pf.csv')
bMktData=. readcsv 'DJI2007-2008.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
)