File Coverage

blib/lib/Performance/Probability.pm
Criterion Covered Total %
statement 100 115 86.9
branch 10 18 55.5
condition 2 6 33.3
subroutine 14 14 100.0
pod 1 1 100.0
total 127 154 82.4


line stmt bran cond sub pod time code
1             package Performance::Probability;
2              
3 1     1   83551 use 5.010;
  1         2  
4 1     1   4 use strict;
  1         2  
  1         14  
5 1     1   2 use warnings;
  1         2  
  1         18  
6              
7 1     1   358 use Math::BivariateCDF;
  1         362  
  1         31  
8 1     1   366 use Math::Gauss::XS;
  1         329  
  1         33  
9 1     1   345 use Machine::Epsilon;
  1         234  
  1         33  
10              
11 1     1   4 use Exporter;
  1         1  
  1         732  
12              
13             our @ISA = qw(Exporter);
14              
15             our @EXPORT_OK = qw(get_performance_probability);
16              
17             our $VERSION = '0.05';
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         17 my $payout = shift;
48              
49 1         1 my @w_k;
50              
51             my $i;
52              
53 1         2 for ($i = 0; $i < @{$payout}; ++$i) {
  101         112  
54 100         61 my $tmp_w_k = $payout->[$i] - $bought_price->[$i];
55 100         64 push @w_k, $tmp_w_k;
56             }
57              
58 1         2 return \@w_k;
59             }
60              
61             #Loss in case of losing. (Minus bought price).
62             sub _build_lk {
63              
64 1     1   2 my $bought_price = shift;
65 1         1 my @l_k;
66              
67             my $i;
68              
69 1         2 for ($i = 0; $i < @{$bought_price}; ++$i) {
  101         112  
70 100         75 push @l_k, 0 - $bought_price->[$i];
71             }
72              
73 1         2 return \@l_k;
74             }
75              
76             #Winning probability. ( Bought price / Payout ).
77             sub _build_pk {
78              
79 1     1   2 my $bought_price = shift;
80 1         1 my $payout = shift;
81              
82 1         2 my @p_k;
83              
84             my $i;
85              
86 1         1 for ($i = 0; $i < @{$bought_price}; ++$i) {
  101         113  
87 100         88 my $tmp_pk = $bought_price->[$i] / $payout->[$i];
88 100         69 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   1 my $pk = shift;
98 1         1 my $lk = shift;
99 1         1 my $wk = shift;
100              
101 1         1 my $i;
102 1         2 my $sum = 0;
103              
104 1         2 for ($i = 0; $i < @{$wk}; ++$i) {
  101         114  
105 100         108 $sum = $sum + ($wk->[$i] * $pk->[$i]) + ($lk->[$i] * (1 - $pk->[$i]));
106             }
107              
108 1         3 return $sum;
109             }
110              
111             #Sigma( (profit**2) * winning probability + (loss**2) * losing probability ).
112             sub _variance_x_square {
113              
114 1     1   1 my $pk = shift;
115 1         1 my $lk = shift;
116 1         2 my $wk = shift;
117              
118 1         3 my $sum = 0;
119 1         1 my $i;
120              
121 1         2 for ($i = 0; $i < @{$wk}; ++$i) {
  101         111  
122 100         108 $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   2 my ($start_time, $sell_time, $underlying, $types, $pk, $lk, $wk) = @_;
133              
134 1         1 my ($i, $j);
135 1         1 my $covariance = 0;
136              
137 1         2 for ($i = 0; $i < @{$start_time}; ++$i) {
  101         115  
138 100         67 for ($j = 0; $j < @{$sell_time}; ++$j) {
  10100         11390  
139 10000 100 66     22547 if ($i != $j and $underlying->[$i] eq $underlying->[$j]) {
140              
141             #check for time overlap.
142 9900 100       9625 my $min_end_time = $sell_time->[$i] < $sell_time->[$j] ? $sell_time->[$i] : $sell_time->[$j];
143 9900 100       9261 my $max_start_time = $start_time->[$i] > $start_time->[$j] ? $start_time->[$i] : $start_time->[$j];
144 9900         5831 my $b_interval = $min_end_time - $max_start_time;
145              
146 9900 50       11990 if ($b_interval > 0) {
147              
148             #calculate first and second contracts durations. please see the documentation for details
149              
150 0         0 my $first_contract_duration = ($sell_time->[$i] - $start_time->[$i]);
151 0         0 my $second_contract_duration = ($sell_time->[$j] - $start_time->[$j]);
152              
153 0         0 my $i_strike = 0.0 - Math::Gauss::XS::inv_cdf($pk->[$i]);
154 0         0 my $j_strike = 0.0 - Math::Gauss::XS::inv_cdf($pk->[$j]);
155              
156 0         0 my $corr_ij = $b_interval / (sqrt($first_contract_duration) * sqrt($second_contract_duration));
157              
158 0 0       0 if ($types->[$i] ne $types->[$j]) {
159 0         0 $corr_ij = -1 * $corr_ij;
160             }
161              
162 0 0 0     0 if ($corr_ij < -1 or $corr_ij > 1) {
163 0         0 next;
164             }
165              
166 0         0 my $p_ij = Math::BivariateCDF::bivnor($i_strike, $j_strike, $corr_ij);
167              
168 0         0 my $covariance_ij =
169             ($p_ij - $pk->[$i] * $pk->[$j]) * ($wk->[$i] - $lk->[$i]) * ($wk->[$j] - $lk->[$j]);
170              
171 0         0 $covariance = $covariance + $covariance_ij;
172             }
173             }
174             }
175             }
176              
177 1         5 return $covariance;
178             }
179              
180             =head2 get_performance_probability
181              
182             Calculate performance probability ( modified sharpe ratio )
183              
184             =cut
185              
186             sub get_performance_probability {
187              
188 1     1 1 1948 my $params = shift;
189              
190 1         2 my $pnl = $params->{pnl};
191              
192 1 50       4 if (not defined $pnl) {
193 0         0 die "pnl is a required parameter.";
194             }
195              
196             #Below variables are all arrays.
197 1         1 my $start_time = $params->{start_time};
198 1         2 my $sell_time = $params->{sell_time};
199 1         2 my $types = $params->{types};
200 1         1 my $underlying = $params->{underlying};
201 1         1 my $bought_price = $params->{bought_price};
202 1         2 my $payout = $params->{payout};
203              
204 1 50       2 if (grep { $_ != scalar(@$start_time) } (scalar(@$sell_time), scalar(@$types), scalar(@$underlying), scalar(@$bought_price), scalar(@$payout))) {
  5         7  
205 0         0 die "start_time, sell_time, types, underlying, bought_price and payout are required parameters and need to be arrays of same lengths.";
206             }
207              
208 1         2 my $i = 0;
209 1         2 for ($i = 0; $i < @{$start_time}; ++$i) {
  101         117  
210 100 50       140 if ($sell_time->[$i] - $start_time->[$i] == 0) {
211 0         0 die "Contract duration ( sell_time minus start_time ) cannot be zero.";
212             }
213             }
214              
215 1         3 my $pk = _build_pk($bought_price, $payout);
216 1         2 my $lk = _build_lk($bought_price);
217 1         4 my $wk = _build_wk($bought_price, $payout);
218              
219 1         3 my $mean = _mean($pk, $lk, $wk);
220              
221 1         2 my $variance = _variance_x_square($pk, $lk, $wk);
222              
223 1         3 my $covariance = _covariance($start_time, $sell_time, $underlying, $types, $pk, $lk, $wk);
224              
225             #Calculate the performance probability here.
226 1         1 my $prob = 0;
227              
228 1         8 my $epsilon = machine_epsilon();
229              
230 1         87 $prob = $pnl - $mean;
231 1         4 $prob = $prob / (sqrt(($variance - ($mean**2.0)) + 2.0 * $covariance) + $epsilon);
232              
233 1         10 $prob = 1.0 - Math::Gauss::XS::cdf($prob, 0.0, 1.0);
234              
235 1         16 return $prob;
236             }
237              
238             1;