Перейти к основному содержимому

Стакан заявок (orderbooks)

Open In Colab
#!pip install moexalgo
#!pip install matplotlib

import pandas as pd
import matplotlib.pyplot as plt
from moexalgo.beta import issplus

# Данные для входа
ws_login = 'ваш_логин'
ws_passcode = 'ваш_пароль'

ticker = 'SBER'

async def websocket_orderbook():
url = 'wss://iss.moex.com/infocx/v3/websocket'
credentials = issplus.Credentials('passport', ws_login, ws_passcode)

prev_snapshot = None
count = 0

async with issplus.connect(url, credentials) as client:
# Подписка на стакан заявок (orderbooks)
destination = 'MXSE.orderbooks'
selector = f'TICKER="MXSE.TQBR.{ticker}"'

subscription = await client.subscribe(destination, selector)
print(f"Подписка ID: {subscription.id}\n")

async for data in subscription:
count += 1

# Обработка данных стакана
df = pd.DataFrame(data['data'], columns=data['columns'])
df['price'] = df['PRICE'].apply(lambda x: x[0])
df['qty'] = df['QUANTITY']

# Разделение на заявки на покупку (BID) и продажу (ASK)
bids = df[df['BUYSELL'] == 'B'].copy()
asks = df[df['BUYSELL'] == 'S'].copy()

print(f"\nСнэпшот (snapshot) #{count}")
print(f"Спред: {asks['price'].min():.2f} - {bids['price'].max():.2f} = {asks['price'].min() - bids['price'].max():.2f}")


print("\nBID (покупка) ASK (продажа)")
for i in range(min(5, len(bids))):
bid_price = bids.iloc[-(i+1)]['price']
bid_qty = bids.iloc[-(i+1)]['qty']
ask_price = asks.iloc[i]['price']
ask_qty = asks.iloc[i]['qty']
print(f"{bid_price:>7.2f} x {bid_qty:<6} {ask_price:>7.2f} x {ask_qty:<6}")


# Сравнение с предыдущим снэпшотом (snapshot) для отслеживания изменений объемов
if prev_snapshot is not None:
prev_df = prev_snapshot

changes = []
for _, row in df.iterrows():
price = row['price']
qty = row['qty']
side = row['BUYSELL']

prev_row = prev_df[(prev_df['price'] == price) & (prev_df['BUYSELL'] == side)]
if not prev_row.empty:
prev_qty = prev_row.iloc[0]['qty']
if qty != prev_qty:
changes.append((side, price, prev_qty, qty, qty - prev_qty))

if changes:
print("\nИзменения:")
for side, price, old_qty, new_qty, diff in changes[:3]:
print(f"{price:.2f}: {old_qty}{new_qty} ({diff:+d})")

prev_snapshot = df.copy()

# Остановка после пяти итераций (для примера)
if count >= 5:
break

return prev_snapshot


orderbook_snapshot = await websocket_orderbook()
Подписка ID: 90cd3b43-0d3d-40e6-953b-439fb5d9c3d7


Снэпшот (snapshot) #1
Спред: 306.26 - 306.24 = 0.02

BID (покупка) ASK (продажа)
306.24 x 784 306.26 x 1499
306.23 x 16744 306.27 x 10
306.22 x 10 306.29 x 225
306.21 x 734 306.30 x 61
306.20 x 1730 306.31 x 1005

Снэпшот (snapshot) #2
Спред: 306.26 - 306.24 = 0.02

BID (покупка) ASK (продажа)
306.24 x 785 306.26 x 1499
306.23 x 16744 306.27 x 10
306.22 x 10 306.29 x 225
306.21 x 734 306.30 x 61
306.20 x 1730 306.31 x 1005

Изменения:
306.24: 784 → 785 (+1)

Снэпшот (snapshot) #3
Спред: 306.26 - 306.24 = 0.02

BID (покупка) ASK (продажа)
306.24 x 786 306.26 x 1498
306.23 x 16744 306.27 x 10
306.22 x 10 306.29 x 225
306.21 x 734 306.30 x 61
306.20 x 1730 306.31 x 1005

Изменения:
306.24: 785 → 786 (+1)
306.26: 1499 → 1498 (-1)

Снэпшот (snapshot) #4
Спред: 306.26 - 306.24 = 0.02

BID (покупка) ASK (продажа)
306.24 x 786 306.26 x 1206
306.23 x 16744 306.27 x 13
306.22 x 10 306.29 x 225
306.21 x 734 306.30 x 61
306.20 x 1730 306.31 x 1005

Изменения:
306.26: 1498 → 1206 (-292)
306.27: 10 → 13 (+3)

Снэпшот (snapshot) #5
Спред: 306.26 - 306.24 = 0.02

BID (покупка) ASK (продажа)
306.24 x 786 306.26 x 1532
306.23 x 16744 306.27 x 13
306.22 x 10 306.29 x 225
306.21 x 734 306.30 x 61
306.20 x 1730 306.31 x 1005

Изменения:
306.26: 1206 → 1532 (+326)
if orderbook_snapshot is not None:
bids = orderbook_snapshot[orderbook_snapshot['BUYSELL'] == 'B'].sort_values('price')
asks = orderbook_snapshot[orderbook_snapshot['BUYSELL'] == 'S'].sort_values('price')

fig, ax = plt.subplots(figsize=(12, 6))

ax.barh(range(len(bids)), bids['qty'].values, color='#26a69a', alpha=0.7, label='Bids')
ax.barh(range(len(bids), len(bids) + len(asks)), asks['qty'].values, color='#ef5350', alpha=0.7, label='Asks')

all_prices = list(bids['price'].values) + list(asks['price'].values)
ax.set_yticks(range(len(all_prices)))
ax.set_yticklabels([f'{p:.2f}' for p in all_prices])

ax.set_xlabel('Количество (лоты)')
ax.set_ylabel('Цена')
ax.set_title(f'Стакан заявок {ticker}')
ax.legend()
ax.grid(alpha=0.3, axis='x')

plt.tight_layout()
plt.show()

if orderbook_snapshot is not None:
bids = orderbook_snapshot[orderbook_snapshot['BUYSELL'] == 'B'].sort_values('price', ascending=False)
asks = orderbook_snapshot[orderbook_snapshot['BUYSELL'] == 'S'].sort_values('price')

bids['cumqty'] = bids['qty'].cumsum()
asks['cumqty'] = asks['qty'].cumsum()

fig, ax = plt.subplots(figsize=(12, 6))

ax.step(bids['price'], bids['cumqty'], where='post', color='#26a69a', linewidth=2, label='Bids')
ax.step(asks['price'], asks['cumqty'], where='post', color='#ef5350', linewidth=2, label='Asks')

ax.fill_between(bids['price'], bids['cumqty'], step='post', color='#26a69a', alpha=0.3)
ax.fill_between(asks['price'], asks['cumqty'], step='post', color='#ef5350', alpha=0.3)

ax.set_xlabel('Цена')
ax.set_ylabel('Накопленное количество (лоты)')
ax.set_title(f'Стакан заявок {ticker}')
ax.legend()
ax.grid(alpha=0.3)

plt.tight_layout()
plt.show()