rosacastillo commited on
Commit
285f2a6
·
1 Parent(s): b3b7123

updating week format starting on Monday, new staking contracts and new weekly data

Browse files
data/all_trades_profitability.parquet CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:04134fbaceed13c1c02eeb952134df58ec787afddcba0e09b69b7ea5ae8693cd
3
- size 6590780
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6256840b7a7704aa5618fd5a4fed41b9444bbf80ea1dcaae068715026c8d52b0
3
+ size 8218375
data/daily_info.parquet CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:880e896031e8e87ddc4e9fc952941d7bbe5196b86e56ebdf003e572182db6b76
3
- size 1050038
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f3d8ec77951dad3d522c90ea0009c15e5ab717c3f34624b4f0d205ad58cfa16e
3
+ size 1054780
data/error_by_markets.parquet CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:5b0f69c6df66a53208bd47167d39db8ef7ec965a47a1a90fe429815cc44cbad9
3
- size 14168
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9dff09a27b7b5ac4a527d679c446627c6ca4fb2653c6bc50e818d79e29e3c1be
3
+ size 12928
data/invalid_trades.parquet CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:f29e5a59d7a2c60ef8408cddf58785f601a472105a99bd7339a45de8389fa36f
3
- size 159829
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:099e999dc46d4a2d7086838f3645475aecf27fa88331a8b2d5fd4c9937f1ad81
3
+ size 782151
data/service_map.pkl CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:93ac540e1bcd347a48b9978b87443ae64af0f8b0a4daff305c4ad99cd0959a73
3
- size 90766
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:32d288a076f719a659159ffdb2bca3f132c3efe3f62ee0412c11e8094c36ffc8
3
+ size 164076
data/service_map_bak.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:93ac540e1bcd347a48b9978b87443ae64af0f8b0a4daff305c4ad99cd0959a73
3
+ size 90766
data/tools_accuracy.csv CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:a521cd7735fbd9aecd1fce18836b08dc03cc58dca1f52add0b60ec2bbb00f722
3
- size 1099
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bb5a70b32e6a7dbd75c7924a2fa887612bf7523a62f6710f2e2397cdc3664fa2
3
+ size 1100
data/unknown_traders.parquet CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:5c237838416f8339b145719e5b370df1d9763099f9e5fa3e75d4d69053e5d311
3
- size 200722
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1633afc5d408263251ae5290e1f45972abaf0d3f0358ab880604de8a0baae559
3
+ size 283140
data/winning_df.parquet CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:6c31991027458844d3c8e72da29bb9acd7bcef8ad31ae7c95a59ee168a34ba58
3
- size 14407
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f394838074669231dc3f8dc46167bb05019ae12eb798933e99b2c2de9b9a2c1f
3
+ size 12636
notebooks/markets_analysis.ipynb CHANGED
@@ -2,7 +2,7 @@
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
- "execution_count": 3,
6
  "metadata": {},
7
  "outputs": [],
8
  "source": [
@@ -15,32 +15,67 @@
15
  },
16
  {
17
  "cell_type": "code",
18
- "execution_count": 7,
 
 
 
 
 
 
 
 
 
19
  "metadata": {},
20
  "outputs": [
21
  {
22
- "ename": "ModuleNotFoundError",
23
- "evalue": "No module named 'scripts'",
24
- "output_type": "error",
25
- "traceback": [
26
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
27
- "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
28
- "Cell \u001b[0;32mIn[7], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mscripts\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mget_mech_info\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m update_fpmmTrades_parquet\n",
29
- "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'scripts'"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  ]
31
  }
32
  ],
33
  "source": [
34
- "from scripts.get_mech_info import update_fpmmTrades_parquet"
35
  ]
36
  },
37
  {
38
  "cell_type": "code",
39
- "execution_count": 2,
40
  "metadata": {},
41
  "outputs": [],
42
  "source": [
43
- "fpmms = pd.read_parquet('../data/all_fpmms.parquet')"
44
  ]
45
  },
46
  {
@@ -54,7 +89,7 @@
54
  },
