2
0

2 Commits cebcdd0188 ... c392cb08a7

Autor SHA1 Mensagem Data
  raylu c392cb08a7 market_stats: more stats 1 semana atrás
  raylu f0ce5fe52f market: handle books with no bids better 1 semana atrás
2 arquivos alterados com 36 adições e 36 exclusões
  1. 4 2
      market.py
  2. 32 34
      market_stats.py

+ 4 - 2
market.py

@@ -112,12 +112,14 @@ def analyze_markets(raw_prices: typing.Sequence[RawPrice]) -> typing.Iterator[Ma
 			if price['Ask'] is None or price['Ask'] > highest_ask:
 				highest_ask = price['Ask'] or 0
 			if price['Bid'] is None or price['Bid'] < lowest_bid:
-				lowest_bid = price['Bid'] or 10_000_000
+				lowest_bid = price['Bid'] or 0
 			if price['Traded30D'] is None or price['Traded30D'] < lowest_traded:
 				lowest_traded = price['Traded30D'] or 0
 		if lowest_traded == 0:
 			continue
-		score = (lowest_bid - (ic1_price['Bid'] or 0)) / lowest_bid * 100
+		score = 0
+		if lowest_bid > 0:
+			score = (lowest_bid - (ic1_price['Bid'] or 0)) / lowest_bid * 100
 		if highest_ask > 0:
 			score += ((ic1_price['Ask'] or 10_000_000) - highest_ask) / highest_ask * 100
 		if score > 0 and (trade_activity_deficit := lowest_traded - (ic1_price['Traded30D'] or 0)) > 0:

+ 32 - 34
market_stats.py

@@ -16,15 +16,23 @@ if typing.TYPE_CHECKING:
 	import market
 
 def main() -> None:
+	weeks = int(sys.argv[1])
 	repo = dulwich.repo.Repo('../refined-prices')
 
 	today = datetime.datetime.now(tz=datetime.UTC).replace(hour=0, minute=0, second=0, microsecond=0)
 	sunday = (today - datetime.timedelta(days=(today.weekday() + 1) % 7))
 	weekly_stats: list[MarketStats] = []
-	for _ in range(52):
+	for _ in range(weeks):
 		weekly_stats.append(analyze_markets(prices_on_day(repo, sunday), sunday.date()))
 		sunday -= datetime.timedelta(days=7)
-	render_chart(list(reversed(weekly_stats)))
+	weekly_stats.reverse()
+
+	base = altair.Chart({'values': weekly_stats}).encode(x=altair.X('date:T', title='week'))
+	render_chart(base, 'ranking:Q', 'ranking', 'average IC1 rank')
+	print('higher is better\naverage = (0 + 3) ÷ 2 = 1.5')
+	render_chart(base, 'ic1_lowest:Q', '%', 'IC1 lowest')
+	render_chart(base, 'ic1_zero:Q', '#', 'IC1 zero trades')
+	render_chart(base, 'gap:Q', '%', 'trade gap')
 
 def prices_on_day(repo: dulwich.repo.Repo, day: datetime.datetime) -> typing.Sequence[market.RawPrice]:
 	'''refined-prices for the earliest commit on the given day'''
@@ -37,13 +45,6 @@ def prices_on_day(repo: dulwich.repo.Repo, day: datetime.datetime) -> typing.Seq
 	_, blob = tree[b'all.json']
 	return json.loads(typing.cast(dulwich.objects.Blob, repo[blob]).data.decode())
 
-class MarketStats(typing.TypedDict):
-	date: str
-	ranking: int
-	ic1_lowest: int
-	markets_with_trades: int
-	gap: float
-
 def analyze_markets(prices: typing.Sequence[market.RawPrice], date: datetime.date) -> MarketStats:
 	markets: dict[str, list[market.RawPrice]] = collections.defaultdict(list)
 	for price in prices:
@@ -51,10 +52,10 @@ def analyze_markets(prices: typing.Sequence[market.RawPrice], date: datetime.dat
 			continue
 		markets[price['MaterialTicker']].append(price)
 
-	markets_with_trades = ranking = ic1_lowest = gap = 0
+	markets_with_trades = ranking = ic1_lowest = ic1_zero = gap = 0
 	for mat, mat_prices in markets.items():
 		mat_prices.sort(key=lambda p: (p['Traded7D'] or 0))
-		if mat_prices[0]['Traded7D'] == None:
+		if mat_prices[-1]['Traded7D'] == None:
 			continue
 		markets_with_trades += 1
 		for index, price in enumerate(mat_prices):
@@ -65,33 +66,23 @@ def analyze_markets(prices: typing.Sequence[market.RawPrice], date: datetime.dat
 		ranking += index
 		if index == 0:
 			ic1_lowest += 1
-			gap += (mat_prices[1]['Traded7D'] - mat_prices[0]['Traded7D']) / mat_prices[1]['Traded7D'] # type: ignore
-	print(f'IC1 ranking {ranking}, lowest in {ic1_lowest} of {markets_with_trades} markets, gap {gap:.2f}')
+			ic1_traded = mat_prices[0]['Traded7D'] or 0
+			lowest_traded = mat_prices[1]['Traded7D'] or 0
+			if lowest_traded > 0:
+				if ic1_traded == 0:
+					ic1_zero += 1
+				gap += (lowest_traded - ic1_traded) / lowest_traded
+	print(f'{markets_with_trades} markets with trades, IC1 ranking {ranking}, lowest in {ic1_lowest}, zero in {ic1_zero}, gap {gap:.2f}')
 	return {
 		'date': date.isoformat(),
-		'ranking': ranking,
-		'ic1_lowest': ic1_lowest,
-		'markets_with_trades': markets_with_trades,
-		'gap': gap,
+		'ranking': ranking / markets_with_trades,
+		'ic1_lowest': ic1_lowest / markets_with_trades,
+		'ic1_zero': ic1_zero,
+		'gap': gap / markets_with_trades,
 	}
 
-def render_chart(weekly_stats: typing.Sequence[MarketStats]) -> None:
-	base = altair.Chart({'values': list(weekly_stats)}).encode(
-		x=altair.X('date:T', title='week'),
-		tooltip=[
-			altair.Tooltip('date:T', title='week'),
-			altair.Tooltip('ranking:Q', title='ranking'),
-			altair.Tooltip('ic1_lowest:Q', title='IC1 lowest'),
-			altair.Tooltip('markets_with_trades:Q', title='markets with trades'),
-			altair.Tooltip('gap:Q', title='gap', format='.2f'),
-		],
-	)
-	chart = altair.vconcat(
-		base.mark_line(point=True).encode(y=altair.Y('ranking:Q', title='sum of IC1 rank')).properties(height=160),
-		base.mark_line(point=True).encode(y=altair.Y('ic1_lowest:Q', title='# markets where IC1 is lowest')).properties(height=160),
-		base.mark_line(point=True).encode(y=altair.Y('gap:Q', title='trade gap')).properties(height=160),
-		title='IC1 market stats',
-	)
+def render_chart(base: altair.Chart, y: str, y_label: str, title: str) -> None:
+	chart = base.mark_line(point=True).encode(y=altair.Y(y, title=y_label)).properties(title=title, height=160)
 	buffer = io.BytesIO()
 	chart.save(buffer, format='png')
 	display_kitty_png(buffer.getvalue())
@@ -110,5 +101,12 @@ def display_kitty_png(png: bytes) -> None:
 	sys.stdout.write('\n')
 	sys.stdout.flush()
 
+class MarketStats(typing.TypedDict):
+	date: str
+	ranking: float
+	ic1_lowest: float
+	ic1_zero: int
+	gap: float
+
 if __name__ == '__main__':
 	main()