vincentjim1025 commited on
Commit
ee058aa
·
unverified ·
2 Parent(s): ead51b1 79e70e8

Merge pull request #2 from Vincent-J-Z/aws-prod

Browse files
Dockerfile CHANGED
@@ -1,33 +1,26 @@
1
- # ---------- build ----------
2
- FROM node:20-alpine AS build
3
  WORKDIR /app
4
-
5
- # deps
6
  COPY package.json package-lock.json* ./
7
  RUN npm ci --no-audit --no-fund || npm i --no-audit --no-fund
8
 
9
- # app source
 
 
10
  COPY . .
 
 
 
 
 
 
 
11
 
12
- # Mount HF secrets at build-time, write .env.production, then build
13
- # (These IDs MUST match your Secrets names in the Settings tab)
14
- RUN --mount=type=secret,id=VITE_SUPABASE_URL,mode=0444,required=true \
15
- --mount=type=secret,id=VITE_SUPABASE_ANON_KEY,mode=0444,required=true \
16
- sh -lc '\
17
- URL="$(cat /run/secrets/VITE_SUPABASE_URL)"; \
18
- ANON="$(cat /run/secrets/VITE_SUPABASE_ANON_KEY)"; \
19
- printf "VITE_SUPABASE_URL=%s\nVITE_SUPABASE_ANON_KEY=%s\n" "$URL" "$ANON" > .env.production; \
20
- echo "--- .env.production ---"; cat .env.production; echo "-----------------------"; \
21
- npm run build \
22
- '
23
-
24
- # ---------- runtime ----------
25
- FROM node:20-alpine AS runtime
26
  WORKDIR /app
27
-
28
- # serve built assets
29
  COPY --from=build /app/dist ./dist
30
- RUN npm i -g serve@14
31
-
32
  EXPOSE 4173
33
  CMD ["serve", "-s", "dist", "-l", "4173"]
 
 
 
1
+ FROM node:20-alpine AS deps
 
2
  WORKDIR /app
 
 
3
  COPY package.json package-lock.json* ./
4
  RUN npm ci --no-audit --no-fund || npm i --no-audit --no-fund
5
 
6
+ FROM node:20-alpine AS build
7
+ WORKDIR /app
8
+ COPY --from=deps /app/node_modules ./node_modules
9
  COPY . .
10
+ ARG VITE_SUPABASE_URL
11
+ ARG VITE_SUPABASE_ANON_KEY
12
+ ARG VITE_SUPABASE_SERVICE_ROLE_KEY
13
+ ENV VITE_SUPABASE_URL=$VITE_SUPABASE_URL \
14
+ VITE_SUPABASE_ANON_KEY=$VITE_SUPABASE_ANON_KEY \
15
+ VITE_SUPABASE_SERVICE_ROLE_KEY=$VITE_SUPABASE_SERVICE_ROLE_KEY
16
+ RUN npm run build
17
 
18
+ FROM node:20-alpine AS runner
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  WORKDIR /app
20
+ ENV NODE_ENV=production
 
21
  COPY --from=build /app/dist ./dist
22
+ RUN npm i -g serve@14.2.1
 
23
  EXPOSE 4173
24
  CMD ["serve", "-s", "dist", "-l", "4173"]