55
  {
56
  "cell_type": "code",
57
- "execution_count": 4,
58
  "metadata": {},
59
  "outputs": [],
60
  "source": [
@@ -72,13 +107,674 @@
72
  },
73
  {
74
  "cell_type": "code",
75
- "execution_count": 8,
76
  "metadata": {},
77
  "outputs": [],
78
  "source": [
79
  "new_trades = pd.read_parquet(\"../tmp/new_fpmmTrades.parquet\")"
80
  ]
81
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  {
83
  "cell_type": "code",
84
  "execution_count": 9,
 
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
+ "execution_count": 1,
6
  "metadata": {},
7
  "outputs": [],
8
  "source": [
 
15
  },
16
  {
17
  "cell_type": "code",
18
+ "execution_count": 12,
19
+ "metadata": {},
20
+ "outputs": [],
21
+ "source": [
22
+ "missing_df = pd.read_parquet(\"../data/missing_fpmmTrades.parquet\")"
23
+ ]
24
+ },
25
+ {
26
+ "cell_type": "code",
27
+ "execution_count": 13,
28
  "metadata": {},
29
  "outputs": [
30
  {
31
+ "name": "stdout",
32
+ "output_type": "stream",
33
+ "text": [
34
+ "<class 'pandas.core.frame.DataFrame'>\n",
35
+ "RangeIndex: 24121 entries, 0 to 24120\n",
36
+ "Data columns (total 24 columns):\n",
37
+ " # Column Non-Null Count Dtype \n",
38
+ "--- ------ -------------- ----- \n",
39
+ " 0 collateralAmount 24121 non-null object\n",
40
+ " 1 collateralAmountUSD 24121 non-null object\n",
41
+ " 2 collateralToken 24121 non-null object\n",
42
+ " 3 creationTimestamp 24121 non-null object\n",
43
+ " 4 trader_address 24121 non-null object\n",
44
+ " 5 feeAmount 24121 non-null object\n",
45
+ " 6 id 24121 non-null object\n",
46
+ " 7 oldOutcomeTokenMarginalPrice 24121 non-null object\n",
47
+ " 8 outcomeIndex 24121 non-null object\n",
48
+ " 9 outcomeTokenMarginalPrice 24121 non-null object\n",
49
+ " 10 outcomeTokensTraded 24121 non-null object\n",
50
+ " 11 title 24121 non-null object\n",
51
+ " 12 transactionHash 24121 non-null object\n",
52
+ " 13 type 24121 non-null object\n",
53
+ " 14 market_creator 24121 non-null object\n",
54
+ " 15 fpmm.answerFinalizedTimestamp 22553 non-null object\n",
55
+ " 16 fpmm.arbitrationOccurred 24121 non-null bool \n",
56
+ " 17 fpmm.currentAnswer 22553 non-null object\n",
57
+ " 18 fpmm.id 24121 non-null object\n",
58
+ " 19 fpmm.isPendingArbitration 24121 non-null bool \n",
59
+ " 20 fpmm.openingTimestamp 24121 non-null object\n",
60
+ " 21 fpmm.outcomes 24121 non-null object\n",
61
+ " 22 fpmm.title 24121 non-null object\n",
62
+ " 23 fpmm.condition.id 24121 non-null object\n",
63
+ "dtypes: bool(2), object(22)\n",
64
+ "memory usage: 4.1+ MB\n"
65
  ]
66
  }
67
  ],
68
  "source": [
69
+ "missing_df.info()"
70
  ]
71
  },
72
  {
73
  "cell_type": "code",
74
+ "execution_count": 26,
75
  "metadata": {},
76
  "outputs": [],
77
  "source": [
78
+ "old_markets_df = pd.read_parquet(\"../data/old_fpmmTrades.parquet\")"
79
  ]
80
  },
81
  {
 
89
  },
90
  {
91
  "cell_type": "code",
92
+ "execution_count": 35,
93
  "metadata": {},
94
  "outputs": [],
95
  "source": [
 
107
  },
108
  {
109
  "cell_type": "code",
110
+ "execution_count": 23,
111
  "metadata": {},
112
  "outputs": [],
113
  "source": [
114
  "new_trades = pd.read_parquet(\"../tmp/new_fpmmTrades.parquet\")"
115
  ]
116
  },
117
+ {
118
+ "cell_type": "code",
119
+ "execution_count": 15,
120
+ "metadata": {},
121
+ "outputs": [
122
+ {
123
+ "data": {
124
+ "text/plain": [
125
+ "Index(['collateralAmount', 'collateralAmountUSD', 'collateralToken',\n",
126
+ " 'creationTimestamp', 'trader_address', 'feeAmount', 'id',\n",
127
+ " 'oldOutcomeTokenMarginalPrice', 'outcomeIndex',\n",
128
+ " 'outcomeTokenMarginalPrice', 'outcomeTokensTraded', 'title',\n",
129
+ " 'transactionHash', 'type', 'market_creator',\n",
130
+ " 'fpmm.answerFinalizedTimestamp', 'fpmm.arbitrationOccurred',\n",
131
+ " 'fpmm.currentAnswer', 'fpmm.id', 'fpmm.isPendingArbitration',\n",
132
+ " 'fpmm.openingTimestamp', 'fpmm.outcomes', 'fpmm.title',\n",
133
+ " 'fpmm.condition.id'],\n",
134
+ " dtype='object')"
135
+ ]
136
+ },
137
+ "execution_count": 15,
138
+ "metadata": {},
139
+ "output_type": "execute_result"
140
+ }
141
+ ],
142
+ "source": [
143
+ "new_trades.columns"
144
+ ]
145
+ },
146
+ {
147
+ "cell_type": "code",
148
+ "execution_count": 36,
149
+ "metadata": {},
150
+ "outputs": [],
151
+ "source": [
152
+ "from datetime import datetime, timezone\n",
153
+ "def transform_to_datetime(x):\n",
154
+ " return datetime.fromtimestamp(int(x), tz=timezone.utc)\n"
155
+ ]
156
+ },
157
+ {
158
+ "cell_type": "code",
159
+ "execution_count": 17,
160
+ "metadata": {},
161
+ "outputs": [],
162
+ "source": [
163
+ "new_trades[\"creationTimestamp\"] = new_trades[\"creationTimestamp\"].apply(\n",
164
+ " lambda x: transform_to_datetime(x)\n",
165
+ ")"
166
+ ]
167
+ },
168
+ {
169
+ "cell_type": "code",
170
+ "execution_count": 37,
171
+ "metadata": {},
172
+ "outputs": [],
173
+ "source": [
174
+ "def add_creation_date(df):\n",
175
+ " try:\n",
176
+ " df[\"creationTimestamp\"] = df[\"creationTimestamp\"].apply(\n",
177
+ " lambda x: transform_to_datetime(x)\n",
178
+ " )\n",
179
+ " except Exception:\n",
180
+ " print(\"Ignore\")\n",
181
+ " df[\"creation_timestamp\"] = pd.to_datetime(df[\"creationTimestamp\"])\n",
182
+ " df[\"creation_date\"] = df[\"creation_timestamp\"].dt.date\n",
183
+ " df[\"creation_date\"] = pd.to_datetime(df[\"creation_date\"])\n",
184
+ " return df"
185
+ ]
186
+ },
187
+ {
188
+ "cell_type": "code",
189
+ "execution_count": 27,
190
+ "metadata": {},
191
+ "outputs": [
192
+ {
193
+ "name": "stdout",
194
+ "output_type": "stream",
195
+ "text": [
196
+ "Ignore\n"
197
+ ]
198
+ },
199
+ {
200
+ "data": {
201
+ "text/plain": [
202
+ "Timestamp('2025-01-14 00:00:00')"
203
+ ]
204
+ },
205
+ "execution_count": 27,
206
+ "metadata": {},
207
+ "output_type": "execute_result"
208
+ }
209
+ ],
210
+ "source": [
211
+ "old_markets_df = add_creation_date(old_markets_df)\n",
212
+ "max(old_markets_df.creation_date)"
213
+ ]
214
+ },
215
+ {
216
+ "cell_type": "code",
217
+ "execution_count": 38,
218
+ "metadata": {},
219
+ "outputs": [
220
+ {
221
+ "name": "stdout",
222
+ "output_type": "stream",
223
+ "text": [
224
+ "Ignore\n"
225
+ ]
226
+ },
227
+ {
228
+ "data": {
229
+ "text/plain": [
230
+ "Timestamp('2025-01-16 00:00:00')"
231
+ ]
232
+ },
233
+ "execution_count": 38,
234
+ "metadata": {},
235
+ "output_type": "execute_result"
236
+ }
237
+ ],
238
+ "source": [
239
+ "trades_data = add_creation_date(trades_data)\n",
240
+ "max(trades_data.creation_date)"
241
+ ]
242
+ },
243
+ {
244
+ "cell_type": "code",
245
+ "execution_count": 39,
246
+ "metadata": {},
247
+ "outputs": [
248
+ {
249
+ "data": {
250
+ "text/plain": [
251
+ "Timestamp('2024-11-14 00:00:00')"
252
+ ]
253
+ },
254
+ "execution_count": 39,
255
+ "metadata": {},
256
+ "output_type": "execute_result"
257
+ }
258
+ ],
259
+ "source": [
260
+ "min(trades_data.creation_date)"
261
+ ]
262
+ },
263
+ {
264
+ "cell_type": "code",
265
+ "execution_count": 24,
266
+ "metadata": {},
267
+ "outputs": [
268
+ {
269
+ "data": {
270
+ "text/plain": [
271
+ "Timestamp('2025-01-14 00:00:00')"
272
+ ]
273
+ },
274
+ "execution_count": 24,
275
+ "metadata": {},
276
+ "output_type": "execute_result"
277
+ }
278
+ ],
279
+ "source": [
280
+ "new_trades = add_creation_date(new_trades)\n",
281
+ "max(new_trades.creation_date)"
282
+ ]
283
+ },
284
+ {
285
+ "cell_type": "code",
286
+ "execution_count": 25,
287
+ "metadata": {},
288
+ "outputs": [
289
+ {
290
+ "data": {
291
+ "text/plain": [
292
+ "Timestamp('2025-01-11 00:00:00')"
293
+ ]
294
+ },
295
+ "execution_count": 25,
296
+ "metadata": {},
297
+ "output_type": "execute_result"
298
+ }
299
+ ],
300
+ "source": [
301
+ "min(new_trades.creation_date)"
302
+ ]
303
+ },
304
+ {
305
+ "cell_type": "code",
306
+ "execution_count": 28,
307
+ "metadata": {},
308
+ "outputs": [
309
+ {
310
+ "name": "stdout",
311
+ "output_type": "stream",
312
+ "text": [
313
+ "Transformation not needed\n",
314
+ "Transformation not needed\n",
315
+ "Initial length before removing duplicates in fpmmTrades= 165530\n"
316
+ ]
317
+ }
318
+ ],
319
+ "source": [
320
+ "# lowercase and strip creator_address\n",
321
+ "new_trades[\"trader_address\"] = (\n",
322
+ " new_trades[\"trader_address\"].str.lower().str.strip()\n",
323
+ ")\n",
324
+ "# ensure creationTimestamp compatibility\n",
325
+ "try:\n",
326
+ " new_trades[\"creationTimestamp\"] = new_trades[\"creationTimestamp\"].apply(\n",
327
+ " lambda x: transform_to_datetime(x)\n",
328
+ " )\n",
329
+ "\n",
330
+ "except Exception as e:\n",
331
+ " print(f\"Transformation not needed\")\n",
332
+ "try:\n",
333
+ " old_markets_df[\"creationTimestamp\"] = old_markets_df[\"creationTimestamp\"].apply(\n",
334
+ " lambda x: transform_to_datetime(x)\n",
335
+ " )\n",
336
+ "except Exception as e:\n",
337
+ " print(f\"Transformation not needed\")\n",
338
+ "\n",
339
+ "# merge two dataframes\n",
340
+ "merge_df = pd.concat([old_markets_df, new_trades], ignore_index=True)\n",
341
+ "# avoid numpy objects\n",
342
+ "merge_df[\"fpmm.arbitrationOccurred\"] = merge_df[\"fpmm.arbitrationOccurred\"].astype(\n",
343
+ " bool\n",
344
+ ")\n",
345
+ "merge_df[\"fpmm.isPendingArbitration\"] = merge_df[\n",
346
+ " \"fpmm.isPendingArbitration\"\n",
347
+ "].astype(bool)\n",
348
+ "\n",
349
+ "# Check for duplicates\n",
350
+ "print(f\"Initial length before removing duplicates in fpmmTrades= {len(merge_df)}\")\n",
351
+ "\n",
352
+ "# Remove duplicates\n",
353
+ "# fpmm.outcomes is a numpy array\n",
354
+ "merge_df.drop_duplicates(\"id\", keep=\"last\", inplace=True)"
355
+ ]
356
+ },
357
+ {
358
+ "cell_type": "code",
359
+ "execution_count": 29,
360
+ "metadata": {},
361
+ "outputs": [
362
+ {
363
+ "data": {
364
+ "text/plain": [
365
+ "Index(['collateralAmount', 'collateralAmountUSD', 'collateralToken',\n",
366
+ " 'creationTimestamp', 'trader_address', 'feeAmount', 'id',\n",
367
+ " 'oldOutcomeTokenMarginalPrice', 'outcomeIndex',\n",
368
+ " 'outcomeTokenMarginalPrice', 'outcomeTokensTraded', 'title',\n",
369
+ " 'transactionHash', 'type', 'market_creator',\n",
370
+ " 'fpmm.answerFinalizedTimestamp', 'fpmm.arbitrationOccurred',\n",
371
+ " 'fpmm.currentAnswer', 'fpmm.id', 'fpmm.isPendingArbitration',\n",
372
+ " 'fpmm.openingTimestamp', 'fpmm.outcomes', 'fpmm.title',\n",
373
+ " 'fpmm.condition.id', 'creation_timestamp', 'creation_date'],\n",
374
+ " dtype='object')"
375
+ ]
376
+ },
377
+ "execution_count": 29,
378
+ "metadata": {},
379
+ "output_type": "execute_result"
380
+ }
381
+ ],
382
+ "source": [
383
+ "merge_df.columns"
384
+ ]
385
+ },
386
+ {
387
+ "cell_type": "code",
388
+ "execution_count": 30,
389
+ "metadata": {},
390
+ "outputs": [
391
+ {
392
+ "data": {
393
+ "text/plain": [
394
+ "Timestamp('2025-01-14 00:00:00')"
395
+ ]
396
+ },
397
+ "execution_count": 30,
398
+ "metadata": {},
399
+ "output_type": "execute_result"
400
+ }
401
+ ],
402
+ "source": [
403
+ "max(merge_df.creation_date)"
404
+ ]
405
+ },
406
+ {
407
+ "cell_type": "code",
408
+ "execution_count": 31,
409
+ "metadata": {},
410
+ "outputs": [],
411
+ "source": [
412
+ "cutoff_date=\"2024-11-13\"\n",
413
+ "min_date_utc = pd.to_datetime(cutoff_date, format=\"%Y-%m-%d\", utc=True)"
414
+ ]
415
+ },
416
+ {
417
+ "cell_type": "code",
418
+ "execution_count": 32,
419
+ "metadata": {},
420
+ "outputs": [
421
+ {
422
+ "name": "stdout",
423
+ "output_type": "stream",
424
+ "text": [
425
+ "length before filtering 161781\n",
426
+ "length after filtering 160426\n"
427
+ ]
428
+ }
429
+ ],
430
+ "source": [
431
+ "merge_df[\"creation_timestamp\"] = pd.to_datetime(\n",
432
+ " merge_df[\"creation_timestamp\"], utc=True\n",
433
+ ")\n",
434
+ "\n",
435
+ "print(f\"length before filtering {len(merge_df)}\")\n",
436
+ "merge_df = merge_df.loc[merge_df[\"creation_timestamp\"] > min_date_utc]\n",
437
+ "print(f\"length after filtering {len(merge_df)}\")\n"
438
+ ]
439
+ },
440
+ {
441
+ "cell_type": "code",
442
+ "execution_count": 33,
443
+ "metadata": {},
444
+ "outputs": [
445
+ {
446
+ "data": {
447
+ "text/plain": [
448
+ "Timestamp('2025-01-14 00:00:00')"
449
+ ]
450
+ },
451
+ "execution_count": 33,
452
+ "metadata": {},
453
+ "output_type": "execute_result"
454
+ }
455
+ ],
456
+ "source": [
457
+ "max(merge_df.creation_date)"
458
+ ]
459
+ },
460
+ {
461
+ "cell_type": "code",
462
+ "execution_count": 34,
463
+ "metadata": {},
464
+ "outputs": [],
465
+ "source": [
466
+ "merge_df.to_parquet(\"../tmp/fpmmTrades.parquet\", index=False)"
467
+ ]
468
+ },
469
+ {
470
+ "cell_type": "code",
471
+ "execution_count": 15,
472
+ "metadata": {},
473
+ "outputs": [
474
+ {
475
+ "data": {
476
+ "text/plain": [
477
+ "Index(['collateralAmount', 'collateralAmountUSD', 'collateralToken',\n",
478
+ " 'creationTimestamp', 'trader_address', 'feeAmount', 'id',\n",
479
+ " 'oldOutcomeTokenMarginalPrice', 'outcomeIndex',\n",
480
+ " 'outcomeTokenMarginalPrice', 'outcomeTokensTraded', 'title',\n",
481
+ " 'transactionHash', 'type', 'market_creator',\n",
482
+ " 'fpmm.answerFinalizedTimestamp', 'fpmm.arbitrationOccurred',\n",
483
+ " 'fpmm.currentAnswer', 'fpmm.id', 'fpmm.isPendingArbitration',\n",
484
+ " 'fpmm.openingTimestamp', 'fpmm.outcomes', 'fpmm.title',\n",
485
+ " 'fpmm.condition.id', 'creation_timestamp', 'creation_date'],\n",
486
+ " dtype='object')"
487
+ ]
488
+ },
489
+ "execution_count": 15,
490
+ "metadata": {},
491
+ "output_type": "execute_result"
492
+ }
493
+ ],
494
+ "source": [
495
+ "missing_df = add_creation_date(missing_df)\n",
496
+ "missing_df.columns"
497
+ ]
498
+ },
499
+ {
500
+ "cell_type": "code",
501
+ "execution_count": 16,
502
+ "metadata": {},
503
+ "outputs": [
504
+ {
505
+ "data": {
506
+ "text/plain": [
507
+ "<Axes: xlabel='Count', ylabel='creation_date'>"
508
+ ]
509
+ },
510
+ "execution_count": 16,
511
+ "metadata": {},
512
+ "output_type": "execute_result"
513
+ },
514
+ {
515
+ "data": {
516
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmgAAAGwCAYAAAAdapmWAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAABg+UlEQVR4nO3de1yUdd4//tfIGRGJ4XBrEhkiKtaAg0ABtqvbLZ4NtZtd00VU9AZEO1mDlptIQJC4CioHS0t/siHKIw+1ra5JlmhhQEoZB02MdQRPgQzHuX5/cHt9G5EYccYZ5PV8POaxznX4XO/rvYQvr6NEEAQBRERERGQ0+hm6ACIiIiLSxIBGREREZGQY0IiIiIiMDAMaERERkZFhQCMiIiIyMgxoREREREaGAY2IiIjIyDCgERERERkZBjQiIiIiI2Nq6ALo/ly7Vg+12tBVPDwkEkAqHYCrV+vBd2zoDvuqH+yrfrCv+sG+drjdh+4woPVygoA+/YOuL+yrfrCv+sG+6gf7qh/sq3Z4ipOIiIjIyDCgERERERkZBjQiIiIiI8OARkRERGRkGNCIiIiIjAwDGhEREZGRYUAjIiIiMjIMaERERERGhgGNiIiIyMgwoBEREREZGQY0IiIiIiPDgEZERERkZBjQiIiIiIwMAxoRERGRkTE1dAF0fxoEQC10v5y1mQn6tbXrvyAiIiK6bwxovdyqvWegau0+eKXMkcFG8gAKIiIiovvGU5xERERERoYBjYiIiMjIMKARERERGRkGNCIiIiIjw4BGREREZGR4F2cvFx8yGmp198tZm5kAfMwGERFRr8CA1svZSAC1No/PYDgjIiLqNXiKk4iIiMjIMKARERERGRkGNCIiIiIjw4BGREREZGR4k0Avp+3L0u8FX6xORERkWAxovZy2L0u/F3yxOhERkWEZ9BSnUqlETEwMfH19ERQUhISEBDQ3NwMAqqurERYWBi8vL0yePBnHjx/XWDcvLw/BwcHw9vbGnDlzUFRUJM67efMmPDw8ND5+fn6/W8vXX3+NqVOnQiaTYf78+aiurr7rcqtXr8amTZu03sdvv/0WEyZM0JjW3t6OlJQUBAQEwNvbG8uXL0ddXZ3WYxIREdHDzWABTRAExMTEQKVSYdeuXUhNTcXRo0exYcMGCIKAqKgoODg4IC8vDzNmzEB0dDRqamoAAAUFBVi7di0iIyORn5+PgIAAREREQKlUAgAqKipgZ2eH48ePi59Dhw51WUtNTQ2ioqIQEhKCPXv2wN7eHpGRkRAEzXOHWVlZyM3N1Xofz507h+XLl3caJzMzE4cOHcKGDRuQm5uLmzdvYuXKlVqPS0RERA83gwW0qqoqFBcXIyEhAe7u7vDx8UFMTAwOHDiAwsJCVFdXY+3atXBzc8OSJUvg5eWFvLw8AMC+ffswc+ZMTJ8+Ha6urlixYgUcHBxw7NgxceyhQ4fC0dFR/Eil0i5ryc3NxejRoxEeHg53d3ckJCTgl19+walTpwAADQ0NiImJQVZWFgYNGqTV/uXk5CA0NPSu221vb4dCocDYsWMxbNgwzJs3T+MIIBEREfVtBgtojo6OyM7OhoODg8b0hoYGlJSUYNSoUbC2thany+VyFBcXAwAWLVqEBQsWdBqzvr4eQMcRtMcff1zrWkpKSuDj4yN+t7Kygqenp7i9S5cuobm5GXv37oWLi4tWYxYUFCApKQlhYWGd5kVHR+O5554DAFy9ehW5ubnw9fXVul4iIiJ6uBnsJgFbW1sEBQWJ39VqNXbu3Al/f3/U1tbCyclJY3mpVIrLly8DADw9PTXmFRQU4MKFC/D39wcAVFZWoq2tDbNnz4ZSqYSPjw8UCkWnMW/rbnsjRoxARkbGPe3f5s2bAQB79+7tcpmNGzciPT0dAwcOxO7du+9pfH2SSDo+fdHt/e6r+68v7Kt+sK/6wb7qB/vaQdv9N5q7OJOTk1FWVoY9e/Zg+/btMDc315hvbm6OlpaWTutdvHgRCoUC06ZNE4NbVVUV7O3toVAoIAgCUlNTsXTpUuTm5sLExKTTGCqVSuvt6dKMGTPwxz/+EdnZ2QgPD8fBgwdhY2NzT2MkzZZBx0/ZQH9LUzxibd79gg8xqXSAoUt4KLGv+sG+6gf7qh/sq3aMIqAlJydjx44dSE1NxfDhw2FhYYEbN25oLNPS0gJLS0uNaefPn8eCBQvg4uKCdevWidMPHjwIiUQiLr9x40YEBgaipKQE+fn52L9/v8ayFhYWncJYS0sLbG1tu61969atGkfXsrKyNE6X/h5XV1cAwLvvvotx48bh888/R0hIiFbr3maFdqjV97RKt9ob21HX2KzbQXsJiaTjl8fVq/UQdJ18+zD2VT/YV/1gX/WDfe1wuw/dMXhAi4uLw+7du5GcnIyJEycCAJydnVFRUaGxXF1dncZpyPLycoSFhcHFxQXZ2dka4c3KykpjXalUCjs7OyiVSixfvhwLFy4U5zk5OcHZ2bnTYy7q6uowcuTIbusPDQ3FpEmTxO/Ozs7drnP06FGMGjVKXNbCwgIuLi64fv16t+veSRDQp3/Q9YV91Q/2VT/YV/1gX/WDfdWOQZ+DlpaWhpycHKxfvx5TpkwRp8tkMpw9exZNTU3itKKiIshkMgDAlStXEB4eDldXV2zbtk3jtGBDQwPGjh2LwsJCcZpSqcT169fxxBNPQCqVwtXVVfyYmppCJpNp3EWpUqlQVlYmbu/32NnZaYx351G+u0lKSkJ+fr5GzRcuXICbm1u36xIREdHDz2ABrbKyEps3b8bixYshl8tRW1srfnx9fTFo0CAoFAqUl5cjMzMTpaWlmD17NoCOgKNWqxEfH4/GxkZxvVu3bsHGxgZyuRwJCQkoLS3F2bNn8dJLLyEoKAgeHh53rWXWrFk4ffo0MjMzUV5eDoVCgSFDhnT7cNuemjt3LrZt24Zjx46hvLwcr732Gh577DGMGzdOL9sjIiKi3sVgpziPHDmC9vZ2bNmyBVu2bNGYd+7cOWzevBmrVq1CSEgIXF1dkZ6ejsGDB0MQBBw+fBhNTU0IDg7WWC86OhrLli1DUlISEhMTERERgZaWFkyYMAGrV6/uspYhQ4Zg06ZNeOedd5Ceng5vb2+kp6dDoqdbTebOnQuVSoW//e1vuHbtGgICArBlyxb068d31xMREREgEe58zD31Klev1uv8JoG+TCIBHBwGoK6ub1/Eqmvsq36wr/rBvuoH+9rhdh+6w0M2REREREaGAY2IiIjIyDCgERERERkZBjQiIiIiI8OARkRERGRkGNCIiIiIjAwDGhEREZGRMfi7OOn+NAiA+gE/T8bazAT92tof7EaJiIj6EAa0Xm7V3jNQtT7YsJQyRwYb/bxkgYiIiMBTnERERERGhwGNiIiIyMgwoBEREREZGQY0IiIiIiPDmwR6ufiQ0VCrH+w2rc1MAN7FSUREpDcMaL2cjQRQP+g7KhnOiIiI9IqnOImIiIiMDAMaERERkZFhQCMiIiIyMgxoREREREaGAY2IiIjIyDCgERERERkZBjQiIiIiI8OARkRERGRkGNCIiIiIjAwDGhEREZGRYUAjIiIiMjIMaERERERGhi9L7+UaBEAtGGbb1mYm6McXpxMREemcwQOaUqlEfHw8CgsLYWFhgcmTJ+Pll1+GhYUFqqur8eabb6K4uBiDBw9GbGwsAgMDxXXz8vKQlZUFpVKJYcOG4Y033oBcLgcA3Lx5E76+vhrbsrOzw8mTJ7us5euvv8Y777yD6upqyGQyxMfHw8XFpdNyq1evhrOzM5YtW6bVPn777bd4/fXXceTIkU7ztmzZgp9//hmJiYlajXWnVXvPQNVqmJCUMkcGG4lBNk1ERPRQM+gpTkEQEBMTA5VKhV27diE1NRVHjx7Fhg0bIAgCoqKi4ODggLy8PMyYMQPR0dGoqakBABQUFGDt2rWIjIxEfn4+AgICEBERAaVSCQCoqKiAnZ0djh8/Ln4OHTrUZS01NTWIiopCSEgI9uzZA3t7e0RGRkIQNA9PZWVlITc3V+t9PHfuHJYvX95pHAA4cOAANm3apPVYRERE1DcY9AhaVVUViouL8dVXX8HBwQEAEBMTg6SkJIwbNw7V1dXIycmBtbU13NzccOLECeTl5WHZsmXYt28fZs6cienTpwMAVqxYgU8//RTHjh3DCy+8gKqqKgwdOhSOjo5a1ZKbm4vRo0cjPDwcAJCQkICAgACcOnUKfn5+aGhoQGxsLAoLCzFo0CCtxszJyUFSUhJcXFzQ0NAgTm9ra0NcXBz27dt31yN0RERE1LcZNKA5OjoiOztbDGe3NTQ0oKSkBKNGjYK1tbU4XS6Xo7i4GACwaNEi9O/fv9OY9fX1ADqOoD3++ONa11JSUgIfHx/xu5WVFTw9PVFcXAw/Pz9cunQJzc3N2Lt3LxQKhVZjFhQUICkpCQ0NDUhLSxOnNzY24ty5c/j444+xfft2rWs0NhJJx+dhcnt/Hrb9MjT2VT/YV/1gX/WDfe2g7f4bNKDZ2toiKChI/K5Wq7Fz5074+/ujtrYWTk5OGstLpVJcvnwZAODp6akxr6CgABcuXIC/vz8AoLKyEm1tbZg9ezaUSiV8fHygUCg6jXlbd9sbMWIEMjIy7mn/Nm/eDADYu3dvp/3Oycm5p7G6YmZmgjadjHTvTE1M4GBv3f2CvZBUOsDQJTyU2Ff9YF/1g33VD/ZVOwa/SeC3kpOTUVZWhj179mD79u0wNzfXmG9ubo6WlpZO6128eBEKhQLTpk0Tg1tVVRXs7e2hUCggCAJSU1OxdOlS5ObmwsTEpNMYKpVK6+0Zk79NG4m7XN72QJhJgLq6esNsXE8kko5fHlev1husrw8j9lU/2Ff9YF/1g33tcLsP3TGagJacnIwdO3YgNTUVw4cPh4WFBW7cuKGxTEtLCywtLTWmnT9/HgsWLICLiwvWrVsnTj948CAkEom4/MaNGxEYGIiSkhLk5+dj//79GstaWFh0CmMtLS2wtbXttvatW7dqHF3LysrSOF2qTzYSwz1mA23teFj/GxME9OlfIPrCvuoH+6of7Kt+sK/aMYqAFhcXh927dyM5ORkTJ04EADg7O6OiokJjubq6Oo3TkOXl5QgLC4OLiwuys7M1wpuVlZXGulKpFHZ2dlAqlVi+fDkWLlwoznNycoKzszPq6uo6bW/kyJHd1h8aGopJkyaJ352dnbXYayIiIqK7M/ibBNLS0pCTk4P169djypQp4nSZTIazZ8+iqalJnFZUVASZTAYAuHLlCsLDw+Hq6opt27bBxsZGXK6hoQFjx45FYWGhOE2pVOL69et44oknIJVK4erqKn5MTU0hk8lQVFQkLq9SqVBWViZu7/fY2dlpjHfnUT4iIiKie2HQgFZZWYnNmzdj8eLFkMvlqK2tFT++vr4YNGgQFAoFysvLkZmZidLSUsyePRsAkJSUBLVajfj4eDQ2Norr3bp1CzY2NpDL5UhISEBpaSnOnj2Ll156CUFBQfDw8LhrLbNmzcLp06eRmZmJ8vJyKBQKDBkyBH5+fg+yJURERESGDWhHjhxBe3s7tmzZgsDAQI2PiYkJNm/ejNraWoSEhOCTTz5Beno6Bg8eDEEQcPjwYdTV1SE4OFhjvffffx9AR4AbNWoUIiIiMG/ePDz66KNISUnpspYhQ4Zg06ZNyMvLw+zZs3Hjxg2kp6dD0tfvByYiIqIHTiLc7RH31GtcvVoPtdrQVTw8JBLAwWEA6ur69l1Gusa+6gf7qh/sq36wrx1u96E7Br8GjYiIiIg0MaARERERGRkGNCIiIiIjw4BGREREZGQY0IiIiIiMDAMaERERkZFhQCMiIiIyMkbxLk7quQbhwb0s3drMBP3a2h/MxoiIiPowBrRebtXeM1C1PpjQlDJHBhu+WIGIiEjveIqTiIiIyMgwoBEREREZGQY0IiIiIiPDgEZERERkZBjQiIiIiIwM7+Ls5eJDRkOtfjDbsjYzAfiYDSIiIr1jQOvlbCSA+kE9+oLhjIiI6IHgKU4iIiIiI8OARkRERGRkGNCIiIiIjAwDGhEREZGRYUAjIiIiMjIMaERERERGhgGNiIiIyMgwoBEREREZGQY0IiIiIiPDgEZERERkZBjQiIiIiIyMQQOaUqlETEwMfH19ERQUhISEBDQ3NwMAqqurERYWBi8vL0yePBnHjx/XWDcvLw/BwcHw9vbGnDlzUFRUJM67efMmPDw8ND5+fn6/W8vXX3+NqVOnQiaTYf78+aiurr7rcqtXr8amTZu63beysjLMmTMHMpkMs2bNwpkzZ8R5giBg27ZtGD9+PHx8fKBQKHDr1q1ux7ybBsGwH7WpSY/qJiIioq4Z7GXpgiAgJiYGtra22LVrF27evInY2Fj069cPK1euRFRUFIYPH468vDwcPnwY0dHROHToEAYPHoyCggKsXbsWcXFxkMlk2LdvHyIiInDo0CE4OzujoqICdnZ2OHDggLi9fv26zqI1NTWIiorCsmXLEBQUhPT0dERGRuKTTz6BRPL/3kSelZWF3NxcREdH/+6+NTY2IiIiAtOmTUNiYiJ2796NJUuW4F//+hesra3xj3/8A2lpaYiLi4OHhwcSEhLwyiuvYOvWrffcx1V7z0DVariXmKfMkcHmQb2snYiIqI8w2BG0qqoqFBcXIyEhAe7u7vDx8UFMTAwOHDiAwsJCVFdXY+3atXBzc8OSJUvg5eWFvLw8AMC+ffswc+ZMTJ8+Ha6urlixYgUcHBxw7NgxceyhQ4fC0dFR/Eil0i5ryc3NxejRoxEeHg53d3ckJCTgl19+walTpwAADQ0NiImJQVZWFgYNGtTtvh06dAgWFhZYuXIl3NzcsGrVKvTv3x+fffYZAGDnzp1YsGABpk6dCnd3dyQmJuKLL75AVVXV/baViIiIHgIGC2iOjo7Izs6Gg4ODxvSGhgaUlJRg1KhRsLa2FqfL5XIUFxcDABYtWoQFCxZ0GrO+vh4AUFFRgccff1zrWkpKSuDj4yN+t7Kygqenp7i9S5cuobm5GXv37oWLi4tW48nlcvHom0QiwZgxY8TxqqurIZPJxOWdnJxgb28vziciIqK+zWCnOG1tbREUFCR+V6vV2LlzJ/z9/VFbWwsnJyeN5aVSKS5fvgwA8PT01JhXUFCACxcuwN/fHwBQWVmJtrY2zJ49G0qlUrzO684xb+tueyNGjEBGRobW+1ZbW4thw4Z1Gq+8vFz8s1KpFOc1Njbi5s2buH79utbbMBYSScfnYXF7Xx6mfTIG7Kt+sK/6wb7qB/vaQdv9N1hAu1NycjLKysqwZ88ebN++Hebm5hrzzc3N0dLS0mm9ixcvQqFQYNq0aWJwq6qqgr29PRQKBQRBQGpqKpYuXYrc3FyYmHS+qF2lUmm9PW10N97kyZORkZEBuVyOIUOGIDExEQDQ2tp6z9syMzNBW4+q1A1TExM42Ft3v2AvI5UOMHQJDyX2VT/YV/1gX/WDfdWOUQS05ORk7NixA6mpqRg+fDgsLCxw48YNjWVaWlpgaWmpMe38+fNYsGABXFxcsG7dOnH6wYMHIZFIxOU3btyIwMBAlJSUID8/H/v379dY1sLColMYa2lpga2tbbe1b926VePoWlZWVpfj3a4nMjIS1dXVmDJlCkxNTREaGooRI0bAxsam2+3dqbW1Ha0GvEmgrb0ddXX1Btu+rkkkHb88rl6thyAYupqHB/uqH+yrfrCv+sG+drjdh+4YPKDFxcVh9+7dSE5OxsSJEwFAvBPzt+rq6jROQ5aXlyMsLAwuLi7Izs7WCG9WVlYa60qlUtjZ2UGpVGL58uVYuHChOM/JyQnOzs6oq6vrtL2RI0d2W39oaCgmTZokfnd2du5yvNv1W1tb4+9//zvq6+shkUhgY2ODp59+Go8++mi327tTfMhoqNX3vJrOWJmaQGgzXEDUF0FAn/4Foi/sq36wr/rBvuoH+6odgz4HLS0tDTk5OVi/fj2mTJkiTpfJZDh79iyamprEaUVFReKF9VeuXEF4eDhcXV2xbds2jSNPDQ0NGDt2LAoLC8VpSqUS169fxxNPPAGpVApXV1fxY2pqCplMpvEcNZVKhbKyMo0L+btiZ2enMZ6lpSVkMhm+++47CP/3EygIAk6fPi2O9+6772Lfvn0YMGAAbGxsUFpaivr6enh7e99zD20khv30ewjDGRERkaEZLKBVVlZi8+bNWLx4MeRyOWpra8WPr68vBg0aBIVCgfLycmRmZqK0tBSzZ88GACQlJUGtViM+Ph6NjY3ierdu3YKNjQ3kcjkSEhJQWlqKs2fP4qWXXkJQUBA8PDzuWsusWbNw+vRpZGZmory8HAqFAkOGDOn24bZdCQ4Oxq+//or4+HhUVFQgPj4eKpVKPNLm5OSEtLQ0lJaW4syZM3jttdfw5z//GXZ2dj3aHhERET1cJIJgmAONmZmZeO+99+4679y5c/j555+xatUqlJSUwNXVFbGxsXjmmWcgCAK8vLw0jq7dFh0djWXLluHmzZtITEzE0aNH0dLSggkTJmD16tUYOHBgl/UcO3YM77zzDi5fvgxvb2/ExcXd9ZEa8+bNg6+vL5YtW/a7+1daWoo1a9agsrISHh4eePvttzFq1CgAQHt7OxITE/HJJ5+gX79+mDFjBl599VWYmt77GeerV+sNeorzYSORAA4OA1BX17evkdA19lU/2Ff9YF/1g33tcLsP3S5nqIBGusGAplv8BaIf7Kt+sK/6wb7qB/vaQduAxpelExERERkZBjQiIiIiI8OARkRERGRkGNCIiIiIjAwDGhEREZGRYUAjIiIiMjIMaERERERGhgGNiIiIyMgY/GXpdH8aBED9gB74Z21mwndvEhERPQAMaL3cqr1noGp9MKEpZY4MNpIHsikiIqI+jac4iYiIiIwMAxoRERGRkWFAIyIiIjIyDGhERERERoYBjYiIiMjI8C7OXi4+ZDTU6gezLWszE4CP2SAiItI7BrRezkYCqB/Uoy8YzoiIiB4InuIkIiIiMjIMaERERERGhgGNiIiIyMgwoBEREREZGQY0IiIiIiPDgEZERERkZHoc0Kqrq5GUlITIyEhcuXIFe/bswbfffqvL2oiIiIj6pB4FtG+++QbTp0/HL7/8gi+//BLNzc2oqqpCWFgYPv/8c13XSERERNSn9CigJScn45VXXsHGjRthatrxrNuVK1fi1VdfxcaNG3VaIBEREVFf06OA9tNPP+HZZ5/tNH3ChAm4ePHifRdFRERE1Jf1KKA9+uij+P777ztN/+KLL/Doo4/ed1FEREREfVmPAtqKFSvw5ptvIikpCe3t7cjPz8frr7+OpKQkLFu2TOtxlEolYmJi4Ovri6CgICQkJKC5uRlAx00IYWFh8PLywuTJk3H8+HGNdfPy8hAcHAxvb2/MmTMHRUVF4rybN2/Cw8ND4+Pn5/e7tXz99deYOnUqZDIZ5s+fj+rq6rsut3r1amzatKnbfSsrK8OcOXMgk8kwa9YsnDlzRpx3Z223P/n5+d2Oe6cGQXcftanJPW+fiIiIdK9HL0t/7rnn4OLigvfffx/u7u44cuQIhg4dil27dkEmk2k1hiAIiImJga2tLXbt2oWbN28iNjYW/fr1w8qVKxEVFYXhw4cjLy8Phw8fRnR0NA4dOoTBgwejoKAAa9euRVxcHGQyGfbt24eIiAgcOnQIzs7OqKiogJ2dHQ4cOCBur1+/rrNoTU0NoqKisGzZMgQFBSE9PR2RkZH45JNPIJH8vzeRZ2VlITc3F9HR0b+7b42NjYiIiMC0adOQmJiI3bt3Y8mSJfjXv/4Fa2vrTmFz+/bt+PTTTzFhwgStevdbq/aegapVNy8xT5kjg82DevE6ERERdalHAS0/Px+TJ0/Gu+++qzG9sbERH374IebPn9/tGFVVVSguLsZXX30FBwcHAEBMTAySkpIwbtw4VFdXIycnB9bW1nBzc8OJEyeQl5eHZcuWYd++fZg5cyamT58OoOOI3qeffopjx47hhRdeQFVVFYYOHQpHR0et9ic3NxejR49GeHg4ACAhIQEBAQE4deoU/Pz80NDQgNjYWBQWFmLQoEHdjnfo0CFYWFhg5cqVkEgkWLVqFQoKCvDZZ58hJCREo67q6mp89NFH2Lp1KwYMGKBVvURERPRw0zqgXbt2DU1NTQAAhUIBd3d3PPLIIxrL/Pjjj0hJSdEqoDk6OiI7O1sMZ7c1NDSgpKQEo0aNgrW1tThdLpejuLgYALBo0SL079+/05j19fUAgIqKCjz++OPa7hpKSkrg4+MjfreysoKnpyeKi4vh5+eHS5cuobm5GXv37oVCodBqPLlcLh59k0gkGDNmDIqLixESEqKx7MaNG/H000/jmWee0bpefZFIOj592e397+t90DX2VT/YV/1gX/WDfe2g7f5rHdBOnTqFFStWiKFj9uzZADpOVUokEgiCAADiUa3u2NraIigoSPyuVquxc+dO+Pv7o7a2Fk5OThrLS6VSXL58GQDg6empMa+goAAXLlyAv78/AKCyshJtbW2YPXs2lEolfHx8oFAoOo15W3fbGzFiBDIyMrTar9vjDRs2rNN45eXlGtNqampw4MAB5OTkaD32nczMTNDW47U1mZqYwMHeuvsF+wCplEcz9YF91Q/2VT/YV/1gX7WjdUALDg7Gv//9b6jVavzpT39Cbm4u7O3txfkSiQRWVladjqppKzk5GWVlZdizZw+2b98Oc3Nzjfnm5uZoaWnptN7FixehUCgwbdo0MbhVVVXB3t4eCoUCgiAgNTUVS5cuRW5uLkxMOl8Ir1KptN6eNrQdb8+ePRg9erTW1+3dTWtrO1p1dA1aW3s76urqdTJWbyWRdPzyuHq1Hv/3bw7SAfZVP9hX/WBf9YN97XC7D925p2vQBg8eDKDjVGZXWltbYWZmdi/DIjk5GTt27EBqaiqGDx8OCwsL3LhxQ2OZlpYWWFpaakw7f/48FixYABcXF6xbt06cfvDgQUgkEnH5jRs3IjAwECUlJcjPz8f+/fs1lrWwsOgUnlpaWmBra9tt7Vu3btU4upaVldXleHfW/89//hOhoaHdbuNBEQSgD/83o0EQ0Kd/gegL+6of7Kt+sK/6wb5qp0c3CdTV1SEjIwMVFRVob+84eiMIAlpbW1FZWYlvvvlG67Hi4uKwe/duJCcnY+LEiQAg3ol55zZ/exqyvLwcYWFhcHFxQXZ2tkb4sbKy0lhXKpXCzs4OSqUSy5cvx8KFC8V5Tk5OcHZ2Rl1dXaftjRw5stv6Q0NDMWnSJPG7s7Nzl+P9tv7//Oc/qKio6NGdm78VHzIaavV9DSGyNjMB2nRzNI6IiIh6rkfPQYuNjcWXX36JJ598EqdPn4ZMJoO9vT1KS0vv6TloaWlpyMnJwfr16zFlyhRxukwmw9mzZ8WbEgCgqKhIPBV45coVhIeHw9XVFdu2bYONjY24XENDA8aOHYvCwkJxmlKpxPXr1/HEE09AKpXC1dVV/JiamkImk2k8R02lUqGsrEyrU492dnYa41laWkImk+G7774Tr8sTBEHs020lJSUYNGiQeFSyp2wkuvv0YzgjIiIyCj1+WXpCQgJefvlleHh44A9/+AP+/ve/Y8WKFSgoKNBqjMrKSmzevBmLFy+GXC5HbW2t+PH19cWgQYOgUChQXl6OzMxMlJaWijcmJCUlQa1WIz4+Ho2NjeJ6t27dgo2NDeRyORISElBaWoqzZ8/ipZdeQlBQEDw8PO5ay6xZs3D69GlkZmaivLwcCoUCQ4YM6fbhtl0JDg7Gr7/+ivj4eFRUVCA+Ph4qlUrjSFt5eTnc3Nx6ND4RERE93HoU0ARBgLOzMwBg2LBhKCsrAwBMmjTprq+AupsjR46gvb0dW7ZsQWBgoMbHxMQEmzdvRm1tLUJCQvDJJ58gPT0dgwcPhiAIOHz4MOrq6hAcHKyx3vvvvw+gI8CNGjUKERERmDdvHh599FGkpKR0WcuQIUOwadMm5OXlYfbs2bhx4wbS09M1HlJ7L2xsbJCRkYGioiKEhISgpKQEmZmZGo8Nqaurw8CBA3s0PhERET3cJIJw75fq/eUvf0FQUBD+93//F9u3b0dhYSG2bt2KEydOYMWKFTh58qQ+aqW7uHq1XmfXoFHH3TUODgNQV9e37zLSNfZVP9hX/WBf9YN97XC7D93p0U0Cr7zyCpYuXQorKyvMmDED2dnZmDZtGmpqarR+DhoRERER3V2PAppcLsfRo0fR1NSERx55RHxfpp2dncZ1VkRERER073oU0ICO66xu3z3p7OyMuXPn6qwoIiIior5M64A2YsQIrS+a/+GHH3pcEBEREVFfp3VA+/DDD8U/f//99/jggw8QGRmJJ598EmZmZigrK0NaWppWL0onIiIioq5pHdB8fX3FP7/11ltISkpCQECAOG3EiBF49NFHoVAoEBYWptMiiYiIiPqSHj0H7cqVK5BKpZ2mW1lZ4ddff73vooiIiIj6sh4FtD/84Q+IjY3F6dOn0djYiFu3bqGwsBCxsbG8i5OIiIjoPvXoQbUNDQ1Ys2YNPvvsM6j/7ymppqammDFjBt58801YWFjovFC6u5/r+KBaXZJIAFMTE7S1t/fpBynqGvuqH4bqq7WZyUP97l4+UFU/2NcO2j6otkcB7baGhgacP38eADB06FCNl5YDwIEDBzB+/HiNVxyRbr2YeQKq1of3F6UhmJmZoJU91Tn2VT8M0deUOTLY9OxNeL0Cg4R+sK8d9PomgdtsbGzw5JNPdjn/rbfegkwmY0AjIiIiugc9ugZNW/dxcI6IiIioz9JrQCMiIiKie8eARkRERGRkGNCIiIiIjMx93SRAhhcfMpqP2dAhPg5CP9hX/TDkYzbwED9mg8gYMKD1cjYSQP0Q3+7+oEkkgIO9dcdt4IYu5iHCvuqHwfrKcEakd3o9xRkQEAArKyt9boKIiIjoodPjI2gnTpzA999/j9bW1k6P04iOjgYApKWl3V91RERERH1QjwJaYmIiPvzwQ4wYMQL9+/fXmCeR8HwbERER0f3oUUDLy8tDYmIipk+frut6iIiIiPq8Hl2DZmJigqeeekrXtRARERERehjQ5s6di02bNqGxsVHX9RARERH1eT06xXnq1Cl89913+OyzzyCVSmFmZqYx/8iRIzopjoiIiKgv6lFACwkJQUhIiK5rISIiIiL0MKA9//zzAACVSoWff/4ZarUajz32GGxsbHRaHBEREVFf1KOA1traiuTkZPx//9//h/b2dgiCAFNTU0ybNg1vv/02zM3NdV0nERERUZ/Ro5sEkpKScPToUWzZsgXffPMNTp06hfT0dHz77bdITU3VehylUomYmBj4+voiKCgICQkJaG5uBgBUV1cjLCwMXl5emDx5Mo4fP66xbl5eHoKDg+Ht7Y05c+agqKhInHfz5k14eHhofPz8/H63lq+//hpTp06FTCbD/PnzUV1dfdflVq9ejU2bNnW7b2VlZZgzZw5kMhlmzZqFM2fOaMz38fHpVOOtW7e6HZeIiIgefj0KaAcOHMC6desQFBQEGxsb2Nra4tlnn0VcXBz279+v1RiCICAmJgYqlQq7du1Camoqjh49ig0bNkAQBERFRcHBwQF5eXmYMWMGoqOjUVNTAwAoKCjA2rVrERkZifz8fAQEBCAiIgJKpRIAUFFRATs7Oxw/flz8HDp0qMtaampqEBUVhZCQEOzZswf29vaIjIzs9IaErKws5ObmdrtvjY2NiIiIgI+PD/bu3Qtvb28sWbJEvOtVqVSivr4ehw8f1qjR2tpaq94RERHRw61HpzgFQYBUKu003d7eXuujQFVVVSguLsZXX30FBwcHAEBMTAySkpIwbtw4VFdXIycnB9bW1nBzc8OJEyeQl5eHZcuWYd++fZg5c6b4oNwVK1bg008/xbFjx/DCCy+gqqoKQ4cOhaOjo1a15ObmYvTo0QgPDwcAJCQkICAgAKdOnYKfnx8aGhoQGxuLwsJCDBo0qNvxDh06BAsLC6xcuRISiQSrVq1CQUEBPvvsM4SEhKCyshKOjo5wcXHRqr7f0yAAar59WmckAJquNaJNAAT2VWfYV/1gX7VjbWaCfnzBO/UyPQpo/v7+SElJQUpKinhjwK+//or169d3eyrxNkdHR2RnZ4vh7LaGhgaUlJRg1KhRGkeU5HI5iouLAQCLFi3q9IopAKivrwfQcQTt8ccf13p/SkpK4OPjI363srKCp6cniouL4efnh0uXLqG5uRl79+6FQqHQajy5XC6+9koikWDMmDEoLi5GSEgIKioqMHToUK3r+z2r9p6BqpW/eHTJzMwEreypzrGv+sG+di9ljgw2fAsh9TI9CmixsbGYP38+goKCxKBx/vx5uLi4YMuWLVqNYWtri6CgIPG7Wq3Gzp074e/vj9raWjg5OWksL5VKcfnyZQCAp6enxryCggJcuHAB/v7+AIDKykq0tbVh9uzZUCqV8PHxgUKh6DTmbd1tb8SIEcjIyNBqv26PN2zYsE7jlZeXi/WpVCrMmzcP58+fx8iRIxEbG6uz0EZERP+PRNLxuZflf/u/pBvsawdt979HAc3Z2RkHDhxAQUEBqqqqYGFhgaFDhyIgIAD9+vXosjYkJyejrKwMe/bswfbt2zvdCWpubo6WlpZO6128eBEKhQLTpk0Tg1tVVRXs7e2hUCggCAJSU1OxdOlS5ObmwsTEpNMYKpVK6+1po7vxqqqqcPPmTbz88suwsbFBVlYWwsLCcPDgwXt+VImZmQnaelQl/R4zs84/J3T/2Ff9YF9/n6mJCRzs7/0aX6l0gB6qIfZVOz0KaABgZmaGCRMmYMKECfddRHJyMnbs2IHU1FQMHz4cFhYWuHHjhsYyLS0tsLS01Jh2/vx5LFiwAC4uLli3bp04/eDBg5BIJOLyGzduRGBgIEpKSpCfn69xI8PBgwdhYWHRKYy1tLTA1ta229q3bt2qcXQtKyury/Fu17Nt2za0traKp2lTUlLw7LPP4ujRo5g2bVq32/yt1tZ2nt7QMZ4y0g/2VT/Y1+61tbejrq5e6+Ulko4QcfVqPa/t0yH2tcPtPnRH64A2cuRIHD9+HFKpFCNGjBCvr7qbH374QdthERcXh927dyM5ORkTJ04E0HGErqKiQmO5uro6jdOQ5eXlCAsLg4uLC7KzszXCm5WVlca6UqkUdnZ2UCqVWL58ORYuXCjOc3JygrOzM+rq6jptb+TIkd3WHxoaikmTJonfnZ2duxzvdv3m5uYaR9gsLCwwZMgQ8S5UIiLSHUEAepIHBN58oRfsq3a0Dmg7duzAwIEDAQAffvihTjaelpaGnJwcrF+/HsHBweJ0mUyGzMxMNDU1icGrqKgIcrkcAHDlyhWEh4fD1dUVWVlZGjcMNDQ04I9//CM2bdokXpOmVCpx/fp1PPHEE5BKpZ3uQJXJZBrPUVOpVCgrK0N0dHS3+2BnZwc7O7tO42VlZUEQBEgkEgiCgNOnT2Pp0qUQBAHPPfccIiMjxddlNTY24ueff8YTTzxxD93rEB8yGmr1Pa9GXZBIOk6HtLW38xeIDrGv+sG+asfazATgXZzUy2gd0Hx9fcU/79u3D6tWrep0vdTNmzfx5ptvaizblcrKSmzevBkRERGQy+Wora3V2NagQYOgUCgQGRmJo0ePorS0FAkJCQA6HpSrVqsRHx+PxsZG8fli1tbWsLGxgVwuR0JCAuLi4mBiYoL4+HgEBQXBw8PjrrXMmjUL27ZtQ2ZmJv74xz8iPT0dQ4YM0fqO1DsFBwfjvffeQ3x8PEJDQ5GTkwOVSoVJkyZBIpHgD3/4AzZt2oRHH30U9vb2+Pvf/47/+q//wrPPPnvP27KRAOo+fsGlLkkkgIO9Nerq6nv0L266O/ZVP9hXLTGcUS+kdUD77rvv8PPPPwMA8vPz4enp2SmgVVVVdXrif1eOHDmC9vZ2bNmypdOdn+fOncPmzZuxatUqhISEwNXVFenp6Rg8eDAEQcDhw4fR1NSkcdQNAKKjo7Fs2TIkJSUhMTERERERaGlpwYQJE7B69eouaxkyZAg2bdqEd955B+np6fD29kZ6evrvnsb9PTY2NsjIyMCaNWvw8ccfw8PDA5mZmeJjQ1577TWYmprilVdeQUNDA/z9/ZGZmXnXGxiIiIio75EIdz4uvws//vgjoqKiIAgCampq8F//9V8ad2xKJBJYW1vjz3/+M/7yl7/orWDSdPVqPU9x6pBEAjg4DOg4IsFDEjrDvuoH+6of7Kt+sK8dbvehO1ofQRsxYgSOHDkCAJg3bx7S0tLEa9KIiIiISHd69NCyjz766K7hrKWlBSUlJfddFBEREVFf1qPnoH333Xf429/+hoqKCqjvOL9mYmKCM2fO6KQ4IiIior6oR0fQ4uLi8Oijj2Lr1q2wsrLCpk2bsHr1atjZ2eHdd9/VdY1EREREfUqPjqCVl5cjOTkZbm5u8PT0hJmZGebOnQupVIqsrCxMnjxZ13USERER9Rk9OoJmZWUlPhLiiSeewLlz5wAATz31FM6fP6+76oiIiIj6oB4FNH9/f7z33ntQKpXw9vbGoUOHcOPGDfz73//W6v2VRERERNS1HgW0VatW4ebNm/j8888xZcoU2NjYwN/fHwkJCYiKitJ1jURERER9So+uQXN2dtZ4H+dHH32EiooK2NrawtnZWWfFEREREfVFPTqCBgD19fXYtWsX1q1bh+vXr+PSpUtobm7WZW1EREREfVKPjqD99NNP+Otf/4pBgwaJf/7888/x2WefISMjQ6uXpZNuNAiAug+/MkPXJACarjWiTUCffhXJbdZmJujHF00TET1wPQpo69atw5///GfExMTA29sbAJCQkAB7e3u8++672LNnj06LpK6t2nsGqlb+BapLZmYmaGVPAQApc2SwkRi6CiKivqdHpzi///57zJw5s9P00NBQVFRU3G9NRERERH1ajwKavb39XZ93dvr0aUil0vsuioiIiKgv69EpzsWLF2P16tVYunQpBEFAYWEh9u3bhx07duCll17SdY1EREREfUqPAlpoaCicnJywbds2WFpa4t1338XQoUMRFxfH1zwRERER3aceBbTs7GxMnToVu3bt0nU9RERERH1ejwLa1q1bMXHiRF3XQj0QHzIaarWhq3h4SCSAqYkJ2trb+ZgNdDxmA3zMBhHRA9ejgDZ16lRs2bIFERERGDx4MMzNzXVdF2nJRgKo+RgEnZFIAAd7a9TV1YP5DAxnREQG0qOAVlBQgJqaGuzbt09juiAIkEgk+OGHH3RSHBEREVFf1KOAlpiYiMbGRvTr1w+WlpYQBAHx8fF45ZVXYGVlpesaiYiIiPqUHj0H7dy5c3j55Zdx9epV+Pr6ws/PDwEBAXjppZdw4cIFHZdIRERE1Lf0KKB98MEHeO+99/D888+L015//XUkJycjMzNTZ8URERER9UU9CmjXr1/HY4891mn60KFDUVdXd99FEREREfVlPQpocrkcmzZtgkqlEqc1Nzdj69at4svTiYiIiKhnenSTwFtvvYXw8HAEBgbi8ccfBwBcvHgRDg4O2Lx5sy7rIyIiIupzehTQHnvsMRw6dAhffvklLly4AFNTUzz++OMIDAyEiYmJrmskIiIi6lN6FNAAwNzcHBMmTNBlLURERESEHl6DpitKpRIxMTHw9fVFUFAQEhIS0NzcDACorq5GWFgYvLy8MHnyZBw/flxj3by8PAQHB8Pb2xtz5sxBUVGROO/mzZvw8PDQ+Pj5+f1uLV9//TWmTp0KmUyG+fPno7q6+q7LrV69Gps2bep238rKyjBnzhzIZDLMmjULZ86cEee1t7cjJSUFAQEB8Pb2xvLly3lzBREREYkMFtAEQUBMTAxUKhV27dqF1NRUHD16FBs2bIAgCIiKioKDgwPy8vIwY8YMREdHo6amBkDHmwzWrl2LyMhI5OfnIyAgABEREVAqlQCAiooK2NnZ4fjx4+Ln0KFDXdZSU1ODqKgohISEYM+ePbC3t0dkZCSEO17GmJWVhdzc3G73rbGxEREREfDx8cHevXvh7e2NJUuWoLGxEQCQmZmJQ4cOYcOGDcjNzcXNmzexcuXKnraSiIiIHjIGC2hVVVUoLi5GQkIC3N3d4ePjg5iYGBw4cACFhYWorq7G2rVr4ebmhiVLlsDLywt5eXkAgH379mHmzJmYPn06XF1dsWLFCjg4OODYsWPi2EOHDoWjo6P4kUqlXdaSm5uL0aNHIzw8HO7u7khISMAvv/yCU6dOAQAaGhoQExODrKwsDBo0qNt9O3ToECwsLLBy5Uq4ublh1apV6N+/Pz777DMAHUfQFAoFxo4di2HDhmHevHkaRwCJiIiob+vxNWj3y9HREdnZ2XBwcNCY3tDQgJKSEowaNQrW1tbidLlcjuLiYgDAokWL0L9//05j1tfXA+g4gnb77lJtlJSUwMfHR/xuZWUFT09PFBcXw8/PD5cuXUJzczP27t0LhUKh1XhyuRwSScdbzCUSCcaMGYPi4mKEhIQgOjpaXPbq1avIzc2Fr6+v1vX+lkTS8SHduN1L9lS32Ff9YF/1g33VD/a1g7b7b7CAZmtri6CgIPG7Wq3Gzp074e/vj9raWjg5OWksL5VKcfnyZQCAp6enxryCggJcuHAB/v7+AIDKykq0tbVh9uzZUCqV8PHxgUKh6DTmbd1tb8SIEcjIyNB632prazFs2LBO45WXl2tM27hxI9LT0zFw4EDs3r1b6/F/y95+QI/Wo98nlbKv+sC+6gf7qh/sq36wr9oxWEC7U3JyMsrKyrBnzx5s374d5ubmGvPNzc3R0tLSab2LFy9CoVBg2rRpYnCrqqqCvb09FAoFBEFAamoqli5ditzc3Ls+BkSlUmm9PW1oO96MGTPwxz/+EdnZ2QgPD8fBgwdhY2NzT9u6dq0eanWPyqS7kEg6fnlcvVqPOy5BpPvAvuoH+6of7Kt+sK8dbvehO0YR0JKTk7Fjxw6kpqZi+PDhsLCwwI0bNzSWaWlpgaWlpca08+fPY8GCBXBxccG6devE6QcPHoREIhGX37hxIwIDA1FSUoL8/Hzs379fY1kLC4tO4amlpQW2trbd1r5161aNo2tZWVldjndn/a6urgCAd999F+PGjcPnn3+OkJCQbrf5W4KAPv2Dri/sq36wr/rBvuoH+6of7Kt2DB7Q4uLisHv3biQnJ2PixIkAAGdnZ1RUVGgsV1dXp3Easry8HGFhYXBxcUF2drZG+LGystJYVyqVws7ODkqlEsuXL8fChQvFeU5OTnB2du70mIu6ujqMHDmy2/pDQ0MxadIk8buzs3OX492u/+jRoxg1ahScnZ0BABYWFnBxccH169e73R4RERE9/Az6HLS0tDTk5ORg/fr1mDJlijhdJpPh7NmzaGpqEqcVFRVBJpMBAK5cuYLw8HC4urpi27ZtGqcFGxoaMHbsWBQWForTlEolrl+/jieeeAJSqRSurq7ix9TUFDKZTOMuSpVKhbKyMnF7v8fOzk5jPEtLS8hkMnz33XfiYzoEQcDp06fF8ZKSkpCfn69R84ULF+Dm5naPHSQiIqKHkcECWmVlJTZv3ozFixdDLpejtrZW/Pj6+mLQoEFQKBQoLy9HZmYmSktLMXv2bAAdAUetViM+Ph6NjY3ierdu3YKNjQ3kcjkSEhJQWlqKs2fP4qWXXkJQUBA8PDzuWsusWbNw+vRpZGZmory8HAqFAkOGDOn24bZdCQ4Oxq+//or4+HhUVFQgPj4eKpVKPNI2d+5cbNu2DceOHUN5eTlee+01PPbYYxg3blzPmklEREQPFYlw59NYH5DMzEy89957d5137tw5/Pzzz1i1ahVKSkrg6uqK2NhYPPPMMxAEAV5eXhpH126Ljo7GsmXLcPPmTSQmJuLo0aNoaWnBhAkTsHr1agwcOLDLeo4dO4Z33nkHly9fhre3N+Li4uDi4tJpuXnz5sHX1xfLli373f0rLS3FmjVrUFlZCQ8PD7z99tsYNWoUgI47VrOzs7F7925cu3YNAQEBWLNmjXjK815cvcqbBHRJIgEcHAagrq5vX8Sqa+yrfrCv+sG+6gf72uF2H7pdzlABjXSDAU23+AtEP9hX/WBf9YN91Q/2tYO2Ac2g16ARERERUWcMaERERERGhgGNiIiIyMgwoBEREREZGQY0IiIiIiPDgEZERERkZBjQiIiIiIwMAxoRERGRkWFAIyIiIjIyDGhERERERoYBjYiIiMjIMKARERERGRkGNCIiIiIjw4BGREREZGQY0IiIiIiMDAMaERERkZFhQCMiIiIyMgxoREREREaGAY2IiIjIyDCgERERERkZBjQiIiIiI8OARkRERGRkGNCIiIiIjAwDGhEREZGRYUAjIiIiMjIMaERERERGhgGNiIiIyMgYNKAplUrExMTA19cXQUFBSEhIQHNzMwCguroaYWFh8PLywuTJk3H8+HGNdfPy8hAcHAxvb2/MmTMHRUVF4rybN2/Cw8ND4+Pn5/e7tXz99deYOnUqZDIZ5s+fj+rq6rsut3r1amzatKnbfSsrK8OcOXMgk8kwa9YsnDlz5q7LbdmyBW+88Ua34xEREVHfYbCAJggCYmJioFKpsGvXLqSmpuLo0aPYsGEDBEFAVFQUHBwckJeXhxkzZiA6Oho1NTUAgIKCAqxduxaRkZHIz89HQEAAIiIioFQqAQAVFRWws7PD8ePHxc+hQ4e6rKWmpgZRUVEICQnBnj17YG9vj8jISAiCoLFcVlYWcnNzu923xsZGREREwMfHB3v37oW3tzeWLFmCxsZGjeUOHDigVdgjIiKivsVgAa2qqgrFxcVISEiAu7s7fHx8EBMTgwMHDqCwsBDV1dVYu3Yt3NzcsGTJEnh5eSEvLw8AsG/fPsycORPTp0+Hq6srVqxYAQcHBxw7dkwce+jQoXB0dBQ/Uqm0y1pyc3MxevRohIeHw93dHQkJCfjll19w6tQpAEBDQwNiYmKQlZWFQYMGdbtvhw4dgoWFBVauXAk3NzesWrUK/fv3x2effQYAaGtrw5o1axAbGwsXF5f7bSURERE9ZEwNtWFHR0dkZ2fDwcFBY3pDQwNKSkowatQoWFtbi9PlcjmKi4sBAIsWLUL//v07jVlfXw+g4wja448/rnUtJSUl8PHxEb9bWVnB09MTxcXF8PPzw6VLl9Dc3Iy9e/dCoVBoNZ5cLodEIgEASCQSjBkzBsXFxQgJCUFjYyPOnTuHjz/+GNu3b9e6zrtpEAC10P1ypB0JgKZrjWgTAIF9FVmbmaBfW7uhyyAi6jMMFtBsbW0RFBQkfler1di5cyf8/f1RW1sLJycnjeWlUikuX74MAPD09NSYV1BQgAsXLsDf3x8AUFlZiba2NsyePRtKpRI+Pj5QKBSdxrytu+2NGDECGRkZWu9bbW0thg0b1mm88vJycd9zcnK0Hu/3rNp7BqpW/sWpS2ZmJmhlTzW894IMNpKer/9//1YR/5d0g33VD/ZVP9jXDtruv8EC2p2Sk5NRVlaGPXv2YPv27TA3N9eYb25ujpaWlk7rXbx4EQqFAtOmTRODW1VVFezt7aFQKCAIAlJTU7F06VLk5ubCxMSk0xgqlUrr7WlD1+P9HjMzE7TpfFQyM+v8c9KXmZqYwMHeuvsFuyGVDtBBNXQn9lU/2Ff9YF+1YxQBLTk5GTt27EBqaiqGDx8OCwsL3LhxQ2OZlpYWWFpaakw7f/48FixYABcXF6xbt06cfvDgQUgkEnH5jRs3IjAwECUlJcjPz8f+/fs1lrWwsOgUnlpaWmBra9tt7Vu3btU4upaVldXleHfWrwutre082qNjPILWWVt7O+rq6nu8vkTS8Uv56tV6njrWIfZVP9hX/WBfO9zuQ3cMHtDi4uKwe/duJCcnY+LEiQAAZ2dnVFRUaCxXV1encRqyvLwcYWFhcHFxQXZ2tkb4sbKy0lhXKpXCzs4OSqUSy5cvx8KFC8V5Tk5OcHZ2Rl1dXaftjRw5stv6Q0NDMWnSJPG7s7Nzl+N1dYqVyNgJAqCL36cCr+3TC/ZVP9hX/WBftWPQ56ClpaUhJycH69evx5QpU8TpMpkMZ8+eRVNTkzitqKgIMpkMAHDlyhWEh4fD1dUV27Ztg42NjbhcQ0MDxo4di8LCQnGaUqnE9evX8cQTT0AqlcLV1VX8mJqaQiaTaTxHTaVSoaysTNze77Gzs9MYz9LSEjKZDN999534mA5BEHD69GmtxiMiIiIyWECrrKzE5s2bsXjxYsjlctTW1oofX19fDBo0CAqFAuXl5cjMzERpaSlmz54NAEhKSoJarUZ8fDwaGxvF9W7dugUbGxvI5XIkJCSgtLQUZ8+exUsvvYSgoCB4eHjctZZZs2bh9OnTyMzMRHl5ORQKBYYMGdLtw227EhwcjF9//RXx8fGoqKhAfHw8VCqVxpE2IiIioq4Y7BTnkSNH0N7eji1btmDLli0a886dO4fNmzdj1apVCAkJgaurK9LT0zF48GAIgoDDhw+jqakJwcHBGutFR0dj2bJlSEpKQmJiIiIiItDS0oIJEyZg9erVXdYyZMgQbNq0Ce+88w7S09Ph7e2N9PR08TEZ98rGxgYZGRlYs2YNPv74Y3h4eCAzM1PjsSG6Eh8yGmq1zoftsySSjgvi29rbeQj+N6zNTAA+ZoOI6IGRCHc+Lp96latX6xnQdEgiARwcBqCurm9fxKpr7Kt+sK/6wb7qB/va4XYfusOXpRMREREZGQY0IiIiIiPDgEZERERkZBjQiIiIiIwMAxoRERGRkWFAIyIiIjIyDGhERERERoYBjYiIiMjIGDSgKZVKxMTEwNfXF0FBQUhISEBzczMAoLq6GmFhYfDy8sLkyZNx/PhxjXXz8vIQHBwMb29vzJkzR+Ndmr+VnZ2N8ePHa13TJ598gnnz5mlMa2lpQVJSEsaNG4exY8ciKioKly9f7naciRMn4qmnnkJoaChKS0vvutyWLVvwxhtvaF0fERERPfwMFtAEQUBMTAxUKhV27dqF1NRUHD16FBs2bIAgCIiKioKDgwPy8vIwY8YMREdHo6amBgBQUFCAtWvXIjIyEvn5+QgICEBERASUSqXGNqqrq5GWlqZ1TYWFhXjrrbc6Td+4cSMOHz6MlJQU7N69G21tbYiOjkZXL2H49ttvsWrVKkRGRuLgwYPw9vbG4sWLcevWLY3lDhw4gE2bNmldHxEREfUNBgtoVVVVKC4uRkJCAtzd3eHj44OYmBgcOHAAhYWFqK6uxtq1a+Hm5oYlS5bAy8sLeXl5AIB9+/Zh5syZmD59OlxdXbFixQo4ODjg2LFjGttYs2YNRo4cqVU9aWlpWLx4MVxcXDrN27dvH1566SX4+vpi2LBhiIuLw/fff4+ff/75rmPV1tYiMjISM2bMgIuLC6KionDjxg1UVlYCANra2rBmzRrExsbedXtERETUtxnsZemOjo7Izs6Gg4ODxvSGhgaUlJRg1KhRGi8Xl8vlKC4uBgAsWrQI/fv37zRmfX29+Of8/HyoVCrMnj0b6enp3dbz1VdfYdu2bTh58iROnTolTler1UhOTsaoUaN+d3u/NWnSJPHPTU1N2L59O6RSKdzc3AAAjY2NOHfuHD7++GNs376929p+T4MAqPvwO810TQKg6Voj2gT06XfF6Rr7qh/sq36wr/rRm/pqbWaCfm3tBq3BYAHN1tYWQUFB4ne1Wo2dO3fC398ftbW1cHJy0lheKpWK1315enpqzCsoKMCFCxfg7+8PALh27RpSUlLwwQcf4Pvvv9eqnt27dwMATp48qTG9X79+eOaZZzSmffjhh3jkkUfg4eHxu2OeOHEC4eHhEAQBKSkpYqi0tbVFTk6OVnV1Z9XeM1C1GvaH6GFjZmaCVvZU59hX/WBf9YN91Y/e0tf3XpDBRqKfsSVajmuwgHan5ORklJWVYc+ePdi+fTvMzc015pubm6OlpaXTehcvXoRCocC0adPE4PbOO+/g+eefh7u7u9YBTVuHDx/G+++/j7fffrtTjXdyd3fH3r17cfToUbzxxhsYMmQIvLy8dFqPmZkJ2nQ6IgEdfSXdY1/1g33VD/ZVP3pDX01NTOBgb939gvqswaBb/z/JycnYsWMHUlNTMXz4cFhYWODGjRsay7S0tMDS0lJj2vnz57FgwQK4uLhg3bp1AIAvv/wSxcXF4vc7vfXWW9i/f7/4/eDBgxg8eLBWdR4+fBgrVqzAiy++iDlz5gAAtm7dioyMDHGZrKws+Pj4AAAcHBzg4OCAkSNHoqSkBDk5OToPaK2t7b3iXyO9SW/5F15vw77qB/uqH+yrfvSWvra1t6Ou7u6XMd0viQSQSgd0u5zBA1pcXBx2796N5ORkTJw4EQDg7OyMiooKjeXq6uo0TnuWl5cjLCwMLi4uyM7OFsPboUOHcPnyZTz99NMAOi7Ib21thbe3N7KysrB8+XIsXLhQHOfOU6ldOXjwIFauXInQ0FDExsaK00NDQzWuOXN2dkZpaSlMTEw0TsW6ubmJNwkQERGR8RIEwNCXyRk0oKWlpSEnJwfr169HcHCwOF0mkyEzMxNNTU1i8CoqKoJcLgcAXLlyBeHh4XB1dUVWVpbGDQOvvvoqli5dKn7//PPP8dFHH+Gjjz6Cs7MzLC0tIZVK76nOEydOYOXKlZg7d65GOAMAOzs72NnZaUzbs2cPfvnlF2zbtk2cdvbs2bveaEBERER0J4MFtMrKSmzevBkRERGQy+Wora0V5/n6+mLQoEFQKBSIjIzE0aNHUVpaioSEBABAUlIS1Go14uPj0djYiMbGRgCAtbU1pFKpRgCTSqUwNTWFq6trj+psa2tDbGwsxo4di8WLF2vUOXDgwLteh/Y///M/eOGFF7Bjxw48++yz+OSTT1BaWop33323RzUQERFR32KwgHbkyBG0t7djy5Yt2LJli8a8c+fOYfPmzVi1ahVCQkLg6uqK9PR0DB48GIIg4PDhw2hqatI46gYA0dHRWLZsmU7rPHPmDGpqalBTU4PAwECNeR9++CH8/Pw6rePp6Ym0tDSsX78e7733Htzd3bFt2zY4OzvrtDYAiA8ZDbVa58P2WRJJx8Whbe3tRn8beG/CvuoH+6of7Kt+9Ka+WpuZAAZ+zIZE6Opx+NQrXL1az4CmQxIJ4OAwAHV19Ub/C6Q3YV/1g33VD/ZVP9jXDrf70B2+LJ2IiIjIyDCgERERERkZBjQiIiIiI8OARkRERGRkGNCIiIiIjAwDGhEREZGRYUAjIiIiMjIMaERERERGhgGNiIiIyMgwoBEREREZGQY0IiIiIiPDgEZERERkZEwNXQDdnwYBUPfhl87qmgRA07VGtAno0y/z1TVD9tXazAT92tof7EaJiO4TA1ovt2rvGaha+ZePLpmZmaCVPdU5Q/U1ZY4MNpIHvlkiovvCU5xERERERoYBjYiIiMjIMKARERERGRkGNCIiIiIjw5sEern4kNFQqw1dxcNDIgFMTUzQ1t7Ouzh1yJB9tTYzAXgXJxH1MgxovZyNBFDzDjWdkUgAB3tr1NXVg/lMdwzaV4YzIuqFeIqTiIiIyMgwoBEREREZGQY0IiIiIiPDgEZERERkZBjQiIiIiIwMAxoRERGRkWFAIyIiIjIyDGhERERERsagAU2pVCImJga+vr4ICgpCQkICmpubAQDV1dUICwuDl5cXJk+ejOPHj2usm5eXh+DgYHh7e2POnDkoKiq66zays7Mxfvx4rWv65JNPMG/ePI1pLS0tSEpKwrhx4zB27FhERUXh8uXLvzvOF198gRkzZsDb2xvTpk3DkSNHxHmCIGDbtm0YP348fHx8oFAocOvWLa1rJCIiooebwQKaIAiIiYmBSqXCrl27kJqaiqNHj2LDhg0QBAFRUVFwcHBAXl4eZsyYgejoaNTU1AAACgoKsHbtWkRGRiI/Px8BAQGIiIiAUqnU2EZ1dTXS0tK0rqmwsBBvvfVWp+kbN27E4cOHkZKSgt27d6OtrQ3R0dEQunhnzY8//ojo6GjMmjUL+fn5CA0NxfLly/Hjjz8CAP7xj38gLS0NL7/8Mnbv3g2lUolXXnlF6zqJiIjo4WawgFZVVYXi4mIkJCTA3d0dPj4+iImJwYEDB1BYWIjq6mqsXbsWbm5uWLJkCby8vJCXlwcA2LdvH2bOnInp06fD1dUVK1asgIODA44dO6axjTVr1mDkyJFa1ZOWlobFixfDxcWl07x9+/bhpZdegq+vL4YNG4a4uDh8//33+Pnnn+861oEDB+Dv74/58+fD1dUVc+fOhZ+fHz799FMAwM6dO7FgwQJMnToV7u7uSExMxBdffIGqqqp7aSERERE9pAz2Lk5HR0dkZ2fDwcFBY3pDQwNKSkowatQoWFtbi9PlcjmKi4sBAIsWLUL//v07jVlfXy/+OT8/HyqVCrNnz0Z6enq39Xz11VfYtm0bTp48iVOnTonT1Wo1kpOTMWrUqN/d3m89//zzaG1t7XL56upqyGQycbqTkxPs7e1RXFyMJ554ottaf0si6fiQbtzuJXuqW+yrfrCv+sG+6gf72kHb/TdYQLO1tUVQUJD4Xa1WY+fOnfD390dtbS2cnJw0lpdKpeJ1X56enhrzCgoKcOHCBfj7+wMArl27hpSUFHzwwQf4/vvvtapn9+7dAICTJ09qTO/Xrx+eeeYZjWkffvghHnnkEXh4eNx1LDc3N43v5eXlOHHiBEJDQ8V9+e3p2MbGRty8eRPXr1/XqtbfUsEEAm/10KlL1xoBiQnQx3+J6Jqx9LW/pSkesTY3bBE6JpUOMHQJDyX2VT/YV+0YLKDdKTk5GWVlZdizZw+2b98Oc3PNX6Dm5uZoaWnptN7FixehUCgwbdo0Mbi98847eP755+Hu7q51QNPW4cOH8f777+Ptt9/uVOPdXLt2DcuWLcOYMWMwYcIEAMDkyZORkZEBuVyOIUOGIDExEQDuetStO6/vKYGqtf2e16OumZmZoJU91Tlj6et7L8jQ3ths6DJ0QiLp+Mvu6tV6dHFJLPUA+6of7GuH233ojlEEtOTkZOzYsQOpqakYPnw4LCwscOPGDY1lWlpaYGlpqTHt/PnzWLBgAVxcXLBu3ToAwJdffoni4mLx+53eeust7N+/X/x+8OBBDB48WKs6Dx8+jBUrVuDFF1/EnDlzAABbt25FRkaGuExWVhZ8fHwAAHV1dViwYAEEQcDGjRvRr1/Hoa7IyEhUV1djypQpMDU1RWhoKEaMGAEbGxut6iCinhME4GH7u0EQ0Kf/wtMX9lU/2FftGDygxcXFYffu3UhOTsbEiRMBAM7OzqioqNBYrq6uTuO0Z3l5OcLCwuDi4oLs7GwxvB06dAiXL1/G008/DQBoa2tDa2srvL29kZWVheXLl2PhwoXiOHeeSu3KwYMHsXLlSoSGhiI2NlacHhoaikmTJonfnZ2dAXQ8QmT+/PkAOk6J2tvbi8tYW1vj73//O+rr6yGRSGBjY4Onn34ajz76qFa1EBER0cPNoAEtLS0NOTk5WL9+PYKDg8XpMpkMmZmZaGpqEoNXUVER5HI5AODKlSsIDw+Hq6srsrKyNG4YePXVV7F06VLx++eff46PPvoIH330EZydnWFpaQmpVHpPdZ44cQIrV67E3LlzNcIZANjZ2cHOzk5jWmNjIxYtWoR+/frhww8/hKOjo8b8d999F+7u7nj++ecBAKWlpaivr4e3t/c91UVEREQPJ4MFtMrKSmzevBkRERGQy+Wora0V5/n6+mLQoEFQKBSIjIzE0aNHUVpaioSEBABAUlIS1Go14uPj0djYiMbGRgAdR6akUqlGAJNKpTA1NYWrq2uP6mxra0NsbCzGjh2LxYsXa9Q5cODAu16HlpGRgYsXL+Kjjz4CAHEdS0tLDBgwAE5OTkhLS4Obmxv69euH1157DX/+8587BT0iIiLqmwwW0I4cOYL29nZs2bIFW7Zs0Zh37tw5bN68GatWrUJISAhcXV2Rnp6OwYMHQxAEHD58GE1NTRpH3QAgOjoay5Yt02mdZ86cQU1NDWpqahAYGKgx78MPP4Sfn1+ndf75z3+iqalJvE7ttueffx6JiYmYN28efvnlFyxevBj9+vXDjBkz8Oqrr/aovviQ0VCre7Qq3YVEApiamKCtvZ3XSOiQMfXV2swEaDP8zQpERL9HInT1OHzqFa5erWdA0yGJBHBwGIC6ur59l5Gusa/6wb7qB/uqH+xrh9t96A6foEVERERkZBjQiIiIiIwMAxoRERGRkWFAIyIiIjIyDGhERERERsbgbxKg+9MgAOo+dDeMtZkJ+vERCURE9JBjQOvlVu0906delp4yRwYbiaGrICIi0i+e4iQiIiIyMgxoREREREaGAY2IiIjIyDCgERERERkZBjQiIiIiI8O7OHu5+JDRfepl6dZmJgAfs0FERA85BrRezkYCqPvSYycYzoiIqA/gKU4iIiIiI8OARkRERGRkGNCIiIiIjAwDGhEREZGRYUAjIiIiMjIMaERERERGhgGNiIiIyMgwoBEREREZGQY0IiIiIiPDgEZERERkZBjQiIiIiIwMAxoRERGRkWFAIyIiIjIyDGhERERERsbU0AXQ/ZFIOj6kG7d7yZ7qFvuqH+yrfrCv+sG+dtB2/yWCIAj6LYWIiIiI7gVPcRIREREZGQY0IiIiIiPDgEZERERkZBjQiIiIiIwMAxoRERGRkWFAIyIiIjIyDGhERERERoYBjYiIiMjIMKARERERGRkGtF6mubkZsbGx8PHxQWBgIN5//31Dl2T0WlpaMHXqVJw8eVKcVl1djbCwMHh5eWHy5Mk4fvy4xjpff/01pk6dCplMhvnz56O6ulpj/vbt2xEUFARvb2/ExsZCpVI9kH0xBkqlEjExMfD19UVQUBASEhLQ3NwMgH29Hz///DMWLlwIb29v/OEPf0B2drY4j33VjYiICLzxxhvi97KyMsyZMwcymQyzZs3CmTNnNJY/cOAA/vSnP0EmkyEqKgrXrl0T5wmCgJSUFPj7+8PX1xfvvvsu1Gr1A9sXQ/vXv/4FDw8PjU9MTAwA9lVnBOpV1q5dK0ybNk04c+aM8Pnnnwve3t7Cp59+auiyjFZTU5MQFRUlDB8+XCgsLBQEQRDUarUwbdo04ZVXXhEqKiqErVu3CjKZTPjll18EQRCEX375RfDy8hK2bdsm/PTTT8Ly5cuFqVOnCmq1WhAEQfjss88EuVwu/Pvf/xZKSkqEyZMnC2+//bbB9vFBUqvVwgsvvCAsWrRI+Omnn4RvvvlGeO6554TExET29T60t7cL//3f/y288sorwvnz54UvvvhCGDNmjPDJJ5+wrzpy4MABYfjw4cLrr78uCIIg3Lp1SwgICBASExOFiooKIS4uTnjmmWeEW7duCYIgCCUlJcJTTz0l7Nu3T/jhhx+EF198UYiIiBDH27Ztm/Dss88K33zzjXDixAkhMDBQyM7ONsi+GcLmzZuFJUuWCFeuXBE/N2/eZF91iAGtF7l165bw5JNPikFDEAQhPT1dePHFFw1YlfEqLy8Xpk+fLkybNk0joH399deCl5eX+AtDEAThr3/9q7Bx40ZBEARhw4YNGj1tbGwUvL29xfX/8pe/iMsKgiB88803wlNPPSU0NjY+iN0yqIqKCmH48OFCbW2tOG3//v1CYGAg+3oflEqlsHz5cqG+vl6cFhUVJaxZs4Z91YHr168L48aNE2bNmiUGtNzcXGH8+PFikFWr1cJzzz0n5OXlCYIgCK+99pq4rCAIQk1NjeDh4SFcvHhREARBePbZZ8VlBUEQ8vPzhT/+8Y8PapcM7pVXXhHee++9TtPZV93hKc5e5Mcff0RbWxu8vb3FaXK5HCUlJX33EPDvOHXqFPz8/PCPf/xDY3pJSQlGjRoFa2trcZpcLkdxcbE438fHR5xnZWUFT09PFBcXo729Hd9//73GfC8vL7S2tuLHH3/U7w4ZAUdHR2RnZ8PBwUFjekNDA/t6H5ycnLBhwwbY2NhAEAQUFRXhm2++ga+vL/uqA0lJSZgxYwaGDRsmTispKYFcLodEIgEASCQSjBkzpsu+Dho0CIMHD0ZJSQmUSiX+85//YOzYseJ8uVyOX375BVeuXHkwO2VglZWVePzxxztNZ191hwGtF6mtrcUjjzwCc3NzcZqDgwOam5tx48YNwxVmpP7yl78gNjYWVlZWGtNra2vh5OSkMU0qleLy5cvdzv/111/R3NysMd/U1BR2dnbi+g8zW1tbBAUFid/VajV27twJf39/9lVHxo8fj7/85S/w9vbGxIkT2df7dOLECXz77beIjIzUmN5dX69cudLl/NraWgDQmH/7Hy19oa+CIOD8+fM4fvw4Jk6ciD/96U9ISUlBS0sL+6pDpoYugLSnUqk0whkA8XtLS4shSuqVuurj7R7+3vympibxe1fr9yXJyckoKyvDnj17sH37dvZVBzZu3Ii6ujr87W9/Q0JCAn9e70NzczPWrFmDt956C5aWlhrzuutrU1PTPfW1L/0urqmpEfu3YcMGXLp0CevWrUNTUxP7qkMMaL2IhYVFpx/S29/v/OVDXbOwsOh0xLGlpUXsYVd9trW1hYWFhfj9zvl3Hql72CUnJ2PHjh1ITU3F8OHD2VcdefLJJwF0hItXX30Vs2bN6nTXJfuqnbS0NIwePVrjqO9tXfWtu75aWVlphIY7e9wX+vroo4/i5MmTGDhwICQSCUaOHAm1Wo3XXnsNvr6+7KuO8BRnL+Ls7Izr16+jra1NnFZbWwtLS0vY2toasLLexdnZGXV1dRrT6urqxMPqXc13dHSEnZ0dLCwsNOa3tbXhxo0bcHR01H/xRiIuLg4ffPABkpOTMXHiRADs6/2oq6vD4cOHNaYNGzYMra2tcHR0ZF976ODBgzh8+DC8vb3h7e2N/fv3Y//+/fD29r6vn1dnZ2cAEE/J/fbPfaGvAGBnZydeZwYAbm5uaG5uvq+fV/ZVEwNaLzJy5EiYmpqKF1sCQFFREZ588kn068f/K7Ulk8lw9uxZ8XA60NFHmUwmzi8qKhLnqVQqlJWVQSaToV+/fnjyySc15hcXF8PU1BQjRox4cDthQGlpacjJycH69esxZcoUcTr72nOXLl1CdHQ0lEqlOO3MmTOwt7eHXC5nX3voo48+wv79+5Gfn4/8/HyMHz8e48ePR35+PmQyGb777jsIggCg47qq06dPd9nX//znP/jPf/4DmUwGZ2dnDB48WGN+UVERBg8e3On6qofRl19+CT8/P40juz/88APs7Owgl8vZV10x5C2kdO/efPNNYcqUKUJJSYnwr3/9SxgzZozwz3/+09BlGb3fPmajra1NmDx5srBixQrhp59+EjIyMgQvLy/xuVLV1dXCk08+KWRkZIjPlZo2bZp42/iBAweEMWPGCP/617+EkpISYcqUKUJcXJzB9u1BqqioEEaOHCmkpqZqPP/oypUr7Ot9aGtrE0JCQoTw8HChvLxc+OKLL4RnnnlG2L59O/uqQ6+//rr4iIf6+nrB399fiIuLE8rLy4W4uDghICBAfJzJ6dOnBU9PT+Hjjz8Wn9e1ZMkScayMjAwhMDBQKCwsFAoLC4XAwEDh/fffN8h+PWj19fVCUFCQ8PLLLwuVlZXCF198IQQGBgqZmZnsqw4xoPUyjY2NwsqVKwUvLy8hMDBQ+OCDDwxdUq/w24AmCIJw4cIFYe7cucLo0aOFKVOmCF999ZXG8l988YXw3//938JTTz0l/PWvfxWf0XNbRkaG8PTTTwtyuVxQKBRCU1PTA9kPQ8vIyBCGDx9+148gsK/34/Lly0JUVJQwZswYISAgQNiyZYsYsthX3fhtQBOEjoemzpw5U3jyySeF2bNnC2fPntVYPi8vT3j22WcFLy8vISoqSrh27Zo4r62tTXjnnXcEHx8fwc/PT0hOThb//+oLfvrpJyEsLEzw8vISAgIChE2bNon7z77qhkQQ/u84JBEREREZBV64RERERGRkGNCIiIiIjAwDGhEREZGRYUAjIiIiMjIMaERERERGhgGNiIiIyMgwoBEREREZGQY0IiIiIiPDgEZEpAc3b95EYmIixo8fD5lMhkmTJmH79u1Qq9V633ZDQwPy8/P1vh0i0h9TQxdARPSwuX79Ov7nf/4HTk5OiI+Px5AhQ/D9998jLi4O1dXVePPNN/W6/e3bt+PkyZOYOXOmXrdDRPrDgEZEpGPvvfcezM3NsW3bNlhYWAAAXFxcYGlpicjISLz44osYOnSo3rbPN/gR9X58FycRkQ61tLTAz88PK1euxJ///GeNeYIg4OTJkxgzZgxUKhVSUlJw5MgRNDc3Y/z48Vi9ejUGDhyIkydPYv78+Th37py47htvvAEASExMxKZNm3DhwgXY2Nhg//79sLCwQHh4OBYvXoy9e/dCoVCI6/12DCLqPXgNGhGRDl28eBGNjY148sknO82TSCTw9/eHubk5oqOj8cMPP2Dr1q344IMPUFlZKYYwbfzzn/+EhYUF9u3bh4ULFyIlJQXnz5/H5MmTER4eDm9vbxw/flyXu0ZEDxBPcRIR6dCvv/4KABgwYECXy/z44484deoUPvvsM/FUZ3JyMiZPnoyqqiqttmNnZ4fXX38dJiYmWLRoEbKysnDmzBkMHToU1tbWMDMzg6Oj4/3vEBEZBI+gERHpkJ2dHYCOuzi7UlVVBVtbW43r0Nzc3DBw4ECtA9qQIUNgYmIifu/fvz/a2tp6VjQRGR0GNCIiHXrssccwYMAAnD179q7z//d//xfm5uZ3ndfe3o729nZIJJJO8+4MX2ZmZp2W4SXFRA8PBjQiIh0yNTXF5MmTsWvXLrS0tGjM+/e//41///vfePzxx/Hrr79qHC2rqKhAQ0MDhg4dKoavhoYGcf6lS5e0ruFuAY+IehcGNCIiHVu2bBkaGhqwcOFCnDp1ChcvXkRubi7eeOMNzJ8/H8OGDcO4cePw+uuvo7S0FKWlpXj99dcxduxYDB8+HO7u7rC0tMTWrVtRXV2N7OxslJWVab19KysrXLly5Z5CHREZFwY0IiIdc3R0xO7du+Hi4oJXX30VU6dOxY4dOxATEyPeqZmUlAQXFxeEhYVh4cKFcHd3R3p6OgDAxsYGcXFxOHjwIKZOnYoff/wRc+fO1Xr7zz33HNRqNaZMmYKrV6/qZR+JSL/4HDQiIiIiI8MjaERERERGhgGNiIiIyMgwoBEREREZGQY0IiIiIiPDgEZERERkZBjQiIiIiIwMAxoRERGRkWFAIyIiIjIyDGhERERERoYBjYiIiMjIMKARERERGZn/HzAO1AiVGXppAAAAAElFTkSuQmCC",
517
+ "text/plain": [
518
+ "<Figure size 640x480 with 1 Axes>"
519
+ ]
520
+ },
521
+ "metadata": {},
522
+ "output_type": "display_data"
523
+ }
524
+ ],
525
+ "source": [
526
+ "import seaborn as sns\n",
527
+ "\n",
528
+ "sns.histplot(missing_df, y=\"creation_date\")"
529
+ ]
530
+ },
531
+ {
532
+ "cell_type": "code",
533
+ "execution_count": 40,
534
+ "metadata": {},
535
+ "outputs": [
536
+ {
537
+ "data": {
538
+ "text/plain": [
539
+ "<Axes: xlabel='Count', ylabel='creation_date'>"
540
+ ]
541
+ },
542
+ "execution_count": 40,
543
+ "metadata": {},
544
+ "output_type": "execute_result"
545
+ },
546
+ {
547
+ "data": {
548
+ "image/png": "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",
549
+ "text/plain": [
550
+ "<Figure size 640x480 with 1 Axes>"
551
+ ]
552
+ },
553
+ "metadata": {},
554
+ "output_type": "display_data"
555
+ }
556
+ ],
557
+ "source": [
558
+ "sns.histplot(trades_data, y=\"creation_date\")"
559
+ ]
560
+ },
561
+ {
562
+ "cell_type": "code",
563
+ "execution_count": 19,
564
+ "metadata": {},
565
+ "outputs": [],
566
+ "source": [
567
+ "def add_extra_columns(new_trades):\n",
568
+ " new_trades[\"creation_timestamp\"] = pd.to_datetime(new_trades[\"creationTimestamp\"])\n",
569
+ " new_trades[\"creation_date\"] = new_trades[\"creation_timestamp\"].dt.date\n",
570
+ " new_trades[\"creation_date\"] = pd.to_datetime(new_trades[\"creation_date\"])"
571
+ ]
572
+ },
573
+ {
574
+ "cell_type": "code",
575
+ "execution_count": null,
576
+ "metadata": {},
577
+ "outputs": [],
578
+ "source": [
579
+ "add_extra_columns(new_trades=new_trades)"
580
+ ]
581
+ },
582
+ {
583
+ "cell_type": "code",
584
+ "execution_count": 9,
585
+ "metadata": {},
586
+ "outputs": [
587
+ {
588
+ "data": {
589
+ "text/plain": [
590
+ "Timestamp('2025-01-13 00:00:00')"
591
+ ]
592
+ },
593
+ "execution_count": 9,
594
+ "metadata": {},
595
+ "output_type": "execute_result"
596
+ }
597
+ ],
598
+ "source": [
599
+ "max(new_trades.creation_date)"
600
+ ]
601
+ },
602
+ {
603
+ "cell_type": "code",
604
+ "execution_count": 12,
605
+ "metadata": {},
606
+ "outputs": [
607
+ {
608
+ "data": {
609
+ "text/plain": [
610
+ "Index(['collateralAmount', 'collateralAmountUSD', 'collateralToken',\n",
611
+ " 'creationTimestamp', 'trader_address', 'feeAmount', 'id',\n",
612
+ " 'oldOutcomeTokenMarginalPrice', 'outcomeIndex',\n",
613
+ " 'outcomeTokenMarginalPrice', 'outcomeTokensTraded', 'title',\n",
614
+ " 'transactionHash', 'type', 'market_creator',\n",
615
+ " 'fpmm.answerFinalizedTimestamp', 'fpmm.arbitrationOccurred',\n",
616
+ " 'fpmm.currentAnswer', 'fpmm.id', 'fpmm.isPendingArbitration',\n",
617
+ " 'fpmm.openingTimestamp', 'fpmm.outcomes', 'fpmm.title',\n",
618
+ " 'fpmm.condition.id', 'creation_timestamp', 'creation_date'],\n",
619
+ " dtype='object')"
620
+ ]
621
+ },
622
+ "execution_count": 12,
623
+ "metadata": {},
624
+ "output_type": "execute_result"
625
+ }
626
+ ],
627
+ "source": [
628
+ "new_trades.columns"
629
+ ]
630
+ },
631
+ {
632
+ "cell_type": "code",
633
+ "execution_count": 23,
634
+ "metadata": {},
635
+ "outputs": [
636
+ {
637
+ "name": "stdout",
638
+ "output_type": "stream",
639
+ "text": [
640
+ "Transformation not needed\n",
641
+ "Initial length before removing duplicates in fpmmTrades= 137851\n",
642
+ "Final length after removing duplicates in fpmmTrades= 137851\n"
643
+ ]
644
+ }
645
+ ],
646
+ "source": [
647
+ "old_trades_df = pd.read_parquet(\"../tmp/fpmmTrades.parquet\")\n",
648
+ "\n",
649
+ "\n",
650
+ "# lowercase and strip creator_address\n",
651
+ "new_trades[\"trader_address\"] = (\n",
652
+ " new_trades[\"trader_address\"].str.lower().str.strip()\n",
653
+ ")\n",
654
+ "\n",
655
+ "\n",
656
+ "try:\n",
657
+ " old_trades_df[\"creationTimestamp\"] = old_trades_df[\"creationTimestamp\"].apply(\n",
658
+ " lambda x: transform_to_datetime(x)\n",
659
+ " )\n",
660
+ "except Exception as e:\n",
661
+ " print(f\"Transformation not needed\")\n",
662
+ "\n",
663
+ "# merge two dataframes\n",
664
+ "merge_df = pd.concat([old_trades_df, new_trades], ignore_index=True)\n",
665
+ "# avoid numpy objects\n",
666
+ "merge_df[\"fpmm.arbitrationOccurred\"] = merge_df[\"fpmm.arbitrationOccurred\"].astype(\n",
667
+ " bool\n",
668
+ ")\n",
669
+ "merge_df[\"fpmm.isPendingArbitration\"] = merge_df[\n",
670
+ " \"fpmm.isPendingArbitration\"\n",
671
+ "].astype(bool)\n",
672
+ "\n",
673
+ "# Check for duplicates\n",
674
+ "print(f\"Initial length before removing duplicates in fpmmTrades= {len(merge_df)}\")\n",
675
+ "\n",
676
+ "# Remove duplicates\n",
677
+ "# fpmm.outcomes is a numpy array\n",
678
+ "merge_df.drop_duplicates(\"id\", keep=\"last\", inplace=True)\n",
679
+ "print(f\"Final length after removing duplicates in fpmmTrades= {len(merge_df)}\")"
680
+ ]
681
+ },
682
+ {
683
+ "cell_type": "code",
684
+ "execution_count": 24,
685
+ "metadata": {},
686
+ "outputs": [],
687
+ "source": [
688
+ "merge_df.to_parquet(\"../tmp/fpmmTrades.parquet\", index=False)"
689
+ ]
690
+ },
691
+ {
692
+ "cell_type": "code",
693
+ "execution_count": 13,
694
+ "metadata": {},
695
+ "outputs": [
696
+ {
697
+ "data": {
698
+ "text/plain": [
699
+ "Index(['collateralAmount', 'collateralAmountUSD', 'collateralToken',\n",
700
+ " 'creationTimestamp', 'trader_address', 'feeAmount', 'id',\n",
701
+ " 'oldOutcomeTokenMarginalPrice', 'outcomeIndex',\n",
702
+ " 'outcomeTokenMarginalPrice', 'outcomeTokensTraded', 'title',\n",
703
+ " 'transactionHash', 'type', 'market_creator',\n",
704
+ " 'fpmm.answerFinalizedTimestamp', 'fpmm.arbitrationOccurred',\n",
705
+ " 'fpmm.currentAnswer', 'fpmm.id', 'fpmm.isPendingArbitration',\n",
706
+ " 'fpmm.openingTimestamp', 'fpmm.outcomes', 'fpmm.title',\n",
707
+ " 'fpmm.condition.id', 'creation_timestamp', 'creation_date'],\n",
708
+ " dtype='object')"
709
+ ]
710
+ },
711
+ "execution_count": 13,
712
+ "metadata": {},
713
+ "output_type": "execute_result"
714
+ }
715
+ ],
716
+ "source": [
717
+ "old_trades_df.columns"
718
+ ]
719
+ },
720
+ {
721
+ "cell_type": "code",
722
+ "execution_count": 20,
723
+ "metadata": {},
724
+ "outputs": [],
725
+ "source": [
726
+ "add_extra_columns(new_trades=merge_df)"
727
+ ]
728
+ },
729
+ {
730
+ "cell_type": "code",
731
+ "execution_count": 21,
732
+ "metadata": {},
733
+ "outputs": [
734
+ {
735
+ "data": {
736
+ "text/plain": [
737
+ "Index(['collateralAmount', 'collateralAmountUSD', 'collateralToken',\n",
738
+ " 'creationTimestamp', 'trader_address', 'feeAmount', 'id',\n",
739
+ " 'oldOutcomeTokenMarginalPrice', 'outcomeIndex',\n",
740
+ " 'outcomeTokenMarginalPrice', 'outcomeTokensTraded', 'title',\n",
741
+ " 'transactionHash', 'type', 'market_creator',\n",
742
+ " 'fpmm.answerFinalizedTimestamp', 'fpmm.arbitrationOccurred',\n",
743
+ " 'fpmm.currentAnswer', 'fpmm.id', 'fpmm.isPendingArbitration',\n",
744
+ " 'fpmm.openingTimestamp', 'fpmm.outcomes', 'fpmm.title',\n",
745
+ " 'fpmm.condition.id', 'creation_timestamp', 'creation_date'],\n",
746
+ " dtype='object')"
747
+ ]
748
+ },
749
+ "execution_count": 21,
750
+ "metadata": {},
751
+ "output_type": "execute_result"
752
+ }
753
+ ],
754
+ "source": [
755
+ "merge_df.columns"
756
+ ]
757
+ },
758
+ {
759
+ "cell_type": "code",
760
+ "execution_count": 22,
761
+ "metadata": {},
762
+ "outputs": [
763
+ {
764
+ "data": {
765
+ "text/plain": [
766
+ "Timestamp('2025-01-13 00:00:00')"
767
+ ]
768
+ },
769
+ "execution_count": 22,
770
+ "metadata": {},
771
+ "output_type": "execute_result"
772
+ }
773
+ ],
774
+ "source": [
775
+ "max(merge_df.creation_date)"
776
+ ]
777
+ },
778
  {
779
  "cell_type": "code",
780
  "execution_count": 9,
notebooks/weekly_analysis.ipynb CHANGED
@@ -4676,7 +4676,7 @@
4676
  ],
4677
  "metadata": {
4678
  "kernelspec": {
4679
- "display_name": ".venv",
4680
  "language": "python",
4681
  "name": "python3"
4682
  },
@@ -4690,7 +4690,7 @@
4690
  "name": "python",
4691
  "nbconvert_exporter": "python",
4692
  "pygments_lexer": "ipython3",
4693
- "version": "3.12.2"
4694
  },
4695
  "orig_nbformat": 4
4696
  },
 
4676
  ],
4677
  "metadata": {
4678
  "kernelspec": {
4679
+ "display_name": "Python 3",
4680
  "language": "python",
4681
  "name": "python3"
4682
  },
 
4690
  "name": "python",
4691
  "nbconvert_exporter": "python",
4692
  "pygments_lexer": "ipython3",
4693
+ "version": "3.12.3"
4694
  },
4695
  "orig_nbformat": 4
4696
  },
scripts/cleaning_old_info.py CHANGED
@@ -1,5 +1,5 @@
1
  import pandas as pd
2
- from utils import DATA_DIR, TMP_DIR
3
 
4
 
5
  def clean_old_data_from_parquet_files(cutoff_date: str):
@@ -9,7 +9,7 @@ def clean_old_data_from_parquet_files(cutoff_date: str):
9
 
10
  # clean tools.parquet
11
  try:
12
- tools = pd.read_parquet(DATA_DIR / "tools.parquet")
13
 
14
  # make sure creator_address is in the columns
15
  assert "trader_address" in tools.columns, "trader_address column not found"
@@ -22,7 +22,7 @@ def clean_old_data_from_parquet_files(cutoff_date: str):
22
  print(f"length before filtering {len(tools)}")
23
  tools = tools.loc[tools["request_time"] > min_date_utc]
24
  print(f"length after filtering {len(tools)}")
25
- tools.to_parquet(DATA_DIR / "tools.parquet", index=False)
26
 
27
  except Exception as e:
28
  print(f"Error cleaning tools file {e}")
@@ -53,11 +53,11 @@ def clean_old_data_from_parquet_files(cutoff_date: str):
53
  unknown_traders["creation_timestamp"], utc=True
54
  )
