Ejemplo n.º 1
0
def make_order_performance_chart_with_context(**context):
    game_id, start_time, end_time = context_parser(context, "game_id", "start_time", "end_time")
    start_time, end_time = get_time_defaults(game_id, start_time, end_time)
    user_ids = get_active_game_user_ids(game_id)
    for user_id in user_ids:
        print(f"*** user id: {user_id} ***")
        serialize_and_pack_order_performance_assets(game_id, user_id, start_time, end_time)
Ejemplo n.º 2
0
def make_the_field_charts(game_id: int, start_time: float = None, end_time: float = None):
    """This function wraps a loop that produces the balances chart for each user and the field chart for the game. This
    will run every time a user places and order, and periodically as prices are collected
    """
    user_ids = get_active_game_user_ids(game_id)
    portfolios = []
    portfolio_table_keys = list(portfolio_comps_schema.keys())
    for user_id in user_ids:
        df = make_user_balances_chart_data(game_id, user_id, start_time, end_time)
        serialize_and_pack_balances_chart(df, game_id, user_id)
        portfolio = aggregate_portfolio_value(df)
        portfolio["username"] = get_usernames([user_id])[0]
        apply_validation(portfolio, portfolio_comps_schema)
        portfolios.append(portfolio[portfolio_table_keys])

    # add index data
    if check_single_player_mode(game_id):
        for index in TRACKED_INDEXES:
            df = get_index_portfolio_value_data(game_id, index, start_time, end_time)
            df["timestamp"] = df["timestamp"].apply(lambda x: posix_to_datetime(x))
            df = add_time_labels(df)
            df = df.groupby("t_index", as_index=False).agg(
                {"username": "******", "label": "last", "value": "last", "timestamp": "last"})
            apply_validation(df, portfolio_comps_schema)
            portfolios.append(df[portfolio_table_keys])

    portfolios_df = pd.concat(portfolios)
    relabelled_df = relabel_aggregated_portfolios(portfolios_df)
    relabelled_df.sort_values("timestamp", inplace=True)
    serialize_and_pack_portfolio_comps_chart(relabelled_df, game_id)
Ejemplo n.º 3
0
def close_finished_game_with_context(**context):
    game_id = context_parser(context, "game_id")[0]
    _, game_end = get_game_start_and_end(game_id)
    current_time = time.time()
    if current_time >= game_end:
        user_ids = get_active_game_user_ids(game_id)
        last_status_entry = query_to_dict("""SELECT * FROM game_status 
                                             WHERE game_id = %s ORDER BY id DESC LIMIT 0, 1""", game_id)[0]
        if last_status_entry["status"] == "active":
            # close game and update ratings
            add_row("game_status", game_id=game_id, status="finished", users=user_ids, timestamp=current_time)
            update_ratings(game_id)
Ejemplo n.º 4
0
def calculate_and_pack_game_metrics(game_id: int, start_time: float = None, end_time: float = None):
    for user_id in get_active_game_user_ids(game_id):
        return_ratio, sharpe_ratio = calculate_metrics(game_id, user_id, start_time, end_time)
        rds.set(f"{RETURN_RATIO_PREFIX}_{game_id}_{user_id}", return_ratio)
        rds.set(f"{SHARPE_RATIO_PREFIX}_{game_id}_{user_id}", sharpe_ratio)

    if check_single_player_mode(game_id):
        for index in TRACKED_INDEXES:
            df = get_index_portfolio_value_data(game_id, index, start_time, end_time)
            index_return_ratio = portfolio_return_ratio(df)
            index_sharpe_ratio = portfolio_sharpe_ratio(df, RISK_FREE_RATE_DEFAULT)
            rds.set(f"{RETURN_RATIO_PREFIX}_{game_id}_{index}", index_return_ratio)
            rds.set(f"{SHARPE_RATIO_PREFIX}_{game_id}_{index}", index_sharpe_ratio)
Ejemplo n.º 5
0
def get_winner(game_id: int, start_time: float, end_time: float, benchmark: str):
    assert benchmark in ["return_ratio", "sharpe_ratio"]
    ids_and_scores = []

    user_ids = get_active_game_user_ids(game_id)
    for user_id in user_ids:
        return_ratio, sharpe_ratio = calculate_metrics(game_id, user_id, start_time, end_time)
        metric = return_ratio
        if benchmark == "sharpe_ratio":
            metric = sharpe_ratio
        ids_and_scores.append((user_id, metric))

    max_score = max([x[1] for x in ids_and_scores])
    return [x for x in ids_and_scores if x[1] == max_score][0]  # TODO: handle ties (mathematically unlikely)
