コード例 #1
0
    def __init__(
            self,
            input_shape: Tuple[int, ...],
            trading_fraction: int = 10,
            trading_assets:
        int = 1,  # later we want the bot to trade one of multiple possible assets
            allow_short: bool = False,
            stop_if_lost: float = None,
            initial_capital: float = 100000,
            commission=lambda size: 0.025):
        super().__init__(
            MultiDiscrete([trading_assets, trading_fraction])
            if trading_assets > 1 else Discrete(trading_fraction + 1),
            Box(low=-1, high=1, shape=input_shape)
        )  # FIXME what shape? we also need historic trades?

        self.trading_fraction = trading_fraction
        self.initial_capital = initial_capital
        self.commission = commission
        self.stop_if_lost = stop_if_lost
        self.allow_short = allow_short

        if allow_short and (trading_fraction % 2) != 0:
            _log.warning('short trades expect even nr of trading fraction')

        # eventually do not serialize ..
        self.trade_log = StreamingTransactionLog()
        self.current_net = 0
コード例 #2
0
def ta_backtest(signal: Typing.PatchedDataFrame,
                prices: Typing.PatchedPandas,
                action: Callable[[pd.Series], Tuple[int, float]],
                slippage: Callable[[float], float] = lambda _: 0):
    if has_indexed_columns(signal):
        assert len(signal.columns) == len(
            prices.columns), "Signal and Prices need the same shape!"
        res = pd.DataFrame({},
                           index=signal.index,
                           columns=pd.MultiIndex.from_product([[], []]))

        for i in range(len(signal.columns)):
            df = ta_backtest(signal[signal.columns[i]],
                             prices[prices.columns[i]], action, slippage)

            top_level_name = ",".join(prices.columns[i]) if isinstance(
                prices.columns[i], tuple) else prices.columns[i]
            df.columns = pd.MultiIndex.from_product([[top_level_name],
                                                     df.columns.to_list()])
            res = res.join(df)

        return res

    assert isinstance(prices, pd.Series), "prices need to be a series!"
    trades = StreamingTransactionLog()

    def trade_log_action(row):
        direction_amount = action(row)
        if isinstance(direction_amount, tuple):
            trades.perform_action(*direction_amount)
        else:
            trades.rebalance(float(direction_amount))

    signal.to_frame().apply(trade_log_action, axis=1, raw=True)
    return trades.evaluate(prices.rename("price"), slippage)
コード例 #3
0
    def calculate_performance(self,
                              initial_capital=100,
                              prediction_name="0",
                              target_name="Close"):
        log = StreamingTransactionLog()
        self.df[PREDICTION_COLUMN_NAME, prediction_name].apply(
            lambda action: log.rebalance(action * initial_capital))
        perf = log.evaluate(self.df[TARGET_COLUMN_NAME,
                                    target_name].rename("prices"))

        return perf
コード例 #4
0
 def reset(self) -> np.ndarray:
     self.trade_log = StreamingTransactionLog()
     self.current_net = 0
     return super().reset()
コード例 #5
0
class TradingAgentGym(ReinforcementModel.DataFrameGym):
    def __init__(
            self,
            input_shape: Tuple[int, ...],
            trading_fraction: int = 10,
            trading_assets:
        int = 1,  # later we want the bot to trade one of multiple possible assets
            allow_short: bool = False,
            stop_if_lost: float = None,
            initial_capital: float = 100000,
            commission=lambda size: 0.025):
        super().__init__(
            MultiDiscrete([trading_assets, trading_fraction])
            if trading_assets > 1 else Discrete(trading_fraction + 1),
            Box(low=-1, high=1, shape=input_shape)
        )  # FIXME what shape? we also need historic trades?

        self.trading_fraction = trading_fraction
        self.initial_capital = initial_capital
        self.commission = commission
        self.stop_if_lost = stop_if_lost
        self.allow_short = allow_short

        if allow_short and (trading_fraction % 2) != 0:
            _log.warning('short trades expect even nr of trading fraction')

        # eventually do not serialize ..
        self.trade_log = StreamingTransactionLog()
        self.current_net = 0

    def reset(self) -> np.ndarray:
        self.trade_log = StreamingTransactionLog()
        self.current_net = 0
        return super().reset()

    def interpret_action(self, action, idx: int, features: np.ndarray,
                         labels: np.ndarray, targets: np.ndarray,
                         weights: np.ndarray) -> float:
        if self.allow_short:
            # 0 - 10  ->  -10 - 10
            action -= int(self.trading_fraction / 2) * 2

        # we convert the action in to a target balance we pass to the transaction log
        balance = action / self.trading_fraction / float(
            targets) * self.initial_capital
        return balance

    def take_action(self, action, idx: int, features: np.ndarray,
                    labels: np.ndarray, targets: np.ndarray,
                    weights: np.ndarray) -> float:

        # skip the very first bar
        if idx <= 0:
            self.trade_log.rebalance(0)
            return 0

        # we use a n/fractions as target balance -> 10 shares 20,... shares, ...
        balance = action / self.trading_fraction * self.initial_capital
        self.trade_log.rebalance(balance)

        # new we need to evaluate the portfolio performance
        # where the targets are the prices
        prices = pd.Series(self.train[2][:idx + 1].ravel(), name="prices")
        perf = self.trade_log.evaluate(prices, self.commission)
        self.current_net = perf["net"].iloc[-1]

        if self.stop_if_lost is not None and self.current_net < self.initial_capital * (
                1 - self.stop_if_lost):
            raise StopIteration(f"lost more then {self.stop_if_lost}%")

        return self.calculate_trade_reward(perf)

    def calculate_trade_reward(self, portfolio_performance_log) -> float:
        return portfolio_performance_log["net"].iloc[-1]

    def next_observation(self, idx: int, features: np.ndarray,
                         labels: np.ndarray, targets: np.ndarray,
                         weights: np.ndarray) -> np.ndarray:
        # currently returns only the features, but we also want to return some net worth, history, ...
        # for CNNs input must be [0, 255] and channel last, this means we need to reshape our feature space
        # return features.swapaxes(0, 1).swapaxes(1, 2) * 255
        pass

    def render(self, mode='human'):
        if mode == 'system':
            print(self.current_net)
        elif mode == 'notebook':
            # TODO plot something using matplotlib
            pass
        elif mode == 'human':
            # TODO plot something using matplotlib
            pass
コード例 #6
0
    def test_rebalance(self):
        df = DF_TEST[-10:].copy()
        df["Price"] = range(1, 11)

        trans = StreamingTransactionLog()
        trans.rebalance(0.1)
        trans.rebalance(0)
        trans.rebalance(0.5)
        trans.rebalance(0.6)
        trans.rebalance(0.4)
        trans.rebalance(0.2)
        trans.rebalance(-0.2)
        trans.rebalance(-0.3)
        trans.rebalance(-0.1)
        trans.rebalance(0)

        values = trans.evaluate(df["Price"])
        print(values)
        self.assertEqual(0, values.iloc[-1, 1])
        np.testing.assert_almost_equal(1.2, values.iloc[-1, -1])