def update_index_value(symbol): value = get_index_value(symbol) if during_trading_day(): add_row("indexes", symbol=symbol, value=value, timestamp=time.time()) return True # a bit of logic to get the close of day price with engine.connect() as conn: max_time = conn.execute( "SELECT MAX(timestamp) FROM indexes WHERE symbol = %s;", symbol).fetchone()[0] if max_time is None: max_time = 0 ref_day = time.time() eod = get_end_of_last_trading_day(ref_day) while eod > ref_day: ref_day -= SECONDS_IN_A_DAY eod = get_end_of_last_trading_day(ref_day) if max_time < eod <= time.time(): add_row("indexes", symbol=symbol, value=value, timestamp=eod) return True return False
def test_index_scrapers(self): """This test make sure that our external integration with yahoo finance via the celery workers is running properly, as well as that new indexes that get added to our inventory will be properly intitialized, as well as that close of day index values will be stored """ with self.engine.connect() as conn: conn.execute("TRUNCATE indexes;") async_update_all_index_values.delay() df = pd.read_sql("SELECT * FROM indexes;", conn) iteration = 0 while df.shape != (3, 4) and iteration < 120: time.sleep(1) with self.engine.connect() as conn: df = pd.read_sql("SELECT * FROM indexes;", conn) iteration += 1 self.assertEqual(df.shape, (3, 4)) if not during_trading_day(): ref_day = time.time() eod = get_end_of_last_trading_day(ref_day) while eod > ref_day: ref_day -= SECONDS_IN_A_DAY eod = get_end_of_last_trading_day(ref_day) [self.assertEqual(eod, x) for x in df["timestamp"].to_list()]
def async_cache_price(self, symbol: str, price: float, last_updated: float): cache_price, cache_time = get_cache_price(symbol) if cache_price is not None and cache_time == last_updated: return if during_trading_day(): add_row("prices", symbol=symbol, price=price, timestamp=last_updated) set_cache_price(symbol, price, last_updated)
def process_order(order_id: int): timestamp = time.time() if get_order_expiration_status(order_id): add_row("order_status", order_id=order_id, timestamp=timestamp, status="expired", clear_price=None) return order_ticket = query_to_dict("SELECT * FROM orders WHERE id = %s", order_id)[0] symbol = order_ticket["symbol"] game_id = order_ticket["game_id"] user_id = order_ticket["user_id"] buy_or_sell = order_ticket["buy_or_sell"] quantity = order_ticket["quantity"] order_type = order_ticket["order_type"] market_price, _ = fetch_price(symbol) # Only process active outstanding orders during trading day cash_balance = get_current_game_cash_balance(user_id, game_id) current_holding = get_current_stock_holding(user_id, game_id, symbol) if during_trading_day(): if execute_order(buy_or_sell, order_type, market_price, order_ticket["price"], cash_balance, current_holding, quantity): order_status_id = add_row("order_status", order_id=order_id, timestamp=timestamp, status="fulfilled", clear_price=market_price) update_balances(user_id, game_id, order_status_id, timestamp, buy_or_sell, cash_balance, current_holding, market_price, quantity, symbol) serialize_and_pack_pending_orders( game_id, user_id) # refresh the pending orders table add_fulfilled_order_entry( game_id, user_id, order_id) # add the new fulfilled orders entry to the table serialize_and_pack_portfolio_details(game_id, user_id) else: # if a market order was placed after hours, there may not be enough cash on hand to clear it at the new # market price. If this happens, cancel the order and recalculate the purchase quantity with the new price if order_type == "market": cancel_order(order_id) updated_quantity = cash_balance // market_price if updated_quantity <= 0: return place_order(user_id, game_id, symbol, buy_or_sell, cash_balance, current_holding, order_type, "Shares", market_price, updated_quantity, order_ticket["time_in_force"]) serialize_and_pack_portfolio_details(game_id, user_id)
def place_order(user_id: int, game_id: int, symbol: str, buy_or_sell: str, cash_balance: float, current_holding: int, order_type: str, quantity_type: str, market_price: float, amount: float, time_in_force: str, stop_limit_price: float = None): timestamp = time.time() order_price = get_order_price(order_type, market_price, stop_limit_price) order_quantity = get_order_quantity(order_price, amount, quantity_type) if order_quantity <= 0: raise NoNegativeOrders if buy_or_sell == "buy": qc_buy_order(order_type, quantity_type, order_price, market_price, amount, cash_balance) elif buy_or_sell == "sell": qc_sell_order(order_type, quantity_type, order_price, market_price, amount, current_holding) else: raise Exception(f"Invalid buy or sell option {buy_or_sell}") # having validated the order, now we'll go ahead and book it order_id = add_row("orders", user_id=user_id, game_id=game_id, symbol=symbol, buy_or_sell=buy_or_sell, quantity=order_quantity, price=order_price, order_type=order_type, time_in_force=time_in_force) add_row("order_status", order_id=order_id, timestamp=timestamp, status="pending", clear_price=None) # If this is a market order and we're inside a trading day we'll execute this order at the current price if order_type == "market" and during_trading_day(): os_id = add_row("order_status", order_id=order_id, timestamp=timestamp, status="fulfilled", clear_price=order_price) update_balances(user_id, game_id, os_id, timestamp, buy_or_sell, cash_balance, current_holding, order_price, order_quantity, symbol) return order_id
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())
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