Exemple #1
0
class TestRules(TestCase):
    def setUp(self):
        self.timer = SettableTimer()

    def test_market_open_rule(self):
        self.timer.set_current_time(
            str_to_date("2018-01-01 00:00:00.000000", DateFormat.FULL_ISO))
        now = self.timer.now()
        next_trigger_time = MarketOpenEvent.next_trigger_time(now)
        self.assertEqual(
            str_to_date("2018-01-01 09:30:00.000000", DateFormat.FULL_ISO),
            next_trigger_time)

        self.timer.set_current_time(
            str_to_date("2018-01-01 09:29:59.999999", DateFormat.FULL_ISO))
        now = self.timer.now()
        next_trigger_time = MarketOpenEvent.next_trigger_time(now)
        self.assertEqual(
            str_to_date("2018-01-01 09:30:00.000000", DateFormat.FULL_ISO),
            next_trigger_time)

        self.timer.set_current_time(
            str_to_date("2018-01-01 09:30:00.000000", DateFormat.FULL_ISO))
        now = self.timer.now()
        next_trigger_time = MarketOpenEvent.next_trigger_time(now)
        self.assertEqual(
            str_to_date("2018-01-02 09:30:00.000000", DateFormat.FULL_ISO),
            next_trigger_time)
Exemple #2
0
class TestDataHandler(TestCase):
    @classmethod
    def setUpClass(cls):
        cls.spx_index_ticker = BloombergTicker("SPX Index")
        cls.google_ticker = BloombergTicker("GOOGL US Equity")
        cls.microsoft_ticker = BloombergTicker("MSFT US Equity")

        cls.start_date = str_to_date("2018-01-02")
        cls.end_date = str_to_date("2018-01-31")
        cls.end_date_trimmed = str_to_date("2018-01-30")
        cls.get_history_field = "PX_TO_BOOK_RATIO"

    def setUp(self):
        try:
            self.price_data_provider = get_data_provider()
        except Exception as e:
            raise self.skipTest(e)

        self.timer = SettableTimer()
        self.data_handler = DailyDataHandler(self.price_data_provider,
                                             self.timer)

        MarketOpenEvent.set_trigger_time({
            "hour": 13,
            "minute": 30,
            "second": 0,
            "microsecond": 0
        })
        MarketCloseEvent.set_trigger_time({
            "hour": 20,
            "minute": 0,
            "second": 0,
            "microsecond": 0
        })

    def test_get_price_when_end_date_is_in_the_past(self):
        self.timer.set_current_time(
            str_to_date("2018-02-12 00:00:00.000000", DateFormat.FULL_ISO))
        prices_tms = self.data_handler.get_price(self.spx_index_ticker,
                                                 PriceField.Close,
                                                 self.start_date,
                                                 self.end_date)

        self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime())
        self.assertEqual(self.end_date, prices_tms.index[-1].to_pydatetime())

    def test_get_price_when_end_date_is_today_after_market_close(self):
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketCloseEvent.trigger_time() +
            RelativeDelta(hours=1))
        prices_tms = self.data_handler.get_price(self.spx_index_ticker,
                                                 PriceField.Close,
                                                 self.start_date,
                                                 self.end_date)

        self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime())
        self.assertEqual(self.end_date, prices_tms.index[-1].to_pydatetime())

    def test_get_price_when_end_date_is_today_before_market_close(self):
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() +
            RelativeDelta(hours=1))
        close_prices_tms = self.data_handler.get_price(self.spx_index_ticker,
                                                       PriceField.Close,
                                                       self.start_date,
                                                       self.end_date)

        self.assertEqual(self.start_date,
                         close_prices_tms.index[0].to_pydatetime())
        self.assertEqual(self.end_date_trimmed,
                         close_prices_tms.index[-1].to_pydatetime())

    def test_get_open_price_when_end_date_is_today_before_market_close__single_ticker(
            self):
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() +
            RelativeDelta(hours=1))
        current_date = datetime(self.timer.now().year,
                                self.timer.now().month,
                                self.timer.now().day)
        open_prices_tms = self.data_handler.get_price(self.spx_index_ticker,
                                                      PriceField.Open,
                                                      self.start_date,
                                                      self.timer.now())

        self.assertEqual(self.start_date,
                         open_prices_tms.index[0].to_pydatetime())
        self.assertEqual(current_date,
                         open_prices_tms.index[-1].to_pydatetime())

        prices_df = self.data_handler.get_price(
            self.spx_index_ticker, [PriceField.Open, PriceField.Close],
            self.start_date, self.timer.now())
        self.assertEqual(self.start_date, prices_df.index[0].to_pydatetime())
        self.assertEqual(current_date, prices_df.index[-1].to_pydatetime())

        last_bar = prices_df.iloc[-1]
        self.assertTrue(isnan(last_bar.loc[PriceField.Close]))
        self.assertIsNotNone(last_bar.loc[PriceField.Open])

    def test_get_open_price_when_end_date_is_today_before_market_close__multiple_tickers(
            self):
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() +
            RelativeDelta(hours=1))
        current_date = datetime(self.timer.now().year,
                                self.timer.now().month,
                                self.timer.now().day)

        tickers = [self.spx_index_ticker, self.microsoft_ticker]
        open_prices_tms = self.data_handler.get_price(tickers, PriceField.Open,
                                                      self.start_date,
                                                      self.timer.now())

        self.assertEqual(self.start_date,
                         open_prices_tms.index[0].to_pydatetime())
        self.assertEqual(current_date,
                         open_prices_tms.index[-1].to_pydatetime())

        prices_data_array = self.data_handler.get_price(
            tickers, [PriceField.Open, PriceField.Close], self.start_date,
            self.timer.now())
        last_bar_df = prices_data_array.loc[current_date, :, :].to_pandas()

        self.assertTrue(last_bar_df[PriceField.Close].isna().all())
        self.assertTrue(last_bar_df[PriceField.Open].notna().all())

    def test_get_price_when_end_date_is_tomorrow(self):
        self.timer.set_current_time(
            str_to_date("2018-01-30") + MarketCloseEvent.trigger_time() +
            RelativeDelta(hours=1))
        prices_tms = self.data_handler.get_price(self.spx_index_ticker,
                                                 PriceField.Close,
                                                 self.start_date,
                                                 self.end_date_trimmed)

        self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime())
        self.assertEqual(self.end_date_trimmed,
                         prices_tms.index[-1].to_pydatetime())

    def test_get_last_price_single_ticker(self):
        # just test if getting single ticker value works, when a single ticker (not wrapped in a list) is passed
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() +
            RelativeDelta(hours=1))
        single_price = self.data_handler.get_last_available_price(
            self.spx_index_ticker)
        self.assertTrue(isinstance(single_price, float))

        # at market open
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketOpenEvent.trigger_time())
        at_market_open = self.data_handler.get_last_available_price(
            [self.spx_index_ticker])

        self.assertEqual(self.spx_index_ticker, at_market_open.index[0])
        self.assertEqual(single_price, at_market_open[0])

        # during the trading session
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() +
            RelativeDelta(hours=1))
        during_the_day_last_prices = self.data_handler.get_last_available_price(
            [self.spx_index_ticker])

        self.assertEqual(self.spx_index_ticker,
                         during_the_day_last_prices.index[0])
        self.assertEqual(single_price, during_the_day_last_prices[0])

        # after the trading session
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketCloseEvent.trigger_time() +
            RelativeDelta(hours=1))
        after_close_last_prices = self.data_handler.get_last_available_price(
            [self.spx_index_ticker])

        self.assertEqual(self.spx_index_ticker,
                         after_close_last_prices.index[0])
        self.assertNotEqual(during_the_day_last_prices[0],
                            after_close_last_prices[0])

        # before the trading session
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() -
            RelativeDelta(hours=1))
        before_trading_session_prices = self.data_handler.get_last_available_price(
            [self.spx_index_ticker])

        self.assertEqual(self.spx_index_ticker,
                         before_trading_session_prices.index[0])
        self.assertNotEqual(during_the_day_last_prices[0],
                            before_trading_session_prices[0])
        self.assertNotEqual(after_close_last_prices[0],
                            before_trading_session_prices[0])

    def test_get_last_price_with_multiple_tickers_when_current_data_is_unavailable(
            self):
        self.timer.set_current_time(
            str_to_date("2018-01-01") + MarketOpenEvent.trigger_time() +
            RelativeDelta(hours=1))
        last_prices = self.data_handler.get_last_available_price(
            [self.spx_index_ticker, self.google_ticker])

        self.assertEqual(self.spx_index_ticker, last_prices.index[0])
        self.assertEqual(self.google_ticker, last_prices.index[1])

    def test_get_last_price_with_empty_tickers_list(self):
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() +
            RelativeDelta(hours=1))
        last_prices = self.data_handler.get_last_available_price([])
        assert_series_equal(QFSeries(), last_prices)

    def test_get_history_when_end_date_is_in_the_past(self):
        self.timer.set_current_time(
            str_to_date("2018-02-12 00:00:00.000000", DateFormat.FULL_ISO))
        prices_tms = self.data_handler.get_history(self.spx_index_ticker,
                                                   self.get_history_field,
                                                   self.start_date,
                                                   self.end_date)

        self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime())
        self.assertEqual(self.end_date, prices_tms.index[-1].to_pydatetime())

    def test_get_history_when_end_date_is_today_after_market_close(self):
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketCloseEvent.trigger_time() +
            RelativeDelta(hours=1))
        prices_tms = self.data_handler.get_history(self.spx_index_ticker,
                                                   self.get_history_field,
                                                   self.start_date,
                                                   self.end_date)

        self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime())
        self.assertEqual(self.end_date, prices_tms.index[-1].to_pydatetime())

    def test_get_history_when_end_date_is_today_before_market_close(self):
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() +
            RelativeDelta(hours=1))
        prices_tms = self.data_handler.get_history(self.spx_index_ticker,
                                                   self.get_history_field,
                                                   self.start_date,
                                                   self.end_date)

        self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime())
        self.assertEqual(self.end_date_trimmed,
                         prices_tms.index[-1].to_pydatetime())

    def test_get_history_when_end_date_is_tomorrow(self):
        self.timer.set_current_time(
            str_to_date("2018-01-30") + MarketCloseEvent.trigger_time() +
            RelativeDelta(hours=1))
        prices_tms = self.data_handler.get_history(self.spx_index_ticker,
                                                   self.get_history_field,
                                                   self.start_date,
                                                   self.end_date_trimmed)

        self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime())
        self.assertEqual(self.end_date_trimmed,
                         prices_tms.index[-1].to_pydatetime())

    def test_get_history_with_multiple_tickers(self):
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() +
            RelativeDelta(hours=1))
        resilt_df = self.data_handler.get_history(
            [self.microsoft_ticker, self.google_ticker],
            self.get_history_field, self.start_date, self.end_date_trimmed)

        self.assertEqual(self.microsoft_ticker, resilt_df.columns[0])
        self.assertEqual(self.google_ticker, resilt_df.columns[1])
        self.assertEqual(self.start_date, resilt_df.index[0].to_pydatetime())
        self.assertEqual(self.end_date_trimmed,
                         resilt_df.index[-1].to_pydatetime())
        self.assertEqual(resilt_df.shape, (20, 2))

    def test_historical_price_many_tickers_many_fields(self):
        self.timer.set_current_time(
            str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() +
            RelativeDelta(hours=1))
        result_array = self.data_handler.historical_price(
            [self.microsoft_ticker], [PriceField.Open, PriceField.Close],
            nr_of_bars=5)

        self.assertEqual(QFDataArray, type(result_array))
        self.assertEqual((5, 1, 2), result_array.shape)

        expected_dates_str = [
            "2018-01-24", "2018-01-25", "2018-01-26", "2018-01-29",
            "2018-01-30"
        ]
        expected_dates = [
            str_to_date(date_str) for date_str in expected_dates_str
        ]
        assert_same_index(pd.DatetimeIndex(expected_dates, name=DATES),
                          result_array.dates.to_index(),
                          check_index_type=True,
                          check_names=True)

    def test_historical_price_many_tickers_one_field(self):
        self.timer.set_current_time(
            str_to_date("2018-01-04") + MarketOpenEvent.trigger_time() +
            RelativeDelta(hours=1))
        result_df = self.data_handler.historical_price([self.microsoft_ticker],
                                                       PriceField.Open,
                                                       nr_of_bars=5)

        self.assertEqual(PricesDataFrame, type(result_df))

        expected_dates_idx = pd.DatetimeIndex([
            '2017-12-27', '2017-12-28', '2017-12-29', '2018-01-02',
            '2018-01-03'
        ],
                                              name=DATES)
        assert_same_index(expected_dates_idx,
                          result_df.index,
                          check_index_type=True,
                          check_names=True)

        expected_tickers_idx = pd.Index([self.microsoft_ticker], name=TICKERS)
        assert_same_index(expected_tickers_idx,
                          result_df.columns,
                          check_index_type=True,
                          check_names=True)
