Exemplo n.º 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()
def create_trade_env(quotes, observations, symbol):

    # Add features
    features = []
    #exclude "date/Column [0]" from observation - start from column 1
    for c in data.columns[0:]:
        s = Stream.source(list(data[c]), dtype="float").rename(data[c].name)
        features += [s]
    feed = DataFeed(features)
    feed.compile()

    # define exchange - needs to specify Price-Quote Stream
    exchange = Exchange("sim-exchange", service=execute_order)(
        Stream.source(list(quotes["close"]),
                      dtype="float").rename(str("USD-{}").format(symbol)))

    # add current cash, initial-asset
    cash = Wallet(exchange, 10000 * USD)
    asset = Wallet(exchange, 0 * Instrument(symbol, 2, symbol))

    # initialize portfolio - base currency USD
    portfolio = Portfolio(base_instrument=USD, wallets=[cash, asset])

    # add element for rendered feed
    renderer_feed = DataFeed([
        Stream.source(list(data.index)).rename("date"),
        Stream.source(list(data["open"]), dtype="float").rename("open"),
        Stream.source(list(data["high"]), dtype="float").rename("high"),
        Stream.source(list(data["low"]), dtype="float").rename("low"),
        Stream.source(list(data["close"]), dtype="float").rename("close")
        #Stream.source(list(data["volume"]), dtype="float").rename("volume")
    ])

    reward_scheme = default.rewards.SimpleProfit()
    action_scheme = default.actions.SimpleOrders(trade_sizes=1)
    '''
  # define reward-scheme
  # define action-scheme
  action_scheme = default.actions.BSH(
      cash=cash,
      asset=asset
  )
  '''

    # create env
    env = default.create(
        portfolio=portfolio,
        action_scheme=action_scheme,
        reward_scheme=reward_scheme,
        feed=feed,
        renderer_feed=renderer_feed,
        #renderer="screen-log",
        #window_size=20,
        max_allowed_loss=0.6)

    return env
def create_env(envs_config=None):
    features = []
    for c in data.columns[5:]:
        s = Stream.source(list(data[c][-100:]),
                          dtype="float").rename(data[c].name)
        features += [s]

    cp = Stream.select(features, lambda s: s.name == "close")

    features = [cp.log().diff().fillna(0).rename("lr")] + features[1:]

    feed = DataFeed(features)
    feed.compile()

    bitstamp = Exchange("bitstamp", service=execute_order)(Stream.source(
        list(data["close"]), dtype="float").rename("USD-BTC"))

    portfolio = Portfolio(
        USD, [Wallet(bitstamp, 10000 * USD),
              Wallet(bitstamp, 10 * BTC)])

    renderer_feed = DataFeed([
        Stream.source(list(data["date"])).rename("date"),
        Stream.source(list(data["open"]), dtype="float").rename("open"),
        Stream.source(list(data["high"]), dtype="float").rename("high"),
        Stream.source(list(data["low"]), dtype="float").rename("low"),
        Stream.source(list(data["close"]), dtype="float").rename("close"),
        Stream.source(list(data["volume"]), dtype="float").rename("volume")
    ])

    # reward_scheme = RiskAdjustedReturns(
    #     return_algorithm='sortino',
    #     risk_free_rate=0.025,
    #     target_returns=0.1,
    #     window_size=200
    # )
    reward_scheme = SimpleProfit(window_size=200)  #PBR(price=cp)

    # action_scheme = BSH(
    #     cash=portfolio.wallets[0],
    #     asset=portfolio.wallets[1]
    # ).attach(reward_scheme)

    env = default.create(
        portfolio=portfolio,
        action_scheme="managed-risk",
        reward_scheme=reward_scheme,  #"risk-adjusted",
        feed=feed,
        renderer_feed=renderer_feed,
        renderer=default.renderers.PlotlyTradingChart(display=False,
                                                      save_format='html',
                                                      path='./agents/charts/'),
        window_size=40)
    return env
    def _setup_render_stream(self):
        """
        Sets up indicator data stream for renderers with OHLC data
        :return: tensortrade.feed.core.DataFeed
        """
        renderer_feed = DataFeed([
            Stream.source(list(self._data["date"])).rename("date"),
            Stream.source(list(self._data["open"]), dtype="float").rename("open"),
            Stream.source(list(self._data["high"]), dtype="float").rename("high"),
            Stream.source(list(self._data["low"]), dtype="float").rename("low"),
            Stream.source(list(self._data["close"]), dtype="float").rename("close"),
            Stream.source(list(self._data["volume"]), dtype="float").rename("volume")
        ])

        renderer_feed.compile()

        return renderer_feed
