File Coverage

blib/lib/Finance/GeniusTrader/ArgsTree.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Finance::GeniusTrader::ArgsTree;
2              
3             # Copyright 2000-2002 Raphaėl Hertzog, Fabien Fulhaber
4             # This file is distributed under the terms of the General Public License
5             # version 2 or (at your option) any later version.
6              
7 1     1   5 use strict;
  1         1  
  1         31  
8 1     1   4 use vars qw();
  1         2  
  1         15  
9              
10 1     1   6 use Finance::GeniusTrader::Eval;
  1         2  
  1         35  
11 1     1   620 use Finance::GeniusTrader::Tools qw(:generic :conf);
  0            
  0            
12              
13             #ALL# use Log::Log4perl qw(:easy);
14              
15             =head1 NAME
16              
17             Finance::GeniusTrader::ArgsTree - Represent the arguments of calculation objects (indics/signals/systems)
18              
19             =head1 DESCRIPTION
20              
21             Each calculation object can be parameterized with arguments.
22             But those arguments can themselves be calculation objects.
23             This is represented by a complex syntax that this module can understand
24             and use to create a tree of arguments.
25              
26             =head1 SYNTAX
27              
28             The argument list is a space separated list of arguments.
29             However when the argument is not a readable value but a
30             computable one, it should be given with a different syntax :
31              
32             { I::Indicator }
33              
34             =head1 AVAILABLE FUNCTIONS
35              
36             =over
37              
38             =item C<< Finance::GeniusTrader::ArgsTree->new(@args) >>
39              
40             Create an ArgsTree object for the given list of arguments. Instead of a
41             list you can give a string representation of all the arguments.
42              
43             =cut
44             sub new {
45             my ($type, @args) = @_;
46             my $class = ref($type) || $type;
47             my $self = [ { "full_name" => "", "name" => "" } ];
48             bless $self, $class;
49             $self->add_args(@args);
50             return $self;
51             }
52              
53             =item C<< $at->add_args(@args) >>
54              
55             Process the list of arguments and adds them to the arguments tree.
56              
57             =cut
58             sub add_args {
59             my ($self, @args) = @_;
60              
61             my ($name, @objects) = parse_args(join(" ", @args));
62            
63             push @{$self}, @objects;
64             if ($self->[0]{"full_name"}) {
65             $self->[0]{"full_name"} .= " $name";
66             } else {
67             $self->[0]{"full_name"} = $name;
68             }
69             $self->create_objects(); # Update the associated objects
70             return;
71             }
72              
73             =item C<< $at->create_objects() >>
74              
75             Creates the required objects to compute the various arguments.
76              
77             =cut
78             sub create_objects {
79             my ($self) = @_;
80             for (my $i = 1; $i < scalar(@{$self}); $i++)
81             {
82             next if (ref($self->[$i]) !~ /ARRAY/);
83             my @args = get_arg_names($self->[$i]);
84             my $object = Finance::GeniusTrader::Eval::create_standard_object($self->[$i][0]{"name"}, @args);
85             my $number = extract_object_number($self->[$i][0]{"name"});
86             $self->[$i][0]{"object"} = $object;
87             $self->[$i][0]{"standard_name"} = "{" . Finance::GeniusTrader::Eval::get_standard_name($object, 1, $number) . "}";
88             $self->[$i][0]{"number"} = $number;
89             }
90             return;
91             }
92              
93             =item C<< $at->is_constant($arg_number) >>
94              
95             =item C<< $at->is_constant() >>
96              
97             Return true if the corresponding argument is of constant value
98             (ie it doesn't have to be computed each time). If no argument is given,
99             then return true if all arguments are constant.
100              
101             The first argument is numbered "1" (and not "0").
102              
103             =cut
104             sub is_constant {
105             my ($self, $n) = @_;
106             my $res = 1;
107             if (defined($n)) {
108             #ERR# ERROR "Bad argument index in is_constant" if ( $n >= 1);
109             $res = (ref($self->[$n]) =~ /ARRAY/) ? 0 : 1;
110             } else {
111             for (my $i = 1; $i < scalar(@{$self}); $i++)
112             {
113             if (ref($self->[$i]) =~ /ARRAY/) {
114             $res = 0;
115             last;
116             }
117             }
118             }
119             return $res;
120             }
121              
122             =item C<< $at->get_arg_values($calc, $day) >>
123              
124             =item C<< $at->get_arg_values($calc, $day, $n) >>
125              
126             Return the (computed) value of the indicated argument. Returns the list
127             of values of all arguments if no parameter is given.
128              
129             The first argument is numbered "1" (and not "0").
130              
131             =cut
132             sub get_arg_values {
133             my ($self, $calc, $day, $n) = @_;
134             #ERR# ERROR "Bad calculator argument for get_arg_values" if ( ref($calc) =~ /Finance::GeniusTrader::Calculator/);
135             #ERR# ERROR "Bad day argument for get_arg_values" if ( $day =~ /^\d+$/);
136             if (defined($n)) {
137             #ERR# ERROR "Bad argument index in get_arg_values" if ( $n >= 1 && $n < scalar(@{$self}));
138             my $res = undef;
139             if (ref($self->[$n]) =~ /ARRAY/) {
140             my $object = $self->[$n][0]{"object"};
141             my $number = $self->[$n][0]{"number"};
142             my $name = $object->get_name($number);
143             if (ref($object) =~ /Finance::GeniusTrader::Indicators/) {
144             $object->calculate($calc, $day)
145             unless ($calc->indicators->is_available($name, $day));
146             if ($calc->indicators->is_available($name, $day)) {
147             $res = $calc->indicators->get($name, $day);
148             return $res;
149             }
150             } elsif (ref($object) =~ /Finance::GeniusTrader::Signals/) {
151             $object->detect($calc, $day)
152             unless ($calc->signals->is_available($name, $day));
153             if ($calc->signals->is_available($name, $day)) {
154             $res = $calc->signals->get($name, $day);
155             return $res;
156             }
157             } elsif (ref($object) =~ /Finance::GeniusTrader::Analyzers/) {
158             $object->calculate($calc, $day)
159             unless ($calc->indicators->is_available($name, $day));
160             if ($calc->indicators->is_available($name, $day)) {
161             $res = $calc->indicators->get($name, $day);
162             return $res;
163             }
164             }
165             } else {
166             $res = $self->[$n];
167             }
168             return $res;
169             } else {
170             my @res;
171             for(my $i = 1; $i < scalar(@{$self}); $i++) {
172             push @res, get_arg_values($self, $calc, $day, $i);
173             }
174             return @res;
175             }
176             return;
177             }
178              
179             =item C<< $at->get_arg_constant($n) >>
180              
181             Return the constant value of the given argument. Make sure to check
182             that the argument is constant before otherwise it will die.
183              
184             =cut
185             sub get_arg_constant {
186             my ($self, $n) = @_;
187             #ERR# ERROR "The argument number $n is not a constant value" if ( ref($self->[$n]) !~ /ARRAY/);
188             #ERR# ERROR "Bad argument index in get_arg_constant" if ( $n >= 1 && $n < scalar(@{$self}));
189             my $res = $self->[$n];
190             return $res;
191             }
192              
193             =item C<< $at->get_arg_object($n) >>
194              
195             Return the associated object of the given argument. The object is something
196             able to compute the value of the argument. Make sure the argument is not a
197             constant otherwise it will die.
198              
199             =cut
200             sub get_arg_object {
201             my ($self, $n) = @_;
202             #ERR# ERROR "The argument number $n has no associated object" if ( ref($self->[$n]) =~ /ARRAY/);
203             #ERR# ERROR "Bad argument index in get_arg_object" if ( $n >= 1 && $n < scalar(@{$self}));
204             my $res = $self->[$n][0]{"object"};
205             return $res;
206             }
207              
208             =item C<< $at->get_arg_names() >>
209              
210             =item C<< $at->get_arg_names($n) >>
211              
212             Return the name the indicated argument. Returns the list
213             of names of all arguments if no parameter is given.
214              
215             The first argument is numbered "1" (and not "0").
216              
217             =cut
218             sub get_arg_names {
219             my ($self, $n) = @_;
220             if (defined($n)) {
221             #ERR# ERROR "Bad argument index in get_arg_names" if ( $n >= 1 && $n < scalar(@{$self}));
222             my $res;
223             if (ref($self->[$n]) =~ /ARRAY/) {
224             $res = $self->[$n][0]{"standard_name"} || $self->[$n][0]{"full_name"}
225             } else {
226             $res = $self->[$n];
227             }
228             return $res;
229             } else {
230             my @res;
231             for(my $i = 1; $i < scalar(@{$self}); $i++) {
232             push @res, get_arg_names($self, $i);
233             }
234             return @res;
235             }
236             return;
237             }
238              
239             =item C<< $at->get_nb_args() >>
240              
241             Return the number of arguments available.
242              
243             =cut
244             sub get_nb_args {
245             my ($self) = @_;
246             my $res = scalar(@{$self}) - 1;
247             return $res;
248             }
249              
250             =item C<< my ($full_name, @args) = Finance::GeniusTrader::ArgsTree::parse_args($args) >>
251              
252             Parse the arguments in $args and return the parsed content in the form
253             of two arrays (list of arguments).
254              
255             =cut
256             sub parse_args {
257            
258             my ($args) = @_;
259            
260             my (@objects) = ();
261             my $full_name = "";
262              
263             my @l = split(/(\s*[\{\}]\s*|\"|\s+)/, $args);
264            
265             my $started = 0;
266             my $in_quote = 0;
267             my $string = "";
268            
269             # Remove leading/trailing empty elements
270             while (scalar(@l) > 0) {
271             if ($l[0] =~ m/^\s*$/) {
272             shift @l;
273             } else {
274             last;
275             }
276             }
277             while (scalar(@l) > 0) {
278             if ($l[$#l] =~ m/^\s*$/) {
279             pop @l;
280             } else {
281             last;
282             }
283             }
284             # Treatment
285             #DEB# DEBUG "Parse args : @l\n";
286             while (scalar(@l)) {
287             $_ = shift @l;
288             #DEB# DEBUG "Treating part of argument list: « $_ »\n";
289             if ($in_quote) {
290             $full_name .= $_;
291             if (/^"$/) { #end of string
292             push @objects, $string;
293             $in_quote = 0;
294             #DEB# DEBUG "Full string argument: $string";
295             } else {
296             $string .= $_;
297             }
298             } else {
299             if (/^\s*{\s*$/) { # New complex subargument
300             my $obj = shift @l;
301            
302             # Retrieve all arguments of this new object
303             my $args = "";
304             my $count = 1;
305             while (scalar(@l)) {
306             $_ = shift @l;
307             $count++ if /^\s*{\s*$/;
308             $count-- if /^\s*}\s*$/;
309             last if $count == 0;
310             $args .= $_;
311             }
312             #WAR# WARN "Unmatched brackets in arg processing\n" if ( $count == 0);
313            
314             # Parse the arguments ///
315             my ($n, @args) = parse_args($args);
316             #DEB# DEBUG "Left to parse: @l\n";
317              
318             my (@names) = args_to_ascii(@args);
319            
320             # If the object is an alias, resolve it
321             if ($obj =~ /^@(\S+)$/) {
322             my $def = resolve_object_alias(long_name($1), @names);
323             #DEB# DEBUG "Alias $1 maps to $def\n";
324             unshift @l, split(/(\s*[\{\}]\s*|\"|\s+)/, $def);
325             next;
326             }
327              
328             # Add the new object
329             $full_name .= " {$obj $n}";
330             push @objects, [ { "full_name" => "{$obj $n}", "name" => $obj }, @args ];
331             #DEB# DEBUG "New argument: $objects[$#objects]\n";
332             #DEB# DEBUG "Current list of argument: @objects\n";
333            
334             } elsif (/^\s*$/) { # New argument
335             # Nothing
336             $full_name .= " ";
337             } elsif (/^"$/) {
338             $in_quote = 1;
339             $string = "";
340             $full_name .= '"';
341             } else { # New data
342             if (/^@(\S+)$/) {
343             my $def = resolve_object_alias(long_name($1));
344             #DEB# DEBUG "Alias $1 maps to $def\n";
345             unshift @l, split(/(\s*[\{\}]\s*|\"|\s+)/, $def);
346             next;
347             } else {
348             $full_name .= $_;
349             push @objects, $_;
350             #DEB# DEBUG "New argument: $_\n";
351             #DEB# DEBUG "Current list of argument: @objects\n";
352             }
353             }
354             }
355             }
356            
357             return ($full_name, @objects);
358             }
359              
360             =item C<< Finance::GeniusTrader::ArgsTree::args_to_ascii(@args) >>
361              
362             Return the ascii representation of all the parameters described in
363             @args.
364              
365             =cut
366             sub args_to_ascii {
367             my @args = @_;
368             my @res = map {
369             if (ref($_) =~ /ARRAY/) {
370             $_->[0]{'standard_name'}||$_->[0]{'full_name'}
371             } else {
372             $_
373             }
374             } @args;
375             return @res;
376             }
377              
378             =item C<< $args->prepare($calc, $day) >>
379              
380             Precalculate all possible values for the given day.
381              
382             =cut
383             sub prepare {
384             my ($self, $calc, $day) = @_;
385             for(my $i = 1; $i < scalar(@{$self}); $i++) {
386             next if $self->is_constant($i);
387             my $object = $self->[$i][0]{"object"};
388             if (ref($object) =~ /Finance::GeniusTrader::Indicators/) {
389             $object->calculate($calc, $day);
390             } elsif (ref($object) =~ /Finance::GeniusTrader::Signals/) {
391             $object->detect($calc, $day);
392             } elsif (ref($object) =~ /Finance::GeniusTrader::Analyzers/) {
393             $object->calculate($calc, $day);
394             }
395             }
396             return;
397             }
398              
399             =item C<< $args->prepare_interval($calc, $first, $last) >>
400              
401             Precalculate all possible values for the given interval.
402              
403             =cut
404             sub prepare_interval {
405             my ($self, $calc, $first, $last) = @_;
406             for(my $i = 1; $i < scalar(@{$self}); $i++) {
407             next if $self->is_constant($i);
408             my $object = $self->[$i][0]{"object"};
409             if (ref($object) =~ /Finance::GeniusTrader::Indicators/) {
410             $object->calculate_interval($calc, $first, $last);
411             } elsif (ref($object) =~ /Finance::GeniusTrader::Signals/) {
412             $object->detect_interval($calc, $first, $last);
413             } elsif (ref($object) =~ /Finance::GeniusTrader::Analyzers/) {
414             $object->calculate_interval($calc, $first, $last);
415             }
416             }
417             return;
418             }
419              
420             =back
421              
422             =cut
423             1;