44import pandas as pd
55from pandas .tseries .offsets import MonthBegin
66from six import iteritems
7+ from toolz import merge
8+ from trading_calendars import get_calendar
79
810from .futures import CMES_CODE_TO_MONTH
911
1012
11- def make_rotating_equity_info (num_assets ,
13+ def make_rotating_equity_info (sids ,
1214 first_start ,
1315 frequency ,
1416 periods_between_starts ,
@@ -38,6 +40,7 @@ def make_rotating_equity_info(num_assets,
3840 info : pd.DataFrame
3941 DataFrame representing newly-created assets.
4042 """
43+ num_assets = len (sids )
4144 return pd .DataFrame (
4245 {
4346 'symbol' : [chr (ord ('A' ) + i ) for i in range (num_assets )],
@@ -55,7 +58,7 @@ def make_rotating_equity_info(num_assets,
5558 ),
5659 'exchange' : exchange ,
5760 },
58- index = range ( num_assets ) ,
61+ index = sids ,
5962 )
6063
6164
@@ -117,12 +120,13 @@ def make_simple_equity_info(sids,
117120 )
118121
119122
120- def make_jagged_equity_info (num_assets ,
123+ def make_jagged_equity_info (sids ,
121124 start_date ,
122125 first_end ,
123126 frequency ,
124127 periods_between_ends ,
125- auto_close_delta ):
128+ auto_close_delta ,
129+ exchange = 'TEST' ):
126130 """
127131 Create a DataFrame representing assets that all begin at the same start
128132 date, but have cascading end dates.
@@ -146,6 +150,7 @@ def make_jagged_equity_info(num_assets,
146150 info : pd.DataFrame
147151 DataFrame representing newly-created assets.
148152 """
153+ num_assets = len (sids )
149154 frame = pd .DataFrame (
150155 {
151156 'symbol' : [chr (ord ('A' ) + i ) for i in range (num_assets )],
@@ -155,9 +160,9 @@ def make_jagged_equity_info(num_assets,
155160 freq = (periods_between_ends * frequency ),
156161 periods = num_assets ,
157162 ),
158- 'exchange' : 'TEST' ,
163+ 'exchange' : exchange ,
159164 },
160- index = range ( num_assets ) ,
165+ index = sids ,
161166 )
162167
163168 # Explicitly pass None to disable setting the auto_close_date column.
@@ -167,6 +172,61 @@ def make_jagged_equity_info(num_assets,
167172 return frame
168173
169174
175+ def make_multi_exchange_equity_info (factory ,
176+ exchange_sids ,
177+ exchange_kwargs = None ,
178+ ** common_kwargs ):
179+ """
180+ Create an "equity_info" DataFrame for multiple exchanges by calling an
181+ existing factory function for each exchange and concatting the results.
182+
183+ Parameters
184+ ----------
185+ factory : function
186+ Function to use to create equity info for each exchange.
187+ exchange_sids : dict[str -> list[sids]]
188+ Map from exchange to list of sids to be created for that exchange.
189+ exchange_kwargs : dict[str -> dict], optional
190+ Map from exchange to additional kwargs to be passed for that exchange.
191+ **common_kwargs
192+ Additional keyword-arguments are forwarded to ``factory``.
193+
194+ Returns
195+ -------
196+ info : pd.DataFrame
197+ DataFrame representing newly-created assets.
198+ """
199+ if exchange_kwargs is None :
200+ exchange_kwargs = {e : {} for e in exchange_sids }
201+ else :
202+ assert exchange_kwargs .keys () == exchange_sids .keys ()
203+
204+ # When using frequency-based factories, use each calendar's trading
205+ # calendar for frequency by default.
206+ provide_default_frequency = (
207+ 'frequency' not in common_kwargs
208+ and factory in (make_rotating_equity_info , make_jagged_equity_info )
209+ )
210+ if provide_default_frequency :
211+ for e , kw in iteritems (exchange_kwargs ):
212+ kw .setdefault ('frequency' , get_calendar (e ).day )
213+
214+ frame_per_exchange = [
215+ factory (
216+ sids = sids ,
217+ exchange = e ,
218+ ** merge (common_kwargs , exchange_kwargs [e ])
219+ )
220+ for e , sids in iteritems (exchange_sids )
221+ ]
222+
223+ result = pd .concat (frame_per_exchange )
224+ if not result .index .is_unique :
225+ raise AssertionError ("Duplicate sids: {}" .format (result .index ))
226+
227+ return result
228+
229+
170230def make_future_info (first_sid ,
171231 root_symbols ,
172232 years ,
0 commit comments