55
 
56
- print(f"length before filtering {len(unknown_traders)}")
57
  unknown_traders = unknown_traders.loc[
58
  unknown_traders["creation_timestamp"] > min_date_utc
59
  ]
60
- print(f"length after filtering {len(unknown_traders)}")
61
  unknown_traders.to_parquet(DATA_DIR / "unknown_traders.parquet", index=False)
62
 
63
  except Exception as e:
@@ -66,7 +66,15 @@ def clean_old_data_from_parquet_files(cutoff_date: str):
66
  # clean fpmmTrades.parquet
67
  try:
68
  fpmmTrades = pd.read_parquet(TMP_DIR / "fpmmTrades.parquet")
69
-
 
 
 
 
 
 
 
 
70
  fpmmTrades["creation_timestamp"] = pd.to_datetime(
71
  fpmmTrades["creation_timestamp"], utc=True
72
  )
 
1
  import pandas as pd
2
+ from utils import DATA_DIR, TMP_DIR, transform_to_datetime
3
 
4
 
5
  def clean_old_data_from_parquet_files(cutoff_date: str):
 
9
 
10
  # clean tools.parquet
11
  try:
12
+ tools = pd.read_parquet(TMP_DIR / "tools.parquet")
13
 
14
  # make sure creator_address is in the columns
