File Coverage

blib/lib/Finance/Nadex.pm
Criterion Covered Total %
statement 355 400 88.7
branch 166 278 59.7
condition 27 63 42.8
subroutine 39 43 90.7
pod 18 18 100.0
total 605 802 75.4


line stmt bran cond sub pod time code
1             package Finance::Nadex;
2              
3 14     14   790428 use strict;
  14         23  
  14         326  
4 14     14   44 use warnings;
  14         17  
  14         393  
5             our $VERSION = '0.07';
6              
7 14     14   1115 use LWP::UserAgent;
  14         61792  
  14         226  
8 14     14   7947 use JSON;
  14         110827  
  14         51  
9 14     14   1444 use Carp;
  14         19  
  14         689  
10 14     14   4800 use Finance::Nadex::Order;
  14         106  
  14         314  
11 14     14   4484 use Finance::Nadex::Position;
  14         19  
  14         298  
12 14     14   4489 use Finance::Nadex::Contract;
  14         25  
  14         322  
13              
14 14     14   53 use constant LOGIN_URL => '/iDeal/v2/security/authenticate';
  14         18  
  14         602  
15 14     14   46 use constant SEND_ORDER_URL => '/iDeal/dma/workingorders';
  14         14  
  14         480  
16 14     14   44 use constant RETRIEVE_ORDERS_URL => '/iDeal/orders/workingorders';
  14         12  
  14         463  
17 14     14   42 use constant RETRIEVE_ORDER_URL => '/iDeal/markets/details/workingorders';
  14         14  
  14         436  
18 14     14   41 use constant CANCEL_ORDER_URL => '/iDeal/orders/workingorders/dma';
  14         16  
  14         417  
19 14     14   41 use constant RETRIEVE_POSITIONS_URL => '/iDeal/orders/positions';
  14         13  
  14         410  
20 14     14   41 use constant RETRIEVE_POSITION_URL => '/iDeal/markets/details/position';
  14         12  
  14         506  
21 14     14   41 use constant EPIC_URL => '/iDeal/markets/details';
  14         17  
  14         478  
