|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
|
|
|
import collections
|
|
|
import dataclasses
|
|
|
+import datetime
|
|
|
+import statistics
|
|
|
import sys
|
|
|
import typing
|
|
|
|
|
|
@@ -16,7 +18,7 @@ def main() -> None:
|
|
|
for ticker in exchange_tickers:
|
|
|
(price,) = (p for p in raw_prices if p['FullTicker'] == ticker)
|
|
|
a = analyze_price_chart(ticker, (price['Bid'] + price['Ask']) / 2) # pyright: ignore[reportOperatorIssue]
|
|
|
- print(f'{ticker}: bids filled = {a.bids_filled:6.0f}, asks filled = {a.asks_filled:6.0f}, profit per interval = {a.profit_per_interval:10.1f}')
|
|
|
+ print(f'{ticker}: bids filled = {a.bids_filled:6.0f}, asks filled = {a.asks_filled:6.0f}, profit per interval = {a.profits:10.1f}')
|
|
|
return
|
|
|
|
|
|
check_cxos()
|
|
|
@@ -35,16 +37,15 @@ def main() -> None:
|
|
|
if spread < 0.15:
|
|
|
continue
|
|
|
chart_analysis = analyze_price_chart(price['FullTicker'], (price['Bid'] + price['Ask']) / 2)
|
|
|
- max_profit = (price['Ask'] - price['Bid']) * min(chart_analysis.bids_filled, chart_analysis.asks_filled)
|
|
|
markets[price['ExchangeCode']].append(Market(price['FullTicker'], bid=price['Bid'], ask=price['Ask'],
|
|
|
- spread=spread, max_profit=max_profit, chart_analysis=chart_analysis))
|
|
|
+ spread=spread, chart_analysis=chart_analysis))
|
|
|
|
|
|
- print(' mat bid ask spread bids filled asks filled profit/time max profit')
|
|
|
+ print(' mat bid ask spread bids filled asks filled profit p75 fill time')
|
|
|
for commodities in markets.values():
|
|
|
commodities.sort(reverse=True)
|
|
|
for m in commodities:
|
|
|
print(f'{m.full_ticker:>8} {m.bid:5} {m.ask:5} {m.spread*100: 5.0f}% {m.chart_analysis.bids_filled:12.0f} '
|
|
|
- f'{m.chart_analysis.asks_filled:12.0f} {m.chart_analysis.profit_per_interval:12.0f} {m.max_profit:11.0f}')
|
|
|
+ f'{m.chart_analysis.asks_filled:12.0f} {m.chart_analysis.profits:10.0f} {m.chart_analysis.p75_fill_time}')
|
|
|
print()
|
|
|
|
|
|
def check_cxos() -> None:
|
|
|
@@ -82,7 +83,8 @@ def analyze_price_chart(exchange_ticker: str, midpoint: float) -> ChartAnalysis:
|
|
|
pcpoints.reverse()
|
|
|
five_min = 5 * 60 * 1000
|
|
|
asks_filled: list[AskFilled] = []
|
|
|
- r = ChartAnalysis(bids_filled=0, asks_filled=0, profit_per_interval=0)
|
|
|
+ fill_time = []
|
|
|
+ r = ChartAnalysis(bids_filled=0, asks_filled=0, profits=0, p75_fill_time=datetime.timedelta.max)
|
|
|
for hist in pcpoints:
|
|
|
time = hist['DateEpochMs'] // five_min
|
|
|
bids = asks = 0
|
|
|
@@ -103,24 +105,28 @@ def analyze_price_chart(exchange_ticker: str, midpoint: float) -> ChartAnalysis:
|
|
|
asks = int(interval_asks)
|
|
|
if bids and asks:
|
|
|
intra_interval_trades = min(bids, asks)
|
|
|
- r.profit_per_interval += (hist['High'] - hist['Low']) * intra_interval_trades
|
|
|
+ r.profits += (hist['High'] - hist['Low']) * intra_interval_trades
|
|
|
bids -= intra_interval_trades
|
|
|
asks -= intra_interval_trades
|
|
|
assert bids == 0 or asks == 0
|
|
|
while bids > 0 and len(asks_filled) > 0:
|
|
|
- profit_per_unit_per_interval = (asks_filled[-1].price - hist['Low']) / (asks_filled[-1].time - time)
|
|
|
+ profit = (asks_filled[-1].price - hist['Low'])
|
|
|
if asks_filled[-1].amount >= bids:
|
|
|
- r.profit_per_interval += bids * profit_per_unit_per_interval
|
|
|
+ r.profits += bids * profit
|
|
|
asks_filled[-1].amount -= bids
|
|
|
if asks_filled[-1].amount == 0:
|
|
|
+ fill_time.append(asks_filled[-1].time - time)
|
|
|
asks_filled.pop()
|
|
|
bids = 0
|
|
|
else:
|
|
|
- r.profit_per_interval += asks_filled[-1].amount * profit_per_unit_per_interval
|
|
|
+ r.profits += asks_filled[-1].amount * profit
|
|
|
bids -= asks_filled[-1].amount
|
|
|
+ fill_time.append(asks_filled[-1].time - time)
|
|
|
asks_filled.pop()
|
|
|
if asks:
|
|
|
asks_filled.append(AskFilled(price=hist['High'], amount=asks, time=time))
|
|
|
+ if len(fill_time) > 0:
|
|
|
+ r.p75_fill_time = statistics.quantiles(fill_time, n=4)[2] * datetime.timedelta(minutes=5)
|
|
|
return r
|
|
|
|
|
|
class ExchangeOrder(typing.TypedDict):
|
|
|
@@ -180,7 +186,8 @@ class AskFilled:
|
|
|
class ChartAnalysis:
|
|
|
bids_filled: float
|
|
|
asks_filled: float
|
|
|
- profit_per_interval: float
|
|
|
+ profits: float
|
|
|
+ p75_fill_time: datetime.timedelta
|
|
|
|
|
|
@dataclasses.dataclass(eq=False, frozen=True, slots=True)
|
|
|
class Market:
|
|
|
@@ -188,11 +195,10 @@ class Market:
|
|
|
bid: float
|
|
|
ask: float
|
|
|
spread: float
|
|
|
- max_profit: float
|
|
|
chart_analysis: ChartAnalysis
|
|
|
|
|
|
def __lt__(self, o: Market) -> bool:
|
|
|
- return self.chart_analysis.profit_per_interval < o.chart_analysis.profit_per_interval
|
|
|
+ return self.chart_analysis.profits < o.chart_analysis.profits
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
main()
|