예제 #1
0
class PBR(TensorTradeRewardScheme):

    registered_name = "pbr"

    def __init__(self, price: 'Stream'):
        super().__init__()
        self.position = -1

        r = Stream.sensor(price, lambda p: p.value, dtype="float").diff()
        position = Stream.sensor(self, lambda rs: rs.position, dtype="float")

        reward = (r * position).fillna(0).rename("reward")

        self.feed = DataFeed([reward])
        self.feed.compile()

    def on_action(self, action: int):
        self.position = -1 if action == 0 else 1

    def get_reward(self, portfolio: 'Portfolio'):
        return self.feed.next()["reward"]

    def reset(self):
        self.position = -1
        self.feed.reset()
예제 #2
0
def test_price_ds():

    btc_price = Stream.source([7000, 7500, 8300],
                              dtype="float").rename("USD-BTC")
    eth_price = Stream.source([200, 212, 400], dtype="float").rename("USD-ETH")

    feed = DataFeed([btc_price, eth_price])

    assert feed.next() == {"USD-BTC": 7000, "USD-ETH": 200}
예제 #3
0
def test_exchange_feed():

    btc_price = Stream.source([7000, 7500, 8300],
                              dtype="float").rename("USD-BTC")
    eth_price = Stream.source([200, 212, 400], dtype="float").rename("USD-ETH")

    exchange = Exchange("bitfinex", service=execute_order)(btc_price,
                                                           eth_price)

    feed = DataFeed(exchange.streams())

    assert feed.next() == {"bitfinex:/USD-BTC": 7000, "bitfinex:/USD-ETH": 200}
예제 #4
0
def assert_op(streams, expected):

    feed = DataFeed(streams)
    feed.compile()

    actual = []
    while feed.has_next():
        d = feed.next()

        v = None
        for k in d.keys():
            if v is None:
                v = d[k]
            else:
                assert d[k] == v
        actual += [v]

    np.testing.assert_allclose(actual, expected)
예제 #5
0
class PBR(TensorTradeRewardScheme):
    """A reward scheme for position-based returns.

    * Let :math:`p_t` denote the price at time t.
    * Let :math:`x_t` denote the position at time t.
    * Let :math:`R_t` denote the reward at time t.

    Then the reward is defined as,
    :math:`R_{t} = (p_{t} - p_{t-1}) \cdot x_{t}`.

    Parameters
    ----------
    price : `Stream`
        The price stream to use for computing rewards.
    """

    registered_name = "pbr"

    def __init__(self, price: 'Stream') -> None:
        super().__init__()
        self.position = -1

        ## PBR works when commissions are negligible.
        ## Need to modify the following line to make it work for cases where commissions are substantial:
        ###NOTE:  the agent learns to hold its position when abs(r) < commission
        #r = Stream.sensor(price, lambda p: p.value, dtype="float").diff()
        r = Stream.sensor(price, lambda p: p.value, dtype="float").pct_change()
        position = Stream.sensor(self, lambda rs: rs.position, dtype="float")
        reward = ((position * r).fillna(0)).rename("reward")

        self.feed = DataFeed([reward])
        self.feed.compile()

    def on_action(self, action: int, hasOrder: bool,
                  current_step: int) -> None:
        self.position = -1 if action == 0 else 1

    def get_reward(self, portfolio: 'Portfolio') -> float:
        return self.feed.next()["reward"]

    def reset(self) -> None:
        """Resets the `position` and `feed` of the reward scheme."""
        self.position = -1
        self.feed.reset()
예제 #6
0
class SinglePositionProfit(TensorTradeRewardScheme):
    """A reward scheme for single position return.


    Parameters
    ----------
    price : `Stream`
        The price stream to use for computing rewards.
    """

    registered_name = "spp"

    def __init__(self, price: 'Stream') -> None:
        super().__init__()
        self.position = -1
        self.executed = 0
        r = (Stream.sensor(price, lambda p: p.value,
                           dtype="float").pct_change())  #.log10()).diff()
        position = Stream.sensor(self, lambda rs: rs.position, dtype="float")
        executed = Stream.sensor(self, lambda rs: rs.executed, dtype="float")
        reward = (position * r - executed).fillna(0).rename("reward")

        self.feed = DataFeed([reward])
        self.feed.compile()

    def on_action(self, action: int, hasOrder: bool,
                  current_step: int) -> None:
        self.position = -1 if action == 0 else 1
        performance = pd.DataFrame.from_dict(portfolio.performance,
                                             orient='index')
        net_worths = performance["net_worth"]
        self.executed = len(self.broker.executed) * 0.002606

    def get_reward(self, portfolio: 'Portfolio') -> float:
        #n_executed = len(self.broker.executed)/2
        return (self.feed.next()["reward"])  # - n_executed*0.002606)

    def reset(self) -> None:
        """Resets the `position` and `feed` of the reward scheme."""
        self.position = -1
        self.feed.reset()
        self.executed = 0
