File Coverage

blib/lib/Performance/Probability.pm
Criterion Covered Total %
statement 142 150 94.6
branch 27 36 75.0
condition 14 21 66.6
subroutine 16 16 100.0
pod 3 3 100.0
total 202 226 89.3


line stmt bran cond sub pod time code
1             package Performance::Probability;
2              
3 1     1   125198 use 5.010;
  1         17  
4 1     1   5 use strict;
  1         2  
  1         19  
5 1     1   5 use warnings;
  1         1  
  1         22  
6              
7 1     1   417 use Math::BivariateCDF;
  1         528  
  1         42  
8 1     1   407 use Math::Gauss::XS;
  1         440  
  1         41  
9 1     1   461 use Machine::Epsilon;
  1         354  
  1         45  
10              
11 1     1   7 use Exporter;
  1         2  
  1         1549  
12              
13             our @ISA = qw(Exporter);
14              
15             our @EXPORT_OK = qw(get_performance_probability);
16              
17             our $VERSION = '0.06';
18              
19             =head1 NAME
20              
21             Performance::Probability - The performance probability is a likelihood measure of a client reaching his/her current profit and loss.
22              
23             =head1 SYNOPSYS
24              
25             use Performance::Probability qw(get_performance_probability);
26              
27             my $probability = Performance::Probability::get_performance_probability(
28             types => [qw/CALL PUT/],
29             payout => [100, 100],
30             bought_price => [75, 55],
31             pnl => 1000.0,
32             underlying => [qw/EURUSD EURUSD/],
33             start_time => [1461847439, 1461930839], #time in epoch
34             sell_time => [1461924960, 1461931561], #time in epoch
35             );
36              
37             =head1 DESCRIPTION
38              
39             The performance probability is a likelihood measure of a client reaching his/her current profit and loss.
40              
41             =cut
42              
43             #Profit in case of winning. ( Payout minus bought price ).
44             sub _build_wk {
45              
46 1     1   1 my $bought_price = shift;
47 1         2 my $payout = shift;
48              
49 1         2 my @w_k;
50              
51             my $i;
52              
53 1         3 for ($i = 0; $i < @{$payout}; ++$i) {
  24         43  
54 23         35 my $tmp_w_k = $payout->[$i] - $bought_price->[$i];
55 23         42 push @w_k, $tmp_w_k;
56             }
57              
58 1         3 return \@w_k;
59             }
60              
61             #Loss in case of losing. (Minus bought price).
62             sub _build_lk {
63              
64 1     1   3 my $bought_price = shift;
65 1         2 my @l_k;
66              
67             my $i;
68              
69 1         3 for ($i = 0; $i < @{$bought_price}; ++$i) {
  24         45  
70 23         35 push @l_k, 0 - $bought_price->[$i];
71             }
72              
73 1         3 return \@l_k;
74             }
75              
76             #Winning probability. ( Bought price / Payout ).
77             sub _build_pk {
78              
79 1     1   3 my $bought_price = shift;
80 1         3 my $payout = shift;
81              
82 1         2 my @p_k;
83              
84             my $i;
85              
86 1         3 for ($i = 0; $i < @{$bought_price}; ++$i) {
  24         49  
87 23         57 my $tmp_pk = $bought_price->[$i] / $payout->[$i];
88 23         35 push @p_k, $tmp_pk;
89             }
90              
91 1         3 return \@p_k;
92             }
93              
94             #Sigma( profit * winning probability + loss * losing probability ).
95             sub _mean {
96              
97 1     1   2 my $pk = shift;
98 1         2 my $lk = shift;
99 1         2 my $wk = shift;
100              
101 1         1 my $i;
102 1         4 my $sum = 0;
103              
104 1         14 for ($i = 0; $i < @{$wk}; ++$i) {
  24         45  
105 23         46 $sum = $sum + ($wk->[$i] * $pk->[$i]) + ($lk->[$i] * (1 - $pk->[$i]));
106             }
107              
108 1         4 return $sum;
109             }
110              
111             #Sigma( (profit**2) * winning probability + (loss**2) * losing probability ).
112             sub _variance_x_square {
113              
114 1     1   3 my $pk = shift;
115 1         2 my $lk = shift;
116 1         3 my $wk = shift;
117              
118 1         2 my $sum = 0;
119 1         1 my $i;
120              
121 1         4 for ($i = 0; $i < @{$wk}; ++$i) {
  24         46  
122 23         45 $sum = $sum + (($wk->[$i]**2) * $pk->[$i]) + (($lk->[$i]**2) * (1 - $pk->[$i]));
123             }
124              
125 1         2 return $sum;
126             }
127              
128             #Sum of Covariance(i,j). See the documentation for the details.
129             #Covariance(i, j) is the covariance between contract i and j with time overlap.
130             sub _covariance {
131              
132 1     1   5 my ($start_time, $sell_time, $underlying, $types, $pk, $lk, $wk, $exit_tick_epoch, $barriers) = @_;
133 1         1 my ($i, $j);
134 1         2 my $covariance = 0;
135              
136 1         3 for ($i = 0; $i < @{$types}; ++$i) {
  24         52  
137 23 100 100     102 if ($types->[$i] =~ /^CALL/ or $types->[$i] =~ /^PUT/) {
    50          
138              
139 17         29 for ($j = 0; $j < @{$sell_time}; ++$j) {
  408         731  
140 391 100 66     1035 if ($i != $j and $underlying->[$i] eq $underlying->[$j]) {
141              
142             #check for time overlap.
143 374 100       676 my $min_end_time = $sell_time->[$i] < $sell_time->[$j] ? $sell_time->[$i] : $sell_time->[$j];
144 374 100       639 my $max_start_time = $start_time->[$i] > $start_time->[$j] ? $start_time->[$i] : $start_time->[$j];
145 374         519 my $b_interval = $min_end_time - $max_start_time;
146              
147 374 100       687 if ($b_interval > 0) {
148              
149             #calculate first and second contracts durations. please see the documentation for details
150              
151 208         298 my $first_contract_duration = ($sell_time->[$i] - $start_time->[$i]);
152 208         309 my $second_contract_duration = ($sell_time->[$j] - $start_time->[$j]);
153              
154 208         451 my $i_strike = 0.0 - Math::Gauss::XS::inv_cdf($pk->[$i]);
155 208         341 my $j_strike = 0.0 - Math::Gauss::XS::inv_cdf($pk->[$j]);
156              
157 208         360 my $corr_ij = $b_interval / (sqrt($first_contract_duration) * sqrt($second_contract_duration));
158              
159 208 100       378 if ($types->[$i] ne $types->[$j]) {
160 112         174 $corr_ij = -1 * $corr_ij;
161             }
162              
163 208 50 33     576 if ($corr_ij < -1 or $corr_ij > 1) {
164 0         0 next;
165             }
166              
167 208         519 my $p_ij = Math::BivariateCDF::bivnor($i_strike, $j_strike, $corr_ij);
168              
169 208         396 my $covariance_ij =
170             ($p_ij - $pk->[$i] * $pk->[$j]) * ($wk->[$i] - $lk->[$i]) * ($wk->[$j] - $lk->[$j]);
171 208         379 $covariance = $covariance + $covariance_ij;
172             }
173             }
174             }
175             } elsif ($types->[$i] =~ /^DIGIT/) {
176 6         11 for ($j = 0; $j < @{$exit_tick_epoch}; ++$j) {
  144         262  
177 138 100 66     522 if ($i != $j and $underlying->[$i] eq $underlying->[$j] and $exit_tick_epoch->[$i] == $exit_tick_epoch->[$j]) {
      100        
178              
179 30         104 my $p_ij = get_shared_winning_probability({
180             type_1 => $types->[$i],
181             type_2 => $types->[$j],
182             barrier_1 => $barriers->[$i],
183             barrier_2 => $barriers->[$j]});
184              
185 30         84 my $covariance_ij = ($p_ij - $pk->[$i] * $pk->[$j]) * ($wk->[$i] - $lk->[$i]) * ($wk->[$j] - $lk->[$j]);
186 30         53 $covariance = $covariance + $covariance_ij;
187              
188             }
189             }
190             } else {
191              
192 0         0 next;
193             }
194             }
195 1         4 return $covariance;
196             }
197              
198             =head2 get_shared_winning_probability
199              
200             Calculate probability that a pair of digit contracts winning together.
201              
202             The outcome of a digit contract pairs are correlated if they expire at same time( same digit).
203              
204             The probability of a digit contract pair expiring at same digit is equal to the number of shared winning digits of the pair divied by 10.
205              
206             Example:
207             i. The shared winning digits for a DIGITEVEN and a DIGITOVER 2 are: 4,6, and 8. The probability would be equal to 3/10.
208              
209             ii. For a DIGITOVER 3 and a DIGITUNDER 9: 4,5,6,7, and 8. The probability would be equal to 5/10.
210              
211             =cut
212              
213             sub get_shared_winning_probability {
214              
215 30     30 1 49 my $params = shift;
216 30         48 my $c1_type = $params->{type_1};
217 30         42 my $c2_type = $params->{type_2};
218 30         41 my $c1_choosen_digit = $params->{barrier_1};
219 30         38 my $c2_choosen_digit = $params->{barrier_2};
220              
221 30         49 my @c1_winning_digits = get_winning_digits($c1_type, $c1_choosen_digit);
222 30         50 my @c2_winning_digits = get_winning_digits($c2_type, $c2_choosen_digit);
223              
224 30         49 my %c2_winning_digits = map { $_ => 1 } @c2_winning_digits;
  130         237  
225              
226 30         57 my @shared_winning_digits = grep { $c2_winning_digits{$_} } @c1_winning_digits;
  130         237  
227              
228 30         94 return scalar(@shared_winning_digits) / 10;
229              
230             }
231              
232             =head2 get_winning_digits
233              
234             Return the digits that contribute to a winning contract.
235              
236             Example:DIGITEVEN : 0, 2, 4, 6, 8.
237             DIGITODD: 1, 3, 5,7,9
238              
239             =cut
240              
241             sub get_winning_digits {
242              
243 60     60 1 111 my ($type, $digit) = @_;
244              
245 60         72 my @winning_digits;
246 60         118 my @all_digit = (0 .. 9);
247              
248 60 50 66     231 if ($type eq 'DIGITEVEN') {
    100 33        
    100          
    50          
249              
250 0         0 @winning_digits = (0, 2, 4, 6, 8);
251              
252             } elsif ($type eq 'DIGITODD') {
253              
254 10         17 @winning_digits = (1, 3, 5, 7, 9);
255             } elsif ($type eq 'DIGITDIFF' or $type eq 'DIGITMATCH') {
256              
257 20 50       49 @winning_digits = $type eq 'DIGITDIFF' ? grep { $all_digit[$_] != $digit } @all_digit : grep { $all_digit[$_] == $digit } @all_digit;
  0         0  
  200         346  
258              
259             } elsif ($type eq 'DIGITOVER' or $type eq 'DIGITUNDER') {
260              
261 30 50       56 @winning_digits = $type eq 'DIGITOVER' ? grep { $all_digit[$_] > $digit } @all_digit : grep { $all_digit[$_] < $digit } @all_digit;
  300         572  
  0         0  
262              
263             }
264              
265 60         137 return @winning_digits;
266              
267             }
268              
269             =head2 get_performance_probability
270              
271             Calculate performance probability ( modified sharpe ratio )
272              
273             =cut
274              
275             sub get_performance_probability {
276              
277 1     1 1 2314 my $params = shift;
278              
279 1         5 my $pnl = $params->{pnl};
280              
281 1 50       6 if (not defined $pnl) {
282 0         0 die "pnl is a required parameter.";
283             }
284              
285             #Below variables are all arrays.
286 1         3 my $start_time = $params->{start_time};
287 1         2 my $sell_time = $params->{sell_time};
288 1         2 my $types = $params->{types};
289 1         13 my $underlying = $params->{underlying};
290 1         17 my $bought_price = $params->{bought_price};
291 1         3 my $payout = $params->{payout};
292 1         3 my $exit_tick_epoch = $params->{exit_tick_epoch};
293 1         2 my $barriers = $params->{barriers};
294              
295 1 50       5 if (
296 7         38 grep { $_ != scalar(@$start_time) } (
297             scalar(@$sell_time), scalar(@$types), scalar(@$underlying), scalar(@$bought_price),
298             scalar(@$payout), scalar(@$exit_tick_epoch), scalar(@$barriers)))
299             {
300 0         0 die "start_time, sell_time, types, underlying, bought_price and payout are required parameters and need to be arrays of same lengths.";
301             }
302              
303 1         9 my $i = 0;
304 1         4 for ($i = 0; $i < @{$start_time}; ++$i) {
  24         81  
305 23 50       53 if ($sell_time->[$i] - $start_time->[$i] == 0) {
306 0         0 die "Contract duration ( sell_time minus start_time ) cannot be zero.";
307             }
308             }
309              
310 1         7 my $pk = _build_pk($bought_price, $payout);
311 1         8 my $lk = _build_lk($bought_price);
312 1         4 my $wk = _build_wk($bought_price, $payout);
313              
314 1         4 my $mean = _mean($pk, $lk, $wk);
315              
316 1         3 my $variance = _variance_x_square($pk, $lk, $wk);
317              
318 1         4 my $covariance = _covariance($start_time, $sell_time, $underlying, $types, $pk, $lk, $wk, $exit_tick_epoch, $barriers);
319              
320             #Calculate the performance probability here.
321 1         2 my $prob = 0;
322              
323 1         5 my $epsilon = machine_epsilon();
324              
325 1         186 $prob = $pnl - $mean;
326 1         4 $prob = $prob / (sqrt(($variance - ($mean**2.0)) + $covariance) + $epsilon);
327              
328 1         3 $prob = 1.0 - Math::Gauss::XS::cdf($prob, 0.0, 1.0);
329              
330 1         6 return $prob;
331             }
332              
333             1;