File Coverage

blib/lib/VolSurface/Calibration/Equities.pm
Criterion Covered Total %
statement 225 231 97.4
branch 45 52 86.5
condition 17 21 80.9
subroutine 22 22 100.0
pod 3 3 100.0
total 312 329 94.8


line stmt bran cond sub pod time code
1             package VolSurface::Calibration::Equities;
2              
3 2     2   60387 use strict;
  2         5  
  2         52  
4 2     2   10 use warnings;
  2         5  
  2         84  
5              
6             our $VERSION = '0.03';
7              
8             =head1 NAME
9              
10             VolSurface::Calibration::Equities
11              
12             =head1 DESCRIPTION
13              
14             This module implements Binary.com's volatility surface calibration algorithm which is based on SABR.
15             The SABR (Stochastic alpha, beta, rho) model is a stochastic volatility model, which is used to
16             estimate the volatility smile in the derivatives market. For a more general overview of the general
17             SABR model, please refer https://en.wikipedia.org/wiki/SABR_volatility_model.
18              
19             =head1 VERSION
20              
21             Version 0.03
22              
23             =cut
24              
25 2     2   2796 use Moose;
  2         970465  
  2         14  
26              
27 2     2   16137 use Date::Utility;
  2         700665  
  2         105  
28 2     2   20 use List::MoreUtils qw(first_index);
  2         4  
  2         22  
29 2     2   913 use List::Util qw(min max);
  2         5  
  2         158  
30 2     2   3047 use Math::Trig qw(tanh);
  2         42046  
  2         185  
31 2     2   21 use Try::Tiny;
  2         5  
  2         116  
32 2     2   1830 use Format::Util::Numbers qw(roundnear);
  2         2274  
  2         5863  
