Beispiel #1
0
    def test_index_single_data(self):
        # Auto broadcast for scalar
        sd = idd.SingleData(0, index=["foo", "bar"])
        print(sd)

        # Support empty value
        sd = idd.SingleData()
        print(sd)

        # Bad case: the input is not aligned
        with self.assertRaises(ValueError):
            idd.SingleData(range(10), index=["foo", "bar"])

        # test indexing
        sd = idd.SingleData([1, 2, 3, 4], index=["foo", "bar", "f", "g"])
        print(sd)
        print(sd.iloc[1])  # get second row

        # Bad case: it is not in the index
        with self.assertRaises(KeyError):
            print(sd.loc[1])

        print(sd.loc["foo"])

        # Test slicing
        print(sd.loc[:"bar"])

        print(sd.iloc[:3])
Beispiel #2
0
    def test_ops(self):
        sd1 = idd.SingleData([1, 2, 3, 4], index=["foo", "bar", "f", "g"])
        sd2 = idd.SingleData([1, 2, 3, 4], index=["foo", "bar", "f", "g"])
        print(sd1 + sd2)
        new_sd = sd2 * 2
        self.assertTrue(new_sd.index == sd2.index)

        sd1 = idd.SingleData([1, 2, None, 4], index=["foo", "bar", "f", "g"])
        sd2 = idd.SingleData([1, 2, 3, None], index=["foo", "bar", "f", "g"])
        self.assertTrue(np.isnan((sd1 + sd2).iloc[3]))
        self.assertTrue(sd1.add(sd2).sum() == 13)

        self.assertTrue(idd.sum_by_index([sd1, sd2], sd1.index, fill_value=0.0).sum() == 13)
Beispiel #3
0
 def test_squeeze(self):
     sd1 = idd.SingleData([1, 2, 3, 4], index=["foo", "bar", "f", "g"])
     # automatically squeezing
     self.assertTrue(not isinstance(np.nansum(sd1), idd.IndexData))
     self.assertTrue(not isinstance(np.sum(sd1), idd.IndexData))
     self.assertTrue(not isinstance(sd1.sum(), idd.IndexData))
     self.assertEqual(np.nansum(sd1), 10)
     self.assertEqual(np.sum(sd1), 10)
     self.assertEqual(sd1.sum(), 10)
     self.assertEqual(np.nanmean(sd1), 2.5)
     self.assertEqual(np.mean(sd1), 2.5)
     self.assertEqual(sd1.mean(), 2.5)
Beispiel #4
0
    def test_corner_cases(self):
        sd = idd.MultiData([[1, 2], [3, np.NaN]],
                           index=["foo", "bar"],
                           columns=["f", "g"])
        print(sd)

        self.assertTrue(np.isnan(sd.loc["bar", "g"]))

        # support slicing
        print(sd.loc[~sd.loc[:, "g"].isna().data.astype(np.bool)])

        print(
            self.assertTrue(idd.SingleData().index == idd.SingleData().index))

        # empty dict
        print(idd.SingleData({}))
        print(idd.SingleData(pd.Series()))

        sd = idd.SingleData()
        with self.assertRaises(KeyError):
            sd.loc["foo"]

        # replace
        sd = idd.SingleData([1, 2, 3, 4], index=["foo", "bar", "f", "g"])
        sd = sd.replace(dict(zip(range(1, 5), range(2, 6))))
        print(sd)
        self.assertTrue(sd.iloc[0] == 2)
Beispiel #5
0
 def get_data(self, stock_id, start_time, end_time, field, method=None):
     if method == "ts_data_last":
         method = ts_data_last
     stock_data = resam_ts_data(self.data[stock_id][field],
                                start_time,
                                end_time,
                                method=method)
     if stock_data is None:
         return None
     elif isinstance(stock_data, (bool, np.bool_, int, float, np.number)):
         return stock_data
     elif isinstance(stock_data, pd.Series):
         return idd.SingleData(stock_data)
     else:
         raise ValueError(
             f"stock data from resam_ts_data must be a number, pd.Series or pd.DataFrame"
         )