Ejemplo n.º 6
0
def init_game_assets(game_id: int):
    calculate_and_pack_game_metrics(game_id)

    # leaderboard
    compile_and_pack_player_leaderboard(game_id)

    # the field and balance charts
    make_the_field_charts(game_id)

    # tables and performance breakout charts
    user_ids = get_active_game_user_ids(game_id)
    for user_id in user_ids:
        # game/user-level assets
        serialize_and_pack_pending_orders(game_id, user_id)
        serialize_and_pack_order_performance_assets(game_id, user_id)
        serialize_and_pack_portfolio_details(game_id, user_id)

    if not check_single_player_mode(game_id):
        # winners/payouts table
        update_performed = log_winners(game_id, time.time())
        if update_performed:
            serialize_and_pack_winners_table(game_id)
Ejemplo n.º 7
0
def leave_game(game_id: int, user_id: int):
    """Users in non-paid games can leave at any time
    """
    current_time = time.time()
    add_row("game_invites",
            game_id=game_id,
            user_id=user_id,
            status="left",
            timestamp=current_time)
    current_game_users = get_active_game_user_ids(game_id)
    remaining_game_users = [x for x in current_game_users if x != user_id]
    if not remaining_game_users:
        add_row("game_status",
                game_id=game_id,
                status="cancelled",
                users=[],
                timestamp=current_time)
        return
    add_row("game_status",
            game_id=game_id,
            status="active",
            users=remaining_game_users,
            timestamp=current_time)
Ejemplo n.º 8
0
    def test_visuals_with_data(self, mock_base_time, mock_game_time):
        mock_base_time.time.return_value = mock_game_time.time.return_value = simulation_end_time
        game_id = 3
        user_id = 1
        serialize_and_pack_pending_orders(game_id, user_id)
        pending_order_table = s3_cache.unpack_s3_json(
            f"{game_id}/{user_id}/{PENDING_ORDERS_PREFIX}")
        self.assertNotIn("order_id", pending_order_table["headers"])
        self.assertEqual(len(pending_order_table["headers"]), 9)

        user_ids = get_active_game_user_ids(game_id)
        for player_id in user_ids:
            serialize_and_pack_order_performance_assets(game_id, player_id)

        op_chart_3_1 = s3_cache.unpack_s3_json(
            f"{game_id}/{user_id}/{ORDER_PERF_CHART_PREFIX}")
        chart_stocks = set(
            [x["label"].split("/")[0] for x in op_chart_3_1["datasets"]])
        expected_stocks = {"AMZN", "TSLA", "LYFT", "SPXU", "NVDA"}
        self.assertEqual(chart_stocks, expected_stocks)
        for user_id in user_ids:
            self.assertIn(f"{game_id}/{user_id}/{ORDER_PERF_CHART_PREFIX}",
                          s3_cache.keys())
Ejemplo n.º 9
0
def compile_and_pack_player_leaderboard(game_id: int, start_time: float = None, end_time: float = None):
    user_ids = get_active_game_user_ids(game_id)
    usernames = get_game_users(game_id)
    user_colors = assign_colors(usernames)
    records = []
    for user_id in user_ids:
        user_info = get_user_information(user_id)  # this is where username and profile pic get added in
        cash_balance = get_current_game_cash_balance(user_id, game_id)
        balances = get_active_balances(game_id, user_id)
        stocks_held = list(balances["symbol"].unique())
        portfolio_value = get_user_portfolio_value(game_id, user_id)
        stat_info = make_stat_entry(color=user_colors[user_info["username"]],
                                    cash_balance=cash_balance,
                                    portfolio_value=portfolio_value,
                                    stocks_held=stocks_held,
                                    return_ratio=rds.get(f"{RETURN_RATIO_PREFIX}_{game_id}_{user_id}"),
                                    sharpe_ratio=rds.get(f"{SHARPE_RATIO_PREFIX}_{game_id}_{user_id}"))
        records.append({**user_info, **stat_info})

    if check_single_player_mode(game_id):
        for index in TRACKED_INDEXES:
            index_info = query_to_dict("""
                SELECT name as username, avatar AS profile_pic 
                FROM index_metadata WHERE symbol = %s""", index)[0]
            portfolio_value = get_index_portfolio_value(game_id, index, start_time, end_time)
            stat_info = make_stat_entry(color=user_colors[index_info["username"]],
                                        cash_balance=None,
                                        portfolio_value=portfolio_value,
                                        stocks_held=[],
                                        return_ratio=rds.get(f"{RETURN_RATIO_PREFIX}_{game_id}_{index}"),
                                        sharpe_ratio=rds.get(f"{SHARPE_RATIO_PREFIX}_{game_id}_{index}"))
            records.append({**index_info, **stat_info})

    benchmark = get_game_info(game_id)["benchmark"]  # get game benchmark and use it to sort leaderboard
    records = sorted(records, key=lambda x: -x[benchmark])
    output = dict(days_left=_days_left(game_id), records=records)
    s3_cache.set(f"{game_id}/{LEADERBOARD_PREFIX}", json.dumps(output))
