def test_apply_dividends_to_games(self):
        user_id = 1
        date = datetime_to_posix(dt.now().replace(hour=0,
                                                  minute=0,
                                                  second=0,
                                                  microsecond=0))
        amzn_dividend = 10
        tsla_dividend = 20
        envidia_dividend = 15
        fake_dividends = ([['AMZN', 'Amazon INC', amzn_dividend, date],
                           ['TSLA', 'Tesla Motors', tsla_dividend, date],
                           ['NVDA', 'Envidia SA', envidia_dividend, date]])
        fake_dividends = pd.DataFrame(
            fake_dividends,
            columns=['symbol', 'company', 'amount', 'exec_date'])

        insert_dividends_to_db(fake_dividends)
        user_1_game_8_balance = get_current_game_cash_balance(user_id, 8)
        user_1_game_3_balance = get_current_game_cash_balance(user_id, 3)
        should_remain_1_000_000 = get_current_game_cash_balance(user_id, 6)
        apply_dividends_to_stocks()

        # user 1, game 8 is holding NVDA. we should see their holding * the dividend in their updated cash balance
        envidia_holding = get_current_stock_holding(user_id, 8, "NVDA")
        self.assertEqual(
            get_current_game_cash_balance(user_id, 8) - user_1_game_8_balance,
            envidia_holding * envidia_dividend)

        # user 1, game 3 is holding AMZN, TSLA, and NVDA. Their change in cash balance should be equal to the sum of
        # each position * its corresponding dividend
        active_balances = get_active_balances(3, user_id)
        merged_table = fake_dividends.merge(active_balances,
                                            how="left",
                                            on="symbol")
        merged_table[
            "payout"] = merged_table["amount"] * merged_table["balance"]
        total_dividend = merged_table["payout"].sum()
        self.assertEqual(
            get_current_game_cash_balance(user_id, 3) - user_1_game_3_balance,
            total_dividend)

        # user 1 isn't holding any dividend-paying shares in game 6. they should have no cash balance change
        self.assertEqual(get_current_game_cash_balance(user_id, 6),
                         should_remain_1_000_000)
Exemple #2
0
def api_place_order():
    """Placing an order involves several layers of conditional logic: is this is a buy or sell order? Stop, limit, or
    market? Do we either have the adequate cash on hand, or enough of a position in the stock for this order to be
    valid? Here an order_ticket from the frontend--along with the user_id tacked on during the API call--gets decoded,
    checked for validity, and booked. Market orders are fulfilled in the same step. Stop/limit orders are monitored on
    an ongoing basis by the celery schedule and book as their requirements are satisfies"""
    user_id = decode_token(request)
    order_ticket = request.json
    game_id = order_ticket["game_id"]
    stop_limit_price = order_ticket.get("stop_limit_price")
    if stop_limit_price:
        stop_limit_price = float(stop_limit_price)
    try:
        symbol = order_ticket["symbol"].upper()  # ensure upper casing
        market_price, last_updated = fetch_price(symbol)
        async_cache_price.delay(symbol, market_price, last_updated)
        cash_balance = get_current_game_cash_balance(user_id, game_id)
        current_holding = get_current_stock_holding(user_id, game_id, symbol)
        order_id = place_order(
            user_id,
            game_id,
            symbol,
            order_ticket["buy_or_sell"],
            cash_balance,
            current_holding,
            order_ticket["order_type"],
            order_ticket["quantity_type"],
            market_price,
            float(order_ticket["amount"]),
            order_ticket["time_in_force"],
            stop_limit_price)
    except Exception as e:
        return make_response(str(e), 400)

    serialize_and_pack_pending_orders(game_id, user_id)
    add_fulfilled_order_entry(game_id, user_id, order_id)
    serialize_and_pack_portfolio_details(game_id, user_id)
    return make_response(ORDER_PLACED_MESSAGE, 200)
