bulltrackers-module 1.0.950 → 1.0.951

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,7 +29,7 @@ exports.config = {
29
29
  filter: { user_type: 'POPULAR_INVESTOR' }
30
30
  },
31
31
  'portfolio_snapshots': {
32
- lookback: 0,
32
+ lookback: 7, // FIX: Changed to 7 to allow historical diffing of positions
33
33
  mandatory: false,
34
34
  fields: ['user_id', 'portfolio_data', 'date'],
35
35
  filter: { user_type: 'POPULAR_INVESTOR' }
@@ -122,21 +122,99 @@ exports.process = (ctx) => {
122
122
  const topPIRiskChange7dSlice = topPIRiskChange7d.slice(0, TOP_RISK_CHANGE_N);
123
123
 
124
124
  // -------------------------------------------------------------------------
125
- // 2. Most purchased / most sold assets (from trade_history_snapshots)
125
+ // Define the 7-day boundary for asset trading tracking
126
126
  // -------------------------------------------------------------------------
127
+ const targetDate = new Date(date);
128
+ const sevenDaysAgo = new Date(targetDate.getTime() - 7 * 24 * 60 * 60 * 1000);
129
+
127
130
  const purchaseCount = new Map();
128
131
  const saleCount = new Map();
129
132
 
133
+ // -------------------------------------------------------------------------
134
+ // 2A. Most purchased / most sold assets (from trade_history_snapshots)
135
+ // -------------------------------------------------------------------------
130
136
  tradeHistory.forEach(row => {
131
137
  const trades = lib.trades.extractTrades(row);
132
138
  trades.forEach(trade => {
133
139
  const instrumentId = lib.trades.getInstrumentId(trade);
134
140
  if (instrumentId == null) return;
135
141
  const ticker = resolveTicker(instrumentId);
136
- if (lib.trades.isBuy(trade)) {
137
- purchaseCount.set(ticker, (purchaseCount.get(ticker) || 0) + 1);
142
+
143
+ const openDate = lib.trades.getOpenDate(trade);
144
+ const closeDate = lib.trades.getCloseDate(trade);
145
+ const isBuy = lib.trades.isBuy(trade); // True if Long position
146
+
147
+ if (isBuy) {
148
+ // If it's a Long position and was opened recently -> Purchase
149
+ if (openDate && openDate >= sevenDaysAgo && openDate <= targetDate) {
150
+ purchaseCount.set(ticker, (purchaseCount.get(ticker) || 0) + 1);
151
+ }
152
+ // If it's a Long position and was closed recently -> Sale
153
+ if (closeDate && closeDate >= sevenDaysAgo && closeDate <= targetDate) {
154
+ saleCount.set(ticker, (saleCount.get(ticker) || 0) + 1);
155
+ }
138
156
  } else {
139
- saleCount.set(ticker, (saleCount.get(ticker) || 0) + 1);
157
+ // For Short positions, opening is a Sale, closing is a Purchase
158
+ if (openDate && openDate >= sevenDaysAgo && openDate <= targetDate) {
159
+ saleCount.set(ticker, (saleCount.get(ticker) || 0) + 1);
160
+ }
161
+ if (closeDate && closeDate >= sevenDaysAgo && closeDate <= targetDate) {
162
+ purchaseCount.set(ticker, (purchaseCount.get(ticker) || 0) + 1);
163
+ }
164
+ }
165
+ });
166
+ });
167
+
168
+ // -------------------------------------------------------------------------
169
+ // 2B. Most purchased assets (from diffing portfolio_snapshots)
170
+ // -------------------------------------------------------------------------
171
+ const portfolioByUser = new Map();
172
+ const portfolioRows = toArray(data['portfolio_snapshots'] || []);
173
+
174
+ // Group snapshots by user
175
+ portfolioRows.forEach(row => {
176
+ const userId = lib.portfolio.getUserId(row);
177
+ if (!userId) return;
178
+
179
+ const rowDateStr = row.date && (typeof row.date.value === 'string' ? row.date.value : String(row.date).slice(0, 10));
180
+ if (!rowDateStr) return;
181
+
182
+ const rowDate = new Date(rowDateStr);
183
+ if (rowDate < sevenDaysAgo || rowDate > targetDate) return;
184
+
185
+ if (!portfolioByUser.has(userId)) portfolioByUser.set(userId, []);
186
+ portfolioByUser.get(userId).push({ date: rowDateStr, row });
187
+ });
188
+
189
+ // Diff the oldest and newest snapshot for each user in the 7-day window
190
+ portfolioByUser.forEach((snapshots) => {
191
+ if (snapshots.length < 2) return; // Need at least 2 days to diff
192
+
193
+ snapshots.sort((a, b) => a.date.localeCompare(b.date));
194
+
195
+ const oldestPositions = lib.portfolio.extractPositions(lib.portfolio.extractPortfolioData(snapshots[0].row));
196
+ const newestPositions = lib.portfolio.extractPositions(lib.portfolio.extractPortfolioData(snapshots[snapshots.length - 1].row));
197
+
198
+ // Helper to sum invested amount per asset
199
+ const getInvestedMap = (positions) => {
200
+ const map = new Map();
201
+ positions.forEach(p => {
202
+ const id = lib.portfolio.getInstrumentId(p);
203
+ if (id != null) map.set(id, (map.get(id) || 0) + lib.portfolio.getInvested(p));
204
+ });
205
+ return map;
206
+ };
207
+
208
+ const oldMap = getInvestedMap(oldestPositions);
209
+ const newMap = getInvestedMap(newestPositions);
210
+
211
+ newMap.forEach((newInvested, instrumentId) => {
212
+ const oldInvested = oldMap.get(instrumentId) || 0;
213
+
214
+ // If the total invested amount in an asset increased, they purchased more of it
215
+ if (newInvested > oldInvested) {
216
+ const ticker = resolveTicker(instrumentId);
217
+ purchaseCount.set(ticker, (purchaseCount.get(ticker) || 0) + 1);
140
218
  }
141
219
  });
142
220
  });
@@ -226,4 +304,4 @@ exports.process = (ctx) => {
226
304
  recentAlertEvents
227
305
  }
228
306
  };
229
- };
307
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.950",
3
+ "version": "1.0.951",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [