Пример #1
0
    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))
Пример #5
0
    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}")
Пример #6
0
    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)
Пример #7
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