File Coverage

lib/Finance/Robinhood/Equity/Account.pm
Criterion Covered Total %
statement 28 92 30.4
branch 0 16 0.0
condition 9 18 50.0
subroutine 16 25 64.0
pod 8 8 100.0
total 61 159 38.3


line stmt bran cond sub pod time code
1             package Finance::Robinhood::Equity::Account;
2              
3             =encoding utf-8
4              
5             =for stopwords watchlist watchlists untradable urls
6              
7             =head1 NAME
8              
9             Finance::Robinhood::Equity::Account - Represents a Single Robinhood Account
10              
11             =head1 SYNOPSIS
12              
13             use Finance::Robinhood;
14             my $rh = Finance::Robinhood->new->login('user', 'pass');
15             my $account = $rh->equity_accounts->current();
16              
17             CORE::say sprintf '$%0.2f of $%0.2f can be withdrawn',
18             $account->cash_available_for_withdrawal,
19             $account->cash;
20              
21             =cut
22              
23             our $VERSION = '0.92_001';
24              
25             sub _test__init {
26 1     1   11075 my $rh = t::Utility::rh_instance(1);
27 0         0 my $acct = $rh->equity_accounts->current;
28 0         0 isa_ok( $acct, __PACKAGE__ );
29 0         0 t::Utility::stash( 'ACCT', $acct ); # Store it for later
30             }
31 1     1   7 use Mojo::Base-base, -signatures;
  1         2  
  1         6  
32 1     1   184 use Mojo::URL;
  1         2  
  1         5  
33 1     1   96 use overload '""' => sub ( $s, @ ) { $s->{url} }, fallback => 1;
  1     0   2  
  1         11  
  0         0  
  0         0  
  0         0  
  0         0  
34 1     1   659 use Finance::Robinhood::Equity::Account::Portfolio;
  1         3  
  1         6  
35 1     1   588 use Finance::Robinhood::Equity::Account::InstantEligibility;
  1         3  
  1         6  
36 1     1   494 use Finance::Robinhood::Equity::Account::MarginBalances;
  1         2  
  1         7  
