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
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()
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)
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()
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
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()
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
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")
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
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()
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
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
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
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