25
+
26
+
src/components/AgentFilters.vue CHANGED
@@ -186,18 +186,29 @@ export default {
186
  initializeSlider() {
187
  if (this.hasDateBounds) {
188
  // 查找 2025-08-01 在日期数组中的索引
189
- const defaultStartDate = new Date('2025-08-01')
190
  let startIndex = 0
191
 
 
 
 
 
 
 
 
 
192
  // 如果默认起始日期在范围内,找到对应的索引
193
- if (defaultStartDate >= new Date(this.dateBounds.min) && defaultStartDate <= new Date(this.dateBounds.max)) {
194
  startIndex = this.allDates.findIndex(d => {
195
- const dateStr = d.toISOString().split('T')[0]
196
- return dateStr === '2025-08-01'
197
  })
198
  // 如果没找到精确日期,找最接近的日期
199
  if (startIndex === -1) {
200
- startIndex = this.allDates.findIndex(d => d >= defaultStartDate)
 
 
 
201
  if (startIndex === -1) startIndex = 0
202
  }
203
  }
@@ -281,6 +292,8 @@ export default {
281
  // 重置到默认起始日期 2025-08-01
282
  this.initializeSlider()
283
  this.datesModel = []
 
 
284
  }
285
  },
286
  beforeUnmount() {
 
186
  initializeSlider() {
187
  if (this.hasDateBounds) {
188
  // 查找 2025-08-01 在日期数组中的索引
189
+ const targetDateStr = '2025-08-01'
190
  let startIndex = 0
191
 
192
+ // Normalize date bounds to strings for comparison
193
+ const minDateStr = this.dateBounds.min instanceof Date
194
+ ? this.dateBounds.min.toISOString().split('T')[0]
195
+ : (typeof this.dateBounds.min === 'string' ? this.dateBounds.min.split('T')[0] : '')
196
+ const maxDateStr = this.dateBounds.max instanceof Date
197
+ ? this.dateBounds.max.toISOString().split('T')[0]
198
+ : (typeof this.dateBounds.max === 'string' ? this.dateBounds.max.split('T')[0] : '')
199
+
200
  // 如果默认起始日期在范围内,找到对应的索引
201
+ if (targetDateStr >= minDateStr && targetDateStr <= maxDateStr) {
202
  startIndex = this.allDates.findIndex(d => {
203
+ const dateStr = d instanceof Date ? d.toISOString().split('T')[0] : (typeof d === 'string' ? d.split('T')[0] : '')
204
+ return dateStr === targetDateStr
205
  })
206
  // 如果没找到精确日期,找最接近的日期
207
  if (startIndex === -1) {
208
+ startIndex = this.allDates.findIndex(d => {
209
+ const dateStr = d instanceof Date ? d.toISOString().split('T')[0] : (typeof d === 'string' ? d.split('T')[0] : '')
210
+ return dateStr >= targetDateStr
211
+ })
212
  if (startIndex === -1) startIndex = 0
213
  }
214
  }
 
292
  // 重置到默认起始日期 2025-08-01
293
  this.initializeSlider()
294
  this.datesModel = []
295
+ // 触发 slider 结束事件
296
+ this.onSliderEnd()
297
  }
298
  },
299
  beforeUnmount() {
src/views/LeaderboardView.vue CHANGED
@@ -94,7 +94,6 @@ import { countNonTradingDaysBetweenForAsset, countTradingDaysBetweenForAsset } f
94
  import { computeBuyHoldEquity, computeStrategyEquity, calculateMetricsFromSeries, computeWinRate } from '../lib/perf.js'
95
  import { STRATEGIES } from '../lib/strategies.js'
96
  import emailjs from 'emailjs-com'
97
-
98
  export default {
99
  name: 'LeaderboardView',
100
  components: { AgentTable, AgentFilters, AssetsFilter, CompareChartE, Dialog, InputText },
@@ -208,9 +207,17 @@ export default {
208
  try {
209
  const isCrypto = row.asset === 'BTC' || row.asset === 'ETH'
210
  const seriesAll = Array.isArray(row.series) ? row.series : []
 
 
 
 
 
211
  const inRange = seriesAll.filter(p => {
212
- const d = new Date(p.date)
213
- return d >= start && d <= end
 
 
 
214
  })
215
  if (!inRange.length) return null
216
  // Get correct strategy config - row.strategy is the ID, we need the strategy type
 
94
  import { computeBuyHoldEquity, computeStrategyEquity, calculateMetricsFromSeries, computeWinRate } from '../lib/perf.js'
95
  import { STRATEGIES } from '../lib/strategies.js'
96
  import emailjs from 'emailjs-com'
 
97
  export default {
98
  name: 'LeaderboardView',
99
  components: { AgentTable, AgentFilters, AssetsFilter, CompareChartE, Dialog, InputText },
 
207
  try {
208
  const isCrypto = row.asset === 'BTC' || row.asset === 'ETH'
209
  const seriesAll = Array.isArray(row.series) ? row.series : []
210
+
211
+ // Normalize dates to YYYY-MM-DD format for comparison to avoid timezone issues
212
+ const startDateStr = start instanceof Date ? start.toISOString().split('T')[0] : (typeof start === 'string' ? start.split('T')[0] : start)
213
+ const endDateStr = end instanceof Date ? end.toISOString().split('T')[0] : (typeof end === 'string' ? end.split('T')[0] : end)
214
+
215
  const inRange = seriesAll.filter(p => {
216
+ if (!p || !p.date) return false
217
+ // Extract date part (YYYY-MM-DD) from the date string
218
+ const dateStr = typeof p.date === 'string' ? p.date.split('T')[0] : new Date(p.date).toISOString().split('T')[0]
219
+ // Use string comparison for dates to avoid timezone issues
220
+ return dateStr >= startDateStr && dateStr <= endDateStr
221
  })
222
  if (!inRange.length) return null
223
  // Get correct strategy config - row.strategy is the ID, we need the strategy type
src/views/LiveView.vue CHANGED
@@ -204,6 +204,7 @@ const asset = ref('BTC')
204
  const rowsRef = ref([])
205
  let allDecisions = []
206
  const cards = shallowRef([])
 
207
  const refreshing = ref(false)
208
 
209
  let unsubscribe = null
@@ -318,24 +319,22 @@ async function buildSeq(sel) {
318
  : allDecisions.filter(r => r.agent_name === agentName && r.asset === assetCode && r.model === model)
319
 
320
  seq.sort((a,b) => (a.date > b.date ? 1 : -1))
321
-
 
 
 
 
322
  // if using decision_ids, data is already prefiltered
323
  if (!ids.length) {
324
  const isCrypto = assetCode === 'BTC' || assetCode === 'ETH'
325
  if (!isCrypto) seq = await filterRowsToNyseTradingDays(seq)
326
-
327
- const cutoff = ASSET_CUTOFF[assetCode]
328
- if (cutoff) {
329
- const t0 = new Date(cutoff + 'T00:00:00Z')
330
- seq = seq.filter(r => new Date(r.date + 'T00:00:00Z') >= t0)
331
- }
332
  }
333
-
334
  return seq
335
  }
336
 
337
  async function computeEquities(sel) {
338
  const seq = await buildSeq(sel)
 
339
  if (!seq.length) return null
340
 
341
  const cfg = (STRATEGIES || []).find(s => s.id === sel.strategy) || { strategy: 'long_only', tradingMode: 'aggressive', fee: 0.0005 }
 
204
  const rowsRef = ref([])
205
  let allDecisions = []
206
  const cards = shallowRef([])
207
+ window.cards = cards
208
  const refreshing = ref(false)
209
 
210
  let unsubscribe = null
 
319
  : allDecisions.filter(r => r.agent_name === agentName && r.asset === assetCode && r.model === model)
320
 
321
  seq.sort((a,b) => (a.date > b.date ? 1 : -1))
322
+ const cutoff = ASSET_CUTOFF[assetCode]
323
+ if (cutoff) {
324
+ const t0 = new Date(cutoff + 'T00:00:00Z')
325
+ seq = seq.filter(r => new Date(r.date + 'T00:00:00Z') >= t0)
326
+ }
327
  // if using decision_ids, data is already prefiltered
328
  if (!ids.length) {
329
  const isCrypto = assetCode === 'BTC' || assetCode === 'ETH'
330
  if (!isCrypto) seq = await filterRowsToNyseTradingDays(seq)
 
 
 
 
 
 
331
  }
 
332
  return seq
333
  }
334
 
335
  async function computeEquities(sel) {
336
  const seq = await buildSeq(sel)
337
+
338
  if (!seq.length) return null
339
 
340
  const cfg = (STRATEGIES || []).find(s => s.id === sel.strategy) || { strategy: 'long_only', tradingMode: 'aggressive', fee: 0.0005 }