33              
34             =head2 calibration_param_names
35              
36             Calibration parameter names.
37             It is hard-coded here because it needs to be in this sequence.
38              
39             =cut
40              
41             our @calibration_param_names = qw/
42             atmvolshort
43             atmvol1year
44             atmvolLong
45             atmWingL
46             atmWingR
47             skewshort
48             skew1year
49             skewlong
50             skewwingL
51             skewwingR
52             kurtosisshort
53             kurtosislong
54             kurtosisgrowth/;
55              
56             has surface => (
57             is => 'ro',
58             isa => 'HashRef',
59             required => 1,
60             );
61              
62             =head2 term_by_day
63              
64             Get all the terms in a surface in ascending order.
65              
66             =cut
67              
68             has term_by_day => (
69             is => 'ro',
70             isa => 'ArrayRef',
71             required => 1,
72             );
73              
74             =head2 parameterization
75              
76             The parameterized (and, thus, smoothed) version of this surface.
77              
78             =cut
79              
80             has parameterization => (
81             is => 'rw',
82             isa => 'Maybe[HashRef]',
83             );
84              
85             =head2 smile_points
86              
87             The points across a smile.
88              
89             It can be delta points, moneyness points or any other points that we might have in the future.
90              
91             =cut
92              
93             has smile_points => (
94             is => 'ro',
95             isa => 'ArrayRef',
96             required => 1,
97             );
98              
99             has _max_iteration => (
100             is => 'ro',
101             isa => 'Int',
102             default => 500,
103             );
104              
105             has _tolerance_level => (
106             is => 'ro',
107             isa => 'Num',
108             default => 1e-05,
109             );
110              
111             has _scale => (
112             is => 'ro',
113             isa => 'Num',
114             default => 10,
115             );
116              
117             =head2 function_to_optimize
118              
119             The function that we want to optimize.
120              
121             =cut
122              
123             sub function_to_optimize {
124 1835     1835 1 3013 my ($self, $params) = @_;
125              
126             # this number is returned when the optimization is heading the wrong path
127 1835         2393 my $error = 1000;
128              
129 1835         2777 my %params;
130 1835         13171 @params{@calibration_param_names} = @$params;
131              
132 1835         3188 my @tenors = @{$self->term_by_day};
  1835         79922  
133             my @variance =
134 1835         3252 map { $self->_calculate_variance($_ / 365, \%params) } @tenors;
  12845         30578  
135              
136 1835         4022 foreach my $param_name (@calibration_param_names) {
137 23503         36703 my $param_value = $params{$param_name};
138 23503 100 100     87353 return $error
139             if ($param_name =~ /wing(R|L)/i && abs($param_value) > 20.0);
140 23465 100 100     65020 return $error
141             if ($param_name =~ /growth/i && abs($param_value) > 5.0);
142 23457 100 100     154488 return $error
      100        
143             if ($param_name !~ /wing(R|L)/i
144             && $param_name !~ /growth/i
145             && abs($param_value) > 2.0);
146             }
147              
148 1778 100       6725 return $error if (min(@variance) < 0);
149              
150 1713         2658 my @atmvols = map { sqrt($_) } @variance;
  11991         18574  
151 1713         4668 my $calibrated_surface = $self->get_calibrated_surface(\%params);
152 1713         71612 my $actual_surface = $self->surface;
153              
154 1713         2638 my $total = 0;
155 1713         2027 foreach my $day (@{$self->term_by_day}) {
  1713         71251  
156 11991         14540 my $sum = 0;
157 11991         13376 my $tenorvega = 0;
158 11991         16188 my $atm_vol = shift @atmvols;
159 11991         16151 foreach my $point (@{$self->smile_points}) {
  11991         506620  
160 251811         410634 my $atm_check = log($point / 100) / $atm_vol / sqrt($day / 365);
161 251811         408508 my $calibrated_vol = $calibrated_surface->{$day}->{$point};
162 251811         436414 my $actual_vol = $actual_surface->{$day}->{smile}->{$point};
163 251811         596693 my $d1 = (log(100 / $point) + (0.5 * $actual_vol**2) * ($day / 365)) / ($actual_vol * ($day / 365)**0.5);
164 251811         415449 my $nd1 = (1 / (2 * 3.1416)**0.5) * exp(-$d1 * $d1 / 2);
165 251811         395514 my $vega = $nd1 * ($day / 365)**0.5;
166 251811         349097 $sum += $vega * abs($calibrated_vol - $actual_vol);
167 251811         403469 $tenorvega += $vega;
168             }
169 11991         22247 $total += $sum / $tenorvega;
170             }
171              
172 1713         30001 return $total;
173             }
174              
175             =head2 default_initial_guess
176              
177             Initial guess for parameters. We need to start with something.
178              
179             =cut
180              
181             has default_initial_guess => (
182             is => 'ro',
183             isa => 'HashRef',
184             default => sub {
185             {
186             atmvolshort => 0.13,
187             atmvol1year => 0.19,
188             atmvolLong => 0.12,
189             atmWingL => 1.27,
190             atmWingR => 1.56,
191             skewshort => -0.27,
192             skew1year => -0.08,
193             skewlong => -0.03,
194             skewwingL => 2.01,
195             skewwingR => 2.03,
196             kurtosisshort => 0.001,
197             kurtosislong => 0.001,
198             kurtosisgrowth => 0.001
199             };
200             },
201             );
202              
203             =head2 strike_ratio
204              
205             Bounds for moneyness (strike ratio of 1 is equivalent to 100% moneyness).
206              
207             =cut
208              
209             has strike_ratio => (
210             is => 'ro',
211             isa => 'HashRef',
212             default => sub {
213             {
214             lower => 0.60,
215             higher => 1.30,
216             };
217             },
218             );
219              
220             =head2 compute_parameterization
221              
222             Returns a hash reference with new parameterization and calibration_error.
223             These new parameters are calculated using the current parameterization values saved in cache if present,
224             else it would use default parameterization.
225              
226             my $new_values = $self->compute_parameterization;
227             my $new_params = $new_values->{values};
228             my $new_calibration_error = $new_values->{calibration_error};
229              
230             =cut
231              
232             sub compute_parameterization {
233 1     1 1 2698 my $self = shift;
234              
235 1         2 my $opt_using_params_from_cache;
236 1 50       50 if ($self->parameterization) {
237 1         45 my $params_from_cache = $self->_get_params_from($self->parameterization->{values});
238 1     1   8 $opt_using_params_from_cache = try { $self->_optimize($params_from_cache) };
  1         48  
239             }
240              
241 1         72 my $initial_params = $self->_get_params_from($self->default_initial_guess);
242 1         4 my $opt_using_initial_params = $self->_optimize($initial_params);
243              
244 1         3 my $new_values;
245 1 50       6 if ($opt_using_params_from_cache) {
246             $new_values =
247             ($opt_using_params_from_cache->{calibration_error} < $opt_using_initial_params->{calibration_error})
248 1 50       6 ? $opt_using_params_from_cache
249             : $opt_using_initial_params;
250             } else {
251 0         0 $new_values = $opt_using_initial_params;
252             }
253              
254             # we expect the params to be passed in the correct order
255 1         3 my %calib_params;
256 1         2 @calib_params{@calibration_param_names} = @{$new_values->{params}};
  1         10  
257             my %computed_params = (
258             values => \%calib_params,
259             calibration_error => $new_values->{calibration_error},
260 1         14 date => Date::Utility->new->datetime_iso8601,
261             );
262              
263 1         1137 return \%computed_params;
264             }
265              
266             =head2 get_calibrated_surface
267              
268             compute the calibrated surface with the parameters being passed.
269             my $calibrated = $calibrator->get_calibrated_surface($parameters);
270              
271             =cut
272              
273             sub get_calibrated_surface {
274 1713     1713 1 2357 my $self = shift;
275 1713         2155 my $params = shift;
276 1713         1944 my $surface;
277 1713         2046 foreach my $tenor (@{$self->term_by_day}) {
  1713         75167  
278 11991         14413 foreach my $point (@{$self->smile_points}) {
  11991         502896  
279 251811         540872 $surface->{$tenor}->{$point} = $self->_calculate_calibrated_vol($tenor, $point, $params);
280             }
281             }
282              
283 1713         3540 return $surface;
284             }
285              
286             =head2 _calculate_calibrated_vol
287              
288             The method that calculates calibrated vol using given parameters
289              
290             =cut
291              
292             sub _calculate_calibrated_vol {
293 251811     251811   411955 my ($self, $tenor, $point, $params) = @_;
294              
295 251811 50       496563 return unless defined $point;
296 251811 50       524791 if (not $params) {
297             $params = $self->parameterization->{values}
298 0   0     0 || $self->default_initial_guess;
299             }
300              
301 251811         377874 my $tiy = $tenor / 365;
302 251811         504397 my $kurtosis = $self->_calculate_kurtosis($tiy, $params);
303 251811         543331 my $variance = $self->_calculate_variance($tiy, $params);
304 251811         526902 my $skew = $self->_calculate_skew($tiy, $params);
305              
306 251811         343494 my $atm_vol = sqrt($variance);
307 251811         443738 my $tmp_corr = 6 * (0.25 + $kurtosis * $atm_vol / 4 / $skew**2);
308 251811 50       822199 my $corr = ($tmp_corr < 0) ? -0.99 : (-0.99 * min(1, 1 / sqrt($tmp_corr)));
309 251811         418093 my $volofvol = 2 * $skew / $corr;
310              
311 251811         446538 my $x = log($point / 100) / $atm_vol / sqrt($tiy);
312 251811         10615602 my $x_min = log($self->strike_ratio->{lower}) / $atm_vol / sqrt($tiy);
313 251811         10446317 my $x_max = log($self->strike_ratio->{higher}) / $atm_vol / sqrt($tiy);
314              
315 251811 100       619781 return $atm_vol if abs($x) < 0.00000001;
316              
317 239820 100       786259 $x =
318             ($x > 0)
319             ? $x_max * tanh($x / $x_max)
320             : $x_min * tanh($x / $x_min);
321 239820         4425655 my $z = (-1 * $volofvol * $x);
322 239820 50       525360 return $atm_vol if abs($z) < 0.00000001;
323              
324 239820         596439 my $d = log((sqrt(1 - 2 * $corr * $z + $z**2) - $corr + $z) / (1 - $corr));
325 239820         822766 return $atm_vol * $z / $d;
326             }
327              
328             =head2 _get_params_from
329              
330             calculation metohds which mostly do "mathematical" jobs.
331              
332             =cut
333              
334             sub _get_params_from {
335 2     2   5 my ($self, $param_hash) = @_;
336              
337             my @guess =
338 2         5 map { roundnear(0.0001, $param_hash->{$_}) } @calibration_param_names;
  26         248  
339              
340 2         23 return \@guess;
341             }
342              
343             =head2 _calculate_skew
344              
345             The calibration approach is based upon modeling the term structure of ATM Volatility and ATM Skew using exponential functions.
346              
347             =cut
348              
349             sub _calculate_skew {
350 251811     251811   364472 my ($self, $tiy, $params) = @_;
351              
352 251811         352013 my $sig_small = $params->{skewshort};
353 251811         353579 my $sig_1y = $params->{skew1year};
354 251811         333210 my $sig_inf = $params->{skewlong};
355 251811         301622 my $t_small = 1.0;
356 251811         337874 my $atm_alpha1 = $params->{skewwingL};
357 251811         349320 my $atm_alpha2 = $params->{skewwingR};
358              
359 251811         1021390 my $skew =
360             $sig_inf +
361             ($sig_1y - $sig_inf) *
362             ((exp(-$atm_alpha2 * $tiy) - exp(-$atm_alpha1 * $tiy)) / (exp(-$atm_alpha2 * $t_small) - exp(-$atm_alpha1 * $t_small))) +
363             ($sig_small - $sig_inf) *
364             ((exp(-$atm_alpha2 * $t_small - $atm_alpha1 * $tiy) - exp(-$atm_alpha1 * $t_small - $atm_alpha2 * $tiy)) /
365             (exp(-$atm_alpha2 * $t_small) - exp(-$atm_alpha1 * $t_small)));
366              
367 251811         434336 return $skew;
368             }
369              
370             =head2 _calculate_kurtosis
371              
372             kurtosis can be manipulated using a simple growth rate function.
373             Kurtosis provides a symmetric control over the wings of a
374             surface. Kurtosis basically shifts the wings of the curve in a symetric way.
375              
376             =cut
377              
378             sub _calculate_kurtosis {
379 251811     251811   385189 my ($self, $tiy, $params) = @_;
380              
381 251811         350086 my $kurt_small = $params->{kurtosisshort};
382 251811         347281 my $kurt_alpha = $params->{kurtosisgrowth};
383 251811         345870 my $kurt_inf = $params->{kurtosislong};
384              
385 251811         624457 return $kurt_small + ($kurt_inf - $kurt_small) * (1 - exp(-$kurt_alpha * $tiy));
386             }
387              
388             sub _calculate_variance {
389 264656     264656   377555 my ($self, $tiy, $params) = @_;
390              
391 264656         388961 my $sig_small = $params->{atmvolshort}**2;
392 264656         351044 my $sig_1y = $params->{atmvol1year}**2;
393 264656         374143 my $sig_inf = $params->{atmvolLong}**2;
394 264656         336880 my $t_small = 1.0;
395 264656         394755 my $atm_alpha1 = $params->{atmWingL};
396 264656         357175 my $atm_alpha2 = $params->{atmWingR};
397              
398 264656         1159907 my $atm_vol =
399             $sig_inf +
400             ($sig_1y - $sig_inf) *
401             ((exp(-$atm_alpha2 * $tiy) - exp(-$atm_alpha1 * $tiy)) / (exp(-$atm_alpha2 * $t_small) - exp(-$atm_alpha1 * $t_small))) +
402             ($sig_small - $sig_inf) *
403             ((exp(-$atm_alpha2 * $t_small - $atm_alpha1 * $tiy) - exp(-$atm_alpha1 * $t_small - $atm_alpha2 * $tiy)) /
404             (exp(-$atm_alpha2 * $t_small) - exp(-$atm_alpha1 * $t_small)));
405              
406 264656         456605 return $atm_vol;
407             }
408              
409             =head2 _optimize
410              
411             Algorithm change - now based on centroid calculations
412             A function that optimizes a set of parameters against a function.
413             This optimization method is based on Amoeba optimization. We use a form of the
414             Downhill Simplex Method or Nelder-Mead (available as the R function optim).
415             This can also be coded in other languages.
416              
417             =cut
418              
419             sub _optimize {
420 2     2   5 my ($self, $params) = @_;
421              
422 2         101 my $intol = $self->_tolerance_level;
423 2         4 my $num_of_var = scalar(@$params);
424 2         3 my $num_of_points = $num_of_var;
425              
426 2         3 my $highest_value;
427              
428 2         102 my $lambda1 = ((($num_of_points + 1)**0.5) - 1 + $num_of_points) / ($num_of_points * (2**0.5)) * $self->_scale;
429 2         92 my $lambda2 = ((($num_of_points + 1)**0.5) - 1) / ($num_of_points * (2**0.5)) * $self->_scale;
430              
431 2         4 my $i;
432             my $j;
433              
434             #initialize the simplex with the initial guess
435 2         4 my @simplex = ($params);
436              
437 2         3 my $step = 0;
438 2         7 for ($i = 0; $i < $num_of_var; $i++) {
439 26 100       90 $step = 0.1 * abs($params->[$i])
440             if (0.1 * abs($params->[$i]) > $step);
441             }
442              
443 2         8 for (my $i = 0; $i < $num_of_points; $i++) {
444 26 100       49 push @simplex, [map { $simplex[0][$_] + ($i == $_ ? $step : 0) } (0 .. $num_of_var - 1)];
  338         801  
445             }
446              
447 2         4 my @function_eval = map { $self->function_to_optimize($_) } @simplex;
  28         66  
448 2         6 my $starting_value = $function_eval[0];
449              
450 2         4 my @current_reflection;
451             my @current_expansion;
452 0         0 my @current_contraction;
453              
454 0         0 my $expansion_function_eval;
455 0         0 my $reflected_function_eval;
456 0         0 my $contraction_function_eval;
457 2         3 my $lowest_point_index = 0;
458              
459 2         3 my $calcvert = 1;
460 2         4 my $counter = 0;
461              
462 2         4 while (1) {
463 798 100       1895 if ($calcvert == 1) {
464 9         12 $calcvert = 0;
465 9         29 for ($j = 0; $j <= $num_of_points; $j++) {
466 126 100       279 if ($j != $lowest_point_index) {
467 117         274 $function_eval[$j] = $self->function_to_optimize($simplex[$j]);
468             }
469             }
470             }
471              
472 798         3199 my @sorted_eval = sort { $a <=> $b } @function_eval;
  30252         36720  
473 798     6004   4902 $lowest_point_index = first_index { $_ == $sorted_eval[0] } @function_eval;
  6004         7664  
474 798     5814   4604 my $highest_point_index = first_index { $_ == $sorted_eval[-1] } @function_eval;
  5814         7696  
475              
476 798         3057 my @simplex_centroid = _calculate_simplex_centroid(\@simplex, $num_of_points, $num_of_var, $highest_point_index);
477              
478 798         1624 $highest_value = $function_eval[$highest_point_index];
479              
480 798         1284 my $convtol = $intol * ($starting_value + $intol);
481              
482             last
483 798 100 66     40782 if ($function_eval[$lowest_point_index] < $intol
      100        
484             or ($function_eval[$highest_point_index] < ($function_eval[$lowest_point_index] + $convtol))
485             or $counter > $self->_max_iteration);
486              
487             #imple reflection of the highest point
488             @current_reflection =
489 796         2194 map { 2 * $simplex_centroid[$_] - $simplex[$highest_point_index][$_] } (0 .. $num_of_var - 1);
  10348         20808  
490 796         3061 my $reflected_function_eval = $self->function_to_optimize(\@current_reflection);
491 796 100       2466 if ($reflected_function_eval < $function_eval[$lowest_point_index]) {
492              
493             #Do simple expansion or in other words look up a little further in this direction
494 134         424 @current_expansion = map { 2 * $current_reflection[$_] - $simplex_centroid[$_] } (0 .. $num_of_var - 1);
  1742         3562  
495              
496 134         508 $expansion_function_eval = $self->function_to_optimize(\@current_expansion);
497 134 100       455 if ($expansion_function_eval < $reflected_function_eval) {
498              
499             #replace highest point with expansion point
500             #assing function value to highest point
501 81         140 @{$simplex[$highest_point_index]} = @current_expansion;
  81         404  
502 81         203 $function_eval[$highest_point_index] = $expansion_function_eval;
503             } else {
504              
505             #replace highest point with reflected point
506             #assign highest value to reflection point
507 53         101 @{$simplex[$highest_point_index]} = @current_reflection;
  53         266  
508 53         126 $function_eval[$highest_point_index] = $reflected_function_eval;
509             }
510              
511             } else {
512 662 100       1687 if ($reflected_function_eval < $highest_value) {
513              
514             #replace the simplex highest point with reflected point;
515 558         847 @{$simplex[$highest_point_index]} = @current_reflection;
  558         2408  
516 558         1189 $function_eval[$highest_point_index] = $reflected_function_eval;
517             }
518              
519             @current_contraction =
520 662         1719 map { ($simplex_centroid[$_] + $simplex[$highest_point_index][$_]) / 2 } (0 .. $num_of_var - 1);
  8606         17619  
521 662         2458 $contraction_function_eval = $self->function_to_optimize(\@current_contraction);
522 662 100       2406 if ($contraction_function_eval < $function_eval[$highest_point_index]) {
    100          
523 379         595 @{$simplex[$highest_point_index]} = @current_contraction;
  379         1638  
524 379         849 $function_eval[$highest_point_index] = $contraction_function_eval;
525             } elsif ($reflected_function_eval >= $highest_value) {
526 7         11 $calcvert = 1;
527 7         19 for ($i = 0; $i <= $num_of_points; $i++) {
528 98 100       235 if ($i != $lowest_point_index) {
529 91         379 @{$simplex[$i]} =
530 91         192 map { ($simplex[$lowest_point_index][$_] + $simplex[$i][$_]) / 2.0 } (0 .. $num_of_var - 1);
  1183         2081  
531             }
532 98         333 $function_eval[$highest_point_index] = $self->function_to_optimize($simplex[$highest_point_index]);
533             }
534             }
535             }
536 796         2870 $counter++;
537             }
538              
539 2         5 my $min_counter = 0;
540 2         5 my $lowest_point_value = $function_eval[0];
541 2         4 $lowest_point_index = 0;
542              
543 2         5 for my $eval_item (@function_eval) {
544 28 100       52 if ($eval_item < $lowest_point_value) {
545 4         6 $lowest_point_index = $min_counter;
546 4         7 $lowest_point_value = $eval_item;
547             }
548              
549 28         39 $min_counter++;
550             }
551              
552 2         4 my $new_params = $simplex[$lowest_point_index];
553 2         5 my $new_calibration_error = $function_eval[$lowest_point_index] * 1000;
554              
555             return {
556 2         35 params => $new_params,
557             calibration_error => $new_calibration_error,
558             };
559             }
560              
561             sub _calculate_simplex_centroid {
562 798     798   1450 my ($simplex, $num_of_points, $num_of_var, $highest_point_index) = @_;
563              
564 798         921 my @simplex_sum;
565              
566 798         2106 for (my $j = 0; $j < $num_of_var; $j++) {
567 10374         12213 my $sum = 0;
568 10374         21290 for (my $i = 0; $i <= $num_of_points; $i++) {
569 145236 100       436433 $sum += $simplex->[$i]->[$j] if ($i != $highest_point_index);
570             }
571              
572             #Centroied instead of sum for easy of calculation
573 10374         25767 $simplex_sum[$j] = $sum / $num_of_var;
574             }
575 798         3341 return @simplex_sum;
576             }
577              
578             1;
579              
580             =head1 AUTHOR
581              
582             Binary.com, C<< <support at binary.com> >>
583              
584             =head1 BUGS
585              
586             Please report any bugs or feature requests to C<bug-volsurface-calibration-equities at rt.cpan.org>, or through
587             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=VolSurface-Calibration-Equities>. I will be notified, and then you'll
588             automatically be notified of progress on your bug as I make changes.
589              
590             =head1 SUPPORT
591              
592             You can find documentation for this module with the perldoc command.
593             perldoc VolSurface::Calibration::Equities
594             You can also look for information at:
595              
596             =over 4
597              
598             =item * RT: CPAN's request tracker (report bugs here)
599             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=VolSurface-Calibration-Equities>
600              
601             =item * AnnoCPAN: Annotated CPAN documentation
602              
603             L<http://annocpan.org/dist/VolSurface-Calibration-Equities>
604              
605             =item * CPAN Ratings
606              
607             L<http://cpanratings.perl.org/d/VolSurface-Calibration-Equities>
608              
609             =item * Search CPAN
610              
611             L<http://search.cpan.org/dist/VolSurface-Calibration-SAB/>
612              
613             =back
614              
615             =head1 LICENSE AND COPYRIGHT
616              
617             Copyright 2015 Binary.com.
618             This program is free software; you can redistribute it and/or modify it
619             under the terms of the the Artistic License (2.0). You may obtain a
620             copy of the full license at:
621             L<http://www.perlfoundation.org/artistic_license_2_0>
622             Any use, modification, and distribution of the Standard or Modified
623             Versions is governed by this Artistic License. By using, modifying or
624             distributing the Package, you accept this license. Do not use, modify,
625             or distribute the Package, if you do not accept this license.
626             If your Modified Version has been derived from a Modified Version made
627             by someone other than you, you are nevertheless required to ensure that
628             your Modified Version complies with the requirements of this license.
629             This license does not grant you the right to use any trademark, service
630             mark, tradename, or logo of the Copyright Holder.
631             This license includes the non-exclusive, worldwide, free-of-charge
632             patent license to make, have made, use, offer to sell, sell, import and
633             otherwise transfer the Package with respect to any patent claims
634             licensable by the Copyright Holder that are necessarily infringed by the
635             Package. If you institute patent litigation (including a cross-claim or
636             counterclaim) against any party alleging that the Package constitutes
637             direct or contributory patent infringement, then this Artistic License
638             to you shall terminate on the date that such litigation is filed.
639             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
640             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
641             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
642             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
643             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
644             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
645             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
646             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
647              
648             =cut