22 14     14   42 use constant MARKET_LIST_URL => '/iDeal/markets/navigation';
  14         14  
  14         46236  
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 3858 my $self = shift;
100 18         47 my %args = @_;
101              
102 18         21 my $order_time = time;
103              
104 18 100       34 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       39 unless exists $args{price};
110             croak
111             "ERROR: Finance::Nadex::create_order(): must specify a named argument 'direction'\n"
112 10 100       22 unless exists $args{direction};
113             croak
114             "ERROR: Finance::Nadex::create_order(): must specify a named argument 'epic'\n"
115 9 100       19 unless exists $args{epic};
116             croak
117             "ERROR: Finance::Nadex::create_order(): must specify a named argument 'size'\n"
118 8 100       16 unless exists $args{size};
119              
120 7         9 $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         11 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       26 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       12 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       9 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       8 if $contract->type eq 'binary';
148              
149 4         16 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         10 my $order = $self->_post( $self->{base_url}.SEND_ORDER_URL, $order_content );
166              
167 4 50       10 my $deal_reference_id = $order->{dealReference} if defined $order;
168              
169 4         23 return $deal_reference_id;
170             }
171              
172             sub get_contract {
173              
174 7     7 1 7 my $self = shift;
175 7         10 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       8 unless exists $args{epic};
183             croak
184             "ERROR: Finance::Nadex::get_contract(): specified 'epic' (->$args{epic}<-) is not valid\n"
185 7 50       17 unless $args{epic};
186              
187 7         14 my $epic_url = $self->{base_url}.EPIC_URL . '/' . $args{epic};
188 7         13 my $epic_ref = $self->_get($epic_url);
189              
190 7 50       17 return unless $epic_ref;
191              
192             return
193             unless exists $epic_ref->{instrument}
194 7 50 33     23 && exists $epic_ref->{marketSnapshot};
195             return
196             unless $epic_ref->{instrument}{instrumentType}
197             && $epic_ref->{instrument}{marketName}
198 7 50 33     26 && $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         60 );
212              
213             }
214              
215             sub get_contracts {
216              
217 6     6 1 2154 my $self = shift;
218 6         15 my %args = @_;
219 6         5 my $found;
220              
221 6 50       11 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       14 unless exists $args{instrument};
230             croak
231             "ERROR: Finance::Nadex::get_conttracts(): must specify a named argument 'series'\n"
232 3 100       37 unless exists $args{series};
233              
234             croak "ERROR: Finance::Nadex::get_contracts(): invalid market\n"
235 2 50       5 unless $args{market};
236             croak "ERROR: Finance::Nadex::get_contracts(): invalid instrument\n"
237 2 50       5 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       9 if exists $index_name{ $args{instrument} };
243              
244 2         9 my $market_list_ref = $self->_get($self->{base_url}.MARKET_LIST_URL);
245              
246 2 50       6 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         7 market_list_ref => $market_list_ref
253             );
254              
255 2 50       4 return unless $market_id;
256              
257             my $instruments_list_ref =
258 2         14 $self->_get( $self->{base_url}.MARKET_LIST_URL . '/' . $market_id );
259              
260 2 50       7 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         4 my $instrument_id;
265 2         2 foreach my $instrument ( @{ $instruments_list_ref->{'hierarchy'} } ) {
  2         6  
266             $instrument_id = $instrument->{id}
267 2 50       11 if $instrument->{name} eq $args{instrument};
268             }
269              
270 2 50       11 return unless $instrument_id;
271              
272             my $instrument_list_ref =
273 2         14 $self->_get( $self->{base_url}.MARKET_LIST_URL . '/' . $instrument_id );
274              
275 2 50       8 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         2 my $time_series_id;
280 2         3 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         9 $self->_get( $self->{base_url}.MARKET_LIST_URL . '/' . $time_series_id );
289              
290 2 50       8 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         5  
295 2         8 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 2319 my $self = shift;
304 6         19 my %args = @_;
305              
306 6         3 my $market_id;
307             my $instrument;
308              
309 6 50       12 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       28 unless exists $args{period};
315             croak
316             "ERROR: Finance::Nadex::get_epic(): must specify a named argument 'market'\n"
317 4 50       6 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     28 && $args{period} =~ /^event$/i );
      66        