Exemple #3
0
    def test_async_process_all_open_orders(self, base_time_mock):
        base_time_mock.time.return_value = 1596738863.111858
        user_id = 1
        game_id = 3
        _user_ids = get_active_game_user_ids(game_id)
        for _user_id in _user_ids:
            no_pending_orders_table(game_id, _user_id)
            no_fulfilled_orders_table(game_id, _user_id)

        # Place a guaranteed-to-clear order
        buy_stock = "MSFT"
        mock_buy_order = {"amount": 1,
                          "buy_or_sell": "buy",
                          "game_id": 3,
                          "order_type": "stop",
                          "stop_limit_price": 1,
                          "market_price": 0.5,
                          "quantity_type": "Shares",
                          "symbol": buy_stock,
                          "time_in_force": "until_cancelled"
                          }
        current_cash_balance = get_current_game_cash_balance(user_id, game_id)
        current_holding = get_current_stock_holding(user_id, game_id, buy_stock)
        place_order(user_id,
                    game_id,
                    mock_buy_order["symbol"],
                    mock_buy_order["buy_or_sell"],
                    current_cash_balance,
                    current_holding,
                    mock_buy_order["order_type"],
                    mock_buy_order["quantity_type"],
                    mock_buy_order["market_price"],
                    mock_buy_order["amount"],
                    mock_buy_order["time_in_force"],
                    mock_buy_order["stop_limit_price"])

        # Place a guaranteed-to-clear order
        buy_stock = "AAPL"
        mock_buy_order = {"amount": 1,
                          "buy_or_sell": "buy",
                          "game_id": 3,
                          "order_type": "stop",
                          "stop_limit_price": 1,
                          "market_price": 0.5,
                          "quantity_type": "Shares",
                          "symbol": buy_stock,
                          "time_in_force": "until_cancelled"
                          }
        current_cash_balance = get_current_game_cash_balance(user_id, game_id)
        current_holding = get_current_stock_holding(user_id, game_id, buy_stock)
        place_order(user_id,
                    game_id,
                    mock_buy_order["symbol"],
                    mock_buy_order["buy_or_sell"],
                    current_cash_balance,
                    current_holding,
                    mock_buy_order["order_type"],
                    mock_buy_order["quantity_type"],
                    mock_buy_order["market_price"],
                    mock_buy_order["amount"],
                    mock_buy_order["time_in_force"],
                    mock_buy_order["stop_limit_price"])

        open_orders = get_all_open_orders(game_id)
        starting_open_orders = len(open_orders)
        self.assertEqual(starting_open_orders, 4)
        for order_id, _ in open_orders.items():
            process_order(order_id)
        new_open_orders = get_all_open_orders(game_id)
        self.assertLessEqual(starting_open_orders - len(new_open_orders), 4)