Beispiel #6
0
    def _agg_base_price(
        self,
        inner_order_indicators: List[Dict[str, Union[SingleMetric,
                                                     idd.SingleData]]],
        decision_list: List[Tuple[BaseTradeDecision, pd.Timestamp,
                                  pd.Timestamp]],
        trade_exchange: Exchange,
        pa_config: dict = {},
    ):
        """
        # NOTE:!!!!
        # Strong assumption!!!!!!
        # the correctness of the base_price relies on that the **same** exchange is used

        Parameters
        ----------
        inner_order_indicators : List[Dict[str, pd.Series]]
            the indicators of account of inner executor
        decision_list: List[Tuple[BaseTradeDecision, pd.Timestamp, pd.Timestamp]],
            a list of decisions according to inner_order_indicators
        trade_exchange : Exchange
            for retrieving trading price
        pa_config : dict
            For example
            {
                "agg": "twap",  # "vwap"
                "price": "$close",  # TODO: this is not supported now!!!!!
                                    # default to use deal price of the exchange
            }
        """

        # TODO: I think there are potentials to be optimized
        trade_dir = self.order_indicator.get_index_data("trade_dir")
        if len(trade_dir) > 0:
            bp_all, bv_all = [], []
            # <step, inst, (base_volume | base_price)>
            for oi, (dec, start, end) in zip(inner_order_indicators,
                                             decision_list):
                bp_s = oi.get_index_data("base_price").reindex(trade_dir.index)
                bv_s = oi.get_index_data("base_volume").reindex(
                    trade_dir.index)

                bp_new, bv_new = {}, {}
                for pr, v, (inst, direction) in zip(
                        bp_s.data, bv_s.data,
                        zip(trade_dir.index, trade_dir.data)):
                    if np.isnan(pr):
                        bp_tmp, bv_tmp = self._get_base_vol_pri(
                            inst,
                            start,
                            end,
                            decision=dec,
                            direction=direction,
                            trade_exchange=trade_exchange,
                            pa_config=pa_config,
                        )
                        if (bp_tmp is not None) and (bv_tmp is not None):
                            bp_new[inst], bv_new[inst] = bp_tmp, bv_tmp
                    else:
                        bp_new[inst], bv_new[inst] = pr, v

                bp_new = idd.SingleData(bp_new)
                bv_new = idd.SingleData(bv_new)
                bp_all.append(bp_new)
                bv_all.append(bv_new)
            bp_all = idd.concat(bp_all, axis=1)
            bv_all = idd.concat(bv_all, axis=1)

            base_volume = bv_all.sum(axis=1)
            self.order_indicator.assign("base_volume", base_volume.to_dict())
            self.order_indicator.assign("base_price",
                                        ((bp_all * bv_all).sum(axis=1) /
                                         base_volume).to_dict())
Beispiel #7
0
    def _get_base_vol_pri(
        self,
        inst: str,
        trade_start_time: pd.Timestamp,
        trade_end_time: pd.Timestamp,
        direction: OrderDir,
        decision: BaseTradeDecision,
        trade_exchange: Exchange,
        pa_config: dict = {},
    ):
        """
        Get the base volume and price information
        All the base price values are rooted from this function
        """

        agg = pa_config.get("agg", "twap").lower()
        price = pa_config.get("price", "deal_price").lower()

        if decision.trade_range is not None:
            trade_start_time, trade_end_time = decision.trade_range.clip_time_range(
                start_time=trade_start_time, end_time=trade_end_time)

        if price == "deal_price":
            price_s = trade_exchange.get_deal_price(inst,
                                                    trade_start_time,
                                                    trade_end_time,
                                                    direction=direction,
                                                    method=None)
        else:
            raise NotImplementedError(f"This type of input is not supported")

        # if there is no stock data during the time period
        if price_s is None:
            return None, None

        if isinstance(price_s, (int, float, np.number)):
            price_s = idd.SingleData(price_s, [trade_start_time])
        elif isinstance(price_s, idd.SingleData):
            pass
        else:
            raise NotImplementedError(f"This type of input is not supported")

        # NOTE: there are some zeros in the trading price. These cases are known meaningless
        # for aligning the previous logic, remove it.
        # remove zero and negative values.
        price_s = price_s.loc[(price_s > 1e-08).data.astype(np.bool)]
        # NOTE ~(price_s < 1e-08) is different from price_s >= 1e-8
        #   ~(np.NaN < 1e-8) -> ~(False)  -> True

        if agg == "vwap":
            volume_s = trade_exchange.get_volume(inst,
                                                 trade_start_time,
                                                 trade_end_time,
                                                 method=None)
            if isinstance(volume_s, (int, float, np.number)):
                volume_s = idd.SingleData(volume_s, [trade_start_time])
            volume_s = volume_s.reindex(price_s.index)
        elif agg == "twap":
            volume_s = idd.SingleData(1, price_s.index)
        else:
            raise NotImplementedError(f"This type of input is not supported")

        base_volume = volume_s.sum()
        base_price = (price_s * volume_s).sum() / base_volume
        return base_price, base_volume
Beispiel #8
0
 def get_index_data(self, metric: str) -> SingleData:
     if metric in self.data:
         return self.data[metric]
     else:
         return idd.SingleData()
Beispiel #9
0
 def assign(self, col: str, metric: dict) -> None:
     self.data[col] = idd.SingleData(metric)
Beispiel #10
0
 def get_index_data(self, metric):
     if metric in self.data:
         return idd.SingleData(self.data[metric].metric)
     else:
         return idd.SingleData()