class TestMarketOnOpenExecutionStyle(TestCase):
    MarketOpenEvent.set_trigger_time({
        "hour": 13,
        "minute": 30,
        "second": 0,
        "microsecond": 0
    })
    MarketCloseEvent.set_trigger_time({
        "hour": 20,
        "minute": 0,
        "second": 0,
        "microsecond": 0
    })

    def setUp(self):
        self.scheduling_time_delay = 1
        start_date = str_to_date("2018-02-04")

        before_close = start_date + MarketCloseEvent.trigger_time(
        ) - RelativeDelta(minutes=self.scheduling_time_delay)
        self.timer = SettableTimer(initial_time=before_close)

        contracts_to_tickers_mapper = SimulatedBloombergContractTickerMapper()
        msft_contract = Contract("MSFT US Equity",
                                 security_type='STK',
                                 exchange='TEST')
        self.msft_ticker = contracts_to_tickers_mapper.contract_to_ticker(
            msft_contract)

        self.data_handler = Mock(spec=DataHandler)
        self.scheduler = Mock(spec=Scheduler)

        self.commission_model = FixedCommissionModel(commission=0.0)
        self.monitor = Mock(spec=AbstractMonitor)
        self.portfolio = Mock(spec=Portfolio)

        slippage_model = PriceBasedSlippage(0.0, self.data_handler,
                                            contracts_to_tickers_mapper)
        self.exec_handler = SimulatedExecutionHandler(
            self.data_handler, self.timer, self.scheduler, self.monitor,
            self.commission_model, contracts_to_tickers_mapper, self.portfolio,
            slippage_model, RelativeDelta(minutes=self.scheduling_time_delay))

        self.order_1 = Order(msft_contract,
                             quantity=10,
                             execution_style=MarketOrder(),
                             time_in_force=TimeInForce.OPG)
        self.order_2 = Order(msft_contract,
                             quantity=-5,
                             execution_style=MarketOrder(),
                             time_in_force=TimeInForce.OPG)
        self.order_3 = Order(msft_contract,
                             quantity=-7,
                             execution_style=MarketOrder(),
                             time_in_force=TimeInForce.OPG)

        self.order_4 = Order(msft_contract,
                             quantity=4,
                             execution_style=MarketOnCloseOrder(),
                             time_in_force=TimeInForce.DAY)

    def _trigger_single_time_event(self):
        self.timer.set_current_time(self.timer.now() + RelativeDelta(
            minutes=self.scheduling_time_delay))
        event = ScheduleOrderExecutionEvent()
        self.exec_handler.on_orders_accept(event)

    def test_1_order_fill(self):
        self.exec_handler.assign_order_ids([self.order_1])
        self._set_current_price(101)
        self._trigger_single_time_event()
        self.exec_handler.on_market_open(...)

        self.monitor.record_transaction.assert_called_once()
        self.portfolio.transact_transaction.assert_called_once()

        actual_orders = self.exec_handler.get_open_orders()
        expected_orders = []
        assert_lists_equal(expected_orders, actual_orders)

    def test_3_orders_fill(self):
        self.exec_handler.assign_order_ids(
            [self.order_1, self.order_2, self.order_3])
        self._set_current_price(101)
        self._trigger_single_time_event()
        self.exec_handler.on_market_open(...)

        self.assertEqual(self.monitor.record_transaction.call_count, 3)
        self.assertEqual(self.portfolio.transact_transaction.call_count, 3)

        actual_orders = self.exec_handler.get_open_orders()
        expected_orders = []
        assert_lists_equal(expected_orders, actual_orders)

    def test_3_orders_fill_only_at_open(self):
        self.exec_handler.assign_order_ids(
            [self.order_1, self.order_2, self.order_3])
        self._set_current_price(101)
        self._trigger_single_time_event()
        self.exec_handler.on_market_close(...)

        self.portfolio.transact_transaction.assert_not_called()
        self.monitor.record_transaction.assert_not_called()

        actual_orders = self.exec_handler.get_open_orders()
        expected_orders = [self.order_1, self.order_2, self.order_3]
        self.assertCountEqual(expected_orders, actual_orders)

    def test_fill_open_and_close(self):
        self.exec_handler.assign_order_ids([self.order_1, self.order_2])
        self.exec_handler.assign_order_ids([self.order_2, self.order_3])
        self.exec_handler.assign_order_ids([self.order_3, self.order_4])
        self.exec_handler.assign_order_ids([self.order_4, self.order_4])

        self._set_current_price(101)
        self._trigger_single_time_event()
        self.exec_handler.on_market_open(...)

        self.assertEqual(self.monitor.record_transaction.call_count, 3)
        self.assertEqual(self.portfolio.transact_transaction.call_count, 3)

        actual_orders = self.exec_handler.get_open_orders()
        expected_orders = [self.order_4]
        assert_lists_equal(expected_orders, actual_orders)

        self.exec_handler.on_market_close(...)

        self.assertEqual(self.monitor.record_transaction.call_count, 4)
        self.assertEqual(self.portfolio.transact_transaction.call_count, 4)

        actual_orders = self.exec_handler.get_open_orders()
        expected_orders = []
        assert_lists_equal(expected_orders, actual_orders)

    def test_fill_close_and_open(self):
        self.exec_handler.assign_order_ids([self.order_1, self.order_2])
        self.exec_handler.assign_order_ids([self.order_2, self.order_3])
        self.exec_handler.assign_order_ids([self.order_3, self.order_4])
        self.exec_handler.assign_order_ids([self.order_4, self.order_4])

        self._set_current_price(101)

        self._trigger_single_time_event()
        self.exec_handler.on_market_close(...)

        # Transaction related to order 4 will be executed only once, as only one Order object was passed
        self.monitor.record_transaction.assert_called_once()
        self.portfolio.transact_transaction.assert_called_once()

        actual_orders = self.exec_handler.get_open_orders()
        expected_orders = [self.order_1, self.order_2, self.order_3]
        self.assertCountEqual(expected_orders, actual_orders)

        self.exec_handler.on_market_open(...)

        self.assertEqual(self.monitor.record_transaction.call_count, 4)
        self.assertEqual(self.portfolio.transact_transaction.call_count, 4)

        actual_orders = self.exec_handler.get_open_orders()
        expected_orders = []
        self.assertCountEqual(expected_orders, actual_orders)

    def test_market_open_transaction(self):
        order = self.order_1
        price = 102
        self.exec_handler.assign_order_ids([order])
        self._set_current_price(price)
        self._trigger_single_time_event()
        self.exec_handler.on_market_open(...)

        timestamp = self.timer.now()
        contract = order.contract
        quantity = order.quantity
        commission = self.commission_model.calculate_commission(order, price)
        expected_transaction = Transaction(timestamp, contract, quantity,
                                           price, commission)

        self.monitor.record_transaction.assert_called_once_with(
            expected_transaction)

    def test_market_close_transaction(self):
        order = self.order_4
        price = 102
        self.exec_handler.assign_order_ids([self.order_1, order])
        self._set_current_price(price)
        self._trigger_single_time_event()
        self.exec_handler.on_market_close(...)

        timestamp = self.timer.now()
        contract = order.contract
        quantity = order.quantity
        commission = self.commission_model.calculate_commission(order, price)
        expected_transaction = Transaction(timestamp, contract, quantity,
                                           price, commission)

        self.monitor.record_transaction.assert_called_once_with(
            expected_transaction)

    def test_market_close_does_not_trade(self):
        price = None
        self.exec_handler.assign_order_ids([self.order_1, self.order_4])
        self._set_current_price(price)
        self._trigger_single_time_event()
        self.exec_handler.on_market_close(...)

        self.portfolio.transact_transaction.assert_not_called()
        self.monitor.record_transaction.assert_not_called()

    def test_market_open_does_not_trade(self):
        price = None
        self.exec_handler.assign_order_ids([self.order_1, self.order_4])
        self._set_current_price(price)
        self._trigger_single_time_event()
        self.exec_handler.on_market_open(...)

        self.portfolio.transact_transaction.assert_not_called()
        self.monitor.record_transaction.assert_not_called()

    def _set_last_available_price(self, price):
        self.data_handler.get_last_available_price.side_effect = lambda t: QFSeries(
            data=[price], index=pd.Index([self.msft_ticker]))

    def _set_current_price(self, price):
        self.data_handler.get_current_price.side_effect = lambda t: QFSeries(
            data=[price], index=pd.Index([self.msft_ticker]))
