NAME
Finance::Shares::test - user programmable control
SYNOPSIS
Three examples of how to specify a test, one showing the minimum required and the other illustrating all the possible fields.
use Finance::Shares::Model;
my @spec = (
...
tests => [
basic => qq( # perl code here ),
iterative => {
before => qq( # perl code here ),
during => qq( # perl code here ),
after => qq( # perl code here ),
},
code => sub { print "hello world\n" },
],
samples => [
...
one => {
tests => ['basic', 'iterative'],
# NOT 'code' !
...
}
],
);
my $fsm = new Finance::Shares::Model( @spec );
$fsm->build();
DESCRIPTION
This module allows model test code to write points or lines on the graphs.
The code must be declared within a tests block in a Finance::Shares::Model specification. Each test is a segment of plain text containing perl code. This is compiled by the model engine and run as required.
Test Types
As shown by the SYNOPSIS examples, there are three types of test.
Simple text
The simplest and most common form, this perl fragment is run on every data point in the sample where it is applied. It can be used to produce conditional signal marks on the charts, noting dates and prices for potential stock positions either as printed output or by invoking a callback function.
Hash ref
This may have three keys, before
, during
and after
. Each is a perl fragment in text form. before
is run once, before the sample is considered. during
is run for every point in the sample and is identical to "Simple text". after
is run once after during
has finished.
This form allows one-off invocation of callbacks, or an open-print-close sequence for writing signals to file.
To assist debugging there is an additional field, verbose
, which overrides the global value. Setting this to 2 or more shows the code fragments that are actually evaluated. With '2', the line substitutions are listed by their keys while '3' lists them by their internal names.
Raw code
These are the callbacks invoked by the previous types of perl fragment. The parameters are those used to call the code. See "call" below.
Variables
In addition to perl variables declared with my
or our
, undeclared scalar variables are used to refer to the values of other lines on the chart.
Before the code is compiled a search is made for everything that looks like a scalar or list variable that expands to a line name. The name is then replaced by a reference to the internal data so that it executes correctly.
As in the lines specifications, it is possible to refer to several lines at once. If this is prefixed by '@', all matching values are returned as a list. The same expression prefixed by '$' returns the first value. [Note that it does NOT return the number in the array.]
The special variables are as follows.
- (a)
-
A tag from names or lines;
Avoid all variable names that are used by Model. These include $open, $high, $low, $close and $volume - aliases for the Finance::Shares::data lines.
[Warning Due to the way code fragments are pre-processed, variables should not begin with these names, either.]
- (b)
-
A fully qualified line name (see "Fully Qualified Line Names" in Finance::Shares::Model);
- (c)
-
A FQLN with wildcards or (some) regular expressions.
As with lines, the wildcard '*' means "any (sample, stock or date) other than this one". Use the regular expression '.*' to match every page including the current one.
[The support for regular expressions is quite limited. Code fragments handle line references by rewriting (like 'C' macro pre-processing). So to use a regular expression as a name, it becomes necessary to search/replace a regexp string, temporarily ignoring any special characters. Only a few of these are escaped at present: '$', '@', '.', '*'. These are just enough to handle lists of lines, the wildcard '*' and regular expression '.*'.]
Otherwise the '$' or '@' value is assumed to be a valid perl variable and is left alone.
Examples
lines => [
avg => {
function => 'moving_average',
},
boll => {
function => 'bollinger_band',
},
],
test => q(
$v1 = $avg; # normal line
$v2 = $boll/high; # one of the 2 produced
@v3 = @*/*/*/close; # i.e. all other close prices
@v4 = @.*/.*//volume; # i.e. all volumes with this date
$v5 = $morrison/MRW.L/default/close; # 'close' is an alias (a 'name')
$v6 = $morrison/MRW.L/default/boll/low; # fully qualified line name
$v7 = $tesco///close; # fqln with current stock & date
@v8 = @data; # all the 'data' lines
$v9 = @data; # the number of 'data' lines
$v0 = $data; # the first 'data' line
),
samples => [
morrison => {
stock => 'MRW.L',
test => 'default',
},
tesco => {
stock => 'TSCO.L',
test => 'default',
},
],
If it does refer to a line, the value becomes the value for the line at that date. Clearly this only makes sense while the test is iterating through the data in the during
phase. [If a line is referenced in before
or after
code fragments, they return the Finance::Shares::Line object rather than its data.]
Each test also has a special variable $self
available. This is a hash ref holding persistent values (similar to an object ref, but not blessed). These values are predefined.
- $self->{date}
-
Only useful in the
during
phase, this is set to the current date. - $self->{page}
-
This returns the string identifying the current page.
- $self->{stock}
-
This is the stock code, as given to the model's sample entry for this page.
- $self->{first}
-
The first date which has data available. The
during
phase starts with this. - $self->{last}
-
The last date which has data available. The
during
phase ends with this. - $self->{start}
-
The first date normally displayed on the chart.
- $self->{end}
-
The last date normally displayed on the chart.
- $self->{by}
-
How the time periods are counted. Will be one of quotes, weekdays, days, weeks, months.
- $self->{quotes}
-
This is the Finance::Shares::data object used to store the dates, price and volume data for the page. See "Page Names" in Finance::Shares::Model.
It should not normally be needed, but it provides a door into the engine for those who need to access it.
Although some effort has been made to make the code appear to be 'natural' perl, it is worth remembering that it is not. For example, if something won't parse, try putting in more spaces. Setting the verbose
option to 2+ shows the code that is actually evaluated - i.e. after the preprocessor substitutions.
Marking the Chart
A special function is provided which will add a point to a chart line. It is called as
mark( tag, value )
tag
-
This must refer to lines block entry which has
mark
as the function field. value
-
Can be a number or undefined, but is often a psuedo-scalar variable refering to a line. See "Variables" above.
Example
lines => [
circle => {
function => 'mark',
},
average => {
function => 'moving_average',
},
],
tests => [
show => q(
mark('circle', $average);
),
],
sample => {
test => 'show',
},
In this example the moving average line is marked twice. As well as the normal line, the show
test is invoked and a circle (the default mark) is placed at every position where average
is defined.
Notice that the engine invokes the moving average function for you as it is referred to in the test. There is no need to list it explicitly as a line.
Calling Foreign Code
Model test entries may be code refs as well as text. The code is then called using a special function, which will return whatever the code returns.
call( tag, arguments )
scalar = call( tag, arguments )
list = call( tag, arguments )
tag
-
This must be a tag in a tests block identifying a code ref.
arguments
-
This list of arguments (or none) is passed to the subroutine identified by
tag
.
Example
tests => [
tcode => {
my $v = shift;
print "$v->{stock} quotes from $v->{first}\n";
},
doit => {
before => q(
call('tcode', $self);
),
},
],
sample => {
test => 'doit',
},
Remember that $self
has a number of predefined fields, {stock}
and {first}
among them. The before
code is only invoked at the start of the test. It calls the subroutine which prints a message.
This might seem an involved way to go about it, but it is extremely powerful. The subroutine call is made within code which has all the model values (as well as the complete power of perl) available. It may be passed whatever values you choose.
Example
This is a slightly more useful example. It assumes that the MyPortfolio module has exported subroutines defined for simulating buying/selling shares and keeping track of positions you hold in the market. A 'buy' call is made when the stock price seems to rise.
# declare function modules used
use Finance::Shares::moving_average;
# import callback from another module
use MyPortFolio 'enter_long_position';
# Model specification begins
...
lines => [
fast => {
function => 'moving_average',
period => 5,
},
slow => {
function => 'moving_average',
period => 20,
},
],
tests => [
buy => &enter_long_position,
code => q(
call('buy', 'pf1', $self->{date}, $low, $high)
if $fast >= $slow;
);
],
sample => {
test => 'code',
},
When the 5 day moving average crosses above the 20 day one, the callback is invoked. It will be passed a portfolio ID, the date, lowest and highest proces for the day.
<realism> A buy signal would need to be invoked on the basis if at least yesterday's data as todays is usually not yet known! This might be done by using a $self->{buy} flag and having after => 1
set in the appropriate dates entry. </realism>
BUGS
These lines are reserved: $open, $high, $low, $close, $volume. If a test uses a tag name starting with any of these, it will get confused.
Please let me know when you suspect something isn't right. A short script working from a CSV file demonstrating the problem would be very helpful.
AUTHOR
Chris Willmot, chris@willmot.org.uk
LICENCE
Copyright (c) 2002-2003 Christopher P Willmot
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. A copy can be found at http://www.gnu.org/copyleft/gpl.html
SEE ALSO
Finance::Shares::Overview provides an introduction to the suite, and fsmodel is the principal script.
Modules involved in processing the model include Finance::Shares::Model, Finance::Shares::MySQL, Finance::Shares::Chart. Chart and file details may be found in PostScript::File, PostScript::Graph::Paper, PostScript::Graph::Key, PostScript::Graph::Style.
All functions are invoked from their own modules, all with lower-case names such as Finance::Shares::moving_average. The nitty-gritty on how to write each line specification are found there.
The quote data is stored in a Finance::Shares::data object. For information on writing additional line functions see Finance::Share::Function and Finance::Share::Line. Also, Finance::Share::test covers writing your own tests.