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   62317 use strict;
  2         5  
  2         49  
4 2     2   10 use warnings;
  2         4  
  2         1121  
5              
6             our $VERSION = '0.01';
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.01
22              
23             =cut
24              
25 2     2   1707 use Moose;
  2         966829  
  2         13  
26              
27 2     2   16156 use Date::Utility;
  2         735003  
  2         104  
28 2     2   18 use List::MoreUtils qw(first_index);
  2         4  
  2         23  
29 2     2   926 use List::Util qw(min max);
  2         4  
  2         161  
30 2     2   3722 use Math::Trig qw(tanh);
  2         37007  
  2         178  
31 2     2   20 use Try::Tiny;
  2         5  
  2         112  
32 2     2   1764 use Format::Util::Numbers qw(roundnear);
  2         2161  
  2         5820  
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             my @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 3027 my ($self, $params) = @_;
125              
126             # this number is returned when the optimization is heading the wrong path
127 1835         2270 my $error = 1000;
128              
129 1835         2296 my %params;
130 1835         13278 @params{@calibration_param_names} = @$params;
131              
132 1835         3308 my @tenors = @{$self->term_by_day};
  1835         80930  
133             my @variance =
134 1835         3719 map { $self->_calculate_variance($_ / 365, \%params) } @tenors;
  12845         28087  
