def test_update__sufficient_cash_reserve(self):
        """
        Tests: Portfolio#update

        Flavour: Enough cash in the portfolio, so the trades should be applied

        Creates a portfolio, a stock market data object and a arbitrary `OrderList` and executes these orders on the
        portfolio. Checks if those are applied correctly
        """
        cash_reserve = 20000.0

        data = StockData([(date(2017, 1, 1), 150.0)])
        stock_market_data = StockMarketData({CompanyEnum.COMPANY_A: data})

        portfolio = Portfolio(cash_reserve,
                              [SharesOfCompany(CompanyEnum.COMPANY_A, 200)])

        order_list = OrderList()
        order_list.buy(CompanyEnum.COMPANY_A, 100)

        updated_portfolio = portfolio.update(stock_market_data, order_list)

        # Current cash reserve is sufficient for trade volume. Trade should happen
        assert updated_portfolio.cash < cash_reserve
        assert updated_portfolio.cash < portfolio.cash
        assert updated_portfolio.shares[
            0].company_enum == CompanyEnum.COMPANY_A
        assert updated_portfolio.shares[0].amount == 300
예제 #2
0
    def is_order_list_valid(self, order_list: OrderList,
                            stock_market_data: StockMarketData) -> bool:
        """
        Validates if generated OrderList is valid in comparison to current Portfolio

        Args:
          order_list: OrderList containing generated orders to be sent into evaluation
          stock_market_data: StockMarketData containing dates and prices for all companies

        Returns:
          `True` if given OrderList is valid in comparison to current Portfolio, `False` otherwise, never None
        """
        current_cash = self.cash

        most_recent_price_company_a = stock_market_data.get_most_recent_price(
            CompanyEnum.COMPANY_A)
        order_company_a = order_list.get_by_company_enum(CompanyEnum.COMPANY_A)

        is_valid, current_cash = self.__is_order_valid(
            current_cash, CompanyEnum.COMPANY_A, order_company_a,
            most_recent_price_company_a)
        if is_valid is False:
            return False

        most_recent_price_company_b = stock_market_data.get_most_recent_price(
            CompanyEnum.COMPANY_B)
        order_company_b = order_list.get_by_company_enum(CompanyEnum.COMPANY_B)

        is_valid, current_cash = self.__is_order_valid(
            current_cash, CompanyEnum.COMPANY_B, order_company_b,
            most_recent_price_company_b)
        if is_valid is False:
            return False

        return True
    def test_update__do_not_drop_below_cash_0(self):
        """
        Tests: Portfolio#update

        Flavour: When receiving two BUY orders the `#update` method should regard the available cash and NEVER drop
         below 0

        Creates a portfolio, a stock market data object and a arbitrary `OrderList` and executes these orders on the
        portfolio. Checks if those are applied correctly
        """
        cash_reserve = 16000.0

        data = StockData([(date(2017, 1, 1), 150.0)])
        stock_market_data = StockMarketData({CompanyEnum.COMPANY_A: data})

        portfolio = Portfolio(cash_reserve, [])

        # Create a order list whose individual actions are within the limit but in sum are over the limit
        # Stock price: 150.0, quantity: 100 -> trade volume: 15000.0; cash: 16000.0
        order_list = OrderList()
        order_list.buy(CompanyEnum.COMPANY_A, 100)
        order_list.buy(CompanyEnum.COMPANY_A, 100)

        updated_portfolio = portfolio.update(stock_market_data, order_list)

        assert updated_portfolio.cash >= 0
예제 #4
0
    def __trade_for_company(self, company_enum: CompanyEnum,
                            company_data: StockData, predictor: IPredictor,
                            portfolio: Portfolio,
                            result_order_list: OrderList):

        last_value = company_data.get_last()[-1]

        # This determines the trade action to apply
        order = self.__determine_action(company_data, predictor, last_value)

        if order == OrderType.BUY:
            if portfolio.cash > last_value:
                # We can buy something
                amount_of_units_to_buy = int(portfolio.cash // last_value)
                result_order_list.buy(company_enum, amount_of_units_to_buy)

                # Update Cash in portfolio
                portfolio.cash = portfolio.cash - (amount_of_units_to_buy *
                                                   last_value)

        elif order == OrderType.SELL:
            # Check if something can be selled
            shares_in_portfolio = self.__find_shares_of_company(
                company_enum, portfolio.shares)
            if shares_in_portfolio is not None:
                # Sell everything
                result_order_list.sell(company_enum,
                                       shares_in_portfolio.amount)
    def doTrade(self, portfolio: Portfolio, current_portfolio_value: float,
                stock_market_data: StockMarketData) -> OrderList:
        """
        Generate action to be taken on the "stock market"
    
        Args:
          portfolio : current Portfolio of this trader
          current_portfolio_value : value of Portfolio at given Momemnt
          stock_market_data : StockMarketData for evaluation

        Returns:
          A OrderList instance, may be empty never None
        """

        order_list = OrderList()

        pred_a_value = self.stock_a_predictor.doPredict(
            stock_market_data[CompanyEnum.COMPANY_A])
        pred_b_value = self.stock_b_predictor.doPredict(
            stock_market_data[CompanyEnum.COMPANY_B])

        stock_a = portfolio.get_amount(CompanyEnum.COMPANY_A)
        stock_a_value = stock_market_data.get_most_recent_price(
            CompanyEnum.COMPANY_A)
        stock_b = portfolio.get_amount(CompanyEnum.COMPANY_B)
        stock_b_value = stock_market_data.get_most_recent_price(
            CompanyEnum.COMPANY_B)

        increase_a = pred_a_value - stock_a_value
        increase_b = pred_b_value - stock_b_value

        new_cash = 0.0

        if stock_a > 0 and increase_a < 0.0:
            order_list.sell(CompanyEnum.COMPANY_A, stock_a)

        if stock_b > 0 and increase_b < 0.0:
            order_list.sell(CompanyEnum.COMPANY_B, stock_b)

        if increase_a > increase_b and increase_a > 0.0:
            count_a = portfolio.cash / stock_a_value
            if count_a > 0:
                order_list.buy(CompanyEnum.COMPANY_A, int(count_a))
        elif increase_b > increase_a and increase_b > 0.0:
            count_b = portfolio.cash / stock_b_value
            if count_b > 0:
                order_list.buy(CompanyEnum.COMPANY_B, int(count_b))

        # TODO: implement trading logic

        return order_list
예제 #6
0
    def doTrade(self, portfolio: Portfolio, current_portfolio_value: float,
                stock_market_data: StockMarketData) -> OrderList:
        """
        Generate action to be taken on the "stock market"
    
        Args:
          portfolio : current Portfolio of this trader
          current_portfolio_value : value of Portfolio at given Momemnt
          stock_market_data : StockMarketData for evaluation

        Returns:
          A OrderList instance, may be empty never None
        """

        y_a = stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_A)
        y_b = stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_B)

        p_a = self.stock_a_predictor.doPredict(
            stock_market_data[CompanyEnum.COMPANY_A])
        p_b = self.stock_b_predictor.doPredict(
            stock_market_data[CompanyEnum.COMPANY_B])

        r_a = (p_a - y_a) / y_a * 100
        r_b = (p_b - y_b) / y_b * 100

        result = OrderList()

        if (r_a < 0 and portfolio.get_amount(CompanyEnum.COMPANY_A) > 0):
            result.sell(CompanyEnum.COMPANY_A,
                        portfolio.get_amount(CompanyEnum.COMPANY_A))

        if (r_b < 0 and portfolio.get_amount(CompanyEnum.COMPANY_B) > 0):
            result.sell(CompanyEnum.COMPANY_B,
                        portfolio.get_amount(CompanyEnum.COMPANY_B))

        if (r_a <= 0 and r_b <= 0):
            return result

        company = CompanyEnum.COMPANY_A

        if (r_b > r_a):
            company = CompanyEnum.COMPANY_B

        buy_amount = math.floor(
            portfolio.cash / stock_market_data.get_most_recent_price(company))

        if (portfolio.cash > 0 and buy_amount > 0):
            result.buy(company, buy_amount)

        print("Portfolio: " + str(
            portfolio.total_value(
                stock_market_data.get_most_recent_trade_day(),
                stock_market_data)))

        # TODO: implement trading logic

        return result
    def doTrade(self, portfolio: Portfolio, current_portfolio_value: float,
                stock_market_data: StockMarketData) -> OrderList:
        """
        Generate action to be taken on the "stock market"
    
        Args:
          portfolio : current Portfolio of this trader
          current_portfolio_value : value of Portfolio at given Momemnt
          stock_market_data : StockMarketData for evaluation

        Returns:
          A OrderList instance, may be empty never None
        """

        result = OrderList()

        predictions = {
            CompanyEnum.COMPANY_A: self.stock_a_predictor.doPredict(stock_market_data[CompanyEnum.COMPANY_A]),
            CompanyEnum.COMPANY_B: self.stock_b_predictor.doPredict(stock_market_data[CompanyEnum.COMPANY_B])
        }

        zuwachs = {}

        # sell companies which get worse
        for company in CompanyEnum:
            prediction = predictions[company]
            anzahl = portfolio.get_amount(company)
            current = stock_market_data.get_most_recent_price(company)

            zuwachs[company] = prediction / current

            # sell if getting worse
            if anzahl > 0:
                if current > prediction:
                    result.sell(company, anzahl)

        best = sorted(zuwachs, key=zuwachs.__getitem__)[::-1]

        currentCash = portfolio.cash

        for b in best:
            prediction = predictions[b]
            current = stock_market_data.get_most_recent_price(b)
            wachstum = (prediction / current)
            vola = self.isVolatile(stock_market_data[b].get_values())

            if wachstum > 1:
                if vola or wachstum > 1.001:
                    count = math.floor(currentCash / current)
                    result.buy(b, count)
                    currentCash -= count * current
                else:
                    result.sell(b, portfolio.get_amount(b))

        return result
    def doTrade(self, portfolio: Portfolio, current_portfolio_value: float,
                stock_market_data: StockMarketData) -> OrderList:

        orders = OrderList()

        cheapest_company = min(
            list(CompanyEnum),
            key=lambda c: stock_market_data.get_most_recent_price(c))

        cash_per_comp = portfolio.cash / stock_market_data.get_number_of_companies(
        )

        price = stock_market_data.get_most_recent_price(cheapest_company)
        num_shares_to_buy = cash_per_comp // price
        if num_shares_to_buy > 0:
            orders.buy(cheapest_company, num_shares_to_buy)
            logger.info("Bought {0} shares.".format(num_shares_to_buy))

        return orders
