Repository: rsheftel/pandas_market_calendars

Issue:
Issues with merge_schedules when list is larger than 2
Firstly thanks for the great project, it's very helpful. I am having issues with `merge_schedules` when the input list is greater than two. The issue seems to be that multiple calls to `pd.merge` happen causing repeated columns, e.g. `market_open_x`, which then fails when the `lambda` function is applied. Here is an illustrative example

```python
import pandas_market_calendars as mcal

start_date = "20170103"
end_date = "20170104"

cme = mcal.get_calendar("CME")
nyse = mcal.get_calendar("NYSE")
ice = mcal.get_calendar("ICE")

s1 = cme.schedule(start_date, end_date)
s2 = nyse.schedule(start_date, end_date)
s3 = ice.schedule(start_date, end_date)

schedules = [s1, s2, s3]

mcal.merge_schedules(schedules, how='inner')

ValueError: ('Can only compare identically-labeled Series objects', 'occurred at index 2017-01-03 00:00:00')
```

As described above, here is an illustration of the internal code causing the
issue

```python
how = "inner"
result = s1
result = result.merge(s2, how=how, right_index=True, left_index=True)
result['market_open'] = result.apply(lambda x: max(x.market_open_x, x.market_open_y), axis=1)
result['market_close'] = result.apply(lambda x: min(x.market_close_x, x.market_close_y), axis=1)
result = result.merge(s3, how=how, right_index=True, left_index=True)
print(result)
```
```
                       market_open_x            market_close_x  \
2017-01-03 2017-01-02 23:01:00+00:00 2017-01-03 23:00:00+00:00   
2017-01-04 2017-01-03 23:01:00+00:00 2017-01-04 23:00:00+00:00   

                       market_open_y            market_close_y  \
2017-01-03 2017-01-03 14:30:00+00:00 2017-01-03 21:00:00+00:00   
2017-01-04 2017-01-04 14:30:00+00:00 2017-01-04 21:00:00+00:00   

                       market_open_x            market_close_x  \
2017-01-03 2017-01-03 14:30:00+00:00 2017-01-03 21:00:00+00:00   
2017-01-04 2017-01-04 14:30:00+00:00 2017-01-04 21:00:00+00:00   

                       market_open_y            market_close_y  
2017-01-03 2017-01-03 01:01:00+00:00 2017-01-03 23:00:00+00:00  
2017-01-04 2017-01-04 01:01:00+00:00 2017-01-04 23:00:00+00:00 
```

In addition, the use of `pop` on the input list has the side effect of changing
the input, I believe this is an unintended side effect?

Discussion:

Patch:
diff --git a/pandas_market_calendars/calendar_utils.py b/pandas_market_calendars/calendar_utils.py
index 55d1c3d..62f51eb 100644
--- a/pandas_market_calendars/calendar_utils.py
+++ b/pandas_market_calendars/calendar_utils.py
@@ -57,9 +57,9 @@ def merge_schedules(schedules, how='outer'):
     :param how: outer or inner
     :return: schedule DataFrame
     """
-    
-    result = schedules.pop(0)
-    for schedule in schedules:
+
+    result = schedules[0]
+    for schedule in schedules[1:]:
         result = result.merge(schedule, how=how, right_index=True, left_index=True)
         if how == 'outer':
             result['market_open'] = result.apply(lambda x: min(x.market_open_x, x.market_open_y), axis=1)
@@ -69,24 +69,25 @@ def merge_schedules(schedules, how='outer'):
             result['market_close'] = result.apply(lambda x: min(x.market_close_x, x.market_close_y), axis=1)
         else:
             raise ValueError('how argument must be "inner" or "outer"')
-    return result[['market_open', 'market_close']]
+        result = result[['market_open', 'market_close']]
+    return result
 
 
 def date_range(schedule, frequency, closed='right', force_close=True, **kwargs):
     """
-    Given a schedule will return a DatetimeIndex will all of the valid datetime at the frequency given. 
+    Given a schedule will return a DatetimeIndex will all of the valid datetime at the frequency given.
     The schedule values are assumed to be in UTC.
 
     :param schedule: schedule DataFrame
     :param frequency: frequency in standard string
     :param closed: same meaning as pandas date_range. 'right' will exclude the first value and should be used when the
       results should only include the close for each bar.
-    :param force_close: if True then the close of the day will be included even if it does not fall on an even 
+    :param force_close: if True then the close of the day will be included even if it does not fall on an even
       frequency. If False then the market close for the day may not be included in the results
     :param kwargs: arguments that will be passed to the pandas date_time
     :return: DatetimeIndex
     """
-    
+
     kwargs['closed'] = closed
     ranges = list()
     for row in schedule.itertuples():


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 743f84e..05a4a98 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -200,5 +200,9 @@ def test_merge_schedules():
     actual = mcal.merge_schedules([sch1, sch2], how='inner')
     assert_frame_equal(actual, expected)
 
+    # joining more than two calendars works correctly
+    actual = mcal.merge_schedules([sch1, sch1, sch1], how='inner')
+    assert_frame_equal(actual, sch1)
+
     with pytest.raises(ValueError):
         mcal.merge_schedules([sch1, sch2], how='left')