예제 #7
0
class PBR(TensorTradeRewardScheme):
    """A reward scheme for position-based returns.

    * Let :math:`p_t` denote the price at time t.
    * Let :math:`x_t` denote the position at time t.
    * Let :math:`R_t` denote the reward at time t.

    Then the reward is defined as,
    :math:`R_{t} = (p_{t} - p_{t-1}) \cdot x_{t}`.

    Parameters
    ----------
    price : `Stream`
        The price stream to use for computing rewards.
    """

    registered_name = "pbr"

    def __init__(self, price: 'Stream') -> None:
        super().__init__()
        self.position = -1

        r = Stream.sensor(price, lambda p: p.value, dtype="float").diff()
        position = Stream.sensor(self, lambda rs: rs.position, dtype="float")

        reward = (position * r).fillna(0).rename("reward")

        self.feed = DataFeed([reward])
        self.feed.compile()

    def on_action(self, action: int) -> None:
        self.position = -1 if action == 0 else 1

    def get_reward(self, portfolio: 'Portfolio') -> float:
        reward_ = self.feed.next()["reward"]
        #print("Reward: {}".format(reward_))
        return reward_

    def reset(self) -> None:
        """Resets the `position` and `feed` of the reward scheme."""
        self.position = -1
        self.feed.reset()
예제 #8
0
def test_exchange_with_wallets_feed():

    ex1 = Exchange("bitfinex", service=execute_order)(
        Stream.source([7000, 7500, 8300], dtype="float").rename("USD-BTC"),
        Stream.source([200, 212, 400], dtype="float").rename("USD-ETH"))

    ex2 = Exchange("binance", service=execute_order)(
        Stream.source([7005, 7600, 8200], dtype="float").rename("USD-BTC"),
        Stream.source([201, 208, 402], dtype="float").rename("USD-ETH"),
        Stream.source([56, 52, 60], dtype="float").rename("USD-LTC"))

    wallet_btc = Wallet(ex1, 10 * BTC)
    wallet_btc_ds = _create_wallet_source(wallet_btc)

    wallet_usd = Wallet(ex2, 1000 * USD)
    wallet_usd.withdraw(quantity=400 * USD, reason="test")
    wallet_usd.deposit(quantity=Quantity(USD, 400, path_id="fake_id"),
                       reason="test")
    wallet_usd_ds = _create_wallet_source(wallet_usd, include_worth=False)

    streams = ex1.streams() + ex2.streams() + wallet_btc_ds + wallet_usd_ds
    feed = DataFeed(streams)

    assert feed.next() == {
        "bitfinex:/USD-BTC": 7000,
        "bitfinex:/USD-ETH": 200,
        "bitfinex:/BTC:/free": 10,
        "bitfinex:/BTC:/locked": 0,
        "bitfinex:/BTC:/total": 10,
        "bitfinex:/BTC:/worth": 70000,
        "binance:/USD-BTC": 7005,
        "binance:/USD-ETH": 201,
        "binance:/USD-LTC": 56,
        "binance:/USD:/free": 600,
        "binance:/USD:/locked": 400,
        "binance:/USD:/total": 1000
    }
예제 #9
0
# # Setup trading env
coinbase = Exchange("coinbase", service=execute_order)(Stream.source(
    prices["close"].tolist(), dtype="float").rename("USD-BTC"))
portfolio = Portfolio(
    USD,
    [Wallet(coinbase, 10000 * USD)],
)

with NameSpace("coinbase"):
    streams = [
        Stream.source(df[c].tolist(), dtype="float").rename(c)
        for c in df.columns
    ]
feed = DataFeed(streams)
print(feed.next())

# # Screen log

