99
1010
1111class FXRateReader (Interface ):
12+ """
13+ Interface for reading foreign exchange (fx) rates.
14+
15+ An FX rate reader contains one or more distinct "rates", each of which
16+ corresponds to a collection of mappings from (quote, base, dt) ->
17+ float. The value produced for a given (quote, base, dt) triple is the
18+ exchange rate to use when converting from ``base`` to ``quote`` on ``dt``.
19+
20+ The specific set of rates contained in a particular reader is
21+ user-defined. We infer no particular semantics from their names, other than
22+ that they are distinct rates. Examples of possible rate names might be
23+ things like "bid", "mid", and "ask", or "london_close", "tokyo_close",
24+ "nyse_close".
25+
26+ Implementations of :class:`FXRateReader` must provide at least one method::
27+
28+ def get_rates(self, rate, quote, bases, dts):
29+
30+ which takes a rate, a quote currency, an array of base currencies, and an
31+ array of dts, and produces a (len(dts), len(base))-shape array containing a
32+ conversion rates for all pairs in the cartesian product of bases and dts.
33+
34+ Given a definition of :meth:`get_rates`, this interface automatically
35+ generates two additional methods::
36+
37+ def get_rates_scalar(self, rate, quote, base, dt):
38+
39+ and::
40+
41+ def get_rates_columnar(self, rate, quote, bases, dts):
42+
43+ :meth:`get_rates_scalar` takes scalar-valued ``base`` and ``dt`` values,
44+ and returns a scalar float value for the requested fx rate.
45+
46+ :meth:`get_rates_columnar` takes parallel arrays of ``bases`` and ``dts``
47+ and returns a same-length array of fx rates by performing a lookup on the
48+ (base, dt) pairs drawn from zipping together ``bases``, and ``dts``. In
49+ other words, its behavior is equivalent to::
50+
51+ def get_rates_columnnar(self, rate, quote, bases, dts):
52+ out = []
53+ for base, dt in zip(bases, dts):
54+ out.append(self.get_rate_scalar(rate, quote, base, dt))
55+ return np.array(out)
56+ """
1257
1358 def get_rates (self , rate , quote , bases , dts ):
1459 """
15- Get rates to convert ``bases`` into ``quote`` .
60+ Load a 2D array of fx rates .
1661
1762 Parameters
1863 ----------
1964 rate : str
20- Rate type to load. Readers intended for use with the Pipeline API
21- should support at least ``zipline.data.fx.DEFAULT_FX_RATE``, which
22- will be used by default for Pipeline API terms that don't specify a
23- specific rate.
65+ Name of the rate to load.
2466 quote : str
2567 Currency code of the currency to convert into.
2668 bases : np.array[object]
27- Array of codes of the currencies to convert from. A single currency
69+ Array of codes of the currencies to convert from. The same currency
2870 may appear multiple times.
2971 dts : pd.DatetimeIndex
3072 Datetimes for which to load rates. Must be sorted in ascending
@@ -42,15 +84,13 @@ def get_rates(self, rate, quote, bases, dts):
4284
4385 @default
4486 def get_rate_scalar (self , rate , quote , base , dt ):
45- """Scalar version of ``get_rates``.
87+ """
88+ Load a scalar FX rate value.
4689
4790 Parameters
4891 ----------
4992 rate : str
50- Rate type to load. Readers intended for use with the Pipeline API
51- should support at least ``zipline.data.fx.DEFAULT_FX_RATE``, which
52- will be used by default for Pipeline API terms that don't specify a
53- specific rate.
93+ Name of the rate to load.
5494 quote : str
5595 Currency code of the currency to convert into.
5696 base : str
@@ -63,10 +103,44 @@ def get_rate_scalar(self, rate, quote, base, dt):
63103 rate : np.float64
64104 Exchange rate from base -> quote on dt.
65105 """
66- rates_array = self .get_rates (
106+ rates_2d = self .get_rates (
67107 rate ,
68108 quote ,
69109 bases = np .array ([base ], dtype = object ),
70110 dts = pd .DatetimeIndex ([dt ], tz = 'UTC' ),
71111 )
72- return rates_array [0 , 0 ]
112+ return rates_2d [0 , 0 ]
113+
114+ @default
115+ def get_rates_columnar (self , rate , quote , bases , dts ):
116+ """
117+ Load a 1D array of FX rates.
118+
119+ Parameters
120+ ----------
121+ rate : str
122+ Name of the rate to load.
123+ quote : str
124+ Currency code of the currency to convert into.
125+ bases : np.array[object]
126+ Array of codes of the currencies to convert from. The same currency
127+ may appear multiple times.
128+ dts : np.DatetimeIndex
129+ Datetimes for which to load rates. The same value may appear
130+ multiple times, but datetimes must be sorted in ascending order and
131+ localized to UTC.
132+ """
133+ if len (bases ) != len (dts ):
134+ raise ValueError (
135+ "len(bases) ({}) != len(dts) ({})" .format (len (bases ), len (dts ))
136+ )
137+
138+ unique_bases , bases_ix = np .unique (bases , return_inverse = True )
139+ unique_dts , dts_ix = np .unique (dts .values , return_inverse = True )
140+ rates_2d = self .get_rates (
141+ rate ,
142+ quote ,
143+ unique_bases ,
144+ pd .DatetimeIndex (unique_dts , tz = 'utc' )
145+ )
146+ return rates_2d [dts_ix , bases_ix ]
0 commit comments