def test_detach(mock_exchange_class, mock_order_listener_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5000.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) listener = mock_order_listener_class.return_value order.attach(listener) assert len(order.listeners) == 1 assert listener in order.listeners order.detach(listener) assert len(order.listeners) == 0 assert listener not in order.listeners
def test_init(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 side = TradeSide.BUY trade_type = TradeType.MARKET # Create order specification without criteria order_spec = OrderSpec(side=side, trade_type=trade_type, exchange_pair=ExchangePair(exchange, USD / BTC)) assert order_spec.id assert order_spec.side == side assert order_spec.type == trade_type assert order_spec.exchange_pair == ExchangePair(exchange, USD / BTC) assert not order_spec.criteria # Create order specification with criteria order_spec = OrderSpec(side=side, trade_type=trade_type, exchange_pair=ExchangePair(exchange, USD / BTC), criteria=lambda order, exchange: True) assert order_spec.id assert order_spec.side == side assert order_spec.type == trade_type assert order_spec.exchange_pair == ExchangePair(exchange, USD / BTC) assert order_spec.criteria
def test_on_cancel(mock_exchange_class, cancel_listener): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "bitfinex" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) order.attach(cancel_listener) assert not cancel_listener.listened order.cancel() assert cancel_listener.listened
def test_to_dict(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) order_spec = OrderSpec(side=TradeSide.BUY, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, USD / BTC)) d = order_spec.to_dict() assert d == { "id": order_spec.id, "type": order_spec.type, "exchange_pair": order_spec.exchange_pair, "criteria": order_spec.criteria } order_spec = OrderSpec(side=TradeSide.BUY, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, USD / BTC), criteria=lambda order, exchange: True) d = order_spec.to_dict() assert d == { "id": order_spec.id, "type": order_spec.type, "exchange_pair": order_spec.exchange_pair, "criteria": order_spec.criteria }
def test_init(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5000 * USD, price=7000, portfolio=portfolio) assert order assert order.id assert order.path_id assert order.step == 0 assert order.quantity.instrument == USD assert order.remaining == order.quantity assert isinstance(order.pair, TradingPair) assert order.pair.base == USD assert order.pair.quote == BTC
def test_iadd(mock_exchange_class, mock_order_spec_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) # Market order order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5000.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) order_spec = mock_order_spec_class.return_value assert len(order._specs) == 0 order.add_order_spec(order_spec) assert len(order._specs) == 1 assert order_spec in order._specs
def test_properties(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.LIMIT, quantity=5000.00 * USD, portfolio=portfolio, price=7000.00) assert order assert order.step == 0 assert order.base_instrument == USD assert order.quote_instrument == BTC assert order.size == 5000.00 * USD assert order.price == 7000.00 assert order.trades == [] assert order.is_buy assert not order.is_sell assert not order.is_market_order assert order.is_limit_order
def test_is_complete(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) # Market order order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5000.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) assert not order.is_complete order.remaining = 0 * USD assert order.is_complete
def test_str(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) pattern = re.compile("<[A-Z][a-zA-Z]*:\\s(\\w+=.*,\\s)*(\\w+=.*)>") string = str(order) assert string assert string == pattern.fullmatch(string).string
def test_release(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) order.execute() wallet_usd = portfolio.get_wallet(exchange.id, USD) assert wallet_usd.balance == 4800 * USD assert wallet_usd.locked_balance == 5200 * USD assert order.path_id in wallet_usd.locked.keys() order.release() assert wallet_usd.balance == 10000 * USD assert wallet_usd.locked_balance == 0 * USD assert order.path_id not in wallet_usd.locked.keys()
def test_on_complete(mock_trade_class, mock_exchange_class, complete_listener): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "bitfinex" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) order.attach(complete_listener) order.execute() trade = mock_trade_class.return_value trade.size = Decimal(5197.00) trade.quantity = trade.size * USD trade.commission = 3.00 * USD order.fill(trade) assert not complete_listener.listened order.complete() assert complete_listener.listened
def test_proportion_order_init(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_id" exchange.name = "fake_exchange" wallet_usd = Wallet(exchange, 10000 * USD) wallet_btc = Wallet(exchange, 0 * BTC) portfolio = Portfolio(USD, [wallet_usd, wallet_btc]) order = proportion_order(portfolio=portfolio, source=wallet_usd, target=wallet_btc, proportion=1.0) assert order
def test_create_from_buy_order(mock_order_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "bitfinex" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 2 * BTC)] portfolio = Portfolio(USD, wallets) order = mock_order_class.return_value order.portfolio = portfolio order.exchange_pair = ExchangePair(exchange, USD / BTC) order.path_id = "fake_path_id" order.price = Decimal(7000.00) wallet_btc = portfolio.get_wallet(exchange.id, BTC) wallet_btc.lock( quantity=0.4 * BTC, order=order, reason="test" ) assert float(wallet_btc.balance.size) == 1.6 assert float(wallet_btc.locked[order.path_id].size) == 0.4 order_spec = OrderSpec( side=TradeSide.SELL, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, USD / BTC) ) next_order = order_spec.create_order(order) assert next_order assert next_order.side == TradeSide.SELL assert next_order.type == TradeType.MARKET assert next_order.exchange_pair == ExchangePair(exchange, USD / BTC) assert next_order.path_id == order.path_id assert next_order.quantity.path_id == order.path_id assert next_order.quantity.instrument == BTC
def test_str(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) order_spec = OrderSpec(side=TradeSide.BUY, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, USD / BTC)) pattern = re.compile("<[A-Z][a-zA-Z]*:\\s(\\w+=.*,\\s)*(\\w+=.*)>") string = str(order_spec) assert string assert string == pattern.fullmatch(string).string
def test_complete_basic_order(mock_order_listener_class, mock_trade_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) listener = mock_order_listener_class.return_value listener.on_complete = mock.Mock(return_value=None) order.attach(listener) order.execute() trade = mock_trade_class.return_value trade.size = Decimal(5197.00) trade.quantity = 5197.00 * USD trade.commission = 3.00 * USD order.fill(trade) assert order.status == OrderStatus.PARTIALLY_FILLED next_order = order.complete() assert order.status == OrderStatus.FILLED listener.on_complete.assert_called_once_with(order) assert not next_order
def test_to_json(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) d = { "id": str(order.id), "step": int(order.step), "exchange_pair": str(order.exchange_pair), "status": str(order.status), "type": str(order.type), "side": str(order.side), "base_symbol": str(order.pair.base.symbol), "quote_symbol": str(order.pair.quote.symbol), "quantity": str(order.quantity), "size": float(order.size), "remaining": str(order.remaining), "price": float(order.price), "criteria": str(order.criteria), "path_id": str(order.path_id), "created_at": str(order.created_at) } assert order.to_json() == d
def test_execute(mock_order_listener_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) listener = mock_order_listener_class.return_value listener.on_execute = mock.Mock(return_value=None) order.attach(listener) assert order.status == OrderStatus.PENDING order.execute() assert order.status == OrderStatus.OPEN wallet_usd = portfolio.get_wallet(exchange.id, USD) wallet_btc = portfolio.get_wallet(exchange.id, BTC) assert wallet_usd.balance == 4800 * USD assert wallet_usd.locked_balance == 5200 * USD assert order.path_id in wallet_usd.locked.keys() assert wallet_btc.balance == 0 * BTC listener.on_execute.assert_called_once_with(order)
def test_cancel(mock_order_listener_class, mock_trade_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) listener = mock_order_listener_class.return_value listener.on_cancel = mock.Mock(return_value=None) order.attach(listener) order.execute() # Execute fake trade price = Decimal(7010.00) scale = order.price / price commission = 3.00 * USD trade = mock_trade_class.return_value trade.size = Decimal(scale * order.size - commission.size) trade.quantity = trade.size * USD trade.price = price trade.commission = commission base_wallet = portfolio.get_wallet(exchange.id, USD) quote_wallet = portfolio.get_wallet(exchange.id, BTC) base_size = trade.size + commission.size quote_size = (order.price / trade.price) * (trade.size / trade.price) base_wallet.withdraw(quantity=Quantity(USD, size=base_size, path_id=order.path_id), reason="test") quote_wallet.deposit(quantity=Quantity(BTC, size=quote_size, path_id=order.path_id), reason="test") order.fill(trade) assert order.status == OrderStatus.PARTIALLY_FILLED assert base_wallet.balance == 4800.00 * USD assert float(round(base_wallet.locked[order.path_id].size, 2)) == 7.42 assert quote_wallet.balance == 0 * BTC assert float(round(quote_wallet.locked[order.path_id].size, 8)) == 0.73925519 order.cancel() listener.on_cancel.assert_called_once_with(order) assert float(round(base_wallet.balance.size, 2)) == 4807.42 assert order.path_id not in base_wallet.locked assert float(round(quote_wallet.balance.size, 8)) == 0.73925519 assert order.path_id not in quote_wallet.locked
def test_complete_complex_order(mock_trade_class, mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 exchange.quote_price = mock.Mock(return_value=Decimal(7000.00)) wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) side = TradeSide.BUY order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5200.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) risk_criteria = Stop("down", 0.03) ^ Stop("up", 0.02) risk_management = OrderSpec( side=TradeSide.SELL if side == TradeSide.BUY else TradeSide.BUY, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, USD / BTC), criteria=risk_criteria) order.add_order_spec(risk_management) order.execute() # Execute fake trade price = Decimal(7010.00) scale = order.price / price commission = 3.00 * USD base_size = scale * order.size - commission.size trade = mock_trade_class.return_value trade.size = Decimal(base_size) trade.quantity = base_size * USD trade.price = price trade.commission = commission base_wallet = portfolio.get_wallet(exchange.id, USD) quote_wallet = portfolio.get_wallet(exchange.id, BTC) base_size = trade.size + trade.commission.size quote_size = (order.price / trade.price) * (trade.size / trade.price) base_wallet.withdraw(quantity=Quantity(USD, size=base_size, path_id=order.path_id), reason="test") quote_wallet.deposit(quantity=Quantity(BTC, size=quote_size, path_id=order.path_id), reason="test") # Fill fake trade order.fill(trade) assert order.path_id in portfolio.get_wallet(exchange.id, USD).locked assert order.status == OrderStatus.PARTIALLY_FILLED next_order = order.complete() assert order.status == OrderStatus.FILLED assert next_order assert next_order.path_id == order.path_id assert next_order.size assert next_order.status == OrderStatus.PENDING assert next_order.side == TradeSide.SELL assert next_order.exchange_pair == ExchangePair(exchange, USD / BTC)
def create_env(config, train="train"): cdd = CryptoDataDownload() data = cdd.fetch("Bitstamp", "USD", "BTC", "1h") if False: data.close = data.close / 20 + range(len(data)) print("genenrating fake increase") if train == "train": data = data[0:int(len(data) / 2)] # training print("using first half for training") elif train == "eval": data = data[int(len(data) / 2):] # validation print("using second half for eval") else: print("using all data") pclose = Stream.source(list(data.close), dtype="float").rename("USD-BTC") pmin = Stream.source(list(data.low), dtype="float").rename("USD-BTClow") pmax = Stream.source(list(data.high), dtype="float").rename("USD-BTChigh") pmin = Stream.source(list(data.low), dtype="float").rename("USD-BTClow") pmax = Stream.source(list(data.high), dtype="float").rename("USD-BTChigh") pmin3 = pmin.rolling(window=3).min() pmin10 = pmin.rolling(window=10).min() pmin20 = pmin.rolling(window=20).min() pmax3 = pmax.rolling(window=3).max() pmax10 = pmax.rolling(window=10).max() pmax20 = pmax.rolling(window=20).max() eo = ExchangeOptions(commission=0.002) # coinbase = Exchange("coinbase", service=execute_order, options=eo)( pclose ) cash = Wallet(coinbase, 100000 * USD) asset = Wallet(coinbase, 0 * BTC) portfolio = Portfolio(USD, [ cash, asset ]) feed = DataFeed([ (pclose.log() - pmin3.log()).fillna(0).rename("relmin3"), (pclose.log() - pmin10.log()).fillna(0).rename("relmin10"), (pclose.log() - pmin20.log()).fillna(0).rename("relmin20"), (pclose.log() - pmax3.log()).fillna(0).rename("relmax3"), (pclose.log() - pmax10.log()).fillna(0).rename("relmax10"), (pclose.log() - pmax20.log()).fillna(0).rename("relmax20"), ]) action_scheme = BSH(cash=cash, asset=asset) renderer_feed = DataFeed([ Stream.source(list(data.close), dtype="float").rename("price"), Stream.sensor(action_scheme, lambda s: s.action, dtype="float").rename("action") # only works for BSH ]) environment = default.create( feed=feed, portfolio=portfolio, action_scheme=action_scheme, reward_scheme="simple", renderer_feed=renderer_feed, renderer=PositionChangeChart(), window_size=config["window_size"], min_periods=20, 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 assert_execute_order(current_price, base_balance, quote_balance, order_side, order_quantity, order_price, ): mock_clock = mock.Mock() clock = mock_clock.return_value clock.step = 3 base = base_balance.instrument quote = quote_balance.instrument current_price = Decimal(current_price).quantize(Decimal(10) ** -base.precision) order_price = Decimal(order_price).quantize(Decimal(10) ** -base.precision) options = ExchangeOptions() mock_exchange = mock.Mock() exchange = mock_exchange.return_value exchange.name = "coinbase" exchange.options = options exchange.quote_price = lambda pair: current_price base_wallet = Wallet(exchange, base_balance) quote_wallet = Wallet(exchange, quote_balance) portfolio = Portfolio(USD, [ base_wallet, quote_wallet ]) order = Order( step=1, side=order_side, trade_type=TradeType.MARKET, exchange_pair=ExchangePair(exchange, base/quote), quantity=order_quantity, portfolio=portfolio, price=order_price, path_id="fake_id" ) order.status = OrderStatus.OPEN if order_side == TradeSide.BUY: trade = execute_buy_order( order, base_wallet, quote_wallet, current_price=current_price, options=options, clock=clock, ) base_balance = base_wallet.locked['fake_id'].size quote_balance = quote_wallet.locked['fake_id'].size expected_base_balance = order_quantity.size - (trade.size + trade.commission.size) expected_quote_balance = trade.size / current_price expected_base_balance = expected_base_balance.quantize(Decimal(10) ** -base.precision) expected_quote_balance = expected_quote_balance.quantize(Decimal(10) ** -quote.precision) assert base_balance == expected_base_balance assert quote_balance == expected_quote_balance else: trade = execute_sell_order( order, base_wallet, quote_wallet, current_price=current_price, options=options, clock=clock, ) base_balance = base_wallet.locked['fake_id'].size quote_balance = quote_wallet.locked['fake_id'].size expected_base_balance = trade.size * current_price expected_base_balance = expected_base_balance.quantize(Decimal(10)**-base.precision) assert base_balance == expected_base_balance assert quote_balance == 0
def test_is_executable_on(mock_exchange_class): exchange = mock_exchange_class.return_value exchange.options = ExchangeOptions() exchange.id = "fake_exchange_id" exchange.name = "coinbase" exchange.clock = mock.Mock() exchange.clock.step = 0 # Market order wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.MARKET, quantity=5000.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) exchange.quote_price = mock.Mock(return_value=Decimal(6800.00)) assert order.is_executable exchange.quote_price = mock.Mock(return_value=Decimal(7200.00)) assert order.is_executable # Limit order wallets = [Wallet(exchange, 10000 * USD), Wallet(exchange, 0 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.BUY, trade_type=TradeType.LIMIT, quantity=5000.00 * USD, portfolio=portfolio, price=Decimal(7000.00)) exchange.quote_price = mock.Mock(return_value=Decimal(6800.00)) assert order.is_executable exchange.quote_price = mock.Mock(return_value=Decimal(7200.00)) assert order.is_executable # Stop Order wallets = [Wallet(exchange, 0 * USD), Wallet(exchange, 2 * BTC)] portfolio = Portfolio(USD, wallets) order = Order(step=0, exchange_pair=ExchangePair(exchange, USD / BTC), side=TradeSide.SELL, trade_type=TradeType.LIMIT, quantity=1 * BTC, portfolio=portfolio, price=Decimal(7000.00), criteria=Stop("down", 0.03)) exchange.quote_price = mock.Mock(return_value=Decimal(1 - 0.031) * order.price) assert order.is_executable exchange.quote_price = mock.Mock(return_value=Decimal(1 - 0.02) * order.price) assert not order.is_executable
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
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
'Low':'low', 'High':'high', 'Volume':'volume' }, inplace = True) df_USD = df_USD[df_USD['close'].notnull()] dataset = ta.add_all_ta_features(df_USD, 'open', 'high', 'low', 'close', 'volume', fillna=True) price_history = dataset[['date', 'open', 'high', 'low', 'close', 'volume']] # chart data dataset.drop(columns=['date', 'open', 'high', 'low', 'close', 'volume'], inplace=True) micex = Exchange("MICEX", service=execute_order, options=ExchangeOptions(commission = 0.0003, #0.003, min_trade_size = 1e-6, max_trade_size = 1e6, min_trade_price = 1e-8, max_trade_price= 1e8, is_live=False) )( Stream.source(price_history['close'].tolist(), dtype="float").rename("RUR-USD")) portfolio = Portfolio(RUR, [ Wallet(micex, 0 * USD), Wallet(micex, 73000 * RUR), ]) with NameSpace("MICEX"): streams = [Stream.source(dataset[c].tolist(), dtype="float").rename(c) for c in dataset.columns] feed = DataFeed(streams) feed.next()