env = default.create(
    portfolio=portfolio,
    action_scheme="managed-risk",
    reward_scheme="risk-adjusted",
    feed=feed,
    renderer="screen-log",  # ScreenLogger used with default settings
    window_size=20,
)
print(env.observation_space)
print(env.action_space)

# model = A2C(MlpLstmPolicy, env, verbose=1)
예제 #10
0
class IntradayObserver(Observer):
    """The IntradayObserver observer that is compatible with the other `default`
    components.
    Parameters
    ----------
    portfolio : `Portfolio`
        The portfolio to be used to create the internal data feed mechanism.
    feed : `DataFeed`
        The feed to be used to collect observations to the observation window.
    renderer_feed : `DataFeed`
        The feed to be used for giving information to the renderer.
    stop_time : datetime.time
        The time at which the episode will stop.
    window_size : int
        The size of the observation window.
    min_periods : int
        The amount of steps needed to warmup the `feed`.
    randomize : bool
        Whether or not to select a random episode when reset.
    **kwargs : keyword arguments
        Additional keyword arguments for observer creation.
    Attributes
    ----------
    feed : `DataFeed`
        The master feed in charge of streaming the internal, external, and
        renderer data feeds.
    stop_time : datetime.time
        The time at which the episode will stop.
    window_size : int
        The size of the observation window.
    min_periods : int
        The amount of steps needed to warmup the `feed`.
    randomize : bool
        Whether or not a random episode is selected when reset.
    history : `ObservationHistory`
        The observation history.
    renderer_history : `List[dict]`
        The history of the renderer data feed.
    """
    def __init__(self,
                 portfolio: 'Portfolio',
                 feed: 'DataFeed' = None,
                 renderer_feed: 'DataFeed' = None,
                 stop_time: 'datetime.time' = dt.time(16, 0, 0),
                 window_size: int = 1,
                 min_periods: int = None,
                 randomize: bool = False,
                 **kwargs) -> None:
        internal_group = Stream.group(
            _create_internal_streams(portfolio)).rename("internal")
        external_group = Stream.group(feed.inputs).rename("external")

        if renderer_feed:
            renderer_group = Stream.group(
                renderer_feed.inputs).rename("renderer")

            self.feed = DataFeed(
                [internal_group, external_group, renderer_group])
        else:
            self.feed = DataFeed([internal_group, external_group])

        self.stop_time = stop_time
        self.window_size = window_size
        self.min_periods = min_periods
        self.randomize = randomize

        self._observation_dtype = kwargs.get('dtype', np.float32)
        self._observation_lows = kwargs.get('observation_lows', -np.inf)
        self._observation_highs = kwargs.get('observation_highs', np.inf)

        self.history = ObservationHistory(window_size=window_size)

        initial_obs = self.feed.next()["external"]
        initial_obs.pop('timestamp', None)
        n_features = len(initial_obs.keys())

        self._observation_space = Box(low=self._observation_lows,
                                      high=self._observation_highs,
                                      shape=(self.window_size, n_features),
                                      dtype=self._observation_dtype)

        self.feed = self.feed.attach(portfolio)

        self.renderer_history = []

        if self.randomize:
            self.num_episodes = 0
            while (self.feed.has_next()):
                ts = self.feed.next()["external"]["timestamp"]
                if ts.time() == self.stop_time:
                    self.num_episodes += 1

        self.feed.reset()
        self.warmup()

        self.stop = False

    @property
    def observation_space(self) -> Space:
        return self._observation_space

    def warmup(self) -> None:
        """Warms up the data feed.
        """
        if self.min_periods is not None:
            for _ in range(self.min_periods):
                if self.has_next():
                    obs_row = self.feed.next()["external"]
                    obs_row.pop('timestamp', None)
                    self.history.push(obs_row)

    def observe(self, env: 'TradingEnv') -> np.array:
        """Observes the environment.
        As a consequence of observing the `env`, a new observation is generated
        from the `feed` and stored in the observation history.
        Returns
        -------
        `np.array`
            The current observation of the environment.
        """
        data = self.feed.next()

        # Save renderer information to history
        if "renderer" in data.keys():
            self.renderer_history += [data["renderer"]]

        # Push new observation to observation history
        obs_row = data["external"]
        try:
            obs_ts = obs_row.pop('timestamp')
        except KeyError:
            raise KeyError(
                "Include Stream of Timestamps named 'timestamp' in feed")
        self.history.push(obs_row)

        # Check if episode should be stopped
        if obs_ts.time() == self.stop_time:
            self.stop = True

        obs = self.history.observe()
        obs = obs.astype(self._observation_dtype)
        return obs

    def has_next(self) -> bool:
        """Checks if there is another observation to be generated.
        Returns
        -------
        bool
            Whether there is another observation to be generated.
        """
        return self.feed.has_next() and not self.stop

    def reset(self) -> None:
        """Resets the observer"""
        self.renderer_history = []
        self.history.reset()

        if self.randomize or not self.feed.has_next():
            self.feed.reset()
            if self.randomize:
                episode_num = 0
                while (episode_num < randrange(self.num_episodes)):
                    ts = self.feed.next()["external"]["timestamp"]
                    if ts.time() == self.stop_time:
                        episode_num += 1

        self.warmup()

        self.stop = False
