line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Finance::Nadex; |
2
|
|
|
|
|
|
|
|
3
|
14
|
|
|
14
|
|
772195
|
use strict; |
|
14
|
|
|
|
|
17
|
|
|
14
|
|
|
|
|
298
|
|
4
|
14
|
|
|
14
|
|
43
|
use warnings; |
|
14
|
|
|
|
|
14
|
|
|
14
|
|
|
|
|
385
|
|
5
|
|
|
|
|
|
|
our $VERSION = '0.06'; |
6
|
|
|
|
|
|
|
|
7
|
14
|
|
|
14
|
|
1083
|
use LWP::UserAgent; |
|
14
|
|
|
|
|
60061
|
|
|
14
|
|
|
|
|
215
|
|
8
|
14
|
|
|
14
|
|
7836
|
use JSON; |
|
14
|
|
|
|
|
108717
|
|
|
14
|
|
|
|
|
45
|
|
9
|
14
|
|
|
14
|
|
1235
|
use Carp; |
|
14
|
|
|
|
|
16
|
|
|
14
|
|
|
|
|
580
|
|
10
|
14
|
|
|
14
|
|
4592
|
use Finance::Nadex::Order; |
|
14
|
|
|
|
|
101
|
|
|
14
|
|
|
|
|
319
|
|
11
|
14
|
|
|
14
|
|
4430
|
use Finance::Nadex::Position; |
|
14
|
|
|
|
|
19
|
|
|
14
|
|
|
|
|
291
|
|
12
|
14
|
|
|
14
|
|
4449
|
use Finance::Nadex::Contract; |
|
14
|
|
|
|
|
21
|
|
|
14
|
|
|
|
|
320
|
|
13
|
|
|
|
|
|
|
|
14
|
14
|
|
|
14
|
|
50
|
use constant LOGIN_URL => '/iDeal/v2/security/authenticate'; |
|
14
|
|
|
|
|
19
|
|
|
14
|
|
|
|
|
619
|
|
15
|
14
|
|
|
14
|
|
48
|
use constant SEND_ORDER_URL => '/iDeal/dma/workingorders'; |
|
14
|
|
|
|
|
11
|
|
|
14
|
|
|
|
|
500
|
|
16
|
14
|
|
|
14
|
|
41
|
use constant RETRIEVE_ORDERS_URL => '/iDeal/orders/workingorders'; |
|
14
|
|
|
|
|
12
|
|
|
14
|
|
|
|
|
435
|
|
17
|
14
|
|
|
14
|
|
42
|
use constant RETRIEVE_ORDER_URL => '/iDeal/markets/details/workingorders'; |
|
14
|
|
|
|
|
11
|
|
|
14
|
|
|
|
|
416
|
|
18
|
14
|
|
|
14
|
|
40
|
use constant CANCEL_ORDER_URL => '/iDeal/orders/workingorders/dma'; |
|
14
|
|
|
|
|
13
|
|
|
14
|
|
|
|
|
407
|
|
19
|
14
|
|
|
14
|
|
40
|
use constant RETRIEVE_POSITIONS_URL => '/iDeal/orders/positions'; |
|
14
|
|
|
|
|
14
|
|
|
14
|
|
|
|
|
401
|
|
20
|
14
|
|
|
14
|
|
39
|
use constant RETRIEVE_POSITION_URL => '/iDeal/markets/details/position'; |
|
14
|
|
|
|
|
12
|
|
|
14
|
|
|
|
|
489
|
|
21
|
14
|
|
|
14
|
|
42
|
use constant EPIC_URL => '/iDeal/markets/details'; |
|
14
|
|
|
|
|
15
|
|
|
14
|
|
|
|
|
438
|
|
22
|
14
|
|
|
14
|
|
42
|
use constant MARKET_LIST_URL => '/iDeal/markets/navigation'; |
|
14
|
|
|
|
|
13
|
|
|
14
|
|
|
|
|
45773
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my $session_id; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# allows caller to use aliases for the market names in addition to those used by the exchange |
27
|
|
|
|
|
|
|
my %index_name = ( |
28
|
|
|
|
|
|
|
'FTSE100', 'FTSE 100', 'G30', 'Germany 30', |
29
|
|
|
|
|
|
|
'J225', 'Japan 225', 'TECH100', 'US Tech 100', |
30
|
|
|
|
|
|
|
'US500', 'US 500', 'WALL30', 'Wall St 30', |
31
|
|
|
|
|
|
|
'SC2000', 'US SmallCap 2000' |
32
|
|
|
|
|
|
|
); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub balance { |
35
|
|
|
|
|
|
|
|
36
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
37
|
|
|
|
|
|
|
|
38
|
0
|
0
|
|
|
|
0
|
croak "ERROR: Finance::Nadex::balance(): must be logged in\n" |
39
|
|
|
|
|
|
|
unless $self->logged_in; |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# it appears the only way to force a |
42
|
|
|
|
|
|
|
# balance refresh is to login again |
43
|
0
|
|
|
|
|
0
|
$self->login(); |
44
|
|
|
|
|
|
|
|
45
|
0
|
|
0
|
|
|
0
|
return $self->{'balance'} || undef; |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub cancel_all_orders { |
50
|
|
|
|
|
|
|
|
51
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
52
|
|
|
|
|
|
|
|
53
|
0
|
0
|
|
|
|
0
|
croak "ERROR: Finance::Nadex::cancel_all_orders(): must be logged in\n" |
54
|
|
|
|
|
|
|
unless $self->logged_in; |
55
|
0
|
|
|
|
|
0
|
my @orders = $self->retrieve_orders(); |
56
|
|
|
|
|
|
|
|
57
|
0
|
0
|
|
|
|
0
|
return if !scalar @orders; |
58
|
|
|
|
|
|
|
|
59
|
0
|
|
|
|
|
0
|
foreach my $order_entry (@orders) { |
60
|
0
|
|
|
|
|
0
|
$self->cancel_order( id => $order_entry->id ); |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub cancel_order { |
66
|
|
|
|
|
|
|
|
67
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
68
|
0
|
|
|
|
|
0
|
my %args = @_; |
69
|
|
|
|
|
|
|
|
70
|
0
|
0
|
|
|
|
0
|
croak "ERROR: Finance::Nadex::cancel_order(): must be logged in\n" |
71
|
|
|
|
|
|
|
unless $self->logged_in; |
72
|
|
|
|
|
|
|
croak |
73
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::cancel_order(): must specify a named argument 'id'\n" |
74
|
0
|
0
|
|
|
|
0
|
unless exists $args{id}; |
75
|
|
|
|
|
|
|
croak "ERROR: Finance::Nadex::cancel_order(): invalid id\n" |
76
|
0
|
0
|
|
|
|
0
|
unless $args{id}; |
77
|
|
|
|
|
|
|
|
78
|
0
|
|
|
|
|
0
|
my $deal_id = $args{id}; |
79
|
|
|
|
|
|
|
|
80
|
0
|
|
|
|
|
0
|
my $cancel_order_url = $self->{base_url}.CANCEL_ORDER_URL . '/' . $deal_id; |
81
|
|
|
|
|
|
|
|
82
|
0
|
|
|
|
|
0
|
my $cancel_time = time; |
83
|
0
|
|
|
|
|
0
|
my $cancel_content = qq~ |
84
|
|
|
|
|
|
|
{ |
85
|
|
|
|
|
|
|
"lsServerName": "https://mdp.nadex.com:443", |
86
|
|
|
|
|
|
|
"timeStamp": "$cancel_time" |
87
|
|
|
|
|
|
|
}~; |
88
|
|
|
|
|
|
|
|
89
|
0
|
|
|
|
|
0
|
my $cancelled_response = |
90
|
|
|
|
|
|
|
$self->_delete( $cancel_order_url, $cancel_content ); |
91
|
|
|
|
|
|
|
|
92
|
0
|
0
|
|
|
|
0
|
return undef unless $cancelled_response; |
93
|
|
|
|
|
|
|
|
94
|
0
|
|
|
|
|
0
|
return $cancelled_response->{'dealReference'}; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub create_order { |
98
|
|
|
|
|
|
|
|
99
|
18
|
|
|
18
|
1
|
4451
|
my $self = shift; |
100
|
18
|
|
|
|
|
46
|
my %args = @_; |
101
|
|
|
|
|
|
|
|
102
|
18
|
|
|
|
|
19
|
my $order_time = time; |
103
|
|
|
|
|
|
|
|
104
|
18
|
100
|
|
|
|
32
|
croak "ERROR: Finance::Nadex::create_order(): must be logged in\n" |
105
|
|
|
|
|
|
|
unless $self->logged_in; |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
croak |
108
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::create_order(): must specify a named argument 'price'\n" |
109
|
12
|
100
|
|
|
|
33
|
unless exists $args{price}; |
110
|
|
|
|
|
|
|
croak |
111
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::create_order(): must specify a named argument 'direction'\n" |
112
|
10
|
100
|
|
|
|
21
|
unless exists $args{direction}; |
113
|
|
|
|
|
|
|
croak |
114
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::create_order(): must specify a named argument 'epic'\n" |
115
|
9
|
100
|
|
|
|
25
|
unless exists $args{epic}; |
116
|
|
|
|
|
|
|
croak |
117
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::create_order(): must specify a named argument 'size'\n" |
118
|
8
|
100
|
|
|
|
19
|
unless exists $args{size}; |
119
|
|
|
|
|
|
|
|
120
|
7
|
|
|
|
|
8
|
$args{direction} = lc( $args{direction} ); |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
croak "ERROR: Finance::Nadex::create_order(): invalid epic\n" |
123
|
7
|
50
|
|
|
|
12
|
unless $args{epic}; |
124
|
|
|
|
|
|
|
|
125
|
7
|
|
|
|
|
9
|
my $contract = $self->get_contract( epic => $args{epic} ); |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
croak |
128
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::create_order(): named argument 'price' (->$args{price}<-) is not valid\n" |
129
|
7
|
100
|
|
|
|
22
|
unless $self->_is_valid_price( $args{price}, $contract->type ); |
130
|
|
|
|
|
|
|
croak |
131
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::create_order(): named argument 'direction' (->$args{direction}<-) is not valid\n" |
132
|
6
|
100
|
|
|
|
13
|
unless $self->_is_valid_direction( $args{direction} ); |
133
|
5
|
50
|
|
|
|
8
|
croak |
134
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::create_order(): named argument 'epic' (->$args{epic}<-) is not valid\n" |
135
|
|
|
|
|
|
|
unless $contract; |
136
|
|
|
|
|
|
|
croak |
137
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::create_order(): named argument 'size' (->$args{size}<-) is not valid\n" |
138
|
5
|
100
|
|
|
|
8
|
unless $self->_is_valid_size( $args{size} ); |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# the market accepts only + or - for the direction; this enables the aliases 'buy' and 'sell' |
141
|
4
|
100
|
|
|
|
8
|
$args{direction} = '+' if $args{direction} eq 'buy'; |
142
|
4
|
100
|
|
|
|
7
|
$args{direction} = '-' if $args{direction} eq 'sell'; |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# the price is dollars and cents for binaries and some instrument level |
145
|
|
|
|
|
|
|
# for spreads; only format the price to a currency if the order type is 'binary' |
146
|
|
|
|
|
|
|
$args{price} = sprintf( "%.2f", $args{price} ) |
147
|
4
|
100
|
|
|
|
7
|
if $contract->type eq 'binary'; |
148
|
|
|
|
|
|
|
|
149
|
4
|
|
|
|
|
27
|
my $order_content = qq~ |
150
|
|
|
|
|
|
|
{ |
151
|
|
|
|
|
|
|
"direction": "$args{direction}", |
152
|
|
|
|
|
|
|
"epic": "$args{epic}", |
153
|
|
|
|
|
|
|
"limitLevel": null, |
154
|
|
|
|
|
|
|
"lsServerName": "https://mdp.nadex.com:443", |
155
|
|
|
|
|
|
|
"orderLevel": "$args{price}", |
156
|
|
|
|
|
|
|
"orderSize": "$args{size}", |
157
|
|
|
|
|
|
|
"orderType": "Limit", |
158
|
|
|
|
|
|
|
"sizeForPandLCalculation": $args{size}, |
159
|
|
|
|
|
|
|
"stopLevel": null, |
160
|
|
|
|
|
|
|
"timeInForce": "GoodTillCancelled", |
161
|
|
|
|
|
|
|
"timeStamp": "$order_time" |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
}~; |
164
|
|
|
|
|
|
|
|
165
|
4
|
|
|
|
|
9
|
my $order = $self->_post( $self->{base_url}.SEND_ORDER_URL, $order_content ); |
166
|
|
|
|
|
|
|
|
167
|
4
|
50
|
|
|
|
8
|
my $deal_reference_id = $order->{dealReference} if defined $order; |
168
|
|
|
|
|
|
|
|
169
|
4
|
|
|
|
|
24
|
return $deal_reference_id; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub get_contract { |
173
|
|
|
|
|
|
|
|
174
|
7
|
|
|
7
|
1
|
7
|
my $self = shift; |
175
|
7
|
|
|
|
|
9
|
my %args = @_; |
176
|
|
|
|
|
|
|
|
177
|
7
|
50
|
|
|
|
8
|
croak "ERROR: Finance::Nadex::get_contracts(): must be logged in\n" |
178
|
|
|
|
|
|
|
unless $self->logged_in; |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
croak |
181
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_contract(): must specify a named argument 'epic'\n" |
182
|
7
|
50
|
|
|
|
10
|
unless exists $args{epic}; |
183
|
|
|
|
|
|
|
croak |
184
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_contract(): specified 'epic' (->$args{epic}<-) is not valid\n" |
185
|
7
|
50
|
|
|
|
15
|
unless $args{epic}; |
186
|
|
|
|
|
|
|
|
187
|
7
|
|
|
|
|
16
|
my $epic_url = $self->{base_url}.EPIC_URL . '/' . $args{epic}; |
188
|
7
|
|
|
|
|
11
|
my $epic_ref = $self->_get($epic_url); |
189
|
|
|
|
|
|
|
|
190
|
7
|
50
|
|
|
|
11
|
return unless $epic_ref; |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
return |
193
|
|
|
|
|
|
|
unless exists $epic_ref->{instrument} |
194
|
7
|
50
|
33
|
|
|
21
|
&& exists $epic_ref->{marketSnapshot}; |
195
|
|
|
|
|
|
|
return |
196
|
|
|
|
|
|
|
unless $epic_ref->{instrument}{instrumentType} |
197
|
|
|
|
|
|
|
&& $epic_ref->{instrument}{marketName} |
198
|
7
|
50
|
33
|
|
|
30
|
&& $epic_ref->{instrument}{displayPrompt}; |
|
|
|
33
|
|
|
|
|
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
return Finance::Nadex::Contract::_new( |
201
|
|
|
|
|
|
|
{ |
202
|
|
|
|
|
|
|
instrumentType => $epic_ref->{instrument}{instrumentType}, |
203
|
|
|
|
|
|
|
epic => $args{epic}, |
204
|
|
|
|
|
|
|
displayOffer => $epic_ref->{marketSnapshot}{displayOffer}, |
205
|
|
|
|
|
|
|
displayBid => $epic_ref->{marketSnapshot}{displayBid}, |
206
|
|
|
|
|
|
|
displayBidSize => $epic_ref->{marketSnapshot}{displayBidSize}, |
207
|
|
|
|
|
|
|
displayOfferSize => $epic_ref->{marketSnapshot}{displayOfferSize}, |
208
|
|
|
|
|
|
|
instrumentName => $epic_ref->{instrument}{marketName}, |
209
|
|
|
|
|
|
|
displayPeriod => $epic_ref->{instrument}{displayPrompt} |
210
|
|
|
|
|
|
|
} |
211
|
7
|
|
|
|
|
52
|
); |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
sub get_contracts { |
216
|
|
|
|
|
|
|
|
217
|
6
|
|
|
6
|
1
|
2021
|
my $self = shift; |
218
|
6
|
|
|
|
|
15
|
my %args = @_; |
219
|
6
|
|
|
|
|
6
|
my $found; |
220
|
|
|
|
|
|
|
|
221
|
6
|
50
|
|
|
|
8
|
croak "ERROR: Finance::Nadex::get_contracts(): must be logged in\n" |
222
|
|
|
|
|
|
|
unless $self->logged_in; |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
croak |
225
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_contracts(): must specify a named argument 'market'\n" |
226
|
6
|
100
|
|
|
|
29
|
unless exists $args{market}; |
227
|
|
|
|
|
|
|
croak |
228
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_contracts(): must specify a named argument 'instrument'\n" |
229
|
4
|
100
|
|
|
|
13
|
unless exists $args{instrument}; |
230
|
|
|
|
|
|
|
croak |
231
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_conttracts(): must specify a named argument 'series'\n" |
232
|
3
|
100
|
|
|
|
34
|
unless exists $args{series}; |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
croak "ERROR: Finance::Nadex::get_contracts(): invalid market\n" |
235
|
2
|
50
|
|
|
|
6
|
unless $args{market}; |
236
|
|
|
|
|
|
|
croak "ERROR: Finance::Nadex::get_contracts(): invalid instrument\n" |
237
|
2
|
50
|
|
|
|
4
|
unless $args{instrument}; |
238
|
|
|
|
|
|
|
croak "ERROR: Finance::Nadex::get_conttracts(): invalid series\n" |
239
|
2
|
50
|
|
|
|
5
|
unless $args{series}; |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
$args{instrument} = $index_name{ $args{instrument} } |
242
|
2
|
50
|
|
|
|
8
|
if exists $index_name{ $args{instrument} }; |
243
|
|
|
|
|
|
|
|
244
|
2
|
|
|
|
|
8
|
my $market_list_ref = $self->_get($self->{base_url}.MARKET_LIST_URL); |
245
|
|
|
|
|
|
|
|
246
|
2
|
50
|
|
|
|
5
|
die |
247
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_contracts(): failed to retrieve the market list from the exchange\n" |
248
|
|
|
|
|
|
|
if !$market_list_ref; |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
my $market_id = $self->_get_market_id( |
251
|
|
|
|
|
|
|
name => $args{market}, |
252
|
2
|
|
|
|
|
5
|
market_list_ref => $market_list_ref |
253
|
|
|
|
|
|
|
); |
254
|
|
|
|
|
|
|
|
255
|
2
|
50
|
|
|
|
8
|
return unless $market_id; |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
my $instruments_list_ref = |
258
|
2
|
|
|
|
|
6
|
$self->_get( $self->{base_url}.MARKET_LIST_URL . '/' . $market_id ); |
259
|
|
|
|
|
|
|
|
260
|
2
|
50
|
|
|
|
6
|
die |
261
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_contracts(): failed to retrieve the market list from the exchange for market $market_id\n" |
262
|
|
|
|
|
|
|
if !$instruments_list_ref; |
263
|
|
|
|
|
|
|
|
264
|
2
|
|
|
|
|
2
|
my $instrument_id; |
265
|
2
|
|
|
|
|
4
|
foreach my $instrument ( @{ $instruments_list_ref->{'hierarchy'} } ) { |
|
2
|
|
|
|
|
4
|
|
266
|
|
|
|
|
|
|
$instrument_id = $instrument->{id} |
267
|
2
|
50
|
|
|
|
10
|
if $instrument->{name} eq $args{instrument}; |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
2
|
50
|
|
|
|
11
|
return unless $instrument_id; |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
my $instrument_list_ref = |
273
|
2
|
|
|
|
|
10
|
$self->_get( $self->{base_url}.MARKET_LIST_URL . '/' . $instrument_id ); |
274
|
|
|
|
|
|
|
|
275
|
2
|
50
|
|
|
|
6
|
die |
276
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_contracts(): failed to retrieve the market list from the exchange for market $market_id\n" |
277
|
|
|
|
|
|
|
if !$instrument_list_ref; |
278
|
|
|
|
|
|
|
|
279
|
2
|
|
|
|
|
3
|
my $time_series_id; |
280
|
2
|
|
|
|
|
2
|
foreach my $series ( @{ $instrument_list_ref->{'hierarchy'} } ) { |
|
2
|
|
|
|
|
5
|
|
281
|
2
|
50
|
|
|
|
12
|
$time_series_id = $series->{id} if $series->{name} eq $args{series}; |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
|
284
|
2
|
50
|
|
|
|
5
|
return unless $time_series_id; |
285
|
|
|
|
|
|
|
|
286
|
2
|
|
|
|
|
2
|
my @contracts; |
287
|
|
|
|
|
|
|
my $series_list_ref = |
288
|
2
|
|
|
|
|
6
|
$self->_get( $self->{base_url}.MARKET_LIST_URL . '/' . $time_series_id ); |
289
|
|
|
|
|
|
|
|
290
|
2
|
50
|
|
|
|
6
|
die |
291
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_contracts(): failed to retrieve the market list from the exchange for market $market_id\n" |
292
|
|
|
|
|
|
|
if !$series_list_ref; |
293
|
|
|
|
|
|
|
|
294
|
2
|
|
|
|
|
3
|
foreach my $contract ( @{ $series_list_ref->{'markets'} } ) { |
|
2
|
|
|
|
|
4
|
|
295
|
2
|
|
|
|
|
9
|
push( @contracts, Finance::Nadex::Contract::_new($contract) ); |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
2
|
|
|
|
|
14
|
return @contracts; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub get_epic { |
302
|
|
|
|
|
|
|
|
303
|
6
|
|
|
6
|
1
|
2005
|
my $self = shift; |
304
|
6
|
|
|
|
|
18
|
my %args = @_; |
305
|
|
|
|
|
|
|
|
306
|
6
|
|
|
|
|
2
|
my $market_id; |
307
|
|
|
|
|
|
|
my $instrument; |
308
|
|
|
|
|
|
|
|
309
|
6
|
50
|
|
|
|
10
|
croak "ERROR: Finance::Nadex::get_epic(): must be logged in\n" |
310
|
|
|
|
|
|
|
unless $self->logged_in; |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
croak |
313
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_epic(): must specify a named argument 'period'\n" |
314
|
6
|
100
|
|
|
|
24
|
unless exists $args{period}; |
315
|
|
|
|
|
|
|
croak |
316
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_epic(): must specify a named argument 'market'\n" |
317
|
4
|
50
|
|
|
|
5
|
unless exists $args{market}; |
318
|
|
|
|
|
|
|
croak |
319
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_epic(): must specify a named argument 'time'\n" |
320
|
|
|
|
|
|
|
unless exists $args{time} |
321
|
|
|
|
|
|
|
|| ( exists $args{period} |
322
|
4
|
100
|
66
|
|
|
26
|
&& $args{period} =~ /^event$/i ); |
|
|
|
66
|
|
|
|
|
323
|
|
|
|
|
|
|
croak |
324
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_epic(): must specify a named argument 'instrument'\n" |
325
|
3
|
100
|
|
|
|
11
|
unless exists $args{instrument}; |
326
|
|
|
|
|
|
|
croak |
327
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_epic(): must specify a named argument 'strike'\n" |
328
|
2
|
50
|
|
|
|
3
|
unless exists $args{strike}; |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
croak "ERROR: Finance::Nadex::get_epic(): invalid period\n" |
331
|
2
|
50
|
|
|
|
3
|
unless $args{period}; |
332
|
|
|
|
|
|
|
croak "ERROR: Finance::Nadex::get_epic(): invalid market\n" |
333
|
2
|
50
|
|
|
|
3
|
unless $args{market}; |
334
|
|
|
|
|
|
|
croak "ERROR: Finance::Nadex::get_epic(): invalid time\n" |
335
|
|
|
|
|
|
|
unless $args{time} |
336
|
|
|
|
|
|
|
|| ( exists $args{period} |
337
|
2
|
50
|
33
|
|
|
8
|
&& $args{period} =~ /^event$/i ); |
|
|
|
66
|
|
|
|
|
338
|
|
|
|
|
|
|
croak "ERROR: Finance::Nadex::get_epic(): invalid instrument\n" |
339
|
2
|
50
|
|
|
|
4
|
unless $args{instrument}; |
340
|
|
|
|
|
|
|
croak "ERROR: Finance::Nadex::get_epic(): invalid strike\n" |
341
|
2
|
50
|
|
|
|
8
|
unless $args{strike}; |
342
|
|
|
|
|
|
|
|
343
|
2
|
50
|
|
|
|
7
|
$args{period} = ucfirst( lc( $args{period} ) ) if exists $args{period}; |
344
|
|
|
|
|
|
|
|
345
|
2
|
100
|
|
|
|
5
|
$args{time} = lc( $args{time} ) if exists $args{time}; |
346
|
|
|
|
|
|
|
|
347
|
2
|
|
|
|
|
6
|
my $market_list_ref = $self->_get($self->{base_url}.MARKET_LIST_URL); |
348
|
|
|
|
|
|
|
|
349
|
2
|
50
|
|
|
|
4
|
die |
350
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_epic(): failed to retrieve the market list from the exchange\n" |
351
|
|
|
|
|
|
|
if !$market_list_ref; |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
$market_id = $self->_get_market_id( |
354
|
|
|
|
|
|
|
name => $args{market}, |
355
|
2
|
|
|
|
|
5
|
market_list_ref => $market_list_ref |
356
|
|
|
|
|
|
|
); |
357
|
|
|
|
|
|
|
|
358
|
2
|
50
|
|
|
|
4
|
return undef unless $market_id; |
359
|
|
|
|
|
|
|
|
360
|
2
|
|
|
|
|
6
|
$market_list_ref = $self->_get( $self->{base_url}.MARKET_LIST_URL . "/$market_id" ); |
361
|
|
|
|
|
|
|
|
362
|
2
|
50
|
|
|
|
7
|
die |
363
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_epic(): failed to retrieve the market list from the exchange for market $market_id\n" |
364
|
|
|
|
|
|
|
if !$market_list_ref; |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
$market_id = $self->_get_market_id( |
367
|
|
|
|
|
|
|
name => $args{instrument}, |
368
|
2
|
|
|
|
|
8
|
market_list_ref => $market_list_ref |
369
|
|
|
|
|
|
|
); |
370
|
|
|
|
|
|
|
|
371
|
2
|
50
|
|
|
|
4
|
return undef unless $market_id; |
372
|
|
|
|
|
|
|
|
373
|
2
|
|
|
|
|
7
|
$market_list_ref = $self->_get( $self->{base_url}.MARKET_LIST_URL . "/$market_id" ); |
374
|
|
|
|
|
|
|
|
375
|
2
|
50
|
|
|
|
6
|
die |
376
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_epic(): failed to retrieve the market list from the exchange for market $market_id\n" |
377
|
|
|
|
|
|
|
if !$market_list_ref; |
378
|
|
|
|
|
|
|
|
379
|
2
|
|
|
|
|
3
|
my $target_period_time; |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
$target_period_time = "$args{period} ($args{time})" |
382
|
2
|
100
|
|
|
|
7
|
if $args{period} eq 'Daily'; |
383
|
2
|
50
|
|
|
|
4
|
$target_period_time = "-$args{time}" if $args{period} eq 'Intraday'; |
384
|
2
|
50
|
|
|
|
3
|
$target_period_time = "$args{period}" if $args{period} eq 'Weekly'; |
385
|
2
|
100
|
|
|
|
12
|
$target_period_time = "Open" if $args{period} eq 'Event'; |
386
|
|
|
|
|
|
|
|
387
|
2
|
50
|
|
|
|
4
|
croak |
388
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_epic(): invalid period; must be one of: daily, weekly, intraday, event\n" |
389
|
|
|
|
|
|
|
if !$target_period_time; |
390
|
|
|
|
|
|
|
|
391
|
2
|
|
|
|
|
4
|
$market_id = $self->_get_market_id( |
392
|
|
|
|
|
|
|
name => $target_period_time, |
393
|
|
|
|
|
|
|
market_list_ref => $market_list_ref, |
394
|
|
|
|
|
|
|
accept_match => 1 |
395
|
|
|
|
|
|
|
); |
396
|
|
|
|
|
|
|
|
397
|
2
|
50
|
|
|
|
5
|
return undef unless $market_id; |
398
|
|
|
|
|
|
|
|
399
|
2
|
|
|
|
|
6
|
$market_list_ref = $self->_get( $self->{base_url}.MARKET_LIST_URL . "/$market_id" ); |
400
|
|
|
|
|
|
|
|
401
|
2
|
50
|
|
|
|
6
|
die |
402
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_epic(): failed to retrieve the market list from the exchange for market $market_id\n" |
403
|
|
|
|
|
|
|
if !$market_list_ref; |
404
|
|
|
|
|
|
|
|
405
|
2
|
|
|
|
|
2
|
my $epic; |
406
|
2
|
|
|
|
|
3
|
foreach my $market ( @{ $market_list_ref->{'markets'} } ) { |
|
2
|
|
|
|
|
3
|
|
407
|
2
|
100
|
|
|
|
11
|
$args{time} = uc( $args{time} ) if exists $args{time}; |
408
|
2
|
100
|
|
|
|
5
|
$args{time} = "" if !exists $args{time}; |
409
|
2
|
50
|
|
|
|
36
|
if ( $market->{instrumentName} =~ /$args{strike}( \($args{time}\))?$/ ) |
410
|
|
|
|
|
|
|
{ |
411
|
2
|
|
|
|
|
3
|
$epic = $market->{epic}; |
412
|
2
|
|
|
|
|
2
|
last; |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
2
|
|
|
|
|
11
|
return $epic; |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
sub get_market_instruments { |
421
|
|
|
|
|
|
|
|
422
|
3
|
|
|
3
|
1
|
425
|
my $self = shift; |
423
|
3
|
|
|
|
|
5
|
my %args = @_; |
424
|
|
|
|
|
|
|
|
425
|
3
|
50
|
|
|
|
6
|
croak "ERROR: Finance::Nadex::get_market_instruments(): must be logged in\n" |
426
|
|
|
|
|
|
|
unless $self->logged_in; |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
croak |
429
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_market_instruments(): must provide market as named argument 'name'\n" |
430
|
3
|
100
|
|
|
|
14
|
unless exists $args{name}; |
431
|
|
|
|
|
|
|
croak |
432
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_market_instruments(): invalid market name\n" |
433
|
2
|
50
|
|
|
|
5
|
unless $args{name}; |
434
|
|
|
|
|
|
|
|
435
|
2
|
|
|
|
|
8
|
my $market_list_ref = $self->_get($self->{base_url}.MARKET_LIST_URL); |
436
|
|
|
|
|
|
|
|
437
|
2
|
50
|
|
|
|
6
|
die |
438
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_market_instruments(): failed to retrieve the market list from the exchange\n" |
439
|
|
|
|
|
|
|
if !$market_list_ref; |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
my $market_id = $self->_get_market_id( |
442
|
|
|
|
|
|
|
name => $args{name}, |
443
|
2
|
|
|
|
|
12
|
market_list_ref => $market_list_ref |
444
|
|
|
|
|
|
|
); |
445
|
|
|
|
|
|
|
|
446
|
2
|
50
|
|
|
|
5
|
return unless $market_id; |
447
|
|
|
|
|
|
|
|
448
|
2
|
|
|
|
|
11
|
$market_list_ref = $self->_get( $self->{base_url}.MARKET_LIST_URL . '/' . $market_id ); |
449
|
|
|
|
|
|
|
|
450
|
2
|
50
|
|
|
|
8
|
die |
451
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_market_instruments(): failed to retrieve the market list from the exchange for market $market_id\n" |
452
|
|
|
|
|
|
|
if !$market_list_ref; |
453
|
|
|
|
|
|
|
|
454
|
2
|
|
|
|
|
4
|
my @instruments; |
455
|
2
|
|
|
|
|
2
|
foreach my $market ( @{ $market_list_ref->{'hierarchy'} } ) { |
|
2
|
|
|
|
|
5
|
|
456
|
2
|
|
|
|
|
4
|
push( @instruments, $market->{name} ); |
457
|
|
|
|
|
|
|
} |
458
|
2
|
|
|
|
|
8
|
return @instruments; |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
sub get_markets { |
462
|
|
|
|
|
|
|
|
463
|
2
|
|
|
2
|
1
|
6
|
my $self = shift; |
464
|
|
|
|
|
|
|
|
465
|
2
|
50
|
|
|
|
4
|
croak "ERROR: Finance::Nadex::get_markets(): must be logged in\n" |
466
|
|
|
|
|
|
|
unless $self->logged_in; |
467
|
|
|
|
|
|
|
|
468
|
2
|
|
|
|
|
9
|
my $market_list_ref = $self->_get($self->{base_url}.MARKET_LIST_URL); |
469
|
|
|
|
|
|
|
|
470
|
2
|
50
|
|
|
|
7
|
die |
471
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_markets(): failed to retrieve the market list from the exchange\n" |
472
|
|
|
|
|
|
|
if !$market_list_ref; |
473
|
|
|
|
|
|
|
|
474
|
2
|
|
|
|
|
2
|
my @markets; |
475
|
2
|
|
|
|
|
3
|
foreach my $market ( @{ $market_list_ref->{'hierarchy'} } ) { |
|
2
|
|
|
|
|
4
|
|
476
|
2
|
|
|
|
|
5
|
push( @markets, $market->{name} ); |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
2
|
|
|
|
|
9
|
return @markets; |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
sub get_quote { |
483
|
|
|
|
|
|
|
|
484
|
2
|
|
|
2
|
1
|
613
|
my $self = shift; |
485
|
2
|
|
|
|
|
5
|
my %args = @_; |
486
|
|
|
|
|
|
|
|
487
|
2
|
50
|
|
|
|
2
|
croak "ERROR: Finance::Nadex::get_quote(): must be logged in\n" |
488
|
|
|
|
|
|
|
unless $self->logged_in; |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
croak |
491
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_quote(): must specify a named argument 'instrument'\n" |
492
|
2
|
100
|
|
|
|
23
|
unless exists $args{instrument}; |
493
|
|
|
|
|
|
|
croak "ERROR: Finance::Nadex::get_quote(): invalid instrument\n" |
494
|
1
|
50
|
|
|
|
3
|
unless $args{instrument}; |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
$args{instrument} = $index_name{ $args{instrument} } |
497
|
1
|
50
|
|
|
|
3
|
if exists $index_name{ $args{instrument} }; |
498
|
|
|
|
|
|
|
|
499
|
1
|
|
|
|
|
4
|
my @markets = $self->get_markets(); |
500
|
1
|
|
|
|
|
1
|
foreach my $market (@markets) { |
501
|
1
|
|
|
|
|
3
|
my @instruments = $self->get_market_instruments( name => $market ); |
502
|
1
|
|
|
|
|
1
|
foreach my $instrument (@instruments) { |
503
|
1
|
50
|
|
|
|
8
|
if ( $instrument eq $args{instrument} ) { |
504
|
1
|
|
|
|
|
3
|
my ($series) = $self->get_time_series( |
505
|
|
|
|
|
|
|
market => $market, |
506
|
|
|
|
|
|
|
instrument => $instrument |
507
|
|
|
|
|
|
|
); |
508
|
1
|
|
|
|
|
4
|
my ($contract) = $self->get_contracts( |
509
|
|
|
|
|
|
|
market => $market, |
510
|
|
|
|
|
|
|
instrument => $instrument, |
511
|
|
|
|
|
|
|
series => $series |
512
|
|
|
|
|
|
|
); |
513
|
1
|
|
|
|
|
11
|
my $epic_url = $self->{base_url}.EPIC_URL . '/' . $contract->epic; |
514
|
1
|
|
|
|
|
2
|
my $epic_ref = $self->_get($epic_url); |
515
|
|
|
|
|
|
|
|
516
|
1
|
50
|
|
|
|
4
|
return undef unless $epic_ref; |
517
|
|
|
|
|
|
|
|
518
|
1
|
50
|
|
|
|
1
|
die |
519
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_quote(): failed to retrieve the market list from the exchange for epic $args{epic}\n" |
520
|
|
|
|
|
|
|
if !$epic_ref; |
521
|
1
|
|
|
|
|
11
|
return $epic_ref->{instrument}->{underlyingIndicativePrice}; |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
sub get_time_series { |
528
|
|
|
|
|
|
|
|
529
|
5
|
|
|
5
|
1
|
1545
|
my $self = shift; |
530
|
5
|
|
|
|
|
12
|
my %args = @_; |
531
|
5
|
|
|
|
|
3
|
my $found; |
532
|
|
|
|
|
|
|
|
533
|
5
|
50
|
|
|
|
9
|
croak "ERROR: Finance::Nadex::get_time_series(): must be logged in\n" |
534
|
|
|
|
|
|
|
unless $self->logged_in; |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
$args{instrument} = $index_name{ $args{instrument} } |
537
|
5
|
50
|
66
|
|
|
22
|
if exists $args{instrument} && $index_name{ $args{instrument} }; |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
croak |
540
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_time_series(): must specify a named argument 'market'\n" |
541
|
5
|
100
|
|
|
|
22
|
unless exists $args{market}; |
542
|
|
|
|
|
|
|
croak |
543
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_time_series(): must specify a named argument 'instrument'\n" |
544
|
3
|
100
|
|
|
|
14
|
unless exists $args{instrument}; |
545
|
|
|
|
|
|
|
|
546
|
2
|
|
|
|
|
8
|
my $market_list_ref = $self->_get($self->{base_url}.MARKET_LIST_URL); |
547
|
|
|
|
|
|
|
|
548
|
2
|
50
|
|
|
|
5
|
die |
549
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_time_series(): failed to retrieve the market list from the exchange\n" |
550
|
|
|
|
|
|
|
if !$market_list_ref; |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
my $market_id = $self->_get_market_id( |
553
|
|
|
|
|
|
|
name => $args{market}, |
554
|
2
|
|
|
|
|
6
|
market_list_ref => $market_list_ref |
555
|
|
|
|
|
|
|
); |
556
|
|
|
|
|
|
|
|
557
|
2
|
50
|
|
|
|
13
|
return unless $market_id; |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
my $instruments_list_ref = |
560
|
2
|
|
|
|
|
8
|
$self->_get( $self->{base_url}.MARKET_LIST_URL . '/' . $market_id ); |
561
|
|
|
|
|
|
|
|
562
|
2
|
50
|
|
|
|
7
|
die |
563
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_time_series(): failed to retrieve the market list from the exchange for market $market_id\n" |
564
|
|
|
|
|
|
|
if !$market_list_ref; |
565
|
|
|
|
|
|
|
|
566
|
2
|
|
|
|
|
2
|
my $instrument_id; |
567
|
2
|
|
|
|
|
2
|
foreach my $instrument ( @{ $instruments_list_ref->{'hierarchy'} } ) { |
|
2
|
|
|
|
|
6
|
|
568
|
|
|
|
|
|
|
$instrument_id = $instrument->{id} |
569
|
2
|
50
|
|
|
|
8
|
if $instrument->{name} eq $args{instrument}; |
570
|
|
|
|
|
|
|
$instrument_id = $instrument->{id} |
571
|
|
|
|
|
|
|
if $instrument->{name} eq 'Forex' |
572
|
2
|
50
|
33
|
|
|
7
|
&& $args{market} eq '5 Minute Binaries'; |
573
|
|
|
|
|
|
|
$instrument_id = $instrument->{id} |
574
|
|
|
|
|
|
|
if $instrument->{name} eq 'Indices' |
575
|
2
|
50
|
33
|
|
|
9
|
&& $args{market} eq '20 Minute Binaries'; |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
|
578
|
2
|
50
|
|
|
|
4
|
return unless $instrument_id; |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
my $instrument_list_ref = |
581
|
2
|
|
|
|
|
9
|
$self->_get( $self->{base_url}.MARKET_LIST_URL . '/' . $instrument_id ); |
582
|
|
|
|
|
|
|
|
583
|
2
|
50
|
|
|
|
7
|
die |
584
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_time_series(): failed to retrieve the market list from the exchange for market $instrument_id\n" |
585
|
|
|
|
|
|
|
if !$market_list_ref; |
586
|
|
|
|
|
|
|
|
587
|
2
|
50
|
33
|
|
|
12
|
if ( $args{market} eq '5 Minute Binaries' |
588
|
|
|
|
|
|
|
|| $args{market} eq '20 Minute Binaries' ) |
589
|
|
|
|
|
|
|
{ |
590
|
0
|
|
|
|
|
0
|
my @instruments; |
591
|
0
|
|
|
|
|
0
|
foreach my $instrument ( @{ $instrument_list_ref->{'hierarchy'} } ) { |
|
0
|
|
|
|
|
0
|
|
592
|
|
|
|
|
|
|
$instrument_id = $instrument->{id} |
593
|
0
|
0
|
|
|
|
0
|
if $instrument->{name} eq $args{instrument}; |
594
|
|
|
|
|
|
|
} |
595
|
|
|
|
|
|
|
|
596
|
0
|
0
|
|
|
|
0
|
return unless $instrument_id; |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
$instrument_list_ref = |
599
|
0
|
|
|
|
|
0
|
$self->_get( $self->{base_url}.MARKET_LIST_URL . '/' . $instrument_id ); |
600
|
|
|
|
|
|
|
|
601
|
0
|
0
|
|
|
|
0
|
die |
602
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::get_time_series(): failed to retrieve the market list from the exchange for market $instrument_id\n" |
603
|
|
|
|
|
|
|
if !$market_list_ref; |
604
|
|
|
|
|
|
|
|
605
|
0
|
|
|
|
|
0
|
my @contracts; |
606
|
0
|
|
|
|
|
0
|
foreach my $contract ( @{ $instrument_list_ref->{'markets'} } ) { |
|
0
|
|
|
|
|
0
|
|
607
|
0
|
|
|
|
|
0
|
push( @contracts, Finance::Nadex::Contract::_new($contract) ); |
608
|
|
|
|
|
|
|
} |
609
|
0
|
|
|
|
|
0
|
return @contracts; |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
|
613
|
2
|
|
|
|
|
3
|
my @time_series; |
614
|
2
|
|
|
|
|
2
|
foreach my $series ( @{ $instrument_list_ref->{'hierarchy'} } ) { |
|
2
|
|
|
|
|
5
|
|
615
|
2
|
|
|
|
|
5
|
push( @time_series, $series->{name} ); |
616
|
|
|
|
|
|
|
} |
617
|
|
|
|
|
|
|
|
618
|
2
|
|
|
|
|
19
|
return @time_series; |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
sub logged_in { |
623
|
|
|
|
|
|
|
|
624
|
72
|
|
|
72
|
1
|
60
|
my $self = shift; |
625
|
|
|
|
|
|
|
|
626
|
72
|
100
|
|
|
|
280
|
return 0 unless $self->{security_token}; |
627
|
|
|
|
|
|
|
|
628
|
61
|
50
|
|
|
|
106
|
return 0 unless $self->{session_id}; |
629
|
|
|
|
|
|
|
|
630
|
61
|
|
|
|
|
115
|
return 1; |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
} |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
sub login { |
635
|
|
|
|
|
|
|
|
636
|
13
|
|
|
13
|
1
|
314
|
my $self = shift; |
637
|
13
|
|
|
|
|
32
|
my %args = @_; |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
croak |
640
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::login(): must specify a named argument 'username'\n" |
641
|
13
|
0
|
33
|
|
|
44
|
unless exists $args{username} || exists $self->{username}; |
642
|
|
|
|
|
|
|
croak |
643
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::login(): must specify a named argument 'password'\n" |
644
|
13
|
0
|
33
|
|
|
38
|
unless exists $args{password} || exists $self->{password}; |
645
|
|
|
|
|
|
|
|
646
|
13
|
50
|
|
|
|
42
|
$self->{username} = $args{username} if exists $args{username}; |
647
|
13
|
50
|
|
|
|
38
|
$self->{password} = $args{password} if exists $args{password}; |
648
|
|
|
|
|
|
|
|
649
|
13
|
|
|
|
|
32
|
my $login_url = $self->{base_url}.LOGIN_URL; |
650
|
|
|
|
|
|
|
|
651
|
13
|
|
|
|
|
35
|
my $login_content = qq~ |
652
|
|
|
|
|
|
|
{ |
653
|
|
|
|
|
|
|
"advertisingId" : "", |
654
|
|
|
|
|
|
|
"password": "$self->{password}", |
655
|
|
|
|
|
|
|
"username": "$self->{username}" |
656
|
|
|
|
|
|
|
}~; |
657
|
|
|
|
|
|
|
|
658
|
13
|
|
|
|
|
35
|
my $json_obj = $self->_post( $login_url, $login_content ); |
659
|
|
|
|
|
|
|
|
660
|
13
|
|
|
|
|
50
|
$self->{user_agent}->cookie_jar->scan( \&_get_session_id ); |
661
|
|
|
|
|
|
|
|
662
|
13
|
|
|
|
|
65
|
$self->{session_id} = $session_id; |
663
|
|
|
|
|
|
|
|
664
|
13
|
|
|
|
|
30
|
$self->{balance} = $json_obj->{accountInfo}->{available}; |
665
|
|
|
|
|
|
|
|
666
|
13
|
|
|
|
|
29
|
return $self->logged_in(); |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
} |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
sub new { |
671
|
|
|
|
|
|
|
|
672
|
25
|
|
|
25
|
1
|
870298
|
my $class = shift; |
673
|
25
|
|
|
|
|
43
|
my %args = @_; |
674
|
|
|
|
|
|
|
|
675
|
25
|
50
|
33
|
|
|
107
|
if (exists $args{platform} && $args{platform} eq 'demo') { |
676
|
0
|
|
|
|
|
0
|
$args{base_url} = 'https://demo-trade.nadex.com'; |
677
|
|
|
|
|
|
|
} else { |
678
|
25
|
|
|
|
|
226
|
$args{base_url} = 'https://trade.nadex.com'; |
679
|
|
|
|
|
|
|
} |
680
|
|
|
|
|
|
|
|
681
|
25
|
|
|
|
|
129
|
my $ua = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0 } ); |
682
|
|
|
|
|
|
|
|
683
|
25
|
|
|
|
|
4483
|
push @{ $ua->requests_redirectable }, 'POST', 'DELETE'; |
|
25
|
|
|
|
|
62
|
|
684
|
25
|
|
|
|
|
244
|
$ua->agent( |
685
|
|
|
|
|
|
|
"vendor=IG Group | applicationType=dxd | platform=Android | deviceType=generic | version=1.13.2" |
686
|
|
|
|
|
|
|
); |
687
|
25
|
|
|
|
|
884
|
$ua->cookie_jar( { autosave => 1, ignore_discard => 1 } ); |
688
|
|
|
|
|
|
|
|
689
|
25
|
|
|
|
|
6816
|
$args{user_agent} = $ua; |
690
|
|
|
|
|
|
|
|
691
|
25
|
|
|
|
|
57
|
bless \%args, __PACKAGE__; |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
} |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
sub retrieve_order { |
696
|
|
|
|
|
|
|
|
697
|
3
|
|
|
3
|
1
|
597
|
my $self = shift; |
698
|
3
|
|
|
|
|
5
|
my %args = @_; |
699
|
|
|
|
|
|
|
|
700
|
3
|
100
|
|
|
|
4
|
croak "ERROR: Finance::Nadex::retrieve_order(): must be logged in\n" |
701
|
|
|
|
|
|
|
unless $self->logged_in; |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
croak "ERROR: retrieve_order(): must specify a named argument 'id'\n" |
704
|
2
|
100
|
|
|
|
12
|
unless exists $args{id}; |
705
|
1
|
50
|
|
|
|
2
|
croak "ERROR: retrieve_order(): invalid id\n" unless $args{id}; |
706
|
|
|
|
|
|
|
|
707
|
1
|
|
|
|
|
2
|
my $order_id = $args{id}; |
708
|
|
|
|
|
|
|
|
709
|
1
|
|
|
|
|
3
|
my $retrieve_order_url = $self->{base_url}.RETRIEVE_ORDER_URL . '/' . $order_id; |
710
|
|
|
|
|
|
|
|
711
|
1
|
|
|
|
|
3
|
my $order_ref = $self->_get($retrieve_order_url); |
712
|
|
|
|
|
|
|
|
713
|
1
|
50
|
|
|
|
3
|
return undef unless $order_ref; |
714
|
|
|
|
|
|
|
|
715
|
1
|
|
|
|
|
4
|
return Finance::Nadex::Order::_new($order_ref); |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
} |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
sub retrieve_orders { |
720
|
|
|
|
|
|
|
|
721
|
2
|
|
|
2
|
1
|
79
|
my $self = shift; |
722
|
|
|
|
|
|
|
|
723
|
2
|
100
|
|
|
|
5
|
croak "ERROR: Finance::Nadex::retrieve_orders(): must be logged in\n" |
724
|
|
|
|
|
|
|
unless $self->logged_in(); |
725
|
|
|
|
|
|
|
|
726
|
1
|
|
|
|
|
3
|
my $retrieve_orders_url = $self->{base_url}.RETRIEVE_ORDERS_URL; |
727
|
|
|
|
|
|
|
|
728
|
1
|
|
|
|
|
2
|
my $order_list_ref = $self->_get($retrieve_orders_url); |
729
|
|
|
|
|
|
|
|
730
|
1
|
50
|
|
|
|
3
|
die |
731
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::retrieve_orders(): failed to retrieve the order list from the exchange \n" |
732
|
|
|
|
|
|
|
if !$order_list_ref; |
733
|
|
|
|
|
|
|
|
734
|
1
|
|
|
|
|
2
|
my $order_obj_list_ref = []; |
735
|
1
|
|
|
|
|
2
|
foreach my $order (@$order_list_ref) { |
736
|
1
|
|
|
|
|
9
|
push( @$order_obj_list_ref, Finance::Nadex::Order::_new($order) ); |
737
|
|
|
|
|
|
|
} |
738
|
|
|
|
|
|
|
|
739
|
1
|
|
|
|
|
8
|
return @$order_obj_list_ref; |
740
|
|
|
|
|
|
|
} |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
sub retrieve_position { |
743
|
|
|
|
|
|
|
|
744
|
3
|
|
|
3
|
1
|
483
|
my $self = shift; |
745
|
3
|
|
|
|
|
6
|
my %args = @_; |
746
|
|
|
|
|
|
|
|
747
|
3
|
100
|
|
|
|
6
|
croak "ERROR: Finance::Nadex::retrieve_position(): must be logged in\n" |
748
|
|
|
|
|
|
|
unless $self->logged_in; |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
croak |
751
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::retrieve_position(): must specify a named argument 'id'\n" |
752
|
2
|
100
|
|
|
|
12
|
unless exists $args{id}; |
753
|
|
|
|
|
|
|
croak "ERROR: Finance::Nadex::retrieve_position(): invalid id\n" |
754
|
1
|
50
|
|
|
|
4
|
unless $args{id}; |
755
|
|
|
|
|
|
|
|
756
|
1
|
|
|
|
|
2
|
my $position_id = $args{id}; |
757
|
|
|
|
|
|
|
|
758
|
1
|
|
|
|
|
4
|
my $retrieve_positions_url = $self->{base_url}.RETRIEVE_POSITION_URL . '/' . $position_id; |
759
|
|
|
|
|
|
|
|
760
|
1
|
|
|
|
|
4
|
my $position_ref = $self->_get($retrieve_positions_url); |
761
|
|
|
|
|
|
|
|
762
|
1
|
50
|
|
|
|
3
|
return undef unless $position_ref; |
763
|
|
|
|
|
|
|
|
764
|
1
|
|
|
|
|
3
|
return Finance::Nadex::Position::_new($position_ref); |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
} |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
sub retrieve_positions { |
769
|
|
|
|
|
|
|
|
770
|
2
|
|
|
2
|
1
|
60
|
my $self = shift; |
771
|
|
|
|
|
|
|
|
772
|
2
|
100
|
|
|
|
4
|
croak "ERROR: Finance::Nadex::retrieve_positions(): must be logged in\n" |
773
|
|
|
|
|
|
|
unless $self->logged_in; |
774
|
|
|
|
|
|
|
|
775
|
1
|
|
|
|
|
5
|
my $retrieve_positions_url = $self->{base_url}.RETRIEVE_POSITIONS_URL; |
776
|
|
|
|
|
|
|
|
777
|
1
|
|
|
|
|
3
|
my $position_list_ref = $self->_get($retrieve_positions_url); |
778
|
|
|
|
|
|
|
|
779
|
1
|
50
|
|
|
|
3
|
die |
780
|
|
|
|
|
|
|
"ERROR: Finance::Nadex::retrieve_positions(): failed to retrieve the position list from the exchange\n" |
781
|
|
|
|
|
|
|
if !$position_list_ref; |
782
|
|
|
|
|
|
|
|
783
|
1
|
|
|
|
|
2
|
my $position_obj_list_ref = []; |
784
|
1
|
|
|
|
|
1
|
foreach my $position (@$position_list_ref) { |
785
|
1
|
|
|
|
|
10
|
push( @$position_obj_list_ref, |
786
|
|
|
|
|
|
|
Finance::Nadex::Position::_new($position) ); |
787
|
|
|
|
|
|
|
} |
788
|
|
|
|
|
|
|
|
789
|
1
|
|
|
|
|
9
|
return @$position_obj_list_ref; |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
} |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
sub _delete { |
794
|
|
|
|
|
|
|
|
795
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
796
|
0
|
|
|
|
|
0
|
my $url = shift; |
797
|
0
|
|
|
|
|
0
|
my $delete_content = shift; |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
my $response = $self->{user_agent}->delete( |
800
|
|
|
|
|
|
|
$url, |
801
|
|
|
|
|
|
|
clientApplication => 'dxd', |
802
|
|
|
|
|
|
|
clientPlatform => 'ANDROID_PHONE', |
803
|
|
|
|
|
|
|
clientVersion => '1.13.2', |
804
|
|
|
|
|
|
|
Accept => 'application/json; charset=UTF-8', |
805
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
806
|
|
|
|
|
|
|
'Accept-Encoding' => 'text/html', |
807
|
|
|
|
|
|
|
Host => 'www.nadex.com', |
808
|
|
|
|
|
|
|
Connection => 'Keep-Alive', |
809
|
|
|
|
|
|
|
'X-SECURITY-TOKEN' => $self->{security_token}, |
810
|
0
|
|
|
|
|
0
|
Content => $delete_content |
811
|
|
|
|
|
|
|
); |
812
|
|
|
|
|
|
|
|
813
|
0
|
0
|
|
|
|
0
|
$self->{security_token} = $response->header('X-SECURITY-TOKEN') |
814
|
|
|
|
|
|
|
if $response->header('X-SECURITY-TOKEN'); |
815
|
0
|
|
|
|
|
0
|
$self->{code} = $response->code; |
816
|
0
|
|
|
|
|
0
|
$self->{content} = $response->content; |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
my $delete_response = |
819
|
0
|
|
|
|
|
0
|
eval { JSON->new->utf8->decode( $response->content ); }; |
|
0
|
|
|
|
|
0
|
|
820
|
|
|
|
|
|
|
|
821
|
0
|
|
|
|
|
0
|
return $delete_response; |
822
|
|
|
|
|
|
|
} |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
sub _get { |
825
|
|
|
|
|
|
|
|
826
|
40
|
|
|
40
|
|
45
|
my $self = shift; |
827
|
40
|
|
|
|
|
38
|
my $url = shift; |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
my $response = $self->{user_agent}->get( |
830
|
|
|
|
|
|
|
$url, |
831
|
|
|
|
|
|
|
clientApplication => 'dxd', |
832
|
|
|
|
|
|
|
clientPlatform => 'ANDROID_PHONE', |
833
|
|
|
|
|
|
|
clientVersion => '1.13.2', |
834
|
|
|
|
|
|
|
Accept => 'application/json; charset=UTF-8', |
835
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
836
|
|
|
|
|
|
|
'Accept-Encoding' => 'text/html', |
837
|
|
|
|
|
|
|
Host => 'www.nadex.com', |
838
|
|
|
|
|
|
|
Connection => 'Keep-Alive', |
839
|
|
|
|
|
|
|
'X-SECURITY-TOKEN' => $self->{security_token}, |
840
|
40
|
|
|
|
|
159
|
Content => "" |
841
|
|
|
|
|
|
|
); |
842
|
|
|
|
|
|
|
|
843
|
40
|
50
|
|
|
|
49626
|
$self->{security_token} = $response->header('X-SECURITY-TOKEN') |
844
|
|
|
|
|
|
|
if $response->header('X-SECURITY-TOKEN'); |
845
|
40
|
|
|
|
|
1798
|
$self->{code} = $response->code; |
846
|
40
|
|
|
|
|
225
|
$self->{content} = $response->content; |
847
|
|
|
|
|
|
|
|
848
|
40
|
|
|
|
|
259
|
my $json = eval { JSON->new->utf8->decode( $response->content ); }; |
|
40
|
|
|
|
|
212
|
|
849
|
|
|
|
|
|
|
|
850
|
40
|
|
|
|
|
505
|
return $json; |
851
|
|
|
|
|
|
|
} |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
sub _post { |
854
|
|
|
|
|
|
|
|
855
|
17
|
|
|
17
|
|
22
|
my $self = shift; |
856
|
17
|
|
|
|
|
19
|
my $url = shift; |
857
|
17
|
|
|
|
|
19
|
my $post_content = shift; |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
my $response = $self->{user_agent}->post( |
860
|
|
|
|
|
|
|
$url, |
861
|
|
|
|
|
|
|
clientApplication => 'dxd', |
862
|
|
|
|
|
|
|
clientPlatform => 'APPLE_TABLET', |
863
|
|
|
|
|
|
|
version => '1', |
864
|
|
|
|
|
|
|
Accept => 'application/json; charset=UTF-8', |
865
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
866
|
|
|
|
|
|
|
'Accept-Encoding' => 'text/html', |
867
|
|
|
|
|
|
|
'X-IG-DEVICE_MANUFACTURER' => 'LGE', |
868
|
|
|
|
|
|
|
'X-IG-DEVICE_MODEL' => 'Nexus 4', |
869
|
|
|
|
|
|
|
'X-IG-DEVICE_OS_NAME' => 'Android', |
870
|
|
|
|
|
|
|
'X-IG-DEVICE_OS_VERSION' => '5.0.1', |
871
|
|
|
|
|
|
|
'X-IG-DEVICE_LOCALE' => 'en_US', |
872
|
|
|
|
|
|
|
'X-IG-DEVICE_CARRIER' => 'AT&T', |
873
|
|
|
|
|
|
|
'X-DEVICE-USER-AGENT' => |
874
|
|
|
|
|
|
|
'vendor=Minsk | applicationType=NADEX_PROMO | platform=Android | deviceType=phone | version=5.0.5', |
875
|
|
|
|
|
|
|
Host => 'www.nadex.com', |
876
|
|
|
|
|
|
|
Connection => 'Keep-Alive', |
877
|
|
|
|
|
|
|
'X-SECURITY-TOKEN' => $self->{security_token}, |
878
|
17
|
|
|
|
|
114
|
Content => $post_content |
879
|
|
|
|
|
|
|
); |
880
|
|
|
|
|
|
|
|
881
|
17
|
100
|
|
|
|
114334
|
$self->{security_token} = $response->header('X-SECURITY-TOKEN') |
882
|
|
|
|
|
|
|
if $response->header('X-SECURITY-TOKEN'); |
883
|
17
|
|
|
|
|
773
|
$self->{code} = $response->code; |
884
|
17
|
|
100
|
|
|
112
|
$self->{content} = $response->content || undef; |
885
|
|
|
|
|
|
|
|
886
|
17
|
|
|
|
|
163
|
my $json_obj = eval { JSON->new->utf8->decode( $response->content ); }; |
|
17
|
|
|
|
|
211
|
|
887
|
|
|
|
|
|
|
|
888
|
17
|
|
|
|
|
371
|
return $json_obj; |
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
} |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
sub _get_market_id { |
893
|
|
|
|
|
|
|
|
894
|
12
|
|
|
12
|
|
13
|
my $self = shift; |
895
|
12
|
|
|
|
|
28
|
my %args = @_; |
896
|
12
|
|
|
|
|
12
|
my $market_id; |
897
|
|
|
|
|
|
|
|
898
|
12
|
|
|
|
|
9
|
foreach my $market ( @{ $args{market_list_ref}->{'hierarchy'} } ) { |
|
12
|
|
|
|
|
31
|
|
899
|
14
|
100
|
66
|
|
|
42
|
if ( !exists $args{accept_match} || $args{accept_match} == 0 ) { |
900
|
12
|
100
|
|
|
|
26
|
if ( $market->{name} eq $args{name} ) { |
901
|
10
|
|
|
|
|
16
|
$market_id = $market->{id}; |
902
|
|
|
|
|
|
|
} |
903
|
|
|
|
|
|
|
} |
904
|
|
|
|
|
|
|
else { |
905
|
2
|
50
|
66
|
|
|
31
|
if ( $market->{name} =~ /$args{name}/ |
906
|
|
|
|
|
|
|
|| $market->{name} eq $args{name} ) |
907
|
|
|
|
|
|
|
{ |
908
|
2
|
|
|
|
|
5
|
$market_id = $market->{id}; |
909
|
|
|
|
|
|
|
} |
910
|
|
|
|
|
|
|
} |
911
|
|
|
|
|
|
|
} |
912
|
|
|
|
|
|
|
|
913
|
12
|
|
50
|
|
|
33
|
return $market_id || undef; |
914
|
|
|
|
|
|
|
} |
915
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
sub _get_session_id { |
917
|
|
|
|
|
|
|
|
918
|
12
|
|
|
12
|
|
222
|
my $key = $_[1]; |
919
|
12
|
|
|
|
|
15
|
my $val = $_[2]; |
920
|
|
|
|
|
|
|
|
921
|
12
|
50
|
|
|
|
70
|
$session_id = $val if $key =~ /JSESSIONID/; |
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
} |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
sub _is_valid_price { |
926
|
|
|
|
|
|
|
|
927
|
7
|
|
|
7
|
|
5
|
my $self = shift; |
928
|
7
|
|
|
|
|
4
|
my $price = shift; |
929
|
7
|
|
|
|
|
6
|
my $type = shift; |
930
|
|
|
|
|
|
|
|
931
|
7
|
100
|
|
|
|
34
|
return 0 if $price =~ /-|\+/; |
932
|
|
|
|
|
|
|
|
933
|
6
|
100
|
|
|
|
10
|
if ( $type eq 'binary' ) { |
934
|
5
|
50
|
|
|
|
16
|
return 0 if $price !~ /^(\d+\.\d{1,2}|\.\d{1,2}|\d+)$/; |
935
|
|
|
|
|
|
|
|
936
|
5
|
50
|
|
|
|
11
|
if ( $price =~ /\.(\d+)/ ) { |
937
|
5
|
0
|
33
|
|
|
17
|
return 0 if $1 != 0 && $1 != 25 && $1 != 50 && $1 != 75; |
|
|
|
33
|
|
|
|
|
|
|
|
0
|
|
|
|
|
938
|
|
|
|
|
|
|
} |
939
|
|
|
|
|
|
|
} |
940
|
|
|
|
|
|
|
|
941
|
6
|
100
|
|
|
|
7
|
if ( $type eq 'spread' ) { |
942
|
1
|
50
|
|
|
|
6
|
return 0 if $price !~ /^(\d+|\d+\.\d{1,4})$/; |
943
|
|
|
|
|
|
|
} |
944
|
|
|
|
|
|
|
|
945
|
6
|
|
|
|
|
11
|
return 1; |
946
|
|
|
|
|
|
|
} |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
sub _is_valid_direction { |
949
|
|
|
|
|
|
|
|
950
|
6
|
|
|
6
|
|
5
|
my $self = shift; |
951
|
6
|
|
|
|
|
4
|
my $direction = shift; |
952
|
|
|
|
|
|
|
|
953
|
6
|
|
|
|
|
19
|
my %valid = qw( - 1 + 1 buy 1 sell 1); |
954
|
|
|
|
|
|
|
|
955
|
6
|
100
|
|
|
|
32
|
return exists $valid{$direction} ? 1 : 0; |
956
|
|
|
|
|
|
|
} |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
sub _is_valid_size { |
959
|
|
|
|
|
|
|
|
960
|
5
|
|
|
5
|
|
4
|
my $self = shift; |
961
|
5
|
|
|
|
|
5
|
my $size = shift; |
962
|
|
|
|
|
|
|
|
963
|
5
|
100
|
|
|
|
23
|
return 0 if $size !~ /^\d+$/; |
964
|
|
|
|
|
|
|
|
965
|
4
|
50
|
|
|
|
9
|
return 0 if $size == 0; |
966
|
|
|
|
|
|
|
|
967
|
4
|
|
|
|
|
6
|
return 1; |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
} |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
=head1 NAME |
972
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
Finance::Nadex - Interact with the North American Derivatives Exchange |
974
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
=head1 VERSION |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
Version 0.03 |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
=head1 SYNOPSIS |
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
Easily create orders, cancel orders, retrieve orders and retrieve positions on the North American Derivatives Exchange |
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
use Finance::Nadex; |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
my $client = Finance::Nadex->new(); |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
# must login to perform any actions, including simply querying market info |
989
|
|
|
|
|
|
|
$client->login(username => 'yourusername', password => 'yourpassword'); |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
# get the available balance in the account |
992
|
|
|
|
|
|
|
my $balance = $client->balance(); |
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
# get a quote for GBP/USD |
995
|
|
|
|
|
|
|
my $quote = $client->get_quote('instrument' => 'GBP/USD'); |
996
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
# $quote may now be (for example), 1.5010 |
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
# retrieve the epic (Nadex-assigned contract unique identifier) for the |
1000
|
|
|
|
|
|
|
# Daily, 3pm, GBP/USD > 1.5120 contract |
1001
|
|
|
|
|
|
|
my $epic = $client->get_epic( period => 'Daily', market => 'Forex (Binaries)', time => '3pm', instrument => 'GBP/USD', strike => '1.5120'); |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
# suppose $epic now contains 'NB.D.GBP-USD.OPT-23-17-23Jan15.IP'; |
1004
|
|
|
|
|
|
|
# create an order to buy 3 of those contracts for $34.50 each |
1005
|
|
|
|
|
|
|
my $order_id = $client->create_order( price => '34.50', direction => 'buy', epic => $epic, size => 3 ); |
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
# check the status of the order using the order id returned by the exchange; |
1008
|
|
|
|
|
|
|
# this call will return undef if the order doesn't exist; the order may not exist |
1009
|
|
|
|
|
|
|
# because the order was rejected by the exchange or because it was filled immediately |
1010
|
|
|
|
|
|
|
# and therefore is no longer a working order |
1011
|
|
|
|
|
|
|
my $order = $client->retrieve_order( id => $order_id ); |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
# let's assume the order was created and is still a working (or pending) order; |
1014
|
|
|
|
|
|
|
# get the details of the order using the accessor methods provided by Finance::Nadex::Order |
1015
|
|
|
|
|
|
|
print join(" ", $order->direction, $order->size, $order->contract, $order->price), "\n"; |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
# suppose the order has now been filled; this means we have one open position; |
1018
|
|
|
|
|
|
|
# get the open positions |
1019
|
|
|
|
|
|
|
my @positions = $client->retrieve_positions(); |
1020
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
# @positions now has 1 element which is a Finance::Nadex::Position; get its details |
1022
|
|
|
|
|
|
|
print join(" ", $positions[0]->id, $positions[0]->direction(), $positions[0]->size(), $positions[0]->contract(), $positions[0]->price), "\n"; |
1023
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
# get the current best bid (the price at which we could sell the contract back immediately) |
1025
|
|
|
|
|
|
|
my $bid = $positions[0]->bid(); |
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
# suppose $bid is now $64.50 (we bought at $34.50, so we have a profit of $30); |
1028
|
|
|
|
|
|
|
# sell to close the position at $64.50 |
1029
|
|
|
|
|
|
|
my $sell_to_close_order_id = $client->create_order( price => $bid, direction => 'sell', epic => $positions[0]->epic, size => $positions[0]->size() ); |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
# get all the time series (trading periods for contracts) currently |
1032
|
|
|
|
|
|
|
# available for GBP/USD binaries; the list of currently available markets |
1033
|
|
|
|
|
|
|
# can be obtained via a call to get_markets() |
1034
|
|
|
|
|
|
|
my @series = $client->get_time_series( market => 'Forex (Binaries)', instrument => 'GBP/USD' ); |
1035
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
# elements of @series are simply strings, such as '2pm-4pm' or 'Daily (3pm)'; |
1037
|
|
|
|
|
|
|
# suppose one of the elements of series is '8pm-10pm'; get a list of |
1038
|
|
|
|
|
|
|
# contracts available for trading within that market |
1039
|
|
|
|
|
|
|
my @contracts = $client->get_contracts( market => 'Forex (Binaries)', instrument => 'GBP/USD', series => 'Daily (3pm)' ); |
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
# @contracts now has a list in which each element is a Finance::Nadex::Contract; get |
1042
|
|
|
|
|
|
|
# the details of the available contracts using the accessors of Finance::Nadex::Contract |
1043
|
|
|
|
|
|
|
# including the current best bid and offer available on the exchange |
1044
|
|
|
|
|
|
|
foreach my $contract (@contracts) { |
1045
|
|
|
|
|
|
|
print join(" ", $contract->epic(), $contract->contract(), $contract->expirydate(), $contract->bid(), $contract->offer()); |
1046
|
|
|
|
|
|
|
} |
1047
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
# cancel any remaining open orders |
1050
|
|
|
|
|
|
|
$client->cancel_all_orders(); |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
=head1 SUBROUTINES/METHODS |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
=head2 balance |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
Retrieves the available account balance |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
balance() |
1060
|
|
|
|
|
|
|
|
1061
|
|
|
|
|
|
|
Returns a number representing the available account balance |
1062
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
=head2 cancel_all_orders |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
Cancels all pending orders |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
cancel_all_orders() |
1068
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
Returns nothing |
1070
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
=head2 cancel_order |
1072
|
|
|
|
|
|
|
|
1073
|
|
|
|
|
|
|
Cancels the order with the specified order id |
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
cancel_order( id => 'NZ1234FGQ4AFFOPA12Z' ) |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
Returns the reference id created by the exchange for the cancelled order |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
=head2 create_order |
1080
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
Creates an order on the exchange with the specified parameters |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
create_order( price => '34.50', direction => 'buy', epic => 'NB.D.GBP-USD.OPT-23-17-23Jan15.IP', size => 2 ) |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
price : the amount at which to buy or sell; the decimal portion of the number, if provided, must be .50 or .00 for binaries |
1086
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
direction : one of 'buy', 'sell', '+', '-' |
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
size : the number of contracts to buy or sell |
1090
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
epic : the unique identifier for the contract to be bought or sold |
1092
|
|
|
|
|
|
|
|
1093
|
|
|
|
|
|
|
Returns: the order id created by the exchange to identify the order |
1094
|
|
|
|
|
|
|
|
1095
|
|
|
|
|
|
|
Note: '+' is an alias for 'buy'; '-' is an alias for 'sell'; the get_epic() method can be used to get the identifier for the contract of interest |
1096
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
=head2 get_contract |
1098
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
Retrieves the contract specified by an epic |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
get_contract( epic => 'NB.D.GBP-USD.OPT-23-17-23Jan15.IP' ) |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
epic : the unique identifier created by the exchange for the contract |
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
Returns a L instance for the specified epic |
1106
|
|
|
|
|
|
|
|
1107
|
|
|
|
|
|
|
=head2 get_contracts |
1108
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
Retrieves all the contracts available for trading within the given time series for the specified market and instrument |
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
get_contracts( market => 'Forex (Binaries)', instrument => 'GBP/USD', series => 'Daily (3pm)' ) |
1112
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
market : the name of the market for which the contracts are to be retrieved |
1114
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
instrument : the name of the instrument within the market for which contracts are to be retrieved; the |
1116
|
|
|
|
|
|
|
instrument specified must be one of the instruments currently available for trading on the exchange |
1117
|
|
|
|
|
|
|
for the provided market; the list of valid instruments can be obtained via get_market_instruments() |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
Returns a list in which each element is a L instance for each contract in the specified |
1120
|
|
|
|
|
|
|
time series |
1121
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
=head2 get_epic |
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
Retrieves the epic(unique identifier) for a contract with the specified parameters |
1125
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
get_epic(period => 'daily', strike => '1.5080', time => '3pm', instrument => 'GBP/USD', market => 'Forex (Binaries)') |
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
retrieves the epic with the specified parameters |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
period : specifies the frequency or period of the contract being searched for; one of 'daily', 'intraday', 'weekly', or 'event' |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
time : specifies the time at which the contract being searched for expires (not required when retrieving an event epic) |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
instrument : the asset type from which the contract being searched for derives value; an index, currency, or commodity |
1135
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
market : the market in which the specified contract exists (e.g. 'Forex (Binaries)', 'Indices (Binaries)'); must be one of markets returned by get_markets() |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
strike : the level of the underlying asset for the desired contract (e.g. 1.5010) |
1139
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
Returns the unique identifier of the contract |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
=head2 get_market_instruments |
1143
|
|
|
|
|
|
|
|
1144
|
|
|
|
|
|
|
Retrieves the list of instruments associated with the specified market |
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
get_market_instruments( name => 'Forex (Binaries)' ) |
1147
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
name : the name of the market for which instruments are to be retrieved; this must match one of the names returned by get_markets() |
1149
|
|
|
|
|
|
|
|
1150
|
|
|
|
|
|
|
Returns a list in which each element is a string containing the name of an instrument available for trading in the markets |
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
=head2 get_markets |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
Retrieves the list of available markets on the exchange; the list of market names returned can be used in a call to get_market_instruments() to get further |
1156
|
|
|
|
|
|
|
information about the market |
1157
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
get_markets() |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
Returns a list in which each element is a string containing the name of a market in which instruments are traded |
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
=head2 get_quote |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
Retrieves the current price level of the instrument specified as reported by the exchange (the Indicative Price) |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
get_quote( instrument => 'GBP/USD' ); |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
instrument : the name of the instrument within the market for which a quote is to be retrieved; the |
1169
|
|
|
|
|
|
|
instrument specified must be one of the instruments currently available for trading |
1170
|
|
|
|
|
|
|
on the exchange for the provided market; the list of valid instruments can be obtained |
1171
|
|
|
|
|
|
|
via get_market_instruments() |
1172
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
Returns the current price level or undef if it cannot be obtained |
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
=head2 get_time_series |
1176
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
Retrieves the contract periods available for trading in the specified market for the given instrument |
1178
|
|
|
|
|
|
|
|
1179
|
|
|
|
|
|
|
get_time_series( market => 'Forex (Binaries)', instrument => 'AUD/USD' ) |
1180
|
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
market : the name of the market for which a time series is to be retrieved |
1182
|
|
|
|
|
|
|
|
1183
|
|
|
|
|
|
|
instrument : the name of the instrument within the market for which a time series is to be retrieved; the |
1184
|
|
|
|
|
|
|
instrument specified must be one of the instruments currently available for trading on the exchange |
1185
|
|
|
|
|
|
|
for the provided market; the list of valid instruments can be obtained via get_market_instruments() |
1186
|
|
|
|
|
|
|
|
1187
|
|
|
|
|
|
|
In the case of the '5 Minute Binaries' and '20 Minute Binaries' markets, returns a list in which each element is |
1188
|
|
|
|
|
|
|
a L representing each contract available in the series; for all other markets, returns |
1189
|
|
|
|
|
|
|
a string containing the name of a time series for the given market and instrument; |
1190
|
|
|
|
|
|
|
a time series designates the period during which the contract is available for trading including the expiration time |
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
=head2 login |
1193
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
Submits the specified username and password to the exchange for authorization |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
login( username => 'someusername', password => 'somepassword' ); |
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
username : the username of the account |
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
password : the password of the account |
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
Returns a true value on successful login; otherwise returns a false value |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
=head2 logged_in |
1205
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
Reports whether login was previously attempted and succeeded |
1207
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
logged_in() |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
Returns true if login was previously attempted and succeded; otherwise returns false |
1211
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
=head2 new |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
Creates an instance of a Finance::Nadex object |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
new() |
1217
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
Returns a reference to a Finance::Nadex instance |
1219
|
|
|
|
|
|
|
|
1220
|
|
|
|
|
|
|
=head2 retrieve_order |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
Gets the details of the pending order with the specified order id |
1223
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
retrieve_order( id => 'NZ1234FGQ4AFFOPA12Z' ) |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
Returns an instance of L |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
=head2 retrieve_orders |
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
Gets the details of all pending orders |
1231
|
|
|
|
|
|
|
|
1232
|
|
|
|
|
|
|
retrieve_orders() |
1233
|
|
|
|
|
|
|
|
1234
|
|
|
|
|
|
|
Returns a list in which each element is a L |
1235
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
=head2 retrieve_position |
1237
|
|
|
|
|
|
|
|
1238
|
|
|
|
|
|
|
Retrieves the details of an individual open position |
1239
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
retrieve_position ( id => 'NA12DZ45BNVA12A9BQZ' ) |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
Returns a L object corresponding to the specified position id |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
=head2 retrieve_positions |
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
Retrieves the details of all the open positions |
1247
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
retrieve_positions() |
1249
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
Returns a list in which each element is a L |
1251
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
=head1 AUTHOR |
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
mhandisi, C<< >> |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
=head1 BUGS |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through |
1259
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you will |
1260
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
=head1 TODO |
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
Add support for watchlists. |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
=head1 SUPPORT |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
1269
|
|
|
|
|
|
|
|
1270
|
|
|
|
|
|
|
perldoc Finance::Nadex |
1271
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
|
1273
|
|
|
|
|
|
|
You can also look for information at: |
1274
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
=over 4 |
1276
|
|
|
|
|
|
|
|
1277
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here) |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
L |
1280
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
L |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
=item * CPAN Ratings |
1286
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
L |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
=item * Search CPAN |
1290
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
L |
1292
|
|
|
|
|
|
|
|
1293
|
|
|
|
|
|
|
=back |
1294
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
1297
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
|
1299
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
1300
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
Copyright 2015 mhandisi. |
1302
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
1304
|
|
|
|
|
|
|
under the terms of the the Artistic License (2.0). You may obtain a |
1305
|
|
|
|
|
|
|
copy of the full license at: |
1306
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
L |
1308
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
Any use, modification, and distribution of the Standard or Modified |
1310
|
|
|
|
|
|
|
Versions is governed by this Artistic License. By using, modifying or |
1311
|
|
|
|
|
|
|
distributing the Package, you accept this license. Do not use, modify, |
1312
|
|
|
|
|
|
|
or distribute the Package, if you do not accept this license. |
1313
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
If your Modified Version has been derived from a Modified Version made |
1315
|
|
|
|
|
|
|
by someone other than you, you are nevertheless required to ensure that |
1316
|
|
|
|
|
|
|
your Modified Version complies with the requirements of this license. |
1317
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
This license does not grant you the right to use any trademark, service |
1319
|
|
|
|
|
|
|
mark, tradename, or logo of the Copyright Holder. |
1320
|
|
|
|
|
|
|
|
1321
|
|
|
|
|
|
|
This license includes the non-exclusive, worldwide, free-of-charge |
1322
|
|
|
|
|
|
|
patent license to make, have made, use, offer to sell, sell, import and |
1323
|
|
|
|
|
|
|
otherwise transfer the Package with respect to any patent claims |
1324
|
|
|
|
|
|
|
licensable by the Copyright Holder that are necessarily infringed by the |
1325
|
|
|
|
|
|
|
Package. If you institute patent litigation (including a cross-claim or |
1326
|
|
|
|
|
|
|
counterclaim) against any party alleging that the Package constitutes |
1327
|
|
|
|
|
|
|
direct or contributory patent infringement, then this Artistic License |
1328
|
|
|
|
|
|
|
to you shall terminate on the date that such litigation is filed. |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER |
1331
|
|
|
|
|
|
|
AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. |
1332
|
|
|
|
|
|
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
1333
|
|
|
|
|
|
|
PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY |
1334
|
|
|
|
|
|
|
YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR |
1335
|
|
|
|
|
|
|
CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR |
1336
|
|
|
|
|
|
|
CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, |
1337
|
|
|
|
|
|
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
1338
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
=cut |
1341
|
|
|
|
|
|
|
|
1342
|
|
|
|
|
|
|
42; # End of Finance::Nadex |