File Coverage

blib/lib/Finance/StockAccount/Transaction.pm
Criterion Covered Total %
statement 156 205 76.1
branch 59 92 64.1
condition 12 25 48.0
subroutine 35 38 92.1
pod 0 30 0.0
total 262 390 67.1


line stmt bran cond sub pod time code
1             package Finance::StockAccount::Transaction;
2              
3             our $VERSION = '0.01';
4              
5 10     10   6011 use Time::Moment;
  10         9309  
  10         249  
6              
7 10     10   4108 use Finance::StockAccount::Stock;
  10         15  
  10         242  
8              
9 10     10   46 use strict;
  10         12  
  10         245  
10 10     10   37 use warnings;
  10         12  
  10         213  
11              
12 10     10   35 use constant BUY => 0;
  10         400  
  10         572  
13 10     10   40 use constant SELL => 1;
  10         13  
  10         404  
14 10     10   116 use constant SHORT => 2;
  10         13  
  10         355  
15 10     10   56 use constant COVER => 3;
  10         10  
  10         17260  
16              
17             my $lineFormatPattern = "%-35s %-6s %-6s %8s %7.2f %10.2f %5.2f %10.2f\n";
18             my $headerPattern = "%-35s %-6s %-6s %8s %7s %10s %5s %10s\n";
19             my @headerNames = qw(Date Symbol Action Quantity Price Commission Fees CashEffect);
20              
21             sub new {
22 278     278 0 1097 my ($class, $init) = @_;
23 278         1186 my $self = {
24             tm => undef,
25             action => undef,
26             stock => undef,
27             quantity => undef,
28             price => undef,
29             commission => 0,
30             regulatoryFees => 0,
31             otherFees => 0,
32             };
33 278         681 bless($self, $class);
34 278 100       707 $init and $self->set($init);
35 278         495 return $self;
36             }
37              
38             sub order {
39 2     2 0 5 return qw(date action stock quantity price commission regulatoryFees otherFees);
40             }
41              
42             sub tm { # Time::Moment object getter/setter
43 13414     13414 0 10739 my ($self, $tm) = @_;
44 13414 100       15611 if ($tm) {
45 241 50 33     921 if (ref($tm) and ref($tm) eq 'Time::Moment') {
46 241         254 $self->{tm} = $tm;
47 241         428 return 1;
48             }
49             else {
50 0         0 warn "$tm not a valid Time::Moment object.\n";
51 0         0 return 0;
52             }
53             }
54             else {
55 13173         25110 return $self->{tm};
56             }
57             }
58              
59              
60             sub dateString {
61 29     29 0 36 my ($self, $dateString) = @_;
62 29 100       62 if ($dateString) {
63 27         174 my $tm = Time::Moment->from_string($dateString);
64 27 50       356 if ($tm) {
65 27         39 $self->{tm} = $tm;
66 27         101 return 1;
67             }
68             else {
69 0         0 warn "Unable to create Time::Moment object from date string $dateString.\n";
70 0         0 return 0;
71             }
72             }
73             else {
74 2         3 my $tm = $self->{tm};
75 2 50       12 if ($tm) {
76 2         15 return $tm->to_string();
77             }
78             else {
79 0         0 warn "Time::Moment property not set.\n";
80 0         0 return undef;
81             }
82             }
83             }
84              
85             sub action {
86 36     36 0 44 my ($self, $action) = @_;
87 36 50       53 if ($action) {
88 36 100       84 if ($action eq 'buy') {
    100          
    50          
    0          
89 21         54 return $self->buy(1);
90             }
91             elsif ($action eq 'sell') {
92 14         44 return $self->sell(1);
93             }
94             elsif ($action eq 'short') {
95 1         5 return $self->short(1);
96             }
97             elsif ($action eq 'cover') {
98 0         0 return $self->cover(1);
99             }
100             else {
101 0         0 die "Action must a string, one of 'buy', 'sell', 'short', 'cover'.\n";
102             }
103             }
104             else {
105 0         0 return $self->{action};
106             }
107             }
108              
109             sub stock {
110 2678     2678 0 2184 my ($self, $stock) = @_;
111 2678 50       3138 if ($stock) {
112 0 0 0     0 if (ref($stock) and 'Finance::StockAccount::Stock' eq ref($stock)) {
113 0         0 $self->{stock} = $stock;
114 0         0 return 1;
115             }
116             else {
117 0         0 warn "$stock is not a recognized Finance::StockAccount::Stock object.\n";
118 0         0 return 0;
119             }
120             }
121             else {
122 2678         2510 $stock = $self->{stock};
123 2678 100       3944 if (!$stock) {
124 275         705 $stock = Finance::StockAccount::Stock->new();
125 275         329 $self->{stock} = $stock;
126             }
127 2678         5784 return $stock;
128             }
129             }
130              
131             sub sameStock {
132 0     0 0 0 my ($self, $testStock) = @_;
133 0         0 my $stock = $self->{stock};
134 0 0       0 if ($stock) {
135 0         0 return $stock->same($testStock);
136             }
137             else {
138 0         0 warn "Can't test for sameStock, object stock property not yet defined.\n";
139 0         0 return 0;
140             }
141             }
142              
143             sub symbol {
144 1101     1101 0 1086 my ($self, $symbol) = @_;
145 1101         1483 my $stock = $self->stock();
146 1101         2429 return $stock->symbol($symbol);
147             }
148              
149             sub exchange {
150 2     2 0 2 my ($self, $exchange) = @_;
151 2         4 my $stock = $self->stock();
152 2         5 return $stock->exchange($exchange);
153             }
154              
155             sub quantity {
156 3065     3065 0 2526 my ($self, $quantity) = @_;
157 3065 100       3569 if ($quantity) {
158 1         1 $self->{quantity} = $quantity;
159 1         2 return 1;
160             }
161             else {
162 3064         5978 return $self->{quantity};
163             }
164             }
165              
166             sub price {
167 1     1 0 2 my ($self, $price) = @_;
168 1 50       2 if ($price) {
169 0         0 $self->{price} = $price;
170 0         0 return 1;
171             }
172             else {
173 1         4 return $self->{price};
174             }
175             }
176              
177             sub commission {
178 3010     3010 0 2568 my ($self, $commission) = @_;
179 3010 50       3713 if ($commission) {
180 0         0 $self->{commission} = $commission;
181 0         0 return 1;
182             }
183             else {
184 3010         6184 return $self->{commission};
185             }
186             }
187              
188             sub regulatoryFees {
189 3009     3009 0 2510 my ($self, $regulatoryFees) = @_;
190 3009 50       3407 if ($regulatoryFees) {
191 0         0 $self->{regulatoryFees} = $regulatoryFees;
192 0         0 return 1;
193             }
194             else {
195 3009         5585 return $self->{regulatoryFees};
196             }
197             }
198              
199             sub otherFees {
200 3009     3009 0 2346 my ($self, $otherFees) = @_;
201 3009 50       3536 if ($otherFees) {
202 0         0 $self->{otherFees} = $otherFees;
203 0         0 return 1;
204             }
205             else {
206 3009         5434 return $self->{otherFees};
207             }
208             }
209              
210             sub priceByQuantity {
211 3543     3543 0 2687 my $self = shift;
212 3543         6712 return $self->{price} * $self->{quantity};
213             }
214              
215             sub feesAndCommissions {
216 3543     3543 0 2524 my $self = shift;
217 3543         6253 return $self->{commission} + $self->{regulatoryFees} + $self->{otherFees};
218             }
219              
220             sub cashEffect {
221 3543     3543 0 2981 my $self = shift;
222 3543         2579 my $cashEffect;
223 3543 100 66     4580 if ($self->buy() or $self->short()) {
    50 33        
224 1604         2415 $cashEffect = 0 - ($self->priceByQuantity() + $self->feesAndCommissions());
225             }
226             elsif ($self->sell() or $self->cover()) {
227 1939         2398 $cashEffect = $self->priceByQuantity() - $self->feesAndCommissions();
228             }
229 3543 50       4974 if ($cashEffect) {
230 3543         6985 return $cashEffect;
231             }
232             else {
233 0         0 warn "Cannot calculate cash effect.\n";
234 0         0 return 0;
235             }
236             }
237              
238             sub set {
239 280     280 0 245 my ($self, $init) = @_;
240 280         223 my $status = 1;
241 280         227 foreach my $key (keys %{$init}) {
  280         787  
242 1649 100       2269 if (exists($self->{$key})) {
    100          
    50          
    50          
243 1348 100       1998 if ($key eq 'action') {
    100          
244 36         90 $self->action($init->{$key});
245             }
246             elsif ($key eq 'tm') {
247 241         363 $self->tm($init->{$key});
248             }
249             else {
250 1071         1638 $self->{$key} = $init->{$key};
251             }
252             }
253             elsif ($key eq 'symbol') {
254 274         460 $self->symbol($init->{$key});
255             }
256             elsif ($key eq 'exchange') {
257 0         0 $self->exchange($init->{$key});
258             }
259             elsif ($key eq 'dateString') {
260 27         63 $self->dateString($init->{$key});
261             }
262             else {
263 0         0 $status = 0;
264 0         0 warn "Tried to set $key in StockAccount::Transaction object, but that's not a known key.\n";
265             }
266             }
267 280         427 return $status;
268             }
269              
270             sub get {
271 0     0 0 0 my ($self, $key) = @_;
272 0 0 0     0 if ($key and exists($self->{$key})) {
273 0         0 return $self->{$key};
274             }
275             else {
276 0         0 warn "Tried to get key from StockAccount::Transaction object, but that's not a known key.\n";
277 0         0 return 0;
278             }
279             }
280              
281             sub validateAction {
282 24843     24843 0 19359 my $self = shift;
283 24843 100       31899 if (!defined($self->{action})) {
284 1         10 die "Action has not yet been set.";
285 0         0 return 0;
286             }
287             else {
288 24842         22283 return 1;
289             }
290             }
291              
292             sub buy {
293 15847     15847 0 12427 my ($self, $assertion) = @_;
294 15847 100       16577 if ($assertion) {
295 167         180 $self->{action} = BUY;
296 167         266 return 1;
297             }
298             else {
299 15680         17398 $self->validateAction();
300 15680         58483 return $self->{action} == BUY;
301             }
302             }
303              
304             sub sell {
305 5727     5727 0 4624 my ($self, $assertion) = @_;
306 5727 100       6868 if ($assertion) {
307 111         124 $self->{action} = SELL;
308 111         174 return 1;
309             }
310             else {
311 5616         5851 $self->validateAction();
312 5615         14499 return $self->{action} == SELL;
313             }
314             }
315              
316             sub short {
317 3548     3548 0 2871 my ($self, $assertion) = @_;
318 3548 100       4011 if ($assertion) {
319 1         3 $self->{action} = SHORT;
320 1         4 return 1;
321             }
322             else {
323 3547         3801 $self->validateAction();
324 3547         12236 return $self->{action} == SHORT;
325             }
326             }
327              
328             sub cover {
329 0     0 0 0 my ($self, $assertion) = @_;
330 0 0       0 if ($assertion) {
331 0         0 $self->{action} = COVER;
332 0         0 return 1;
333             }
334             else {
335 0         0 $self->validateAction();
336 0         0 return $self->{action} == COVER;
337             }
338             }
339              
340             sub actionString {
341 1154     1154 0 1023 my $self = shift;
342 1154 100       1422 if ($self->buy()) {
    50          
    0          
    0          
343 93         560 return 'buy';
344             }
345             elsif ($self->sell()) {
346 1061         1909 return 'sell';
347             }
348             elsif ($self->short()) {
349 0         0 return 'short';
350             }
351             elsif ($self->cover()) {
352 0         0 return 'cover';
353             }
354             else {
355 0         0 return '';
356             }
357             }
358              
359             sub string {
360 2     2 0 231 my $self = shift;
361 2         3 my $pattern = "%14s %-35s\n";
362 2         2 my $string;
363 2         4 foreach my $key ($self->order()) {
364 16 100       29 if (defined($self->{$key})) {
    50          
365 14 100       15 if ($key eq 'stock') {
366 2         4 my $symbol = $self->symbol();
367 2         6 my $exchange = $self->exchange();
368 2 50       13 if (defined($symbol)) {
369 2         3 $string .= sprintf($pattern, 'symbol', $self->symbol());
370             }
371 2 50       7 if (defined($exchange)) {
372 0         0 $string .= sprintf($pattern, 'exchange', $self->exchange());
373             }
374             }
375             else {
376 12         9 my $value;
377 12 100       17 if ($key eq 'action') {
378 2         7 $value = $self->actionString();
379             }
380             else {
381 10         10 $value = $self->{$key};
382             }
383 12         38 $string .= sprintf($pattern, $key, $value);
384             }
385             }
386             elsif ($key eq 'date') {
387 2 50       18 if ($self->{tm}) {
388 2         7 $string .= sprintf($pattern, $key, $self->dateString());
389             }
390             }
391             }
392 2         9 return $string;
393             }
394              
395             sub lineFormatHeader {
396 3     3 0 26 return sprintf($headerPattern, @headerNames);
397             }
398              
399             sub lineFormatPattern {
400 148     148 0 856 return $lineFormatPattern;
401             }
402              
403             sub lineFormatValues {
404 148     148 0 109 my $self = shift;
405             return [
406 148   100     1172 $self->{tm} || '', $self->symbol(), $self->actionString(), $self->{quantity}, $self->{price} || 0,
      50        
      100        
      100        
      50        
407             $self->{commission} || 0, ($self->{regulatoryFees} + $self->{otherFees}) || 0, $self->cashEffect() || 0
408             ];
409             }
410              
411             sub lineFormatString {
412 1     1 0 1 my $self = shift;
413 1         1 return sprintf($lineFormatPattern, @{$self->lineFormatValues()});
  1         2  
414             }
415              
416              
417             1;
418              
419             __END__