Exemple #4
0
    def test_play_game_tasks(self):
        start_time = time.time()
        game_title = "lucky few"
        creator_id = 1
        mock_game = {
            "creator_id": creator_id,
            "title": game_title,
            "game_mode": "multi_player",
            "duration": 180,
            "buy_in": 100,
            "benchmark": "return_ratio",
            "side_bets_perc": 50,
            "side_bets_period": "weekly",
            "invitees": ["miguel", "murcitdev", "toofast"],
            "invite_window": DEFAULT_INVITE_OPEN_WINDOW,
            "stakes": "monopoly"
        }

        game_id = add_game(
            mock_game["creator_id"],
            mock_game["title"],
            mock_game["game_mode"],
            mock_game["duration"],
            mock_game["benchmark"],
            mock_game["stakes"],
            mock_game["buy_in"],
            mock_game["side_bets_perc"],
            mock_game["side_bets_period"],
            mock_game["invitees"],
            mock_game["invite_window"]
        )

        game_entry = query_to_dict("SELECT * FROM games WHERE title = %s", game_title)[0]

        # Check the game entry table
        # OK for these results to shift with the test fixtures
        self.assertEqual(game_entry["id"], game_id)
        for k, v in mock_game.items():
            if k == "invitees":
                continue
            if k == "duration":
                continue
            if k == "invite_window":
                self.assertAlmostEqual(game_entry[k], start_time + DEFAULT_INVITE_OPEN_WINDOW * SECONDS_IN_A_DAY, 3)
                continue
            self.assertAlmostEqual(game_entry[k], v, 1)

        # Confirm that game status was updated as expected
        # ------------------------------------------------
        game_status_entry = query_to_dict("SELECT * FROM game_status WHERE game_id = %s", game_id)[0]
        self.assertEqual(game_status_entry["id"], 17)
        self.assertEqual(game_status_entry["game_id"], game_id)
        self.assertEqual(game_status_entry["status"], "pending")
        users_from_db = json.loads(game_status_entry["users"])
        self.assertEqual(set(users_from_db), {3, 4, 5, 1})

        # and that the game invites table is working as well
        # --------------------------------------------------
        with self.engine.connect() as conn:
            game_invites_df = pd.read_sql("SELECT * FROM game_invites WHERE game_id = %s", conn, params=[game_id])

        self.assertEqual(game_invites_df.shape, (4, 5))
        for _, row in game_invites_df.iterrows():
            self.assertIn(row["user_id"], users_from_db)
            status = "invited"
            if row["user_id"] == creator_id:
                status = "joined"
            self.assertEqual(row["status"], status)
            # less than a two-second difference between when we sent the data and when it was logged. If the local
            # celery worked is gummed up and not working properly this can fail
            self.assertTrue(row["timestamp"] - start_time < 2)

        # we'll mock in a time value for the current game in a moment, but first check that async_service_open_games is
        # working as expected

        with self.engine.connect() as conn:
            gi_count_pre = conn.execute("SELECT COUNT(*) FROM game_invites;").fetchone()[0]

        open_game_ids = get_open_game_ids_past_window()
        for _id in open_game_ids:
            service_open_game(_id)

        with self.engine.connect() as conn:
            gi_count_post = conn.execute("SELECT COUNT(*) FROM game_invites;").fetchone()[0]

        self.assertEqual(gi_count_post - gi_count_pre, 4)
        with self.engine.connect() as conn:
            df = pd.read_sql("SELECT game_id, user_id, status FROM game_invites WHERE game_id in (1, 2)", conn)
            self.assertEqual(df[df["user_id"] == 5]["status"].to_list(), ["invited", "expired"])
            self.assertEqual(df[(df["user_id"] == 3) & (df["game_id"] == 2)]["status"].to_list(), ["joined"])

        # murcitdev is going to decline to play, toofast and miguel will play and receive their virtual cash balances
        # -----------------------------------------------------------------------------------------------------------
        for user_id in [3, 4]:
            respond_to_game_invite(game_id, user_id, "joined", time.time())
        respond_to_game_invite(game_id, 5, "declined", time.time())

        # So far so good. Pretend that we're now past the invite open window and it's time to play
        # ----------------------------------------------------------------------------------------
        game_start_time = time.time() + DEFAULT_INVITE_OPEN_WINDOW * SECONDS_IN_A_DAY + 1
        with patch("backend.logic.games.time") as mock_time:
            # users have joined, and we're past the invite window
            mock_time.time.return_value = game_start_time
            open_game_ids = get_open_game_ids_past_window()
            for _id in open_game_ids:
                service_open_game(_id)

            with self.engine.connect() as conn:
                # Verify game updated to active status and active players
                game_status = conn.execute(
                    "SELECT status, users FROM game_status WHERE game_id = %s ORDER BY id DESC LIMIT 0, 1",
                    game_id).fetchone()
                self.assertEqual(game_status[0], "active")
                self.assertEqual(len(set(json.loads(game_status[1])) - {1, 3, 4}), 0)

                # Verify that we have three plays for game 5 with $1,000,000 virtual cash balances
                res = conn.execute(
                    "SELECT balance FROM game_balances WHERE game_id = %s AND balance_type = 'virtual_cash';",
                    game_id).fetchall()
                balances = [x[0] for x in res]
                self.assertIs(len(balances), 3)
                self.assertTrue(all([x == STARTING_VIRTUAL_CASH for x in balances]))

        # For now I've tried to keep things simple and divorce the ordering part of the integration test from game
        # startup. May need to close the loop on this later when expanding the test to cover payouts
        game_start_time = 1590508896
        # Place two market orders and a buy limit order
        with patch("backend.logic.games.time") as mock_game_time, patch(
                "backend.logic.base.time") as mock_data_time:
            mock_game_time.time.return_value = mock_data_time.time.return_value = game_start_time + 300

            # Everything working as expected. Place a couple buy orders to get things started
            stock_pick = "AMZN"
            user_id = 1
            order_quantity = 500_000
            amzn_price, _ = fetch_price(stock_pick)

            cash_balance = get_current_game_cash_balance(user_id, game_id)
            current_holding = get_current_stock_holding(user_id, game_id, stock_pick)
            place_order(
                user_id=user_id,
                game_id=game_id,
                symbol=stock_pick,
                buy_or_sell="buy",
                cash_balance=cash_balance,
                current_holding=current_holding,
                order_type="market",
                quantity_type="USD",
                market_price=amzn_price,
                amount=order_quantity,
                time_in_force="day"
            )

            original_amzn_holding = get_current_stock_holding(user_id, game_id, stock_pick)
            updated_cash = get_current_game_cash_balance(user_id, game_id)
            expected_quantity = order_quantity // amzn_price
            expected_cost = expected_quantity * amzn_price
            self.assertEqual(original_amzn_holding, expected_quantity)
            test_user_original_cash = STARTING_VIRTUAL_CASH - expected_cost
            self.assertAlmostEqual(updated_cash, test_user_original_cash, 2)

            stock_pick = "MELI"
            user_id = 4
            order_quantity = 600
            meli_price = 600

            cash_balance = get_current_game_cash_balance(user_id, game_id)
            current_holding = get_current_stock_holding(user_id, game_id, stock_pick)
            place_order(
                user_id=user_id,
                game_id=game_id,
                symbol=stock_pick,
                buy_or_sell="buy",
                cash_balance=cash_balance,
                current_holding=current_holding,
                order_type="market",
                quantity_type="Shares",
                market_price=meli_price,
                amount=order_quantity,
                time_in_force="day"
            )

            original_meli_holding = get_current_stock_holding(user_id, game_id, stock_pick)
            original_miguel_cash = get_current_game_cash_balance(user_id, game_id)
            self.assertEqual(original_meli_holding, order_quantity)
            miguel_cash = STARTING_VIRTUAL_CASH - order_quantity * meli_price
            self.assertAlmostEqual(original_miguel_cash, miguel_cash, 2)

            stock_pick = "NVDA"
            user_id = 3
            order_quantity = 1420
            nvda_limit_ratio = 0.95
            nvda_price = 350
            stop_limit_price = nvda_price * nvda_limit_ratio

            cash_balance = get_current_game_cash_balance(user_id, game_id)
            current_holding = get_current_stock_holding(user_id, game_id, stock_pick)
            place_order(
                user_id=user_id,
                game_id=game_id,
                symbol=stock_pick,
                buy_or_sell="buy",
                cash_balance=cash_balance,
                current_holding=current_holding,
                order_type="limit",
                quantity_type="Shares",
                market_price=nvda_price,
                amount=order_quantity,
                time_in_force="until_cancelled",
                stop_limit_price=stop_limit_price
            )

            updated_holding = get_current_stock_holding(user_id, game_id, stock_pick)
            updated_cash = get_current_game_cash_balance(user_id, game_id)
            self.assertEqual(updated_holding, 0)
            self.assertEqual(updated_cash, STARTING_VIRTUAL_CASH)

        with patch("backend.logic.games.fetch_price") as mock_price_fetch, patch(
                "backend.logic.base.time") as mock_base_time, patch("backend.logic.games.time") as mock_game_time:

            order_clear_price = stop_limit_price - 5

            amzn_stop_ratio = 0.9
            meli_limit_ratio = 1.1
            mock_price_fetch.side_effect = [
                (order_clear_price, None),
                (amzn_stop_ratio * amzn_price - 1, None),
                (meli_limit_ratio * meli_price + 1, None),
            ]
            mock_game_time.time.side_effect = [
                game_start_time + 24 * 60 * 60,  # NVDA order from above
                game_start_time + 24 * 60 * 60,
                game_start_time + 24 * 60 * 60,
                game_start_time + 24 * 60 * 60,
                game_start_time + 24 * 60 * 60,
                game_start_time + 24 * 60 * 60 + 1000,  # AMZN order needs to clear on the same day
                game_start_time + 48 * 60 * 60,  # MELI order is open until being cancelled
                game_start_time + 48 * 60 * 60,
                game_start_time + 48 * 60 * 60,
                game_start_time + 48 * 60 * 60,
                game_start_time + 48 * 60 * 60,
            ]

            mock_base_time.time.side_effect = [
                game_start_time + 24 * 60 * 60,
                game_start_time + 24 * 60 * 60,
                game_start_time + 24 * 60 * 60,
                game_start_time + 24 * 60 * 60 + 1000,
                game_start_time + 24 * 60 * 60 + 1000,
                game_start_time + 24 * 60 * 60 + 1000,
                game_start_time + 48 * 60 * 60,
                game_start_time + 48 * 60 * 60,
                game_start_time + 48 * 60 * 60,
                game_start_time + 48 * 60 * 60,
            ]

            # First let's go ahead and clear that last transaction that we had above
            with self.engine.connect() as conn:
                open_order_id = conn.execute("""
                                             SELECT id 
                                             FROM orders 
                                             WHERE user_id = %s AND game_id = %s AND symbol = %s;""",
                                             user_id, game_id, stock_pick).fetchone()[0]

            process_order(open_order_id)
            updated_holding = get_current_stock_holding(user_id, game_id, stock_pick)
            updated_cash = get_current_game_cash_balance(user_id, game_id)
            self.assertEqual(updated_holding, order_quantity)
            self.assertAlmostEqual(updated_cash, STARTING_VIRTUAL_CASH - order_clear_price * order_quantity, 3)

            # Now let's go ahead and place stop-loss and stop-limit orders against existing positions
            stock_pick = "AMZN"
            user_id = 1
            order_quantity = 250_000

            cash_balance = get_current_game_cash_balance(user_id, game_id)
            current_holding = get_current_stock_holding(user_id, game_id, stock_pick)
            stop_limit_price = amzn_stop_ratio * amzn_price
            place_order(
                user_id=user_id,
                game_id=game_id,
                symbol=stock_pick,
                buy_or_sell="sell",
                cash_balance=cash_balance,
                current_holding=current_holding,
                order_type="stop",
                quantity_type="USD",
                market_price=stop_limit_price + 10,
                amount=order_quantity,
                time_in_force="day",
                stop_limit_price=stop_limit_price
            )

            amzn_sales_entry = query_to_dict("""
                SELECT id, price, quantity
                FROM orders 
                WHERE user_id = %s AND game_id = %s AND symbol = %s
                ORDER BY id DESC LIMIT 0, 1;
            """, user_id, game_id, stock_pick)[0]

            stock_pick = "MELI"
            user_id = 4
            miguel_order_quantity = 300

            cash_balance = get_current_game_cash_balance(user_id, game_id)
            current_holding = get_current_stock_holding(user_id, game_id, stock_pick)
            place_order(
                user_id=user_id,
                game_id=game_id,
                symbol=stock_pick,
                buy_or_sell="sell",
                cash_balance=cash_balance,
                current_holding=current_holding,
                order_type="limit",
                quantity_type="Shares",
                market_price=meli_limit_ratio * meli_price - 10,
                amount=miguel_order_quantity,
                time_in_force="until_cancelled",
                stop_limit_price=meli_limit_ratio * meli_price
            )

            with self.engine.connect() as conn:
                meli_open_order_id = conn.execute("""
                                                  SELECT id 
                                                  FROM orders 
                                                  WHERE user_id = %s AND game_id = %s AND symbol = %s
                                                  ORDER BY id DESC LIMIT 0, 1;""",
                                                  user_id, game_id, stock_pick).fetchone()[0]

            process_order(amzn_sales_entry["id"])
            process_order(meli_open_order_id)

            with self.engine.connect() as conn:
                query = """
                    SELECT o.user_id, o.id, o.symbol, os.clear_price
                    FROM orders o
                    INNER JOIN
                    order_status os
                    ON
                      o.id = os.order_id
                    WHERE
                      os.status = 'fulfilled' AND
                      game_id = %s;
                """
                df = pd.read_sql(query, conn, params=[game_id])

            test_user_id = 1
            test_user_stock = "AMZN"
            updated_holding = get_current_stock_holding(test_user_id, game_id, test_user_stock)
            updated_cash = get_current_game_cash_balance(test_user_id, game_id)
            amzn_clear_price = df[df["id"] == amzn_sales_entry["id"]].iloc[0]["clear_price"]
            # This test is a little bit awkward because of how we are handling floating point for prices and
            # doing integer round here. This may need to become more precise in the future
            self.assertEqual(updated_holding, original_amzn_holding - amzn_sales_entry["quantity"])
            self.assertAlmostEqual(updated_cash,
                                   test_user_original_cash + amzn_sales_entry["quantity"] * amzn_clear_price, 2)

            test_user_id = 4
            test_user_stock = "MELI"
            updated_holding = get_current_stock_holding(test_user_id, game_id, test_user_stock)
            updated_cash = get_current_game_cash_balance(test_user_id, game_id)
            meli_clear_price = df[df["id"] == meli_open_order_id].iloc[0]["clear_price"]
            shares_sold = miguel_order_quantity
            self.assertEqual(updated_holding, original_meli_holding - shares_sold)
            self.assertAlmostEqual(updated_cash, original_miguel_cash + shares_sold * meli_clear_price, 2)

            # if all users leave at the end of a game, that game should shut down
            leave_game(game_id, 1)
            leave_game(game_id, 3)
            leave_game(game_id, 4)

            game_status_entry = query_to_dict(
                "SELECT * FROM game_status WHERE game_id = %s ORDER BY id DESC LIMIT 0, 1;", game_id)[0]
            self.assertEqual(game_status_entry["status"], "cancelled")
            self.assertEqual(json.loads(game_status_entry["users"]), [])
            game_invites_entries = query_to_dict(
                "SELECT * FROM game_invites WHERE game_id = %s ORDER BY id DESC LIMIT 0, 3;", game_id)
            for entry in game_invites_entries:
                self.assertEqual(entry["status"], "left")
