-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
224 lines (182 loc) · 6.93 KB
/
Copy pathmain.py
File metadata and controls
224 lines (182 loc) · 6.93 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
213
214
215
216
217
218
219
220
221
222
223
224
import logging
from datetime import datetime
import argparse
import sys
from typing import Dict, List
import importlib.util
import pandas as pd
import matplotlib.pyplot as plt
import copy
from datamodel import Portfolio, State
from dataimport import read_file, extract_orders, extract_bot_orders
from ordermatching import match_order
from analytics_vis import Visualiser
from bots_functions import clean_resting_orders, add_bot_orders
# Set up logging
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
# Constants
POSITION_LIMIT = 20
MAX_TICKS = 1000
def import_trader(file_path: str) -> type:
"""
Import the Trader class from the specified file.
:param file_path: Trading algo filepath.
"""
try:
spec = importlib.util.spec_from_file_location("trader_module", file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module.Trader
except Exception as e:
logging.error(f"Error importing Trader class from {file_path}: {str(e)}")
sys.exit(1)
def initialise_portfolio(products: List[str]) -> Portfolio:
"""
Create an empty portfolio.
:param products: Products to be traded.
"""
portfolio = Portfolio()
for product in products:
portfolio.quantity[product] = 0
return portfolio
def process_tick(state: State, bot_orders: Dict[str, Dict], algo, portfolio) -> None:
# Get orders from the trader
ob_copy = {
product: {side: orders.copy() for side, orders in ob.items()}
for product, ob in state.orderbook.items()
}
publicstate = State(
ob_copy, state.positions.copy(), state.products, state.pos_limit
)
algo_orders = algo.run(publicstate)
# Process algo orders
algo_resting_orders = {
product: {"BUY": {}, "SELL": {}} for product in state.products
}
if algo_orders:
algo_resting_orders = match_order(
algo_orders, state.orderbook, portfolio, state.pos_limit
)
# Add bot orders to the orderbook
add_bot_orders(
bot_orders,
state.orderbook,
algo_resting_orders,
portfolio,
state.pos_limit,
)
portfolio.pnl = portfolio.cash
for product in state.products:
best_bid = next(iter(state.orderbook[product]["BUY"]))
best_ask = next(iter(state.orderbook[product]["SELL"]))
midprice = (best_bid + best_ask) / 2
portfolio.pnl += portfolio.quantity[product] * midprice
def update_quantity_data(
quantity_data: pd.DataFrame, tick: int, portfolio: Portfolio, products: List[str]
) -> None:
quantity_data.loc[tick, "PnL"] = portfolio.pnl
quantity_data.loc[tick, "Cash"] = portfolio.cash
for product in products:
quantity_data.loc[tick, f"{product}_quantity"] = portfolio.quantity[product]
def prepare_analytics_data(
quantity_data: pd.DataFrame, products: List[str], market_data: pd.DataFrame
) -> pd.DataFrame:
analytics_df = pd.DataFrame(index=quantity_data.index)
ticks = quantity_data.index
for product in products:
mid_prices = []
bid_prices = []
offer_prices = []
for tick in ticks:
try:
row = market_data[market_data["timestamp"] == tick * 100]
row = row[row["product"] == product]
best_bid = row["bid_price_1"].iloc[0]
best_ask = row["ask_price_1"].iloc[0]
mid_price = (best_bid + best_ask) / 2
bid_prices.append(best_bid)
offer_prices.append(best_ask)
mid_prices.append(mid_price)
except:
# If data missing, use NaN
mid_prices.append(None)
bid_prices.append(None)
offer_prices.append(None)
analytics_df[product] = mid_prices
analytics_df[f"{product}_bid"] = bid_prices
analytics_df[f"{product}_offer"] = offer_prices
analytics_df["pnl"] = quantity_data["PnL"]
return analytics_df
def main(round_data_path: str, trading_algo: str) -> None:
products, ticks, df = read_file(round_data_path)
bot_df = pd.read_csv(round_data_path[:-4] + "_bots.csv")
market_data = df.copy()
portfolio = initialise_portfolio(products)
pos_limit = {product: POSITION_LIMIT for product in products}
pos_limit["CASTLE_STOCKS"] = 30
pos_limit["JOHNS_STOCKS"] = 30
pos_limit["COLLINGWOOD_STOCKS"] = 30
pos_limit["CHADS_STOCKS"] = 30
pos_limit["CUTHS_STOCKS"] = 30
pos_limit["HATFIELD_STOCKS"] = 30
# Import the Trader class
Trader = import_trader(trading_algo)
algo = Trader()
# Create a DataFrame to store the quantity data
quantity_data = pd.DataFrame(
index=range(1, ticks),
columns=[f"{product}_quantity" for product in products] + ["PnL", "Cash"],
)
start = datetime.now()
metrics = {"tick": [], "PnL": [], "Cash": []}
for product in products:
metrics[f"{product}_quantity"] = []
for tick in range(1, MAX_TICKS):
if tick % 100 == 0:
print(tick)
orderbook = {product: extract_orders(df, tick, product) for product in products}
bot_orders = {
product: extract_bot_orders(bot_df, tick, product) for product in products
}
state = State(orderbook, portfolio.quantity, products, pos_limit)
try:
process_tick(state, bot_orders, algo, portfolio)
metrics["tick"].append(tick)
metrics["PnL"].append(portfolio.pnl)
metrics["Cash"].append(portfolio.cash)
for product in products:
metrics[f"{product}_quantity"].append(portfolio.quantity[product])
except:
break
end = datetime.now()
quantity_data = pd.DataFrame(metrics).set_index("tick")
print(f"Time per tick: {(end-start)/MAX_TICKS}")
# Portfolio summary
print("\n=== Final Portfolio State ===")
print(f"PnL: {portfolio.pnl:.2f}")
analytics_df = prepare_analytics_data(quantity_data, products, market_data)
positions_df = pd.DataFrame(index=quantity_data.index)
for product in products:
positions_df[product] = quantity_data[f"{product}_quantity"]
# Save results for analysis
quantity_data.to_csv("backtest_results.csv")
print(f"\n💾 Results saved to 'backtest_results.csv'")
print(f"📊 Run 'python analyze_performance.py' for detailed analysis")
vis = Visualiser(
dataframe=analytics_df, products=products, volume_data=positions_df
)
vis.display_visualisation()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Run the trading simulation.")
parser.add_argument(
"--round",
default="Round Data/Round_1/Round_1.csv",
help="Main data file path",
)
parser.add_argument(
"--algo", default="examplealgo.py", help="Trading alngorithm path"
)
args = parser.parse_args()
main(args.round, args.algo)