예제 #9
0
    def create_order_list(self, action_a: float, action_b: float,
                          portfolio: Portfolio, stock_market_data: StockMarketData) -> OrderList:
        """
        Take two floats between -1.0 and +1.0 (one for stock A and one for stock B) and convert them into corresponding
        orders.

        Args:
            action_a: float between -1.0 and 1.0, representing buy(positive) / sell(negative) for Company A
            action_b: float between -1.0 and 1.0, representing buy(positive) / sell(negative) for Company B
            portfolio: current portfolio of this trader
            stock_market_data: current stock market data

        Returns:
            List of corresponding orders
        """
        assert -1.0 <= action_a <= +1.0 and -1.0 <= action_b <= +1.0
        assert portfolio is not None and stock_market_data is not None
        order_list = OrderList()

        # Create orders for stock A
        owned_amount_a = portfolio.get_amount(CompanyEnum.COMPANY_A)
        if action_a > 0.0:
            current_price = stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_A)
            amount_to_buy = int(action_a * (portfolio.cash // current_price))
            order_list.buy(CompanyEnum.COMPANY_A, amount_to_buy)
        if action_a < 0.0 and owned_amount_a > 0:
            amount_to_sell = int(abs(action_a) * owned_amount_a)
            order_list.sell(CompanyEnum.COMPANY_A, amount_to_sell)

        # Create orders for stock B
        owned_amount_b = portfolio.get_amount(CompanyEnum.COMPANY_B)
        if action_b > 0.0:
            current_price = stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_B)
            amount_to_buy = int(action_b * (portfolio.cash // current_price))
            order_list.buy(CompanyEnum.COMPANY_B, amount_to_buy)
        if action_b < 0.0 and owned_amount_b > 0:
            amount_to_sell = int(abs(action_b) * owned_amount_b)
            order_list.sell(CompanyEnum.COMPANY_B, amount_to_sell)
        return order_list
 def create_orders(self, action, portfolio, stock_market_data):
     orders = OrderList()
     if action == TradingAction.SELL_A_AND_B:
         self.sell_shares(CompanyEnum.COMPANY_A, orders, portfolio)
         self.sell_shares(CompanyEnum.COMPANY_B, orders, portfolio)
     elif action == TradingAction.BUY_A_AND_SELL_B:
         self.buy_shares(CompanyEnum.COMPANY_A, orders, portfolio.cash, stock_market_data)
         self.sell_shares(CompanyEnum.COMPANY_B, orders, portfolio)
     elif action == TradingAction.BUY_B_AND_SELL_A:
         self.buy_shares(CompanyEnum.COMPANY_B, orders, portfolio.cash, stock_market_data)
         self.sell_shares(CompanyEnum.COMPANY_A, orders, portfolio)
     elif action == TradingAction.BUY_A_AND_B:
         self.buy_shares(CompanyEnum.COMPANY_A, orders, portfolio.cash / 2, stock_market_data)
         self.buy_shares(CompanyEnum.COMPANY_B, orders, portfolio.cash / 2, stock_market_data)
     return orders
    def doTrade(self, portfolio: Portfolio, current_portfolio_value: float,
                stock_market_data: StockMarketData) -> OrderList:
        """
        Generate action to be taken on the "stock market"
    
        Args:
          portfolio : current Portfolio of this trader
          current_portfolio_value : value of Portfolio at given Momemnt
          stock_market_data : StockMarketData for evaluation

        Returns:
          A OrderList instance, may be empty never None
        """
        result = OrderList()

        predictions = {
            CompanyEnum.COMPANY_A: self.stock_a_predictor.doPredict(stock_market_data[CompanyEnum.COMPANY_A]),
            CompanyEnum.COMPANY_B: self.stock_b_predictor.doPredict(stock_market_data[CompanyEnum.COMPANY_B])
        }

        zuwachs = {}

        # sell companies which get worse
        for company in CompanyEnum:
            prediction = predictions[company]
            anzahl = portfolio.get_amount(company)
            current = stock_market_data.get_most_recent_price(company)

            zuwachs[company] = prediction / current

            # sell if getting worse
            if anzahl > 0:
                if current > prediction:
                    result.sell(company, anzahl)

        bestCompany = None
        bestZuwachs = 1
        for company in CompanyEnum:
            if zuwachs[company] > bestZuwachs:
                bestZuwachs = zuwachs[company]
                bestCompany = company

        if bestCompany:
            current = stock_market_data.get_most_recent_price(company)
            count = math.floor(portfolio.cash / current)
            result.buy(bestCompany, count)



        # TODO: implement trading logic

        return result
예제 #12
0
    def doTrade(self, portfolio: Portfolio, current_portfolio_value: float,
                stock_market_data: StockMarketData) -> OrderList:
        """
        Generate action to be taken on the "stock market"
    
        Args:
          portfolio : current Portfolio of this trader
          current_portfolio_value : value of Portfolio at given Momemnt
          stock_market_data : StockMarketData for evaluation

        Returns:
          A OrderList instance, may be empty never None
        """

        result = OrderList()

        # TODO: implement trading logic

        return result
예제 #13
0
    def doTrade(self, portfolio: Portfolio, current_portfolio_value: float,
                stock_market_data: StockMarketData) -> OrderList:
        """
        Generate action to be taken on the "stock market"
    
        Args:
          portfolio : current Portfolio of this trader
          current_portfolio_value : value of Portfolio at given moment
          stock_market_data : StockMarketData for evaluation

        Returns:
          A OrderList instance, may be empty never None
        """
        # TODO: Build and store current state object

        # TODO: Store experience and train the neural network only if doTrade was called before at least once

        # TODO: Create actions for current state and decrease epsilon for fewer random actions

        # TODO: Save created state, actions and portfolio value for the next call of doTrade

        return OrderList()
예제 #14
0
    def doTrade(self, portfolio: Portfolio, current_portfolio_value: float,
                stock_market_data: StockMarketData) -> OrderList:
        """
        Generate action to be taken on the "stock market"
    
        Args:
          portfolio : current Portfolio of this trader
          current_portfolio_value : value of Portfolio at given Momemnt
          stock_market_data : StockMarketData for evaluation

        Returns:
          A OrderList instance, may be empty never None
        """
        local_portfolio = copy.deepcopy(portfolio)

        result = OrderList()

        company_a_data = stock_market_data[CompanyEnum.COMPANY_A]
        if self.stock_a_predictor is not None and company_a_data is not None:
            self.__trade_for_company(CompanyEnum.COMPANY_A, company_a_data,
                                     self.stock_a_predictor, local_portfolio,
                                     result)
        else:
            logger.warning(
                f" stock_a_predictor:  {self.stock_a_predictor} or company_a_data: {company_a_data} is None "
                f"-> No prediction for Company A")

        company_b_data = stock_market_data[CompanyEnum.COMPANY_B]
        if self.stock_b_predictor is not None and company_b_data is not None:
            self.__trade_for_company(CompanyEnum.COMPANY_B, company_b_data,
                                     self.stock_b_predictor, local_portfolio,
                                     result)
        else:
            logger.warning(
                "stock_b_predictor or company_b_data is None -> No prediction for Company B"
            )

        return result
예제 #15
0
    def doTrade(self, portfolio: Portfolio, current_portfolio_value: float,
                stock_market_data: StockMarketData) -> OrderList:
        """
        Generate action to be taken on the "stock market"
    
        Args:
          portfolio : current Portfolio of this trader
          current_portfolio_value : value of Portfolio at given moment
          stock_market_data : StockMarketData for evaluation

        Returns:
          A OrderList instance, may be empty never None
        """
        # TODO: Build and store current state object

        ## cash %, portfolio a value %, portfolio b %, pred a, pred b

        account_value = portfolio.cash + current_portfolio_value
        a_value = portfolio.get_amount(CompanyEnum.COMPANY_A) * stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_A)
        b_value = portfolio.get_amount(CompanyEnum.COMPANY_B) * stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_B)

        a_value_percent = a_value / account_value
        b_value_percent = b_value / account_value
        cash_percent = portfolio.cash / account_value

        pred_a_value = self.stock_a_predictor.doPredict(stock_market_data[CompanyEnum.COMPANY_A])
        pred_b_value = self.stock_b_predictor.doPredict(stock_market_data[CompanyEnum.COMPANY_B])

        stock_a_value = stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_A)
        stock_b_value = stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_B)

        increase_a = (pred_a_value - stock_a_value) / stock_a_value
        increase_b = (pred_b_value - stock_b_value) / stock_b_value

        current_status = [[cash_percent, a_value_percent, b_value_percent, increase_a, increase_b]]

        np_current_status = np.array(current_status)

        # TODO: Store experience and train the neural network only if doTrade was called before at least once

        ## calc rewards = was cash + portfolio - (old cash + old portfolio), map auf 1 0 -1, now simple:
        if self.stored_action >= 0:

            if current_portfolio_value > self.stored_portfolio_value:
                reward = 1.0
            elif current_portfolio_value < self.stored_portfolio_value:
                reward = -1.0
            else:
                reward = 0

            reward_array = [0, 0, 0]

            reward_array[self.stored_action] = reward

            np_reward_array = np.array([reward_array])

            ## train again

            # print(np_reward_array)


            if self.train_while_trading:
                self.model.fit(self.np_previous_status, np_reward_array, epochs=1, batch_size=1, verbose=0)

        # TODO: Create actions for current state and decrease epsilon for fewer random actions

        action = -1

        random_value = uniform(0.0, 1.0)
        if random_value > self.epsilon:
            action = randint(0, 2)
        else:
            pred = self.model.predict(np_current_status)
            prediction = pred[0]

            if len(prediction) != 3:
                print("komische prediction")

            action = 2
            max = prediction[action]

            for i in range(3):
                if prediction[i] > max:
                    action = i


        self.count[action] = self.count[action] + 1
        # print("actions")
        # print(self.count)

        self.epsilon = self.epsilon * self.epsilon_decay
        if self.epsilon < self.epsilon_min:
            self.epsilon = self.epsilon_min

        order_list = OrderList()

        if action < 0 or action > 2:
            print("komische action")
            print(action)
        else:

            stock_a = portfolio.get_amount(CompanyEnum.COMPANY_A)
            stock_b = portfolio.get_amount(CompanyEnum.COMPANY_B)

            if action == 0:
                # sell a
                if stock_a > 0:
                    order_list.sell(CompanyEnum.COMPANY_A, stock_a)
                # buy b
                count_b = portfolio.cash / stock_b_value
                if count_b > 0:
                    order_list.buy(CompanyEnum.COMPANY_B, int(count_b))
            if action == 1:
                # sell b
                if stock_b > 0:
                    order_list.sell(CompanyEnum.COMPANY_B, stock_b)
                # boy a
                count_a = portfolio.cash / stock_a_value
                if count_a > 0:
                    order_list.buy(CompanyEnum.COMPANY_A, int(count_a))

        # TODO: Save created state, actions and portfolio value for the next call of

        self.stored_action = action
        self.stored_portfolio_value = current_portfolio_value
        self.np_previous_status = np_current_status

        ## save current state as laststate, current portfolio and cash

        return order_list
    def doTrade(self, portfolio: Portfolio, current_portfolio_value: float,
                stock_market_data: StockMarketData) -> OrderList:
        """
        Generate action to be taken on the "stock market"
    
        Args:
          portfolio : current Portfolio of this trader
          current_portfolio_value : value of Portfolio at given moment
          stock_market_data : StockMarketData for evaluation

        Returns:
          A OrderList instance, may be empty never None
        """
        # TODO: Build and store current state object
        s_a_current = stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_A)
        s_b_current = stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_B)

        pct_a = round((s_a_current * portfolio.get_amount(CompanyEnum.COMPANY_A))/portfolio.total_value(stock_market_data.get_most_recent_trade_day(), stock_market_data), 2)
        pct_b = round((s_b_current * portfolio.get_amount(CompanyEnum.COMPANY_B))/portfolio.total_value(stock_market_data.get_most_recent_trade_day(), stock_market_data), 2)
        pct_cash = 1 - pct_a - pct_b;


        s_a_next = self.stock_a_predictor.doPredict(stock_market_data[CompanyEnum.COMPANY_A]);
        s_b_next = self.stock_b_predictor.doPredict(stock_market_data[CompanyEnum.COMPANY_B]);

        pct_a_diff = round((s_a_next - s_a_current) / s_a_current, 2);
        pct_b_diff = round((s_b_next - s_b_current) / s_b_current, 2);

        portfolio_value = portfolio.total_value(stock_market_data.get_most_recent_trade_day(), stock_market_data);
        portfolio_diff = round((self.portfolio_value_prev - portfolio_value) / self.portfolio_value_prev, 2);




        #r = -1;
        #if (portfolio_diff < 0):
        #    r = 1;
        #elif (portfolio_diff == 0):
        #    r = 0;

        if (portfolio_diff < 0):
            self.y_prev[0][self.idx_prev] = 1;
        elif (portfolio_diff >= 0):
            self.y_prev[0][self.idx_prev] = -1;




        # TODO: Store experience and train the neural network only if doTrade was called before at least once
        if (self.loop_value > 0):
            self.model.fit(np.array([[self.pct_a_prev, self.pct_b_prev, self.pct_cash_prev, self.diff_a_prev, self.diff_b_prev]]), self.y_prev, epochs=1, batch_size=1)

        # TODO: Create actions for current state and decrease epsilon for fewer random actions

        res = self.model.predict(np.array([[pct_a, pct_b, pct_cash, pct_a_diff, pct_b_diff]])) # [0, 0, 1, 0]
        idx = np.argmax(res);

        ret = OrderList()


        if (idx == 0):
            ret.buy(CompanyEnum.COMPANY_A, math.floor(portfolio.cash / stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_A)))
        elif (idx == 1):
            ret.buy(CompanyEnum.COMPANY_B, math.floor(portfolio.cash / stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_B)))
        elif (idx == 2):
            ret.sell(CompanyEnum.COMPANY_A, portfolio.get_amount(CompanyEnum.COMPANY_A))     
        else:
            ret.sell(CompanyEnum.COMPANY_B, portfolio.get_amount(CompanyEnum.COMPANY_B))



        # TODO: Save created state, actions and portfolio value for the next call of doTrade
        self.portfolio_value_prev = portfolio_value;
        self.pct_a_prev = pct_a;
        self.pct_b_prev = pct_b;
        self.pct_cash_prev = pct_cash;
        self.diff_a_prev = pct_a_diff;
        self.diff_b_prev = pct_a_diff;
        self.idx_prev = idx;
        self.y_prev = res;

        self.loop_value = self.loop_value + 1;

        return ret
    def doTrade(self, portfolio: Portfolio, current_portfolio_value: float,
                stock_market_data: StockMarketData) -> OrderList:
        """
        Generate action to be taken on the "stock market"
    
        Args:
          portfolio : current Portfolio of this trader
          current_portfolio_value : value of Portfolio at given moment
          stock_market_data : StockMarketData for evaluation

        Returns:
          A OrderList instance, may be empty never None
        """

        # TODO: Store experience and train the neural network only if doTrade was called before at least once

        # TODO: Create actions for current state and decrease epsilon for fewer random actions

        # TODO: Save created state, actions and portfolio value for the next call of doTrade

        deltaA = self.stock_a_predictor.doPredict(
            stock_market_data[CompanyEnum.COMPANY_A]) / stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_A)
        deltaB = self.stock_b_predictor.doPredict(
            stock_market_data[CompanyEnum.COMPANY_B]) / stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_B)

        INPUT = numpy.asarray([[
            (deltaA - 1.0) / 0.04,
            (deltaB - 1.0) / 0.04,
        ]])
        qualities = self.model.predict(INPUT)[0]

        qmax = max(qualities[0], qualities[1], qualities[2])

        currentValue = portfolio.total_value(stock_market_data.get_most_recent_trade_day(), stock_market_data)

        if self.lastValue and self.train_while_trading:
            lastReward = min(1, max(-1, (currentValue / self.lastValue - 1) / 0.04))
            shouldBeQ = lastReward + GAMMA * qmax

            self.lastOutput[self.lastAmax] = shouldBeQ

            xtrain = [self.lastInput[0]]
            ytrain = [self.lastOutput]
            for m in self.memory:
                xtrain.append(m[0][0])
                qs = self.model.predict(m[0])[0]
                qs[m[1]] = m[2] + GAMMA * qs[m[1]]
                ytrain.append(qs)

            self.model.fit(numpy.asarray(xtrain), numpy.asarray(ytrain))

            self.memory.append([self.lastInput, self.lastAmax, lastReward])
            if len(self.memory) > MEMOMRY_SIZE:
                self.memory.pop(0)

        self.lastValue = currentValue
        self.lastInput = INPUT
        self.lastOutput = qualities

        result = OrderList()

        actions = ["BUY_A__SELL_B", "BUY_A", "BUY_B__SELL_A", "BUY_B", "SELL_ALL"]

        nextAction = None
        if random.random() < self.epsilon and self.train_while_trading:
            nextAction = actions[random.randint(0, self.action_size - 1)]
        else:
            i = 0 if qualities[0] > qualities[1] else 1
            i = 2 if qualities[2] > qualities[i] else i
            i = 3 if qualities[3] > qualities[i] else i
            i = 4 if qualities[4] > qualities[i] else i
            nextAction = actions[i]

        self.epsilon = max(self.epsilon_decay * self.epsilon, self.epsilon_min)

        if nextAction == "BUY_A__SELL_B":
            result.sell(CompanyEnum.COMPANY_B, portfolio.get_amount(CompanyEnum.COMPANY_B))
            count = math.floor(portfolio.cash / stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_A))
            result.buy(CompanyEnum.COMPANY_A, count)
            self.lastAmax = 0
        elif nextAction == "BUY_A":
            count = math.floor(portfolio.cash / stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_A))
            result.buy(CompanyEnum.COMPANY_A, count)
            self.lastAmax = 1
        elif nextAction == "BUY_B__SELL_A":
            result.sell(CompanyEnum.COMPANY_A, portfolio.get_amount(CompanyEnum.COMPANY_A))
            count = math.floor(portfolio.cash / stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_B))
            result.buy(CompanyEnum.COMPANY_B, count)
            self.lastAmax = 2
        elif nextAction == "BUY_B":
            count = math.floor(portfolio.cash / stock_market_data.get_most_recent_price(CompanyEnum.COMPANY_B))
            result.buy(CompanyEnum.COMPANY_B, count)
            self.lastAmax = 3
        elif nextAction == "SELL_ALL":
            result.sell(CompanyEnum.COMPANY_A, portfolio.get_amount(CompanyEnum.COMPANY_A))
            result.sell(CompanyEnum.COMPANY_B, portfolio.get_amount(CompanyEnum.COMPANY_B))
            self.lastAmax = 4

        return result
