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_003';
24              
25             sub _test__init {
26 1     1   11971 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   6 use Mojo::Base-base, -signatures;
  1         2  
  1         6  
32 1     1   191 use Mojo::URL;
  1         2  
  1         4  
33 1     1   68 use overload '""' => sub ($s, @) { $s->{url} }, fallback => 1;
  1     0   2  
  1         20  
  0         0  
  0         0  
  0         0  
  0         0  
34 1     1   557 use Finance::Robinhood::Equity::Account::Portfolio;
  1         3  
  1         6  
35 1     1   476 use Finance::Robinhood::Equity::Account::InstantEligibility;
  1         2  
  1         5  
36 1     1   407 use Finance::Robinhood::Equity::Account::MarginBalances;
  1         3  
  1         5  
37              
38             sub _test_stringify {
39 1   50 1   1900 t::Utility::stash('ACCT') // skip_all();
40 0         0 like(+t::Utility::stash('ACCT'),
41             qr'https://api.robinhood.com/accounts/.+/');
42             }
43             ##
44              
45             =head1 METHODS
46              
47             =cut
48              
49             has _rh => undef => weak => 1;
50              
51             =head2 C
52              
53             The string used to identify this account. Used in the URL for many endpoints.
54              
55             =head2 C
56              
57             The maximum amount of cash on hand and available margin you may use to buy
58             stuff.
59              
60             =head2 C
61              
62             The amount of settled cash on hand.
63              
64             =head2 C
65              
66             The amount of settled cash on hand that has been in the account long enough to
67             be withdrawn back to the original bank account.
68              
69             =head2 C
70              
71             Money set aside for buy orders that have not yet executed.
72              
73             =head2 C
74              
75             Returns true if the account has been closed.
76              
77             =head2 C
78              
79             Returns true if ACH deposits are disabled.
80              
81             =head2 C
82              
83             True if account is backed by Robinhood Clearing rather than Apex Clearing.
84              
85             =head2 C
86              
87             Maximum amount instantly available after an ACH deposit. Unless you have a Gold
88             subscription, this will likely be Instant's standard $1000.
89              
90             =head2 C
91              
92             If your account is restricted from opening new positions, this will be true.
93              
94             =head2 C
95              
96             One of several options:
97              
98             =over
99              
100             =item * C
101              
102             =item * C
103              
104             =item * C
105              
106             =back
107              
108             =head2 C
109              
110             Internal account number used for official documents (tax forms, etc.)
111              
112             =head2 C
113              
114             TODO
115              
116             =head2 C
117              
118             TODO
119              
120             =head2 C
121              
122             Returns true if sweep is enabled to move cash between your brokerage account to
123             RH's crypto service.
124              
125             =head2 C
126              
127             Simple C or C account flag.
128              
129             =head2 C
130              
131             Incoming ACH deposits that have not cleared yet.
132              
133             =head2 C
134              
135             Outgoing funds that are not yet settled.
136              
137             =head2 C
138              
139             Funds that are not yet settled but may be used thanks to Gold or Instant margin
140             accounts.
141              
142             =head2 C
143              
144             True if the account has been flagged and ACH withdrawal has been disabled.
145              
146             =cut
147              
148             has ['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   1862 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   1854 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   1892 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   1974 t::Utility::stash('ACCT') // skip_all('No account object in stash');
228 0         0 my $can_downgrade_to_cash
229             = t::Utility::stash('ACCT')->can_downgrade_to_cash;
230 0         0 isa_ok($can_downgrade_to_cash, 'JSON::PP::Boolean');
231             }
232              
233             =head2 C
234              
235             Returns the related Finance::Robinhood::Equity::Account::InstantEligibility
236             object.
237              
238             =cut
239              
240 0     0 1 0 sub instant_eligibility ($s) {
  0         0  
  0         0  
241             Finance::Robinhood::Equity::Account::InstantEligibility->new(
242             _rh => $s->_rh,
243 0         0 %{$s->{instant_eligibility}}
  0         0  
244             );
245             }
246              
247             sub _test_instant_eligibility {
248 1   50 1   1877 t::Utility::stash('ACCT') // skip_all('No account object in stash');
249 0         0 my $instant_eligibility = t::Utility::stash('ACCT')->instant_eligibility;
250 0         0 isa_ok($instant_eligibility,
251             'Finance::Robinhood::Equity::Account::InstantEligibility');
252             }
253              
254             =head2 C
255              
256             Returns the related Finance::Robinhood::Equity::Account::MarginBalances object.
257              
258             =cut
259              
260 0     0 1 0 sub margin_balances ($s) {
  0         0  
  0         0  
261             Finance::Robinhood::Equity::Account::MarginBalances->new(
262             _rh => $s->_rh,
263 0         0 %{$s->{margin_balances}}
  0         0  
264             );
265             }
266              
267             sub _test_margin_balances {
268 1   50 1   1855 t::Utility::stash('ACCT') // skip_all('No account object in stash');
269 0         0 my $margin_balances = t::Utility::stash('ACCT')->margin_balances;
270 0         0 isa_ok($margin_balances,
271             'Finance::Robinhood::Equity::Account::MarginBalances');
272             }
273              
274             =head2 C
275              
276             Returns the related Finance::Robinhood::Equity::Account::Portfolio object.
277              
278             =cut
279              
280 0     0 1 0 sub portfolio ($s) {
  0         0  
  0         0  
281 0         0 my $res = $s->_rh->_get($s->{portfolio});
282             $res->is_success
283             ? Finance::Robinhood::Equity::Account::Portfolio->new(_rh => $s->_rh,
284 0 0       0 %{$res->json})
  0 0       0  
285             : Finance::Robinhood::Error->new(
286             $res->is_server_error ? (details => $res->message) : $res->json);
287             }
288              
289             sub _test_portfolio {
290 1   50 1   1863 t::Utility::stash('ACCT') // skip_all('No account object in stash');
291 0         0 my $portfolio = t::Utility::stash('ACCT')->portfolio;
292 0         0 isa_ok($portfolio, 'Finance::Robinhood::Equity::Account::Portfolio');
293             }
294              
295             =head2 C
296              
297             my $positions = $account->equity_positions( );
298              
299             Returns the related paginated list object filled with
300             Finance::Robinhood::Equity::Position objects.
301              
302             my $positions = $account->equity_positions( nonzero => \1 );
303              
304             You can filter and modify the results. All options are optional.
305              
306             =over
307              
308             =item C - true or false. Default is false.
309              
310             =item C - List of equity instruments
311              
312             =back
313              
314             =cut
315              
316 0     0 1 0 sub positions ($s, %filters) {
  0         0  
  0         0  
  0         0  
317             $filters{nonzero} = !!$filters{nonzero} ? 'true' : 'false'
318 0 0       0 if defined $filters{nonzero};
    0          
319             Finance::Robinhood::Utilities::Iterator->new(
320             _rh => $s->_rh,
321 0         0 _next_page => Mojo::URL->new($s->{positions})->query(\%filters),
322             _class => 'Finance::Robinhood::Equity::Position'
323             );
324             }
325              
326             sub _test_positions {
327 1   50 1   1853 t::Utility::stash('ACCT') // skip_all('No account object in stash');
328 0           my $positions = t::Utility::stash('ACCT')->positions;
329 0           isa_ok($positions, 'Finance::Robinhood::Utilities::Iterator');
330 0           isa_ok($positions->current, 'Finance::Robinhood::Equity::Position');
331             }
332              
333             =head1 LEGAL
334              
335             This is a simple wrapper around the API used in the official apps. The author
336             provides no investment, legal, or tax advice and is not responsible for any
337             damages incurred while using this software. This software is not affiliated
338             with Robinhood Financial LLC in any way.
339              
340             For Robinhood's terms and disclosures, please see their website at
341             https://robinhood.com/legal/
342              
343             =head1 LICENSE
344              
345             Copyright (C) Sanko Robinson.
346              
347             This library is free software; you can redistribute it and/or modify it under
348             the terms found in the Artistic License 2. Other copyrights, terms, and
349             conditions may apply to data transmitted through this module. Please refer to
350             the L section.
351              
352             =head1 AUTHOR
353              
354             Sanko Robinson Esanko@cpan.orgE
355              
356             =cut
357              
358             1;