15
  assert "trader_address" in tools.columns, "trader_address column not found"
 
22
  print(f"length before filtering {len(tools)}")
23
  tools = tools.loc[tools["request_time"] > min_date_utc]
24
  print(f"length after filtering {len(tools)}")
25
+ tools.to_parquet(TMP_DIR / "tools.parquet", index=False)
26
 
27
  except Exception as e:
28
  print(f"Error cleaning tools file {e}")
 
53
  unknown_traders["creation_timestamp"], utc=True
54
  )
55
 
56
+ print(f"length unknown traders before filtering {len(unknown_traders)}")
57
  unknown_traders = unknown_traders.loc[
58
  unknown_traders["creation_timestamp"] > min_date_utc
59
  ]
60
+ print(f"length unknown traders after filtering {len(unknown_traders)}")
61
  unknown_traders.to_parquet(DATA_DIR / "unknown_traders.parquet", index=False)
62
 
63
  except Exception as e:
 
66
  # clean fpmmTrades.parquet
67
  try:
68
  fpmmTrades = pd.read_parquet(TMP_DIR / "fpmmTrades.parquet")
69
+ try:
70
+ fpmmTrades["creationTimestamp"] = fpmmTrades["creationTimestamp"].apply(
71
+ lambda x: transform_to_datetime(x)
72
+ )
73
+ except Exception as e:
74
+ print(f"Transformation not needed")
75
+ fpmmTrades["creation_timestamp"] = pd.to_datetime(
76
+ fpmmTrades["creationTimestamp"]
77
+ )
78
  fpmmTrades["creation_timestamp"] = pd.to_datetime(
79
  fpmmTrades["creation_timestamp"], utc=True
80
  )