예제 #11
0
class TensorTradeObserver(Observer):
    """The TensorTrade observer that is compatible with the other `default`
    components.

    Parameters
    ----------
    portfolio : `Portfolio`
        The portfolio to be used to create the internal data feed mechanism.
    feed : `DataFeed`
        The feed to be used to collect observations to the observation window.
    renderer_feed : `DataFeed`
        The feed to be used for giving information to the renderer.
    window_size : int
        The size of the observation window.
    min_periods : int
        The amount of steps needed to warmup the `feed`.
    **kwargs : keyword arguments
        Additional keyword arguments for observer creation.

    Attributes
    ----------
    feed : `DataFeed`
        The master feed in charge of streaming the internal, external, and
        renderer data feeds.
    window_size : int
        The size of the observation window.
    min_periods : int
        The amount of steps needed to warmup the `feed`.
    history : `ObservationHistory`
        The observation history.
    renderer_history : `List[dict]`
        The history of the renderer data feed.
    """
    def __init__(self,
                 portfolio: 'Portfolio',
                 feed: 'DataFeed' = None,
                 renderer_feed: 'DataFeed' = None,
                 window_size: int = 1,
                 min_periods: int = None,
                 **kwargs) -> None:
        internal_group = Stream.group(
            _create_internal_streams(portfolio)).rename("internal")
        external_group = Stream.group(feed.inputs).rename("external")

        if renderer_feed:
            renderer_group = Stream.group(
                renderer_feed.inputs).rename("renderer")

            self.feed = DataFeed(
                [internal_group, external_group, renderer_group])
        else:
            self.feed = DataFeed([internal_group, external_group])

        self.window_size = window_size
        self.min_periods = min_periods

        self._observation_dtype = kwargs.get('dtype', np.float32)
        self._observation_lows = kwargs.get('observation_lows', -np.inf)
        self._observation_highs = kwargs.get('observation_highs', np.inf)

        self.history = ObservationHistory(window_size=window_size)

        initial_obs = self.feed.next()["external"]
        n_features = len(initial_obs.keys())

        self._observation_space = Box(low=self._observation_lows,
                                      high=self._observation_highs,
                                      shape=(self.window_size, n_features),
                                      dtype=self._observation_dtype)

        self.feed = self.feed.attach(portfolio)

        self.renderer_history = []

        self.feed.reset()
        self.warmup()

    @property
    def observation_space(self) -> Space:
        return self._observation_space

    def warmup(self) -> None:
        """Warms up the data feed.
        """
        if self.min_periods is not None:
            for _ in range(self.min_periods):
                if self.has_next():
                    obs_row = self.feed.next()["external"]
                    self.history.push(obs_row)

    def observe(self, env: 'TradingEnv') -> np.array:
        """Observes the environment.

        As a consequence of observing the `env`, a new observation is generated
        from the `feed` and stored in the observation history.

        Returns
        -------
        `np.array`
            The current observation of the environment.
        """
        data = self.feed.next()

        # Save renderer information to history
        if "renderer" in data.keys():
            self.renderer_history += [data["renderer"]]

        # Push new observation to observation history
        obs_row = data["external"]
        self.history.push(obs_row)

        obs = self.history.observe()
        obs = obs.astype(self._observation_dtype)
        return obs

    def has_next(self) -> bool:
        """Checks if there is another observation to be generated.

        Returns
        -------
        bool
            Whether there is another observation to be generated.
        """
        return self.feed.has_next()

    def reset(self) -> None:
        """Resets the observer"""
        self.renderer_history = []
        self.history.reset()
        self.feed.reset()
        self.warmup()