323             croak
324             "ERROR: Finance::Nadex::get_epic(): must specify a named argument 'instrument'\n"
325 3 100       13 unless exists $args{instrument};
326             croak
327             "ERROR: Finance::Nadex::get_epic(): must specify a named argument 'strike'\n"
328 2 50       4 unless exists $args{strike};
329              
330             croak "ERROR: Finance::Nadex::get_epic(): invalid period\n"
331 2 50       4 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     9 && $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       5 unless $args{strike};
342              
343 2 50       16 $args{period} = ucfirst( lc( $args{period} ) ) if exists $args{period};
344              
345 2 100       6 $args{time} = lc( $args{time} ) if exists $args{time};
346              
347 2         10 my $market_list_ref = $self->_get($self->{base_url}.MARKET_LIST_URL);
348              
349 2 50       6 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         8 market_list_ref => $market_list_ref
356             );
357              
358 2 50       3 return undef unless $market_id;
359              
360 2         8 $market_list_ref = $self->_get( $self->{base_url}.MARKET_LIST_URL . "/$market_id" );
361              
362 2 50       8 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         9 market_list_ref => $market_list_ref
369             );
370              
371 2 50       4 return undef unless $market_id;
372              
373 2         8 $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       8 if $args{period} eq 'Daily';
383 2 50       5 $target_period_time = "-$args{time}" if $args{period} eq 'Intraday';
384 2 50       4 $target_period_time = "$args{period}" if $args{period} eq 'Weekly';
385 2 100       4 $target_period_time = "Open" if $args{period} eq 'Event';
386              
387 2 50       5 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         10 $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       4 return undef unless $market_id;
398              
399 2         8 $market_list_ref = $self->_get( $self->{base_url}.MARKET_LIST_URL . "/$market_id" );
400              
401 2 50       7 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         3 my $epic;
406 2         2 foreach my $market ( @{ $market_list_ref->{'markets'} } ) {
  2         6  
407 2 100       8 $args{time} = uc( $args{time} ) if exists $args{time};
408 2 100       6 $args{time} = "" if !exists $args{time};
409 2 50       54 if ( $market->{instrumentName} =~ /$args{strike}( \($args{time}\))?$/ )
410             {
411 2         4 $epic = $market->{epic};
412 2         5 last;
413             }
414             }
415              
416 2         15 return $epic;
417              
418             }
419              
420             sub get_market_instruments {
421              
422 3     3 1 709 my $self = shift;
423 3         6 my %args = @_;
424              
425 3 50       7 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       18 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         9 my $market_list_ref = $self->_get($self->{base_url}.MARKET_LIST_URL);
436              
437 2 50       13 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         7 market_list_ref => $market_list_ref
444             );
445              
446 2 50       5 return unless $market_id;
447              
448 2         12 $market_list_ref = $self->_get( $self->{base_url}.MARKET_LIST_URL . '/' . $market_id );
449              
450 2 50       9 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         3 my @instruments;
455 2         2 foreach my $market ( @{ $market_list_ref->{'hierarchy'} } ) {
  2         4  
456 2         8 push( @instruments, $market->{name} );
457             }
458 2         14 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         10 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         5  
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 631 my $self = shift;
485 2         3 my %args = @_;
486              
487 2 50       5 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       4 unless $args{instrument};
495              
496             $args{instrument} = $index_name{ $args{instrument} }
497 1 50       4 if exists $index_name{ $args{instrument} };
498              
499 1         3 my @markets = $self->get_markets();
500 1         2 foreach my $market (@markets) {
501 1         4 my @instruments = $self->get_market_instruments( name => $market );
502 1         1 foreach my $instrument (@instruments) {
503 1 50       5 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         5 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       2 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 1606 my $self = shift;
530 5         9 my %args = @_;
531 5         6 my $found;
532              
533 5 50       7 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     20 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       26 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         9 my $market_list_ref = $self->_get($self->{base_url}.MARKET_LIST_URL);
547              
548 2 50       7 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         7 market_list_ref => $market_list_ref
555             );
556              
557 2 50       5 return unless $market_id;
558              
559             my $instruments_list_ref =
560 2         13 $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         3 my $instrument_id;
567 2         4 foreach my $instrument ( @{ $instruments_list_ref->{'hierarchy'} } ) {
  2         6  
568             $instrument_id = $instrument->{id}
569 2 50       6 if $instrument->{name} eq $args{instrument};
570             $instrument_id = $instrument->{id}
571             if $instrument->{name} eq 'Forex'
572 2 50 33     8 && $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       6 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     15 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         7 my @time_series;
614 2         3 foreach my $series ( @{ $instrument_list_ref->{'hierarchy'} } ) {
  2         5  
615 2         4 push( @time_series, $series->{name} );
616             }
617              
618 2         17 return @time_series;
619              
620             }
621              
622             sub logged_in {
623              
624 72     72 1 67 my $self = shift;
625              
626 72 100       302 return 0 unless $self->{security_token};
627              
628 61 50       104 return 0 unless $self->{session_id};
629              
630 61         135 return 1;
631              
632             }
633              
634             sub login {
635              
636 13     13 1 327 my $self = shift;
637 13         29 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     46 unless exists $args{password} || exists $self->{password};
645              
646 13 50       42 $self->{username} = $args{username} if exists $args{username};
647 13 50       36 $self->{password} = $args{password} if exists $args{password};
648              
649 13         34 my $login_url = $self->{base_url}.LOGIN_URL;
650              
651 13         44 my $login_content = qq~
652             {
653             "advertisingId" : "",
654             "password": "$self->{password}",
655             "username": "$self->{username}"
656             }~;
657              
658 13         43 my $json_obj = $self->_post( $login_url, $login_content );
659              
660 13         52 $self->{user_agent}->cookie_jar->scan( \&_get_session_id );
661              
662 13         69 $self->{session_id} = $session_id;
663              
664 13         32 $self->{balance} = $json_obj->{accountInfo}->{available};
665              
666 13         36 return $self->logged_in();
667              
668             }
669              
670             sub new {
671              
672 25     25 1 899244 my $class = shift;
673 25         52 my %args = @_;
674              
675 25 50 33     111 if (exists $args{platform} && $args{platform} eq 'demo') {
676 0         0 $args{base_url} = 'https://demo-trade.nadex.com';
677             } else {
678 25         233 $args{base_url} = 'https://trade.nadex.com';
679             }
680              
681 25         138 my $ua = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0 } );
682              
683 25         4570 push @{ $ua->requests_redirectable }, 'POST', 'DELETE';
  25         70  
