Esempio n. 1
0
def n_sidebets_in_game(game_start: float, game_end: float, offset: DateOffset) -> int:
    game_start = posix_to_datetime(game_start)
    game_end = posix_to_datetime(game_end)
    count = 0
    t = game_start + offset
    while t <= game_end:
        count += 1
        t += offset
    return count
    def test_time_handlers(self):
        posix_time = 1590165714.1528566
        actual_date = dt(2020, 5, 22, 12, 41, 54, 152857)
        localizer = pytz.timezone(TIMEZONE)
        localized_date = localizer.localize(actual_date)
        self.assertEqual(posix_to_datetime(posix_time), localized_date)
        self.assertAlmostEqual(posix_time, datetime_to_posix(localized_date),
                               0)

        mexico_date = actual_date.replace(hour=11)
        localizer = pytz.timezone("America/Mexico_City")
        localized_date = localizer.localize(mexico_date)
        self.assertAlmostEqual(posix_time, datetime_to_posix(localized_date),
                               0)

        # Pre-stage all of the mocked current time values that will be called sequentially in the tests below.
        # ----------------------------------------------------------------------------------------------------
        with patch('backend.logic.base.time') as current_time_mock:
            # Check during trading day just one second before and after open/close
            schedule = get_trading_calendar(actual_date, actual_date)
            start_day, end_day = [
                datetime_to_posix(x)
                for x in schedule.iloc[0][["market_open", "market_close"]]
            ]

            current_time_mock.time.side_effect = [
                posix_time,  # during trading day
                posix_time + 8 * 60 * 60,  # 8 hours later--after trading day
                1608908400,  # Christmas 2020, Friday, 11am in NYC. We want to verify that we're accounting for holidays
                start_day - 1,  # one second before trading day
                (start_day + end_day) /
                2,  # right in the middle of trading day
                end_day + 1  # one second after trading day
            ]

            self.assertTrue(during_trading_day())
            self.assertFalse(during_trading_day())
            self.assertFalse(during_trading_day())
            self.assertFalse(during_trading_day())
            self.assertTrue(during_trading_day())
            self.assertFalse(during_trading_day())

        # Finally, just double-check that the real-time, default invocation works as expected
        posix_now = time.time()
        nyc_now = posix_to_datetime(posix_now)
        schedule = get_trading_calendar(nyc_now, nyc_now)
        during_trading = False
        if not schedule.empty:
            start_day, end_day = [
                datetime_to_posix(x)
                for x in schedule.iloc[0][["market_open", "market_close"]]
            ]
            during_trading = start_day <= posix_now <= end_day

        # FYI: there is a non-zero chance that this test will fail at exactly the beginning or end of a trading day
        self.assertEqual(during_trading, during_trading_day())
Esempio n. 3
0
def get_winners_meta_data(game_id: int):
    game_info = query_to_dict("SELECT * FROM games WHERE id = %s", game_id)[0]
    side_bets_perc = game_info.get("side_bets_perc")
    benchmark = game_info["benchmark"]
    stakes = game_info["stakes"]
    game_start, game_end = get_game_start_and_end(game_id)
    offset = make_date_offset(game_info["side_bets_period"])
    start_dt = posix_to_datetime(game_start)
    end_dt = posix_to_datetime(game_end)
    return game_start, game_end, start_dt, end_dt, benchmark, side_bets_perc, stakes, offset