예제 #12
0
def test_create_internal_data_feed():

    ex1 = Exchange("bitfinex", service=execute_order)(
        Stream.source([7000, 7500, 8300], dtype="float").rename("USD-BTC"),
        Stream.source([200, 212, 400], dtype="float").rename("USD-ETH"))

    ex2 = Exchange("binance", service=execute_order)(
        Stream.source([7005, 7600, 8200], dtype="float").rename("USD-BTC"),
        Stream.source([201, 208, 402], dtype="float").rename("USD-ETH"),
        Stream.source([56, 52, 60], dtype="float").rename("USD-LTC"))

    portfolio = Portfolio(USD, [
        Wallet(ex1, 10000 * USD),
        Wallet(ex1, 10 * BTC),
        Wallet(ex1, 5 * ETH),
        Wallet(ex2, 1000 * USD),
        Wallet(ex2, 5 * BTC),
        Wallet(ex2, 20 * ETH),
        Wallet(ex2, 3 * LTC),
    ])

    feed = DataFeed(_create_internal_streams(portfolio))

    data = {
        "bitfinex:/USD-BTC": 7000,
        "bitfinex:/USD-ETH": 200,
        "bitfinex:/USD:/free": 10000,
        "bitfinex:/USD:/locked": 0,
        "bitfinex:/USD:/total": 10000,
        "bitfinex:/BTC:/free": 10,
        "bitfinex:/BTC:/locked": 0,
        "bitfinex:/BTC:/total": 10,
        "bitfinex:/BTC:/worth": 7000 * 10,
        "bitfinex:/ETH:/free": 5,
        "bitfinex:/ETH:/locked": 0,
        "bitfinex:/ETH:/total": 5,
        "bitfinex:/ETH:/worth": 200 * 5,
        "binance:/USD-BTC": 7005,
        "binance:/USD-ETH": 201,
        "binance:/USD-LTC": 56,
        "binance:/USD:/free": 1000,
        "binance:/USD:/locked": 0,
        "binance:/USD:/total": 1000,
        "binance:/BTC:/free": 5,
        "binance:/BTC:/locked": 0,
        "binance:/BTC:/total": 5,
        "binance:/BTC:/worth": 7005 * 5,
        "binance:/ETH:/free": 20,
        "binance:/ETH:/locked": 0,
        "binance:/ETH:/total": 20,
        "binance:/ETH:/worth": 201 * 20,
        "binance:/LTC:/free": 3,
        "binance:/LTC:/locked": 0,
        "binance:/LTC:/total": 3,
        "binance:/LTC:/worth": 56 * 3,
    }

    bitfinex_net_worth = 10000 + (10 * 7000) + (5 * 200)
    binance_net_worth = 1000 + (5 * 7005) + (20 * 201) + (3 * 56)

    data['net_worth'] = sum(
        data[k] if k.endswith("worth") or k.endswith("USD:/total") else 0
        for k in data.keys())

    assert feed.next() == data
예제 #13
0
from tensortrade.oms.instruments import USD, BTC
from tensortrade.oms.wallets import Wallet, Portfolio

bitfinex = Exchange("bitfinex", service=execute_order)(Stream.source(
    price_history['Close'].tolist(), dtype="float").rename("USD-BTC"))
portfolio = Portfolio(USD, [
    Wallet(bitfinex, 10000 * USD),
    Wallet(bitfinex, 10 * BTC),
])
with NameSpace("bitfinex"):
    streams = [
        Stream.source(dataset[c].tolist(), dtype="float").rename(c)
        for c in dataset.columns
    ]
feed = DataFeed(streams)
feed.next()


def create_env(config):
    p = Stream.source(price_history['Close'].tolist(),
                      dtype="float").rename("USD-BTC")
    bitfinex = Exchange("bitfinex", service=execute_order)(Stream.source(
        price_history['Close'].tolist(), dtype="float").rename("USD-BTC"))
    cash = Wallet(bitfinex, 100000 * USD)
    asset = Wallet(bitfinex, 0 * BTC)
    portfolio = Portfolio(USD, [cash, asset])
    feed = DataFeed([
        p,
        p.rolling(window=10).mean().rename("fast"),
        p.rolling(window=50).mean().rename("medium"),
        p.rolling(window=100).mean().rename("slow"),