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