Esempio n. 4
0
def make_orders_per_active_user():
    # get total number of active users on a given day (in at least one active game, or in a live single player mode)
    with engine.connect() as conn:
        game_status = pd.read_sql(
            """
            SELECT game_id, users, timestamp
            FROM game_status WHERE status NOT IN ('expired', 'pending')""",
            conn)
        order_status = pd.read_sql(
            "SELECT id, timestamp FROM order_status WHERE status = 'pending';",
            conn)

    game_status["timestamp"] = game_status["timestamp"].apply(
        lambda x: posix_to_datetime(x))
    game_status["users"] = game_status["users"].apply(lambda x: json.loads(x))
    game_status["user_count"] = game_status["users"].apply(lambda x: len(x))

    # get total number of active users per day
    complete_counts_array = []
    for game_id in game_status["game_id"].unique():
        game_subset = game_status[game_status["game_id"] == game_id]
        if game_subset.shape == 2:
            complete_counts_array += game_subset[["user_count", "timestamp"
                                                  ]].to_dict(orient="records")
            continue
        user_count = game_subset.iloc[0]["user_count"]
        original_entry = dict(user_count=user_count,
                              timestamp=game_subset.iloc[0]["timestamp"],
                              game_id=game_id)
        bookend = dict(user_count=user_count,
                       timestamp=posix_to_datetime(time.time()),
                       game_id=game_id)
        complete_counts_array += [original_entry, bookend]

    user_count_df = pd.DataFrame(complete_counts_array)
    expanded_df = user_count_df.groupby("game_id").apply(expand_counts)
    expanded_df.index = expanded_df.index.droplevel(0)
    daily_active_users = expanded_df.resample("D").sum()

    order_status["timestamp"] = order_status["timestamp"].apply(
        lambda x: posix_to_datetime(x))
    order_status.set_index("timestamp", inplace=True)
    order_totals = order_status.resample("D").count()

    df = pd.concat([daily_active_users, order_totals], axis=1)
    df["orders_per_users"] = df["id"] / df["user_count"]
    df.fillna(0, inplace=True)
    df = df.reset_index()
    df["timestamp"] = df["timestamp"].apply(
        lambda x: datetime_to_posix(x)).astype(float)
    return df
Esempio n. 5
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)
Esempio n. 6
0
def serialize_and_pack_order_performance_chart(df: pd.DataFrame, game_id: int, user_id: int):
    if df.empty:
        chart_json = make_null_chart("Waiting for orders...")
    else:
        apply_validation(df, order_performance_schema, True)
        plot_df = add_bookends(df, group_var="order_label", condition_var="fifo_balance")
        plot_df["cum_pl"] = plot_df.groupby("order_label")["realized_pl"].cumsum()
        plot_df["timestamp"] = plot_df["timestamp"].apply(lambda x: posix_to_datetime(x))
        plot_df.set_index("timestamp", inplace=True)
        plot_df = plot_df.groupby("order_label", as_index=False).resample(f"{RESAMPLING_INTERVAL}T").last().ffill()
        plot_df = plot_df.reset_index(level=1)
        plot_df = filter_for_trade_time(plot_df)
        plot_df = append_price_data_to_balance_histories(plot_df)
        plot_df.sort_values(["order_label", "timestamp"], inplace=True)
        plot_df = add_time_labels(plot_df)
        plot_df = plot_df.groupby(["order_label", "t_index"], as_index=False).agg("last")
        plot_df["label"] = plot_df["timestamp"].apply(lambda x: datetime_to_posix(x)).astype(float)
        plot_df.sort_values("timestamp", inplace=True)
        plot_df["total_pl"] = plot_df["cum_pl"] + plot_df["fifo_balance"] * plot_df["price"] - (
                1 - plot_df["total_pct_sold"]) * plot_df["basis"]
        plot_df["return"] = 100 * plot_df["total_pl"] / plot_df["basis"]
        label_colors = assign_colors(plot_df["order_label"].unique())
        plot_df["color"] = plot_df["order_label"].apply(lambda x: label_colors.get(x))
        chart_json = make_chart_json(plot_df, "order_label", "return", "label", colors=plot_df["color"].unique())
    s3_cache.set(f"{game_id}/{user_id}/{ORDER_PERF_CHART_PREFIX}", json.dumps(chart_json))
 def test_price_fetchers(self):
     symbol = "AMZN"
     amzn_price, updated_at = fetch_price(symbol)
     self.assertIsNotNone(amzn_price)
     self.assertTrue(amzn_price > 0)
     self.assertTrue(
         posix_to_datetime(updated_at) > dt(2000, 1, 1).replace(
             tzinfo=pytz.utc))
Esempio n. 8
0
def make_user_cohorts(users: pd.DataFrame) -> pd.DataFrame:
    users["created_at"] = users["created_at"].apply(
        lambda x: posix_to_datetime(x))
    users["period"] = users["created_at"].dt.to_period("M")
    label_df = users.groupby("period", as_index=False)["id"].count()
    label_df["cohort"] = label_df["period"].astype(
        str) + " [" + label_df["id"].astype(str) + "]"
    return users.merge(label_df[["period", "cohort"]])
