r"""
        Return the active contract based on the previous trading day's volume.

        In the rare case that a double volume switch occurs we treat the first
        switch as the roll. Take the following case for example:

        | +++++             _____
        |      +   __      /       <--- 'G'
        |       ++/++\++++/++
        |       _/    \__/   +
        |      /              +
        | ____/                +   <--- 'F'
        |_________|__|___|________
                  a  b   c         <--- Switches

        We should treat 'a' as the roll date rather than 'c' because from the
        perspective of 'a', if a switch happens and we are pretty close to the
        auto-close date, we would probably assume it is time to roll. This
        means that for every date after 'a', `data.current(cf, 'contract')`
        should return the 'G' contract.

def _active_contract(self, oc, front, back, dt):
        r"""
        Return the active contract based on the previous trading day's volume.

        In the rare case that a double volume switch occurs we treat the first
        switch as the roll. Take the following case for example:

        | +++++             _____
        |      +   __      /       <--- 'G'
        |       ++/++\++++/++
        |       _/    \__/   +
        |      /              +
        | ____/                +   <--- 'F'
        |_________|__|___|________
                  a  b   c         <--- Switches

        We should treat 'a' as the roll date rather than 'c' because from the
        perspective of 'a', if a switch happens and we are pretty close to the
        auto-close date, we would probably assume it is time to roll. This
        means that for every date after 'a', `data.current(cf, 'contract')`
        should return the 'G' contract.
        """
        front_contract = oc.sid_to_contract[front].contract
        back_contract = oc.sid_to_contract[back].contract

        tc = self.trading_calendar
        trading_day = tc.day
        prev = dt - trading_day
        get_value = self.session_reader.get_value

        # If the front contract is past its auto close date it cannot be the
        # active contract, so return the back contract. Similarly, if the back
        # contract has not even started yet, just return the front contract.
        # The reason for using 'prev' to see if the contracts are alive instead
        # of using 'dt' is because we need to get each contract's volume on the
        # previous day, so we need to make sure that each contract exists on
        # 'prev' in order to call 'get_value' below.
        if dt > min(front_contract.auto_close_date, front_contract.end_date):
            return back
        elif front_contract.start_date > prev:
            return back
        elif dt > min(back_contract.auto_close_date, back_contract.end_date):
            return front
        elif back_contract.start_date > prev:
            return front

        front_vol = get_value(front, prev, 'volume')
        back_vol = get_value(back, prev, 'volume')
        if back_vol > front_vol:
            return back

        gap_start = max(
            back_contract.start_date,
            front_contract.auto_close_date - (trading_day * self.GRACE_DAYS),
        )
        gap_end = prev - trading_day
        if dt < gap_start:
            return front

        # If we are within `self.GRACE_DAYS` of the front contract's auto close
        # date, and a volume flip happened during that period, return the back
        # contract as the active one.
        sessions = tc.sessions_in_range(
            tc.minute_to_session_label(gap_start),
            tc.minute_to_session_label(gap_end),
        )
        for session in sessions:
            front_vol = get_value(front, session, 'volume')
            back_vol = get_value(back, session, 'volume')
            if back_vol > front_vol:
                return back
        return front