684 25         257 $ua->agent(
685             "vendor=IG Group | applicationType=dxd | platform=Android | deviceType=generic | version=1.13.2"
686             );
687 25         900 $ua->cookie_jar( { autosave => 1, ignore_discard => 1 } );
688              
689 25         6875 $args{user_agent} = $ua;
690              
691 25         62 bless \%args, __PACKAGE__;
692              
693             }
694              
695             sub retrieve_order {
696              
697 3     3 1 720 my $self = shift;
698 3         7 my %args = @_;
699              
700 3 100       6 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       13 unless exists $args{id};
705 1 50       3 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         4 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 65 my $self = shift;
722              
723 2 100       4 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         3 my $order_list_ref = $self->_get($retrieve_orders_url);
729              
730 1 50       4 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         1 my $order_obj_list_ref = [];
735 1         3 foreach my $order (@$order_list_ref) {
736 1         12 push( @$order_obj_list_ref, Finance::Nadex::Order::_new($order) );
737             }
738              
739 1         11 return @$order_obj_list_ref;
740             }
741              
742             sub retrieve_position {
743              
744 3     3 1 924 my $self = shift;
745 3         6 my %args = @_;
746              
747 3 100       7 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       19 unless exists $args{id};
753             croak "ERROR: Finance::Nadex::retrieve_position(): invalid id\n"
754 1 50       3 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       4 return undef unless $position_ref;
763              
764 1         4 return Finance::Nadex::Position::_new($position_ref);
765              
766             }
767              
768             sub retrieve_positions {
769              
770 2     2 1 64 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         2 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         14 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   59 my $self = shift;
827 40         36 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         177 Content => ""
841             );
842              
843 40 50       51328 $self->{security_token} = $response->header('X-SECURITY-TOKEN')
844             if $response->header('X-SECURITY-TOKEN');
845 40         1819 $self->{code} = $response->code;
846 40         230 $self->{content} = $response->content;
847              
848 40         286 my $json = eval { JSON->new->utf8->decode( $response->content ); };
  40         215  
849              
850 40         596 return $json;
851             }
852              
853             sub _post {
854              
855 17     17   18 my $self = shift;
856 17         21 my $url = shift;
857 17         18 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         137 Content => $post_content
879             );
880              
881 17 100       119454 $self->{security_token} = $response->header('X-SECURITY-TOKEN')
882             if $response->header('X-SECURITY-TOKEN');
883 17         815 $self->{code} = $response->code;
884 17   100     114 $self->{content} = $response->content || undef;
885              
886 17         165 my $json_obj = eval { JSON->new->utf8->decode( $response->content ); };
  17         229  