Esempio n. 9
0
def serialize_and_pack_winners_table(game_id: int):
    """this function serializes the winners data that has already been saved to DB and fills in any missing rows."""
    game_start, game_end, start_dt, end_dt, benchmark, side_bets_perc, stakes, offset = get_winners_meta_data(game_id)

    # pull winners data from DB
    with engine.connect() as conn:
        winners_df = pd.read_sql("SELECT * FROM winners WHERE game_id = %s ORDER BY id", conn, params=[game_id])

    # Where are we at in the current game?
    game_finished = False
    if winners_df.empty:
        last_observed_win = start_dt
    else:
        last_observed_win = posix_to_datetime(winners_df["timestamp"].max())
        if "overall" in winners_df["type"].to_list():
            game_finished = True

    data = []
    if side_bets_perc:
        payout = get_sidebet_payout(game_id, side_bets_perc, offset, stakes)
        expected_sidebet_dates = get_expected_sidebets_payout_dates(start_dt, end_dt, side_bets_perc, offset)
        for _, row in winners_df.iterrows():
            if row["type"] == "sidebet":
                winner = get_usernames([row["winner_id"]])
                data.append(
                    make_payout_table_entry(posix_to_datetime(row["start_time"]), posix_to_datetime(row["end_time"]),
                                            winner, payout, "Sidebet", benchmark, row["score"]))

        dates_to_fill_in = [x for x in expected_sidebet_dates if x > last_observed_win]
        last_date = last_observed_win
        for payout_date in dates_to_fill_in:
            data.append(make_payout_table_entry(last_date, payout_date, "???", payout, "Sidebet"))
            last_date = payout_date

    payout = get_overall_payout(game_id, side_bets_perc, stakes)
    if not game_finished:
        final_entry = make_payout_table_entry(start_dt, end_dt, "???", payout, "Overall")
    else:
        winner_row = winners_df.loc[winners_df["type"] == "overall"].iloc[0]
        winner = get_usernames([int(winner_row["winner_id"])])[0]
        final_entry = make_payout_table_entry(start_dt, end_dt, winner, payout, "Overall", benchmark,
                                              winner_row["score"])

    data.append(final_entry)
    out_dict = dict(data=data, headers=list(data[0].keys()))
    s3_cache.set(f"{game_id}/{PAYOUTS_PREFIX}", json.dumps(out_dict))
Esempio n. 10
0
def get_beginning_of_next_trading_day(ref_time):
    ref_day = posix_to_datetime(ref_time).date()
    schedule = get_trading_calendar(ref_day, ref_day)
    while schedule.empty:
        ref_day += timedelta(days=1)
        schedule = get_trading_calendar(ref_day, ref_day)
    start_day, _ = get_schedule_start_and_end(schedule)
    return start_day
 def test_schedule_handlers(self):
     """These test functions that live in logic.stock_data, but are used when processing orders during game play
     """
     sat_may_23_2020 = dt(2020, 5, 23)
     next_trading_schedule = get_next_trading_day_schedule(sat_may_23_2020)
     start_and_end = get_schedule_start_and_end(next_trading_schedule)
     start_day, end_day = [posix_to_datetime(x) for x in start_and_end]
     localizer = pytz.timezone(TIMEZONE)
     expected_start = localizer.localize(dt(2020, 5, 26, 9, 30))
     expected_end = localizer.localize(dt(2020, 5, 26, 16, 0))
     self.assertEqual(start_day, expected_start)
     self.assertEqual(end_day, expected_end)
Esempio n. 12
0
    def test_trade_time_index(self):
        with self.engine.connect() as conn:
            prices = pd.read_sql("SELECT * FROM prices;", conn)

        prices["timestamp"] = prices["timestamp"].apply(
            lambda x: posix_to_datetime(x))
        with self.assertRaises(Exception):
            prices["t_index"] = trade_time_index(prices["timestamp"])

        prices.sort_values("timestamp", inplace=True)
        prices["t_index"] = trade_time_index(prices["timestamp"])
        self.assertEqual(prices["t_index"].nunique(), N_PLOT_POINTS)
