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