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   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