135              
136 1835         3794 foreach my $param_name (@calibration_param_names) {
137 23503         35899 my $param_value = $params{$param_name};
138 23503 100 100     82920 return $error
139             if ($param_name =~ /wing(R|L)/i && abs($param_value) > 20.0);
140 23465 100 100     65068 return $error
141             if ($param_name =~ /growth/i && abs($param_value) > 5.0);
142 23457 100 100     154617 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       6564 return $error if (min(@variance) < 0);
149              
150 1713         2602 my @atmvols = map { sqrt($_) } @variance;
  11991         18107  
151 1713         4321 my $calibrated_surface = $self->get_calibrated_surface(\%params);
152 1713         73329 my $actual_surface = $self->surface;
153              
154 1713         2688 my $total = 0;
155 1713         2689 foreach my $day (@{$self->term_by_day}) {
  1713         71970  
156 11991         14133 my $sum = 0;
157 11991         13078 my $tenorvega = 0;
158 11991         16407 my $atm_vol = shift @atmvols;
159 11991         15230 foreach my $point (@{$self->smile_points}) {
  11991         513368  
160 251811         415116 my $atm_check = log($point / 100) / $atm_vol / sqrt($day / 365);
161 251811         407027 my $calibrated_vol = $calibrated_surface->{$day}->{$point};
162 251811         407977 my $actual_vol = $actual_surface->{$day}->{smile}->{$point};
163 251811         576331 my $d1 = (log(100 / $point) + (0.5 * $actual_vol**2) * ($day / 365)) / ($actual_vol * ($day / 365)**0.5);
164 251811         396150 my $nd1 = (1 / (2 * 3.1416)**0.5) * exp(-$d1 * $d1 / 2);
165 251811         377825 my $vega = $nd1 * ($day / 365)**0.5;
166 251811         325336 $sum += $vega * abs($calibrated_vol - $actual_vol);
167 251811         393400 $tenorvega += $vega;
168             }
169 11991         22100 $total += $sum / $tenorvega;
170             }
171              
172 1713         30641 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 1529 my $self = shift;
234              
235 1         2 my $opt_using_params_from_cache;
236 1 50       49 if ($self->parameterization) {
237 1         43 my $params_from_cache = $self->_get_params_from($self->parameterization->{values});
238 1     1   10 $opt_using_params_from_cache = try { $self->_optimize($params_from_cache) };
  1         56  
239             }
240              
241 1         70 my $initial_params = $self->_get_params_from($self->default_initial_guess);
242 1         5 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         2 my %calib_params;
256 1         3 @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         16 date => Date::Utility->new->datetime_iso8601,
261             );
262              
263 1         1147 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 2448 my $self = shift;
275 1713         2119 my $params = shift;
276 1713         1937 my $surface;
277 1713         1878 foreach my $tenor (@{$self->term_by_day}) {
  1713         76142  
278 11991         14943 foreach my $point (@{$self->smile_points}) {
  11991         513390  
279 251811         546256 $surface->{$tenor}->{$point} = $self->_calculate_calibrated_vol($tenor, $point, $params);
280             }
281             }
282              
283 1713         4007 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   397778 my ($self, $tenor, $point, $params) = @_;
294              
295 251811 50       474303 return unless defined $point;
296 251811 50       475969 if (not $params) {
297             $params = $self->parameterization->{values}
298 0   0     0 || $self->default_initial_guess;
299             }
300              
301 251811         336394 my $tiy = $tenor / 365;
302 251811         496753 my $kurtosis = $self->_calculate_kurtosis($tiy, $params);
303 251811         503063 my $variance = $self->_calculate_variance($tiy, $params);
304 251811         536340 my $skew = $self->_calculate_skew($tiy, $params);
305              
306 251811         328601 my $atm_vol = sqrt($variance);
307 251811         454057 my $tmp_corr = 6 * (0.25 + $kurtosis * $atm_vol / 4 / $skew**2);
308 251811 50       771306 my $corr = ($tmp_corr < 0) ? -0.99 : (-0.99 * min(1, 1 / sqrt($tmp_corr)));
309 251811         367746 my $volofvol = 2 * $skew / $corr;
310              
311 251811         425326 my $x = log($point / 100) / $atm_vol / sqrt($tiy);
312 251811         10890786 my $x_min = log($self->strike_ratio->{lower}) / $atm_vol / sqrt($tiy);
313 251811         10632509 my $x_max = log($self->strike_ratio->{higher}) / $atm_vol / sqrt($tiy);
314              
315 251811 100       630394 return $atm_vol if abs($x) < 0.00000001;
316              
317 239820 100       768977 $x =
318             ($x > 0)
319             ? $x_max * tanh($x / $x_max)
320             : $x_min * tanh($x / $x_min);
321 239820         4455010 my $z = (-1 * $volofvol * $x);
322 239820 50       484224 return $atm_vol if abs($z) < 0.00000001;
323              
324 239820         627304 my $d = log((sqrt(1 - 2 * $corr * $z + $z**2) - $corr + $z) / (1 - $corr));
325 239820         854843 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   4 my ($self, $param_hash) = @_;
336              
337             my @guess =
338 2         5 map { roundnear(0.0001, $param_hash->{$_}) } @calibration_param_names;
  26         255  
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   396203 my ($self, $tiy, $params) = @_;
351              
352 251811         338151 my $sig_small = $params->{skewshort};
353 251811         325217 my $sig_1y = $params->{skew1year};
354 251811         321795 my $sig_inf = $params->{skewlong};
355 251811         276540 my $t_small = 1.0;
356 251811         340714 my $atm_alpha1 = $params->{skewwingL};
357 251811         322152 my $atm_alpha2 = $params->{skewwingR};
358              
359 251811         1100098 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         463937 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   347368 my ($self, $tiy, $params) = @_;
380              
381 251811         341314 my $kurt_small = $params->{kurtosisshort};
382 251811         308993 my $kurt_alpha = $params->{kurtosisgrowth};
383 251811         307336 my $kurt_inf = $params->{kurtosislong};
384              
385 251811         635818 return $kurt_small + ($kurt_inf - $kurt_small) * (1 - exp(-$kurt_alpha * $tiy));
386             }
387              
388             sub _calculate_variance {
389 264656     264656   373985 my ($self, $tiy, $params) = @_;
390              
391 264656         398270 my $sig_small = $params->{atmvolshort}**2;
392 264656         358864 my $sig_1y = $params->{atmvol1year}**2;
393 264656         380341 my $sig_inf = $params->{atmvolLong}**2;
394 264656         307830 my $t_small = 1.0;
395 264656         336625 my $atm_alpha1 = $params->{atmWingL};
396 264656         345517 my $atm_alpha2 = $params->{atmWingR};
397              
398 264656         1129357 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         464481 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   6 my ($self, $params) = @_;
421              
422 2         99 my $intol = $self->_tolerance_level;
423 2         3 my $num_of_var = scalar(@$params);
424 2         5 my $num_of_points = $num_of_var;
425              
426 2         3 my $highest_value;
427              
428 2         109 my $lambda1 = ((($num_of_points + 1)**0.5) - 1 + $num_of_points) / ($num_of_points * (2**0.5)) * $self->_scale;
429 2         95 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         5 my @simplex = ($params);
436              
437 2         4 my $step = 0;
438 2         7 for ($i = 0; $i < $num_of_var; $i++) {
439 26 100       88 $step = 0.1 * abs($params->[$i])
440             if (0.1 * abs($params->[$i]) > $step);
441             }
442              
443 2         7 for (my $i = 0; $i < $num_of_points; $i++) {
444 26 100       48 push @simplex, [map { $simplex[0][$_] + ($i == $_ ? $step : 0) } (0 .. $num_of_var - 1)];
  338         734  
445             }
446              
447 2         4 my @function_eval = map { $self->function_to_optimize($_) } @simplex;
  28         64  
448 2         5 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         4 my $lowest_point_index = 0;
458              
459 2         3 my $calcvert = 1;
460 2         5 my $counter = 0;
461              
462 2         3 while (1) {
463 798 100       2100 if ($calcvert == 1) {
464 9         13 $calcvert = 0;
465 9         26 for ($j = 0; $j <= $num_of_points; $j++) {
466 126 100       304 if ($j != $lowest_point_index) {
467 117         352 $function_eval[$j] = $self->function_to_optimize($simplex[$j]);
468             }
469             }
470             }
471              
472 798         2920 my @sorted_eval = sort { $a <=> $b } @function_eval;
  30252         40397  
473 798     6004   5375 $lowest_point_index = first_index { $_ == $sorted_eval[0] } @function_eval;
  6004         8244  
474 798     5814   4603 my $highest_point_index = first_index { $_ == $sorted_eval[-1] } @function_eval;
  5814         7351  
475              
476 798         3126 my @simplex_centroid = _calculate_simplex_centroid(\@simplex, $num_of_points, $num_of_var, $highest_point_index);
477              
478 798         1838 $highest_value = $function_eval[$highest_point_index];
479              
480 798         1452 my $convtol = $intol * ($starting_value + $intol);
481              
482             last
483 798 100 66     42017 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         2303 map { 2 * $simplex_centroid[$_] - $simplex[$highest_point_index][$_] } (0 .. $num_of_var - 1);
  10348         20553  
490 796         2863 my $reflected_function_eval = $self->function_to_optimize(\@current_reflection);
491 796 100       2310 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         439 @current_expansion = map { 2 * $current_reflection[$_] - $simplex_centroid[$_] } (0 .. $num_of_var - 1);
  1742         3474  
495              
496 134         548 $expansion_function_eval = $self->function_to_optimize(\@current_expansion);
497 134 100       434 if ($expansion_function_eval < $reflected_function_eval) {
498              
499             #replace highest point with expansion point
500             #assing function value to highest point
501 81         146 @{$simplex[$highest_point_index]} = @current_expansion;
  81         428  
502 81         214 $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         96 @{$simplex[$highest_point_index]} = @current_reflection;
  53         291  
508 53         137 $function_eval[$highest_point_index] = $reflected_function_eval;
509             }
510              
511             } else {
512 662 100       1571 if ($reflected_function_eval < $highest_value) {
513              
514             #replace the simplex highest point with reflected point;
515 558         803 @{$simplex[$highest_point_index]} = @current_reflection;
  558         2695  
516 558         1330 $function_eval[$highest_point_index] = $reflected_function_eval;
517             }
518              
519             @current_contraction =
520 662         1837 map { ($simplex_centroid[$_] + $simplex[$highest_point_index][$_]) / 2 } (0 .. $num_of_var - 1);
  8606         17675  
521 662         2405 $contraction_function_eval = $self->function_to_optimize(\@current_contraction);
522 662 100       2419 if ($contraction_function_eval < $function_eval[$highest_point_index]) {
    100          
523 379         591 @{$simplex[$highest_point_index]} = @current_contraction;
  379         1820  
524 379         970 $function_eval[$highest_point_index] = $contraction_function_eval;
525             } elsif ($reflected_function_eval >= $highest_value) {
526 7         8 $calcvert = 1;
527 7         20 for ($i = 0; $i <= $num_of_points; $i++) {
528 98 100       223 if ($i != $lowest_point_index) {
529 91         355 @{$simplex[$i]} =
530 91         193 map { ($simplex[$lowest_point_index][$_] + $simplex[$i][$_]) / 2.0 } (0 .. $num_of_var - 1);
  1183         1961  
531             }
532 98         327 $function_eval[$highest_point_index] = $self->function_to_optimize($simplex[$highest_point_index]);
533             }
534             }
535             }
536 796         2819 $counter++;
537             }
538              
539 2         6 my $min_counter = 0;
540 2         5 my $lowest_point_value = $function_eval[0];
541 2         5 $lowest_point_index = 0;
542              
543 2         5 for my $eval_item (@function_eval) {
544 28 100       56 if ($eval_item < $lowest_point_value) {
545 4         4 $lowest_point_index = $min_counter;
546 4         8 $lowest_point_value = $eval_item;
547             }
548              
549 28         38 $min_counter++;
550             }
551              
552 2         4 my $new_params = $simplex[$lowest_point_index];
553 2         4 my $new_calibration_error = $function_eval[$lowest_point_index] * 1000;
554              
555             return {
556 2         32 params => $new_params,
557             calibration_error => $new_calibration_error,
558             };
559             }
560              
561             sub _calculate_simplex_centroid {
562 798     798   1630 my ($simplex, $num_of_points, $num_of_var, $highest_point_index) = @_;
563              
564 798         954 my @simplex_sum;
565              
566 798         2444 for (my $j = 0; $j < $num_of_var; $j++) {
567 10374         11406 my $sum = 0;
568 10374         20839 for (my $i = 0; $i <= $num_of_points; $i++) {
569 145236 100       447166 $sum += $simplex->[$i]->[$j] if ($i != $highest_point_index);
570             }
571              
572             #Centroied instead of sum for easy of calculation
573 10374         26963 $simplex_sum[$j] = $sum / $num_of_var;
574             }
575 798         3465 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