def test_run_two_days_two_traders(self): stock_exchange = StockExchange() stock_market_data = StockMarketData([Company.A, Company.B], [Period.TESTING]).deepcopy_first_n_items(2) trader1 = BuyAndHoldTrader() trader2 = BuyAndHoldTrader() result = stock_exchange.run(stock_market_data, [trader1, trader2]) # test final day final_day = stock_market_data.get_most_recent_trade_day() self.assertEqual(final_day, Date(2012, 1, 4)) # test final portfolio1 final_portfolio1 = result[trader1][final_day] self.assertIsNotNone(final_portfolio1) self.assertEqual(final_portfolio1.cash, 24.807061999999974) self.assertEqual(final_portfolio1.get_stock(Company.A), 14) self.assertEqual(final_portfolio1.get_stock(Company.B), 3) self.assertEqual(final_portfolio1.get_value(stock_market_data), 1005.0684910000001) # test final portfolio2 final_portfolio2 = result[trader2][final_day] self.assertIsNotNone(final_portfolio2) self.assertEqual(final_portfolio2.cash, 24.807061999999974) self.assertEqual(final_portfolio2.get_stock(Company.A), 14) self.assertEqual(final_portfolio2.get_stock(Company.B), 3) self.assertEqual(final_portfolio2.get_value(stock_market_data), 1005.0684910000001)
def test_get_most_recent_trade_day(self): """ Tests: StockMarketData#get_most_recent_trade_day Read the stock market data and check if the last available date is determined correctly """ stock_market_data = StockMarketData([Company.A, Company.B], [Period.TRAINING, Period.TESTING]) self.assertEqual(stock_market_data.get_most_recent_trade_day(), stock_market_data[Company.A].get_last()[0])
def test_stock_market_data_one_company_one_period(self): stock_market_data = StockMarketData([Company.A], [Period.TRAINING]) self.assertIsNotNone(stock_market_data) self.assertEqual(stock_market_data.get_number_of_companies(), 1) self.assertEqual(stock_market_data.get_row_count(), 12588) self.assertEqual(stock_market_data.get_most_recent_trade_day(), Date(2011, 12, 30)) self.assertEqual(stock_market_data.get_most_recent_price(Company.A), 34.802376) self.assertIsNone(stock_market_data.get_most_recent_price(Company.B))
def test_stock_market_data_one_company_two_periods(self): stock_market_data = StockMarketData([Company.A], [Period.TRAINING, Period.TESTING]) self.assertIsNotNone(stock_market_data) self.assertEqual(stock_market_data.get_number_of_companies(), 1) self.assertEqual(stock_market_data.get_row_count(), 13594) self.assertEqual(stock_market_data.get_most_recent_trade_day(), Date(2015, 12, 31)) self.assertEqual(stock_market_data.get_most_recent_price(Company.A), 102.759895) self.assertIsNone(stock_market_data.get_most_recent_price(Company.B))
def update_with_order_list(self, stock_market_data: StockMarketData, orders: List[Order]): """ Update the portfolio by executing all given stock orders simultaneously. Executing simultaneously means: 1) The order in which the stock orders are executed does not matter. 2) Cash from selling stocks today is only available for buying stocks tomorrow. If a stock order couldn't be executed (e.g., not enough cash/stocks available), then that order is skipped. :param stock_market_data: Information about all stock prices :param orders: The list of all stock orders :return: """ assert stock_market_data is not None assert orders is not None if len(orders) == 0: logger.debug("The order list is empty. No portfolio update this time") return available_cash = self.cash current_date = stock_market_data.get_most_recent_trade_day() logger.debug(f"Updating portfolio {self}: Available cash on {current_date} is {available_cash}") for order in orders: # get the infos about the order and the existing stock company = order.company current_price = stock_market_data.get_most_recent_price(company) amount = order.amount trade_volume = amount * current_price existing_amount = self.get_stock(company) if order.type is OrderType.BUY: logger.debug(f"Buying {amount} stocks of '{company}' at {current_price} (total {trade_volume})") if trade_volume <= available_cash: self.stocks[company] = existing_amount + amount self.cash -= trade_volume available_cash -= trade_volume else: logger.debug(f"Not enough cash ({available_cash}) for transaction with volume of {trade_volume}") elif order.type is OrderType.SELL: logger.debug(f"Selling {amount} stocks of '{company}' at {current_price} (total {trade_volume})") if existing_amount >= amount: self.stocks[company] = existing_amount - amount self.cash += trade_volume else: logger.debug(f"Not enough stocks ({existing_amount}) for selling {amount} of them") else: assert False logger.debug(f"Resulting available cash after trade: {self.cash}")
def test_run_two_days_correct_offset(self): stock_exchange = StockExchange() stock_market_data = StockMarketData([Company.A, Company.B], [Period.TESTING]).deepcopy_first_n_items(2) trader = BuyAndHoldTrader() result = stock_exchange.run(stock_market_data, [trader], 1) # test final day final_day = stock_market_data.get_most_recent_trade_day() self.assertEqual(final_day, Date(2012, 1, 4)) # test final portfolio final_portfolio = result[trader][final_day] self.assertIsNotNone(final_portfolio) self.assertEqual(final_portfolio.cash, 1000.0) self.assertEqual(final_portfolio.get_stock(Company.A), 0) self.assertEqual(final_portfolio.get_stock(Company.B), 0) self.assertEqual(final_portfolio.get_value(stock_market_data), 1000.0)
def trade(self, portfolio: Portfolio, stock_market_data: StockMarketData) -> List[Order]: """ Generate action to be taken on the "stock marketf" Args: portfolio : current Portfolio of this traders stock_market_data : StockMarketData for evaluation Returns: A OrderList instance, may be empty never None """ assert portfolio is not None assert stock_market_data is not None assert stock_market_data.get_companies() == [Company.A, Company.B] # TODO Compute the current state stock_data_a = None stock_data_b = None last_stock_data_a = None last_stock_data_b = None company_list = stock_market_data.get_companies() for company in company_list: if company == Company.A: stock_data_a = stock_market_data[Company.A] last_stock_data_a = stock_data_a.get_from_offset(-2) elif company == Company.B: stock_data_b = stock_market_data[Company.B] last_stock_data_b = stock_data_b.get_from_offset(-2) else: assert False vote_a = self.expert_a.vote(stock_data_a) vote_b = self.expert_b.vote(stock_data_b) state = State(last_stock_data_a, last_stock_data_b, vote_a, vote_b) # TODO Q-Learning nn_input = np.array( [np.array([state.aDiff, state.vote_a, state.bDiff, state.vote_b])]) action_vals = self.model.predict(nn_input) # TODO Store state as experience (memory) and train the neural network only if trade() was called before at least once # TODO Create actions for current state and decrease epsilon for fewer random actions actions = [[ Order(OrderType.BUY, Company.A, int((portfolio.cash / 2) // stock_data_a.get_last()[-1])), Order(OrderType.BUY, Company.B, int((portfolio.cash / 2) // stock_data_b.get_last()[-1])) ], [ Order( OrderType.BUY, Company.A, int((portfolio.cash) // stock_data_a.get_last()[-1])), Order(OrderType.SELL, Company.B, portfolio.get_stock(Company.B)) ], [ Order(OrderType.SELL, Company.A, portfolio.get_stock(Company.A)), Order( OrderType.BUY, Company.B, int(portfolio.cash // stock_data_b.get_last()[-1])) ], [ Order(OrderType.SELL, Company.A, portfolio.get_stock(Company.A)), Order(OrderType.SELL, Company.B, portfolio.get_stock(Company.B)) ], [ Order(OrderType.SELL, Company.A, 0), Order(OrderType.SELL, Company.B, 0) ]] if not self.train_while_trading: self.epsilon = 0.0 else: if self.epsilon > self.epsilon_min: self.epsilon *= self.epsilon_decay else: self.epsilon = self.epsilon_min # randomize action if random.random() < self.epsilon: next_action = random.choice(list(range(self.action_size))) else: next_action = np.argmax(action_vals[0]) order_list = actions[next_action] portfolio_value = portfolio.get_value( stock_market_data, stock_market_data.get_most_recent_trade_day()) if (self.last_state != None and self.train_while_trading): def reward(oldVal, newVal): neg = -100.0 pos = 100.0 q = newVal / oldVal if q < 1: return neg elif q == 1: return -10 else: print("Q: ", q) return pos / 2 * oldVal / newVal r = reward(self.last_portfolio_value, portfolio_value) action_vals[0][self.last_order] = r self.memory.append([self.last_input, action_vals]) if (len(self.memory) > self.min_size_of_memory_before_training): sample = random.sample(self.memory, self.batch_size) trainSample = list() testSample = list() for [sampleIn, sampleOut] in sample: trainSample.append(sampleIn[0]) testSample.append(sampleOut[0]) self.model.fit(np.array(trainSample), np.array(testSample), self.batch_size) # Save created state, actions and portfolio value for the next call of trade() self.last_input = nn_input self.last_state = state self.last_order = next_action self.last_portfolio_value = portfolio_value print(next_action, action_vals, portfolio.cash, portfolio.get_stock(Company.A), portfolio.get_stock(Company.B)) return order_list