|
|
@@ -0,0 +1,144 @@
|
|
|
+//npm install @polymarket/clob-client
|
|
|
+//npm install ethers
|
|
|
+//npm install dotenv
|
|
|
+
|
|
|
+import {
|
|
|
+ ClobClient,
|
|
|
+ OrderType,
|
|
|
+ Side,
|
|
|
+} from '@polymarket/clob-client'
|
|
|
+import { Wallet } from '@ethersproject/wallet'
|
|
|
+import dotenv from 'dotenv'
|
|
|
+
|
|
|
+// 加载环境变量 (.env.local 优先)
|
|
|
+dotenv.config({ path: '.env.local' })
|
|
|
+
|
|
|
+// 必填校验
|
|
|
+if (!process.env.PRIVATE_KEY) {
|
|
|
+ console.error('错误: 请在 .env.local 文件中设置 PRIVATE_KEY')
|
|
|
+ process.exit(1)
|
|
|
+}
|
|
|
+if (!process.env.FUNDER_ADDRESS) {
|
|
|
+ console.error('错误: 请在 .env.local 文件中设置 FUNDER_ADDRESS')
|
|
|
+ process.exit(1)
|
|
|
+}
|
|
|
+
|
|
|
+const host = 'https://clob.polymarket.com'
|
|
|
+const funder = process.env.FUNDER_ADDRESS
|
|
|
+const signer = new Wallet(process.env.PRIVATE_KEY)
|
|
|
+
|
|
|
+// 从环境变量读取默认下单参数
|
|
|
+const signatureType = parseInt(process.env.SIGNATURE_TYPE) || 1
|
|
|
+const orderPrice = parseFloat(process.env.ORDER_PRICE) || 0.49
|
|
|
+const orderSize = parseInt(process.env.ORDER_SIZE) || 5
|
|
|
+
|
|
|
+function getSlug() {
|
|
|
+ const now = new Date()
|
|
|
+ const utcTimestamp = Math.floor(now.getTime() / 1000)
|
|
|
+ const intervalSeconds = 15 * 60
|
|
|
+ const nextInterval = Math.ceil(utcTimestamp / intervalSeconds) * intervalSeconds
|
|
|
+ const slug = `btc-updown-15m-${nextInterval}`
|
|
|
+
|
|
|
+ console.log('当前时间:', now.toISOString())
|
|
|
+ console.log('当前 UTC 时间戳:', utcTimestamp)
|
|
|
+ console.log('下一个 15 分钟间隔时间戳:', nextInterval)
|
|
|
+ console.log('生成的 slug:', slug)
|
|
|
+ return slug
|
|
|
+}
|
|
|
+
|
|
|
+async function getTokenIdsBySlug() {
|
|
|
+ const slug = getSlug()
|
|
|
+ console.log('正在获取市场信息,slug:', slug)
|
|
|
+
|
|
|
+ const response = await fetch(`https://gamma-api.polymarket.com/markets?slug=${slug}`)
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error(`HTTP 错误: ${response.status} ${response.statusText}`)
|
|
|
+ }
|
|
|
+ const data = await response.json()
|
|
|
+ if (!data || data.length === 0) {
|
|
|
+ throw new Error(`未找到 slug 为 ${slug} 的市场`)
|
|
|
+ }
|
|
|
+ if (!data[0].clobTokenIds) {
|
|
|
+ throw new Error('市场数据中缺少 clobTokenIds 字段')
|
|
|
+ }
|
|
|
+ const clobTokenIds = JSON.parse(data[0].clobTokenIds)
|
|
|
+ console.log('市场信息:', data[0].question)
|
|
|
+ console.log('UP tokenID:', clobTokenIds[0])
|
|
|
+ console.log('DOWN tokenID:', clobTokenIds[1])
|
|
|
+ return { upTokenID: clobTokenIds[0], downTokenID: clobTokenIds[1] }
|
|
|
+}
|
|
|
+
|
|
|
+function parseDirectionArg() {
|
|
|
+ const arg = (process.argv[2] || '').toLowerCase()
|
|
|
+ if (arg !== 'up' && arg !== 'down') {
|
|
|
+ console.error('用法: node bid.mjs <up|down> [price] [size]')
|
|
|
+ console.error('示例: node bid.mjs up 0.49 5')
|
|
|
+ process.exit(1)
|
|
|
+ }
|
|
|
+ const priceArg = process.argv[3]
|
|
|
+ const sizeArg = process.argv[4]
|
|
|
+ const price = priceArg ? parseFloat(priceArg) : orderPrice
|
|
|
+ const size = sizeArg ? parseInt(sizeArg) : orderSize
|
|
|
+ if (Number.isNaN(price) || Number.isNaN(size)) {
|
|
|
+ console.error('价格或数量无效。示例: node bid.mjs up 0.49 5')
|
|
|
+ process.exit(1)
|
|
|
+ }
|
|
|
+ return { direction: arg, price, size }
|
|
|
+}
|
|
|
+
|
|
|
+async function main() {
|
|
|
+ try {
|
|
|
+ const { direction, price, size } = parseDirectionArg()
|
|
|
+ console.log('配置信息:')
|
|
|
+ console.log('- 下单方向:', direction.toUpperCase())
|
|
|
+ console.log('- 订单价格:', price)
|
|
|
+ console.log('- 订单数量:', size)
|
|
|
+ console.log('- 签名类型:', signatureType)
|
|
|
+ console.log('- 地址:', funder)
|
|
|
+
|
|
|
+ console.log('正在初始化 CLOB 客户端...')
|
|
|
+ const creds = await new ClobClient(host, 137, signer).createOrDeriveApiKey()
|
|
|
+
|
|
|
+ const clobClient = new ClobClient(
|
|
|
+ host,
|
|
|
+ 137,
|
|
|
+ signer,
|
|
|
+ creds,
|
|
|
+ signatureType,
|
|
|
+ funder
|
|
|
+ )
|
|
|
+
|
|
|
+ console.log('正在获取市场 tokenID...')
|
|
|
+ const { upTokenID, downTokenID } = await getTokenIdsBySlug()
|
|
|
+ const tokenID = direction === 'up' ? upTokenID : downTokenID
|
|
|
+
|
|
|
+ console.log(`创建 ${direction.toUpperCase()} 限价单...`)
|
|
|
+ const result = await clobClient.createAndPostOrder(
|
|
|
+ {
|
|
|
+ tokenID,
|
|
|
+ price,
|
|
|
+ side: Side.BUY,
|
|
|
+ size,
|
|
|
+ feeRateBps: 0,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ tickSize: '0.01',
|
|
|
+ negRisk: false,
|
|
|
+ },
|
|
|
+ OrderType.GTC
|
|
|
+ )
|
|
|
+
|
|
|
+ if (result?.success) {
|
|
|
+ console.log(`${direction.toUpperCase()} 订单成功:`, result)
|
|
|
+ } else {
|
|
|
+ console.log(`${direction.toUpperCase()} 订单返回:`, result)
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('下单失败:', error.message)
|
|
|
+ console.error('完整错误信息:', error)
|
|
|
+ process.exit(1)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 执行
|
|
|
+main()
|