Exemplo n.º 5
0
class PBREX(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 = "pbrex"

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

        self.pre_networth = 0

        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, side: TradeSide) -> None:
        pass

    def get_reward(self, portfolio: 'Portfolio') -> float:
        net_worths = [nw['net_worth'] for nw in portfolio.performance.values()]
        reward = 0 if len(net_worths) < 2 else net_worths[-1] - net_worths[-2]
        return reward

    def reset(self) -> None:
        """Resets the `position` and `feed` of the reward scheme."""
        self.position = -1
        self.feed.reset()
Exemplo n.º 6
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)
Exemplo n.º 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

        ## 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()
Exemplo n.º 8
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
Exemplo n.º 9
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()
Exemplo n.º 10
0
    def _setup_stream(self):
        """
        Sets up data stream with indicators and close
        :return: tensortrade.feed.core.DataFeed
        """

        features = []
        for c in self._data.columns[1:]:
            s = Stream.source(list(self._data[c]), dtype="float").rename(self._data[c].name)
            features += [s]

        cp = Stream.select(features, lambda s: s.name == "close")

        features = [
            cp.log().diff().rename("lr"),
            rsi(cp, period=20).rename("rsi"),
            macd(cp, fast=10, slow=50, signal=5).rename("macd")
        ]

        feed = DataFeed(features)
        feed.compile()

        return feed
Exemplo n.º 11
0
hp = Stream.select(features, lambda s: s.name == 'high')
lp = Stream.select(features, lambda s: s.name == 'low')
v = Stream.select(features, lambda s: s.name == 'volume')

features = [
    cp.log().diff().rename("lr"),
    op.log().diff().rename("op"),
    hp.log().diff().rename("hp"),
    lp.log().diff().rename("lp"),
    v.log().diff().rename("v"),
    rsi(cp, period=20).rename("rsi"),
    macd(cp, fast=10, slow=50, signal=5).rename("macd"),
]

feed = DataFeed(features)
feed.compile()
# %%
bitstamp = Exchange("bitstamp", service=execute_order)(Stream.source(
    list(data["close"]), dtype="float").rename("USD-BTC"))

portfolio = Portfolio(
    USD, [Wallet(bitstamp, 100000 * USD),
          Wallet(bitstamp, 0 * BTC)])

