-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbase_algo.py
More file actions
212 lines (172 loc) · 7.86 KB
/
base_algo.py
File metadata and controls
212 lines (172 loc) · 7.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
from bots1 import Msg
from base import Exchange, Trade, Order, Product
import numpy as np
class PlayerAlgorithm:
"""
Base trading algorithm with tracking functionality only.
Features:
- Position tracking: All positions and PnL are automatically tracked
- Data export: Player data is exported to log_game.csv with anonymized market data
- Order management utilities: Helper functions for placing/canceling orders
To create a trading strategy, inherit from this class and override send_messages().
"""
def __init__(self, products, num_timestamps, visualisation = True):
self.products = products
self.name = "PlayerAlgorithm"
self.position_limits = {product.ticker: product.pos_limit for product in products}
self.fines = {product.ticker: product.fine for product in products}
self.sent_orders = {product.ticker: [] for product in products} # for cancelation
self.positions = {product.ticker: 0 for product in products}
self.positions["Cash"] = 0.0
self.timestamp = 0
self.visualisation = visualisation
self.memory = {
"last_mids": {product.ticker: 1000.0 for product in products}, # Initialize with default price
}
self.num_timestamps = num_timestamps
self.idx = 0 # Initialize order ID counter
# Player tracking data for CSV export
self.player_view_data = {
'orderbook_snapshots': [],
'visible_trades': [],
'position_history': [],
'pnl_history': []
}
# ===== Main Method - Override this in your trading strategy =====
def send_messages(self, book):
"""
Main method called each timestamp. Override this in your trading algorithm.
Args:
book: Dictionary containing orderbook data for all products
Returns:
List of messages (orders, cancellations, etc.)
"""
# Track player data for this timestamp
messages = []
if self.visualisation:
self.update_fines()
self.update_memory(book)
self._track_player_data(book)
self.timestamp += 1
return messages
# ===== Helper Functions =====
def update_fines(self):
"""Apply position limit fines"""
for ticker, pos in self.positions.items():
if ticker == 'Cash':
continue
if ticker in self.position_limits and abs(pos) > self.position_limits[ticker]:
self.positions['Cash'] -= self.fines[ticker] * (abs(pos) - self.position_limits[ticker])
def mid_price(self, book, ticker, weights=1):
""" Get the best guess at mid price, optionally weighted by size """
bids = book[ticker]["Bids"]
asks = book[ticker]["Asks"]
if not bids or not asks:
last_mid = self.memory["last_mids"][ticker]
if bids:
return min(last_mid, bids[0].price)
elif asks:
return max(last_mid, asks[0].price)
return last_mid
# Calculate weighted prices
total_bid_size = total_ask_size = 0
bid_price = ask_price = 0
for bid in bids:
if total_bid_size >= weights: break
size = min(bid.size, weights - total_bid_size)
bid_price += bid.price * size
total_bid_size += size
for ask in asks:
if total_ask_size >= weights: break
size = min(ask.size, weights - total_ask_size)
ask_price += ask.price * size
total_ask_size += size
bid_price /= total_bid_size if total_bid_size > 0 else 1
ask_price /= total_ask_size if total_ask_size > 0 else 1
return (bid_price + ask_price) / 2
def update_memory(self, book):
""" Update the memory with the latest values from book """
for ticker, data in book.items():
if data['Bids'] and data['Asks']:
mid_price = self.mid_price(book, ticker)
self.memory["last_mids"][ticker] = mid_price
def create_order(self, ticker, price, size, agg_dir):
"""Create and track a new order"""
order = Order(ticker=ticker, price=price, size=size, order_id=self.idx, agg_dir=agg_dir, bot_name=self.name)
self.idx += 1
return Msg("ORDER", order)
def cancel_order(self, ticker, order_id):
"""Cancel a specific order"""
cancel_msg = Msg("REMOVE", order_id)
return cancel_msg
def set_idx(self, idx):
"""Set the order ID counter""" # Do NOT CHANGE
self.idx = idx
def process_trades(self, trades):
"""Process executed trades and update positions"""
for trade in trades:
# Track visible trades for player view
self.player_view_data['visible_trades'].append({
'timestamp': self.timestamp,
'ticker': trade.ticker,
'price': trade.price,
'size': trade.size,
'agg_dir': trade.agg_dir,
'agg_bot': trade.agg_bot,
'rest_bot': trade.rest_bot,
'involves_player': (trade.agg_bot == self.name or trade.rest_bot == self.name)
})
# Update positions based on trade
if trade.agg_bot == self.name:
# If we were aggressor, position changes in direction of aggression
if trade.agg_dir == "Buy":
self.positions[trade.ticker] += trade.size
self.positions["Cash"] -= trade.price * trade.size
else:
self.positions[trade.ticker] -= trade.size
self.positions["Cash"] += trade.price * trade.size
elif trade.rest_bot == self.name:
# If we were resting, position changes opposite to aggressor direction
if trade.agg_dir == "Buy":
self.positions[trade.ticker] -= trade.size
self.positions["Cash"] += trade.price * trade.size
else:
self.positions[trade.ticker] += trade.size
self.positions["Cash"] -= trade.price * trade.size
# ===== Player Tracking Methods =====
def _track_player_data(self, book):
"""Track player-specific data for each timestamp"""
# Store orderbook snapshot with player perspective
book_snapshot = {
'timestamp': self.timestamp,
'book': {}
}
for ticker, data in book.items():
book_snapshot['book'][ticker] = {
'Bids': [{
'price': order.price,
'size': order.size,
'bot_name': order.bot_name,
'is_own': order.bot_name == self.name
} for order in data['Bids']],
'Asks': [{
'price': order.price,
'size': order.size,
'bot_name': order.bot_name,
'is_own': order.bot_name == self.name
} for order in data['Asks']]
}
self.player_view_data['orderbook_snapshots'].append(book_snapshot)
# Track position and PnL history
position_snapshot = {'timestamp': self.timestamp}
pnl = self.positions["Cash"] # Start with cash
for product in self.products:
ticker = product.ticker
position_snapshot[ticker] = self.positions[ticker]
# Calculate mark-to-market PnL if we have mid price
if ticker in book and book[ticker]['Bids'] and book[ticker]['Asks']:
mid_price = (book[ticker]['Bids'][0].price + book[ticker]['Asks'][0].price) / 2
pnl += self.positions[ticker] * mid_price
position_snapshot['Cash'] = self.positions["Cash"]
position_snapshot['PnL'] = pnl
self.player_view_data['position_history'].append(position_snapshot)