Ejemplo n.º 10
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)
Ejemplo n.º 11
0
def refresh_portfolio_details_with_context(**context):
    game_id, = context_parser(context, "game_id")
    user_ids = get_active_game_user_ids(game_id)
    for user_id in user_ids:
        print(f"*** user id: {user_id} ***")
        serialize_and_pack_portfolio_details(game_id, user_id)
Ejemplo n.º 12
0
from argparse import ArgumentParser

import pandas as pd
from backend.database.db import (db, engine)
from backend.database.helpers import drop_all_tables
from backend.logic.base import (get_active_game_user_ids,
                                get_game_start_and_end, SECONDS_IN_A_DAY)

parser = ArgumentParser()
parser.add_argument("--game_id", type=int)
args = parser.parse_args()

if __name__ == '__main__':
    if os.getenv("ENV") == "prod":
        with engine.connect() as conn:
            user_ids = get_active_game_user_ids(args.game_id)
            min_time, max_time = get_game_start_and_end(args.game_id)

            games = pd.read_sql("SELECT * FROM games WHERE id = %s;",
                                conn,
                                params=[args.game_id])
            game_status = pd.read_sql(
                "SELECT * FROM game_status WHERE game_id = %s;",
                conn,
                params=[args.game_id])
            game_balances = pd.read_sql(f"""
                SELECT * FROM game_balances 
                WHERE game_id = %s AND user_id IN ({", ".join(["%s"] * len(user_ids))});
                """,
                                        conn,
                                        params=[args.game_id] + list(user_ids))
Ejemplo n.º 13
0
    def test_winner_payouts(self):
        """Use canonical game #3 to simulate a series of winner calculations on the test data. Note that since we only
        have a week of test data, we'll effectively recycle the same information via mocks
        """
        game_id = 3
        user_ids = get_active_game_user_ids(game_id)
        self.assertEqual(user_ids, [1, 3, 4])
        game_info = get_game_info(game_id)

        n_players = len(user_ids)
        pot_size = n_players * game_info["buy_in"]
        self.assertEqual(pot_size, 300)

        last_payout_date = get_last_sidebet_payout(game_id)
        self.assertIsNone(last_payout_date)

        offset = make_date_offset(game_info["side_bets_period"])
        self.assertEqual(offset, DateOffset(days=7))

        start_time = game_info["start_time"]
        end_time = game_info["end_time"]
        self.assertEqual(start_time, simulation_start_time)

        n_sidebets = n_sidebets_in_game(start_time, end_time, offset)
        self.assertEqual(n_sidebets, 2)

        # we'll mock in daily portfolio values to speed up the time this test takes
        user_1_portfolio = portfolio_value_by_day(game_id, 1, start_time,
                                                  end_time)
        user_3_portfolio = portfolio_value_by_day(game_id, 3, start_time,
                                                  end_time)
        user_4_portfolio = portfolio_value_by_day(game_id, 4, start_time,
                                                  end_time)

        # expected sidebet dates
        start_dt = posix_to_datetime(start_time)
        end_dt = posix_to_datetime(end_time)
        sidebet_dates = get_expected_sidebets_payout_dates(
            start_dt, end_dt, game_info["side_bets_perc"], offset)
        sidebet_dates_posix = [datetime_to_posix(x) for x in sidebet_dates]

        with patch("backend.logic.metrics.portfolio_value_by_day"
                   ) as portfolio_mocks, patch(
                       "backend.logic.base.time") as base_time_mock:
            time = Mock()
            time_1 = datetime_to_posix(posix_to_datetime(start_time) + offset)
            time_2 = end_time

            time.time.side_effect = base_time_mock.time.side_effect = [
                time_1, time_2
            ]
            portfolio_mocks.side_effect = [
                user_1_portfolio, user_3_portfolio, user_4_portfolio
            ] * 4

            winner_id, score = get_winner(game_id, start_time, end_time,
                                          game_info["benchmark"])
            log_winners(game_id, time.time())
            sidebet_entry = query_to_dict(
                "SELECT * FROM winners WHERE id = 1;")[0]
            self.assertEqual(sidebet_entry["winner_id"], winner_id)
            self.assertAlmostEqual(sidebet_entry["score"], score, 4)
            side_pot = pot_size * (game_info["side_bets_perc"] /
                                   100) / n_sidebets
            self.assertEqual(sidebet_entry["payout"],
                             side_pot * PERCENT_TO_USER)
            self.assertEqual(sidebet_entry["type"], "sidebet")
            self.assertEqual(sidebet_entry["start_time"], start_time)
            self.assertEqual(sidebet_entry["end_time"], sidebet_dates_posix[0])
            self.assertEqual(sidebet_entry["timestamp"], time_1)

            log_winners(game_id, time.time())
            sidebet_entry = query_to_dict(
                "SELECT * FROM winners WHERE id = 2;")[0]
            self.assertEqual(sidebet_entry["winner_id"], winner_id)
            self.assertAlmostEqual(sidebet_entry["score"], score, 4)
            self.assertEqual(sidebet_entry["payout"],
                             side_pot * PERCENT_TO_USER)
            self.assertEqual(sidebet_entry["type"], "sidebet")
            self.assertEqual(sidebet_entry["start_time"],
                             sidebet_dates_posix[0])
            self.assertEqual(sidebet_entry["end_time"], sidebet_dates_posix[1])
            self.assertEqual(sidebet_entry["timestamp"], time_2)

            overall_entry = query_to_dict(
                "SELECT * FROM winners WHERE id = 3;")[0]
            final_payout = pot_size * (1 - game_info["side_bets_perc"] / 100)
            self.assertEqual(overall_entry["payout"],
                             final_payout * PERCENT_TO_USER)
            with self.engine.connect() as conn:
                df = pd.read_sql("SELECT * FROM winners", conn)
            self.assertEqual(df.shape, (3, 10))

        serialize_and_pack_winners_table(game_id)
        payouts_table = s3_cache.unpack_s3_json(f"{game_id}/{PAYOUTS_PREFIX}")
        self.assertEqual(len(payouts_table["data"]), 3)
        self.assertTrue(
            sum([x["Type"] == "Sidebet" for x in payouts_table["data"]]), 2)
        self.assertTrue(
            sum([x["Type"] == "Overall" for x in payouts_table["data"]]), 1)
        for entry in payouts_table["data"]:
            if entry["Type"] == "Sidebet":
                self.assertEqual(entry["Payout"], side_pot * PERCENT_TO_USER)
            else:
                self.assertEqual(entry["Payout"],
                                 final_payout * PERCENT_TO_USER)