renderer_feed = DataFeed([
    Stream.source(list(data["date"])).rename("date"),
    Stream.source(list(data["open"]), dtype="float").rename("open"),
    Stream.source(list(data["high"]), dtype="float").rename("high"),
    Stream.source(list(data["low"]), dtype="float").rename("low"),
    Stream.source(list(data["close"]), dtype="float").rename("close"),
    Stream.source(list(data["volume"]), dtype="float").rename("volume")
Exemplo n.º 12
0
def create_env(config, save_path='./agents/charts/', is_eval=False):

    # Load data
    k = -3000
    w = -200
    if is_eval:
        k = -200
        w = None
    y = data['Close'][k:w].to_numpy()

    features = []
    for c in data.columns[5:]:
        s = Stream.source(list(data[c][k:w]),
                          dtype="float").rename(data[c].name)
        features += [s]

    cp = Stream.source(y, dtype="float").rename("EUR-USD")

    coinbase = Exchange("coinbase",
                        service=execute_order,
                        options=ExchangeOptions(commission=0.00005))(cp)

    feature_add = [
        cp,
        cp.ewm(span=10).mean().rename("fast"),
        cp.ewm(span=50).mean().rename("medium"),
        cp.ewm(span=100).mean().rename("slow"),
        cp.log().diff().fillna(0).rename("lr")
    ]
    features = features + feature_add

    feed = DataFeed(features)

    feed.compile()

    cash = Wallet(coinbase, 10000 * EUR)
    asset = Wallet(coinbase, 10000 * USD)

    portfolio = Portfolio(EUR, [cash, asset])

    reward_scheme = PBR(price=cp)
    # reward_scheme = SimpleProfit(window_size=500)

    action_scheme = BSH(cash=cash, asset=asset).attach(reward_scheme)

    # renderer_feed = DataFeed([
    #     Stream.source(y, dtype="float").rename("price"),
    #     Stream.sensor(action_scheme, lambda s: s.action, dtype="float").rename("action")
    # ])

    renderer_feed = DataFeed([
        Stream.source(list(data["Date"][k:w])).rename("date"),
        Stream.source(list(data["Open"][k:w]), dtype="float").rename("open"),
        Stream.source(list(data["High"][k:w]), dtype="float").rename("high"),
        Stream.source(list(data["Low"][k:w]), dtype="float").rename("low"),
        Stream.source(list(data["Close"][k:w]), dtype="float").rename("close"),
        Stream.source(list(data["Volume"][k:w]),
                      dtype="float").rename("volume")
    ])

    environment = default.create(
        feed=feed,
        portfolio=portfolio,
        action_scheme=action_scheme,  #"managed-risk",
        reward_scheme=reward_scheme,
        renderer_feed=renderer_feed,
        renderer=default.renderers.PlotlyTradingChart(display=False,
                                                      save_format='html',
                                                      path=save_path),
        # renderer=PositionChangeChart(),
        window_size=config["window_size"],
        max_allowed_loss=0.6)
    return environment
Exemplo n.º 13
0
class IntradayObserver(Observer):
    """The intraday observer that is compatible with the other `default`
    components and with IntradayExchange.
    Parameters
    ----------
    portfolio : `Portfolio`
        The portfolio to be used to create the internal data feed mechanism.
    feeds : `DataFeed`
        The feeds to be used to collect observations to the observation window.
    renderer_feeds : `DataFeed`
        The feeds 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`.
    randomize : bool
        Whether or not to select a random episode when reset.
    **kwargs : keyword arguments
        Additional keyword arguments for observer creation.
    Attributes
    ----------
    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.
    feed : `DataFeed`
        The master feed in charge of streaming the internal, external, and
        renderer data feeds.
    history : `ObservationHistory`
        The observation history.
    renderer_history : `List[dict]`
        The history of the renderer data feed.
    """

    def __init__(self,
                 portfolio: 'Portfolio',
                 feeds: 'DataFeed' = None,
                 renderer_feeds: 'DataFeed' = None,
                 window_size: int = 1,
                 min_periods: int = None,
                 randomize: bool = False,
                 **kwargs) -> None:
        self.portfolio = portfolio
        self.feeds = feeds
        self.renderer_feeds = renderer_feeds
        self.window_size = window_size
        self.min_periods = min_periods
        self.randomize = randomize

        self.set_feed(0)

        self.num_episodes = len(feeds)
        self.episode = -1

        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.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
        Parameters
        ----------
        episode : `int`
            The episode to set.
        """
        self.renderer_history = []
        self.history.reset()
        self.feed.reset()
        if self.randomize:
            self.episode = randrange(self.num_episodes)
        else:
            self.episode = 0 if self.episode == self.num_episodes - 1 else self.episode + 1
        self.set_feed(self.episode)
        self.warmup()

    def set_feed(self, episode: 'int') -> None:
        """Sets the current feed.
        Parameters
        ----------
        episode : `int`
            The episodes of the feed to set.
        """
        for exchange in self.portfolio.exchanges:
            exchange.set_episode(episode)

        internal_group = Stream.group(_create_internal_streams(self.portfolio)).rename("internal")
        external_group = Stream.group(self.feeds[episode].inputs).rename("external")

        if self.renderer_feeds:
            renderer_group = Stream.group(self.renderer_feeds[episode].inputs).rename("renderer")

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

        self.feed = self.feed.attach(self.portfolio)
        self.feed.compile()
Exemplo n.º 14
0
def create_env(data, config):
    features = []
    for c in data.columns[1:]:
        s = Stream.source(list(data[c]), dtype="float").rename(data[c].name)
        features += [s]

    cp = Stream.select(features, lambda s: s.name == "close")
    high_price = data['high']
    low_price = data['low']
    close_price = data['close']
    # print(data['volume'])

    try:
        data['date']
    except KeyError:
        data['date'] = data.index

    features = [
        cp.rename('USD/BTC'),
        cp.log().diff().rename("lr"),
        rsi(cp, period=14).rename("rsi"),
        macd(cp, fast=10, slow=50, signal=5).rename("macd"),
        Stream.source(ta.cci(high_price, low_price, close_price)).rename('cci')
        # cp.rolling(window=10).mean().rename("fast"),
        # cp.rolling(window=50).mean().rename("medium"),
        # cp.rolling(window=100).mean().rename("slow")
    ]

    feed = DataFeed(features)
    feed.compile()

    # for i in range(5):
    #     print(feed.next())

    coinbase = Exchange("coinbase", service=execute_order)(cp)

    cash = Wallet(coinbase, 100000 * USD)
    asset = Wallet(coinbase, 10 * BTC)

    portfolio = Portfolio(USD, [cash, asset])

    # reward_scheme = PBR(price=cp)
    reward_scheme = RiskAdjustedReturns()
    # action_scheme = BSH(cash=cash, asset=asset).attach(reward_scheme)
    action_scheme = ManagedRiskOrders()

    renderer_feed = DataFeed([
        Stream.source(list(data["date"])).rename("date"),
        Stream.source(list(data["open"]), dtype="float").rename("open"),
        Stream.source(list(data["high"]), dtype="float").rename("high"),
        Stream.source(list(data["low"]), dtype="float").rename("low"),
        Stream.source(list(data["close"]), dtype="float").rename("close"),
        Stream.source(list(data["volume"]), dtype="float").rename("volume"),
        #Stream.sensor(action_scheme, lambda s: s.action, dtype="float").rename("action")
    ])

    renderer = PlotlyTradingChart()

    environment = default.create(
        feed=feed,
        portfolio=portfolio,
        action_scheme=action_scheme,  # The DQN example uses action_scheme="managed-risk"
        reward_scheme=reward_scheme,  # The DQN uses reward_scheme="risk-adjusted"
        renderer_feed=renderer_feed,
        renderer=renderer,
        window_size=config["window_size"],
        max_allowed_loss=0.6
    )
    return environment
Exemplo n.º 15
0
    def create_env(config):

        # Use config param to decide which data set to use
        # Reserve 50 rows of data to fill in NaN values
        if config["train"] == True:
            df = data[50:-dataEnd]
            envData = candles[50:-dataEnd]
            taData = data[:-dataEnd]
        else:
            df = data[-dataEnd:]
            envData = candles[-dataEnd:]
            taData = data[-dataEnd - 50:]

        # === OBSERVER ===
        p = Stream.source(df[(coin + ':close')].tolist(),
                          dtype="float").rename(("USD-" + coin))

        # === EXCHANGE ===
        # Commission on Binance is 0.075% on the lowest level, using BNB (https://www.binance.com/en/fee/schedule)
        binance_options = ExchangeOptions(commission=0.0075,
                                          min_trade_price=10.0)
        binance = Exchange("binance",
                           service=execute_order,
                           options=binance_options)(p)

        # === ORDER MANAGEMENT SYSTEM ===
        # Start with 100.000 usd and 0 assets
        cash = Wallet(binance, 100000 * USD)
        asset = Wallet(binance, 0 * coinInstrument)

        portfolio = Portfolio(USD, [cash, asset])

        # === OBSERVER ===
        dataset = pd.DataFrame()

        # Use log-returns instead of raw OHLCV. This is a refined version of naive standarization
        # log(current_price / previous_price) = log(current_price) - log(previous_price)
        # If log value below 0 current_price > previous_price
        # Above 0 means current_price < previous_price
        dataset['log_open'] = np.log(taData[(coin + ':open')]) - np.log(
            taData[(coin + ':open')].shift(1))
        dataset['log_low'] = np.log(taData[(coin + ':low')]) - np.log(
            taData[(coin + ':low')].shift(1))
        dataset['log_high'] = np.log(taData[(coin + ':high')]) - np.log(
            taData[(coin + ':high')].shift(1))
        dataset['log_close'] = np.log(taData[(coin + ':close')]) - np.log(
            taData[(coin + ':close')].shift(1))
        dataset['log_vol'] = np.log(taData[(coin + ':volume')]) - np.log(
            taData[(coin + ':volume')].shift(1))

        # === TECHNICAL ANALYSIS ===
        # Extra features not described in research, therefore not used.
        #BB_low = ta.volatility.BollingerBands(close = taData[(coin + ':close')], window = 20).bollinger_lband()
        #BB_mid = ta.volatility.BollingerBands(close = taData[(coin + ':close')], window = 20).bollinger_mavg()
        #BB_high = ta.volatility.BollingerBands(close = taData[(coin + ':close')], window = 20).bollinger_hband()

        # Difference between close price and bollinger band
        #dataset['BB_low'] =  np.log(BB_low) - np.log(taData[(coin + ':close')])
        #dataset['BB_mid'] =  np.log(BB_mid) - np.log(taData[(coin + ':close')])
        #dataset['BB_high'] =  np.log(BB_high) - np.log(taData[(coin + ':close')])

        # Take log-returns to standardize
        # Log-returns can not be used if value is 0 or smaller.
        # Use pct_change() instead
        # IDEA: Maybe use volume or close to standardize

        # This line is necessary otherwise read only errors shows up
        taData = taData.copy()

        # For some reasons there are erros when using pct_change() for these indicators
        adi = ta.volume.AccDistIndexIndicator(
            high=taData[(coin + ':high')],
            low=taData[(coin + ':low')],
            close=taData[(coin + ':close')],
            volume=taData[(coin + ':volume')]).acc_dist_index()
        dataset['adi'] = adi.pct_change()

        fi = ta.volume.ForceIndexIndicator(
            close=taData[(coin + ':close')],
            volume=taData[(coin + ':volume')]).force_index()
        dataset['fi'] = fi.pct_change()

        macd_diff = ta.trend.MACD(close=taData[(coin + ':close')]).macd_diff()
        dataset['macd_diff'] = macd_diff.pct_change()

        dpo = ta.trend.DPOIndicator(close=taData[(coin + ':close')]).dpo()
        dataset['dpo'] = dpo.pct_change()

        # Too many outliers in the dataset
        #vpt = ta.volume.VolumePriceTrendIndicator(close=taData[(coin + ':close')], volume=taData[(coin + ':volume')]).volume_price_trend()
        #dataset['vpt'] = vpt.pct_change()
        #em = ta.volume.EaseOfMovementIndicator(high=taData[(coin + ':high')], low=taData[(coin + ':low')], volume=taData[(coin + ':volume')]).ease_of_movement()
        #dataset['em'] = em.pct_change()

        kst_sig = ta.trend.KSTIndicator(close=taData[(coin +
                                                      ':close')]).kst_sig()
        dataset['kst_sig'] = kst_sig.pct_change()

        kst_diff = ta.trend.KSTIndicator(close=taData[(coin +
                                                       ':close')]).kst_diff()
        dataset['kst_diff'] = kst_diff.pct_change()

        nvi = ta.volume.NegativeVolumeIndexIndicator(
            close=taData[(coin + ':close')],
            volume=taData[(coin + ':volume')]).negative_volume_index()
        dataset['nvi'] = np.log(nvi) - np.log(nvi.shift(1))

        bbw = ta.volatility.BollingerBands(
            close=taData[(coin + ':close')]).bollinger_wband()
        dataset['bbw'] = np.log(bbw) - np.log(bbw.shift(1))

        kcw = ta.volatility.KeltnerChannel(
            high=taData[(coin + ':high')],
            low=taData[(coin + ':low')],
            close=taData[(coin + ':close')]).keltner_channel_wband()
        dataset['kcw'] = np.log(kcw) - np.log(kcw.shift(1))

        dcw = ta.volatility.DonchianChannel(
            high=taData[(coin + ':high')],
            low=taData[(coin + ':low')],
            close=taData[(coin + ':close')]).donchian_channel_wband()
        dataset['dcw'] = np.log(dcw) - np.log(dcw.shift(1))

        psar_up = ta.trend.PSARIndicator(high=taData[(coin + ':high')],
                                         low=taData[(coin + ':low')],
                                         close=taData[(coin +
                                                       ':close')]).psar_up()
        dataset['psar_up'] = np.log(psar_up) - np.log(psar_up.shift(1))

        # These indicators have a mean independent of the OHLCV data
        # IDEA: Use log-returns on these as an extra indicator
        # Has a mean of 0
        dataset['cmf'] = ta.volume.ChaikinMoneyFlowIndicator(
            high=taData[(coin + ':high')],
            low=taData[(coin + ':low')],
            close=taData[(coin + ':close')],
            volume=taData[(coin + ':volume')]).chaikin_money_flow()
        dataset['ppo'] = ta.momentum.PercentagePriceOscillator(
            close=taData[(coin + ':close')]).ppo()
        dataset['ppo_signal'] = ta.momentum.PercentagePriceOscillator(
            close=taData[(coin + ':close')]).ppo_signal()
        dataset['ppo_hist'] = ta.momentum.PercentagePriceOscillator(
            close=taData[(coin + ':close')]).ppo_hist()
        dataset['ui'] = ta.volatility.UlcerIndex(
            close=taData[(coin + ':close')]).ulcer_index()
        dataset['aroon_ind'] = ta.trend.AroonIndicator(
            close=taData[(coin + ':close')]).aroon_indicator()

        # Indicator, so has value 0 or 1
        dataset['bbhi'] = ta.volatility.BollingerBands(
            close=taData[(coin + ':close')]).bollinger_hband_indicator()
        dataset['bbli'] = ta.volatility.BollingerBands(
            close=taData[(coin + ':close')]).bollinger_lband_indicator()
        dataset['kchi'] = ta.volatility.KeltnerChannel(
            high=taData[(coin + ':high')],
            low=taData[(coin + ':low')],
            close=taData[(coin + ':close')]).keltner_channel_hband_indicator()
        dataset['kcli'] = ta.volatility.KeltnerChannel(
            high=taData[(coin + ':high')],
            low=taData[(coin + ':low')],
            close=taData[(coin + ':close')]).keltner_channel_lband_indicator()

        # Has a mean of 50
        dataset['stoch_rsi'] = ta.momentum.StochRSIIndicator(
            close=taData[(coin + ':close')]).stochrsi()
        dataset['stoch_rsi_d'] = ta.momentum.StochRSIIndicator(
            close=taData[(coin + ':close')]).stochrsi_d()
        dataset['stoch_rsi_k'] = ta.momentum.StochRSIIndicator(
            close=taData[(coin + ':close')]).stochrsi_k()
        dataset['uo'] = ta.momentum.UltimateOscillator(
            high=taData[(coin + ':high')],
            low=taData[(coin + ':low')],
            close=taData[(coin + ':close')]).ultimate_oscillator()
        dataset['adx'] = ta.trend.ADXIndicator(high=taData[(coin + ':high')],
                                               low=taData[(coin + ':low')],
                                               close=taData[(coin +
                                                             ':close')]).adx()
        dataset['mass_index'] = ta.trend.MassIndex(
            high=taData[(coin + ':high')],
            low=taData[(coin + ':low')]).mass_index()
        dataset['aroon_up'] = ta.trend.AroonIndicator(
            close=taData[(coin + ':close')]).aroon_up()
        dataset['aroon_down'] = ta.trend.AroonIndicator(
            close=taData[(coin + ':close')]).aroon_down()
        dataset['stc'] = ta.trend.STCIndicator(close=taData[(coin +
                                                             ':close')]).stc()

        # Lot of NaN values
        #ta.trend.PSARIndicator(high=df[(coin + ':high')], low=df[(coin + ':low')], close=df[(coin + ':close')]).psar_down()

        dataset = dataset.add_prefix(coin + ":")

        # Drop first 50 rows from dataset
        dataset = dataset.iloc[50:]

        with NameSpace("binance"):
            streams = [
                Stream.source(dataset[c].tolist(), dtype="float").rename(c)
                for c in dataset.columns
            ]

        # This is everything the agent gets to see, when making decisions
        feed = DataFeed(streams)

        # Compiles all the given stream together
        feed.compile()

        # Print feed for debugging
        #print(feed.next())
        #print(feed.next())
        #print(feed.next())

        # === REWARDSCHEME ===
        # RiskAdjustedReturns rewards depends on return_algorithm and its parameters.
        # The risk-free rate is the return that you can expect from taking on zero risk.
        # A target return is what an investor would want to make from any capital invested in the asset.

        # SimpleProfit() or RiskAdjustedReturns() or PBR()
        #reward_scheme = RiskAdjustedReturns(return_algorithm='sortino')#, risk_free_rate=0, target_returns=0)
        #reward_scheme = RiskAdjustedReturns(return_algorithm='sharpe', risk_free_rate=0, target_returns=0, window_size=config["window_size"])

        reward_scheme = SimpleProfit(window_size=config["window_size"])
        #reward_scheme = PBR(price=p)

        # === ACTIONSCHEME ===
        # SimpleOrders() or ManagedRiskOrders() or BSH()

        # ManagedRiskOrders is bad, with default settings!
        # To use ManagedRiskOrders use settings like these:
        # ManagedRiskOrders(stop = [0.02], take = [0.03], trade_sizes=2,)

        action_scheme = ManagedRiskOrders(durations=[100])

        #action_scheme = SimpleOrders()

        #BSH only works with PBR as reward_scheme
        #action_scheme = BSH(cash=cash,asset=asset).attach(reward_scheme)

        # === RENDERER ===
        # Uses the OHCLV data passed to envData
        renderer_feed = DataFeed([
            Stream.source(envData[c].tolist(), dtype="float").rename(c)
            for c in envData
        ])

        # === RESULT ===
        environment = default.create(
            feed=feed,
            portfolio=portfolio,
            action_scheme=action_scheme,
            reward_scheme=reward_scheme,
            renderer_feed=renderer_feed,
            renderer=PlotlyTradingChart(),  #PositionChangeChart()
            window_size=config["window_size"],  #part of OBSERVER
            max_allowed_loss=config["max_allowed_loss"]  #STOPPER
        )
        return environment
Exemplo n.º 16
0
def create_trade_env(quotes, observations, symbol):

    # Add features
    features = []
    #exclude "date/Column [0]" from observation - start from column 1
    #for c in data.columns[0:]:
    #    s = Stream.source(list(data[c]), dtype="float").rename(data[c].name)
    #    features += [s]

    cp = eToroClose(symbol_id=32, field_name='close')
    features = [
        #cp.log().diff().rename("lr"),
        cp.rename("close"),
        eToroClose(symbol_id=32, field_name='high').rename('high'),
        eToroClose(symbol_id=32, field_name='open').rename('open'),
        eToroClose(symbol_id=32, field_name='low').rename('low'),
        #rsi(cp, period=20).rename("rsi"),
        #macd(cp, fast=10, slow=50, signal=5).rename("macd")
    ]

    feed = DataFeed(features)
    feed.compile()

    # define exchange - needs to specify Price-Quote Stream
    exchange = Exchange("etoro-exchange", service=execute_order)(cp.rename(
        str("USD-{}").format(symbol)))

    # add current cash, initial-asset
    cash = Wallet(exchange, 100000 * USD)
    asset = Wallet(exchange, 0 * Instrument(symbol, 2, symbol))

    # initialize portfolio - base currency USD
    portfolio = Portfolio(base_instrument=USD, wallets=[cash, asset])
    '''
    # add element for rendered feed
    renderer_feed = DataFeed([
        Stream.source(list(data.index)).rename("date"),
        Stream.source(list(data["open"]), dtype="float").rename("open"),
        Stream.source(list(data["high"]), dtype="float").rename("high"),
        Stream.source(list(data["low"]), dtype="float").rename("low"),
        Stream.source(list(data["close"]), dtype="float").rename("close")
        #Stream.source(list(data["volume"]), dtype="float").rename("volume") 
    ])
    '''

    # define reward-scheme
    reward_scheme = default.rewards.SimpleProfit()

    # define action-scheme
    action_scheme = default.actions.SimpleOrders(trade_sizes=1)

    # create env
    env = default.create(
        portfolio=portfolio,
        action_scheme=action_scheme,
        reward_scheme=reward_scheme,
        feed=feed,
        #renderer_feed=renderer_feed,
        #renderer="screen-log",
        #window_size=20,
        max_allowed_loss=0.6)

    return env
Exemplo n.º 17
0
def build_env(config):
    worker_index = 1
    if hasattr(config, 'worker_index'):
        worker_index = config.worker_index

    raw_data = pd.read_csv(btc_usd_file, sep=';')
    raw_data['date'] = pd.to_datetime(raw_data['time'], unit='ms')
    data = compute_features(raw_data)

    features = []
    for c in data.columns:
        if c not in raw_data.columns:
            s = Stream.source(list(data[c]),
                              dtype="float").rename(data[c].name)
            features += [s]

    comm = 0.00001
    coinbase = Exchange("coinbase",
                        service=execute_order,
                        options=ExchangeOptions(commission=comm))(
                            Stream.source(list(data["close"]),
                                          dtype="float").rename("USD-BTC"))

    cash = Wallet(coinbase, 10000 * USD)
    asset = Wallet(coinbase, 0 * BTC)

    portfolio = Portfolio(USD, [cash, asset])

    renderer_feed = DataFeed([
        Stream.source(list(data["date"])).rename("date"),
        Stream.source(list(data["open"]), dtype="float").rename("open"),
        Stream.source(list(data["high"]), dtype="float").rename("high"),
        Stream.source(list(data["low"]), dtype="float").rename("low"),
        Stream.source(list(data["close"]), dtype="float").rename("close"),
        Stream.source(list(data["volume"]), dtype="float").rename("volume")
    ])

    # reward_scheme = rewards.SimpleProfit()
    rsi = Stream.select(features, lambda x: x.name == "rsi")
    reward_scheme = SparseReward(rsi=rsi, window_size=10)
    action_scheme = BuySellHoldActionSchemes(cash, asset)
    action_scheme.attach(reward_scheme)

    plotly = PlotlyTradingChart(display=True, height=700, save_format="html")

    class EpisodeStopper(Stopper):
        def stop(self, env: 'TradingEnv') -> bool:
            return env.clock.num_steps > 1000

    open_position = Stream.sensor(asset,
                                  lambda a: asset.total_balance.as_float() > 0)
    # open_position = Stream.sensor(
    #     action_scheme, lambda action_scheme: action_scheme.has_asset
    # )
    features.append(open_position)
    feed = DataFeed(features)
    feed.compile()

    env = default.create(
        portfolio=portfolio,
        action_scheme=action_scheme,
        reward_scheme=reward_scheme,
        feed=feed,
        renderer_feed=renderer_feed,
        renderer=plotly,
        window_size=20,
        max_allowed_loss=0.5,
        stopper=EpisodeStopper(),
        callback=(LoggingCallback('http://165.227.193.153:8050', plotly)
                  if worker_index == 1 else None))

    import logging
    import os
    LOGGER = logging.getLogger(__name__)
    logging.basicConfig(
        level=logging.INFO,
        format=
        '%(asctime)s - %(name)s [%(threadName)s] - %(levelname)s - %(message)s',
    )
    LOGGER.info('env created logger')
    LOGGER.info(f'env: {os.environ}')
    print(f'env: {os.environ}')
    print('env created')

    return env