>Common Lisp: Importing price data

>The following code is the first part of the project to make a system for back-testing trading strategies. The raw data that we work with is defined by a “bar”. A bar is is a representation of the movement of price over, usually and traditionally, a period of time. A bar is further defined as the set of 4 values of opening, closing, high, and low prices. The raw data is obtained from numerous websites and by the brokers themselves. I download the data in the form of CSV-files since they are plain-text files that can be read by any computer system.

The code that is on this page is also available as a lisp file, just in case anyone wants to download it and not worry about the indentation soup that Blogger seems to like doing. The code is available here.

How this program works

The “bar” class (Object-oriented programming) that is defined characterises one bar. It contains, apart from open, high, low, and close variables (Called “slots” in Common Lisp), a whole bunch of other variables that I also add to the mix during the process of creating a trading system.

The heavy lifting in this program is done, naturally, by the only top-level function there is, named READ-OHLC (For “Read Open, High, Low, and Close values”). This functions walks over each line in the csv-file offered to it (Argument 1, called “file-name”), extracts the information, and then places the values of interest (O, H, L & C) into an instance (object) of the class. Finally, that instance of the bar-class is added to the end of the array (Argument 2, called “array-name”). The last line of code is an example of a call to the READ-OHLC function.

This program creates a vector of bar-objects. This vector is then used as source data for other programs and functions that will do the various calculations that, when put together, form and simulate a complete trading system.

Note: In Lisp, comments are anything between a “;” (Semi-colon) and the end of the line.

;; This class holds all the variables that we shall be working with, for each bar of the data.
(defclass bar ()
((openb :initarg :open :reader openb :type single-float)
(high :initarg :high :reader high :type single-float)
(low :initarg :low :reader low :type single-float)
(closeb :initarg :close :reader closeb :type single-float)
(sqnb :initform 0 :accessor sqnb) ;SQN of last N bars.
(atrb :initform 0 :accessor atrb) ;ATR of last N bars.
(trb :initform 0 :accessor trb) ;TR of this bar.
(atrperb :initform 0 :accessor atrperb) ;ATR as a percentage of the close.
(atr-perc-deviation :initform 0 :accessor atr-perc-deviation) ;Deviation of ATR of last N bars to the average ATR.
(volatility-type :initform nil :accessor volatility-type) ;Quiet(0), normal(1), volatile(2), or very volatile(3)?
(enter :initform nil :accessor enter))
(:documentation "This object defines the price-points and other qualities of a single bar."))

;; The data in this vector is ordered from oldest to newest.
(defparameter *array* (make-array 200 :fill-pointer 0 :adjustable t :element-type 'bar))

;; This variable will be the average ATR% in the array.
(defparameter *average-atr-percentage* 0
"The average ATR% in the array.")

;; This is the standard deviation of ATR% in the array.
(defparameter *atrperstddev* 0
"The standard deviation of ATR% in the vector.")

;; This function reads in the csv-file and places objects of the 'bar' class into *array*
(defun read-ohlc (file-name array-name)
"This function reads from 'file-name' and puts bar-objects in 'array-name'."
(let ((temp nil))
(with-open-file (file file-name)
(loop for line = (read-line file nil) ;The 'nil' is so that there is no EOF error.
while line do
(setf temp (cl-ppcre:split "," line))
(vector-push-extend (make-instance 'bar
:open (read-from-string (third temp))
:high (read-from-string (fourth temp))
:low (read-from-string (fifth temp))
:close (read-from-string (sixth temp)))

;Data from http://www.fxhistoricaldata.com/

;; Perform actions
(read-ohlc "GBPUSD60.csv" *array*)

I’ve read in books on Lisp programming and in UNIX philosophy that using the correct data-structure is the first step to a good program. I have not been able to think of a data-structure better than the direct representation of the bars themselves. If and when I do, I will change the underlying data structure of the program.

Leave a Reply