File Coverage

lib/Finance/Robinhood/Forex/Pair.pm
Criterion Covered Total %
statement 26 85 30.5
branch 0 10 0.0
condition 5 10 50.0
subroutine 14 23 60.8
pod 6 6 100.0
total 51 134 38.0


line stmt bran cond sub pod time code
1             package Finance::Robinhood::Forex::Pair;
2              
3             =encoding utf-8
4              
5             =for stopwords watchlist watchlists untradable urls forex
6              
7             =head1 NAME
8              
9             Finance::Robinhood::Forex::Pair - Represents a Single Forex Currency Pair
10             Watchlist
11              
12             =head1 SYNOPSIS
13              
14             use Finance::Robinhood;
15             my $rh = Finance::Robinhood->new;
16              
17             # TODO
18              
19             =cut
20              
21             our $VERSION = '0.92_002';
22 1     1   8 use Mojo::Base-base, -signatures;
  1         3  
  1         9  
23 1     1   193 use Mojo::URL;
  1         3  
  1         6  
24 1     1   487 use Finance::Robinhood::Forex::Currency;
  1         4  
  1         7  
25 1     1   533 use Finance::Robinhood::Forex::OrderBuilder;
  1         13  
  1         15  
26 1     1   47 use Finance::Robinhood::Forex::Quote;
  1         2  
  1         7  
27              
28             sub _test__init {
29 1     1   12555 my $rh = t::Utility::rh_instance(1);
30 0         0 my $pair = $rh->forex_pair_by_id('3d961844-d360-45fc-989b-f6fca761d511'); # BTC-USD
31 0         0 isa_ok( $pair, __PACKAGE__ );
32 0         0 t::Utility::stash( 'PAIR', $pair ); # Store it for later
33             }
34 1     1   137 use overload '""' => sub ( $s, @ ) { $s->{id} }, fallback => 1;
  1     0   3  
  1         8  
  0         0  
  0         0  
  0         0  
  0         0  