scripts/cloud_storage.py CHANGED
@@ -23,7 +23,7 @@ def initialize_client():
23
  return client
24
 
25
 
26
- def upload_file(client, filename: str, file_path: str):
27
  """Upload a file to the bucket"""
28
  try:
29
  OBJECT_NAME = FOLDER_NAME + "/" + filename
@@ -34,8 +34,10 @@ def upload_file(client, filename: str, file_path: str):
34
  BUCKET_NAME, OBJECT_NAME, file_path, part_size=10 * 1024 * 1024
35
  ) # 10MB parts
36
  print(f"File '{file_path}' uploaded as '{OBJECT_NAME}'.")
 
37
  except S3Error as err:
38
  print(f"Error uploading file: {err}")
 
39
 
40
 
41
  def download_file(client, filename: str, file_path: str):
@@ -48,11 +50,16 @@ def download_file(client, filename: str, file_path: str):
48
  print(f"Error downloading file: {err}")
49
 
50
 
51
- def load_historical_file(client, filename: str):
52
  """Function to load one file into the cloud storage"""
53
  file_path = filename
54
  file_path = HIST_DIR / filename
55
- upload_file(client, filename, file_path)
 
 
 
 
 
56
 
57
 
58
  def process_historical_files(client):
@@ -63,8 +70,10 @@ def process_historical_files(client):
63
  # Check if file is a parquet file
