File Coverage

lib/Finance/Robinhood/Equity/Watchlist.pm
Criterion Covered Total %
statement 23 88 26.1
branch 0 8 0.0
condition 10 29 34.4
subroutine 15 22 68.1
pod 5 5 100.0
total 53 152 34.8


line stmt bran cond sub pod time code
1             package Finance::Robinhood::Equity::Watchlist;
2              
3             =encoding utf-8
4              
5             =for stopwords watchlist watchlists untradable urls
6              
7             =head1 NAME
8              
9             Finance::Robinhood::Equity::Watchlist - Represents a Single Robinhood Watchlist
10              
11             =head1 SYNOPSIS
12              
13             use Finance::Robinhood;
14             my $rh = Finance::Robinhood->new->login('user', 'pass');
15             my $watchlist = $rh->watchlist_by_name('Default');
16              
17             # TODO
18              
19             =cut
20              
21             our $VERSION = '0.92_003';
22              
23             sub _test__init {
24 1     1   8123 my $rh = t::Utility::rh_instance(1);
25 0         0 my $watchlist = $rh->equity_watchlist_by_name('Default');
26 0         0 isa_ok($watchlist, __PACKAGE__);
27 0         0 t::Utility::stash('WATCHLIST', $watchlist); # Store it for later
28             }
29 1     1   4128 use Mojo::Base 'Finance::Robinhood::Utilities::Iterator', -signatures;
  1         3  
  1         10  
30 1     1   295 use Mojo::URL;
  1         3  
  1         8  
