Don't overlook a simple thing here - you may be surprised at how well it works. You have two orders, so maintain two sequences in sorted order, bytime ordered by time and byvalue ordered by value. Store 2-tuple (value, timestamp) pairs in each. Of course you need to keep them in synch.
Because byvalue is always maintained in order sorted by value, you can, at any time, look at the top n, bottom n, middle n, or any other kind of order statistic you want.
Assuming your timestamps (whatever that may mean to you) only increase over time, "sorting" by time is trivial: use a collections.deque and push new records on one end (say, the right) and discard from the other end. Use a plain list for byvalue. To expire old records, then:
oldest_to_retain = whatever form of timestamp you use
while bytime and bytime[0][1] < oldest_to_retain:
t = bytime.popleft() # discard expired record
# and remove it from the other seq too
i = bisect.bisect_left(byvalue, t)
assert byvalue[i] == t
del byvalue[i]
To insert an incoming value,
t = (the_new_value, current_timestamp)
assert not bytime or bytime[-1][1] <= current_timestamp
bytime.append(t)
bisect.insort(byvalue, t)
Now with some experience, people balk at this idea because these statements have O() behavior linear in len(byvalue):
del byvalue[i]
bisect.insort(byvalue, t)
(And the other statements have O(1) or O(log(N)) behavior.)
With more experience, they get over that ;-) Those occur "at C speed", and unless byvalue grows to several hundreds of elements it's generally faster - and more space-efficient - than fancy tree structures, even if they're coded in optimized C.
If byvalue does grow large, it's an easy change to switch byvalue to use a SortedList from the widely used sortedcontainers package. Then no statement is worse than about O(log(N)). Your part of the code remains just as simple, flexible, and easy to reason about.