1+ <?php
2+
3+ namespace DPRMC \InteractiveData ;
4+
5+ use DPRMC \CUSIP ;
6+ use DPRMC \InteractiveData \RemotePlusClient \Exceptions \UnparsableDateSentToConstructor ;
7+
8+ /**
9+ * Class ClientCustomPriceFixedIncome
10+ * @package DPRMC\InteractiveData
11+ */
12+ class ClientCustomPriceFixedIncome extends RemotePlusClient {
13+
14+ /**
15+ * @var string YYYY-mm-dd The date you want pricing for.
16+ */
17+ protected $ date ;
18+
19+ /**
20+ * @var array The list of CUSIP identifiers that you want daily pricing on.
21+ */
22+ protected $ cusips = [];
23+
24+ /**
25+ * @var array A list of all of the CUSIPs passed into the constructor that were not valid CUSIPs.
26+ */
27+ protected $ invalidCusips = [];
28+
29+
30+ protected $ pricesRequested = [];
31+
32+
33+
34+ /**
35+ * ClientCustomPriceFixedIncome constructor.
36+ * @param $user
37+ * @param $pass
38+ * @param $date
39+ * @param $cusips
40+ * @param bool $debug
41+ * @throws \Exception
42+ */
43+ public function __construct ( $ user , $ pass , $ date , $ cusips , $ debug = FALSE ) {
44+ parent ::__construct ( $ user , $ pass );
45+ $ this ->remotePlusDebug = $ debug ;
46+ $ this ->date = $ this ->formatDateForRemotePlus ( $ date );
47+ $ this ->cusips = $ this ->pruneInvalidCusips ( $ cusips );
48+ $ this ->generateBodyForRequest ();
49+ }
50+
51+ /**
52+ * @return $this
53+ */
54+ public function addBestPrice (){
55+ $ this ->pricesRequested [] = 'PRC ' ; // number 16.7
56+ return $ this ;
57+ }
58+
59+ /**
60+ * @return $this
61+ */
62+ public function addBid () {
63+ $ this ->pricesRequested [] = 'BID ' ; // number 16.7
64+ return $ this ;
65+ }
66+
67+ /**
68+ * @return $this
69+ */
70+ public function addMid () {
71+ $ this ->pricesRequested [] = 'MID ' ; // number 16.7
72+ return $ this ;
73+ }
74+
75+ /**
76+ * @return $this
77+ */
78+ public function addAsk () {
79+ $ this ->pricesRequested [] = 'ASK ' ; // number 16.7
80+ return $ this ;
81+ }
82+
83+
84+ /**
85+ * The Remote Plus API is particular about how dates are formatted.
86+ *
87+ * @param string $date A string that can be parsed by PHP's strtotime() function.
88+ *
89+ * @return string A date formatted as YYYYMMDD that can be read by Remote Plus.
90+ * @throws \Exception Only thrown if the user passes some garbage into the constructor.
91+ */
92+ protected function formatDateForRemotePlus ( $ date ) {
93+ $ strTime = strtotime ( $ date );
94+ if ( $ strTime === FALSE ):
95+ throw new UnparsableDateSentToConstructor ( "We could not parse the date you sent to the constructor: [ " . $ date . "] " );
96+ endif ;
97+ $ date = date ( 'Ymd ' , $ strTime );
98+
99+ return $ date ;
100+ }
101+
102+ /**
103+ * We don't want to waste time (or money) getting prices on identifiers that are not valid cusips.
104+ * Prune out the invalid cusips and save those in the local $invalidCusips property.
105+ *
106+ * @param array $cusips A list of cusips passed in by the user in the constructor.
107+ *
108+ * @return array A list of cusips pruned of any values that aren't valid cusips.
109+ */
110+ protected function pruneInvalidCusips ( $ cusips ) {
111+ $ validCusips = [];
112+ foreach ( $ cusips as $ cusip ):
113+ if ( CUSIP ::isCUSIP ( $ cusip ) ):
114+ $ validCusips [] = $ cusip ;
115+ else :
116+ $ this ->invalidCusips [] = $ cusip ;
117+ endif ;
118+ endforeach ;
119+
120+ return $ validCusips ;
121+ }
122+
123+ /**
124+ * The Remote Plus API requires the request body to be formatted in a very specific way.
125+ * The following body is formatted to pull the prices for a list of CUSIPs from a specific date.
126+ */
127+ protected function generateBodyForRequest () {
128+ $ pricesRequested = $ this ->getPricesRequested ();
129+
130+ $ this ->requestBody = 'Request= ' . urlencode ( "GET,( " . implode ( ', ' , $ this ->cusips ) . "),( " . $ pricesRequested . "), " . $ this ->date ) . "&Done=flag \n" ;
131+ }
132+
133+ /**
134+ * @return string
135+ * @throws \Exception
136+ */
137+ protected function getPricesRequested () {
138+ if ( empty ( $ pricesRequested ) ):
139+ throw new \Exception ( "You have to add at least one price for this request. " );
140+ endif ;
141+
142+ return implode ( ', ' , $ pricesRequested );
143+ }
144+
145+ /**
146+ * @return array
147+ */
148+ protected function processResponse () {
149+ $ body = $ this ->getBodyFromResponse ();
150+ $ prices = explode ( "\n" , $ body );
151+ $ prices = array_map ( 'trim ' , $ prices );
152+ $ prices = array_filter ( $ prices );
153+ array_pop ( $ prices ); // Remove the CRC check.
154+
155+ $ return = [];
156+ foreach ( $ this ->cusips as $ i => $ cusip ):
157+ $ return [ $ cusip ] = $ this ->formatValueReturnedFromInteractiveData ( $ prices [ $ i ] );
158+ endforeach ;
159+
160+ return $ return ;
161+ }
162+
163+ /**
164+ * Extracted this into it's own function so I can stub and test without
165+ * having to make a request to the IDC server.
166+ * @return string
167+ * @codeCoverageIgnore
168+ */
169+ protected function getBodyFromResponse () {
170+ return (string )$ this ->response ->getBody ();
171+ }
172+
173+ /**
174+ * @param $value
175+ *
176+ * @return float
177+ */
178+ protected function formatValueReturnedFromInteractiveData ( $ value ) {
179+ if ( is_numeric ( $ value ) ) {
180+ return (float )$ value ;
181+ }
182+
183+ // @codeCoverageIgnoreStart
184+ return (float )str_replace ( '" ' , '' , $ value );
185+ // @codeCoverageIgnoreEnd
186+ }
187+ }
0 commit comments