Esempio n. 13
0
def make_null_chart(null_label: str):
    """Null chart function for when a game has just barely gotten going / has started after hours and there's no data.
    For now this function is a bit unnecessary, but the idea here is to be really explicit about what's happening so
    that we can add other attributes later if need be.
    """
    schedule = get_next_trading_day_schedule(dt.utcnow())
    start, end = [posix_to_datetime(x) for x in get_schedule_start_and_end(schedule)]
    labels = [datetime_to_posix(t) for t in pd.date_range(start, end, N_PLOT_POINTS)]
    data = [STARTING_VIRTUAL_CASH for _ in labels]
    return dict(labels=labels,
                datasets=[
                    dict(label=null_label, data=data, borderColor=NULL_RGBA, backgroundColor=NULL_RGBA, fill=False)])
Esempio n. 14
0
    def test_task_caching(self):
        rds.flushall()
        test_time = posix_to_datetime(time.time()).date()
        start = time.time()
        _ = get_trading_calendar(test_time, test_time)
        time1 = time.time() - start

        start = time.time()
        _ = get_trading_calendar(test_time, test_time)
        time2 = time.time() - start

        self.assertLess(time2, time1 / 2)  # "2" is a hueristic for 'substantial performance improvement'
        self.assertIn("rc:get_trading_calendar", rds.keys()[0])
Esempio n. 15
0
def check_if_payout_time(current_time: float, payout_time: float) -> bool:
    if current_time >= payout_time:
        return True

    if during_trading_day() and current_time < payout_time:
        return False

    next_day_schedule = get_next_trading_day_schedule(posix_to_datetime(current_time) + timedelta(days=1))
    next_trade_day_start, _ = get_schedule_start_and_end(next_day_schedule)
    if next_trade_day_start > payout_time:
        return True

    return False
Esempio n. 16
0
    def test_metrics(self):
        """The canonical game #3 has 5 days worth of stock data in it. We'll use that data here to test canonical values
        for the game winning metrics
        """
        game_id = 3
        user_id = 1
        game_info = get_game_info(game_id)
        offset = make_date_offset(game_info["side_bets_period"])
        with freeze_time(
                posix_to_datetime(simulation_start_time - 60) + offset):
            return_ratio, sharpe_ratio = calculate_metrics(
                game_id, user_id, simulation_start_time, time.time())

        self.assertAlmostEqual(return_ratio, -0.6133719, 4)
        self.assertAlmostEqual(sharpe_ratio, -0.5495623, 4)
Esempio n. 17
0
def parse_nasdaq_splits(df: pd.DataFrame):
    num_cols = ["numerator", "denominator"]
    current_datetime = posix_to_datetime(
        time.time(), timezone="UTC")  # because selenium defaults to UTC
    current_date = current_datetime.date()
    df["executionDate"] = pd.to_datetime(df["executionDate"])
    df = df[df["executionDate"].dt.date == current_date]
    df = df[df["ratio"].str.contains(
        ":"
    )]  # sometimes the calendar encodes splits as %. We don't handle this for now
    if not df.empty:
        df[num_cols] = df["ratio"].str.split(" : ", expand=True)
        df[num_cols] = df[num_cols].apply(pd.to_numeric)
        df["exec_date"] = get_day_start(current_datetime)
        df = df[num_cols + ["symbol", "exec_date"]]
    return df
Esempio n. 18
0
def parse_yahoo_splits(df: pd.DataFrame, excluded_symbols=None):
    if df.empty:
        return df
    current_datetime = posix_to_datetime(
        time.time(), timezone="UTC")  # because selenium defaults to UTC
    num_cols = ["denominator", "numerator"]
    if excluded_symbols is None:
        excluded_symbols = []
    df = df[~df["symbol"].isin(excluded_symbols)]
    if df.empty:
        del df["ratio"]
        return df
    df[num_cols] = df["ratio"].str.split(" - ", expand=True)
    df[num_cols] = df[num_cols].apply(pd.to_numeric)
    df["exec_date"] = get_day_start(current_datetime)
    return df[["symbol", "exec_date"] + num_cols]