64
  if filename.endswith(".parquet"):
65
  try:
66
- load_historical_file(client, filename)
67
- print(f"Successfully processed {filename}")
 
 
68
  except Exception as e:
69
  print(f"Error processing {filename}: {str(e)}")
70
 
 
23
  return client
24
 
25
 
26
+ def upload_file(client, filename: str, file_path: str) -> bool:
27
  """Upload a file to the bucket"""
28
  try:
29
  OBJECT_NAME = FOLDER_NAME + "/" + filename
 
34
  BUCKET_NAME, OBJECT_NAME, file_path, part_size=10 * 1024 * 1024
35
  ) # 10MB parts
36
  print(f"File '{file_path}' uploaded as '{OBJECT_NAME}'.")
37
+ return True
38
  except S3Error as err:
39
  print(f"Error uploading file: {err}")
40
+ return False
41
 
42
 
43
  def download_file(client, filename: str, file_path: str):
 
50
  print(f"Error downloading file: {err}")
51
 
52
 
53
+ def load_historical_file(client, filename: str) -> bool:
54
  """Function to load one file into the cloud storage"""
55
  file_path = filename
56
  file_path = HIST_DIR / filename
57
+ return upload_file(client, filename, file_path)
58
+
59
+
60
+ def upload_historical_file(filename: str):
61
+ client = initialize_client()
62
+ load_historical_file(client=client, filename=filename)
63
 
