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   60141 use strict;
  2         4  
  2         50  
4 2     2   10 use warnings;
  2         4  
  2         1137  
5              
6             our $VERSION = '0.02';
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.02
22              
23             =cut
24              
25 2     2   1724 use Moose;
  2         974349  
  2         11  
26              
27 2     2   16128 use Date::Utility;
  2         740238  
  2         114  
28 2     2   20 use List::MoreUtils qw(first_index);
  2         4  
  2         22  
29 2     2   912 use List::Util qw(min max);
  2         3  
  2         200  
30 2     2   3106 use Math::Trig qw(tanh);
  2         37323  
  2         179  
31 2     2   28 use Try::Tiny;
  2         4  
  2         118  
32 2     2   1879 use Format::Util::Numbers qw(roundnear);
  2         2311  
  2         5765  
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 3127 my ($self, $params) = @_;
125              
126             # this number is returned when the optimization is heading the wrong path
127 1835         2498 my $error = 1000;
128              
129 1835         2643 my %params;
130 1835         13042 @params{@calibration_param_names} = @$params;
131              
132 1835         3435 my @tenors = @{$self->term_by_day};
  1835         80890  
133             my @variance =
134 1835         3500 map { $self->_calculate_variance($_ / 365, \%params) } @tenors;
  12845         30212  