Exemple #4
0
class TestStopLossExecutionStyle(TestCase):
    MSFT_TICKER_STR = "MSFT US Equity"

    def setUp(self):
        MarketOpenEvent.set_trigger_time({
            "hour": 13,
            "minute": 30,
            "second": 0,
            "microsecond": 0
        })
        MarketCloseEvent.set_trigger_time({
            "hour": 20,
            "minute": 0,
            "second": 0,
            "microsecond": 0
        })

        self.start_date = str_to_date("2018-02-04")
        self.number_of_minutes = 5

        before_close = self.start_date + MarketCloseEvent.trigger_time(
        ) - RelativeDelta(minutes=self.number_of_minutes)

        self.msft_contract = Contract(self.MSFT_TICKER_STR,
                                      security_type='STK',
                                      exchange='TEST')
        self.msft_ticker = BloombergTicker(self.MSFT_TICKER_STR)

        contracts_to_tickers_mapper = SimulatedBloombergContractTickerMapper()
        self.timer = SettableTimer(initial_time=before_close)

        self.data_handler = Mock(spec=DataHandler)

        scheduler = Mock(spec=Scheduler)
        ScheduleOrderExecutionEvent.clear()

        # Set the periodic bar events to intraday trading
        IntradayBarEvent.frequency = Frequency.MIN_1

        commission_model = FixedCommissionModel(commission=0.0)
        self.monitor = Mock(spec=AbstractMonitor)
        self.portfolio = Mock(spec=Portfolio)

        slippage_model = PriceBasedSlippage(0.0, self.data_handler,
                                            contracts_to_tickers_mapper)
        self.exec_handler = SimulatedExecutionHandler(
            self.data_handler, self.timer, scheduler, self.monitor,
            commission_model, contracts_to_tickers_mapper, self.portfolio,
            slippage_model, RelativeDelta(minutes=self.number_of_minutes))

        self._set_last_available_price(100.0)
        self.stop_loss_order_1 = Order(self.msft_contract,
                                       quantity=-1,
                                       execution_style=StopOrder(95.0),
                                       time_in_force=TimeInForce.GTC)
        self.stop_loss_order_2 = Order(self.msft_contract,
                                       quantity=-1,
                                       execution_style=StopOrder(90.0),
                                       time_in_force=TimeInForce.GTC)

        self.stop_loss_order_3 = Order(self.msft_contract,
                                       quantity=-1,
                                       execution_style=StopOrder(50.0),
                                       time_in_force=TimeInForce.DAY)

        self.exec_handler.assign_order_ids([
            self.stop_loss_order_1, self.stop_loss_order_2,
            self.stop_loss_order_3
        ])

    def _trigger_single_time_event(self):
        self.timer.set_current_time(self.timer.now() + RelativeDelta(
            minutes=self.number_of_minutes))
        event = ScheduleOrderExecutionEvent()
        self.exec_handler.on_orders_accept(event)

    def test_day_order_disappears_after_a_day(self):
        self._set_bar_for_today(open_price=105.0,
                                high_price=110.0,
                                low_price=100.0,
                                close_price=105.0,
                                volume=100000000)
        self._trigger_single_time_event()

        expected_orders = [
            self.stop_loss_order_1, self.stop_loss_order_2,
            self.stop_loss_order_3
        ]
        actual_orders = self.exec_handler.get_open_orders()
        assert_lists_equal(expected_orders, actual_orders)

        self.exec_handler.on_market_close(...)

        self.portfolio.transact_transaction.assert_not_called()
        self.monitor.record_transaction.assert_not_called()

        actual_orders = self.exec_handler.get_open_orders()
        expected_orders = [self.stop_loss_order_1, self.stop_loss_order_2]
        assert_lists_equal(expected_orders, actual_orders)

    def test_no_orders_executed_on_market_open(self):
        # Move before the market open event
        self._set_bar_for_today(open_price=105.0,
                                high_price=110.0,
                                low_price=100.0,
                                close_price=105.0,
                                volume=100000000.0)
        self.timer.set_current_time(self.timer.now() +
                                    MarketOpenEvent.trigger_time() -
                                    RelativeDelta(
                                        minutes=self.number_of_minutes))
        self.exec_handler.assign_order_ids([
            self.stop_loss_order_1, self.stop_loss_order_2,
            self.stop_loss_order_3
        ])
        # Trigger the order execution event (the function also forwards time into the future)
        self._trigger_single_time_event()
        self.exec_handler.on_market_open(...)

        self.portfolio.transact_transaction.assert_not_called()
        self.monitor.record_transaction.assert_not_called()

        actual_orders = self.exec_handler.get_open_orders()
        expected_orders = [
            self.stop_loss_order_1, self.stop_loss_order_2,
            self.stop_loss_order_3
        ]
        assert_lists_equal(expected_orders, actual_orders)

    def test_order_not_executed_when_stop_price_not_hit(self):
        self._set_bar_for_today(open_price=105.0,
                                high_price=110.0,
                                low_price=100.0,
                                close_price=105.0,
                                volume=100000000.0)
        self._trigger_single_time_event()
        self.exec_handler.on_market_close(...)

        self.portfolio.transact_transaction.assert_not_called()
        self.monitor.record_transaction.assert_not_called()

        actual_orders = self.exec_handler.get_open_orders()
        expected_orders = [self.stop_loss_order_1, self.stop_loss_order_2]
        assert_lists_equal(expected_orders, actual_orders)

    def test_order_not_executed_when_bar_for_today_is_incomplete(self):
        self._set_bar_for_today(open_price=None,
                                high_price=110.0,
                                low_price=100.0,
                                close_price=105.0,
                                volume=100000000.0)
        self._trigger_single_time_event()
        self.exec_handler.on_market_close(...)

        self.portfolio.transact_transaction.assert_not_called()
        self.monitor.record_transaction.assert_not_called()

        actual_orders = self.exec_handler.get_open_orders()
        expected_orders = [self.stop_loss_order_1, self.stop_loss_order_2]
        assert_lists_equal(expected_orders, actual_orders)

    def test_one_order_executed_when_one_stop_price_hit(self):
        self._set_bar_for_today(open_price=100.0,
                                high_price=110.0,
                                low_price=94.0,
                                close_price=105.0,
                                volume=100000000.0)
        self._trigger_single_time_event()
        self.exec_handler.on_market_close(...)

        assert_lists_equal([self.stop_loss_order_2],
                           self.exec_handler.get_open_orders())

        expected_transaction = Transaction(
            self.timer.now(), self.msft_contract, -1,
            self.stop_loss_order_1.execution_style.stop_price, 0.0)
        self.monitor.record_transaction.assert_called_once_with(
            expected_transaction)
        self.portfolio.transact_transaction.assert_called_once_with(
            expected_transaction)

    def test_both_orders_executed_when_both_stop_prices_hit(self):
        self._set_bar_for_today(open_price=100.0,
                                high_price=110.0,
                                low_price=90.0,
                                close_price=105.0,
                                volume=100000000.0)
        self._trigger_single_time_event()
        self.exec_handler.on_market_close(...)

        assert_lists_equal([], self.exec_handler.get_open_orders())

        expected_transactions = [
            Transaction(self.timer.now(), self.msft_contract, -1,
                        self.stop_loss_order_1.execution_style.stop_price, 0),
            Transaction(self.timer.now(), self.msft_contract, -1,
                        self.stop_loss_order_2.execution_style.stop_price, 0),
        ]
        self.monitor.record_transaction.assert_has_calls(
            call(t) for t in expected_transactions)
        self.portfolio.transact_transaction.assert_has_calls(
            call(t) for t in expected_transactions)

        self.assertEqual(self.monitor.record_transaction.call_count, 2)
        self.assertEqual(self.portfolio.transact_transaction.call_count, 2)

    def test_market_opens_at_much_lower_price_than_it_closed_at_yesterday(
            self):
        self._set_bar_for_today(open_price=70.0,
                                high_price=100.0,
                                low_price=68.0,
                                close_price=90.0,
                                volume=100000000.0)
        self._trigger_single_time_event()
        self.exec_handler.on_market_close(...)

        assert_lists_equal([], self.exec_handler.get_open_orders())
        expected_transactions = [
            Transaction(self.timer.now(), self.msft_contract, -1, 70.0, 0),
            Transaction(self.timer.now(), self.msft_contract, -1, 70.0, 0),
        ]
        self.monitor.record_transaction.assert_has_calls(
            call(t) for t in expected_transactions)
        self.portfolio.transact_transaction.assert_has_calls(
            call(t) for t in expected_transactions)

        self.assertEqual(self.monitor.record_transaction.call_count, 2)
        self.assertEqual(self.portfolio.transact_transaction.call_count, 2)

    def test_market_opens_at_much_higher_price_than_it_closed_at_yesterday(
            self):
        self.buy_stop_loss_order = Order(self.msft_contract,
                                         quantity=1,
                                         execution_style=StopOrder(120.0),
                                         time_in_force=TimeInForce.GTC)

        self.exec_handler.assign_order_ids([self.buy_stop_loss_order])
        self._set_bar_for_today(open_price=120.0,
                                high_price=130.0,
                                low_price=68.0,
                                close_price=90.0,
                                volume=100000000.0)

        self._trigger_single_time_event()
        self.exec_handler.on_market_close(...)

        assert_lists_equal([], self.exec_handler.get_open_orders())

        expected_transactions = [
            Transaction(self.timer.now(), self.msft_contract, -1,
                        self.stop_loss_order_1.execution_style.stop_price, 0),
            Transaction(self.timer.now(), self.msft_contract, -1,
                        self.stop_loss_order_2.execution_style.stop_price, 0),
            Transaction(self.timer.now(), self.msft_contract, 1, 120, 0),
        ]
        self.monitor.record_transaction.assert_has_calls(
            call(t) for t in expected_transactions)
        self.portfolio.transact_transaction.assert_has_calls(
            call(t) for t in expected_transactions)

        self.assertEqual(self.monitor.record_transaction.call_count, 3)
        self.assertEqual(self.portfolio.transact_transaction.call_count, 3)

    def _set_last_available_price(self, price):
        self.data_handler.get_last_available_price.side_effect = lambda tickers: QFSeries(
            data=[price], index=pd.Index(tickers), name=self.start_date)

    def _set_bar_for_today(self, open_price, high_price, low_price,
                           close_price, volume):
        self.data_handler.get_current_bar.side_effect = lambda tickers: pd.DataFrame(
            index=pd.Index(tickers),
            columns=PriceField.ohlcv(),
            data=[[open_price, high_price, low_price, close_price, volume]])