64
 
65
  def process_historical_files(client):
 
70
  # Check if file is a parquet file
71
  if filename.endswith(".parquet"):
72
  try:
73
+ if load_historical_file(client, filename):
74
+ print(f"Successfully processed {filename}")
75
+ else:
76
+ print("Error loading the files")
77
  except Exception as e:
78
  print(f"Error processing {filename}: {str(e)}")
79
 
scripts/daily_data.py CHANGED
@@ -11,6 +11,7 @@ from nr_mech_calls import (
11
  transform_to_datetime,
12
  )
13
  from markets import check_current_week_data
 
14
 
15
  logging.basicConfig(level=logging.INFO)
16
 
@@ -51,6 +52,9 @@ def prepare_live_metrics(
51
  # save into a separate file
52
  all_trades_df.to_parquet(DATA_DIR / "daily_info.parquet", index=False)
53
 
 
 
 
54
 
55
  if __name__ == "__main__":
56
  prepare_live_metrics()
 
11
  transform_to_datetime,
12
  )
13
  from markets import check_current_week_data
14
+ from staking import generate_retention_activity_file
15
 
16
  logging.basicConfig(level=logging.INFO)
17
 
 
52
  # save into a separate file
53
  all_trades_df.to_parquet(DATA_DIR / "daily_info.parquet", index=False)
54
 
55
+ # prepare the retention info file
56
+ generate_retention_activity_file()
57
+
58
 
59
  if __name__ == "__main__":
60
  prepare_live_metrics()
scripts/get_mech_info.py CHANGED
@@ -316,7 +316,7 @@ def get_mech_events_since_last_run(logger):
316
  try:
317
  all_trades = pd.read_parquet(DATA_DIR / "all_trades_profitability.parquet")
318
  latest_timestamp = max(all_trades.creation_timestamp)
319
- # cutoff_date = "2024-12-01"
320
  # latest_timestamp = pd.Timestamp(
321
  # datetime.strptime(cutoff_date, "%Y-%m-%d")
322
  # ).tz_localize("UTC")
 
316
  try:
317
  all_trades = pd.read_parquet(DATA_DIR / "all_trades_profitability.parquet")
318
  latest_timestamp = max(all_trades.creation_timestamp)
319
+ # cutoff_date = "2024-12-22"
320
  # latest_timestamp = pd.Timestamp(
321
  # datetime.strptime(cutoff_date, "%Y-%m-%d")
322
  # ).tz_localize("UTC")
scripts/gnosis_timestamps.py CHANGED
@@ -137,7 +137,9 @@ def compute_request_time(tools_df: pd.DataFrame) -> pd.DataFrame:
137
  tools_df["request_time"]
138
  ).dt.strftime("%Y-%m")
139
  tools_df["request_month_year_week"] = (
140
- pd.to_datetime(tools_df["request_time"]).dt.to_period("W").astype(str)
 
 
141
  )
142
  # Update t_map with new timestamps
143
  new_timestamps = (
 
137
  tools_df["request_time"]
138
  ).dt.strftime("%Y-%m")
139
  tools_df["request_month_year_week"] = (
140
+ pd.to_datetime(tools_df["request_time"])
141
+ .dt.to_period("W")
142
+ .dt.start_time.dt.strftime("%b-%d-%Y")
143
  )
144
  # Update t_map with new timestamps