예제 #18
0
    def update(self, stock_market_data: StockMarketData,
               order_list: OrderList):
        """
        Iterates through the list of orders (`order_list`), applies those orders and returns an updated
        `Portfolio` object based on the given `StockMarketData`. If `order_list` is empty nothing will be changed

        Args:
            stock_market_data: The market data based on which the actions are applied
            order_list: The list of orders to apply

        Returns:
            An updated portfolio. This is a deep copy of the given `portfolio` (see `copy.deepcopy`)
        """
        updated_portfolio = copy.deepcopy(self)

        logger.debug(f"Updating portfolio {self.name}:")

        if order_list.is_empty():
            logger.debug("The order list is empty. No action this time")
            return updated_portfolio

        available_cash = updated_portfolio.cash

        for order in iter(order_list):
            company_enum = order.shares.company_enum

            current_date = stock_market_data.get_most_recent_trade_day()
            current_price = stock_market_data.get_most_recent_price(
                company_enum)

            logger.debug(f"Available cash on {current_date}: {available_cash}")
            # for share in update.shares:
            share = updated_portfolio.get_or_insert(company_enum)
            # if share.name is update.shares.name:
            amount = order.shares.amount
            trade_volume = amount * current_price

            if order.action is OrderType.BUY:
                logger.debug(
                    f"Buying {amount} shares of '{share.company_enum}' with an individual value of "
                    f"{current_price}")
                logger.debug(f"  Volume of this trade: {trade_volume}")

                if trade_volume <= available_cash:
                    share.amount += amount
                    updated_portfolio.cash -= trade_volume
                    available_cash -= trade_volume
                else:
                    logger.warning(
                        f"No sufficient cash reserve ({updated_portfolio.cash}) for planned transaction "
                        f"with volume of {trade_volume}")
            elif order.action is OrderType.SELL:
                logger.debug(
                    f"Selling {amount} shares of {share.company_enum} with individual value of "
                    f"{current_price}")
                logger.debug(f"  Volume of this trade: {trade_volume}")

                if share.amount >= amount:
                    share.amount -= amount
                    updated_portfolio.cash += trade_volume
                else:
                    logger.warning(
                        f"Not sufficient shares in portfolio ({amount}) for planned sale of {share.amount} "
                        f"shares")

            logger.debug(
                f"Resulting available cash after trade: {updated_portfolio.cash}"
            )

            total_portfolio_value = updated_portfolio.total_value(
                current_date, stock_market_data)
            logger.debug(
                f"Total portfolio value after trade: {total_portfolio_value}")

        return updated_portfolio
    def test_update__action_order_does_not_matter(self):
        """
        Tests: Portfolio#update

        Flavour: It shouldn't matter which order the orders are in, the result should always look the same. In
         this case the portfolio's cash reserve is too low to execute a BUY action. However, it shouldn't matter if we
         execute a SELL action first, because the updated cash reserve after a SELL action shouldn't affect the
         available cash reserve for a subsequent BUY action

        Creates a portfolio, a stock market data object and a arbitrary `OrderList` and executes these orders on the
        portfolio. Checks if those are applied correctly
        """
        cash_reserve = 10.0

        data = StockData([(date(2017, 1, 1), 150.0)])
        stock_market_data = StockMarketData({CompanyEnum.COMPANY_A: data})

        # Create two equal designed portfolios
        portfolio1 = Portfolio(cash_reserve,
                               [SharesOfCompany(CompanyEnum.COMPANY_A, 200)])
        portfolio2 = Portfolio(cash_reserve,
                               [SharesOfCompany(CompanyEnum.COMPANY_A, 200)])

        assert portfolio1 == portfolio2

        # Create two order lists with the same entries, however in different order
        order_list_1 = OrderList()
        order_list_1.buy(CompanyEnum.COMPANY_A, 100)
        order_list_1.sell(CompanyEnum.COMPANY_A, 100)

        order_list_2 = OrderList()
        order_list_2.sell(CompanyEnum.COMPANY_A, 100)
        order_list_2.buy(CompanyEnum.COMPANY_A, 100)

        # Execute the trade action lists on the two portfolios
        updated_portfolio_order1 = portfolio1.update(stock_market_data,
                                                     order_list_1)
        updated_portfolio_order2 = portfolio2.update(stock_market_data,
                                                     order_list_2)

        # The portfolios should still be equal after applying the actions
        assert updated_portfolio_order1 == updated_portfolio_order2