887              
888 17         430 return $json_obj;
889              
890             }
891              
892             sub _get_market_id {
893              
894 12     12   18 my $self = shift;
895 12         35 my %args = @_;
896 12         10 my $market_id;
897              
898 12         10 foreach my $market ( @{ $args{market_list_ref}->{'hierarchy'} } ) {
  12         28  
899 14 100 66     46 if ( !exists $args{accept_match} || $args{accept_match} == 0 ) {
900 12 100       31 if ( $market->{name} eq $args{name} ) {
901 10         18 $market_id = $market->{id};
902             }
903             }
904             else {
905 2 50 66     37 if ( $market->{name} =~ /$args{name}/
906             || $market->{name} eq $args{name} )
907             {
908 2         3 $market_id = $market->{id};
909             }
910             }
911             }
912              
913 12   50     39 return $market_id || undef;
914             }
915              
916             sub _get_session_id {
917              
918 12     12   272 my $key = $_[1];
919 12         18 my $val = $_[2];
920              
921 12 50       72 $session_id = $val if $key =~ /JSESSIONID/;
922              
923             }
924              
925             sub _is_valid_price {
926              
927 7     7   6 my $self = shift;
928 7         6 my $price = shift;
929 7         4 my $type = shift;
930              
931 7 100       35 return 0 if $price =~ /-|\+/;
932              
933 6 100       12 if ( $type eq 'binary' ) {
934 5 50       19 return 0 if $price !~ /^(\d+\.\d{1,2}|\.\d{1,2}|\d+)$/;
935              
936 5 50       12 if ( $price =~ /\.(\d+)/ ) {
937 5 0 33     18 return 0 if $1 != 0 && $1 != 25 && $1 != 50 && $1 != 75;
      33        
      0        
938             }
939             }
940              
941 6 100       8 if ( $type eq 'spread' ) {
942 1 50       7 return 0 if $price !~ /^(\d+|\d+\.\d{1,4})$/;
943             }
944              
945 6         14 return 1;
946             }
947              
948             sub _is_valid_direction {
949              
950 6     6   4 my $self = shift;
951 6         5 my $direction = shift;
952              
953 6         18 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   5 my $self = shift;
961 5         6 my $size = shift;
962              
963 5 100       26 return 0 if $size !~ /^\d+$/;
964              
965 4 50       7 return 0 if $size == 0;
966              
967 4         8 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.07
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             # connects to the live platform when called as Finance::Nadex->new(); as an alternative
987             # it is possible to connect to the demo platform with Finance::Nadex->new(platform => 'demo')
988             my $client = Finance::Nadex->new();
989              
990             # must login to perform any actions, including simply querying market info
991             $client->login(username => 'yourusername', password => 'yourpassword');
992            
993             # get the available balance in the account
994             my $balance = $client->balance();
995            
996             # get a quote for GBP/USD
997             my $quote = $client->get_quote('instrument' => 'GBP/USD');
998              
999             # $quote may now be (for example), 1.5010
1000            
1001             # retrieve the epic (Nadex-assigned contract unique identifier) for the
1002             # Daily, 3pm, GBP/USD > 1.5120 contract
1003             my $epic = $client->get_epic( period => 'Daily', market => 'Forex (Binaries)', time => '3pm', instrument => 'GBP/USD', strike => '1.5120');
1004            
1005             # suppose $epic now contains 'NB.D.GBP-USD.OPT-23-17-23Jan15.IP';
1006             # create an order to buy 3 of those contracts for $34.50 each
1007             my $order_id = $client->create_order( price => '34.50', direction => 'buy', epic => $epic, size => 3 );
1008            
1009             # check the status of the order using the order id returned by the exchange;
1010             # this call will return undef if the order doesn't exist; the order may not exist
1011             # because the order was rejected by the exchange or because it was filled immediately
1012             # and therefore is no longer a working order
1013             my $order = $client->retrieve_order( id => $order_id );
1014            
1015             # let's assume the order was created and is still a working (or pending) order;
1016             # get the details of the order using the accessor methods provided by Finance::Nadex::Order
1017             print join(" ", $order->direction, $order->size, $order->contract, $order->price), "\n";
1018              
1019             # suppose the order has now been filled; this means we have one open position;
1020             # get the open positions
1021             my @positions = $client->retrieve_positions();
1022            
1023             # @positions now has 1 element which is a Finance::Nadex::Position; get its details
1024             print join(" ", $positions[0]->id, $positions[0]->direction(), $positions[0]->size(), $positions[0]->contract(), $positions[0]->price), "\n";
1025            
1026             # get the current best bid (the price at which we could sell the contract back immediately)
1027             my $bid = $positions[0]->bid();
1028            
1029             # suppose $bid is now $64.50 (we bought at $34.50, so we have a profit of $30);
1030             # sell to close the position at $64.50
1031             my $sell_to_close_order_id = $client->create_order( price => $bid, direction => 'sell', epic => $positions[0]->epic, size => $positions[0]->size() );
1032              
1033             # get all the time series (trading periods for contracts) currently
1034             # available for GBP/USD binaries; the list of currently available markets
1035             # can be obtained via a call to get_markets()
1036             my @series = $client->get_time_series( market => 'Forex (Binaries)', instrument => 'GBP/USD' );
1037            
1038             # elements of @series are simply strings, such as '2pm-4pm' or 'Daily (3pm)';
1039             # suppose one of the elements of series is '8pm-10pm'; get a list of
1040             # contracts available for trading within that market
1041             my @contracts = $client->get_contracts( market => 'Forex (Binaries)', instrument => 'GBP/USD', series => 'Daily (3pm)' );
1042            
1043             # @contracts now has a list in which each element is a Finance::Nadex::Contract; get
1044             # the details of the available contracts using the accessors of Finance::Nadex::Contract
1045             # including the current best bid and offer available on the exchange
1046             foreach my $contract (@contracts) {
1047             print join(" ", $contract->epic(), $contract->contract(), $contract->expirydate(), $contract->bid(), $contract->offer());
1048             }
1049            
1050              
1051             # cancel any remaining open orders
1052             $client->cancel_all_orders();
1053              
1054            
1055             =head1 SUBROUTINES/METHODS
1056              
1057             =head2 balance
1058              
1059             Retrieves the available account balance
1060              
1061             balance()
1062              
1063             Returns a number representing the available account balance
1064              
1065             =head2 cancel_all_orders
1066              
1067             Cancels all pending orders
1068              
1069             cancel_all_orders()
1070              
1071             Returns nothing
1072              
1073             =head2 cancel_order
1074              
1075             Cancels the order with the specified order id
1076              
1077             cancel_order( id => 'NZ1234FGQ4AFFOPA12Z' )
1078              
1079             Returns the reference id created by the exchange for the cancelled order
1080              
1081             =head2 create_order
1082              
1083             Creates an order on the exchange with the specified parameters
1084              
1085             create_order( price => '34.50', direction => 'buy', epic => 'NB.D.GBP-USD.OPT-23-17-23Jan15.IP', size => 2 )
1086              
1087             price : the amount at which to buy or sell; the decimal portion of the number, if provided, must be .50 or .00 for binaries
1088              
1089             direction : one of 'buy', 'sell', '+', '-'
1090            
1091             size : the number of contracts to buy or sell
1092              
1093             epic : the unique identifier for the contract to be bought or sold
1094              
1095             Returns: the order id created by the exchange to identify the order
1096              
1097             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
1098              
1099             =head2 get_contract
1100              
1101             Retrieves the contract specified by an epic
1102              
1103             get_contract( epic => 'NB.D.GBP-USD.OPT-23-17-23Jan15.IP' )
1104              
1105             epic : the unique identifier created by the exchange for the contract
1106              
1107             Returns a L instance for the specified epic
1108              
1109             =head2 get_contracts
1110              
1111             Retrieves all the contracts available for trading within the given time series for the specified market and instrument
1112              
1113             get_contracts( market => 'Forex (Binaries)', instrument => 'GBP/USD', series => 'Daily (3pm)' )
1114              
1115             market : the name of the market for which the contracts are to be retrieved
1116            
1117             instrument : the name of the instrument within the market for which contracts are to be retrieved; the
1118             instrument specified must be one of the instruments currently available for trading on the exchange
1119             for the provided market; the list of valid instruments can be obtained via get_market_instruments()
1120            
1121             Returns a list in which each element is a L instance for each contract in the specified
1122             time series
1123              
1124             =head2 get_epic
1125              
1126             Retrieves the epic(unique identifier) for a contract with the specified parameters
1127            
1128             get_epic(period => 'daily', strike => '1.5080', time => '3pm', instrument => 'GBP/USD', market => 'Forex (Binaries)')
1129              
1130             retrieves the epic with the specified parameters
1131            
1132             period : specifies the frequency or period of the contract being searched for; one of 'daily', 'intraday', 'weekly', or 'event'
1133            
1134             time : specifies the time at which the contract being searched for expires (not required when retrieving an event epic)
1135            
1136             instrument : the asset type from which the contract being searched for derives value; an index, currency, or commodity
1137            
1138             market : the market in which the specified contract exists (e.g. 'Forex (Binaries)', 'Indices (Binaries)'); must be one of markets returned by get_markets()
1139            
1140             strike : the level of the underlying asset for the desired contract (e.g. 1.5010)
1141            
1142             Returns the unique identifier of the contract
1143              
1144             =head2 get_market_instruments
1145              
1146             Retrieves the list of instruments associated with the specified market
1147              
1148             get_market_instruments( name => 'Forex (Binaries)' )
1149              
1150             name : the name of the market for which instruments are to be retrieved; this must match one of the names returned by get_markets()
1151            
1152             Returns a list in which each element is a string containing the name of an instrument available for trading in the markets
1153              
1154              
1155             =head2 get_markets
1156              
1157             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
1158             information about the market
1159            
1160             get_markets()
1161              
1162             Returns a list in which each element is a string containing the name of a market in which instruments are traded
1163              
1164             =head2 get_quote
1165              
1166             Retrieves the current price level of the instrument specified as reported by the exchange (the Indicative Price)
1167              
1168             get_quote( instrument => 'GBP/USD' );
1169              
1170             instrument : the name of the instrument within the market for which a quote is to be retrieved; the
1171             instrument specified must be one of the instruments currently available for trading
1172             on the exchange for the provided market; the list of valid instruments can be obtained
1173             via get_market_instruments()
1174            
1175             Returns the current price level or undef if it cannot be obtained
1176              
1177             =head2 get_time_series
1178              
1179             Retrieves the contract periods available for trading in the specified market for the given instrument
1180              
1181             get_time_series( market => 'Forex (Binaries)', instrument => 'AUD/USD' )
1182              
1183             market : the name of the market for which a time series is to be retrieved
1184            
1185             instrument : the name of the instrument within the market for which a time series is to be retrieved; the
1186             instrument specified must be one of the instruments currently available for trading on the exchange
1187             for the provided market; the list of valid instruments can be obtained via get_market_instruments()
1188              
1189             In the case of the '5 Minute Binaries' and '20 Minute Binaries' markets, returns a list in which each element is
1190             a L representing each contract available in the series; for all other markets, returns
1191             a string containing the name of a time series for the given market and instrument;
1192             a time series designates the period during which the contract is available for trading including the expiration time
1193              
1194             =head2 login
1195              
1196             Submits the specified username and password to the exchange for authorization
1197              
1198             login( username => 'someusername', password => 'somepassword' );
1199              
1200             username : the username of the account
1201            
1202             password : the password of the account
1203              
1204             Returns a true value on successful login; otherwise returns a false value
1205              
1206             =head2 logged_in
1207              
1208             Reports whether login was previously attempted and succeeded
1209              
1210             logged_in()
1211              
1212             Returns true if login was previously attempted and succeded; otherwise returns false
1213              
1214             =head2 new
1215              
1216             Creates an instance of a Finance::Nadex object
1217              
1218             new()
1219            
1220             Returns a reference to a Finance::Nadex instance
1221              
1222             =head2 retrieve_order
1223              
1224             Gets the details of the pending order with the specified order id
1225              
1226             retrieve_order( id => 'NZ1234FGQ4AFFOPA12Z' )
1227              
1228             Returns an instance of L
1229              
1230             =head2 retrieve_orders
1231              
1232             Gets the details of all pending orders
1233              
1234             retrieve_orders()
1235              
1236             Returns a list in which each element is a L
1237              
1238             =head2 retrieve_position
1239              
1240             Retrieves the details of an individual open position
1241              
1242             retrieve_position ( id => 'NA12DZ45BNVA12A9BQZ' )
1243              
1244             Returns a L object corresponding to the specified position id
1245              
1246             =head2 retrieve_positions
1247              
1248             Retrieves the details of all the open positions
1249              
1250             retrieve_positions()
1251              
1252             Returns a list in which each element is a L
1253              
1254             =head1 AUTHOR
1255              
1256             mhandisi, C<< >>
1257              
1258             =head1 BUGS
1259              
1260             Please report any bugs or feature requests to C, or through
1261             the web interface at L. I will be notified, and then you will
1262             automatically be notified of progress on your bug as I make changes.
1263              
1264             =head1 TODO
1265              
1266             Add support for watchlists.
1267              
1268             =head1 SUPPORT
1269              
1270             You can find documentation for this module with the perldoc command.
1271              
1272             perldoc Finance::Nadex
1273              
1274              
1275             You can also look for information at:
1276              
1277             =over 4
1278              
1279             =item * RT: CPAN's request tracker (report bugs here)
1280              
1281             L
1282              
1283             =item * AnnoCPAN: Annotated CPAN documentation
1284              
1285             L
1286              
1287             =item * CPAN Ratings
1288              
1289             L
1290              
1291             =item * Search CPAN
1292              
1293             L
1294              
1295             =back
1296              
1297              
1298             =head1 ACKNOWLEDGEMENTS
1299              
1300              
1301             =head1 LICENSE AND COPYRIGHT
1302              
1303             Copyright 2015 mhandisi.
1304              
1305             This program is free software; you can redistribute it and/or modify it
1306             under the terms of the the Artistic License (2.0). You may obtain a
1307             copy of the full license at:
1308              
1309             L
1310              
1311             Any use, modification, and distribution of the Standard or Modified
1312             Versions is governed by this Artistic License. By using, modifying or
1313             distributing the Package, you accept this license. Do not use, modify,
1314             or distribute the Package, if you do not accept this license.
1315              
1316             If your Modified Version has been derived from a Modified Version made
1317             by someone other than you, you are nevertheless required to ensure that
1318             your Modified Version complies with the requirements of this license.
1319              
1320             This license does not grant you the right to use any trademark, service
1321             mark, tradename, or logo of the Copyright Holder.
1322              
1323             This license includes the non-exclusive, worldwide, free-of-charge
1324             patent license to make, have made, use, offer to sell, sell, import and
1325             otherwise transfer the Package with respect to any patent claims
1326             licensable by the Copyright Holder that are necessarily infringed by the
1327             Package. If you institute patent litigation (including a cross-claim or
1328             counterclaim) against any party alleging that the Package constitutes
1329             direct or contributory patent infringement, then this Artistic License
1330             to you shall terminate on the date that such litigation is filed.
1331              
1332             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
1333             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
1334             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
1335             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
1336             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
1337             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
1338             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
1339             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1340              
1341              
1342             =cut
1343              
1344             42; # End of Finance::Nadex