35              
36             sub _test_stringify {
37 1   50 1   1970 t::Utility::stash('PAIR') // skip_all();
38 0         0 like(
39             +t::Utility::stash('PAIR'),
40             qr'^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'i
41             );
42             }
43             #
44             has _rh => undef => weak => 1;
45              
46             =head1 METHODS
47            
48              
49             =head2 C
50              
51             Returns a UUID.
52              
53             =head2 C
54              
55             Returns a boolean. If this is true, you can only add the pair to a forex
56             watchlist to monitor the price for now.
57              
58             =head2 C
59              
60             Largest amount of this asset you may trade in a single order.
61              
62             =head2 C
63              
64             Smallest price of the quote currency you may place an order for.
65              
66             =head2 C
67              
68             Smallest increment of the asset you may place an order for.
69              
70             =head2 C
71              
72             Smallest amount of the asset you may place an order for.
73              
74             =head2 C
75              
76             Returns a string suited for display. And example would be 'Bitcoin to US
77             Dollar'.
78              
79             =head2 C
80              
81             String used for display. Example of this might be 'BTC-USD'.
82              
83             =head2 C
84              
85             Either C or C.
86              
87             =cut
88              
89             has [
90             'id', 'display_only', 'max_order_size', 'min_order_price_increment',
91             'min_order_quantity_increment',
92             'min_order_size', 'name', 'symbol', 'tradability'
93             ];
94              
95             =head2 C
96              
97             Returns a Finance::Robinhood::Forex::Currency object.
98              
99             =cut
100              
101 0     0 1 0 sub asset_currency ($s) {
  0         0  
  0         0  
102 0         0 Finance::Robinhood::Forex::Currency->new( _rh => $s, %{ $s->{asset_currency} } );
  0         0  
103             }
104              
105             sub _test_asset_currency {
106 1   50 1   1983 t::Utility::stash('PAIR') // skip_all();
107 0         0 isa_ok( t::Utility::stash('PAIR')->asset_currency, 'Finance::Robinhood::Forex::Currency' );
108             }
109              
110             =head2 C
111              
112             Returns a Finance::Robinhood::Forex::Currency object.
113              
114             =cut
115              
116 0     0 1 0 sub quote_currency ($s) {
  0         0  
  0         0  
117 0         0 Finance::Robinhood::Forex::Currency->new( _rh => $s, %{ $s->{quote_currency} } );
  0         0  
118             }
119              
120             sub _test_quote_currency {
121 1   50 1   1862 t::Utility::stash('PAIR') // skip_all();
122 0         0 isa_ok( t::Utility::stash('PAIR')->quote_currency, 'Finance::Robinhood::Forex::Currency' );
123             }
124              
125             =head2 C
126              
127             my $quote = $pair->quote();
128              
129             Builds a Finance::Robinhood::Forex::Quote object with this currency pair's
130             quote data.
131              
132             You do not need to be logged in for this to work.
133              
134             =cut
135              
136 0     0 1 0 sub quote ($s) {
  0         0  
  0         0  
137             my $res
138 0         0 = $s->_rh->_get( 'https://api.robinhood.com/marketdata/forex/quotes/' . $s->{id} . '/' );
139             $res->is_success
140 0 0       0 ? Finance::Robinhood::Forex::Quote->new( _rh => $s->_rh, %{ $res->json } )
  0 0       0  
141             : Finance::Robinhood::Error->new(
142             $res->is_server_error ? ( details => $res->message ) : $res->json );
143             }
144              
145             sub _test_quote {
146 1   50 1   2173 t::Utility::stash('PAIR') // skip_all();
147 0         0 isa_ok( t::Utility::stash('PAIR')->quote(), 'Finance::Robinhood::Forex::Quote' );
148             }
149              
150             =head2 C
151              
152             my $data = $pair->historicals( interval => '15second' );
153              
154             Returns a Finance::Robinhood::Forex::Historicals object.
155              
156             You may provide the following arguments:
157              
158             =over
159              
160             =item C Required and must be on eof the following:
161              
162             =over
163              
164             =item C<15second>
165              
166             =item C<5minute>
167              
168             =item C<10minute>
169              
170             =item C
171              
172             =item C
173              
174             =item C
175              
176             =item C
177              
178             =back
179              
180             =item C - Optional and must be one of the following:
181              
182             =over
183              
184             =item C
185              
186             =item C
187              
188             =item C
189              
190             =item C<5year>
191              
192             =item C
193              
194             =back
195              
196             =back
197              
198             =cut
199              
200 0     0 1 0 sub historicals ( $s, %filters ) {
  0         0  
  0         0  
  0         0  
201 0         0 my $res = $s->_rh->_get(
202             Mojo::URL->new( 'https://api.robinhood.com/marketdata/forex/historicals/' . $s->id . '/' )
203             ->query( \%filters ),
204             );
205              
206 0 0       0 require Finance::Robinhood::Forex::Historicals if $res->is_success;
207             $res->is_success
208 0 0       0 ? Finance::Robinhood::Forex::Historicals->new( _rh => $s->_rh, %{ $res->json } )
  0 0       0  
209             : Finance::Robinhood::Error->new(
210             $res->is_server_error ? ( details => $res->message ) : $res->json );
211             }
212              
213             sub _test_historicals {
214 1   50 1   1937 t::Utility::stash('PAIR') // skip_all();
215 0         0 warn t::Utility::stash('PAIR')->historicals( interval => 'hour' );
216 0         0 isa_ok(
217             t::Utility::stash('PAIR')->historicals( interval => 'hour' ),
218             'Finance::Robinhood::Forex::Historicals'
219             );
220             }
221              
222             =head2 C
223            
224             my $order = $pair->buy(34);
225              
226             Returns a Finance::Robinhood::Forex::OrderBuilder object.
227              
228             Without any additional method calls, this will create an order that looks like
229             this:
230              
231             {
232             account => "XXXXXXXXX",
233             currency_pair_id => "3d961844-d360-45fc-989b-f6fca761d511",
234             price => "111.700000", # Automatically grabs bid price quote on submission
235             quantity => 4, # Actually the amount of crypto you requested
236             side => "buy",
237             time_in_force => "ioc",
238             type => "market"
239             }
240              
241             =cut
242              
243 0     0 1 0 sub buy ( $s, $quantity, $account = $s->_rh->forex_accounts->next ) {
  0         0  
  0         0  
  0         0  
  0         0  
244 0         0 Finance::Robinhood::Forex::OrderBuilder->new(
245             _rh => $s->_rh,
246             _pair => $s,
247             _account => $account,
248             quantity => $quantity
249             )->buy;
250             }
251              
252             sub _test_buy {
253 1     1   1851 my $rh = t::Utility::rh_instance(1);
254 0         0 my $btc_usd = $rh->forex_pair_by_id('3d961844-d360-45fc-989b-f6fca761d511');
255             #
256 0         0 my $market = $btc_usd->buy(4);
257 0         0 is(
258             { $market->_dump(1) },
259             {
260             account => '--private--',
261             currency_pair_id => '3d961844-d360-45fc-989b-f6fca761d511',
262             quantity => 4,
263             side => 'buy',
264             type => 'market',
265             time_in_force => 'gtc',
266             ref_id => '00000000-0000-0000-0000-000000000000',
267             price => '5.00'
268             }
269             );
270              
271             #->stop(43)->limit(55);#->submit;
272             #ddx \{$order->_dump};
273 0     0   0 todo( "Write actual tests!" => sub { pass('ugh') } );
  0         0  
274              
275             #my $news = t::Utility::stash('MSFT')->news;
276             #isa_ok( $news, 'Finance::Robinhood::Utility::Iterator' );
277             #isa_ok( $news->current, 'Finance::Robinhood::News' );
278             }
279              
280             =head2 C
281            
282             my $order = $btc->sell(34);
283              
284             Returns a Finance::Robinhood::Forex::OrderBuilder object.
285              
286             Without any additional method calls, this will create an order that looks like
287             this:
288              
289             {
290             account => "XXXXXXXXXX",
291             pair_id => "3d961844-d360-45fc-989b-f6fca761d511",
292             price => "111.700000", # Automatically grabs ask price quote on submission
293             quantity => 4, # Actually the amount of currency you requested
294             side => "sell",
295             time_in_force => "ioc",
296             type => "market"
297             }
298              
299             =cut
300              
301 0     0 1 0 sub sell ( $s, $quantity, $account = $s->_rh->forex_accounts->next ) {
  0         0  
  0         0  
  0         0  
  0         0  
302 0         0 Finance::Robinhood::Forex::OrderBuilder->new(
303             _rh => $s->_rh,
304             _pair => $s,
305             _account => $account,
306             quantity => $quantity
307             )->sell;
308             }
309              
310             sub _test_sell {
311 1     1   1820 my $rh = t::Utility::rh_instance(1);
312 0           my $btc_usd = $rh->forex_pair_by_id('3d961844-d360-45fc-989b-f6fca761d511');
313             #
314 0           my $market = $btc_usd->sell(4);
315 0           is(
316             { $market->_dump(1) },
317             {
318             account => '--private--',
319             currency_pair_id => '3d961844-d360-45fc-989b-f6fca761d511',
320             quantity => 4,
321             side => 'sell',
322             type => 'market',
323             time_in_force => 'gtc',
324             ref_id => '00000000-0000-0000-0000-000000000000',
325             price => '5.00'
326             }
327             );
328              
329             #->stop(43)->limit(55);#->submit;
330             #ddx \{$order->_dump};
331 0     0     todo( "Write actual tests!" => sub { pass('ugh') } );
  0            
332              
333             #my $news = t::Utility::stash('MSFT')->news;
334             #isa_ok( $news, 'Finance::Robinhood::Utility::Iterator' );
335             #isa_ok( $news->current, 'Finance::Robinhood::News' );
336             }
337              
338             =head1 LEGAL
339              
340             This is a simple wrapper around the API used in the official apps. The author
341             provides no investment, legal, or tax advice and is not responsible for any
342             damages incurred while using this software. This software is not affiliated
343             with Robinhood Financial LLC in any way.
344              
345             For Robinhood's terms and disclosures, please see their website at
346             https://robinhood.com/legal/
347              
348             =head1 LICENSE
349              
350             Copyright (C) Sanko Robinson.
351              
352             This library is free software; you can redistribute it and/or modify it under
353             the terms found in the Artistic License 2. Other copyrights, terms, and
354             conditions may apply to data transmitted through this module. Please refer to
355             the L section.
356              
357             =head1 AUTHOR
358              
359             Sanko Robinson Esanko@cpan.orgE
360              
361             =cut
362              
363             1;