Esempio n. 19
0
def get_downloadable_transactions_table(game_id: int, user_id: int):
    sql = """
    SELECT g.balance_type, g.symbol, g.balance, g.timestamp, o.buy_or_sell, o.quantity, o.time_in_force, os.clear_price
    FROM game_balances g
    LEFT JOIN (
      SELECT * FROM order_status
      ) os
    ON os.id = g.order_status_id
    LEFT JOIN (
      SELECT  * FROM orders
      ) o
    ON o.id = os.order_id
    WHERE g.game_id = %s AND g.user_id = %s
    ORDER BY g.id;"""
    with engine.connect() as conn:
        df = pd.read_sql(sql, conn, params=[game_id, user_id])
    df = df.where(pd.notnull(df), None)
    df["timestamp"] = df["timestamp"].apply(lambda x: posix_to_datetime(x))
    return df.to_dict(orient="records")
Esempio n. 20
0
def get_order_expiration_status(order_id):
    """Before processing an order, we'll use logic to determine whether that order is still active. This function
    return True if an order is expired, or false otherwise.
    """
    with engine.connect() as conn:
        time_in_force = conn.execute(
            "SELECT time_in_force FROM orders WHERE id = %s;",
            order_id).fetchone()[0]
        if time_in_force == "until_cancelled":
            return False

    # posix_to_datetime
    current_time = time.time()
    with engine.connect() as conn:
        time_placed = conn.execute(
            """SELECT timestamp 
                                      FROM order_status 
                                      WHERE order_id = %s 
                                      ORDER BY id LIMIT 0, 1;""",
            order_id).fetchone()[0]

    time_placed_nyc = posix_to_datetime(time_placed)

    cal_ref_time = time_placed_nyc.date()
    schedule = get_trading_calendar(cal_ref_time, cal_ref_time)
    if schedule.empty:
        next_day_schedule = get_next_trading_day_schedule(time_placed_nyc)
        _, cutoff_time = get_schedule_start_and_end(next_day_schedule)
    else:
        if time_placed_nyc.hour >= END_OF_TRADE_HOUR:
            next_day_schedule = get_next_trading_day_schedule(time_placed_nyc +
                                                              timedelta(
                                                                  days=1))
            _, cutoff_time = get_schedule_start_and_end(next_day_schedule)
        else:
            _, cutoff_time = get_schedule_start_and_end(schedule)

    if current_time > cutoff_time:
        return True
    return False