31 0   0 0   0 use overload '""' => sub ($s, @) { $s->{_next_page} // $s->{_first_page} },
  0         0  
  0         0  
  0         0  
32 1     1   81 fallback => 1;
  1         2  
  1         10  
33 1     1   649 use Finance::Robinhood::Equity::Watchlist::Element;
  1         3  
  1         7  
34              
35             sub _test_stringify {
36 1   50 1   1950 t::Utility::stash('WATCHLIST') // skip_all();
37 0         0 is(+t::Utility::stash('WATCHLIST'),
38             'https://api.robinhood.com/watchlists/Default/');
39             }
40              
41             =head1 METHODS
42              
43             This is a subclass of Finance::Robinhood::Utilities::Iterator. All the sweet,
44             sugary goodness from there works here. Note that C, C,
45             etc. return Finance::Robinhood::Equity::Watchlist::Element objects.
46              
47             =cut
48              
49             # Inherits _rh from Iterator
50              
51             =head2 C
52              
53             warn $watchlist->name;
54              
55             Returns the name given to this watchlist.
56              
57             =cut
58              
59             has 'name' => sub ($s) {
60             $s->{results}->[0]->{watchlist} =~ m[.+\/(\w+)\/$];
61             $1;
62             };
63              
64             sub _test_name {
65 1   50 1   1892 t::Utility::stash('WATCHLIST') // skip_all();
66 0         0 is(t::Utility::stash('WATCHLIST')->name, 'Default');
67             }
68              
69             # Private
70             has 'url' => sub ($s) {
71             sprintf 'https://api.robinhood.com/watchlists/%s/', $s->name;
72             };
73              
74             sub _test_url {
75 1   50 1   1861 t::Utility::stash('WATCHLIST') // skip_all();
76 0         0 is(t::Utility::stash('WATCHLIST')->url,
77             'https://api.robinhood.com/watchlists/Default/');
78             }
79              
80             # Override methods from Iterator
81             has _class => sub ($s) {'Finance::Robinhood::Equity::Watchlist::Element'};
82             has _next_page => sub ($s) { $s->{url} };
83              
84             sub _test_current {
85 1   50 1   1876 t::Utility::stash('WATCHLIST') // skip_all();
86 0         0 t::Utility::stash('WATCHLIST')->reset;
87 0         0 isa_ok(t::Utility::stash('WATCHLIST')->current,
88             'Finance::Robinhood::Equity::Watchlist::Element');
89             }
90              
91             sub _test_next {
92 1   50 1   1859 t::Utility::stash('WATCHLIST') // skip_all();
93 0         0 t::Utility::stash('WATCHLIST')->reset;
94 0         0 isa_ok(t::Utility::stash('WATCHLIST')->next,
95             'Finance::Robinhood::Equity::Watchlist::Element');
96             }
97              
98             =head2 C
99              
100             $watchlist->populate('MSFT', 'QQQ', 'BA');
101              
102             Adds a list of equity instruments in bulk by their ticker symbol and returns a
103             boolean.
104              
105             =cut
106              
107 0     0 1 0 sub populate ($s, @symbols) {
  0         0  
  0         0  
  0         0  
108              
109             # Split symbols into groups of 32 to keep URL length down
110 0         0 my $res;
111 0         0 while (@symbols) {
112 0         0 $res = $s->_rh->_post($s->url . 'bulk_add/',
113             symbols => splice @symbols,
114             0, 32
115             );
116 0 0       0 return Finance::Robinhood::Error->new($res->json)
117             if !$res->is_success;
118             }
119 0   0     0 return $res->is_success ||
120             Finance::Robinhood::Error->new(
121             $res->is_server_error ? (details => $res->message) : $res->json);
122             }
123              
124             sub _test_populate {
125 1   50 1   1870 t::Utility::stash('WATCHLIST') // skip_all();
126 0         0 t::Utility::stash('WATCHLIST')->reset;
127 0         0 ok(t::Utility::stash('WATCHLIST')->populate('MSFT'), 'adding MSFT');
128             todo("Test that the instrument was added by checking watchlist size" =>
129 0     0   0 sub { pass('ugh') });
  0         0  
130             }
131              
132             =head2 C
133              
134             $watchlist->add_instrument($msft);
135              
136             Adds an equity instrument. If successful, a
137             Finance::Robinhood::Equity::Watchlist::Element object is returned.
138              
139             =cut
140              
141 0     0 1 0 sub add_instrument ($s, $instrument) {
  0         0  
  0         0  
  0         0  
142 0         0 my $res = $s->_rh->_post($s->url, instrument => $instrument);
143             return $res->is_success
144             ? Finance::Robinhood::Equity::Watchlist::Element->new(_rh => $s->_rh,
145 0 0       0 %{$res->json})
  0 0       0  
146             : Finance::Robinhood::Error->new(
147             $res->is_server_error ? (details => $res->message) : $res->json);
148             }
149              
150             sub _test_add_instrument {
151 1   50 1   2007 t::Utility::stash('WATCHLIST') // skip_all();
152 0         0 t::Utility::stash('WATCHLIST')->reset;
153 0         0 my $result
154             = t::Utility::stash('WATCHLIST')
155             ->add_instrument(t::Utility::stash('WATCHLIST')
156             ->_rh->equity_instrument_by_symbol('MSFT'));
157 0 0       0 isa_ok($result,
158             $result
159             ? 'Finance::Robinhood::Equity::Watchlist::Element'
160             : 'Finance::Robinhood::Error'
161             );
162             }
163              
164             =head2 C
165              
166             my @instruments = $watchlist->instruments();
167              
168             This method makes a call to grab data for every equity instrument on the
169             watchlist and returns them as a list.
170              
171             =cut
172              
173 0     0 1 0 sub instruments ($s) {
  0         0  
  0         0  
174 0         0 $s->_rh->equity_instruments_by_id(map { $_->id } $s->all);
  0         0  
175             }
176              
177             sub _test_instruments {
178 1   50 1   1870 t::Utility::stash('WATCHLIST') // skip_all();
179 0         0 t::Utility::stash('WATCHLIST')->reset;
180 0         0 my @instruments = t::Utility::stash('WATCHLIST')->instruments;
181 0         0 isa_ok($_, 'Finance::Robinhood::Equity::Instrument') for @instruments;
182             }
183              
184             =head2 C
185              
186             my @ids = $watchlist->ids();
187              
188             Returns the instrument id for all equity instruments as a list.
189              
190             =cut
191              
192 0     0 1 0 sub ids ($s) {
  0         0  
  0         0  
193 0         0 map { $_->id } $s->all;
  0         0  
194             }
195              
196             sub _test_ids {
197 1   50 1   1907 t::Utility::stash('WATCHLIST') // skip_all();
198 0         0 t::Utility::stash('WATCHLIST')->reset;
199 0         0 my @ids = t::Utility::stash('WATCHLIST')->ids;
200             like($_, qr[^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}\-[0-9a-f]{12}$]i, $_)
201 0         0 for @ids;
202             }
203              
204             =head2 C
205              
206             $watchlist->reorder( @ids );
207              
208             This method moves items of the watchlist around.
209              
210             =cut
211              
212 0     0 1 0 sub reorder ($s, @ids) {
  0         0  
  0         0  
  0         0  
213 0         0 my $res = $s->_rh->_post($s->url . 'reorder/', uuids => join ',', @ids);
214 0   0     0 return $res->is_success || Finance::Robinhood::Error->new(%{$res->json});
215             }
216              
217             sub _test_reorder {
218 1   50 1   1861 t::Utility::stash('WATCHLIST') // skip_all();
219 0           t::Utility::stash('WATCHLIST')->reset;
220 0           my @ids = t::Utility::stash('WATCHLIST')->ids;
221 0           ok(t::Utility::stash('WATCHLIST')->reorder(reverse @ids), 'reorder(...)');
222 0           t::Utility::stash('WATCHLIST')->reset;
223 0           my @reordered = t::Utility::stash('WATCHLIST')->ids;
224 0           is([reverse @ids], \@reordered);
225             }
226              
227             =head1 LEGAL
228              
229             This is a simple wrapper around the API used in the official apps. The author
230             provides no investment, legal, or tax advice and is not responsible for any
231             damages incurred while using this software. This software is not affiliated
232             with Robinhood Financial LLC in any way.
233              
234             For Robinhood's terms and disclosures, please see their website at
235             https://robinhood.com/legal/
236              
237             =head1 LICENSE
238              
239             Copyright (C) Sanko Robinson.
240              
241             This library is free software; you can redistribute it and/or modify it under
242             the terms found in the Artistic License 2. Other copyrights, terms, and
243             conditions may apply to data transmitted through this module. Please refer to
244             the L section.
245              
246             =head1 AUTHOR
247              
248             Sanko Robinson Esanko@cpan.orgE
249              
250             =cut
251              
252             1;