Exemple #5
0
    def _start_game_runner(self, start_time, game_id):
        s3_cache.flushall()
        user_statuses = get_user_invite_statuses_for_pending_game(game_id)
        pending_user_usernames = [
            x["username"] for x in user_statuses if x["status"] == "invited"
        ]
        pending_user_ids = get_user_ids(pending_user_usernames)

        # get all user IDs for the game. For this test case everyone is going or accept
        with self.engine.connect() as conn:
            result = conn.execute(
                "SELECT DISTINCT user_id FROM game_invites WHERE game_id = %s",
                game_id).fetchall()
        all_ids = [x[0] for x in result]
        self.user_id = all_ids[0]

        # all users accept their game invite
        with patch("backend.logic.games.time") as game_time_mock, patch(
                "backend.logic.base.time") as base_time_mock:
            game_time_mock.time.return_value = start_time
            time = Mock()
            time.time.return_value = base_time_mock.time.return_value = start_time
            for user_id in pending_user_ids:
                respond_to_game_invite(game_id, user_id, "joined", time.time())

        # check that we have the balances that we expect
        sql = "SELECT balance, user_id from game_balances WHERE game_id = %s;"
        with self.engine.connect() as conn:
            df = pd.read_sql(sql, conn, params=[game_id])
        self.assertTrue(df.shape, (0, 2))

        sql = "SELECT balance, user_id from game_balances WHERE game_id = %s;"
        with self.engine.connect() as conn:
            df = pd.read_sql(sql, conn, params=[game_id])
        self.assertTrue(df.shape, (4, 2))

        # a couple things should have just happened here. We expect to have the following assets available to us
        # now in our redis cache: (1) an empty open orders table for each user, (2) an empty current balances table for
        # each user, (3) an empty field chart for each user, (4) an empty field chart, and (5) an initial game stats
        # list
        cache_keys = s3_cache.keys()
        self.assertIn(f"{game_id}/{LEADERBOARD_PREFIX}", cache_keys)
        self.assertIn(f"{game_id}/{FIELD_CHART_PREFIX}", cache_keys)
        for user_id in all_ids:
            self.assertIn(f"{game_id}/{user_id}/{CURRENT_BALANCES_PREFIX}",
                          cache_keys)
            self.assertIn(f"{game_id}/{user_id}/{PENDING_ORDERS_PREFIX}",
                          cache_keys)
            self.assertIn(f"{game_id}/{user_id}/{FULFILLED_ORDER_PREFIX}",
                          cache_keys)
            self.assertIn(f"{game_id}/{user_id}/{BALANCES_CHART_PREFIX}",
                          cache_keys)
            self.assertIn(f"{game_id}/{user_id}/{ORDER_PERF_CHART_PREFIX}",
                          cache_keys)

        # quickly verify the structure of the chart assets. They should be blank, with transparent colors
        field_chart = s3_cache.unpack_s3_json(
            f"{game_id}/{FIELD_CHART_PREFIX}")
        self.assertEqual(len(field_chart["datasets"]), len(all_ids))
        chart_colors = [x["backgroundColor"] for x in field_chart["datasets"]]
        self.assertTrue(all([x == NULL_RGBA for x in chart_colors]))

        leaderboard = s3_cache.unpack_s3_json(
            f"{game_id}/{LEADERBOARD_PREFIX}")
        self.assertEqual(len(leaderboard["records"]), len(all_ids))
        self.assertTrue(
            all([
                x["cash_balance"] == STARTING_VIRTUAL_CASH
                for x in leaderboard["records"]
            ]))

        a_current_balance_table = s3_cache.unpack_s3_json(
            f"{game_id}/{self.user_id}/{CURRENT_BALANCES_PREFIX}")
        self.assertEqual(a_current_balance_table["data"], [])

        an_open_orders_table = s3_cache.unpack_s3_json(
            f"{game_id}/{self.user_id}/{PENDING_ORDERS_PREFIX}")
        self.assertEqual(an_open_orders_table["data"], [])
        a_fulfilled_orders_table = s3_cache.unpack_s3_json(
            f"{game_id}/{self.user_id}/{FULFILLED_ORDER_PREFIX}")
        self.assertEqual(a_fulfilled_orders_table["data"], [])

        a_balances_chart = s3_cache.unpack_s3_json(
            f"{game_id}/{self.user_id}/{BALANCES_CHART_PREFIX}")
        self.assertEqual(len(a_balances_chart["datasets"]), 1)
        self.assertEqual(a_balances_chart["datasets"][0]["label"], "Cash")
        self.assertEqual(a_balances_chart["datasets"][0]["backgroundColor"],
                         NULL_RGBA)

        # now have a user put an order. It should go straight to the queue and be reflected in the open orders table,
        # but they should not have any impact on the user's balances if the order is placed outside of trading day
        self.stock_pick = "TSLA"
        self.market_price = 1_000
        with patch("backend.logic.games.time") as game_time_mock, patch(
                "backend.logic.base.time") as base_time_mock:
            game_time_mock.time.side_effect = [start_time + 1] * 2
            base_time_mock.time.return_value = start_time + 1
            stock_pick = self.stock_pick
            cash_balance = get_current_game_cash_balance(self.user_id, game_id)
            current_holding = get_current_stock_holding(
                self.user_id, game_id, stock_pick)
            order_id = place_order(user_id=self.user_id,
                                   game_id=game_id,
                                   symbol=self.stock_pick,
                                   buy_or_sell="buy",
                                   cash_balance=cash_balance,
                                   current_holding=current_holding,
                                   order_type="market",
                                   quantity_type="Shares",
                                   market_price=self.market_price,
                                   amount=1,
                                   time_in_force="day")
            serialize_and_pack_pending_orders(game_id, self.user_id)
            add_fulfilled_order_entry(game_id, self.user_id, order_id)
            serialize_and_pack_portfolio_details(game_id, self.user_id)