145
  new_timestamps = (
scripts/markets.py CHANGED
@@ -156,15 +156,19 @@ def transform_fpmmTrades(df: pd.DataFrame) -> pd.DataFrame:
156
  return df
157
 
158
 
159
- def create_fpmmTrades(rpc: str, from_timestamp: float = DEFAULT_FROM_TIMESTAMP):
 
 
 
160
  """Create fpmmTrades for all trades."""
 
161
  # Quickstart trades
162
  qs_trades_json = query_omen_xdai_subgraph(
163
  trader_category="quickstart",
164
  from_timestamp=from_timestamp,
165
- to_timestamp=DEFAULT_TO_TIMESTAMP,
166
  fpmm_from_timestamp=from_timestamp,
167
- fpmm_to_timestamp=DEFAULT_TO_TIMESTAMP,
168
  )
169
 
170
  print(f"length of the qs_trades_json dataset {len(qs_trades_json)}")
@@ -175,6 +179,7 @@ def create_fpmmTrades(rpc: str, from_timestamp: float = DEFAULT_FROM_TIMESTAMP):
175
  qs_df = transform_fpmmTrades(qs_df)
176
 
177
  # Pearl trades
 
178
  pearl_trades_json = query_omen_xdai_subgraph(
179
  trader_category="pearl",
180
  from_timestamp=from_timestamp,
@@ -335,10 +340,14 @@ def add_market_creator(tools: pd.DataFrame) -> None:
335
  return tools
336
 
337
 
338
- def fpmmTrades_etl(rpc: str, trades_filename: str, from_timestamp: str) -> None:
 
 
339
  print("Generating the trades file")
340
  try:
341
- fpmmTrades = create_fpmmTrades(rpc, from_timestamp=from_timestamp)
 
 
342
  except FileNotFoundError:
343
  print(f"Error creating {trades_filename} file .")
344
 
 
156
  return df
157
 
158
 
159
+ def create_fpmmTrades(
160
+ from_timestamp: int = DEFAULT_FROM_TIMESTAMP,
161
+ to_timestamp: int = DEFAULT_TO_TIMESTAMP,
162
+ ):
163
  """Create fpmmTrades for all trades."""
164
+ print("Getting trades from Quickstart markets")
165
  # Quickstart trades
166
  qs_trades_json = query_omen_xdai_subgraph(
167
  trader_category="quickstart",
168
  from_timestamp=from_timestamp,
169
+ to_timestamp=to_timestamp,
170
  fpmm_from_timestamp=from_timestamp,
171
+ fpmm_to_timestamp=to_timestamp,
172
  )
173
 
174
  print(f"length of the qs_trades_json dataset {len(qs_trades_json)}")
 
179
  qs_df = transform_fpmmTrades(qs_df)
180
 
181
  # Pearl trades
182
+ print("Getting trades from Pearl markets")
183
  pearl_trades_json = query_omen_xdai_subgraph(
184
  trader_category="pearl",
185
  from_timestamp=from_timestamp,
 
340
  return tools
341
 
342
 
343
+ def fpmmTrades_etl(
344
+ trades_filename: str, from_timestamp: int, to_timestamp: int = DEFAULT_TO_TIMESTAMP
345
+ ) -> None:
346
  print("Generating the trades file")
347
  try:
348
+ fpmmTrades = create_fpmmTrades(
349
+ from_timestamp=from_timestamp, to_timestamp=to_timestamp
350
+ )
351
  except FileNotFoundError:
352
  print(f"Error creating {trades_filename} file .")
353
 
scripts/profitability.py CHANGED
@@ -146,7 +146,7 @@ def prepare_profitalibity_data(
146
 
147
  # Check if tools.parquet is in the same directory
148
  try:
149
- # new tools parquet
150
  tools = pd.read_parquet(DATA_DIR / tools_filename)
151
 
152
  # make sure creator_address is in the columns
@@ -165,7 +165,7 @@ def prepare_profitalibity_data(
165
  return
166
 
167
  # Check if fpmmTrades.parquet is in the same directory
168
- print("Reading the trades file")
169
  try:
170
  fpmmTrades = pd.read_parquet(DATA_DIR / trades_filename)
171
  except FileNotFoundError:
@@ -413,10 +413,8 @@ def run_profitability_analysis(
413
 
414
  all_trades_df = all_trades_df.loc[all_trades_df["is_invalid"] == False]
415
 
416
- # add staking labels
417
  all_trades_df = label_trades_by_staking(trades_df=all_trades_df)
418
 
419
- # create the unknown traders dataset
420
  print("Creating unknown traders dataset")
421
  unknown_traders_df, all_trades_df = create_unknown_traders_df(
422
  trades_df=all_trades_df
@@ -424,9 +422,10 @@ def run_profitability_analysis(
424
  # merge with previous unknown traders dataset
425
  previous_unknown_traders = pd.read_parquet(DATA_DIR / "unknown_traders.parquet")
426
 
427
- unknown_traders_df = pd.concat(
428
  [unknown_traders_df, previous_unknown_traders], ignore_index=True
429
  )
 
430
  unknown_traders_df.to_parquet(DATA_DIR / "unknown_traders.parquet", index=False)
431
 
432
  # save to parquet
@@ -437,6 +436,81 @@ def run_profitability_analysis(
437
  return all_trades_df
438
 
439
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
  if __name__ == "__main__":
441
  # updating the whole fpmmTrades parquet file instead of just the new ones
442
  # trade_mech_calls = pd.read_parquet(TMP_DIR / "result_df.parquet")
 
146
 
147
  # Check if tools.parquet is in the same directory
148
  try:
149
+ # tools parquet file
150
  tools = pd.read_parquet(DATA_DIR / tools_filename)
151
 
152
  # make sure creator_address is in the columns
 
165
  return
166
 
167
  # Check if fpmmTrades.parquet is in the same directory
168
+ print("Reading the new trades file")
169
  try:
170
  fpmmTrades = pd.read_parquet(DATA_DIR / trades_filename)
171
  except FileNotFoundError:
 
413
 
414
  all_trades_df = all_trades_df.loc[all_trades_df["is_invalid"] == False]
415
 
 
416
  all_trades_df = label_trades_by_staking(trades_df=all_trades_df)
417
 
 
418
  print("Creating unknown traders dataset")
419
  unknown_traders_df, all_trades_df = create_unknown_traders_df(
420
  trades_df=all_trades_df
 
422
  # merge with previous unknown traders dataset
423
  previous_unknown_traders = pd.read_parquet(DATA_DIR / "unknown_traders.parquet")
424
 
425
+ unknown_traders_df: pd.DataFrame = pd.concat(
426
  [unknown_traders_df, previous_unknown_traders], ignore_index=True
427
  )
428
+ unknown_traders_df.drop_duplicates("trade_id", keep="last", inplace=True)
429
  unknown_traders_df.to_parquet(DATA_DIR / "unknown_traders.parquet", index=False)
430
 
431
  # save to parquet
 
436
  return all_trades_df
437
 
438
 
439
+ def add_trades_profitability(trades_filename: str):
440
+ print("Reading the trades file")
441
+ try:
442
+ fpmmTrades = pd.read_parquet(DATA_DIR / trades_filename)
443
+ except FileNotFoundError:
444
+ print(f"Error reading {trades_filename} file .")
445
+
446
+ # make sure trader_address is in the columns
447
+ assert "trader_address" in fpmmTrades.columns, "trader_address column not found"
448
+
449
+ # lowercase and strip creator_address
450
+ fpmmTrades["trader_address"] = fpmmTrades["trader_address"].str.lower().str.strip()
451
+
452
+ print("Reading tools parquet file")
453
+ tools = pd.read_parquet(TMP_DIR / "tools.parquet")
454
+
455
+ try:
456
+ fpmmTrades["creationTimestamp"] = fpmmTrades["creationTimestamp"].apply(
457
+ lambda x: transform_to_datetime(x)
458
+ )
459
+ except Exception as e:
460
+ print(f"Transformation not needed")
461
+
462
+ print("Computing the estimated mech calls dataset")
463
+ trade_mech_calls = compute_mech_calls_based_on_timestamps(
464
+ fpmmTrades=fpmmTrades, tools=tools
465
+ )
466
+ print(trade_mech_calls.total_mech_calls.describe())
467
+ print("Analysing trades...")
468
+ all_trades_df = analyse_all_traders(fpmmTrades, trade_mech_calls)
469
+
470
+ # debugging purposes
471
+ all_trades_df.to_parquet(JSON_DATA_DIR / "missing_trades_df.parquet", index=False)
472
+ # filter invalid markets. Condition: "is_invalid" is True
473
+ print("Checking invalid trades")
474
+ invalid_trades = all_trades_df.loc[all_trades_df["is_invalid"] == True]
475
+ if len(invalid_trades) > 0:
476
+ try:
477
+ print("Merging invalid trades parquet file")
478
+ old_invalid_trades = pd.read_parquet(DATA_DIR / "invalid_trades.parquet")
479
+ merge_df = pd.concat(
480
+ [old_invalid_trades, invalid_trades], ignore_index=True
481
+ )
482
+ invalid_trades = merge_df.drop_duplicates("trade_id")
483
+ except Exception as e:
484
+ print(f"Error updating the invalid trades parquet {e}")
485
+ invalid_trades.to_parquet(DATA_DIR / "invalid_trades.parquet", index=False)
486
+ all_trades_df = all_trades_df.loc[all_trades_df["is_invalid"] == False]
487
+
488
+ print("Adding staking labels")
489
+ all_trades_df = label_trades_by_staking(trades_df=all_trades_df)
490
+ print("Creating unknown traders dataset")
491
+ unknown_traders_df, all_trades_df = create_unknown_traders_df(
492
+ trades_df=all_trades_df
493
+ )
494
+ if len(unknown_traders_df) > 0:
495
+ print("Merging unknown traders info")
496
+ # merge with previous unknown traders dataset
497
+ previous_unknown_traders = pd.read_parquet(DATA_DIR / "unknown_traders.parquet")
498
+
499
+ unknown_traders_df: pd.DataFrame = pd.concat(
500
+ [unknown_traders_df, previous_unknown_traders], ignore_index=True
501
+ )
502
+ unknown_traders_df.drop_duplicates("trade_id", keep="last", inplace=True)
503
+ unknown_traders_df.to_parquet(DATA_DIR / "unknown_traders.parquet", index=False)
504
+
505
+ print("merge with previous all_trades_profitability")
506
+ old_trades = pd.read_parquet(DATA_DIR / "all_trades_profitability.parquet")
507
+ all_trades_df: pd.DataFrame = pd.concat(
508
+ [all_trades_df, old_trades], ignore_index=True
509
+ )
510
+ all_trades_df.drop_duplicates("trade_id", keep="last", inplace=True)
511
+ all_trades_df.to_parquet(DATA_DIR / "all_trades_profitability.parquet", index=False)
512
+
513
+
514
  if __name__ == "__main__":
515
  # updating the whole fpmmTrades parquet file instead of just the new ones
516
  # trade_mech_calls = pd.read_parquet(TMP_DIR / "result_df.parquet")
scripts/pull_data.py CHANGED
@@ -3,7 +3,7 @@ from datetime import datetime
3
  import pandas as pd
4
  from markets import etl as mkt_etl, DEFAULT_FILENAME as MARKETS_FILENAME, fpmmTrades_etl
5
  from tools import DEFAULT_FILENAME as TOOLS_FILENAME, generate_tools_file
6
- from profitability import run_profitability_analysis
7
  from utils import (
8
  get_question,
9
  current_answer,
@@ -22,7 +22,7 @@ from update_tools_accuracy import compute_tools_accuracy
22
  from cleaning_old_info import clean_old_data_from_parquet_files
23
  from web3_utils import updating_timestamps
24
  from manage_space_files import move_files
25
- from cloud_storage import load_historical_file
26
  from tools_metrics import compute_tools_based_datasets
27
 
28
 
@@ -64,7 +64,7 @@ def save_historical_data():
64
  filename = f"tools_{timestamp}.parquet"
65
  tools.to_parquet(HIST_DIR / filename, index=False)
66
  # save into cloud storage
67
- load_historical_file(filename)
68
  except Exception as e:
69
  print(f"Error saving tools file in the historical folder {e}")
70
 
@@ -73,7 +73,7 @@ def save_historical_data():
73
  filename = f"all_trades_profitability_{timestamp}.parquet"
74
  all_trades.to_parquet(HIST_DIR / filename, index=False)
75
  # save into cloud storage
76
- load_historical_file(filename)
77
 
78
  except Exception as e:
79
  print(
@@ -101,7 +101,6 @@ def only_new_weekly_analysis():
101
 
102
  # FpmmTrades ETL
103
  fpmmTrades_etl(
104
- rpc=rpc,
105
  trades_filename="new_fpmmTrades.parquet",
106
  from_timestamp=int(latest_timestamp.timestamp()),
107
  )
@@ -132,7 +131,7 @@ def only_new_weekly_analysis():
132
 
133
  save_historical_data()
134
  try:
135
- clean_old_data_from_parquet_files("2024-11-12")
136
  except Exception as e:
137
  print("Error cleaning the oldest information from parquet files")
138
  print(f"reason = {e}")
@@ -143,5 +142,28 @@ def only_new_weekly_analysis():
143
  logging.info("Weekly analysis files generated and saved")
144
 
145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  if __name__ == "__main__":
147
  only_new_weekly_analysis()
 
 
3
  import pandas as pd
4
  from markets import etl as mkt_etl, DEFAULT_FILENAME as MARKETS_FILENAME, fpmmTrades_etl
5
  from tools import DEFAULT_FILENAME as TOOLS_FILENAME, generate_tools_file
6
+ from profitability import run_profitability_analysis, add_trades_profitability
7
  from utils import (
8
  get_question,
9
  current_answer,
 
22
  from cleaning_old_info import clean_old_data_from_parquet_files
23
  from web3_utils import updating_timestamps
24
  from manage_space_files import move_files
25
+ from cloud_storage import upload_historical_file
26
  from tools_metrics import compute_tools_based_datasets
27
 
28
 
 
64
  filename = f"tools_{timestamp}.parquet"
65
  tools.to_parquet(HIST_DIR / filename, index=False)
66
  # save into cloud storage
67
+ upload_historical_file(filename)
68
  except Exception as e:
69
  print(f"Error saving tools file in the historical folder {e}")
70
 
 
73
  filename = f"all_trades_profitability_{timestamp}.parquet"
74
  all_trades.to_parquet(HIST_DIR / filename, index=False)
75
  # save into cloud storage
76
+ upload_historical_file(filename)
77
 
78
  except Exception as e:
79
  print(
 
101
 
102
  # FpmmTrades ETL
103
  fpmmTrades_etl(
 
104
  trades_filename="new_fpmmTrades.parquet",
105
  from_timestamp=int(latest_timestamp.timestamp()),
106
  )
 
131
 
132
  save_historical_data()
133
  try:
134
+ clean_old_data_from_parquet_files("2024-11-19")
135
  except Exception as e:
136
  print("Error cleaning the oldest information from parquet files")
137
  print(f"reason = {e}")
 
142
  logging.info("Weekly analysis files generated and saved")
143
 
144
 
145
+ def restoring_trades_data(from_date: str, to_date: str):
146
+ # Convert the string to datetime64[ns, UTC]
147
+ min_date_utc = pd.to_datetime(from_date, format="%Y-%m-%d", utc=True)
148
+ max_date_utc = pd.to_datetime(to_date, format="%Y-%m-%d", utc=True)
149
+ logging.info("Running markets ETL")
150
+ mkt_etl(MARKETS_FILENAME)
151
+ logging.info("Markets ETL completed")
152
+
153
+ fpmmTrades_etl(
154
+ trades_filename="missing_fpmmTrades.parquet",
155
+ from_timestamp=int(min_date_utc.timestamp()),
156
+ to_timestamp=int(max_date_utc.timestamp()),
157
+ )
158
+
159
+ # merge with the old file
160
+ print("Merging with previous fpmmTrades file")
161
+ update_fpmmTrades_parquet(trades_filename="missing_fpmmTrades.parquet")
162
+
163
+ # adding tools information
164
+ add_trades_profitability(trades_filename="missing_fpmmTrades.parquet")
165
+
166
+
167
  if __name__ == "__main__":
168
  only_new_weekly_analysis()
169
+ # restoring_trades_data("2024-12-28", "2025-01-07")
scripts/staking.py CHANGED
@@ -22,13 +22,27 @@ STAKING_PROGRAMS_QS = {
22
  "quickstart_beta_expert": "0x5344B7DD311e5d3DdDd46A4f71481bD7b05AAA3e",
23
  "quickstart_beta_expert_2": "0xb964e44c126410df341ae04B13aB10A985fE3513",
24
  "quickstart_beta_expert_3": "0x80faD33Cadb5F53f9D29F02Db97D682E8b101618",
 
 
 
 
 
 
 
 
 
25
  }
26
 
27
  STAKING_PROGRAMS_PEARL = {
28
  "pearl_alpha": "0xEE9F19b5DF06c7E8Bfc7B28745dcf944C504198A",
29
  "pearl_beta": "0xeF44Fb0842DDeF59D37f85D61A1eF492bbA6135d",
30
  "pearl_beta_2": "0x1c2F82413666d2a3fD8bC337b0268e62dDF67434",
 
 
 
31
  }
 
 
32
  SERVICE_REGISTRY_ADDRESS = "0x9338b5153AE39BB89f50468E608eD9d764B755fD"
33
 
34
 
@@ -94,6 +108,8 @@ def get_service_data(service_registry: Any, service_id: int) -> dict:
94
  state = data[-1]
95
  # print(f"address = {address}")
96
  # print(f"state={state}")
 
 
97
  if address != "0x0000000000000000000000000000000000000000":
98
  tmp_map[service_id] = {
99
  "safe_address": address,
@@ -103,7 +119,7 @@ def get_service_data(service_registry: Any, service_id: int) -> dict:
103
  return tmp_map
104
 
105
 
106
- def update_service_map(start: int = 1, end: int = 1000):
107
  if os.path.exists(DATA_DIR / "service_map.pkl"):
108
  with open(DATA_DIR / "service_map.pkl", "rb") as f:
109
  service_map = pickle.load(f)
@@ -197,11 +213,85 @@ def label_trades_by_staking(trades_df: pd.DataFrame, start: int = None) -> None:
197
  return trades_df
198
 
199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  if __name__ == "__main__":
201
  # create_service_map()
202
- trades_df = pd.read_parquet(TMP_DIR / "all_trades_df.parquet")
203
- trades_df = trades_df.loc[trades_df["is_invalid"] == False]
204
-
205
- trades_df = label_trades_by_staking(trades_df=trades_df, start=8)
206
- print(trades_df.staking.value_counts())
207
- trades_df.to_parquet(TMP_DIR / "result_staking.parquet", index=False)
 
 
 
 
 
 
 
 
 
 
 
 
22
  "quickstart_beta_expert": "0x5344B7DD311e5d3DdDd46A4f71481bD7b05AAA3e",
23
  "quickstart_beta_expert_2": "0xb964e44c126410df341ae04B13aB10A985fE3513",
24
  "quickstart_beta_expert_3": "0x80faD33Cadb5F53f9D29F02Db97D682E8b101618",
25
+ "quickstart_beta_expert_4": "0xaD9d891134443B443D7F30013c7e14Fe27F2E029",
26
+ "quickstart_beta_expert_5": "0xE56dF1E563De1B10715cB313D514af350D207212",
27
+ "quickstart_beta_expert_6": "0x2546214aEE7eEa4bEE7689C81231017CA231Dc93",
28
+ "quickstart_beta_expert_7": "0xD7A3C8b975f71030135f1a66e9e23164d54fF455",
29
+ "quickstart_beta_expert_8": "0x356C108D49C5eebd21c84c04E9162de41933030c",
30
+ "quickstart_beta_expert_9": "0x17dBAe44BC5618Cc254055b386A29576b4F87015",
31
+ "quickstart_beta_expert_10": "0xB0ef657b8302bd2c74B6E6D9B2b4b39145b19c6f",
32
+ "quickstart_beta_expert_11": "0x3112c1613eAC3dBAE3D4E38CeF023eb9E2C91CF7",
33
+ "quickstart_beta_expert_12": "0xF4a75F476801B3fBB2e7093aCDcc3576593Cc1fc",
34
  }
35
 
36
  STAKING_PROGRAMS_PEARL = {
37
  "pearl_alpha": "0xEE9F19b5DF06c7E8Bfc7B28745dcf944C504198A",
38
  "pearl_beta": "0xeF44Fb0842DDeF59D37f85D61A1eF492bbA6135d",
39
  "pearl_beta_2": "0x1c2F82413666d2a3fD8bC337b0268e62dDF67434",
40
+ "pearl_beta_3": "0xBd59Ff0522aA773cB6074ce83cD1e4a05A457bc1",
41
+ "pearl_beta_4": "0x3052451e1eAee78e62E169AfdF6288F8791F2918",
42
+ "pearl_beta_5": "0x4Abe376Fda28c2F43b84884E5f822eA775DeA9F4",
43
  }
44
+
45
+
46
  SERVICE_REGISTRY_ADDRESS = "0x9338b5153AE39BB89f50468E608eD9d764B755fD"
47
 
48
 
 
108
  state = data[-1]
109
  # print(f"address = {address}")
110
  # print(f"state={state}")
111
+ # PEARL trade
112
+
113
  if address != "0x0000000000000000000000000000000000000000":
114
  tmp_map[service_id] = {
115
  "safe_address": address,
 
119
  return tmp_map
120
 
121
 
122
+ def update_service_map(start: int = 1, end: int = 2000):
123
  if os.path.exists(DATA_DIR / "service_map.pkl"):
124
  with open(DATA_DIR / "service_map.pkl", "rb") as f:
125
  service_map = pickle.load(f)
 
213
  return trades_df
214
 
215
 
216
+ def generate_retention_activity_file():
217
+ tools = pd.read_parquet(TMP_DIR / "tools.parquet")
218
+ tools["request_time"] = pd.to_datetime(tools["request_time"])
219
+ tools["request_date"] = tools["request_time"].dt.date
220
+ tools = tools.sort_values(by="request_time", ascending=True)
221
+ reduced_tools_df = tools[
222
+ ["trader_address", "request_time", "market_creator", "request_date"]
223
+ ]
224
+ print(f"length of reduced tools before labeling = {len(reduced_tools_df)}")
225
+ reduced_tools_df = label_trades_by_staking(trades_df=reduced_tools_df)
226
+ print(f"length of reduced tools after labeling = {len(reduced_tools_df)}")
227
+ reduced_tools_df = reduced_tools_df.sort_values(by="request_time", ascending=True)
228
+ reduced_tools_df["month_year_week"] = (
229
+ pd.to_datetime(tools["request_time"])
230
+ .dt.to_period("W")
231
+ .dt.start_time.dt.strftime("%b-%d-%Y")
232
+ )
233
+ reduced_tools_df.to_parquet(TMP_DIR / "retention_activity.parquet")
234
+ return True
235
+
236
+
237
+ def check_list_addresses(address_list: list):
238
+ with open(DATA_DIR / "service_map.pkl", "rb") as f:
239
+ service_map = pickle.load(f)
240
+ # check if it is part of any service id on the map
241
+ mapping = {}
242
+ print(f"length of service map={len(service_map)}")
243
+ keys = service_map.keys()
244
+ last_key = max(keys)
245
+
246
+ print(f"last service key = {last_key}")
247
+ update_service_map(start=last_key)
248
+ found_key = -1
249
+ for trader_address in address_list:
250
+ for key, value in service_map.items():
251
+ if value["safe_address"].lower() == trader_address.lower():
252
+ # found a service
253
+ found_key = key
254
+ mapping[trader_address] = "Olas"
255
+
256
+ if found_key == -1:
257
+ mapping[trader_address] = "non_Olas"
258
+ print("mapping")
259
+ print(mapping)
260
+
261
+
262
+ def check_service_map():
263
+ with open(DATA_DIR / "service_map.pkl", "rb") as f:
264
+ service_map = pickle.load(f)
265
+ # check if it is part of any service id on the map
266
+ mapping = {}
267
+ print(f"length of service map={len(service_map)}")
268
+ keys = service_map.keys()
269
+ last_key = max(keys)
270
+ print(f"last key ={last_key}")
271
+ missing_keys = 0
272
+ for i in range(1, last_key):
273
+ if i not in keys:
274
+ missing_keys += 1
275
+ print(f"missing key = {i}")
276
+ print(f"total missing keys = {missing_keys}")
277
+
278
+
279
  if __name__ == "__main__":
280
  # create_service_map()
281
+ # trades_df = pd.read_parquet(TMP_DIR / "all_trades_df.parquet")
282
+ # trades_df = trades_df.loc[trades_df["is_invalid"] == False]
283
+
284
+ # trades_df = label_trades_by_staking(trades_df=trades_df, start=8)
285
+ # print(trades_df.staking.value_counts())
286
+ # trades_df.to_parquet(TMP_DIR / "result_staking.parquet", index=False)
287
+ # generate_retention_activity_file()
288
+ a_list = [
289
+ "0x027592700fafc4db3221bb662d7bdc7f546a2bb5",
290
+ "0x0845f4ad01a2f41da618848c7a9e56b64377965e",
291
+ ]
292
+ # check_list_addresses(address_list=a_list)
293
+ # update_service_map()
294
+ # check_service_map()
295
+ unknown_traders = pd.read_parquet(DATA_DIR / "unknown_traders.parquet")
296
+ unknown_traders = label_trades_by_staking(trades_df=unknown_traders)
297
+ unknown_traders.to_parquet(DATA_DIR / "unknown_traders.parquet", index=False)
scripts/tools_metrics.py CHANGED
@@ -61,7 +61,9 @@ def prepare_tools(tools: pd.DataFrame) -> pd.DataFrame:
61
  tools = tools.sort_values(by="request_time", ascending=True)
62
 
63
  tools["request_month_year_week"] = (
64
- pd.to_datetime(tools["request_time"]).dt.to_period("W").dt.strftime("%b-%d-%Y")
 
 
65
  )
66
  # preparing the tools graph
67
  # adding the total
 
61
  tools = tools.sort_values(by="request_time", ascending=True)
62
 
63
  tools["request_month_year_week"] = (
64
+ pd.to_datetime(tools["request_time"])
65
+ .dt.to_period("W")
66
+ .dt.start_time.dt.strftime("%b-%d-%Y")
67
  )
68
  # preparing the tools graph
69
  # adding the total
scripts/web3_utils.py CHANGED
@@ -132,7 +132,9 @@ def updating_timestamps(rpc: str, tools_filename: str):
132
  "%Y-%m"
133
  )
134
  tools["request_month_year_week"] = (
135
- pd.to_datetime(tools["request_time"]).dt.to_period("W").astype(str)
 
 
136
  )
137
 
138
  # Save the tools data after the updates on the content
@@ -178,7 +180,7 @@ def query_conditional_tokens_gc_subgraph(creator: str) -> dict[str, Any]:
178
  userPositions_id_gt=userPositions_id_gt,
179
  )
180
  content_json = {"query": query}
181
- print("sending query to subgraph")
182
  res = requests.post(subgraph, headers=headers, json=content_json)
183
  result_json = res.json()
184
  # print(f"result = {result_json}")
@@ -229,6 +231,7 @@ def query_omen_xdai_subgraph(
229
  first=QUERY_BATCH_SIZE,
230
  id_gt=id_gt,
231
  )
 
232
  content_json = to_content(query)
233
 
234
  res = requests.post(omen_subgraph, headers=headers, json=content_json)
 
132
  "%Y-%m"
133
  )
134
  tools["request_month_year_week"] = (
135
+ pd.to_datetime(tools["request_time"])
136
+ .dt.to_period("W")
137
+ .dt.start_time.dt.strftime("%b-%d-%Y")
138
  )
139
 
140
  # Save the tools data after the updates on the content
 
180
  userPositions_id_gt=userPositions_id_gt,
181
  )
182
  content_json = {"query": query}
183
+ # print("sending query to subgraph")
184
  res = requests.post(subgraph, headers=headers, json=content_json)
185
  result_json = res.json()
186
  # print(f"result = {result_json}")
 
231
  first=QUERY_BATCH_SIZE,
232
  id_gt=id_gt,
233
  )
234
+ print(f"omen query={query}")
235
  content_json = to_content(query)
236
 
237
  res = requests.post(omen_subgraph, headers=headers, json=content_json)
tabs/tool_win.py CHANGED
@@ -14,7 +14,9 @@ def prepare_tools(tools: pd.DataFrame) -> pd.DataFrame:
14
  tools = tools.sort_values(by="request_time", ascending=True)
15
 
16
  tools["request_month_year_week"] = (
17
- pd.to_datetime(tools["request_time"]).dt.to_period("W").dt.strftime("%b-%d-%Y")
 
 
18
  )
19
  # preparing the tools graph
20
  # adding the total
 
14
  tools = tools.sort_values(by="request_time", ascending=True)
15
 
16
  tools["request_month_year_week"] = (
17
+ pd.to_datetime(tools["request_time"])
18
+ .dt.to_period("W")
19
+ .dt.start_time.dt.strftime("%b-%d-%Y")
20
  )
21
  # preparing the tools graph
22
  # adding the total
tabs/trades.py CHANGED
@@ -21,7 +21,9 @@ def prepare_trades(trades_df: pd.DataFrame) -> pd.DataFrame:
21
  trades_df["creation_timestamp"].dt.to_period("M").astype(str)
22
  )
23
  trades_df["month_year_week"] = (
24
- trades_df["creation_timestamp"].dt.to_period("W").dt.strftime("%b-%d-%Y")
 
 
25
  )
26
  trades_df["winning_trade"] = trades_df["winning_trade"].astype(int)
27
  return trades_df
 
21
  trades_df["creation_timestamp"].dt.to_period("M").astype(str)
22
  )
23
  trades_df["month_year_week"] = (
24
+ trades_df["creation_timestamp"]
25
+ .dt.to_period("W")
26
+ .dt.start_time.dt.strftime("%b-%d-%Y")
27
  )
28
  trades_df["winning_trade"] = trades_df["winning_trade"].astype(int)
29
  return trades_df