135              
136 1835         4065 foreach my $param_name (@calibration_param_names) {
137 23503         38032 my $param_value = $params{$param_name};
138 23503 100 100     89653 return $error
139             if ($param_name =~ /wing(R|L)/i && abs($param_value) > 20.0);
140 23465 100 100     71486 return $error
141             if ($param_name =~ /growth/i && abs($param_value) > 5.0);
142 23457 100 100     158196 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       7051 return $error if (min(@variance) < 0);
149              
150 1713         2971 my @atmvols = map { sqrt($_) } @variance;
  11991         19012  
151 1713         4711 my $calibrated_surface = $self->get_calibrated_surface(\%params);
152 1713         72395 my $actual_surface = $self->surface;
153              
154 1713         2808 my $total = 0;
155 1713         2240 foreach my $day (@{$self->term_by_day}) {
  1713         72114  
156 11991         15924 my $sum = 0;
157 11991         14298 my $tenorvega = 0;
158 11991         18539 my $atm_vol = shift @atmvols;
159 11991         18526 foreach my $point (@{$self->smile_points}) {
  11991         510679  
160 251811         430941 my $atm_check = log($point / 100) / $atm_vol / sqrt($day / 365);
161 251811         414847 my $calibrated_vol = $calibrated_surface->{$day}->{$point};
162 251811         427857 my $actual_vol = $actual_surface->{$day}->{smile}->{$point};
163 251811         599444 my $d1 = (log(100 / $point) + (0.5 * $actual_vol**2) * ($day / 365)) / ($actual_vol * ($day / 365)**0.5);
164 251811         421975 my $nd1 = (1 / (2 * 3.1416)**0.5) * exp(-$d1 * $d1 / 2);
165 251811         413092 my $vega = $nd1 * ($day / 365)**0.5;
166 251811         352530 $sum += $vega * abs($calibrated_vol - $actual_vol);
167 251811         421140 $tenorvega += $vega;
168             }
169 11991         22927 $total += $sum / $tenorvega;
170             }
171              
172 1713         30643 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 1804 my $self = shift;
234              
235 1         2 my $opt_using_params_from_cache;
236 1 50       51 if ($self->parameterization) {
237 1         47 my $params_from_cache = $self->_get_params_from($self->parameterization->{values});
238 1     1   9 $opt_using_params_from_cache = try { $self->_optimize($params_from_cache) };
  1         52  
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         2 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         15 date => Date::Utility->new->datetime_iso8601,
261             );
262              
263 1         1213 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 2460 my $self = shift;
275 1713         2249 my $params = shift;
276 1713         2463 my $surface;
277 1713         2164 foreach my $tenor (@{$self->term_by_day}) {
  1713         75306  
278 11991         16064 foreach my $point (@{$self->smile_points}) {
  11991         507783  
279 251811         539529 $surface->{$tenor}->{$point} = $self->_calculate_calibrated_vol($tenor, $point, $params);
280             }
281             }
282              
283 1713         4034 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   423843 my ($self, $tenor, $point, $params) = @_;
294              
295 251811 50       579279 return unless defined $point;
296 251811 50       543709 if (not $params) {
297             $params = $self->parameterization->{values}
298 0   0     0 || $self->default_initial_guess;
299             }
300              
301 251811         373561 my $tiy = $tenor / 365;
302 251811         551191 my $kurtosis = $self->_calculate_kurtosis($tiy, $params);
303 251811         522612 my $variance = $self->_calculate_variance($tiy, $params);
304 251811         516845 my $skew = $self->_calculate_skew($tiy, $params);
305              
306 251811         387745 my $atm_vol = sqrt($variance);
307 251811         480462 my $tmp_corr = 6 * (0.25 + $kurtosis * $atm_vol / 4 / $skew**2);
308 251811 50       752973 my $corr = ($tmp_corr < 0) ? -0.99 : (-0.99 * min(1, 1 / sqrt($tmp_corr)));
309 251811         391192 my $volofvol = 2 * $skew / $corr;
310              
311 251811         442238 my $x = log($point / 100) / $atm_vol / sqrt($tiy);
312 251811         10672229 my $x_min = log($self->strike_ratio->{lower}) / $atm_vol / sqrt($tiy);
313 251811         10529899 my $x_max = log($self->strike_ratio->{higher}) / $atm_vol / sqrt($tiy);
314              
315 251811 100       708289 return $atm_vol if abs($x) < 0.00000001;
316              
317 239820 100       787145 $x =
318             ($x > 0)
319             ? $x_max * tanh($x / $x_max)
320             : $x_min * tanh($x / $x_min);
321 239820         4483109 my $z = (-1 * $volofvol * $x);
322 239820 50       620861 return $atm_vol if abs($z) < 0.00000001;
323              
324 239820         601086 my $d = log((sqrt(1 - 2 * $corr * $z + $z**2) - $corr + $z) / (1 - $corr));
325 239820         818400 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         6 map { roundnear(0.0001, $param_hash->{$_}) } @calibration_param_names;
  26         256  
339              
340 2         22 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   365829 my ($self, $tiy, $params) = @_;
351              
352 251811         347259 my $sig_small = $params->{skewshort};
353 251811         339476 my $sig_1y = $params->{skew1year};
354 251811         337377 my $sig_inf = $params->{skewlong};
355 251811         310091 my $t_small = 1.0;
356 251811         340164 my $atm_alpha1 = $params->{skewwingL};
357 251811         339519 my $atm_alpha2 = $params->{skewwingR};
358              
359 251811         1044934 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         436684 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   433772 my ($self, $tiy, $params) = @_;
380              
381 251811         371484 my $kurt_small = $params->{kurtosisshort};
382 251811         350423 my $kurt_alpha = $params->{kurtosisgrowth};
383 251811         331716 my $kurt_inf = $params->{kurtosislong};
384              
385 251811         630250 return $kurt_small + ($kurt_inf - $kurt_small) * (1 - exp(-$kurt_alpha * $tiy));
386             }
387              
388             sub _calculate_variance {
389 264656     264656   390120 my ($self, $tiy, $params) = @_;
390              
391 264656         436067 my $sig_small = $params->{atmvolshort}**2;
392 264656         391734 my $sig_1y = $params->{atmvol1year}**2;
393 264656         386221 my $sig_inf = $params->{atmvolLong}**2;
394 264656         337877 my $t_small = 1.0;
395 264656         365580 my $atm_alpha1 = $params->{atmWingL};
396 264656         349975 my $atm_alpha2 = $params->{atmWingR};
397              
398 264656         1104782 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         485177 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   4 my ($self, $params) = @_;
421              
422 2         112 my $intol = $self->_tolerance_level;
423 2         9 my $num_of_var = scalar(@$params);
424 2         4 my $num_of_points = $num_of_var;
425              
426 2         3 my $highest_value;
427              
428 2         104 my $lambda1 = ((($num_of_points + 1)**0.5) - 1 + $num_of_points) / ($num_of_points * (2**0.5)) * $self->_scale;
429 2         86 my $lambda2 = ((($num_of_points + 1)**0.5) - 1) / ($num_of_points * (2**0.5)) * $self->_scale;
430              
431 2         3 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         9 for ($i = 0; $i < $num_of_var; $i++) {
439 26 100       124 $step = 0.1 * abs($params->[$i])
440             if (0.1 * abs($params->[$i]) > $step);
441             }
442              
443 2         52 for (my $i = 0; $i < $num_of_points; $i++) {
444 26 100       47 push @simplex, [map { $simplex[0][$_] + ($i == $_ ? $step : 0) } (0 .. $num_of_var - 1)];
  338         795  
445             }
446              
447 2         4 my @function_eval = map { $self->function_to_optimize($_) } @simplex;
  28         73  
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         4 my $lowest_point_index = 0;
458              
459 2         3 my $calcvert = 1;
460 2         4 my $counter = 0;
461              
462 2         3 while (1) {
463 798 100       2178 if ($calcvert == 1) {
464 9         15 $calcvert = 0;
465 9         27 for ($j = 0; $j <= $num_of_points; $j++) {
466 126 100       319 if ($j != $lowest_point_index) {
467 117         293 $function_eval[$j] = $self->function_to_optimize($simplex[$j]);
468             }
469             }
470             }
471              
472 798         3462 my @sorted_eval = sort { $a <=> $b } @function_eval;
  30252         36467  
473 798     6004   5108 $lowest_point_index = first_index { $_ == $sorted_eval[0] } @function_eval;
  6004         7862  
474 798     5814   4643 my $highest_point_index = first_index { $_ == $sorted_eval[-1] } @function_eval;
  5814         7308  
475              
476 798         2972 my @simplex_centroid = _calculate_simplex_centroid(\@simplex, $num_of_points, $num_of_var, $highest_point_index);
477              
478 798         1895 $highest_value = $function_eval[$highest_point_index];
479              
480 798         1342 my $convtol = $intol * ($starting_value + $intol);
481              
482             last
483 798 100 66     41029 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         2273 map { 2 * $simplex_centroid[$_] - $simplex[$highest_point_index][$_] } (0 .. $num_of_var - 1);
  10348         19511  
490 796         2852 my $reflected_function_eval = $self->function_to_optimize(\@current_reflection);
491 796 100       2404 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         3477  
495              
496 134         528 $expansion_function_eval = $self->function_to_optimize(\@current_expansion);
497 134 100       437 if ($expansion_function_eval < $reflected_function_eval) {
498              
499             #replace highest point with expansion point
500             #assing function value to highest point
501 81         184 @{$simplex[$highest_point_index]} = @current_expansion;
  81         416  
502 81         225 $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         98 @{$simplex[$highest_point_index]} = @current_reflection;
  53         262  
508 53         137 $function_eval[$highest_point_index] = $reflected_function_eval;
509             }
510              
511             } else {
512 662 100       1797 if ($reflected_function_eval < $highest_value) {
513              
514             #replace the simplex highest point with reflected point;
515 558         866 @{$simplex[$highest_point_index]} = @current_reflection;
  558         2609  
516 558         1220 $function_eval[$highest_point_index] = $reflected_function_eval;
517             }
518              
519             @current_contraction =
520 662         1820 map { ($simplex_centroid[$_] + $simplex[$highest_point_index][$_]) / 2 } (0 .. $num_of_var - 1);
  8606         17037  
521 662         2486 $contraction_function_eval = $self->function_to_optimize(\@current_contraction);
522 662 100       2557 if ($contraction_function_eval < $function_eval[$highest_point_index]) {
    100          
523 379         590 @{$simplex[$highest_point_index]} = @current_contraction;
  379         1669  
524 379         853 $function_eval[$highest_point_index] = $contraction_function_eval;
525             } elsif ($reflected_function_eval >= $highest_value) {
526 7         12 $calcvert = 1;
527 7         27 for ($i = 0; $i <= $num_of_points; $i++) {
528 98 100       230 if ($i != $lowest_point_index) {
529 91         399 @{$simplex[$i]} =
530 91         245 map { ($simplex[$lowest_point_index][$_] + $simplex[$i][$_]) / 2.0 } (0 .. $num_of_var - 1);
  1183         1961  
531             }
532 98         330 $function_eval[$highest_point_index] = $self->function_to_optimize($simplex[$highest_point_index]);
533             }
534             }
535             }
536 796         2874 $counter++;
537             }
538              
539 2         6 my $min_counter = 0;
540 2         6 my $lowest_point_value = $function_eval[0];
541 2         5 $lowest_point_index = 0;
542              
543 2         6 for my $eval_item (@function_eval) {
544 28 100       64 if ($eval_item < $lowest_point_value) {
545 4         5 $lowest_point_index = $min_counter;
546 4         5 $lowest_point_value = $eval_item;
547             }
548              
549 28         39 $min_counter++;
550             }
551              
552 2         5 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         31 params => $new_params,
557             calibration_error => $new_calibration_error,
558             };
559             }
560              
561             sub _calculate_simplex_centroid {
562 798     798   1675 my ($simplex, $num_of_points, $num_of_var, $highest_point_index) = @_;
563              
564 798         1069 my @simplex_sum;
565              
566 798         2502 for (my $j = 0; $j < $num_of_var; $j++) {
567 10374         13357 my $sum = 0;
568 10374         24681 for (my $i = 0; $i <= $num_of_points; $i++) {
569 145236 100       515670 $sum += $simplex->[$i]->[$j] if ($i != $highest_point_index);
570             }
571              
572             #Centroied instead of sum for easy of calculation
573 10374         28479 $simplex_sum[$j] = $sum / $num_of_var;
574             }
575 798         3432 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