Exemple #5
0
class TestRules(TestCase):

    def setUp(self):
        self.timer = SettableTimer()
        MarketOpenEvent.set_trigger_time({"hour": 13, "minute": 30, "second": 0, "microsecond": 0})
        MarketCloseEvent.set_trigger_time({"hour": 20, "minute": 0, "second": 0, "microsecond": 0})

    def test_market_open_rule(self):

        market_open_event = MarketOpenEvent()
        self.timer.set_current_time(str_to_date("2018-01-01 00:00:00.000000", DateFormat.FULL_ISO))
        now = self.timer.now()
        next_trigger_time = market_open_event.next_trigger_time(now)
        self.assertEqual(str_to_date("2018-01-01 13:30:00.000000", DateFormat.FULL_ISO), next_trigger_time)

        self.timer.set_current_time(str_to_date("2018-01-01 09:29:59.999999", DateFormat.FULL_ISO))
        now = self.timer.now()
        next_trigger_time = market_open_event.next_trigger_time(now)
        self.assertEqual(str_to_date("2018-01-01 13:30:00.000000", DateFormat.FULL_ISO), next_trigger_time)

        self.timer.set_current_time(str_to_date("2018-01-01 13:30:00.000000", DateFormat.FULL_ISO))
        now = self.timer.now()
        next_trigger_time = market_open_event.next_trigger_time(now)
        self.assertEqual(str_to_date("2018-01-02 13:30:00.000000", DateFormat.FULL_ISO), next_trigger_time)

    def test_periodic_events(self):
        self.timer.set_current_time(str_to_date("2018-01-01 13:23:00.000000", DateFormat.FULL_ISO))
        now = self.timer.now()

        class MinuteBarEvent(PeriodicEvent):
            frequency = Frequency.MIN_1
            start_time = {"hour": 8, "minute": 0, "second": 0}
            end_time = {"hour": 16, "minute": 0, "second": 0}

            def notify(self, listener):
                pass

        class Minutes15BarEvent(PeriodicEvent):
            frequency = Frequency.MIN_15
            start_time = {"hour": 8, "minute": 0, "second": 0}
            end_time = {"hour": 16, "minute": 0, "second": 0}

            def notify(self, listener):
                pass

        self.assertEqual(now + RelativeDelta(minutes=1), MinuteBarEvent().next_trigger_time(now))
        self.assertEqual(now + RelativeDelta(minute=30), Minutes15BarEvent().next_trigger_time(now))

        self.timer.set_current_time(str_to_date("2018-01-01 23:23:00.000000", DateFormat.FULL_ISO))
        now = self.timer.now()

        start_time = str_to_date("2018-01-02 08:00:00.000000", DateFormat.FULL_ISO)
        self.assertEqual(start_time, MinuteBarEvent().next_trigger_time(now))
        self.assertEqual(start_time, Minutes15BarEvent().next_trigger_time(now))

    def test_periodic_events_short_time_range(self):
        self.timer.set_current_time(str_to_date("2018-01-01 9:30:00.000000", DateFormat.FULL_ISO))
        now = self.timer.now()

        class Periodic15MinutesEvent(PeriodicEvent):
            frequency = Frequency.MIN_15
            start_time = {"hour": 9, "minute": 45, "second": 0}
            end_time = {"hour": 10, "minute": 10, "second": 0}

            def notify(self, listener):
                pass

        periodic_15_minutes_event = Periodic15MinutesEvent()

        now = periodic_15_minutes_event.next_trigger_time(now)
        self.assertEqual(str_to_date("2018-01-01 9:45:00.000000", DateFormat.FULL_ISO), now)

        now = periodic_15_minutes_event.next_trigger_time(now)
        self.assertEqual(str_to_date("2018-01-01 10:00:00.000000", DateFormat.FULL_ISO), now)

        now = periodic_15_minutes_event.next_trigger_time(now)
        self.assertEqual(str_to_date("2018-01-02 9:45:00.000000", DateFormat.FULL_ISO), now)

    def test_single_time_events(self):
        self.timer.set_current_time(str_to_date("2018-01-01 13:00:00.000000", DateFormat.FULL_ISO))
        now = self.timer.now()

        # Schedule a few new events
        date_times = (
            str_to_date("2018-01-01 13:15:00.000000", DateFormat.FULL_ISO),
            str_to_date("2018-01-01 13:30:00.000000", DateFormat.FULL_ISO),
            str_to_date("2018-01-01 13:45:00.000000", DateFormat.FULL_ISO),
            str_to_date("2018-01-01 12:45:00.000000", DateFormat.FULL_ISO)
        )

        for date_time in date_times:
            SingleTimeEvent.schedule_new_event(date_time, None)

        self.assertEqual(str_to_date("2018-01-01 13:15:00.000000", DateFormat.FULL_ISO),
                         SingleTimeEvent().next_trigger_time(now))

        self.timer.set_current_time(SingleTimeEvent().next_trigger_time(now))
        now = self.timer.now()

        self.assertEqual(str_to_date("2018-01-01 13:30:00.000000", DateFormat.FULL_ISO),
                         SingleTimeEvent().next_trigger_time(now))

        self.timer.set_current_time(str_to_date("2018-01-01 13:44:50.000000", DateFormat.FULL_ISO))
        now = self.timer.now()

        self.assertEqual(str_to_date("2018-01-01 13:45:00.000000", DateFormat.FULL_ISO),
                         SingleTimeEvent().next_trigger_time(now))

        self.timer.set_current_time(SingleTimeEvent().next_trigger_time(now))
        now = self.timer.now()

        self.assertEqual(None, SingleTimeEvent().next_trigger_time(now))

    def test_simultaneous_time_event(self):

        def schedule_events():
            SingleTimeEvent.schedule_new_event(str_to_date("2017-04-10 14:00:00.000000", DateFormat.FULL_ISO), 1)
            SingleTimeEvent.schedule_new_event(str_to_date("2017-04-10 14:00:00.000000", DateFormat.FULL_ISO), 2)

        self.assertRaises(ValueError, schedule_events)
