fix: correct WorldQuant daily alpha date filters
Deploy production / deploy (push) Successful in 52s

This commit is contained in:
yuxuanhui
2026-09-09 15:12:45 +08:00
parent 4debca7dbd
commit ff840ca140
3 changed files with 30 additions and 10 deletions
+6 -2
View File
@@ -350,9 +350,13 @@ class WqClient:
# Daily intervals are [UTC midnight, next midnight). Submitted dates # Daily intervals are [UTC midnight, next midnight). Submitted dates
# use the actual submission time, independently of creation or stage. # use the actual submission time, independently of creation or stage.
field = "dateCreated" if submission == "UNSUBMITTED" else "dateSubmitted" field = "dateCreated" if submission == "UNSUBMITTED" else "dateSubmitted"
params.update({f"{field}>=": date_from, f"{field}<": date_to, "order": field}) # BRAIN treats the query separator '=' as part of its comparison
# syntax: a '>=' key adds an extra '=' and makes the date invalid.
# Both bounds are inclusive upstream; exclude the next UTC midnight.
upper = (datetime.fromisoformat(date_to) - timedelta(microseconds=1)).isoformat()
params.update({f"{field}>": date_from, f"{field}<": upper, "order": field})
if field == "dateCreated": if field == "dateCreated":
params["dateCreated<"] = min(before, date_to) params["dateCreated<"] = min(before, upper)
return await self.get("/users/self/alphas", params) return await self.get("/users/self/alphas", params)
async def alpha(self, alpha_id): async def alpha(self, alpha_id):
+5 -3
View File
@@ -163,7 +163,9 @@ def create_test_app():
r for r in records if (r["status"] == "UNSUBMITTED") == unsubmitted and r["hidden"] == hidden r for r in records if (r["status"] == "UNSUBMITTED") == unsubmitted and r["hidden"] == hidden
] ]
for field in ("dateCreated", "dateSubmitted"): for field in ("dateCreated", "dateSubmitted"):
for suffix in (">=", "<"): if field + ">=" in request.url.params:
return httpx.Response(400, json=["Expected ISO 8601 datetime with timezone"])
for suffix in (">", "<"):
boundary = request.url.params.get(field + suffix) boundary = request.url.params.get(field + suffix)
if boundary: if boundary:
bound = datetime.fromisoformat(boundary).replace(tzinfo=timezone.utc) bound = datetime.fromisoformat(boundary).replace(tzinfo=timezone.utc)
@@ -173,8 +175,8 @@ def create_test_app():
if r.get(field) if r.get(field)
and ( and (
datetime.fromisoformat(r[field]) >= bound datetime.fromisoformat(r[field]) >= bound
if suffix == ">=" if suffix == ">"
else datetime.fromisoformat(r[field]) < bound else datetime.fromisoformat(r[field]) <= bound
) )
] ]
offset, limit = ( offset, limit = (
+19 -5
View File
@@ -318,12 +318,25 @@ async def test_scoped_date_query_parameters_and_no_platform_check(settings):
def handler(request): def handler(request):
requests.append(request) requests.append(request)
return httpx.Response(200, json={"results": []}) # BRAIN parses the extra '=' as part of the datetime, returning HTTP 400.
if any(key.endswith(">=") for key in request.url.params):
return httpx.Response(400, json=["Expected ISO 8601 datetime with timezone"])
field = "dateCreated" if "status" in request.url.params else "dateSubmitted"
lower = datetime.fromisoformat(request.url.params[field + ">"])
upper = datetime.fromisoformat(request.url.params[field + "<"])
records = [
{"id": "midnight", field: "2025-01-01T00:00:00+00:00"},
{"id": "last", field: "2025-01-01T23:59:59.999999+00:00"},
{"id": "next", field: "2025-01-02T00:00:00+00:00"},
]
return httpx.Response(200, json={"results": [
row for row in records if lower <= datetime.fromisoformat(row[field]) <= upper
]})
client = WqClient(settings, transport=httpx.MockTransport(handler)) client = WqClient(settings, transport=httpx.MockTransport(handler))
client.credentials, client.authenticated = ("test@example.com", "test"), True client.credentials, client.authenticated = ("test@example.com", "test"), True
for submission in ("UNSUBMITTED", "SUBMITTED"): for submission in ("UNSUBMITTED", "SUBMITTED"):
await client.alphas( result = await client.alphas(
submission, submission,
True, True,
100, 100,
@@ -331,10 +344,11 @@ async def test_scoped_date_query_parameters_and_no_platform_check(settings):
date_from="2025-01-01T00:00:00+00:00", date_from="2025-01-01T00:00:00+00:00",
date_to="2025-01-02T00:00:00+00:00", date_to="2025-01-02T00:00:00+00:00",
) )
assert [row["id"] for row in result["results"]] == ["midnight", "last"]
first, second = [dict(r.url.params) for r in requests] first, second = [dict(r.url.params) for r in requests]
assert first["dateCreated>="] == "2025-01-01T00:00:00+00:00" assert first["dateCreated>"] == "2025-01-01T00:00:00+00:00"
assert first["dateCreated<"] == "2025-01-02T00:00:00+00:00" assert first["dateCreated<"] == "2025-01-01T23:59:59.999999+00:00"
assert second["dateSubmitted>="] == first["dateCreated>="] assert second["dateSubmitted>"] == first["dateCreated>"]
assert second["dateSubmitted<"] == first["dateCreated<"] assert second["dateSubmitted<"] == first["dateCreated<"]
assert "status!" in second and second["hidden"] == "true" assert "status!" in second and second["hidden"] == "true"
assert all(r.method == "GET" and r.url.path == "/users/self/alphas" for r in requests) assert all(r.method == "GET" and r.url.path == "/users/self/alphas" for r in requests)