class TestStockbetsRanking(StockbetsRatingCase):
    """Test stockbets ratings updates following a decent-sized multiplayer game"""

    maxDiff = None

    RECORDS = [
        {'id': 1, 'user_id': 1.0, 'index_symbol': None, 'game_id': None, 'rating': 1000.0, 'update_type': 'sign_up', 'timestamp': 1591402922.88987, 'basis': 0.0, 'n_games': 0, 'total_return': 0.0},
        {'id': 10, 'user_id': 10.0, 'index_symbol': None, 'game_id': None, 'rating': 1000.0, 'update_type': 'sign_up', 'timestamp': 1592102702.01045, 'basis': 0.0, 'n_games': 0, 'total_return': 0.0},
        {'id': 28, 'user_id': 28.0, 'index_symbol': None, 'game_id': None, 'rating': 1000.0, 'update_type': 'sign_up', 'timestamp': 1592515077.34491, 'basis': 0.0, 'n_games': 0, 'total_return': 0.0},
        {'id': 29, 'user_id': 29.0, 'index_symbol': None, 'game_id': None, 'rating': 1000.0, 'update_type': 'sign_up', 'timestamp': 1592516128.51439, 'basis': 0.0, 'n_games': 0, 'total_return': 0.0},
        {'id': 86, 'user_id': 44.0, 'index_symbol': None, 'game_id': None, 'rating': 1000.0, 'update_type': 'sign_up', 'timestamp': 1595394720.4184, 'basis': 0.0, 'n_games': 0, 'total_return': 0.0},
        {'id': 87, 'user_id': 45.0, 'index_symbol': None, 'game_id': None, 'rating': 1000.0, 'update_type': 'sign_up', 'timestamp': 1595423428.28603, 'basis': 0.0, 'n_games': 0, 'total_return': 0.0},
        {'id': 110, 'user_id': 55.0, 'index_symbol': None, 'game_id': None, 'rating': 1000.0, 'update_type': 'sign_up', 'timestamp': 1595958906.4312, 'basis': 0.0, 'n_games': 0, 'total_return': 0.0},
        {'id': 111, 'user_id': nan, 'index_symbol': '^IXIC', 'game_id': None, 'rating': 1000.0, 'update_type': 'sign_up', 'timestamp': -99.0, 'basis': 0.0, 'n_games': 0, 'total_return': 0.0},
        {'id': 112, 'user_id': nan, 'index_symbol': '^GSPC', 'game_id': None, 'rating': 1000.0, 'update_type': 'sign_up', 'timestamp': -99.0, 'basis': 0.0, 'n_games': 0, 'total_return': 0.0},
        {'id': 113, 'user_id': nan, 'index_symbol': '^DJI', 'game_id': None, 'rating': 1000.0, 'update_type': 'sign_up', 'timestamp': -99.0, 'basis': 0.0, 'n_games': 0, 'total_return': 0.0},
        {'id': 114, 'user_id': 1.0, 'index_symbol': None, 'game_id': '47', 'rating': 1144.0, 'update_type': 'game_end', 'timestamp': 1599854400.0, 'basis': 1000000.0, 'n_games': 1, 'total_return': 0.0762641150000003},
        {'id': 115, 'user_id': 10.0, 'index_symbol': None, 'game_id': '47', 'rating': 920.0, 'update_type': 'game_end', 'timestamp': 1599854400.0, 'basis': 1000000.0, 'n_games': 1, 'total_return': -0.0165752899999999},
        {'id': 116, 'user_id': 28.0, 'index_symbol': None, 'game_id': '47', 'rating': 952.0, 'update_type': 'game_end', 'timestamp': 1599854400.0, 'basis': 1000000.0, 'n_games': 1, 'total_return': 0.0},
        {'id': 117, 'user_id': 29.0, 'index_symbol': None, 'game_id': '47', 'rating': 856.0, 'update_type': 'game_end', 'timestamp': 1599854400.0, 'basis': 1000000.0, 'n_games': 1, 'total_return': -0.095518835},
        {'id': 118, 'user_id': 44.0, 'index_symbol': None, 'game_id': '47', 'rating': 888.0, 'update_type': 'game_end', 'timestamp': 1599854400.0, 'basis': 1000000.0, 'n_games': 1, 'total_return': -0.0240974450000001},
        {'id': 119, 'user_id': 45.0, 'index_symbol': None, 'game_id': '47', 'rating': 1016.0, 'update_type': 'game_end', 'timestamp': 1599854400.0, 'basis': 1000000.0, 'n_games': 1, 'total_return': 0.01191425},
        {'id': 120, 'user_id': 55.0, 'index_symbol': None, 'game_id': '47', 'rating': 984.0, 'update_type': 'game_end', 'timestamp': 1599854400.0, 'basis': 1000000.0, 'n_games': 1, 'total_return': 9.66199999998807e-05},
        {'id': 121, 'user_id': nan, 'index_symbol': '^DJI', 'game_id': '47', 'rating': 1112.0, 'update_type': 'game_end', 'timestamp': 1599854400.0, 'basis': 1000000.0, 'n_games': 1, 'total_return': 0.0403694553583551},
        {'id': 122, 'user_id': nan, 'index_symbol': '^GSPC', 'game_id': '47', 'rating': 1080.0, 'update_type': 'game_end', 'timestamp': 1599854400.0, 'basis': 1000000.0, 'n_games': 1, 'total_return': 0.0313045035144639},
        {'id': 123, 'user_id': nan, 'index_symbol': '^IXIC', 'game_id': '47', 'rating': 1048.0, 'update_type': 'game_end', 'timestamp': 1599854400.0, 'basis': 1000000.0, 'n_games': 1, 'total_return': 0.030142483456657}
    ]

    LEADERBOARD = [
        {'username': '******', 'user_id': 1.0, 'rating': 1144, 'profile_pic': 'https://s3.amazonaws.com/stockbets-public/profile_pics/c0f0bc6489851026b29b0e1e0e60ece21daf93632347a44f600dc5ce', 'n_games': 1, 'three_month_return': 0.0762641150000003},
        {'username': '******', 'user_id': None, 'rating': 1112, 'profile_pic': 'https://stockbets-public.s3.amazonaws.com/profile_pics/8bd8ec5f6126dbceabe5aae0b255b50dcdf09b3128cea8f53e8eb091', 'n_games': 1, 'three_month_return': 0.0403694553583551},
        {'username': '******', 'user_id': None, 'rating': 1080, 'profile_pic': 'https://stockbets-public.s3.amazonaws.com/profile_pics/fe2862aca264a58ef2f8fb2d22fa8d4dd112fd06bb3e8bf2bb8bddb6', 'n_games': 1, 'three_month_return': 0.0313045035144639},
        {'username': '******', 'user_id': None, 'rating': 1048, 'profile_pic': 'https://stockbets-public.s3.amazonaws.com/profile_pics/044c7859dc114c52135ad159fcb7b817ad04b5a3c44c788672796b9d', 'n_games': 1, 'three_month_return': 0.030142483456657},
        {'username': '******', 'user_id': 45.0, 'rating': 1016, 'profile_pic': 'https://s3.amazonaws.com/stockbets-public/profile_pics/aefac8aa916dccabbf7b444e5a38436d517437f37c3a981f51f68c47', 'n_games': 1, 'three_month_return': 0.01191425},
        {'username': '******', 'user_id': 55.0, 'rating': 984, 'profile_pic': 'https://s3.amazonaws.com/stockbets-public/profile_pics/8b7546390be79ba37a3f31d07caac05fcb0f6deb98f7ff34bb75cd74', 'n_games': 1, 'three_month_return': 9.66199999998807e-05},
        {'username': '******', 'user_id': 28.0, 'rating': 952, 'profile_pic': 'https://s3.amazonaws.com/stockbets-public/profile_pics/1251754a5111216d995e8c9408fd5699a8f73a2117cd2c52143ffd38', 'n_games': 1, 'three_month_return': 0.0},
        {'username': '******', 'user_id': 10.0, 'rating': 920, 'profile_pic': 'https://s3.amazonaws.com/stockbets-public/profile_pics/50ff504bc91aecd2c942758b8adc6c9616ab0ce0951f019ab44571f0', 'n_games': 1, 'three_month_return': -0.0165752899999999},
        {'username': '******', 'user_id': 44.0, 'rating': 888, 'profile_pic': 'https://s3.amazonaws.com/stockbets-public/profile_pics/2321842dbba174eeaf8f75cd2b5798dda2d2be9f321ce5b98533cd48', 'n_games': 1, 'three_month_return': -0.0240974450000001},
        {'username': '******', 'user_id': 29.0, 'rating': 856, 'profile_pic': 'https://s3.amazonaws.com/stockbets-public/profile_pics/4f6199cd963305ef8cb6154956ff875dec86bff210d963b1dccc3263', 'n_games': 1, 'three_month_return': -0.095518835}
    ]

    @freeze_time(posix_to_datetime(1601950823.7715478))
    def test_stockbets_ranking(self):
        update_ratings(self.game_id)
        with self.engine.connect() as conn:
            df = pd.read_sql("SELECT * FROM stockbets_rating", conn)
        pd.testing.assert_frame_equal(df, pd.DataFrame(self.RECORDS))

        # make the public rankings JSON
        serialize_and_pack_rankings()

        session_token = create_jwt("*****@*****.**", 1, "aaron", mins_per_session=1_000_000)
        res = self.requests_session.post(f"{HOST_URL}/public_leaderboard", cookies={"session_token": session_token},
                                         verify=False)
        self.assertEqual(res.json(), self.LEADERBOARD)
        res = self.requests_session.post(f"{HOST_URL}/home", cookies={"session_token": session_token}, verify=False)
        test_user_rank = float(rds.get(f"{PLAYER_RANK_PREFIX}_1"))
        test_user_return = float(rds.get(f"{THREE_MONTH_RETURN_PREFIX}_1"))
        self.assertEqual(res.json()["rating"], test_user_rank)
        self.assertEqual(test_user_rank, 1144.0)
        self.assertEqual(res.json()["three_month_return"], test_user_return)
        self.assertEqual(test_user_return, 0.0762641150000003)