Ejemplo n.º 14
0
def update_ratings(game_id: int):
    start, end = get_game_start_and_end(game_id)
    user_ids = get_active_game_user_ids(game_id)

    # construct the leaderboard, including indexes. use simple return for now
    scoreboard = {}
    for user_id in user_ids:
        ending_value = get_user_portfolio_value(game_id, user_id, end)
        scoreboard[user_id] = ending_value / STARTING_VIRTUAL_CASH - 1

    for index in TRACKED_INDEXES:
        scoreboard[index] = get_index_portfolio_value(game_id, index, start, end) / STARTING_VIRTUAL_CASH - 1

    # now that we have the scoreboard, iterate over its entries to construct and update DF
    update_array = []
    for player, player_return in scoreboard.items():
        other_players = {k: v for k, v in scoreboard.items() if k != player}
        for other_player, other_player_return in other_players.items():
            player_entry = get_rating_info(player)
            other_player_entry = get_rating_info(other_player)
            player_rating = player_entry["rating"]
            other_player_rating = other_player_entry["rating"]

            # fields for return
            total_return = player_entry["total_return"]
            n_games = player_entry["n_games"]

            score = 1 if player_return > other_player_return else 0
            if player_return == other_player_return:
                score = 0.5

            update_array.append(dict(
                player_id=player,
                player_rating=player_rating,
                expected=expected_elo(player_rating, other_player_rating),
                score=score,
                total_return=total_return,
                n_games=n_games
            ))

    update_df = pd.DataFrame(update_array)
    summary_df = update_df.groupby("player_id", as_index=False).agg({
        "player_rating": "first",
        "expected": "sum",
        "score": "sum",
        "total_return": "first",
        "n_games": "first"
    })
    for _, row in summary_df.iterrows():
        player_id = row["player_id"]
        new_rating = elo_update(row["player_rating"], row["expected"], row["score"])

        # update basis and return stats
        game_basis = STARTING_VIRTUAL_CASH
        game_return = scoreboard[player_id]
        add_row("stockbets_rating",
                user_id=int(player_id) if player_id in user_ids else None,
                index_symbol=player_id if player_id in TRACKED_INDEXES else None,
                game_id=game_id,
                basis=float(game_basis),
                total_return=float(game_return),
                n_games=int(row["n_games"] + 1),
                rating=float(new_rating),
                update_type="game_end",
                timestamp=end)
Ejemplo n.º 15
0
def get_pot_size(game_id: int):
    game_info = get_game_info(game_id)
    player_ids = get_active_game_user_ids(game_id)
    n_players = len(player_ids)
    return n_players * game_info["buy_in"]