37              
38             sub _test_stringify {
39 1   50 1   1881 t::Utility::stash('ACCT') // skip_all();
40 0         0 like( +t::Utility::stash('ACCT'), qr'https://api.robinhood.com/accounts/.+/' );
41             }
42             ##
43              
44             =head1 METHODS
45              
46             =cut
47              
48             has _rh => undef => weak => 1;
49              
50             =head2 C
51              
52             The string used to identify this account. Used in the URL for many endpoints.
53              
54             =head2 C
55              
56             The maximum amount of cash on hand and available margin you may use to buy
57             stuff.
58              
59             =head2 C
60              
61             The amount of settled cash on hand.
62              
63             =head2 C
64              
65             The amount of settled cash on hand that has been in the account long enough to
66             be withdrawn back to the original bank account.
67              
68             =head2 C
69              
70             Money set aside for buy orders that have not yet executed.
71              
72             =head2 C
73              
74             Returns true if the account has been closed.
75              
76             =head2 C
77              
78             Returns true if ACH deposits are disabled.
79              
80             =head2 C
81              
82             True if account is backed by Robinhood Clearing rather than Apex Clearing.
83              
84             =head2 C
85              
86             Maximum amount instantly available after an ACH deposit. Unless you have a Gold
87             subscription, this will likely be Instant's standard $1000.
88              
89             =head2 C
90              
91             If your account is restricted from opening new positions, this will be true.
92              
93             =head2 C
94              
95             One of several options:
96              
97             =over
98              
99             =item * C
100              
101             =item * C
102              
103             =item * C
104              
105             =back
106            
107             =head2 C
108              
109             Internal account number used for official documents (tax forms, etc.)
110              
111             =head2 C
112              
113             TODO
114              
115             =head2 C
116              
117             TODO
118              
119             =head2 C
120              
121             Returns true if sweep is enabled to move cash between your brokerage account to
122             RH's crypto service.
123              
124             =head2 C
125              
126             Simple C or C account flag.
127              
128             =head2 C
129              
130             Incoming ACH deposits that have not cleared yet.
131              
132             =head2 C
133              
134             Outgoing funds that are not yet settled.
135              
136             =head2 C
137              
138             Funds that are not yet settled but may be used thanks to Gold or Instant margin
139             accounts.
140              
141             =head2 C
142              
143             True if the account has been flagged and ACH withdrawal has been disabled.
144              
145             =cut
146              
147             has [
148             'account_number', 'cash',
149             'buying_power', 'cash_available_for_withdrawal',
150             'cash_held_for_orders', 'deactivated',
151             'deposit_halted', 'is_pinnacle_account',
152             'max_ach_early_access_amount', 'only_position_closing_trades',
153             'option_level', 'rhs_account_number',
154             'sma', 'sma_held_for_orders',
155             'sweep_enabled', 'type',
156             'uncleared_deposits', 'unsettled_debit',
157             'unsettled_funds', 'url',
158             'withdrawal_halted'
159             ];
160              
161             =head2 C
162              
163             Returns a Time::Moment object.
164              
165             =cut
166              
167 0     0 1 0 sub created_at ($s) {
  0         0  
  0         0  
168 0         0 Time::Moment->from_string( $s->{created_at} );
169             }
170              
171             sub _test_created_at {
172 1   50 1   1808 t::Utility::stash('ACCT') // skip_all('No account object in stash');
173 0         0 isa_ok( t::Utility::stash('ACCT')->created_at, 'Time::Moment' );
174             }
175              
176             =head2 C
177              
178             Returns a Time::Moment object.
179              
180             =cut
181              
182 0     0 1 0 sub updated_at ($s) {
  0         0  
  0         0  
183 0         0 Time::Moment->from_string( $s->{updated_at} );
184             }
185              
186             sub _test_updated_at {
187 1   50 1   1796 t::Utility::stash('ACCT') // skip_all('No account object in stash');
188 0         0 isa_ok( t::Utility::stash('ACCT')->updated_at, 'Time::Moment' );
189             }
190              
191             =head2 C
192              
193             Returns the related Finance::Robinhood::Equity::Account object.
194              
195             =cut
196              
197 0     0 1 0 sub user ($s) {
  0         0  
  0         0  
198 0         0 my $res = $s->_rh->_get( $s->{user} );
199 0         0 require Finance::Robinhood::User;
200             $res->is_success
201 0 0       0 ? Finance::Robinhood::User->new( _rh => $s->_rh, %{ $res->json } )
  0 0       0  
202             : Finance::Robinhood::Error->new(
203             $res->is_server_error ? ( details => $res->message ) : $res->json );
204             }
205              
206             sub _test_user {
207 1   50 1   2213 t::Utility::stash('ACCT') // skip_all('No account object in stash');
208 0         0 isa_ok( t::Utility::stash('ACCT')->user, 'Finance::Robinhood::User' );
209             }
210              
211             =head2 C
212              
213             This method returns a true value if your account is currently eligible for
214             downgrading from a margin account (Instant or Gold) to a cash account.
215              
216             =cut
217              
218 0     0 1 0 sub can_downgrade_to_cash ($s) {
  0         0  
  0         0  
219 0         0 my $res = $s->_rh->_get( $s->{can_downgrade_to_cash} );
220             $res->is_success
221             ? $res->json->{can_downgrade_to_cash}
222 0 0       0 : Finance::Robinhood::Error->new(
    0          
223             $res->is_server_error ? ( details => $res->message ) : $res->json );
224             }
225              
226             sub _test_can_downgrade_to_cash {
227 1   50 1   1929 t::Utility::stash('ACCT') // skip_all('No account object in stash');
228 0         0 my $can_downgrade_to_cash = t::Utility::stash('ACCT')->can_downgrade_to_cash;
229 0         0 isa_ok( $can_downgrade_to_cash, 'JSON::PP::Boolean' );
230             }
231              
232             =head2 C
233              
234             Returns the related Finance::Robinhood::Equity::Account::InstantEligibility
235             object.
236              
237             =cut
238              
239 0     0 1 0 sub instant_eligibility ($s) {
  0         0  
  0         0  
240             Finance::Robinhood::Equity::Account::InstantEligibility->new(
241             _rh => $s->_rh,
242 0         0 %{ $s->{instant_eligibility} }
  0         0  
243             );
244             }
245              
246             sub _test_instant_eligibility {
247 1   50 1   1874 t::Utility::stash('ACCT') // skip_all('No account object in stash');
248 0         0 my $instant_eligibility = t::Utility::stash('ACCT')->instant_eligibility;
249 0         0 isa_ok( $instant_eligibility, 'Finance::Robinhood::Equity::Account::InstantEligibility' );
250             }
251              
252             =head2 C
253              
254             Returns the related Finance::Robinhood::Equity::Account::MarginBalances object.
255              
256             =cut
257              
258 0     0 1 0 sub margin_balances ($s) {
  0         0  
  0         0  
259             Finance::Robinhood::Equity::Account::MarginBalances->new(
260             _rh => $s->_rh,
261 0         0 %{ $s->{margin_balances} }
  0         0  
262             );
263             }
264              
265             sub _test_margin_balances {
266 1   50 1   1798 t::Utility::stash('ACCT') // skip_all('No account object in stash');
267 0         0 my $margin_balances = t::Utility::stash('ACCT')->margin_balances;
268 0         0 isa_ok( $margin_balances, 'Finance::Robinhood::Equity::Account::MarginBalances' );
269             }
270              
271             =head2 C
272              
273             Returns the related Finance::Robinhood::Equity::Account::Portfolio object.
274              
275             =cut
276              
277 0     0 1 0 sub portfolio ($s) {
  0         0  
  0         0  
278 0         0 my $res = $s->_rh->_get( $s->{portfolio} );
279             $res->is_success
280 0 0       0 ? Finance::Robinhood::Equity::Account::Portfolio->new( _rh => $s->_rh, %{ $res->json } )
  0 0       0  
281             : Finance::Robinhood::Error->new(
282             $res->is_server_error ? ( details => $res->message ) : $res->json );
283             }
284              
285             sub _test_portfolio {
286 1   50 1   1870 t::Utility::stash('ACCT') // skip_all('No account object in stash');
287 0         0 my $portfolio = t::Utility::stash('ACCT')->portfolio;
288 0         0 isa_ok( $portfolio, 'Finance::Robinhood::Equity::Account::Portfolio' );
289             }
290              
291             =head2 C
292              
293             my $positions = $account->equity_positions( );
294              
295             Returns the related paginated list object filled with
296             Finance::Robinhood::Equity::Position objects.
297              
298             my $positions = $account->equity_positions( nonzero => \1 );
299              
300             You can filter and modify the results. All options are optional.
301              
302             =over
303              
304             =item C - true or false. Default is false.
305              
306             =item C - List of equity instruments
307              
308             =back
309              
310             =cut
311              
312 0     0 1 0 sub positions ( $s, %filters ) {
  0         0  
  0         0  
  0         0  
313 0 0       0 $filters{nonzero} = !!$filters{nonzero} ? 'true' : 'false' if defined $filters{nonzero};
    0          
314             Finance::Robinhood::Utility::Iterator->new(
315             _rh => $s->_rh,
316 0         0 _next_page => Mojo::URL->new( $s->{positions} )->query( \%filters ),
317             _class => 'Finance::Robinhood::Equity::Position'
318             );
319             }
320              
321             sub _test_positions {
322 1   50 1   1807 t::Utility::stash('ACCT') // skip_all('No account object in stash');
323 0           my $positions = t::Utility::stash('ACCT')->positions;
324 0           isa_ok( $positions, 'Finance::Robinhood::Utility::Iterator' );
325 0           isa_ok( $positions->current, 'Finance::Robinhood::Equity::Position' );
326             }
327              
328             =head1 LEGAL
329              
330             This is a simple wrapper around the API used in the official apps. The author
331             provides no investment, legal, or tax advice and is not responsible for any
332             damages incurred while using this software. This software is not affiliated
333             with Robinhood Financial LLC in any way.
334              
335             For Robinhood's terms and disclosures, please see their website at
336             https://robinhood.com/legal/
337              
338             =head1 LICENSE
339              
340             Copyright (C) Sanko Robinson.
341              
342             This library is free software; you can redistribute it and/or modify it under
343             the terms found in the Artistic License 2. Other copyrights, terms, and
344             conditions may apply to data transmitted through this module. Please refer to
345             the L section.
346              
347             =head1 AUTHOR
348              
349             Sanko Robinson Esanko@cpan.orgE
350              
351             =cut
352              
353             1;