Esempio n. 22
0
def log_winners(game_id: int, current_time: float):
    update_performed = False
    game_info = query_to_dict("SELECT * FROM games WHERE id = %s", game_id)[0]
    game_start, game_end, start_dt, end_dt, benchmark, side_bets_perc, stakes, offset = get_winners_meta_data(game_id)
    start_dt = posix_to_datetime(game_start)
    end_dt = posix_to_datetime(game_end)

    # If we have sidebets to monitor, see if we have a winner
    if side_bets_perc:
        last_interval_end = get_last_sidebet_payout(game_id)
        if not last_interval_end:
            last_interval_end = game_start
        last_interval_dt = posix_to_datetime(last_interval_end)
        payout_time = datetime_to_posix(last_interval_dt + offset)
        if check_if_payout_time(current_time, payout_time):
            win_type = "sidebet"
            curr_time_dt = posix_to_datetime(current_time)
            anchor_time = last_interval_dt + timedelta(days=1)
            expected_sidebet_dates = get_expected_sidebets_payout_dates(start_dt, end_dt, side_bets_perc, offset)
            curr_interval_end = [date for date in expected_sidebet_dates if anchor_time < date <= curr_time_dt][0]
            curr_interval_posix = datetime_to_posix(curr_interval_end)
            winner_id, score = get_winner(game_id, last_interval_end, curr_interval_posix, benchmark)
            payout = get_sidebet_payout(game_id, side_bets_perc, offset, stakes)
            winner_table_id = add_row("winners", game_id=game_id, winner_id=winner_id, score=float(score),
                                      timestamp=current_time, payout=payout, type=win_type, benchmark=benchmark,
                                      end_time=curr_interval_posix, start_time=last_interval_end)
            update_performed = True
            if stakes == "real":
                payment_profile = get_payment_profile_uuids([winner_id])[0]
                send_paypal_payment(
                    uuids=[payment_profile["uuid"]],
                    amount=payout,
                    payment_type="sidebet",
                    email_subject=f"Congrats on winning the {game_info['side_bets_period']} sidebet!",
                    email_content=f"You came out on top in {game_info['title']} this week. Here's your payment of {USD_FORMAT.format(payout)}",
                    note_content="Keep on crushing it."
                )
                add_row("payments", user_id=winner_id, profile_id=payment_profile["id"], game_id=game_id,
                        winner_table_id=winner_table_id, type=win_type, amount=payout, currency="USD",
                        direction="outflow", timestamp=current_time)

    # if we've reached the end of our game, pay out the winner and mark the game as completed
    if current_time >= game_end:
        win_type = "overall"
        payout = get_overall_payout(game_id, side_bets_perc, stakes)
        winner_id, score = get_winner(game_id, game_start, game_end, benchmark)
        winner_table_id = add_row("winners", game_id=game_id, winner_id=winner_id, benchmark=benchmark,
                                  score=float(score), start_time=game_start, end_time=game_end,
                                  payout=payout, type=win_type, timestamp=current_time)
        update_performed = True

        if stakes == "real":
            pass  # TODO: uncomment when we're able to take real money
            # payment_profile = get_payment_profile_uuids([winner_id])[0]
            # send_paypal_payment(
            #     uuids=[payment_profile["uuid"]],
            #     amount=payout,
            #     payment_type="overall",
            #     email_subject=f"Congrats on winning the {game_info['title']}!",
            #     email_content=f"You were the overall winner of {game_info['title']}. Awesome work. Here's your payment of {USD_FORMAT.format(payout)}. Come back soon!",
            #     note_content="Keep on crushing it."
            # )
            # add_row("payments", user_id=winner_id, profile_id=payment_profile["id"], game_id=game_id,
            #         winner_table_id=winner_table_id, type=win_type, amount=payout, currency="USD",
            #         direction="outflow", timestamp=current_time)

    return update_performed
Esempio n. 23
0
def portfolio_value_by_day(game_id: int, user_id: int, start_time: float, end_time: float) -> pd.DataFrame:
    start_time, end_time = get_time_defaults(game_id, start_time, end_time)
    df = make_historical_balances_and_prices_table(game_id, user_id)
    df = df[(df["timestamp"] >= posix_to_datetime(start_time)) & (df["timestamp"] <= posix_to_datetime(end_time))]
    df = df.groupby(["symbol", "timestamp"], as_index=False)["value"].agg("last")
    return df.groupby("timestamp", as_index=False)["value"].sum()
Esempio n. 24
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)
Esempio n. 25
0
def format_posix_time(ts: float):
    if np.isnan(ts):
        return ts
    dtime = posix_to_datetime(ts)
    return dtime.strftime(DATE_LABEL_FORMAT)