Exemple #6
0
class TestMarketOnOpenExecutionStyle(TestCase):
    MSFT_TICKER_STR = "MSFT US Equity"

    def setUp(self):
        self.start_date = str_to_date("2018-02-04")
        self.msft_contract = Contract(self.MSFT_TICKER_STR,
                                      security_type='SEK',
                                      exchange='TEST')
        self.msft_ticker = BloombergTicker(self.MSFT_TICKER_STR)

        self.contracts_to_tickers_mapper = DummyBloombergContractTickerMapper()
        self.timer = SettableTimer(initial_time=self.start_date)

        self.data_handler = mock(strict=True)

        self.scheduler = mock()

        self.commission_model = FixedCommissionModel(commission=0.0)
        self.monitor = _MonitorMock()
        self.spied_monitor = spy(self.monitor)
        self.portfolio = mock()

        slippage_model = PriceBasedSlippage(0.0)
        self.exec_hanlder = SimulatedExecutionHandler(
            self.data_handler, self.timer, self.scheduler, self.spied_monitor,
            self.commission_model, self.contracts_to_tickers_mapper,
            self.portfolio, slippage_model)

        self._set_last_msft_price(100.0)
        self.order_1 = Order(self.msft_contract,
                             quantity=10,
                             execution_style=MarketOrder(),
                             time_in_force=TimeInForce.OPG)
        self.order_2 = Order(self.msft_contract,
                             quantity=-5,
                             execution_style=MarketOrder(),
                             time_in_force=TimeInForce.OPG)
        self.order_3 = Order(self.msft_contract,
                             quantity=-7,
                             execution_style=MarketOrder(),
                             time_in_force=TimeInForce.OPG)

        self.order_4 = Order(self.msft_contract,
                             quantity=4,
                             execution_style=MarketOnCloseOrder(),
                             time_in_force=TimeInForce.DAY)

    def test_1_order_fill(self):
        self.exec_hanlder.accept_orders([self.order_1])
        self._set_price_for_now(101)
        self.exec_hanlder.on_market_open(...)

        verify(self.spied_monitor, times=1).record_transaction(...)
        verify(self.portfolio, times=1).transact_transaction(...)

        actual_orders = self.exec_hanlder.get_open_orders()
        expected_orders = []
        assert_lists_equal(expected_orders, actual_orders)

    def test_3_orders_fill(self):
        self.exec_hanlder.accept_orders(
            [self.order_1, self.order_2, self.order_3])
        self._set_price_for_now(101)
        self.exec_hanlder.on_market_open(...)

        verify(self.spied_monitor, times=3).record_transaction(...)
        verify(self.portfolio, times=3).transact_transaction(...)

        actual_orders = self.exec_hanlder.get_open_orders()
        expected_orders = []
        assert_lists_equal(expected_orders, actual_orders)

    def test_3_orders_fill_only_at_open(self):
        self.exec_hanlder.accept_orders(
            [self.order_1, self.order_2, self.order_3])
        self._set_price_for_now(101)
        self.exec_hanlder.on_market_close(...)

        verifyZeroInteractions(self.portfolio, self.spied_monitor)

        actual_orders = self.exec_hanlder.get_open_orders()
        expected_orders = [self.order_1, self.order_2, self.order_3]
        assert_lists_equal(expected_orders, actual_orders)

    def test_fill_open_and_close(self):
        self.exec_hanlder.accept_orders([self.order_1, self.order_2])
        self.exec_hanlder.accept_orders([self.order_2, self.order_3])
        self.exec_hanlder.accept_orders([self.order_3, self.order_4])
        self.exec_hanlder.accept_orders([self.order_4, self.order_4])

        self._set_price_for_now(101)
        self.exec_hanlder.on_market_open(...)

        verify(self.spied_monitor, times=5).record_transaction(...)
        verify(self.portfolio, times=5).transact_transaction(...)

        actual_orders = self.exec_hanlder.get_open_orders()
        expected_orders = [self.order_4, self.order_4, self.order_4]
        assert_lists_equal(expected_orders, actual_orders)

        self.exec_hanlder.on_market_close(...)

        verify(self.spied_monitor, times=8).record_transaction(...)
        verify(self.portfolio, times=8).transact_transaction(...)

        actual_orders = self.exec_hanlder.get_open_orders()
        expected_orders = []
        assert_lists_equal(expected_orders, actual_orders)

    def test_fill_close_and_open(self):
        self.exec_hanlder.accept_orders([self.order_1, self.order_2])
        self.exec_hanlder.accept_orders([self.order_2, self.order_3])
        self.exec_hanlder.accept_orders([self.order_3, self.order_4])
        self.exec_hanlder.accept_orders([self.order_4, self.order_4])

        self._set_price_for_now(101)
        self.exec_hanlder.on_market_close(...)

        verify(self.spied_monitor, times=3).record_transaction(...)
        verify(self.portfolio, times=3).transact_transaction(...)

        actual_orders = self.exec_hanlder.get_open_orders()
        expected_orders = [
            self.order_1, self.order_2, self.order_2, self.order_3,
            self.order_3
        ]
        assert_lists_equal(expected_orders, actual_orders)

        self.exec_hanlder.on_market_open(...)

        verify(self.spied_monitor, times=8).record_transaction(...)
        verify(self.portfolio, times=8).transact_transaction(...)

        actual_orders = self.exec_hanlder.get_open_orders()
        expected_orders = []
        assert_lists_equal(expected_orders, actual_orders)

    def test_market_open_transaction(self):
        order = self.order_1
        price = 102
        self.exec_hanlder.accept_orders([order])
        self._set_price_for_now(price)
        self.exec_hanlder.on_market_open(...)

        timestamp = self.timer.now()
        contract = order.contract
        quantity = order.quantity
        commission = self.commission_model.calculate_commission(order, price)
        expected_transaction = Transaction(timestamp, contract, quantity,
                                           price, commission)

        captor = _ArgCaptor()
        verify(self.spied_monitor, times=1).record_transaction(captor)
        self.assertEqual(expected_transaction, captor.get_value())

    def test_market_close_transaction(self):
        order = self.order_4
        price = 102
        self.exec_hanlder.accept_orders([self.order_1, order])
        self._set_price_for_now(price)
        self.exec_hanlder.on_market_close(...)

        timestamp = self.timer.now()
        contract = order.contract
        quantity = order.quantity
        commission = self.commission_model.calculate_commission(order, price)
        expected_transaction = Transaction(timestamp, contract, quantity,
                                           price, commission)

        captor = _ArgCaptor()
        verify(self.spied_monitor, times=1).record_transaction(captor)
        self.assertEqual(expected_transaction, captor.get_value())

    def test_market_close_does_not_trade(self):
        price = None
        self.exec_hanlder.accept_orders([self.order_1, self.order_4])
        self._set_price_for_now(price)
        self.exec_hanlder.on_market_close(...)

        verifyZeroInteractions(self.portfolio, self.spied_monitor)

    def test_market_open_does_not_trade(self):
        price = None
        self.exec_hanlder.accept_orders([self.order_1, self.order_4])
        self._set_price_for_now(price)
        self.exec_hanlder.on_market_open(...)

        verifyZeroInteractions(self.portfolio, self.spied_monitor)

    def _set_last_msft_price(self, price):
        when(self.data_handler).get_last_available_price(
            [self.msft_ticker]).thenReturn(
                pd.Series(data=[price],
                          index=pd.Index([self.msft_ticker]),
                          name=self.start_date))

    def _set_price_for_now(self, price):
        when(self.data_handler).get_current_price(
            [self.msft_ticker]).thenReturn(
                pd.Series(data=[price], index=pd.Index([self.msft_ticker])))