├── .github └── workflows │ └── release.yaml ├── .gitignore ├── CHANGELOG.md ├── DOC.md ├── README.EN.md ├── README.md ├── STRATEGY.CN.md ├── STRATEGY.md ├── command ├── db_update.go └── sql │ ├── strategy_templates.sql │ └── version │ ├── .gitkeep │ ├── 2.sql │ ├── 3.sql │ ├── 4.sql │ ├── 5.sql │ └── 6.sql ├── conf └── app.conf.example ├── controllers ├── account.go ├── command.go ├── config.go ├── eatRate.go ├── feature.go ├── futuresOrders.go ├── index.go ├── listenCoin.go ├── login.go ├── noticeCoin.go ├── orders.go ├── rush.go ├── spot.go ├── strategyTemplate.go └── testStrategyResult.go ├── db └── .gitkeep ├── doc ├── depth.js ├── exchangeInfo.js ├── fundingRate.js ├── fundingRateHistory.js ├── futuresAccount.js ├── futuresPrices.js ├── income.js ├── kline.js ├── order.js ├── position.js ├── userTrades.js ├── wsMarketTicker.js └── 常量含义.md ├── feature ├── api │ └── binance │ │ ├── delivery.go │ │ └── index.go ├── delivery.go ├── delivery_debug.go ├── feature.go ├── feature_debug.go ├── feature_listen.go ├── feature_notice.go ├── feature_rush.go ├── feature_test_strategy.go ├── feature_userdata.go ├── feature_util.go └── strategy │ ├── coin │ ├── coin1.go │ ├── coin2.go │ ├── coin3.go │ ├── coin4.go │ ├── coin5.go │ ├── coin6.go │ └── common.go │ ├── coin_Interface.go │ ├── line │ ├── common.go │ ├── line1.go │ ├── line2.go │ ├── line3.go │ ├── line4.go │ ├── line5.go │ ├── line6.go │ ├── line7.go │ ├── line_custom.go │ ├── parse_technology_config.go │ └── technology.go │ └── line_Interface.go ├── go.mod ├── go.sum ├── image.jpg ├── img ├── en │ ├── coins.jpg │ ├── config.png │ ├── custom_type1.png │ ├── custom_type2.png │ ├── dinding_future1.jpg │ ├── dingding_listen1.jpg │ ├── fundingrate.jpg │ ├── fundingrate_history.jpg │ ├── futures_notice.jpg │ ├── listen_chat_kc.jpg │ ├── listen_futures.jpg │ ├── listen_slack.jpg │ ├── listen_spot.jpg │ ├── order.jpg │ ├── rush.jpg │ ├── spot_notice.jpg │ ├── strategy_001.png │ ├── strategy_002.png │ ├── te_001.jpg │ ├── te_002.jpg │ └── te_003.jpg └── zh │ ├── coins.jpg │ ├── config.png │ ├── custom_type1.png │ ├── custom_type2.png │ ├── dingding.jpeg │ ├── dingding_future1.jpg │ ├── dingding_listen1.jpg │ ├── feature_notice.jpg │ ├── fundingrate.jpg │ ├── fundingrate_history.jpg │ ├── listen_chat_kc.jpg │ ├── listen_dingding.jpg │ ├── listen_futures.jpg │ ├── listen_slack.jpg │ ├── listen_spot.jpg │ ├── order.jpg │ ├── rush.jpg │ ├── spot_notice.jpg │ ├── strategy_001.png │ ├── strategy_002.png │ ├── te_001.jpg │ ├── te_002.jpg │ └── te_003.jpg ├── lang ├── config │ ├── en.json │ └── zh.json └── utils.go ├── main.go ├── middlewares ├── auth.go └── cors.go ├── models ├── delivery_symbols.go ├── futures_orders.go ├── futures_postions.go ├── spot_symbols.go └── tableStruct.go ├── notify ├── common.go ├── dingding.go ├── notify_interface.go └── slack.go ├── rate └── index.go ├── routers └── router.go ├── spot ├── api │ └── binance │ │ └── index.go ├── spot.go └── spot_util.go ├── static ├── css │ ├── app.efb72e8a.css │ ├── chunk-0b00d329.0729eea3.css │ ├── chunk-0fa4606c.55664688.css │ ├── chunk-18e00042.53be0b48.css │ ├── chunk-241e90b9.fcc2b018.css │ ├── chunk-267b3368.7b9e4106.css │ ├── chunk-36094948.703d19c2.css │ ├── chunk-3b6cce88.13a7e89e.css │ ├── chunk-5150fc2a.0729eea3.css │ ├── chunk-689b6e77.a034e0e4.css │ ├── chunk-6d7794e0.329dbe9f.css │ ├── chunk-73c4c992.098e3fbb.css │ ├── chunk-937da73a.329dbe9f.css │ ├── chunk-d696a108.b1dd7a76.css │ └── chunk-libs.3dfb7769.css ├── favicon.ico ├── fonts │ ├── element-icons.535877f5.woff │ └── element-icons.732389de.ttf ├── img │ ├── 401.089007e7.gif │ ├── 404.a57b6f31.png │ └── 404_cloud.0f4bc32b.png ├── index.html └── js │ ├── app.399bcfc5.js │ ├── chunk-0b00d329.80819188.js │ ├── chunk-0fa4606c.5c248b40.js │ ├── chunk-18ba7d2d.6ba40780.js │ ├── chunk-18e00042.79e93592.js │ ├── chunk-241e90b9.ec4fda7f.js │ ├── chunk-267b3368.26ed6cec.js │ ├── chunk-2d2105d3.f51ab7bf.js │ ├── chunk-2d230fe7.1a45108d.js │ ├── chunk-36094948.457e0652.js │ ├── chunk-3b6cce88.0bf5b908.js │ ├── chunk-3c45c1ca.a9a54f64.js │ ├── chunk-3d2677a1.418a9ce1.js │ ├── chunk-4aaec48c.c8b93b79.js │ ├── chunk-5150fc2a.643c77ab.js │ ├── chunk-5f0eac28.7d24c47c.js │ ├── chunk-65d81035.e12cbb45.js │ ├── chunk-689b6e77.aa195dd7.js │ ├── chunk-6d7794e0.a994fc2d.js │ ├── chunk-70e6c787.05006783.js │ ├── chunk-73c4c992.e2128958.js │ ├── chunk-7539a299.14d2c85e.js │ ├── chunk-8109388c.3ef4ccb3.js │ ├── chunk-937da73a.01614a27.js │ ├── chunk-c504c1b2.a37cd66b.js │ ├── chunk-d696a108.0e3e7205.js │ ├── chunk-elementUI.0a9cbfa2.js │ └── chunk-libs.42d32b68.js ├── technology └── define.go ├── tests └── default_test.go ├── types └── futures_define.go ├── utils ├── config.go ├── index.go └── jwt.go └── views └── index.tpl /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Go Release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | # 触发条件:当推送到 master 分支或创建新标签时运行 7 | # branches: 8 | # - master 9 | tags: 10 | - 'v*' 11 | 12 | jobs: 13 | build-and-release: 14 | runs-on: ubuntu-22.04 15 | 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v4 19 | 20 | - name: Install MinGW-w64 21 | run: | 22 | sudo apt-get update 23 | sudo apt-get install -y mingw-w64 24 | 25 | - name: Set up Go 26 | uses: actions/setup-go@v4 27 | with: 28 | go-version: 1.21.x # 使用所需的 Go 版本 29 | cache: true 30 | - name: Install Bee tool 31 | run: | # 安装 Bee 工具 32 | go install github.com/beego/bee/v2@latest 33 | 34 | # - name: Build for multiple platforms 35 | # env: 36 | # GOOS: linux darwin windows # 指定目标操作系统 37 | # GOARCH: amd64 arm64 # 指定目标架构 38 | # run: | 39 | # for os in $GOOS; do 40 | # for arch in $GOARCH; do 41 | # output_name="myapp_${os}_${arch}" 42 | # echo "Building for $os/$arch..." 43 | # GOOS=$os GOARCH=$arch bee pack -o $output_name -be GOOS=$os GOARCH=$arch 44 | # done 45 | # done 46 | 47 | - name: Build and package for Linux 48 | # 构建并打包 Linux 版本, sqllite3 需要 CGO_ENABLED=1 49 | run: | 50 | export GOOS=linux 51 | export GOARCH=amd64 52 | export CGO_ENABLED=1 53 | bee pack -exr='.*.tar.gz' -a linux_amd64_feature -be GOOS=$GOOS GOARCH=$GOARCH 54 | 55 | - name: Build and package for Windows 56 | run: | 57 | export CC=x86_64-w64-mingw32-gcc 58 | export CXX=x86_64-w64-mingw32-g++ 59 | export CGO_ENABLED=1 60 | export GOOS=windows 61 | export GOARCH=amd64 62 | bee pack -exr='.*.tar.gz' -a windows_amd64_feature -be GOOS=$GOOS GOARCH=$GOARCH 63 | 64 | - name: Release Tag 65 | uses: softprops/action-gh-release@v2 66 | if: startsWith(github.ref, 'refs/tags/') 67 | with: 68 | draft: false 69 | prerelease: false 70 | files: | 71 | linux_amd64_feature.tar.gz 72 | windows_amd64_feature.tar.gz 73 | env: 74 | GITHUB_TOKEN: ${{ secrets.WORKFLOW_PERSONAL_ACCESS_TOKEN }} 75 | 76 | - name: Release Master 77 | uses: softprops/action-gh-release@v2 78 | if: startsWith(github.ref, 'refs/heads/master') 79 | with: 80 | tag_name: Master 81 | draft: false 82 | prerelease: false 83 | files: | 84 | linux_amd64_feature.tar.gz 85 | windows_amd64_feature.tar.gz 86 | env: 87 | GITHUB_TOKEN: ${{ secrets.WORKFLOW_PERSONAL_ACCESS_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | conf/app.conf 2 | go_binance_futures 3 | db/coin.db-shm 4 | db/coin.db-wal 5 | db/coin.db 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### Change Log 2 | 3 | ## v0.0.74 4 | - 修复一处问题 5 | 6 | ## v0.0.73 7 | - 增加最大亏损数量的自动缩放功能(连续3次盈利平仓+1,反之-1) 8 | - 修改合约列表页面的接口,改为后端分页(取消了自定义排序功能) 9 | - 修复一些其它问题 10 | 11 | ## v0.0.72 12 | - 更新测试策略的逻辑和保持和真实交易逻辑一致 13 | 14 | ## v0.0.71 15 | - 更新依赖和自定义策略的 position 16 | - 修复 bug 17 | - adshao/go-binance/v2 v2.8.2 版本有问题,ws 稳定断线,暂时降级到v2.6.2 18 | 19 | ## v0.0.70 20 | - 增加亏损限制开仓最大持仓数量限制 21 | 22 | ## v0.0.69 23 | - 修复 bug 24 | 25 | ## v0.0.68 26 | - 修复 bug 27 | 28 | ## v0.0.67 29 | - 适配 mysql 修复 bug 30 | 31 | ## v0.0.66 32 | - 修复一些 bug 33 | 34 | ## v0.0.65 35 | - 修复一些 bug 36 | 37 | ## v0.0.64 38 | - 更新数据库 futures_order 接入更多信息 39 | - 更新 ui 40 | 41 | ## v0.0.63 42 | 43 | - 更新获取订单和仓位的方式,使用 `futures_user_data` 可以切换,为 1 时使用 ws 时采用本地数据库存储方式(缺点需要保证ws稳定,无法实时获取仓位标记价格,使用ws订阅的最新价格代替,未实现收益手动计算得出),其它采用查询 binance 接口获取(缺点经常容易超过binance 的接口调用频率限制) 44 | - 更新自定义策略中仓位变量的名称和类型 45 | 46 | - before 47 | ``` 48 | Position.EntryPrice = "2500.2" 49 | Position.MarkPrice = "2400.2" 50 | Position.PositionAmt = "100.2" 51 | Position.UnRealizedProfit = "32.2" 52 | Position.Leverage = "3" 53 | Position.PositionSide = "LONG" 54 | Position.MarginType = "CROSS" 55 | ``` 56 | 57 | - after 58 | 59 | ``` 60 | Position.Symbol = "ETHUSDT" // 交易对 61 | Position.EntryPrice = 2500.2 // 持仓价格 62 | Position.MarkPrice = 2400.2 // 当前的标记价格 63 | Position.Amount = 1.2 // 当前的持仓数量,做空是负数 64 | Position.UnRealizedProfit = 32.2 // 当前收益 usdt, 亏损是负数 65 | Position.Leverage = 3 // 杠杆 66 | Position.PositionSide = "LONG" // 合约方向 67 | ``` 68 | 69 | ## v0.0.62 70 | - fixed: 抢购逻辑修复 71 | 72 | ## v0.0.61 73 | - 更新数据库 74 | - 更新抢购的逻辑,现在可以自定义预测价格,如果填写价格,就会第一时间按照价格进行挂单买卖,否则就会第一时间获取最新撮合交易的价格,进行市价买卖 75 | 76 | ## v0.0.60 77 | - 更新数据库 78 | - 更新合约订单页面的展示样式 79 | 80 | ## v0.0.59 81 | - fixed bug 82 | - 更新 ui 83 | 84 | ## v0.0.58 85 | - 重构数据库更新方式, 以后版本不再强绑定版本,可以安全无缝升级旧数据库 86 | 87 | ## v0.0.57 88 | - fixed bug 89 | - 合约列表和现货列表增加分页 90 | 91 | ## v0.0.56 92 | - fixed bug 93 | - 优化部分ui和底层逻辑 94 | 95 | ## v0.0.55 96 | - 降低接口请求频率,否则会超过 1 min 中 2400 权重和的限制 97 | 98 | ## v0.0.54 99 | - fixed bug 100 | 101 | ## v0.0.53 102 | - 增加仓位监听通知 103 | - 更新数据库 104 | - 暂时取消 spot 和 delivery 的 ws 监听 105 | - 更新 app.conf 添加数据库配置 106 | 107 | ``` 108 | [database] 109 | path = "./db/coin.db?_journal_mode=WAL&_busy_timeout=5000" 110 | ``` 111 | 112 | ## v0.0.52 113 | - 优化数据库连接方式,减少数据库锁死现象 114 | - 修复其它bug 115 | 116 | ## v0.0.51 117 | - 优化后台逻辑 118 | - 更新数据库 119 | 120 | ## v0.0.50 121 | - 合约列表页面增加对 usdc 的支持 122 | - 增加现货和币本位的数据库设计 123 | - 修复部分 bug 124 | - 更新数据库 125 | 126 | ## v0.0.49 127 | - 合约列表页面增加 置顶 功能 128 | - 修复部分 bug 129 | - 更新数据库 130 | 131 | ## v0.0.48 132 | - 添加一个变量 SystemStartTime 133 | 134 | ## v0.0.47 135 | - 修复bug 136 | - 更新 ui 137 | 138 | ## v0.0.46 139 | - 订单增加对币安订单的 orderId 关联 140 | - 增加自定义策略的测试结果集 141 | - 更新数据库 142 | 143 | ## v0.0.45 144 | - 自定义策略增加额外可用参数 145 | - 调整line3 146 | - 增加coin6 147 | - 更新数据库 148 | 149 | ## v0.0.44 150 | - 重构策略逻辑的入参和出参,在自定义策略时提供更多的可用参数(包含了当前币的仓位信息) 151 | 152 | ## v0.0.43 153 | - update conf 154 | 155 | ## v0.0.42 156 | - fixed bug 157 | 158 | ## v0.0.41 159 | - 平仓现在也支持自定义策略了 160 | - 合约的整体自定义策略测试功能添加了通知时间间隔 161 | 162 | ## v0.0.40 163 | - fixed bug 164 | 165 | ## v0.0.39 166 | - 增加新的变量 167 | - 增加合约的整体自定义策略测试功能 168 | - 增加合约的单独复制策略模板功能 169 | - 调整批量更新,开始影响所有的交易对 170 | - 更新数据库 171 | 172 | ## v0.0.38 173 | - 增加实验性质的套利资金费率功能 174 | - 更新数据库 175 | 176 | ## v0.0.37 177 | - 部分配置从文件切换为数据库,现在修改和可以实时生效,不用再重启程序 178 | - 更新数据库 179 | 180 | ## v0.0.36 181 | - 用户登录增加自定义的过期时间 182 | - 更新数据库 183 | 184 | ## v0.0.35 185 | - 增加了 `自定义策略模板` 页面,可以在合约交易页面批量更新中使用模板,批量更新自定义策略,只影响开启的交易对 186 | - 自定义策略中添加 BTCUSDT 和 ETHUSDT 的参数变量 187 | - 更新数据库 188 | 189 | ## v0.0.34 190 | - 修复了前端的 `自定义策略` 支持代码自动提示不正确的问题 191 | - 增加了2个自定义函数 `IsAsc` 是否是升序数组, `IsDesc` 是否是降序数组 192 | 193 | ## v0.0.33 194 | - 重构了 `自定义策略` 的逻辑,更新 `自定义策略` 文档 195 | - 编写 `自定义策略` 支持代码自动提示和测试功能 196 | 197 | ## v0.0.32 198 | - 更新 `自定义策略` 文档 199 | - 修复数据库 200 | 201 | ## v0.0.31 202 | - 合约交易升级,增加自定义策略规则 203 | - 更新数据库 204 | 205 | ## v0.0.30 206 | - 合约监听升级,增加增定义监听策略 207 | - 更新数据库 208 | 209 | ## v0.0.29 210 | - fixed:bug 211 | - 更新数据库 212 | - 合约监听添加技术指标(目前只有添加修改,没有后端监听逻辑) 213 | 214 | ## v0.0.28 215 | - fixed:bug 216 | 217 | ## v0.0.27 218 | - 合约交易增加当前信息的查看(账户信息,当前持仓,当前委托) 219 | - 修复其它报错情况 220 | 221 | ## v0.0.26 222 | - 取消前端自定义tab(切换tab不会带有缓存,切多个页面api一直存在影响性能) 223 | - 修复其它报错情况 224 | 225 | ## v0.0.25 226 | - 暂时取消抢币失败的实时通知,避免刷爆通知被封 227 | 228 | ## v0.0.24 229 | - 合约列表页面修正价格的精度,增加自定义刷新频率 230 | 231 | ## v0.0.23 232 | - 接入 slack 推送 233 | 234 | ## v0.0.22 235 | - 整体升级后台服务,重构通知服务 236 | - 推送添加多语言功能,目前有 中文, English 237 | 238 | ## v0.0.21 239 | - 整体升级 ui 240 | - 添加多语言功能,目前有 中文, English 241 | 242 | ## v0.0.20 243 | - 合约资金费率监听调整为单独开关 244 | - 增加合约资金费率页面,可以查询当前所有币种的资金费率和资金费率历史记录 245 | 246 | ## v0.0.19 247 | - 修复资金费率的监控逻辑 248 | 249 | ## v0.0.18 250 | - 首页调整 251 | - 增加合约监听页面的 kc 通道图的查看 252 | 253 | ## v0.0.17 254 | - fixed: listen bug 255 | 256 | ## v0.0.16 257 | - fixed: listen bug 258 | 259 | ## v0.0.15 260 | - 添加资金费率的监听 261 | 262 | ## v0.0.14 263 | - 添加 line7 策略 264 | - 添加新的监听策略 265 | - 更新 conf 说明 266 | - 添加 binance api proxy 配置(不含 websocket 的代理) 267 | 268 | ## v0.0.13 269 | - 修复 window 下打包程序不能正常运行的问题 270 | 271 | -------------------------------------------------------------------------------- /DOC.md: -------------------------------------------------------------------------------- 1 | ### 可能有用的策略 2 | 3 | 1. 监控 btc 每小时变化幅度,判定基本盘是震荡还是趋势 4 | 5 | 6 | ### 抢币心得 7 | 8 | 1. 如果是上币当天是普遍涨,那应该不会破发,可以直接抢买(第一时间抢购有可能买到比较高的价格,币安的初始定价很坑爹) 9 | 2. 如果感觉上去就破发,那应该第一时间就把挖到的币抢卖了 10 | 3. 合约初始的价格会比较靠谱,但是如果不能确定上去就涨的话,也少买因为初始交易量很少,不能一下买到最优价格,如果买到多会把你的订单打散成很多个提高你的购买均价 11 | 4. 小发现,币安的订单号竟然用的是自增ID,根据订单号就能知道你买的时间 12 | 5. 新币上线的价格预测,根据之前大家了解到的情况,挂一个大致的订单,例如大家都是预测0.4-0.6之间,可以尝试在第一时间挂单位0.4 * 0.7 = 0.28 的单,测试一下如果用程序第一时间挂单,能否买进,测试了几次,第一时间挂市价单,通常会比手动第一秒买入的价格还高 13 | 14 | 15 | ### 问题 16 | 1. spot 的 ws 正式 url 报错,走的 test url 17 | 18 | 19 | ### 数据库修复 20 | 21 | ``` 22 | PRAGMA integrity_check; // 检查 23 | VACUUM; // 修复 24 | ``` 25 | 26 | ### 想法 27 | 28 | 1. btc(最近1小时涨幅) 0.6 + eth 0.2 + bnb 0.1 + sol 0.1 29 | 2. 根据 1 的值,自定义选用不同的策略,大幅度涨跌的话选用 boll1 -------------------------------------------------------------------------------- /command/db_update.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "go_binance_futures/utils" 7 | "os" 8 | "strings" 9 | 10 | "github.com/beego/beego/v2/client/orm" 11 | "github.com/beego/beego/v2/core/logs" 12 | ) 13 | 14 | func InitData(version int64) error { 15 | createConfig(version) 16 | createStrategyTemplates() 17 | return nil 18 | } 19 | 20 | func createConfig(version int64) error { 21 | _, err := orm.NewOrm().Raw("INSERT INTO config (version,future_enable,future_buy_timeout,future_exclude_symbols,future_max_count,future_order_type,future_allow_long,future_allow_short,future_strategy_trade,future_strategy_coin,future_new_enable,spot_new_enable,notice_coin_enable,listen_coin_enable,listen_funding_rate_enable,future_test,future_test_notice_limit_min,spot_enable,delivery_enable,ws_futures_enable,ws_spot_enable,ws_delivery_enable,futures_position_convert_enable,loss_max_count,loss_auto_scale) VALUES (?, '0','300','BTCUSDT','10','MARKET','1','1','line3','coin6','0','0','0','0','1',0,65,0,0,1,0,0,0,10,0);", version).Exec() 22 | if err != nil { 23 | logs.Error("init config table error:", err) 24 | } 25 | return err 26 | } 27 | 28 | func createStrategyTemplates() error { 29 | filepath := "./command/sql/strategy_templates.sql" 30 | err := readAndExecuteSQLFile(filepath) 31 | if err != nil { 32 | logs.Error("init strategy_templates table error:", err) 33 | } 34 | return err 35 | } 36 | 37 | func ExecSqlFile(filepath string) error { 38 | // filepath := "./command/sql/strategy_templates.sql" 39 | err := readAndExecuteSQLFile(filepath) 40 | if err != nil { 41 | logs.Error("init strategy_templates table error:", err) 42 | } 43 | return err 44 | } 45 | 46 | func readAndExecuteSQLFile(filePath string) error { 47 | file, err := os.Open(filePath) 48 | if err != nil { 49 | return fmt.Errorf("can't open SQL file: %v", err) 50 | } 51 | defer file.Close() 52 | 53 | // 创建一个新的 ORM 实例 54 | o := orm.NewOrm() 55 | 56 | // 读取文件内容并逐行执行 SQL 语句 57 | scanner := bufio.NewScanner(file) 58 | var sqlStatements []string 59 | for scanner.Scan() { 60 | line := scanner.Text() 61 | if line == "" || strings.HasPrefix(line, "--") { 62 | continue // 跳过空行和注释 63 | } 64 | sqlStatements = append(sqlStatements, utils.EscapeJSON(line)) 65 | } 66 | 67 | if err := scanner.Err(); err != nil { 68 | return fmt.Errorf("read SQL file error: %v", err) 69 | } 70 | 71 | // 将多行 SQL 语句合并为一个完整的 SQL 语句 72 | sqlQuery := strings.Join(sqlStatements, " ") 73 | 74 | // 执行 SQL 语句 75 | _, err = o.Raw(sqlQuery).Exec() 76 | if err != nil { 77 | return fmt.Errorf("exec SQL error: %v", err) 78 | } 79 | 80 | return nil 81 | } 82 | 83 | func UpdateDatabase(oldVersion int64, newVersion int64) error { 84 | o := orm.NewOrm() 85 | to, err := o.Begin() 86 | if err != nil { 87 | logs.Error("begin transaction error:", err) 88 | return err 89 | } 90 | version := oldVersion + 1 91 | for ;version <= newVersion; version++ { 92 | // 逐个执行数据库更新脚本 93 | filepath := fmt.Sprintf("./command/sql/version/%d.sql", version) 94 | readAndExecuteSQLFile(filepath) 95 | } 96 | 97 | err = to.Commit() 98 | if err != nil { 99 | logs.Error("commit transaction error:", err) 100 | return err 101 | } 102 | return nil 103 | } -------------------------------------------------------------------------------- /command/sql/version/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/command/sql/version/.gitkeep -------------------------------------------------------------------------------- /command/sql/version/2.sql: -------------------------------------------------------------------------------- 1 | 2 | ALTER TABLE "order" ADD leverage INTEGER DEFAULT (5); -------------------------------------------------------------------------------- /command/sql/version/3.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE new_symbols ADD expect_price VARCHAR DEFAULT ('0'); 2 | -------------------------------------------------------------------------------- /command/sql/version/4.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `futures_orders` ( 2 | `id` integer NOT NULL primary key autoincrement, 3 | `symbol` varchar(255), 4 | `client_order_id` varchar(255), 5 | `order_id` varchar(255), 6 | `side` varchar(255), 7 | `position_side` varchar(255), 8 | `type` varchar(255), 9 | `status` varchar(255), 10 | `price` varchar(255), 11 | `orig_qty` varchar(255), 12 | `executed_qty` varchar(255) DEFAULT ('0'), 13 | 14 | `createTime` integer, 15 | `updateTime` integer 16 | ); -------------------------------------------------------------------------------- /command/sql/version/5.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE futures_positions ADD leverage INTEGER DEFAULT (1); 2 | -------------------------------------------------------------------------------- /command/sql/version/6.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE futures_orders ADD average_price VARCHAR DEFAULT ('0'); 2 | ALTER TABLE futures_orders ADD stop_price VARCHAR DEFAULT ('0'); 3 | ALTER TABLE futures_orders ADD commission_asset VARCHAR DEFAULT ('USDT'); 4 | ALTER TABLE futures_orders ADD commission VARCHAR DEFAULT ('0'); 5 | ALTER TABLE futures_orders ADD realized_pnl VARCHAR DEFAULT ('0'); 6 | -------------------------------------------------------------------------------- /conf/app.conf.example: -------------------------------------------------------------------------------- 1 | # 切记不要使用单引号, 只能使用双引号或者不使用引号, 注释需要单独一行 2 | appname = binance_futures 3 | # zh, en 4 | language = zh 5 | log = 1 6 | debug = 0 7 | 8 | [binance] 9 | api_key = "" 10 | api_secret = "" 11 | # 本地代理(如果不需要改为"") 12 | proxy_url = "http://127.0.0.1:7890" 13 | 14 | [database] 15 | # sqlite, mysql 16 | driver = "sqlite" 17 | path = "./db/coin.db?_journal_mode=WAL&_busy_timeout=5000" 18 | 19 | # mysql 20 | # driver = "mysql" 21 | # username = "" 22 | # password = "" 23 | # host= "" 24 | # port= "" 25 | # dbname = "" 26 | 27 | [ws] 28 | futures_user_data = 0 29 | 30 | [web] 31 | # web端口 32 | port = 3333 33 | # 首页 path 34 | index = zmkm 35 | # jwt key 36 | secret_key = 12321 37 | # 用户名 38 | username = admin 39 | # 密码 40 | password = admin 41 | # 过期时间(hour) 42 | expires = 24 43 | # 重启服务按钮命令 44 | commend_start = pm2 restart binance_futures 45 | # 停止合约服务命令 46 | commend_stop = pm2 stop binance_futures 47 | # 查看 web 日志的命令 /pm2-log 48 | commend_log = pm2 log binance_futures 49 | 50 | [notification] 51 | # dingding, slack 52 | channel = dingding 53 | 54 | [dingding] 55 | # token 56 | dingding_token = "" 57 | # 触发关键词 58 | dingding_word = "报警" 59 | 60 | [slack] 61 | slack_token = "" 62 | slack_channel_id = "" 63 | 64 | [external] 65 | # 外部链接 66 | links = [{"url": "url1", "title": "title1"}] -------------------------------------------------------------------------------- /controllers/account.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | "go_binance_futures/models" 6 | "go_binance_futures/utils" 7 | "math" 8 | "strconv" 9 | 10 | "github.com/adshao/go-binance/v2/futures" 11 | "github.com/beego/beego/v2/client/orm" 12 | "github.com/beego/beego/v2/server/web" 13 | ) 14 | 15 | type AccountController struct { 16 | web.Controller 17 | } 18 | 19 | func (ctrl *AccountController) GetBinanceFuturesAccount() { 20 | account, err := binance.GetFuturesAccount() 21 | 22 | if err != nil { 23 | ctrl.Ctx.Resp(map[string]interface{} { 24 | "code": 400, 25 | "msg": err.Error(), 26 | }) 27 | } 28 | 29 | var assets = make([]*futures.AccountAsset, len(account.Assets)) 30 | for _, asset := range account.Assets { 31 | walletBalance, _ := strconv.ParseFloat(asset.WalletBalance, 64) 32 | if walletBalance < 0.0000001 { 33 | continue 34 | } 35 | assets = append(assets, asset) 36 | } 37 | 38 | var positions = make([]*futures.AccountPosition, len(account.Positions)) 39 | for _, position := range account.Positions { 40 | positionAmt, _ := strconv.ParseFloat(position.PositionAmt, 64) 41 | positionAmtFloatAbs := math.Abs(positionAmt) // 空单为负数,纠正为绝对值 42 | if positionAmtFloatAbs < 0.0000001 { 43 | continue 44 | } 45 | positions = append(positions, position) 46 | } 47 | 48 | ctrl.Ctx.Resp(map[string]interface{} { 49 | "code": 200, 50 | "data": map[string]interface{} { 51 | "assets": assets, 52 | "positions": positions, 53 | }, 54 | "msg": "success", 55 | }) 56 | } 57 | 58 | func (ctrl *AccountController) GetBinanceFuturesPositions() { 59 | allPositions, err := binance.GetPosition(binance.PositionParams{}) 60 | if err != nil { 61 | ctrl.Ctx.Resp(map[string]interface{} { 62 | "code": 400, 63 | "msg": err.Error(), 64 | }) 65 | } 66 | 67 | var positions []*futures.PositionRisk 68 | for _, position := range allPositions { 69 | positionAmt, _ := strconv.ParseFloat(position.PositionAmt, 64) 70 | positionAmtFloatAbs := math.Abs(positionAmt) // 空单为负数,纠正为绝对值 71 | if positionAmtFloatAbs < 0.0000001 { 72 | continue 73 | } 74 | positions = append(positions, position) 75 | } 76 | 77 | ctrl.Ctx.Resp(map[string]interface{} { 78 | "code": 200, 79 | "data": map[string]interface{} { 80 | "positions": positions, 81 | }, 82 | "msg": "success", 83 | }) 84 | } 85 | 86 | func (ctrl *AccountController) GetBinanceFuturesOpenOrders() { 87 | allOpenOrders, err := binance.GetOpenOrder() 88 | 89 | if err != nil { 90 | ctrl.Ctx.Resp(map[string]interface{} { 91 | "code": 400, 92 | "msg": err.Error(), 93 | }) 94 | } 95 | 96 | ctrl.Ctx.Resp(map[string]interface{} { 97 | "code": 200, 98 | "data": map[string]interface{} { 99 | "openOrders": allOpenOrders, 100 | }, 101 | "msg": "success", 102 | }) 103 | } 104 | 105 | func (ctrl *AccountController) GetLocalFuturesPositions() { 106 | var positions []models.FuturesPosition 107 | o := orm.NewOrm() 108 | sql := "SELECT f.id, f.symbol, f.side, f.amount, f.leverage, f.margin_type, f.isolated_wallet, f.entry_price, s.close as mark_price FROM `futures_positions` f LEFT JOIN symbols s ON f.symbol = s.symbol where 1 = 1" 109 | sql += ` and f.amount <> '0'` 110 | _, err := o.Raw(sql).QueryRows(&positions) 111 | if err != nil { 112 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 113 | } 114 | 115 | var usePositions []models.FuturesPosition 116 | for _, position := range positions { 117 | positionAmt, _ := strconv.ParseFloat(position.Amount, 64) 118 | positionAmtFloatAbs := math.Abs(positionAmt) // 空单为负数,纠正为绝对值 119 | if positionAmtFloatAbs < 0.0000001 { 120 | continue 121 | } 122 | enterPrice_float64, _ := strconv.ParseFloat(position.EntryPrice, 64) 123 | markPrice_float64, _ := strconv.ParseFloat(position.MarkPrice, 64) 124 | unRealizedProfit := (markPrice_float64 - enterPrice_float64) * positionAmt // 未实现盈亏 125 | position.UnrealizedProfit = strconv.FormatFloat(unRealizedProfit, 'f', -1, 64) 126 | 127 | usePositions = append(usePositions, position) 128 | } 129 | 130 | ctrl.Ctx.Resp(map[string]interface{} { 131 | "code": 200, 132 | "data": map[string]interface{} { 133 | "positions": usePositions, 134 | }, 135 | "msg": "success", 136 | }) 137 | } 138 | 139 | func (ctrl *AccountController) GetLocalFuturesOpenOrders() { 140 | var orders []models.FuturesOrder 141 | o := orm.NewOrm() 142 | sql := "SELECT * FROM `futures_orders` as f where 1 = 1" 143 | sql += ` and (f.status = 'NEW' or f.status = 'PARTIALLY_FILLED')` // 下单类型 144 | _, err := o.Raw(sql).QueryRows(&orders) 145 | if err != nil { 146 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 147 | } 148 | 149 | ctrl.Ctx.Resp(map[string]interface{} { 150 | "code": 200, 151 | "data": map[string]interface{} { 152 | "openOrders": orders, 153 | }, 154 | "msg": "success", 155 | }) 156 | } 157 | 158 | func (ctrl *AccountController) EditLocalFuturesPositions() { 159 | id := ctrl.Ctx.Input.Param(":id") 160 | var position models.FuturesPosition 161 | o := orm.NewOrm() 162 | o.QueryTable(position.TableName()).Filter("Id", id).One(&position) 163 | 164 | ctrl.BindJSON(&position) 165 | 166 | _, err := o.Update(&position) // _ 是受影响的条数 167 | if err != nil { 168 | // 处理错误 169 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 170 | return 171 | } 172 | ctrl.Ctx.Resp(map[string]interface{} { 173 | "code": 200, 174 | "data": position, 175 | "msg": "success", 176 | }) 177 | } 178 | 179 | func (ctrl *AccountController) DelLocalFuturesPositions() { 180 | id := ctrl.Ctx.Input.Param(":id") 181 | var position models.FuturesPosition 182 | o := orm.NewOrm() 183 | o.QueryTable(position.TableName()).Filter("Id", id).One(&position) 184 | 185 | _, err := o.Delete(&position) 186 | if err != nil { 187 | // 处理错误 188 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 189 | return 190 | } 191 | ctrl.Ctx.Resp(map[string]interface{} { 192 | "code": 200, 193 | "data": nil, 194 | "msg": "success", 195 | }) 196 | } -------------------------------------------------------------------------------- /controllers/command.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "io" 5 | "os/exec" 6 | 7 | "github.com/beego/beego/v2/core/config" 8 | "github.com/beego/beego/v2/server/web" 9 | ) 10 | 11 | type CommandController struct { 12 | web.Controller 13 | } 14 | 15 | // git pull 16 | func (ctrl *CommandController) GitPull() { 17 | cmd := exec.Command("bash", "-c", "git pull") 18 | 19 | // 获取标准输出 20 | output, err := cmd.CombinedOutput() 21 | if err != nil { 22 | ctrl.Ctx.Resp(map[string]interface{} { 23 | "code": 400, 24 | "data": err, 25 | "msg": "error", 26 | }) 27 | } 28 | 29 | ctrl.Ctx.Resp(map[string]interface{} { 30 | "code": 200, 31 | "data": string(output), 32 | "msg": "success", 33 | }) 34 | } 35 | 36 | // 开启后台任务 37 | func (ctrl *CommandController) Start() { 38 | commend_start, _ := config.String("web::commend_start") 39 | cmd := exec.Command("bash", "-c", commend_start) 40 | cmd.Start() // 异步执行,不要获取结果,因为重启时此进程会挂掉导致http请求失败 41 | 42 | // // 获取标准输出 43 | // output, err := cmd.CombinedOutput() 44 | // if err != nil { 45 | // ctrl.Ctx.Resp(map[string]interface{} { 46 | // "code": 400, 47 | // "data": err, 48 | // "msg": "error", 49 | // }) 50 | // } 51 | 52 | ctrl.Ctx.Resp(map[string]interface{} { 53 | "code": 200, 54 | "msg": "success", 55 | }) 56 | } 57 | 58 | // 关闭后台任务 59 | func (ctrl *CommandController) Stop() { 60 | // content, err := os.ReadFile("./conf/app.conf") 61 | // if err != nil { 62 | // ctrl.Ctx.Resp(utils.ResJson(400, nil, "获取错误")) 63 | // return 64 | // } 65 | 66 | // pattern := "future_enable = 1" 67 | // replacement := "future_enable = 0" 68 | // r, err := regexp.Compile(pattern) 69 | // if err != nil { 70 | // panic(err) 71 | // } 72 | // result := r.ReplaceAllString(string(content), replacement) 73 | // err = os.WriteFile("./conf/app.conf", []byte(result), 0644) 74 | // if err != nil { 75 | // ctrl.Ctx.Resp(utils.ResJson(400, nil, "写入文件错误")) 76 | // return 77 | // } 78 | commend_stop, _ := config.String("web::commend_stop") 79 | cmd := exec.Command("bash", "-c", commend_stop) 80 | cmd.Start() // 异步执行,不要获取结果,因为重启时此进程会挂掉导致http请求失败 81 | 82 | ctrl.Ctx.Resp(map[string]interface{} { 83 | "code": 200, 84 | "msg": "success", 85 | }) 86 | } 87 | 88 | // pm2-log 89 | func (ctrl *CommandController) Pm2Log() { 90 | commend_log, _ := config.String("web::commend_log") 91 | paramsKey := ctrl.GetString("key") 92 | if paramsKey != "sorry510" { 93 | ctrl.Ctx.Resp(map[string]interface{} { 94 | "code": 400, 95 | "msg": "error", 96 | }) 97 | return 98 | } 99 | 100 | cmd := exec.Command("bash", "-c", commend_log) 101 | 102 | // 创建标准输出管道,用于读取命令的输出 103 | stdout, err := cmd.StdoutPipe() 104 | if err != nil { 105 | ctrl.Ctx.Resp(map[string]interface{} { 106 | "code": 400, 107 | "msg": err, 108 | }) 109 | return 110 | } 111 | 112 | ctrl.EnableRender = false 113 | ctrl.Ctx.ResponseWriter.Header().Set("Content-Type", "text/plain; charset=utf-8") 114 | 115 | // 开始执行命令 116 | if err := cmd.Start(); err != nil { 117 | ctrl.Ctx.Resp(map[string]interface{} { 118 | "code": 400, 119 | "msg": err, 120 | }) 121 | return 122 | } 123 | // ctrl.Ctx.Output.Body(stdout) 124 | // 直接从命令输出流读取数据并写入响应 125 | if _, err := io.Copy(ctrl.Ctx.ResponseWriter, stdout); err != nil { 126 | ctrl.Ctx.Resp(map[string]interface{} { 127 | "code": 400, 128 | "msg": err, 129 | }) 130 | return 131 | } 132 | } -------------------------------------------------------------------------------- /controllers/config.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "os" 5 | 6 | "go_binance_futures/utils" 7 | 8 | "github.com/beego/beego/v2/server/web" 9 | ) 10 | 11 | type ConfigController struct { 12 | web.Controller 13 | } 14 | 15 | func (ctrl *ConfigController) Get() { 16 | content, err := os.ReadFile("./conf/app.conf") 17 | if err != nil { 18 | ctrl.Ctx.Resp(utils.ResJson(400, nil, "获取错误")) 19 | return 20 | } 21 | 22 | ctrl.Ctx.Resp(map[string]interface{} { 23 | "code": 200, 24 | "data": map[string]interface{}{ 25 | "content": string(content), 26 | }, 27 | "msg": "success", 28 | }) 29 | } 30 | 31 | func (ctrl *ConfigController) Edit() { 32 | configContent := new(ConfigContent) 33 | ctrl.BindJSON(&configContent) 34 | 35 | err := os.WriteFile("./conf/app.conf", []byte(configContent.Code), 0644) 36 | if err != nil { 37 | ctrl.Ctx.Resp(utils.ResJson(400, nil, "获取错误")) 38 | return 39 | } 40 | 41 | ctrl.Ctx.Resp(map[string]interface{} { 42 | "code": 200, 43 | "msg": "success", 44 | }) 45 | } 46 | 47 | type ConfigContent struct { 48 | Code string `json:"code"` 49 | } 50 | -------------------------------------------------------------------------------- /controllers/futuresOrders.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "strconv" 5 | 6 | "go_binance_futures/models" 7 | "go_binance_futures/utils" 8 | 9 | "github.com/beego/beego/v2/client/orm" 10 | "github.com/beego/beego/v2/server/web" 11 | ) 12 | 13 | type FuturesOrderController struct { 14 | web.Controller 15 | } 16 | 17 | type FuturesOrderTableList struct { 18 | models.FuturesOrder 19 | NowPrice string `orm:"column(now_price)" json:"now_price"` 20 | } 21 | 22 | func (ctrl *FuturesOrderController) Get() { 23 | paramsSymbol := ctrl.GetString("symbol") 24 | paramPositionSide := ctrl.GetString("position_side") // LONG, SHORT, all 25 | paramsPage := ctrl.GetString("page", "1") 26 | paramsLimit := ctrl.GetString("limit", "20") 27 | paramsStartTime := ctrl.GetString("start_time") 28 | paramsEndTime := ctrl.GetString("end_time") 29 | paramsSide := ctrl.GetString("type") // BUY, SELL, all 30 | paramsStatus := ctrl.GetString("status") // NEW, PARTIALLY_FILLED, FILLED, CANCELED, REJECTED, EXPIRED, all 31 | 32 | page, _ := strconv.Atoi(paramsPage) 33 | limit, _ := strconv.Atoi(paramsLimit) 34 | offset := (page - 1) * limit 35 | 36 | o := orm.NewOrm() 37 | var orders []OrderTableList 38 | 39 | var total int64 40 | sql := "SELECT t.*, s.close as now_price FROM `futures_orders` t LEFT JOIN symbols s ON t.symbol = s.symbol where 1 = 1" 41 | countSql := "SELECT COUNT(*) FROM `futures_orders` t LEFT JOIN symbols s ON t.symbol = s.symbol where 1 = 1" 42 | 43 | if (paramsSymbol != "") { 44 | sql += ` and t.symbol like '%` + paramsSymbol + `%'` 45 | countSql += ` and t.symbol like '%` + paramsSymbol + `%'` 46 | } 47 | if (paramPositionSide != "all" && paramPositionSide != "") { 48 | sql += ` and t.positionSide = '` + paramPositionSide + `'` 49 | countSql += ` and t.positionSide = '` + paramPositionSide + `'` 50 | } 51 | if (paramsStartTime != "") { 52 | sql += ` and t.updateTime >= '` + paramsStartTime + `'` 53 | countSql += ` and t.updateTime >= '` + paramsStartTime + `'` 54 | } 55 | if (paramsEndTime != "") { 56 | sql += ` and t.updateTime <= '` + paramsEndTime + `'` 57 | countSql += ` and t.updateTime <= '` + paramsEndTime + `'` 58 | } 59 | if (paramsSide != "all" && paramsSide != "") { 60 | sql += ` and t.side = '` + paramsSide + `'` 61 | countSql += ` and t.side = '` + paramsSide + `'` 62 | } 63 | if (paramsStatus != "all" && paramsStatus != "") { 64 | sql += ` and t.status = '` + paramsStatus + `'` 65 | countSql += ` and t.status = '` + paramsStatus + `'` 66 | } 67 | 68 | sql = sql + " ORDER BY t.updateTime DESC LIMIT " + strconv.Itoa(limit) + " OFFSET " + strconv.Itoa(offset) 69 | _, err := o.Raw(sql).QueryRows(&orders) 70 | if err != nil { 71 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 72 | } 73 | err = o.Raw(countSql).QueryRow(&total) 74 | if err != nil { 75 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 76 | } 77 | 78 | ctrl.Ctx.Resp(map[string]interface{} { 79 | "code": 200, 80 | "data": map[string]interface{} { 81 | "total": total, 82 | "list": orders, 83 | }, 84 | "msg": "success", 85 | }) 86 | } 87 | 88 | // func (ctrl *FuturesOrderController) Delete() { 89 | // id := ctrl.Ctx.Input.Param(":id") 90 | // o := orm.NewOrm() 91 | // _, err := o.Raw("DELETE FROM order where id = ?", id).Exec() 92 | // if err != nil { 93 | // ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 94 | // return 95 | // } 96 | // ctrl.Ctx.Resp(utils.ResJson(200, nil)) 97 | // } 98 | 99 | // func (ctrl *FuturesOrderController) DeleteAll() { 100 | 101 | // o := orm.NewOrm() 102 | // _, err := o.Raw("DELETE FROM order where 1=1").Exec() 103 | // if err != nil { 104 | // // 处理错误 105 | // ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 106 | // return 107 | // } 108 | // ctrl.Ctx.Resp(utils.ResJson(200, nil)) 109 | // } 110 | -------------------------------------------------------------------------------- /controllers/index.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "go_binance_futures/notify" 5 | "go_binance_futures/utils" 6 | 7 | "github.com/beego/beego/v2/client/orm" 8 | "github.com/beego/beego/v2/core/config" 9 | "github.com/beego/beego/v2/core/logs" 10 | "github.com/beego/beego/v2/server/web" 11 | ) 12 | 13 | type IndexController struct { 14 | web.Controller 15 | } 16 | 17 | func (ctrl *IndexController) GetServiceConfig() { 18 | var debug, _ = config.String("debug") 19 | systemConfig, err := utils.GetSystemConfig() 20 | if err != nil { 21 | logs.Error("GetSystemConfig:", err) 22 | return 23 | } 24 | 25 | var coinExcludeSymbols = systemConfig.FutureExcludeSymbols 26 | var coinMaxCount = systemConfig.FutureMaxCount 27 | var coinOrderType = systemConfig.FutureOrderType 28 | var coinAllowLong = systemConfig.FutureAllowLong 29 | var coinAllowShort = systemConfig.FutureAllowShort 30 | 31 | var tradeFutureTest = systemConfig.FutureTest 32 | var tradeFutureTestNoticeLimitMin = systemConfig.FutureTestNoticeLimitMin 33 | var tradeFutureEnable = systemConfig.FutureEnable 34 | var tradeSpotEnable = systemConfig.SpotEnable 35 | var tradeDeliveryEnable = systemConfig.DeliveryEnable 36 | var tradeStrategyTrade = systemConfig.FutureStrategyTrade 37 | var tradeStrategyCoin = systemConfig.FutureStrategyCoin 38 | var tradeNewEnable = systemConfig.FutureNewEnable 39 | 40 | var spotNewEnable = systemConfig.SpotNewEnable 41 | 42 | var noticeCoinEnable = systemConfig.NoticeCoinEnable 43 | 44 | var listenCoinEnable = systemConfig.ListenCoinEnable 45 | var listenFundingRate = systemConfig.ListenFundingRateEnable 46 | var lossMaxCount = systemConfig.LossMaxCount 47 | var externalLinks, _ = config.String("external::links") 48 | 49 | ctrl.Ctx.Resp(map[string]interface{} { 50 | "code": 200, 51 | "data": map[string]interface{} { 52 | "debug": debug, 53 | 54 | "wsFuturesEnable": systemConfig.WsFuturesEnable, 55 | "wsSpotEnable": systemConfig.WsSpotEnable, 56 | "wsDeliveryEnable": systemConfig.WsDeliveryEnable, 57 | "futuresPositionConvertEnable": systemConfig.FuturesPositionConvertEnable, 58 | 59 | "coinExcludeSymbols": coinExcludeSymbols, 60 | "coinMaxCount": coinMaxCount, 61 | "coinOrderType": coinOrderType, 62 | "coinAllowLong": coinAllowLong, 63 | "coinAllowShort": coinAllowShort, 64 | 65 | "tradeFutureTest": tradeFutureTest, 66 | "tradeFutureTestNoticeLimitMin": tradeFutureTestNoticeLimitMin, 67 | "tradeFutureEnable": tradeFutureEnable, 68 | "tradeSpotEnable": tradeSpotEnable, 69 | "tradeDeliveryEnable": tradeDeliveryEnable, 70 | "tradeStrategyTrade": tradeStrategyTrade, 71 | "tradeStrategyCoin": tradeStrategyCoin, 72 | "tradeNewEnable": tradeNewEnable, 73 | 74 | "spotNewEnable": spotNewEnable, 75 | 76 | "noticeCoinEnable": noticeCoinEnable, 77 | 78 | "listenCoinEnable": listenCoinEnable, 79 | "listenFundingRate": listenFundingRate, 80 | "lossMaxCount": lossMaxCount, 81 | "lossAutoScale": systemConfig.LossAutoScale, 82 | 83 | "externalLinks": externalLinks, 84 | }, 85 | "msg": "success", 86 | }) 87 | } 88 | 89 | func (ctrl *IndexController) EditServiceConfig() { 90 | systemConfig, _ := utils.GetSystemConfig() 91 | 92 | ctrl.BindJSON(&systemConfig) 93 | 94 | _, err := orm.NewOrm().Update(&systemConfig) // _ 是受影响的条数 95 | if err != nil { 96 | // 处理错误 97 | ctrl.Ctx.Resp(utils.ResJson(400, nil, "edit failed")) 98 | return 99 | } 100 | ctrl.Ctx.Resp(map[string]interface{} { 101 | "code": 200, 102 | "data": systemConfig, 103 | "msg": "success", 104 | }) 105 | } 106 | 107 | func (ctrl *IndexController) TestPusher() { 108 | var pusher = notify.GetNotifyChannel() 109 | pusher.TestPusher() 110 | ctrl.Ctx.Resp(map[string]interface{} { 111 | "code": 200, 112 | "msg": "success", 113 | }) 114 | } -------------------------------------------------------------------------------- /controllers/login.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "go_binance_futures/utils" 5 | "strconv" 6 | 7 | "github.com/beego/beego/v2/core/config" 8 | "github.com/beego/beego/v2/server/web" 9 | ) 10 | 11 | var username, _ = config.String("web::username") 12 | var password, _ = config.String("web::password") 13 | var expires, _ = config.String("web::expires") 14 | var expires_int, _ = strconv.Atoi(expires) 15 | 16 | type LoginController struct { 17 | web.Controller 18 | } 19 | 20 | func (ctrl *LoginController) Post() { 21 | user := User{} 22 | err := ctrl.BindJSON(&user) 23 | if err != nil { 24 | ctrl.Ctx.Resp(utils.ResJson(101, nil, "参数错误")) 25 | } else { 26 | if user.UserName == username && user.Password == password { 27 | token, err := utils.GenerateToken(user.UserName, expires_int) 28 | if err != nil { 29 | ctrl.Ctx.WriteString(err.Error()) 30 | return 31 | } 32 | ctrl.Ctx.Resp(utils.ResJson(200, map[string]interface{}{ 33 | "token": "Bearer " +token, 34 | })) 35 | } else { 36 | ctrl.Ctx.Resp(utils.ResJson(101, nil, "账号或密码错误")) 37 | } 38 | } 39 | } 40 | 41 | type User struct { 42 | UserName string `json:"username"` 43 | Password string `json:"password"` 44 | } -------------------------------------------------------------------------------- /controllers/noticeCoin.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "strconv" 5 | 6 | "go_binance_futures/feature" 7 | "go_binance_futures/models" 8 | "go_binance_futures/spot" 9 | "go_binance_futures/utils" 10 | 11 | "github.com/beego/beego/v2/client/orm" 12 | "github.com/beego/beego/v2/server/web" 13 | ) 14 | 15 | type NoticeCoinController struct { 16 | web.Controller 17 | } 18 | 19 | func (ctrl *NoticeCoinController) Get() { 20 | paramsType := ctrl.GetString("type", "") 21 | 22 | o := orm.NewOrm() 23 | var symbols []models.NoticeSymbols 24 | query := o.QueryTable("notice_symbols") 25 | if paramsType != "" { 26 | query = query.Filter("Type", paramsType) 27 | } 28 | _, err := query.OrderBy("ID").All(&symbols) 29 | if err != nil { 30 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 31 | } 32 | 33 | ctrl.Ctx.Resp(map[string]interface{} { 34 | "code": 200, 35 | "data": symbols, 36 | "msg": "success", 37 | }) 38 | } 39 | 40 | func (ctrl *NoticeCoinController) Edit() { 41 | id := ctrl.Ctx.Input.Param(":id") 42 | symbols := new(models.NoticeSymbols) 43 | ctrl.BindJSON(&symbols) 44 | intId, _ := strconv.ParseInt(id, 10, 64) 45 | symbols.ID = intId 46 | 47 | // tickSize, stepSize := spot.GetCoinOrderSize(symbols.Symbol) 48 | // symbols.TickSize = tickSize 49 | // symbols.StepSize = stepSize 50 | 51 | o := orm.NewOrm() 52 | _, err := o.Update(symbols) // _ 是受影响的条数 53 | if err != nil { 54 | // 处理错误 55 | ctrl.Ctx.Resp(utils.ResJson(400, nil, "修改失败")) 56 | return 57 | } 58 | ctrl.Ctx.Resp(map[string]interface{} { 59 | "code": 200, 60 | "data": symbols, 61 | "msg": "success", 62 | }) 63 | } 64 | 65 | func (ctrl *NoticeCoinController) Delete() { 66 | id := ctrl.Ctx.Input.Param(":id") 67 | symbols := new(models.NoticeSymbols) 68 | intId, _ := strconv.ParseInt(id, 10, 64) 69 | symbols.ID = intId 70 | o := orm.NewOrm() 71 | 72 | _, err := o.Delete(symbols) 73 | if err != nil { 74 | // 处理错误 75 | ctrl.Ctx.Resp(utils.ResJson(400, nil, "删除错误")) 76 | return 77 | } 78 | ctrl.Ctx.Resp(map[string]interface{} { 79 | "code": 200, 80 | "msg": "success", 81 | }) 82 | } 83 | 84 | func (ctrl *NoticeCoinController) Post() { 85 | symbols := new(models.NoticeSymbols) 86 | ctrl.BindJSON(&symbols) 87 | 88 | symbols.Enable = 0 // 默认不开启 89 | symbols.HasNotice = 0 // 默认未通知 90 | symbols.AutoOrder = 1 // 默认自动下单 91 | symbols.ProfitPrice = "0" // 默认止盈价格(0表示不止盈) 92 | symbols.LossPrice = "0" // 默认止损价格(0表示不止损) 93 | 94 | symbols.Leverage = 3 95 | symbols.MarginType = "ISOLATED" 96 | symbols.StepSize = "0" 97 | symbols.TickSize = "0" 98 | symbols.Usdt = "10" 99 | symbols.Side = "buy" 100 | symbols.Quantity = "0" 101 | 102 | if symbols.Type == 1 { 103 | // 现货 104 | tickSize, stepSize := spot.GetCoinOrderSize(symbols.Symbol) 105 | symbols.TickSize = tickSize 106 | symbols.StepSize = stepSize 107 | } else { 108 | // 合约 109 | tickSize, stepSize := feature.GetCoinOrderSize(symbols.Symbol) 110 | symbols.TickSize = tickSize 111 | symbols.StepSize = stepSize 112 | } 113 | 114 | o := orm.NewOrm() 115 | id, err := o.Insert(symbols) 116 | 117 | if err != nil { 118 | // 处理错误 119 | ctrl.Ctx.Resp(utils.ResJson(400, nil, "新增失败")) 120 | return 121 | } 122 | symbols.ID = id 123 | 124 | ctrl.Ctx.Resp(map[string]interface{} { 125 | "code": 200, 126 | "data": symbols, 127 | "msg": "success", 128 | }) 129 | } 130 | 131 | func (ctrl *NoticeCoinController) UpdateEnable() { 132 | flag := ctrl.Ctx.Input.Param(":flag") 133 | 134 | o := orm.NewOrm() 135 | _, err := o.Raw("UPDATE notice_symbols SET enable = ?", flag).Exec() 136 | if err != nil { 137 | // 处理错误 138 | ctrl.Ctx.Resp(utils.ResJson(400, nil, "更新错误")) 139 | return 140 | } 141 | ctrl.Ctx.Resp(utils.ResJson(200, nil)) 142 | } -------------------------------------------------------------------------------- /controllers/orders.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "strconv" 5 | 6 | "go_binance_futures/models" 7 | "go_binance_futures/utils" 8 | 9 | "github.com/beego/beego/v2/client/orm" 10 | "github.com/beego/beego/v2/server/web" 11 | ) 12 | 13 | type OrderController struct { 14 | web.Controller 15 | } 16 | 17 | type OrderTableList struct { 18 | models.Order 19 | NowPrice string `orm:"column(now_price)" json:"now_price"` 20 | } 21 | 22 | func (ctrl *OrderController) Get() { 23 | paramsSymbol := ctrl.GetString("symbol") 24 | paramPositionSide := ctrl.GetString("position_side") // LONG, SHORT, all 25 | paramsPage := ctrl.GetString("page", "1") 26 | paramsLimit := ctrl.GetString("limit", "20") 27 | paramsStartTime := ctrl.GetString("start_time") 28 | paramsEndTime := ctrl.GetString("end_time") 29 | paramsType := ctrl.GetString("type") // open, close, all 30 | 31 | page, _ := strconv.Atoi(paramsPage) 32 | limit, _ := strconv.Atoi(paramsLimit) 33 | offset := (page - 1) * limit 34 | 35 | o := orm.NewOrm() 36 | var orders []OrderTableList 37 | 38 | var total int64 39 | sql := "SELECT t.*, s.close as now_price FROM `order` t LEFT JOIN symbols s ON t.symbol = s.symbol where 1 = 1" 40 | countSql := "SELECT COUNT(*) FROM `order` t LEFT JOIN symbols s ON t.symbol = s.symbol where 1 = 1" 41 | 42 | if (paramsSymbol != "") { 43 | sql += ` and t.symbol like '%` + paramsSymbol + `%'` 44 | countSql += ` and t.symbol like '%` + paramsSymbol + `%'` 45 | } 46 | if (paramPositionSide != "all" && paramPositionSide != "") { 47 | sql += ` and t.positionSide = '` + paramPositionSide + `'` 48 | countSql += ` and t.positionSide = '` + paramPositionSide + `'` 49 | } 50 | if (paramsStartTime != "") { 51 | sql += ` and t.updateTime >= '` + paramsStartTime + `'` 52 | countSql += ` and t.updateTime >= '` + paramsStartTime + `'` 53 | } 54 | if (paramsEndTime != "") { 55 | sql += ` and t.updateTime <= '` + paramsEndTime + `'` 56 | countSql += ` and t.updateTime <= '` + paramsEndTime + `'` 57 | } 58 | if (paramsType == "open") { 59 | sql += ` and t.side = '` + paramsType + `'` 60 | countSql += ` and t.side = '` + paramsType + `'` 61 | } else if (paramsType == "close") { 62 | sql += ` and t.side = '` + paramsType + `'` 63 | countSql += ` and t.side = '` + paramsType + `'` 64 | } 65 | 66 | sql = sql + " ORDER BY t.updateTime DESC LIMIT " + strconv.Itoa(limit) + " OFFSET " + strconv.Itoa(offset) 67 | _, err := o.Raw(sql).QueryRows(&orders) 68 | if err != nil { 69 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 70 | } 71 | err = o.Raw(countSql).QueryRow(&total) 72 | if err != nil { 73 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 74 | } 75 | 76 | ctrl.Ctx.Resp(map[string]interface{} { 77 | "code": 200, 78 | "data": map[string]interface{} { 79 | "total": total, 80 | "list": orders, 81 | }, 82 | "msg": "success", 83 | }) 84 | } 85 | 86 | func (ctrl *OrderController) Delete() { 87 | id := ctrl.Ctx.Input.Param(":id") 88 | o := orm.NewOrm() 89 | _, err := o.Raw("DELETE FROM `order` where id = ?", id).Exec() 90 | if err != nil { 91 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 92 | return 93 | } 94 | ctrl.Ctx.Resp(utils.ResJson(200, nil)) 95 | } 96 | 97 | func (ctrl *OrderController) DeleteAll() { 98 | 99 | o := orm.NewOrm() 100 | _, err := o.Raw("DELETE FROM `order` where 1=1").Exec() 101 | if err != nil { 102 | // 处理错误 103 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 104 | return 105 | } 106 | ctrl.Ctx.Resp(utils.ResJson(200, nil)) 107 | } 108 | -------------------------------------------------------------------------------- /controllers/rush.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "strconv" 5 | 6 | "go_binance_futures/models" 7 | "go_binance_futures/utils" 8 | 9 | "github.com/beego/beego/v2/client/orm" 10 | "github.com/beego/beego/v2/server/web" 11 | ) 12 | 13 | type RushController struct { 14 | web.Controller 15 | } 16 | 17 | func (ctrl *RushController) Get() { 18 | 19 | o := orm.NewOrm() 20 | var symbols []models.NewSymbols 21 | _, err := o.QueryTable("new_symbols").OrderBy("ID").All(&symbols) 22 | if err != nil { 23 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 24 | } 25 | 26 | ctrl.Ctx.Resp(map[string]interface{} { 27 | "code": 200, 28 | "data": symbols, 29 | "msg": "success", 30 | }) 31 | } 32 | 33 | func (ctrl *RushController) Edit() { 34 | id := ctrl.Ctx.Input.Param(":id") 35 | symbols := new(models.NewSymbols) 36 | ctrl.BindJSON(&symbols) 37 | intId, _ := strconv.ParseInt(id, 10, 64) 38 | symbols.ID = intId 39 | 40 | // 币还没上线可能会报错 41 | // marginType := futures.MarginTypeIsolated 42 | // if symbols.MarginType == "CROSSED" { 43 | // marginType = futures.MarginTypeCrossed 44 | // } 45 | // binance.SetLeverage(symbols.Symbol, int(symbols.Leverage)) // 修改合约倍数 46 | // binance.SetMarginType(symbols.Symbol, marginType) // 修改仓位模式 47 | 48 | o := orm.NewOrm() 49 | _, err := o.Update(symbols) // _ 是受影响的条数 50 | if err != nil { 51 | // 处理错误 52 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 53 | return 54 | } 55 | ctrl.Ctx.Resp(map[string]interface{} { 56 | "code": 200, 57 | "data": symbols, 58 | "msg": "success", 59 | }) 60 | } 61 | 62 | func (ctrl *RushController) Delete() { 63 | id := ctrl.Ctx.Input.Param(":id") 64 | symbols := new(models.NewSymbols) 65 | intId, _ := strconv.ParseInt(id, 10, 64) 66 | symbols.ID = intId 67 | o := orm.NewOrm() 68 | 69 | _, err := o.Delete(symbols) 70 | if err != nil { 71 | // 处理错误 72 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 73 | return 74 | } 75 | ctrl.Ctx.Resp(map[string]interface{} { 76 | "code": 200, 77 | "msg": "success", 78 | }) 79 | } 80 | 81 | func (ctrl *RushController) Post() { 82 | symbols := new(models.NewSymbols) 83 | ctrl.BindJSON(&symbols) 84 | 85 | symbols.Leverage = 3 86 | symbols.MarginType = "ISOLATED" 87 | symbols.StepSize = "0" 88 | symbols.TickSize = "0" 89 | symbols.Usdt = "10" 90 | symbols.Type = 1 91 | symbols.Enable = 0 92 | symbols.Side = "buy" 93 | symbols.Quantity = "0" 94 | symbols.ExpectPrice = "0" 95 | 96 | o := orm.NewOrm() 97 | id, err := o.Insert(symbols) 98 | 99 | if err != nil { 100 | // 处理错误 101 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 102 | return 103 | } 104 | symbols.ID = id 105 | 106 | ctrl.Ctx.Resp(map[string]interface{} { 107 | "code": 200, 108 | "data": symbols, 109 | "msg": "success", 110 | }) 111 | } 112 | 113 | func (ctrl *RushController) UpdateEnable() { 114 | flag := ctrl.Ctx.Input.Param(":flag") 115 | 116 | o := orm.NewOrm() 117 | _, err := o.Raw("UPDATE newSymbols SET enable = ?", flag).Exec() 118 | if err != nil { 119 | // 处理错误 120 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 121 | return 122 | } 123 | ctrl.Ctx.Resp(utils.ResJson(200, nil)) 124 | } -------------------------------------------------------------------------------- /controllers/spot.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "sort" 5 | "strconv" 6 | "strings" 7 | 8 | "go_binance_futures/models" 9 | "go_binance_futures/spot" 10 | "go_binance_futures/utils" 11 | 12 | "github.com/beego/beego/v2/client/orm" 13 | "github.com/beego/beego/v2/server/web" 14 | ) 15 | 16 | 17 | type SpotController struct { 18 | web.Controller 19 | } 20 | 21 | func (ctrl *SpotController) Get() { 22 | paramsSort := ctrl.GetString("sort") 23 | symbol_type := ctrl.GetString("symbol_type") 24 | symbol := ctrl.GetString("symbol") 25 | enable := ctrl.GetString("enable") 26 | pin := ctrl.GetString("pin") 27 | 28 | o := orm.NewOrm() 29 | var symbols []models.SpotSymbols 30 | query := o.QueryTable("spot_symbols") 31 | if symbol_type != "" { 32 | query = query.Filter("type", symbol_type) 33 | } 34 | if symbol != "" { 35 | query = query.Filter("symbol__contains", symbol) 36 | } 37 | if enable != "" { 38 | query = query.Filter("enable", enable) 39 | } 40 | if pin != "" { 41 | query = query.Filter("pin", 1) 42 | } 43 | _, err := query.OrderBy("-Pin", "ID").All(&symbols) 44 | if err != nil { 45 | ctrl.Ctx.Resp(utils.ResJson(400, nil, "error")) 46 | } 47 | 48 | if strings.HasPrefix(paramsSort, "percent_change") { 49 | sort.SliceStable(symbols, func(i, j int) bool { 50 | base := symbols[i].Pin >= symbols[j].Pin 51 | if paramsSort == "percent_change+" { 52 | return base && symbols[i].PercentChange >= symbols[j].PercentChange // 涨幅从大到小排序 53 | } else if paramsSort == "percent_change-" { 54 | return base && symbols[i].PercentChange < symbols[j].PercentChange // 涨幅从小到大排序 55 | } else { 56 | return true 57 | } 58 | }) 59 | } 60 | 61 | ctrl.Ctx.Resp(map[string]interface{} { 62 | "code": 200, 63 | "data": symbols, 64 | "msg": "success", 65 | }) 66 | } 67 | 68 | func (ctrl *SpotController) Edit() { 69 | id := ctrl.Ctx.Input.Param(":id") 70 | var symbols models.SpotSymbols 71 | o := orm.NewOrm() 72 | o.QueryTable(symbols.TableName()).Filter("Id", id).One(&symbols) 73 | 74 | ctrl.BindJSON(&symbols) 75 | 76 | _, err := o.Update(&symbols) // _ 是受影响的条数 77 | if err != nil { 78 | // 处理错误 79 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 80 | return 81 | } 82 | ctrl.Ctx.Resp(map[string]interface{} { 83 | "code": 200, 84 | "data": symbols, 85 | "msg": "success", 86 | }) 87 | } 88 | 89 | func (ctrl *SpotController) Delete() { 90 | id := ctrl.Ctx.Input.Param(":id") 91 | symbols := new(models.SpotSymbols) 92 | intId, _ := strconv.ParseInt(id, 10, 64) 93 | symbols.ID = intId 94 | o := orm.NewOrm() 95 | 96 | _, err := o.Delete(symbols) 97 | if err != nil { 98 | // 处理错误 99 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 100 | return 101 | } 102 | ctrl.Ctx.Resp(map[string]interface{} { 103 | "code": 200, 104 | "msg": "success", 105 | }) 106 | } 107 | 108 | func (ctrl *SpotController) Post() { 109 | symbols := new(models.SpotSymbols) 110 | ctrl.BindJSON(&symbols) 111 | symbols.PercentChange = 0 112 | symbols.Close = "0" 113 | symbols.Open = "0" 114 | symbols.Low = "0" 115 | symbols.High = "0" 116 | 117 | symbols.StepSize = "0.1" 118 | symbols.TickSize = "0.1" 119 | symbols.Usdt = "10" 120 | symbols.Profit = "100" 121 | symbols.Loss = "100" 122 | 123 | o := orm.NewOrm() 124 | id, err := o.Insert(symbols) 125 | 126 | go func() { 127 | spot.UpdateSymbolsTradePrecision() // 更新合约交易精度 128 | }() 129 | 130 | if err != nil { 131 | // 处理错误 132 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 133 | return 134 | } 135 | symbols.ID = id 136 | 137 | ctrl.Ctx.Resp(map[string]interface{} { 138 | "code": 200, 139 | "data": symbols, 140 | "msg": "success", 141 | }) 142 | } 143 | 144 | func (ctrl *SpotController) UpdateEnable() { 145 | flag := ctrl.Ctx.Input.Param(":flag") 146 | 147 | o := orm.NewOrm() 148 | _, err := o.Raw("UPDATE spot_symbols SET enable = ?", flag).Exec() 149 | if err != nil { 150 | // 处理错误 151 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 152 | return 153 | } 154 | ctrl.Ctx.Resp(utils.ResJson(200, nil)) 155 | } 156 | 157 | func (ctrl *SpotController) BatchEdit() { 158 | params := new(BatchEditParams) 159 | ctrl.BindJSON(¶ms) 160 | query := "UPDATE spot_symbols SET" 161 | 162 | if params.Usdt != "" { 163 | query += " usdt = " + params.Usdt + "," 164 | } 165 | if (params.Profit != "") { 166 | query += " profit = " + params.Profit + "," 167 | } 168 | if (params.Loss != "") { 169 | query += " loss = " + params.Loss + "," 170 | } 171 | if (params.StrategyType != "") { 172 | query += " strategy_type = '" + params.StrategyType + "'," 173 | } 174 | if (params.StrategyTemplateId != 0) { 175 | var template models.StrategyTemplates 176 | orm.NewOrm().QueryTable("strategy_templates").Filter("Id", params.StrategyTemplateId).One(&template) 177 | query += " technology = '" + template.Technology + "'," 178 | query += " strategy = '" + template.Strategy + "'," 179 | } 180 | 181 | if strings.HasSuffix(query, ",") { 182 | query = strings.TrimSuffix(query, ",") 183 | // query += " WHERE enable = 1" // 只更新开启的交易对 184 | _, err := orm.NewOrm().Raw(query).Exec() 185 | if err != nil { 186 | // 处理错误 187 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 188 | return 189 | } 190 | } 191 | 192 | // go func() { 193 | // spot.UpdateSymbolsTradePrecision() // 更新合约交易精度 194 | // }() 195 | 196 | ctrl.Ctx.Resp(map[string]interface{} { 197 | "code": 200, 198 | "msg": "success", 199 | }) 200 | } 201 | -------------------------------------------------------------------------------- /controllers/strategyTemplate.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "encoding/json" 5 | "go_binance_futures/feature/strategy/line" 6 | "go_binance_futures/models" 7 | "go_binance_futures/technology" 8 | "go_binance_futures/types" 9 | "go_binance_futures/utils" 10 | "strconv" 11 | 12 | "github.com/beego/beego/v2/client/orm" 13 | "github.com/beego/beego/v2/core/logs" 14 | "github.com/beego/beego/v2/server/web" 15 | "github.com/expr-lang/expr" 16 | ) 17 | 18 | type StrategyTemplateController struct { 19 | web.Controller 20 | } 21 | 22 | func (ctrl *StrategyTemplateController) Post() { 23 | templates := new(models.StrategyTemplates) 24 | ctrl.BindJSON(&templates) 25 | 26 | o := orm.NewOrm() 27 | id, err := o.Insert(templates) 28 | 29 | if err != nil { 30 | // 处理错误 31 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 32 | return 33 | } 34 | templates.ID = id 35 | 36 | ctrl.Ctx.Resp(map[string]interface{} { 37 | "code": 200, 38 | "data": templates, 39 | "msg": "success", 40 | }) 41 | } 42 | 43 | func (ctrl *StrategyTemplateController) Get() { 44 | paramsName := ctrl.GetString("name", "") 45 | 46 | o := orm.NewOrm() 47 | var templates []models.StrategyTemplates 48 | query := o.QueryTable("strategy_templates") 49 | if paramsName != "" { 50 | query = query.Filter("Type", paramsName) 51 | } 52 | _, err := query.OrderBy("ID").All(&templates) 53 | if err != nil { 54 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 55 | } 56 | 57 | ctrl.Ctx.Resp(map[string]interface{} { 58 | "code": 200, 59 | "data": templates, 60 | "msg": "success", 61 | }) 62 | } 63 | 64 | func (ctrl *StrategyTemplateController) Edit() { 65 | id := ctrl.Ctx.Input.Param(":id") 66 | var templates models.StrategyTemplates 67 | o := orm.NewOrm() 68 | o.QueryTable("strategy_templates").Filter("Id", id).One(&templates) 69 | 70 | ctrl.BindJSON(&templates) 71 | 72 | _, err := o.Update(&templates) // _ 是受影响的条数 73 | if err != nil { 74 | // 处理错误 75 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 76 | return 77 | } 78 | ctrl.Ctx.Resp(map[string]interface{} { 79 | "code": 200, 80 | "data": templates, 81 | "msg": "success", 82 | }) 83 | } 84 | 85 | func (ctrl *StrategyTemplateController) Delete() { 86 | id := ctrl.Ctx.Input.Param(":id") 87 | templates := new(models.StrategyTemplates) 88 | intId, _ := strconv.ParseInt(id, 10, 64) 89 | templates.ID = intId 90 | o := orm.NewOrm() 91 | 92 | _, err := o.Delete(templates) 93 | if err != nil { 94 | // 处理错误 95 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 96 | return 97 | } 98 | ctrl.Ctx.Resp(map[string]interface{} { 99 | "code": 200, 100 | "msg": "success", 101 | }) 102 | } 103 | 104 | func (ctrl *StrategyTemplateController) TestStrategyRule() { 105 | symbol := ctrl.Ctx.Input.Param(":symbol") 106 | var symbols models.Symbols 107 | o := orm.NewOrm() 108 | o.QueryTable("symbols").Filter("Symbol", symbol).One(&symbols) 109 | 110 | ctrl.BindJSON(&symbols) 111 | 112 | var strategyConfig technology.StrategyConfig 113 | err := json.Unmarshal([]byte(symbols.Strategy), &strategyConfig) 114 | if err != nil { 115 | logs.Error("Error unmarshalling JSON:", err.Error()) 116 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 117 | return 118 | } 119 | env := line.InitParseEnv(symbols.Symbol, symbols.Technology) 120 | env["ROI"] = 10.2 121 | env["Position"] = types.FuturesPositionCode{ 122 | Symbol: symbols.Symbol, 123 | EntryPrice: 68000.0, 124 | MarkPrice: 72000.0, 125 | Amount: -0.02, 126 | UnrealizedProfit: 100.2, 127 | Leverage: 3, 128 | Side: "SHORT", 129 | Mock: true, 130 | CreateTime: 1234567890000, 131 | SourceType: "api", 132 | } 133 | for _, strategy := range strategyConfig { 134 | if strategy.Enable { 135 | program, err := expr.Compile(strategy.Code, expr.Env(env)) 136 | if err != nil { 137 | logs.Error("Error Strategy Compile:", err.Error()) 138 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 139 | return 140 | } 141 | output, err := expr.Run(program, env) 142 | if err != nil { 143 | logs.Error("Error Strategy Run:", err.Error()) 144 | ctrl.Ctx.Resp(utils.ResJson(400, nil, err.Error())) 145 | return 146 | } 147 | ctrl.Ctx.Resp(map[string]interface{} { 148 | "code": 200, 149 | "data": map[string]interface{} { 150 | "pass": output, 151 | "type": strategy.Type, 152 | }, 153 | "msg": "success", 154 | }) 155 | } 156 | } 157 | } -------------------------------------------------------------------------------- /db/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/db/.gitkeep -------------------------------------------------------------------------------- /doc/fundingRate.js: -------------------------------------------------------------------------------- 1 | // https://binance-docs.github.io/apidocs/futures/cn/#69f9b0b2f3 2 | // 最新标记价格和资金费率 3 | const data = [ 4 | { 5 | "symbol": "BTCUSDT", // 交易对 6 | "markPrice": "11793.63104562", // 标记价格 7 | "indexPrice": "11781.80495970", // 指数价格 8 | "estimatedSettlePrice": "11781.16138815", // 预估结算价,仅在交割开始前最后一小时有意义 9 | "lastFundingRate": "0.00038246", // 最近更新的预估资金费率 10 | "nextFundingTime": 1597392000000, // 下次资金费时间 11 | "interestRate": "0.00010000", // 标的资产基础利率 12 | "time": 1597370495002 // 更新时间 13 | } 14 | ] -------------------------------------------------------------------------------- /doc/fundingRateHistory.js: -------------------------------------------------------------------------------- 1 | // https://binance-docs.github.io/apidocs/futures/cn/#31dbeb24c4 2 | // https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Get-Funding-Rate-History 3 | // 查询资金费率历史 4 | const data = [ 5 | { 6 | "symbol": "BTCUSDT", // 交易对 7 | "fundingTime": 1698768000000, // 资金费时间 8 | "fundingRate": "0.00010000", // 资金费率 9 | "markPrice": "34287.54619963" // 资金费对应标记价格 10 | }, 11 | { 12 | "symbol": "BTCUSDT", 13 | "fundingTime": 1698796800000, 14 | "fundingRate": "0.00010000", 15 | "markPrice": "34651.40000000" 16 | } 17 | ] -------------------------------------------------------------------------------- /doc/futuresAccount.js: -------------------------------------------------------------------------------- 1 | // https://binance-docs.github.io/apidocs/futures/cn/#v2-user_data-2 2 | const data = { 3 | "feeTier": 0, // 手续费等级 4 | "canTrade": true, // 是否可以交易 5 | "canDeposit": true, // 是否可以入金 6 | "canWithdraw": true, // 是否可以出金 7 | "updateTime": 0, 8 | "totalInitialMargin": "0.00000000", // 但前所需起始保证金总额(存在逐仓请忽略), 仅计算usdt资产 9 | "totalMaintMargin": "0.00000000", // 维持保证金总额, 仅计算usdt资产 10 | "totalWalletBalance": "23.72469206", // 账户总余额, 仅计算usdt资产 11 | "totalUnrealizedProfit": "0.00000000", // 持仓未实现盈亏总额, 仅计算usdt资产 12 | "totalMarginBalance": "23.72469206", // 保证金总余额, 仅计算usdt资产 13 | "totalPositionInitialMargin": "0.00000000", // 持仓所需起始保证金(基于最新标记价格), 仅计算usdt资产 14 | "totalOpenOrderInitialMargin": "0.00000000", // 当前挂单所需起始保证金(基于最新标记价格), 仅计算usdt资产 15 | "totalCrossWalletBalance": "23.72469206", // 全仓账户余额, 仅计算usdt资产 16 | "totalCrossUnPnl": "0.00000000", // 全仓持仓未实现盈亏总额, 仅计算usdt资产 17 | "availableBalance": "23.72469206", // 可用余额, 仅计算usdt资产 18 | "maxWithdrawAmount": "23.72469206", // 最大可转出余额, 仅计算usdt资产 19 | "assets": [ // 资产 20 | { 21 | "asset": "USDT", //资产 22 | "walletBalance": "23.72469206", //余额 23 | "unrealizedProfit": "0.00000000", // 未实现盈亏 24 | "marginBalance": "23.72469206", // 保证金余额 25 | "maintMargin": "0.00000000", // 维持保证金 26 | "initialMargin": "0.00000000", // 当前所需起始保证金 27 | "positionInitialMargin": "0.00000000", // 持仓所需起始保证金(基于最新标记价格) 28 | "openOrderInitialMargin": "0.00000000", // 当前挂单所需起始保证金(基于最新标记价格) 29 | "crossWalletBalance": "23.72469206", //全仓账户余额 30 | "crossUnPnl": "0.00000000" // 全仓持仓未实现盈亏 31 | "availableBalance": "23.72469206", // 可用余额 32 | "maxWithdrawAmount": "23.72469206", // 最大可转出余额 33 | "marginAvailable": true, // 是否可用作联合保证金 34 | "updateTime": 1625474304765 //更新时间 35 | }, 36 | { 37 | "asset": "BUSD", //资产 38 | "walletBalance": "103.12345678", //余额 39 | "unrealizedProfit": "0.00000000", // 未实现盈亏 40 | "marginBalance": "103.12345678", // 保证金余额 41 | "maintMargin": "0.00000000", // 维持保证金 42 | "initialMargin": "0.00000000", // 当前所需起始保证金 43 | "positionInitialMargin": "0.00000000", // 持仓所需起始保证金(基于最新标记价格) 44 | "openOrderInitialMargin": "0.00000000", // 当前挂单所需起始保证金(基于最新标记价格) 45 | "crossWalletBalance": "103.12345678", //全仓账户余额 46 | "crossUnPnl": "0.00000000" // 全仓持仓未实现盈亏 47 | "availableBalance": "103.12345678", // 可用余额 48 | "maxWithdrawAmount": "103.12345678", // 最大可转出余额 49 | "marginAvailable": true, // 否可用作联合保证金 50 | "updateTime": 0 // 更新时间 51 | } 52 | ], 53 | "positions": [ // 头寸,将返回所有市场symbol。 54 | //根据用户持仓模式展示持仓方向,即单向模式下只返回BOTH持仓情况,双向模式下只返回 LONG 和 SHORT 持仓情况 55 | { 56 | "symbol": "BTCUSDT", // 交易对 57 | "initialMargin": "0", // 当前所需起始保证金(基于最新标记价格) 58 | "maintMargin": "0", //维持保证金 59 | "unrealizedProfit": "0.00000000", // 持仓未实现盈亏 60 | "positionInitialMargin": "0", // 持仓所需起始保证金(基于最新标记价格) 61 | "openOrderInitialMargin": "0", // 当前挂单所需起始保证金(基于最新标记价格) 62 | "leverage": "100", // 杠杆倍率 63 | "isolated": true, // 是否是逐仓模式 64 | "entryPrice": "0.00000", // 持仓成本价 65 | "maxNotional": "250000", // 当前杠杆下用户可用的最大名义价值 66 | "positionSide": "BOTH", // 持仓方向 67 | "positionAmt": "0", // 持仓数量 68 | "updateTime": 0 // 更新时间 69 | } 70 | ] 71 | } -------------------------------------------------------------------------------- /doc/futuresPrices.js: -------------------------------------------------------------------------------- 1 | const data = { 2 | BTTUSDT: '0.003157', 3 | IOTXUSDT: '0.15889', 4 | XLMUSDT: '0.32540', 5 | OMGUSDT: '8.1000', 6 | ETHUSDT_211231: '4385.65', 7 | BALUSDT: '20.979', 8 | DOGEBUSD: '0.205760', 9 | XTZUSDT: '4.679', 10 | ARUSDT: '57.275', 11 | DENTUSDT: '0.004889', 12 | GALAUSDT: '0.72475', 13 | ONTUSDT: '0.9781', 14 | ADABUSD: '1.59650', 15 | BNBUSDT: '612.190', 16 | DODOUSDT: '1.424', 17 | DEFIUSDT: '2879.0', 18 | ATOMUSDT: '27.442', 19 | CRVUSDT: '4.889', 20 | ETHBUSD: '4329.10', 21 | RAYUSDT: '10.033', 22 | COTIUSDT: '0.39550', 23 | ALPHAUSDT: '0.94280', 24 | AXSUSDT: '135.92000', 25 | ATAUSDT: '1.1226', 26 | XRPBUSD: '0.9742', 27 | KNCUSDT: '2.02800', 28 | RENUSDT: '0.91540', 29 | C98USDT: '2.9975', 30 | DASHUSDT: '178.95', 31 | KLAYUSDT: '1.3924', 32 | ZECUSDT: '247.25', 33 | STMXUSDT: '0.03090', 34 | CHRUSDT: '1.0256', 35 | RLCUSDT: '4.0555', 36 | IOSTUSDT: '0.038368', 37 | RUNEUSDT: '10.9750', 38 | '1INCHUSDT': '3.6385', 39 | CELOUSDT: '4.886', 40 | AAVEUSDT: '238.740', 41 | ANKRUSDT: '0.162610', 42 | CTSIUSDT: '0.9846', 43 | CVCUSDT: '0.58533', 44 | XMRUSDT: '238.78', 45 | ARPAUSDT: '0.14825', 46 | LTCUSDT: '200.15', 47 | BTCBUSD: '57561.2', 48 | BNBBUSD: '612.420', 49 | FILUSDT: '56.304', 50 | REEFUSDT: '0.024848', 51 | BCHUSDT: '566.91', 52 | CTKUSDT: '1.67700', 53 | ONEUSDT: '0.26997', 54 | TOMOUSDT: '3.0100', 55 | NEARUSDT: '8.3616', 56 | AKROUSDT: '0.03108', 57 | BTCUSDT: '57550.13', 58 | KAVAUSDT: '4.9113', 59 | LUNAUSDT: '50.1460', 60 | FTMUSDT: '2.104290', 61 | SCUSDT: '0.025898', 62 | DYDXUSDT: '12.585', 63 | SXPUSDT: '2.2567', 64 | HBARUSDT: '0.33304', 65 | ZRXUSDT: '1.1587', 66 | SOLUSDT: '201.2540', 67 | '1000XECUSDT': '0.14947', 68 | XRPUSDT: '0.9736', 69 | BELUSDT: '2.19150', 70 | ADAUSDT: '1.59500', 71 | KEEPUSDT: '0.8082', 72 | FLMUSDT: '0.5548', 73 | SKLUSDT: '0.33602', 74 | BZRXUSDT: '0.2889', 75 | OCEANUSDT: '1.08832', 76 | ICPUSDT: '42.17', 77 | ALICEUSDT: '21.328', 78 | DOGEUSDT: '0.205580', 79 | CHZUSDT: '0.44403', 80 | VETUSDT: '0.115750', 81 | BLZUSDT: '0.34214', 82 | MASKUSDT: '16.3275', 83 | RVNUSDT: '0.11220', 84 | SUSHIUSDT: '7.8510', 85 | ICXUSDT: '1.7492', 86 | STORJUSDT: '2.9830', 87 | ENJUSDT: '3.82660', 88 | NUUSDT: '0.9471', 89 | HNTUSDT: '40.7430', 90 | DGBUSDT: '0.04690', 91 | LINAUSDT: '0.05340', 92 | IOTAUSDT: '1.3405', 93 | NEOUSDT: '37.990', 94 | LPTUSDT: '57.441', 95 | ALGOUSDT: '1.7182', 96 | UNIUSDT: '20.1280', 97 | TRBUSDT: '54.540', 98 | MTLUSDT: '2.9530', 99 | YFIIUSDT: '3531.1', 100 | EGLDUSDT: '405.270', 101 | TRXUSDT: '0.09523', 102 | NKNUSDT: '0.55165', 103 | RSRUSDT: '0.046170', 104 | DOTUSDT: '36.602', 105 | AUDIOUSDT: '2.4061', 106 | BANDUSDT: '8.0271', 107 | ZILUSDT: '0.08478', 108 | LITUSDT: '5.931', 109 | BAKEUSDT: '1.7363', 110 | LINKUSDT: '24.936', 111 | WAVESUSDT: '20.4520', 112 | SOLBUSD: '201.4270', 113 | BATUSDT: '1.6344', 114 | BTCUSDT_211231: '58242.8', 115 | YFIUSDT: '30112.0', 116 | AVAXUSDT: '111.9650', 117 | GTCUSDT: '18.409', 118 | MANAUSDT: '5.1400', 119 | ETCUSDT: '47.490', 120 | UNFIUSDT: '10.904', 121 | MKRUSDT: '3070.40', 122 | COMPUSDT: '286.22', 123 | XEMUSDT: '0.1668', 124 | ETHUSDT: '4326.42', 125 | SANDUSDT: '7.72275', 126 | GRTUSDT: '0.99011', 127 | QTUMUSDT: '15.052', 128 | OGNUSDT: '1.0376', 129 | KSMUSDT: '359.300', 130 | BTCDOMUSDT: '1084.5', 131 | CELRUSDT: '0.11066', 132 | BTSUSDT: '0.04807', 133 | ZENUSDT: '108.340', 134 | LRCUSDT: '3.11437', 135 | THETAUSDT: '6.5850', 136 | '1000SHIBUSDT': '0.039109', 137 | EOSUSDT: '3.955', 138 | SRMUSDT: '5.1820', 139 | HOTUSDT: '0.012360', 140 | FTTBUSD: '51.074', 141 | TLMUSDT: '0.4304', 142 | MATICUSDT: '1.64790', 143 | SNXUSDT: '7.635', 144 | SFPUSDT: '2.0466', 145 | } 146 | -------------------------------------------------------------------------------- /doc/income.js: -------------------------------------------------------------------------------- 1 | // incomeType 收益类型: TRANSFER 转账, WELCOME_BONUS 欢迎奖金, REALIZED_PNL 已实现盈亏, FUNDING_FEE 资金费用, COMMISSION 佣金, INSURANCE_CLEAR 强平, REFERRAL_KICKBACK 推荐人返佣, COMMISSION_REBATE 被推荐人返佣, API_REBATE API佣金回扣, CONTEST_REWARD 交易大赛奖金, CROSS_COLLATERAL_TRANSFER cc转账, OPTIONS_PREMIUM_FEE 期权购置手续费, OPTIONS_SETTLE_PROFIT 期权行权收益, INTERNAL_TRANSFER 内部账户,给普通用户划转, AUTO_EXCHANGE 自动兑换, DELIVERED_SETTELMENT 下架结算, COIN_SWAP_DEPOSIT 闪兑转入, COIN_SWAP_WITHDRAW 闪兑转出, POSITION_LIMIT_INCREASE_FEE 仓位限制上调费用 2 | 3 | const data = [ 4 | { 5 | "symbol": "", // 交易对,仅针对涉及交易对的资金流 6 | "incomeType": "TRANSFER", // 资金流类型 7 | "income": "-0.37500000", // 资金流数量,正数代表流入,负数代表流出 8 | "asset": "USDT", // 资产内容 9 | "info":"TRANSFER", // 备注信息,取决于流水类型 10 | "time": 1570608000000, // 时间 11 | "tranId":"9689322392", // 划转ID 12 | "tradeId":"" // 引起流水产生的原始交易ID 13 | }, 14 | { 15 | "symbol": "BTCUSDT", 16 | "incomeType": "COMMISSION", 17 | "income": "-0.01000000", 18 | "asset": "USDT", 19 | "info":"COMMISSION", 20 | "time": 1570636800000, 21 | "tranId":9689322392, 22 | "tradeId":"2059192" 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /doc/order.js: -------------------------------------------------------------------------------- 1 | const data = { 2 | clientOrderId: 'testOrder', // 用户自定义的订单号 3 | cumQty: '0', 4 | cumQuote: '0', // 成交金额 5 | executedQty: '0', // 成交量 6 | orderId: 22542179, // 系统订单号 7 | avgPrice: '0.00000', // 平均成交价 8 | origQty: '10', // 原始委托数量 9 | price: '0', // 委托价格 10 | reduceOnly: false, // 仅减仓 11 | side: 'SELL', // 买卖方向 12 | positionSide: 'SHORT', // 持仓方向 13 | status: 'NEW', // 订单状态 14 | stopPrice: '0', // 触发价,对`TRAILING_STOP_MARKET`无效 15 | closePosition: false, // 是否条件全平仓 16 | symbol: 'BTCUSDT', // 交易对 17 | timeInForce: 'GTC', // 有效方法 18 | type: 'TRAILING_STOP_MARKET', // 订单类型 19 | origType: 'TRAILING_STOP_MARKET', // 触发前订单类型 20 | activatePrice: '9020', // 跟踪止损激活价格, 仅`TRAILING_STOP_MARKET` 订单返回此字段 21 | priceRate: '0.3', // 跟踪止损回调比例, 仅`TRAILING_STOP_MARKET` 订单返回此字段 22 | updateTime: 1566818724722, // 更新时间 23 | workingType: 'CONTRACT_PRICE', // 条件价格触发类型 24 | priceProtect: false, // 是否开启条件单触发保护 25 | } 26 | -------------------------------------------------------------------------------- /doc/position.js: -------------------------------------------------------------------------------- 1 | const data = [ 2 | { 3 | symbol: 'BTCUSDT', // 交易对 4 | initialMargin: '0', // 当前所需起始保证金(基于最新标记价格) 5 | maintMargin: '0', //维持保证金 6 | unrealizedProfit: '0.00000000', // 持仓未实现盈亏 7 | positionInitialMargin: '0', // 持仓所需起始保证金(基于最新标记价格) 8 | openOrderInitialMargin: '0', // 当前挂单所需起始保证金(基于最新标记价格) 9 | leverage: '100', // 杠杆倍率 10 | isolated: true, // 是否是逐仓模式 11 | entryPrice: '0.00000', // 持仓成本价 12 | maxNotional: '250000', // 当前杠杆下用户可用的最大名义价值 13 | positionSide: 'BOTH', // 持仓方向 14 | positionAmt: '0', // 持仓数量 15 | updateTime: 0, // 更新时间(订单交易成功时的时间,毫秒时间戳) 16 | }, 17 | { 18 | symbol: 'XTZUSDT', 19 | positionAmt: '23.5', // 持仓数量 20 | entryPrice: '5.09', // 买入价格 21 | markPrice: '5.07920469', 22 | unRealizedProfit: '-0.25368978', // 当前收益 23 | liquidationPrice: '0.45161396', 24 | leverage: '10', 25 | maxNotionalValue: '1000000', 26 | marginType: 'cross', 27 | isolatedMargin: '0.00000000', 28 | isAutoAddMargin: 'false', 29 | positionSide: 'LONG', 30 | notional: '119.36131021', // 当前持仓usdt,原始等于 notional - unRealizedProfit 31 | isolatedWallet: '0', 32 | updateTime: 1638199621922, 33 | }, 34 | { 35 | symbol: 'XTZUSDT', 36 | positionAmt: '0.0', 37 | entryPrice: '0.0', 38 | markPrice: '5.07920469', 39 | unRealizedProfit: '0.00000000', 40 | liquidationPrice: '0', 41 | leverage: '10', 42 | maxNotionalValue: '1000000', 43 | marginType: 'cross', 44 | isolatedMargin: '0.00000000', 45 | isAutoAddMargin: 'false', 46 | positionSide: 'SHORT', 47 | notional: '0', 48 | isolatedWallet: '0', 49 | updateTime: 0, 50 | }, 51 | ] 52 | -------------------------------------------------------------------------------- /doc/userTrades.js: -------------------------------------------------------------------------------- 1 | const data = [ 2 | { 3 | buyer: false, // 是否是买方 4 | commission: '-0.07819010', // 手续费 5 | commissionAsset: 'USDT', // 手续费计价单位 6 | id: 698759, // 交易ID 7 | maker: false, // 是否是挂单方 8 | orderId: 25851813, // 订单编号 9 | price: '7819.01', // 成交价 10 | qty: '0.002', // 成交量 11 | quoteQty: '15.63802', // 成交额 12 | realizedPnl: '-0.91539999', // 实现盈亏 13 | side: 'SELL', // 买卖方向 14 | positionSide: 'SHORT', // 持仓方向 15 | symbol: 'BTCUSDT', // 交易对 16 | time: 1569514978020, // 时间 17 | }, 18 | ] 19 | -------------------------------------------------------------------------------- /doc/wsMarketTicker.js: -------------------------------------------------------------------------------- 1 | // https://binance-docs.github.io/apidocs/futures/cn/#ticker 2 | const data = [ 3 | { 4 | "e": "24hrTicker", // 事件类型 5 | "E": 123456789, // 事件时间 6 | "s": "BNBUSDT", // 交易对 7 | "p": "0.0015", // 24小时价格变化 8 | "P": "250.00", // 24小时价格变化(百分比) 9 | "w": "0.0018", // 平均价格 10 | "c": "0.0025", // 最新成交价格 11 | "Q": "10", // 最新成交价格上的成交量 12 | "o": "0.0010", // 24小时内第一比成交的价格 13 | "h": "0.0025", // 24小时内最高成交价 14 | "l": "0.0010", // 24小时内最低成交价 15 | "v": "10000", // 24小时内成交量 16 | "q": "18", // 24小时内成交额 17 | "O": 0, // 统计开始时间 18 | "C": 86400000, // 统计关闭时间 19 | "F": 0, // 24小时内第一笔成交交易ID 20 | "L": 18150, // 24小时内最后一笔成交交易ID 21 | "n": 18151 // 24小时内成交数 22 | } 23 | ] -------------------------------------------------------------------------------- /doc/常量含义.md: -------------------------------------------------------------------------------- 1 | ## 交易对类型: 2 | - FUTURE 期货 3 | 4 | ## 合约类型 (contractType): 5 | 6 | - PERPETUAL 永续合约 7 | - CURRENT_MONTH 当月交割合约 8 | - NEXT_MONTH 次月交割合约 9 | - CURRENT_QUARTER 当季交割合约 10 | - NEXT_QUARTER 次季交割合约 11 | 12 | ## 合约状态 (contractStatus, status): 13 | 14 | - PENDING_TRADING 待上市 15 | - TRADING 交易中 16 | - PRE_DELIVERING 预交割 17 | - DELIVERING 交割中 18 | - DELIVERED 已交割 19 | - PRE_SETTLE 预结算 20 | - SETTLING 结算中 21 | - CLOSE 已下架 22 | 23 | ## 订单状态 (status): 24 | 25 | - NEW 新建订单 26 | - PARTIALLY_FILLED 部分成交 27 | - FILLED 全部成交 28 | - CANCELED 已撤销 29 | - REJECTED 订单被拒绝 30 | - EXPIRED 订单过期(根据timeInForce参数规则) 31 | 32 | ## 订单种类 (orderTypes, type): 33 | 34 | - LIMIT 限价单 35 | - MARKET 市价单 36 | - STOP 止损限价单 37 | - STOP_MARKET 止损市价单 38 | - TAKE_PROFIT 止盈限价单 39 | - TAKE_PROFIT_MARKET 止盈市价单 40 | - TRAILING_STOP_MARKET 跟踪止损单 41 | 42 | ## 订单方向 (side): 43 | 44 | - BUY 买入 45 | - SELL 卖出 46 | 47 | ## 持仓方向: 48 | 49 | - BOTH 单一持仓方向 50 | - LONG 多头(双向持仓下) 51 | - SHORT 空头(双向持仓下) 52 | 53 | ## 有效方式 (timeInForce): 54 | 55 | - GTC - Good Till Cancel 成交为止 56 | - IOC - Immediate or Cancel 无法立即成交(吃单)的部分就撤销 57 | - FOK - Fill or Kill 无法全部立即成交就撤销 58 | - GTX - Good Till Crossing 无法成为挂单方就撤销 59 | 60 | ## 条件价格触发类型 (workingType) 61 | 62 | - MARK_PRICE 63 | - CONTRACT_PRICE 64 | 65 | ## 响应类型 (newOrderRespType) 66 | 67 | - ACK 68 | - RESULT 69 | 70 | ## K线间隔: 71 | 72 | >m -> 分钟; h -> 小时; d -> 天; w -> 周; M -> 月 73 | 74 | - 1m 75 | - 3m 76 | - 5m 77 | - 15m 78 | - 30m 79 | - 1h 80 | - 2h 81 | - 4h 82 | - 6h 83 | - 8h 84 | - 12h 85 | - 1d 86 | - 3d 87 | - 1w 88 | - 1M 89 | 90 | ## 限制种类 (rateLimitType) 91 | 92 | - REQUEST_WEIGHT 93 | 94 | ``` 95 | { 96 | "rateLimitType": "REQUEST_WEIGHT", 97 | "interval": "MINUTE", 98 | "intervalNum": 1, 99 | "limit": 2400 100 | } 101 | ``` 102 | 103 | - ORDERS 104 | 105 | ``` 106 | { 107 | "rateLimitType": "ORDERS", 108 | "interval": "MINUTE", 109 | "intervalNum": 1, 110 | "limit": 1200 111 | } 112 | ``` 113 | 114 | - REQUESTS_WEIGHT 单位时间请求权重之和上限 115 | 116 | - ORDERS 单位时间下单(撤单)次数上限 117 | 118 | ## 限制间隔 119 | 120 | MINUTE -------------------------------------------------------------------------------- /feature/api/binance/delivery.go: -------------------------------------------------------------------------------- 1 | package binance 2 | 3 | import ( 4 | "context" 5 | "go_binance_futures/models" 6 | "go_binance_futures/utils" 7 | 8 | "github.com/adshao/go-binance/v2/delivery" 9 | "github.com/beego/beego/v2/adapter/logs" 10 | "github.com/beego/beego/v2/client/orm" 11 | ) 12 | 13 | // @returns /doc/futuresAccount.js 14 | func GetDeliveryAccount() (res *delivery.Account, err error) { 15 | res, err = deliveryClient.NewGetAccountService().Do(context.Background()) 16 | if err != nil { 17 | logs.Error(err) 18 | return nil, err 19 | } 20 | logs.Info(utils.ToJson(res)) 21 | return res, err 22 | } 23 | 24 | // @see https://developers.binance.com/docs/zh-CN/derivatives/coin-margined-futures/market-data/Exchange-Information 25 | func GetDeliveryExchangeInfo()(res *delivery.ExchangeInfo, err error) { 26 | res, err = deliveryClient.NewExchangeInfoService().Do(context.Background()) 27 | if err != nil { 28 | logs.Error(err) 29 | return nil, err 30 | } 31 | // logs.Info(utils.ToJson(res)) 32 | return res, err 33 | } 34 | 35 | var flagWsDelivery = 0 36 | func UpdateDeliveryCoinByWs(systemConfig *models.Config) { 37 | // binance.BaseWsMainURL = "wss://testnet.binance.vision/ws" 38 | var lock = false 39 | var o = orm.NewOrm() 40 | _, _, err := delivery.WsAllMarketTickerServe(func(event delivery.WsAllMarketTickerEvent) { 41 | if (systemConfig.WsDeliveryEnable == 1) { 42 | if (flagWsDelivery == 0) { 43 | logs.Info("delivery ws start") 44 | flagWsDelivery = 1 45 | } 46 | } else { 47 | if (flagWsDelivery == 1) { 48 | logs.Info("delivery ws stop") 49 | flagWsDelivery = 0 50 | } 51 | lock = false 52 | return 53 | } 54 | if !lock { 55 | lock = true 56 | for _, ticker := range event { 57 | // 永续合约 58 | o.Raw( 59 | "UPDATE `delivery_symbols` set `percentChange` = ?, `close` = ?, `open` = ?, `low` = ?, `high` = ?, `updateTime` = ?, `baseVolume` = ?, `quoteVolume` = ?, `closeQty` = ?, `tradeCount` = ?, `lastClose` = close, `lastUpdateTime` = updateTime WHERE `symbol` = ?", 60 | ticker.PriceChangePercent, 61 | ticker.ClosePrice, 62 | ticker.OpenPrice, 63 | ticker.LowPrice, 64 | ticker.HighPrice, 65 | ticker.Time, 66 | ticker.BaseVolume, // 成交量 67 | ticker.QuoteVolume, // 成交额 68 | ticker.CloseQty, // 最新成交价格上的成交量 69 | ticker.TradeCount, // 成交数 70 | 71 | ticker.Symbol, 72 | ).Exec() 73 | } 74 | lock = false 75 | } 76 | }, func(err error) { 77 | lock = false 78 | logs.Error("delivery ws run error:", err.Error()) 79 | }) 80 | if err != nil { 81 | logs.Error("delivery ws start error:", err.Error()) 82 | return 83 | } 84 | } -------------------------------------------------------------------------------- /feature/delivery.go: -------------------------------------------------------------------------------- 1 | package feature 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | "go_binance_futures/models" 6 | "strings" 7 | 8 | "github.com/beego/beego/v2/client/orm" 9 | "github.com/beego/beego/v2/core/logs" 10 | ) 11 | 12 | // 更新币种的交易精度和插入新币 13 | func UpdateDeliverySymbolsTradePrecision() { 14 | res, err := binance.GetDeliveryExchangeInfo() 15 | if err == nil { 16 | for _, symbol := range res.Symbols { 17 | o := orm.NewOrm() 18 | var tickSize string 19 | var stepSize string 20 | priceFilter := symbol.PriceFilter() 21 | if priceFilter != nil { 22 | tickSize = priceFilter.TickSize 23 | } 24 | lotSizeFilter := symbol.LotSizeFilter() 25 | if lotSizeFilter != nil { 26 | stepSize = lotSizeFilter.StepSize 27 | } 28 | o.QueryTable("delivery_symbols").Filter("symbol", symbol.Symbol).Update(orm.Params{ 29 | "tickSize": tickSize, 30 | "stepSize": stepSize, 31 | }) 32 | 33 | suffixType := "" 34 | if strings.HasSuffix(symbol.Symbol, "_PERP") { 35 | suffixType = "PERP" // 只处理永续合约 36 | } 37 | 38 | if suffixType != "" { // 只要永续合约 39 | if !o.QueryTable("delivery_symbols").Filter("symbol", symbol.Symbol).Exist() { 40 | // 没有的币种插入 41 | logs.Info("add new futures symbol", symbol.Symbol) 42 | o.Insert(&models.DeliverySymbols{ 43 | Symbol: symbol.Symbol, 44 | Enable: 0, // 默认不开启 45 | Leverage: 3, 46 | MarginType: "CROSSED", // 杠杆类型 ISOLATED(逐仓), CROSSED(全仓) 47 | TickSize: tickSize, 48 | StepSize: stepSize, 49 | Usdt: "10", 50 | Profit: "20", 51 | Loss: "20", 52 | StrategyType: "global", 53 | Type: suffixType, // 永续合约 54 | }) 55 | } 56 | } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /feature/delivery_debug.go: -------------------------------------------------------------------------------- 1 | package feature 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | ) 6 | 7 | func GoTestDeliveryAccount() { 8 | binance.GetDeliveryAccount() 9 | } -------------------------------------------------------------------------------- /feature/feature_rush.go: -------------------------------------------------------------------------------- 1 | package feature 2 | 3 | import ( 4 | "errors" 5 | "go_binance_futures/feature/api/binance" 6 | "go_binance_futures/lang" 7 | "go_binance_futures/models" 8 | "go_binance_futures/notify" 9 | "go_binance_futures/utils" 10 | "strconv" 11 | 12 | "github.com/adshao/go-binance/v2/futures" 13 | "github.com/beego/beego/v2/client/orm" 14 | "github.com/beego/beego/v2/core/logs" 15 | ) 16 | 17 | var flagFuturesRush = 0 18 | func TryRush(systemConfig models.Config) { 19 | if (systemConfig.FutureNewEnable == 1) { 20 | if (flagFuturesRush == 0) { 21 | logs.Info("futures rush bot start") 22 | flagFuturesRush = 1 23 | } 24 | } else { 25 | if (flagFuturesRush == 1) { 26 | logs.Info("futures rush bot stop") 27 | flagFuturesRush = 0 28 | } 29 | return 30 | } 31 | o := orm.NewOrm() 32 | var coins []models.NewSymbols 33 | o.QueryTable("new_symbols").OrderBy("ID").Filter("enable", 1).Filter("type", 2).All(&coins) // 允许抢购的合约币 34 | 35 | notHasSizeSymbols := []string{} 36 | 37 | for _, coin := range coins { 38 | if coin.StepSize != "0" { 39 | _, err := tryBuyMarket(coin, coin.StepSize) 40 | if err == nil { 41 | coin.Enable = 0 // 更新为禁用 42 | } 43 | orm.NewOrm().Update(&coin) 44 | } else { 45 | notHasSizeSymbols = append(notHasSizeSymbols, coin.Symbol) 46 | } 47 | } 48 | if len(notHasSizeSymbols) == 0 { 49 | // logs.Info("没有币需要更新交易精度") 50 | return 51 | } 52 | 53 | res, err := binance.GetExchangeInfo() 54 | if err != nil { 55 | logs.Error("GetExchangeInfoError:", err) 56 | return 57 | } 58 | symbolMap := make(map[string]string) 59 | for _, item := range res.Symbols { 60 | lotSizeFilter := item.LotSizeFilter() 61 | if lotSizeFilter != nil { 62 | symbolMap[item.Symbol] = lotSizeFilter.StepSize 63 | } 64 | } 65 | 66 | for _, coin := range coins { 67 | // 找到了币的精度,说明币可能上线了 68 | if stepSize, ok := symbolMap[coin.Symbol]; ok { 69 | logs.Info("lotSize:", stepSize) 70 | _, err := tryBuyMarket(coin, stepSize) 71 | 72 | if err == nil { 73 | coin.Enable = 0 // 更新为禁用 74 | logs.Info("合约抢购成功,关闭交易") 75 | } 76 | coin.StepSize = stepSize 77 | orm.NewOrm().Update(&coin) 78 | } else { 79 | logs.Info("还未上线此合约币种,未确定交易价格数量精度:", coin.Symbol) 80 | } 81 | } 82 | } 83 | 84 | func tryBuyMarket(coin models.NewSymbols, stepSize string) (res *futures.CreateOrderResponse, err error) { 85 | symbol := coin.Symbol 86 | usdt := coin.Usdt 87 | usdt_float64, _ := strconv.ParseFloat(usdt, 64) // 交易金额 88 | buyPrice := 0.0 89 | if (coin.ExpectPrice != "0") { 90 | // 定义的挂单价格 91 | buyPrice, _ = strconv.ParseFloat(coin.ExpectPrice, 64) // 挂单价格 92 | } else { 93 | // 获取最新的交易价格 94 | resPrice, err1 := binance.GetTickerPrice(symbol) 95 | if err1 != nil { 96 | logs.Info("还未上线此合约币种,未确定交易价格symbol:", symbol) 97 | return nil, err1 98 | } 99 | buyPrice, _ = strconv.ParseFloat(resPrice[0].Price, 64) // 预计交易价格 100 | if buyPrice < 0.000000001 { 101 | logs.Info("还未正式上线此合约币种,没有交易盘价格", symbol) 102 | return nil, errors.New("无交易价格") 103 | } 104 | } 105 | logs.Info("尝试开始合约抢币symbol:", symbol) 106 | logs.Info("预计交易价格为:", buyPrice) 107 | // 修改仓位模式 108 | if coin.MarginType == "ISOLATED" { 109 | binance.SetMarginType(symbol, futures.MarginTypeIsolated) 110 | } else { 111 | binance.SetMarginType(symbol, futures.MarginTypeCrossed) 112 | } 113 | 114 | binance.SetLeverage(symbol, int(coin.Leverage)) // 修改合约倍数 115 | leverage_float64 := float64(coin.Leverage) // 合约倍数 116 | quantity := (usdt_float64 / buyPrice) * leverage_float64 // 购买数量 117 | quantity = utils.GetTradePrecision(quantity, stepSize) // 合理精度的数量 118 | // logs.Info("symbol:", symbol, "buyPrice:", buyPrice, "quantity:", quantity) 119 | 120 | if coin.Side == "buy" { 121 | if coin.ExpectPrice != "0" { 122 | // 挂单价格 123 | res, err = binance.BuyLimit(symbol, quantity, buyPrice, futures.PositionSideTypeLong) 124 | } else { 125 | // 市价 126 | res, err = binance.BuyMarket(symbol, quantity, futures.PositionSideTypeLong) 127 | } 128 | } else if coin.Side == "sell" { 129 | if coin.ExpectPrice != "0" { 130 | // 挂单价格 131 | res, err = binance.SellLimit(symbol, quantity, buyPrice, futures.PositionSideTypeShort) 132 | } else { 133 | // 市价 134 | res, err = binance.SellMarket(symbol, quantity, futures.PositionSideTypeShort) 135 | } 136 | } 137 | 138 | positionSide := "long" 139 | if coin.Side == "sell" { 140 | positionSide = "short" 141 | } 142 | if err != nil { 143 | logs.Info("rush error symbol: ", symbol) 144 | logs.Info("err in feature_rush: ", err.Error()) 145 | } else { 146 | pusher.FuturesOpenOrder(notify.FuturesOrderParams{ 147 | Title: lang.Lang("futures.new_coin_rush_notice_title"), 148 | Symbol: symbol, 149 | Side: coin.Side, 150 | PositionSide: positionSide, 151 | Price: buyPrice, 152 | Quantity: quantity, 153 | Leverage: leverage_float64, 154 | Status: "success", 155 | }) 156 | } 157 | return res, err 158 | } -------------------------------------------------------------------------------- /feature/feature_userdata.go: -------------------------------------------------------------------------------- 1 | package feature 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | "go_binance_futures/models" 6 | "math" 7 | "strconv" 8 | "time" 9 | 10 | "github.com/beego/beego/v2/client/orm" 11 | "github.com/beego/beego/v2/core/logs" 12 | ) 13 | 14 | 15 | func SyncUserData() { 16 | deleteOldUserData() 17 | getNowUserData() 18 | go func() { 19 | binance.WsUserData() 20 | }() 21 | } 22 | 23 | // 删除数据表旧数据 24 | func deleteOldUserData() { 25 | o := orm.NewOrm() 26 | o.Raw("DELETE FROM futures_orders where 1=1 and (status = 'NEW' or status = 'PARTIALLY_FILLED')").Exec() // 只删除未成交的订单 27 | o.Raw("DELETE FROM futures_positions where 1=1").Exec() 28 | } 29 | 30 | // 查询 api 接口获取最新数据 31 | func getNowUserData() { 32 | o := orm.NewOrm() 33 | nowTime := time.Now().Unix() * 1000 34 | 35 | // positions 36 | allPositions, err := binance.GetPosition(binance.PositionParams{}) 37 | if err != nil { 38 | logs.Error("GetPosition err in feature_userdata:", err.Error()) 39 | return 40 | } 41 | for _, position := range allPositions { 42 | positionAmt, _ := strconv.ParseFloat(position.PositionAmt, 64) 43 | positionAmtFloatAbs := math.Abs(positionAmt) // 空单为负数,纠正为绝对值 44 | if positionAmtFloatAbs < 0.0000001 { 45 | continue 46 | } 47 | leverage, _ := strconv.ParseInt(position.Leverage, 10, 64) 48 | 49 | var positionModel models.FuturesPosition 50 | o.QueryTable("futures_positions").Filter("symbol", position.Symbol).Filter("side", position.PositionSide).One(&positionModel) 51 | positionModel.Symbol = position.Symbol 52 | positionModel.Side = position.PositionSide 53 | positionModel.Amount = position.PositionAmt 54 | positionModel.Leverage = leverage 55 | positionModel.MarginType = position.MarginType 56 | positionModel.IsolatedWallet = position.IsolatedWallet 57 | positionModel.EntryPrice = position.EntryPrice 58 | positionModel.MarkPrice = position.MarkPrice 59 | positionModel.UnrealizedProfit = position.UnRealizedProfit 60 | positionModel.AccumulatedRealized = "0" 61 | positionModel.MaintenanceMarginRequired = "0" 62 | positionModel.CreateTime = nowTime 63 | positionModel.UpdateTime = nowTime 64 | if positionModel.ID == 0 { 65 | o.Insert(&positionModel) 66 | } else { 67 | o.Update(&positionModel) 68 | } 69 | } 70 | 71 | // open orders 72 | allOpenOrders, err := binance.GetOpenOrder() 73 | if err != nil { 74 | logs.Error("GetOpenOrder err in feature_userdata:", err.Error()) 75 | return 76 | } 77 | for _, order := range allOpenOrders { 78 | var orderModel models.FuturesOrder 79 | o.QueryTable("futures_orders").Filter("order_id", order.OrderID).One(&orderModel) 80 | orderModel.Symbol = order.Symbol 81 | orderModel.ClientOrderId = order.ClientOrderID 82 | orderModel.OrderId = strconv.FormatInt(order.OrderID, 10) 83 | orderModel.Side = string(order.Side) 84 | orderModel.PositionSide = string(order.PositionSide) 85 | orderModel.Type = string(order.Type) 86 | orderModel.Status = string(order.Status) // NEW 87 | orderModel.Price = string(order.Price) 88 | orderModel.OrigQty = order.OrigQuantity 89 | orderModel.ExecutedQty = order.ExecutedQuantity 90 | orderModel.CreateTime = nowTime 91 | orderModel.UpdateTime = nowTime 92 | if orderModel.ID == 0 { 93 | o.Insert(&orderModel) 94 | } else { 95 | o.Update(&orderModel) 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /feature/feature_util.go: -------------------------------------------------------------------------------- 1 | package feature 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | 6 | "github.com/beego/beego/v2/core/logs" 7 | ) 8 | 9 | // 获取币的交易精度 10 | func GetCoinOrderSize(symbol string) (string, string) { 11 | tickSize := "0.0" 12 | stepSize := "0.0" 13 | res, err := binance.GetExchangeInfo() 14 | if err != nil { 15 | logs.Error("GetExchangeInfoError:", err) 16 | return tickSize, stepSize 17 | } 18 | for _, item := range res.Symbols { 19 | if item.Symbol == symbol { 20 | priceFilter := item.PriceFilter() 21 | if priceFilter != nil { 22 | tickSize = priceFilter.TickSize // 价格精度 23 | } 24 | lotSizeFilter := item.LotSizeFilter() 25 | if lotSizeFilter != nil { 26 | stepSize = lotSizeFilter.StepSize // 数量精度 27 | } 28 | return tickSize, stepSize 29 | } 30 | } 31 | return tickSize, stepSize 32 | } 33 | -------------------------------------------------------------------------------- /feature/strategy/coin/coin1.go: -------------------------------------------------------------------------------- 1 | package coin 2 | 3 | import ( 4 | "go_binance_futures/models" 5 | "sort" 6 | ) 7 | 8 | type TradeCoin1 struct { 9 | } 10 | 11 | // 策略: 从涨幅榜中前6个里面随机选取2个,从跌幅榜中的前6个里面随机选取2个, 最近5min交易过的币不再交易 12 | func (tradeCoin1 TradeCoin1) SelectCoins(allCoins []*models.Symbols) (coins []*models.Symbols) { 13 | exclude_symbols_map := getLimitMinOrder(5) 14 | sort.SliceStable(allCoins, func(i, j int) bool { 15 | return allCoins[i].PercentChange < allCoins[j].PercentChange // 涨幅从小到大排序 16 | }) 17 | 18 | filterCoins := []*models.Symbols{} 19 | for _, coin := range allCoins { 20 | if _, exist := exclude_symbols_map[coin.Symbol]; exist { 21 | continue 22 | } // 最近交易过的排除 23 | if coin.Enable == 1 { // 只获取允许交易的币 24 | filterCoins = append(filterCoins, coin) 25 | } 26 | } 27 | sliceLength := 6 28 | if len(filterCoins) < sliceLength { 29 | sliceLength = len(filterCoins) 30 | } 31 | res1 := GetRandArr(filterCoins[:sliceLength], 2) 32 | res2 := GetRandArr(filterCoins[len(filterCoins) - sliceLength:], 2) 33 | coins = append(coins, res1...) 34 | coins = append(coins, res2...) 35 | return coins 36 | } 37 | -------------------------------------------------------------------------------- /feature/strategy/coin/coin2.go: -------------------------------------------------------------------------------- 1 | package coin 2 | 3 | import ( 4 | "go_binance_futures/models" 5 | "sort" 6 | ) 7 | 8 | type TradeCoin2 struct { 9 | } 10 | 11 | // 策略: 最近5min交易过的币不再交易,随机选取3个 12 | func (tradeCoin1 TradeCoin2) SelectCoins(allCoins []*models.Symbols) (coins []*models.Symbols) { 13 | exclude_symbols_map := getLimitMinOrder(5) 14 | sort.SliceStable(allCoins, func(i, j int) bool { 15 | return allCoins[i].PercentChange < allCoins[j].PercentChange // 涨幅从小到大排序 16 | }) 17 | 18 | filterCoins := []*models.Symbols{} 19 | for _, coin := range allCoins { 20 | if _, exist := exclude_symbols_map[coin.Symbol]; exist { 21 | continue 22 | } // 最近交易过的排除 23 | if coin.Enable == 1 { // 只获取允许交易的币 24 | filterCoins = append(filterCoins, coin) 25 | } 26 | } 27 | 28 | coins = GetRandArr(filterCoins, 3) 29 | return coins 30 | } 31 | -------------------------------------------------------------------------------- /feature/strategy/coin/coin3.go: -------------------------------------------------------------------------------- 1 | package coin 2 | 3 | import ( 4 | "go_binance_futures/models" 5 | "sort" 6 | ) 7 | 8 | type TradeCoin3 struct { 9 | } 10 | 11 | // 策略: 最近10min交易过的币不再交易,随机选取2个 12 | func (tradeCoin3 TradeCoin3) SelectCoins(allCoins []*models.Symbols) (coins []*models.Symbols) { 13 | exclude_symbols_map := getLimitMinOrder(10) 14 | sort.SliceStable(allCoins, func(i, j int) bool { 15 | return allCoins[i].PercentChange < allCoins[j].PercentChange // 涨幅从小到大排序 16 | }) 17 | 18 | filterCoins := []*models.Symbols{} 19 | for _, coin := range allCoins { 20 | if _, exist := exclude_symbols_map[coin.Symbol]; exist { 21 | continue 22 | } // 最近交易过的排除 23 | if coin.Enable == 1 { // 只获取允许交易的币 24 | filterCoins = append(filterCoins, coin) 25 | } 26 | } 27 | 28 | coins = GetRandArr(filterCoins, 2) 29 | return coins 30 | } 31 | -------------------------------------------------------------------------------- /feature/strategy/coin/coin4.go: -------------------------------------------------------------------------------- 1 | package coin 2 | 3 | import ( 4 | "go_binance_futures/models" 5 | "sort" 6 | ) 7 | 8 | type TradeCoin4 struct { 9 | } 10 | 11 | // 策略: 最近10min交易过的币不再交易,随机选取2个 12 | func (tradeCoin3 TradeCoin4) SelectCoins(allCoins []*models.Symbols) (coins []*models.Symbols) { 13 | exclude_symbols_map := getLimitMinOrder(10) 14 | sort.SliceStable(allCoins, func(i, j int) bool { 15 | return allCoins[i].PercentChange < allCoins[j].PercentChange // 涨幅从小到大排序 16 | }) 17 | 18 | filterCoins := []*models.Symbols{} 19 | for _, coin := range allCoins { 20 | if _, exist := exclude_symbols_map[coin.Symbol]; exist { 21 | continue 22 | } // 最近交易过的排除 23 | if coin.Enable == 1 { // 只获取允许交易的币 24 | filterCoins = append(filterCoins, coin) 25 | } 26 | } 27 | 28 | coins = GetRandArr(filterCoins, 3) 29 | return coins 30 | } 31 | -------------------------------------------------------------------------------- /feature/strategy/coin/coin5.go: -------------------------------------------------------------------------------- 1 | package coin 2 | 3 | import ( 4 | "go_binance_futures/models" 5 | ) 6 | 7 | type TradeCoin5 struct { 8 | } 9 | 10 | // 策略: 最近5min交易过的币不再交易,随机选取5个 11 | func (tradeCoin5 TradeCoin5) SelectCoins(allCoins []*models.Symbols) (coins []*models.Symbols) { 12 | exclude_symbols_map := getLimitMinLocalOrder(5) 13 | // sort.SliceStable(allCoins, func(i, j int) bool { 14 | // return allCoins[i].PercentChange < allCoins[j].PercentChange // 涨幅从小到大排序 15 | // }) 16 | 17 | filterCoins := []*models.Symbols{} 18 | for _, coin := range allCoins { 19 | if _, exist := exclude_symbols_map[coin.Symbol]; exist { 20 | continue 21 | } // 最近交易过的排除 22 | if coin.Enable == 1 { // 只获取允许交易的币 23 | filterCoins = append(filterCoins, coin) 24 | } 25 | } 26 | 27 | coins = GetRandArr(filterCoins, 5) 28 | return coins 29 | } 30 | -------------------------------------------------------------------------------- /feature/strategy/coin/coin6.go: -------------------------------------------------------------------------------- 1 | package coin 2 | 3 | import ( 4 | "go_binance_futures/models" 5 | "sort" 6 | ) 7 | 8 | type TradeCoin6 struct { 9 | } 10 | 11 | // 策略: 最近5min交易过的币不再交易,从交易额最高的前150个币中随机选取5个 12 | func (tradeCoin TradeCoin6) SelectCoins(allCoins []*models.Symbols) (coins []*models.Symbols) { 13 | exclude_symbols_map := getLimitMinLocalOrder(5) 14 | filterCoins := []*models.Symbols{} 15 | for _, coin := range allCoins { 16 | if _, exist := exclude_symbols_map[coin.Symbol]; exist { 17 | continue 18 | } // 最近交易过的排除 19 | if coin.Enable == 1 { // 只获取允许交易的币 20 | filterCoins = append(filterCoins, coin) 21 | } 22 | } 23 | 24 | sort.SliceStable(filterCoins, func(i, j int) bool { 25 | return filterCoins[i].QuoteVolume > filterCoins[j].QuoteVolume // 交易金额从高到低 26 | }) 27 | 28 | maxCount := len(filterCoins) 29 | if maxCount >= 200 { 30 | maxCount = 200 31 | } 32 | 33 | coins = GetRandArr(filterCoins[0:maxCount], 5) 34 | return coins 35 | } 36 | -------------------------------------------------------------------------------- /feature/strategy/coin/common.go: -------------------------------------------------------------------------------- 1 | package coin 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | "go_binance_futures/models" 6 | "math/rand" 7 | "time" 8 | 9 | "github.com/beego/beego/v2/client/orm" 10 | ) 11 | 12 | // 最近5min交易过的币种订单 13 | func getLimitMinOrder(minute int64) (symbols map[string]bool) { 14 | nowTime := time.Now().Unix() * 1000 // 毫秒时间戳 15 | orders, _ := binance.GetOrders(binance.ListOrderParams{ 16 | StartTime: nowTime - minute * 60 * 1000, 17 | }) 18 | symbols = make(map[string]bool) 19 | for _, order := range orders { 20 | symbols[order.Symbol] = true 21 | } 22 | return symbols 23 | } 24 | 25 | // 最近min交易过的币种local订单 26 | func getLimitMinLocalOrder(minute int64) (symbols map[string]bool) { 27 | startTime := time.Now().Unix() * 1000 - minute * 60 * 1000 // 毫秒时间戳 28 | o := orm.NewOrm() 29 | var orders []models.Order 30 | _, _ = o.QueryTable("order"). 31 | Filter("UpdateTime__gte", startTime). 32 | Filter("Side", "close"). 33 | All(&orders) 34 | symbols = make(map[string]bool) 35 | for _, order := range orders { 36 | symbols[order.Symbol] = true 37 | } 38 | return symbols 39 | } 40 | 41 | 42 | func GetRandArr(arr []*models.Symbols, num int) (result []*models.Symbols) { 43 | if num >= len(arr) { 44 | return arr 45 | } 46 | 47 | indices := make(map[int]bool, len(arr)) 48 | for len(indices) < num { 49 | index := rand.Intn(len(arr)) 50 | indices[index] = true 51 | } 52 | 53 | result = make([]*models.Symbols, num) 54 | i := 0 55 | for k := range indices { 56 | result[i] = arr[k] 57 | i++ 58 | } 59 | 60 | return result 61 | } -------------------------------------------------------------------------------- /feature/strategy/coin_Interface.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import "go_binance_futures/models" 4 | 5 | type CoinStrategy interface { 6 | SelectCoins(allCoins []*models.Symbols) (coins []*models.Symbols) 7 | } 8 | 9 | -------------------------------------------------------------------------------- /feature/strategy/line/common.go: -------------------------------------------------------------------------------- 1 | package line 2 | 3 | import ( 4 | "go_binance_futures/models" 5 | "strconv" 6 | 7 | "github.com/adshao/go-binance/v2/futures" 8 | "github.com/beego/beego/v2/client/orm" 9 | ) 10 | 11 | type Line struct { 12 | Position string 13 | High float64 14 | Low float64 15 | Close float64 16 | Open float64 17 | TradeNum int64 18 | } 19 | type LineData struct { 20 | MaxIndex int 21 | MinIndex int 22 | Line []*Line 23 | } 24 | 25 | // 归一化处理k线数据 26 | func normalizationLineData(data []*futures.Kline) (*LineData) { 27 | maxIndex := 0 28 | maxPrice := 0.0 29 | minIndex := 0 30 | minPrice := 0.0 31 | line := make([]*Line, len(data)) 32 | for key, item := range data { 33 | open, _ := strconv.ParseFloat(item.Open, 64) 34 | high, _ := strconv.ParseFloat(item.High, 64) 35 | low, _ := strconv.ParseFloat(item.Low, 64) 36 | close, _ := strconv.ParseFloat(item.Close, 64) 37 | if key == 0 { 38 | maxPrice = high 39 | minPrice = close 40 | } else { 41 | if (high > maxPrice) { 42 | maxPrice = high 43 | maxIndex = key 44 | } 45 | if (low < minPrice) { 46 | minPrice = low 47 | minIndex = key 48 | } 49 | } 50 | position := "LONG" 51 | if close < open { 52 | position = "SHORT" 53 | } 54 | line[key] = &Line{ 55 | Position: position, 56 | High: high, 57 | Low: low, 58 | Close: close, 59 | Open: open, 60 | TradeNum: item.TradeNum, 61 | } 62 | } 63 | return &LineData{ 64 | MaxIndex: maxIndex, 65 | MinIndex: minIndex, 66 | Line: line, 67 | } 68 | } 69 | 70 | // 获取收盘价列表 71 | func GetClosePrices(data []*Line) ([]float64) { 72 | clonePrices := make([]float64, len(data)) 73 | for key, line := range data { 74 | clonePrices[key] = line.Close 75 | } 76 | return clonePrices 77 | } 78 | 79 | // 从k线获取收盘价列表 80 | func GetLineClosePrices(data []*futures.Kline) ([]float64) { 81 | clonePrices := make([]float64, len(data)) 82 | for key, item := range data { 83 | close, _ := strconv.ParseFloat(item.Close, 64) 84 | clonePrices[key] = close 85 | } 86 | return clonePrices 87 | } 88 | 89 | // 从k线获取最高价、最低价、收盘价列表 90 | func GetLineFloatPrices(data []*futures.Kline) (high, low, close, open []float64) { 91 | high = make([]float64, len(data)) 92 | low = make([]float64, len(data)) 93 | close = make([]float64, len(data)) 94 | open = make([]float64, len(data)) 95 | for key, item := range data { 96 | highPrice, _ := strconv.ParseFloat(item.High, 64) 97 | lowPrice, _ := strconv.ParseFloat(item.Low, 64) 98 | closePrice, _ := strconv.ParseFloat(item.Close, 64) 99 | openPrice, _ := strconv.ParseFloat(item.Open, 64) 100 | high[key] = highPrice 101 | low[key] = lowPrice 102 | close[key] = closePrice 103 | open[key] = openPrice 104 | } 105 | return high, low, close, open 106 | } 107 | 108 | func GetLineFloatValues(data []*futures.Kline) (high, low, close, open, amount, qps []float64) { 109 | high = make([]float64, len(data)) 110 | low = make([]float64, len(data)) 111 | close = make([]float64, len(data)) 112 | open = make([]float64, len(data)) 113 | amount = make([]float64, len(data)) 114 | qps = make([]float64, len(data)) 115 | for key, item := range data { 116 | highPrice, _ := strconv.ParseFloat(item.High, 64) 117 | lowPrice, _ := strconv.ParseFloat(item.Low, 64) 118 | closePrice, _ := strconv.ParseFloat(item.Close, 64) 119 | openPrice, _ := strconv.ParseFloat(item.Open, 64) 120 | amountFloat, _ := strconv.ParseFloat(item.QuoteAssetVolume, 64) 121 | high[key] = highPrice 122 | low[key] = lowPrice 123 | close[key] = closePrice 124 | open[key] = openPrice 125 | amount[key] = amountFloat 126 | qps[key] = amountFloat 127 | if item.CloseTime - item.OpenTime > 0 { 128 | qps[key] = amountFloat / float64(item.CloseTime - item.OpenTime) 129 | } 130 | } 131 | return high, low, close, open, qps, amount 132 | } 133 | 134 | // 获取某中类型的line的数量是否超过阈值 135 | func getRightLine(data []*Line, position string) bool { 136 | positionCount := 0 137 | for _, line := range data { 138 | if line.Position == position { 139 | positionCount++ 140 | } 141 | } 142 | return len(data) - positionCount <= 2 143 | } 144 | 145 | // 获取所有交易的币 146 | func GetAllSymbols() (symbols []*models.Symbols, err error) { 147 | o := orm.NewOrm() 148 | _, err = o.QueryTable("symbols").OrderBy("ID").All(&symbols) 149 | return symbols, err 150 | } 151 | 152 | // 判断 btc 的涨跌是否大于 5%,判断 当前所有币种涨跌数量是否 80%,是否是一个单项行情 153 | func BaseCheckCanLongOrShort() (canLong bool, canShort bool) { 154 | coins, err := GetAllSymbols() 155 | if err != nil { 156 | return false, false 157 | } 158 | canLong, canShort = true, true 159 | riseCount, fallCount := 0, 0 160 | btcPercentChange := 0.0 161 | for _, coin := range coins { 162 | if coin.PercentChange >= 0 { 163 | riseCount++ 164 | } else { 165 | fallCount++ 166 | } 167 | if coin.Symbol == "BTCUSDT" { 168 | btcPercentChange = coin.PercentChange 169 | } 170 | } 171 | // logs.Info(riseCount, fallCount, btcPercentChange, len(coins)) 172 | if riseCount / len(coins) > 75 { 173 | // 都在涨,不要做空 174 | canShort = false 175 | } 176 | if fallCount / len(coins) > 75 { 177 | // 都在跌,不要做多 178 | canLong = false 179 | } 180 | if riseCount / len(coins) > 60 && btcPercentChange > 5 { 181 | // 60% 的币种都在涨,btc 涨幅大于 5,不要做空 182 | canShort = false 183 | } 184 | if fallCount / len(coins) < 60 && btcPercentChange < -5 { 185 | // 60% 的币种都在跌,btc 跌幅大于 5,不要做多 186 | canLong = false 187 | } 188 | return canLong, canShort 189 | } 190 | 191 | func BaseTrend() float64 { 192 | o := orm.NewOrm() 193 | var symbols []models.Symbols 194 | sql := "SELECT * FROM symbols WHERE symbol = ? OR symbol = ? OR symbol = ? OR symbol = ?" 195 | o.Raw(sql, "BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT").QueryRows(&symbols) 196 | 197 | basicTrend := 0.0 198 | for _, v := range symbols { 199 | if v.Symbol == "BTCUSDT" { 200 | basicTrend += v.PercentChange * 0.6 201 | } else if v.Symbol == "ETHUSDT" { 202 | basicTrend += v.PercentChange * 0.3 203 | } else if v.Symbol == "SOLUSDT" { 204 | basicTrend += v.PercentChange * 0.05 205 | } else if v.Symbol == "BNBUSDT" { 206 | basicTrend += v.PercentChange * 0.05 207 | } 208 | } 209 | return basicTrend 210 | } 211 | -------------------------------------------------------------------------------- /feature/strategy/line/line1.go: -------------------------------------------------------------------------------- 1 | package line 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | "go_binance_futures/feature/strategy" 6 | "go_binance_futures/utils" 7 | "math" 8 | "strconv" 9 | ) 10 | 11 | // 策略逻辑: 看的是 3min k线,高频交易 12 | // 做多逻辑 13 | // 1. 最低点在最近3个line,最高点在8个line之前 14 | // 2. 最低点到现在在涨, 最低点之前9个line里面至少7个是红线,最低点的line是红线,(下影线长度 / 实体长度)/ 0.66 15 | // 做空相反逻辑 16 | type TradeLine1 struct { 17 | } 18 | 19 | func (tradeLine1 TradeLine1) GetCanLongOrShort(openParams strategy.OpenParams) (openResult strategy.OpenResult) { 20 | symbols := openParams.Symbols 21 | openResult.CanLong = false 22 | openResult.CanShort = false 23 | 24 | kline_3m, err := binance.GetKlineData(symbols.Symbol, "3m", 50) 25 | if err != nil { 26 | return openResult 27 | } 28 | line3m_result := normalizationLineData(kline_3m) 29 | if checkLongLine3m(line3m_result) { 30 | openResult.CanLong = true 31 | } 32 | if checkShortLine3m(line3m_result) { 33 | openResult.CanShort = true 34 | } 35 | return openResult 36 | } 37 | 38 | // 达到止盈或止损后判断是否可以平仓 39 | func (tradeLine1 TradeLine1) CanOrderComplete(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 40 | symbols := closeParams.Symbols // 交易对 41 | position := closeParams.Position // 当前仓位 42 | closeResult.Complete = false 43 | 44 | lines, err := binance.GetKlineData(symbols.Symbol, "1m", 2) // 1min 线最近2条 45 | if err != nil { 46 | return closeResult 47 | } 48 | close0, _ := strconv.ParseFloat(lines[0].Close, 64) 49 | close1, _ := strconv.ParseFloat(lines[1].Close, 64) 50 | if position.Side == "LONG" { 51 | closeResult.Complete = close0 < close1 // 价格在下跌中 52 | } else if position.Side == "SHORT" { 53 | closeResult.Complete = close0 > close1 // 价格在上涨中 54 | } else { 55 | closeResult.Complete = true 56 | } 57 | return closeResult 58 | } 59 | 60 | func (tradeLine1 TradeLine1) AutoStopOrder(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 61 | closeResult.Complete = false 62 | // symbol := position.Symbol 63 | // side := position.Side 64 | // // if hold_max_time_float64 > 0 { 65 | // // // 最大持仓时间(api的bug,没有UpdateTime字段) 66 | // // // if position.UpdateTime+hold_max_time_float64*60*60*1000 < time.Now().UnixNano()/1e6 { 67 | // // // return true 68 | // // // } 69 | // // } 70 | // if side == "LONG" { 71 | // if (nowProfit < profit_float64 * 0.6 && nowProfit > 0) || nowProfit < -profit_float64 { 72 | // return true 73 | // } 74 | // } 75 | return closeResult 76 | } 77 | 78 | func checkLongLine3m(lineData *LineData) bool { 79 | maxIndex := lineData.MaxIndex 80 | minIndex := lineData.MinIndex 81 | line := lineData.Line 82 | if minIndex >= 1 && minIndex <= 3 && maxIndex >= 8 { 83 | // 最低点在3分前,最高点之前24分 84 | ma3List := utils.MaNList(GetClosePrices(line), 3, 20) // 3min kline 最近20条ma均线,时间从最新到最老 85 | linePoint := line[minIndex] // 最低的那个line 86 | underLength := math.Abs(linePoint.Close - linePoint.Low) // 下影线长度 87 | entityLength := math.Abs(linePoint.Open - linePoint.Close) // 实体长度 88 | if utils.IsDesc(ma3List[0:minIndex]) && // 最低点到现在在涨 89 | getRightLine(line[minIndex:minIndex+9], "SHORT") && // 最低点到最低点+9个line里面至少7个是红线 90 | linePoint.Position == "SHORT" && // 最低点的line是红线 91 | (underLength / entityLength) > 0.66 { // 下影线长度 实体长度 92 | return true 93 | } 94 | } 95 | return false 96 | } 97 | 98 | func checkShortLine3m(lineData *LineData) bool { 99 | maxIndex := lineData.MaxIndex 100 | minIndex := lineData.MinIndex 101 | line := lineData.Line 102 | if maxIndex >= 1 && maxIndex <= 3 && minIndex >= 8 { 103 | // 最高点在3分前,最低点之前30分 104 | ma3List := utils.MaNList(GetClosePrices(line), 3, 20) // 3min kline 最近20条ma均线,时间从最新到最老 105 | linePoint := line[maxIndex] // 最高的那个line 106 | upperLength := math.Abs(linePoint.High - linePoint.Close) // 上影线长度 107 | entityLength := math.Abs(linePoint.Open - linePoint.Close) // 实体长度 108 | if utils.IsAsc(ma3List[0:maxIndex]) && 109 | getRightLine(line[maxIndex:maxIndex+9], "LONG") && // 最低点到最低点+9个line里面至少7个是绿线 110 | linePoint.Position == "LONG" && // 最低点的line是红线 111 | (upperLength / entityLength) > 0.66 { // 上影线长度 > 实体长度 112 | return true 113 | } 114 | } 115 | return false 116 | } 117 | -------------------------------------------------------------------------------- /feature/strategy/line/line2.go: -------------------------------------------------------------------------------- 1 | package line 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | "go_binance_futures/feature/strategy" 6 | "go_binance_futures/utils" 7 | "strconv" 8 | ) 9 | 10 | // 交易逻辑: 看的是 6h k线 11 | // 买入逻辑 12 | // 1. 在4个line线之内,只发生一次金叉,7日 ema 金叉 15日 ema, 3 日 ema 金叉 7 日 ema 13 | // 2. rsi6 < 80, rsi14 < 75 14 | // 3. 3日 ema 最近3条线是上涨的 15 | // 做空相反 16 | type TradeLine2 struct { 17 | } 18 | 19 | // 6小时线的ema金叉 20 | func (TradeLine2 TradeLine2) GetCanLongOrShort(openParams strategy.OpenParams) (openResult strategy.OpenResult) { 21 | symbols := openParams.Symbols 22 | openResult.CanLong = false 23 | openResult.CanShort = false 24 | 25 | kline_6h, err1 := binance.GetKlineData(symbols.Symbol, "6h", 50) 26 | if err1 != nil { 27 | return openResult 28 | } 29 | kline_6h_close := GetLineClosePrices(kline_6h) 30 | 31 | ema6h_3, _ := CalculateExponentialMovingAverage(kline_6h_close, 3) // ma3 32 | ema6h_7, _ := CalculateExponentialMovingAverage(kline_6h_close, 7) // ma7 33 | ema6h_15, _ := CalculateExponentialMovingAverage(kline_6h_close, 15) // ma15 34 | rsi6, _ := CalculateRSI(kline_6h_close, 6) // rsi6 35 | rsi14, _ := CalculateRSI(kline_6h_close, 14) // rsi14 36 | if (rsi6 == nil || rsi14 == nil) { 37 | // 开盘小于 4.5 天 38 | return openResult 39 | } 40 | // logs.Info(symbol, Kdj(ema6h_3, ema6h_7, 4), Kdj(ema6h_7, ema6h_3, 4), utils.IsDesc(ema6h_3[0:2]), rsi6[1], rsi14[1]) 41 | if Kdj(ema6h_3, ema6h_7, 4) && Kdj(ema6h_7, ema6h_15, 4) && utils.IsDesc(ema6h_3[0:2]) && rsi6[0] < 80 && rsi14[0] < 75 { // 1天之内发生过金叉, rsi 没有超买 42 | // 短线穿越长线金叉 43 | openResult.CanLong = true 44 | return openResult 45 | } else if Kdj(ema6h_7, ema6h_3, 4) && Kdj(ema6h_15, ema6h_7, 4) && utils.IsAsc(ema6h_3[0:2]) && rsi6[0] < 80 && rsi14[0] < 75 { 46 | openResult.CanShort = true 47 | return openResult 48 | } 49 | return openResult 50 | } 51 | 52 | // 达到止盈或止损后判断是否可以平仓 53 | func (TradeLine2 TradeLine2) CanOrderComplete(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 54 | symbols := closeParams.Symbols // 交易对 55 | position := closeParams.Position // 当前仓位 56 | closeResult.Complete = false 57 | 58 | lines, err := binance.GetKlineData(symbols.Symbol, "1m", 2) // 1min 线最近2条 59 | if err != nil { 60 | closeResult.Complete = true 61 | return closeResult 62 | } 63 | close0, _ := strconv.ParseFloat(lines[0].Close, 64) 64 | close1, _ := strconv.ParseFloat(lines[1].Close, 64) 65 | if position.Side == "LONG" { 66 | closeResult.Complete = close0 < close1 // 价格在下跌中 67 | } else if position.Side == "SHORT" { 68 | closeResult.Complete = close0 > close1 // 价格在上涨中 69 | } else { 70 | closeResult.Complete = true 71 | } 72 | return closeResult 73 | } 74 | 75 | func (TradeLine2 TradeLine2) AutoStopOrder(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 76 | closeResult.Complete = false 77 | return closeResult 78 | } 79 | -------------------------------------------------------------------------------- /feature/strategy/line/line3.go: -------------------------------------------------------------------------------- 1 | package line 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | "go_binance_futures/feature/strategy" 6 | "go_binance_futures/utils" 7 | "math" 8 | "strconv" 9 | ) 10 | 11 | type TradeLine3 struct { 12 | } 13 | 14 | // 交易逻辑: 主要看 4h 线 15 | // 做多逻辑 16 | // 1. rsi < 70 17 | // 2. 2小时线最低点在 11个之内,最低点到最低点+8个line里面至少6个是红线,最低点事红线,(下影线长度 / 实体长度) > 0.5 18 | // 3. rsi6 < 80, rsi14 < 75 19 | // 4. 4个line之内,4小时线产生金叉 20 | // 做空相反 21 | func (TradeLine3 TradeLine3) GetCanLongOrShort(openParams strategy.OpenParams) (openResult strategy.OpenResult) { 22 | symbols := openParams.Symbols 23 | openResult.CanLong = false 24 | openResult.CanShort = false 25 | 26 | limit := 50 27 | kline_interval1 := "4h" 28 | rsi_period1 := 14 29 | ema_period1 := 3 30 | ema_period2 := 7 31 | 32 | kline_1, err := binance.GetKlineData(symbols.Symbol, kline_interval1, limit) 33 | if err != nil { 34 | return openResult 35 | } 36 | _, _, close1, _ := GetLineFloatPrices(kline_1) // high, low, close 37 | 38 | ema1, _ := CalculateExponentialMovingAverage(close1, ema_period1) 39 | ema2, _ := CalculateExponentialMovingAverage(close1, ema_period2) 40 | 41 | lineData := normalizationLineData(kline_1) // 归一化处理数据 42 | 43 | rsi1, _ := CalculateRSI(close1, rsi_period1) // 获取 rsi 44 | 45 | baseTrend := BaseTrend() // 基础趋势涨跌幅 46 | 47 | if ((Kdj(ema1, ema2, 3) && rsi1[0] > 40) || (lineData.Line[0].Position == "LONG" && rsi1[0] < 18)) && 48 | baseTrend > -3 && // 基础趋势不是大幅下跌 49 | TradeLine3.checkLongLine(lineData) { 50 | // 产生金叉 或 rsi 超卖了 51 | openResult.CanLong = true 52 | return openResult 53 | } 54 | if ((Kdj(ema2, ema1, 3) && rsi1[0] < 60) || (lineData.Line[0].Position == "SHORT" && rsi1[0] > 82)) && 55 | baseTrend < 3 && // 基础趋势不是大幅上涨 56 | // 产生死叉 或 rsi 超买了 57 | TradeLine3.checkShortLine(lineData) { 58 | openResult.CanShort = true 59 | return openResult 60 | } 61 | 62 | return openResult 63 | } 64 | 65 | // 达到止盈或止损后判断是否可以平仓 66 | // 5min 最新价格是否跌破前一个5min的收盘价 67 | func (TradeLine3 TradeLine3) CanOrderComplete(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 68 | symbols := closeParams.Symbols // 交易对 69 | position := closeParams.Position // 当前仓位 70 | closeResult.Complete = false 71 | 72 | lines, err := binance.GetKlineData(symbols.Symbol, "5m", 2) 73 | if err != nil { 74 | closeResult.Complete = true 75 | return closeResult 76 | } 77 | close0, _ := strconv.ParseFloat(lines[0].Close, 64) 78 | close1, _ := strconv.ParseFloat(lines[1].Close, 64) 79 | if position.Side == "LONG" { 80 | closeResult.Complete = close0 < close1 // 价格在下跌中 81 | } else if position.Side == "SHORT" { 82 | closeResult.Complete = close0 > close1 // 价格在上涨中 83 | } else { 84 | closeResult.Complete = true 85 | } 86 | return closeResult 87 | } 88 | 89 | // 达到止盈或止损前判定是否可以平仓 90 | // 1. 1天的kline线,ma7和ma3金叉,ma15和ma3金叉,ma3线3连跌 91 | func (TradeLine3 TradeLine3) AutoStopOrder(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 92 | position := closeParams.Position // 当前仓位 93 | closeResult.Complete = false 94 | 95 | if closeParams.NowProfit < 3 || closeParams.NowProfit > -3 { 96 | closeResult.Complete = false 97 | return closeResult 98 | } 99 | closeResult.Complete = TradeLine3.MarketReversal(position.Symbol, position.Side) 100 | return closeResult 101 | } 102 | 103 | func (TradeLine3 TradeLine3) MarketReversal(symbol string, positionSide string) (isReversal bool) { 104 | kline_1d, err1 := binance.GetKlineData(symbol, "1d", 50) 105 | if err1 != nil { 106 | return false 107 | } 108 | kline_1d_close := GetLineClosePrices(kline_1d) 109 | 110 | ma1d_3, _ := CalculateSimpleMovingAverage(kline_1d_close, 3) // ma3 111 | ma1d_7, _ := CalculateSimpleMovingAverage(kline_1d_close, 7) // ma7 112 | ma1d_15, _ := CalculateSimpleMovingAverage(kline_1d_close, 15) // ma15 113 | 114 | if positionSide== "LONG" { 115 | if Kdj(ma1d_7, ma1d_3, 4) && Kdj(ma1d_15, ma1d_3, 4) && utils.IsAsc(ma1d_3[0:3]) { 116 | return true 117 | } 118 | } 119 | if positionSide == "SHORT" { 120 | if Kdj(ma1d_3, ma1d_7, 4) && Kdj(ma1d_3, ma1d_15, 4) && utils.IsDesc(ma1d_3[0:3]) { 121 | return true 122 | } 123 | } 124 | return false 125 | } 126 | 127 | func (TradeLine3 TradeLine3) checkLongLine(lineData *LineData) bool { 128 | minIndex := lineData.MinIndex 129 | line := lineData.Line 130 | if minIndex >= 0 && minIndex <= 8 { 131 | linePoint := line[minIndex] // 最低的那个line 132 | underLength := math.Abs(linePoint.Close - linePoint.Low) // 下影线长度 133 | entityLength := math.Abs(linePoint.Open - linePoint.Close) // 实体长度 134 | if entityLength / linePoint.Close > 0.02 && // 涨跌幅度 2% 以上 135 | ((underLength / entityLength) < 0.15 || (underLength / entityLength) > 0.6) { // 十字形形态或者大跌形态 136 | return true 137 | } 138 | } 139 | return false 140 | } 141 | 142 | func (TradeLine3 TradeLine3) checkShortLine(lineData *LineData) bool { 143 | maxIndex := lineData.MaxIndex 144 | line := lineData.Line 145 | if maxIndex >= 0 && maxIndex <= 8 { 146 | linePoint := line[maxIndex] // 最高的那个line 147 | upperLength := math.Abs(linePoint.High - linePoint.Close) // 上影线长度 148 | entityLength := math.Abs(linePoint.Open - linePoint.Close) // 实体长度 149 | if entityLength / linePoint.Close > 0.02 && // 涨跌幅度 2% 以上 150 | ((upperLength / entityLength) < 0.15 || (upperLength / entityLength) > 0.6) { // 十字形形态或者大跌形态 151 | return true 152 | } 153 | } 154 | return false 155 | } 156 | 157 | 158 | -------------------------------------------------------------------------------- /feature/strategy/line/line4.go: -------------------------------------------------------------------------------- 1 | package line 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | "go_binance_futures/feature/strategy" 6 | "go_binance_futures/utils" 7 | "math" 8 | "strconv" 9 | 10 | "github.com/adshao/go-binance/v2/futures" 11 | ) 12 | 13 | type TradeLine4 struct { 14 | } 15 | 16 | // 交易逻辑: 看的是 6h k线 和 2h k线 17 | // 做多逻辑 18 | // 1. 在4个line线之内,6小时线产生金叉 19 | // 2. 2小时线最低点在 11个之内,最低点到最低点+8个line里面至少6个是红线,最低点事红线,(下影线长度 / 实体长度) > 0.5 20 | // 3. rsi6 < 80, rsi14 < 75 21 | // 4. 基本盘逻辑: btc 的跌幅大于 5%,当前所有币种跌的数量>= 75% 时禁止做多,反之禁止做空 22 | // 做空相反 23 | func (TradeLine4 TradeLine4) GetCanLongOrShort(openParams strategy.OpenParams) (openResult strategy.OpenResult) { 24 | symbols := openParams.Symbols 25 | openResult.CanLong = false 26 | openResult.CanShort = false 27 | 28 | kline_6h, err1 := binance.GetKlineData(symbols.Symbol, "6h", 50) 29 | kline_1h, err2 := binance.GetKlineData(symbols.Symbol, "2h", 24) 30 | if err1 != nil || err2 != nil { 31 | return openResult 32 | } 33 | kline_6h_close := GetLineClosePrices(kline_6h) 34 | 35 | ma6h_3, _ := CalculateSimpleMovingAverage(kline_6h_close, 3) // ma3 36 | ma6h_7, _ := CalculateSimpleMovingAverage(kline_6h_close, 7) // ma7 37 | rsi6, _ := CalculateRSI(kline_6h_close, 6) // rsi6 38 | rsi14, _ := CalculateRSI(kline_6h_close, 14) // rsi14 39 | if (rsi6 == nil || rsi14 == nil || len(rsi6) < 2 || len(rsi14) < 2) { 40 | // 开盘小于 4.5 天 41 | return openResult 42 | } 43 | baseCanLong, baseCanShort := BaseCheckCanLongOrShort() // 基本盘 44 | isRsi := rsi6[0] < 80 && rsi6[0] > 30 && rsi14[0] < 75 && rsi14[0] > 28 45 | // logs.Info(symbol, Kdj(ma6h_3, ma6h_7, 4), Kdj(ma6h_7, ma6h_3, 4), rsi6[1], rsi14[1]) 46 | if Kdj(ma6h_3, ma6h_7, 4) && TradeLine4.checkLongLine(kline_1h) && isRsi && baseCanLong{ // 1天之内发生过金叉, rsi 没有超买 47 | // 短线穿越长线金叉 48 | openResult.CanLong = true 49 | return openResult 50 | } 51 | if Kdj(ma6h_7, ma6h_3, 4) && TradeLine4.checkShortLine(kline_1h)&& isRsi && baseCanShort { 52 | openResult.CanShort = true 53 | return openResult 54 | } 55 | return openResult 56 | } 57 | 58 | // 达到止盈或止损后判断是否可以平仓 59 | // 5min 最新价格是否跌破前一个5min的收盘价 60 | func (TradeLine4 TradeLine4) CanOrderComplete(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 61 | symbols := closeParams.Symbols // 交易对 62 | position := closeParams.Position // 当前仓位 63 | closeResult.Complete = false 64 | 65 | lines, err := binance.GetKlineData(symbols.Symbol, "5m", 2) 66 | if err != nil { 67 | closeResult.Complete = true 68 | return closeResult 69 | } 70 | close0, _ := strconv.ParseFloat(lines[0].Close, 64) 71 | close1, _ := strconv.ParseFloat(lines[1].Close, 64) 72 | if position.Side == "LONG" { 73 | closeResult.Complete = close0 < close1 // 价格在下跌中 74 | } else if position.Side == "SHORT" { 75 | closeResult.Complete = close0 > close1 // 价格在上涨中 76 | } else { 77 | closeResult.Complete = true 78 | } 79 | return closeResult 80 | } 81 | 82 | // 达到止盈或止损前判定是否可以平仓 83 | // 1. 1天的kline线,ma7和ma3金叉,ma15和ma3金叉,ma3线3连跌 84 | func (TradeLine4 TradeLine4) AutoStopOrder(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 85 | position := closeParams.Position // 当前仓位 86 | closeResult.Complete = false 87 | 88 | if closeParams.NowProfit < 3 || closeParams.NowProfit > -3 { 89 | closeResult.Complete = false 90 | return closeResult 91 | } 92 | closeResult.Complete = TradeLine4.MarketReversal(position.Symbol, position.Side) 93 | return closeResult 94 | } 95 | 96 | func (TradeLine4 TradeLine4) MarketReversal(symbol string, positionSide string) (isReversal bool) { 97 | kline_1d, err1 := binance.GetKlineData(symbol, "1d", 50) 98 | if err1 != nil { 99 | return false 100 | } 101 | kline_1d_close := GetLineClosePrices(kline_1d) 102 | 103 | ma1d_3, _ := CalculateSimpleMovingAverage(kline_1d_close, 3) // ma3 104 | ma1d_7, _ := CalculateSimpleMovingAverage(kline_1d_close, 7) // ma7 105 | ma1d_15, _ := CalculateSimpleMovingAverage(kline_1d_close, 15) // ma15 106 | 107 | if positionSide== "LONG" { 108 | if Kdj(ma1d_7, ma1d_3, 4) && Kdj(ma1d_15, ma1d_3, 4) && utils.IsAsc(ma1d_3[0:3]) { 109 | return true 110 | } 111 | } 112 | if positionSide == "SHORT" { 113 | if Kdj(ma1d_3, ma1d_7, 4) && Kdj(ma1d_3, ma1d_15, 4) && utils.IsDesc(ma1d_3[0:3]) { 114 | return true 115 | } 116 | } 117 | return false 118 | } 119 | 120 | func (TradeLine4 TradeLine4) checkLongLine(klines []*futures.Kline) bool { 121 | lineData := normalizationLineData(klines) // 24条线 122 | minIndex := lineData.MinIndex 123 | line := lineData.Line 124 | if minIndex >= 1 && minIndex <= 11 { 125 | linePoint := line[minIndex] // 最低的那个line 126 | underLength := math.Abs(linePoint.Close - linePoint.Low) // 下影线长度 127 | entityLength := math.Abs(linePoint.Open - linePoint.Close) // 实体长度 128 | if getRightLine(line[minIndex:minIndex+8], "SHORT") && // 最低点到最低点+8个line里面至少6个是红线 129 | linePoint.Position == "SHORT" && // 最低点的line是跌 130 | (underLength / entityLength) > 0.5 { // 下影线长度 实体长度 131 | return true 132 | } 133 | } 134 | return false 135 | } 136 | 137 | func (TradeLine4 TradeLine4) checkShortLine(klines []*futures.Kline) bool { 138 | lineData := normalizationLineData(klines) // 24条线 139 | maxIndex := lineData.MaxIndex 140 | line := lineData.Line 141 | if maxIndex >= 1 && maxIndex <= 11 { 142 | linePoint := line[maxIndex] // 最高的那个line 143 | upperLength := math.Abs(linePoint.High - linePoint.Close) // 上影线长度 144 | entityLength := math.Abs(linePoint.Open - linePoint.Close) // 实体长度 145 | if getRightLine(line[maxIndex:maxIndex+8], "LONG") && // 最低点到最低点+8个line里面至少6个是绿线 146 | linePoint.Position == "LONG" && // 最低点的line是涨 147 | (upperLength / entityLength) > 0.5 { // 上影线长度 > 实体长度 148 | return true 149 | } 150 | } 151 | return false 152 | } 153 | 154 | -------------------------------------------------------------------------------- /feature/strategy/line/line5.go: -------------------------------------------------------------------------------- 1 | package line 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | "go_binance_futures/feature/strategy" 6 | "go_binance_futures/utils" 7 | "strconv" 8 | 9 | "github.com/adshao/go-binance/v2/futures" 10 | ) 11 | 12 | type TradeLine5 struct { 13 | } 14 | 15 | // 交易逻辑: 看的是 1min 线,高频逻辑,适合单边行情,右侧交易,猛涨时追涨,反之依然 16 | // 做多逻辑 17 | // 1. 当前价格 > 1min前k线的开盘价格 18 | // 2. 最近一分钟的变化幅度大于 0.9% 19 | // 做空相反 20 | func (TradeLine5 TradeLine5) GetCanLongOrShort(openParams strategy.OpenParams) (openResult strategy.OpenResult) { 21 | symbols := openParams.Symbols 22 | openResult.CanLong = false 23 | openResult.CanShort = false 24 | 25 | kline_1, err1 := binance.GetKlineData(symbols.Symbol, "1m", 30) 26 | if err1 != nil { 27 | return openResult 28 | } 29 | if TradeLine5.checkLine(kline_1) { 30 | return openResult 31 | } 32 | 33 | lastOpenPrice, _ := strconv.ParseFloat(kline_1[1].Open, 64) // 1min 前的价格 34 | nowPrice, _ := strconv.ParseFloat(kline_1[0].Close, 64) 35 | 36 | percentLimit := 0.009 // 变化幅度 37 | 38 | if (nowPrice > lastOpenPrice) && (nowPrice - lastOpenPrice) / lastOpenPrice >= percentLimit { 39 | openResult.CanLong = true 40 | return openResult 41 | } 42 | if (nowPrice < lastOpenPrice) && (lastOpenPrice - nowPrice) / lastOpenPrice >= percentLimit { 43 | openResult.CanShort = true 44 | return openResult 45 | } 46 | return openResult 47 | } 48 | 49 | // 达到止盈或止损后判断是否可以平仓 50 | // 3min 最新价格是否跌破前一个3min的收盘价 51 | func (TradeLine5 TradeLine5) CanOrderComplete(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 52 | symbols := closeParams.Symbols // 交易对 53 | position := closeParams.Position // 当前仓位 54 | closeResult.Complete = false 55 | 56 | lines, err := binance.GetKlineData(symbols.Symbol, "5m", 2) 57 | if err != nil { 58 | closeResult.Complete = true 59 | return closeResult 60 | } 61 | close0, _ := strconv.ParseFloat(lines[0].Close, 64) 62 | close1, _ := strconv.ParseFloat(lines[1].Close, 64) 63 | if position.Side == "LONG" { 64 | closeResult.Complete = close0 < close1 // 价格在下跌中 65 | } else if position.Side == "SHORT" { 66 | closeResult.Complete = close0 > close1 // 价格在上涨中 67 | } else { 68 | closeResult.Complete = true 69 | } 70 | return closeResult 71 | } 72 | 73 | // 达到止盈或止损前判定是否可以平仓 74 | // 1. 1天的kline线,ma7和ma3金叉,ma15和ma3金叉,ma3线3连跌 75 | func (TradeLine5 TradeLine5) AutoStopOrder(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 76 | position := closeParams.Position // 当前仓位 77 | closeResult.Complete = false 78 | 79 | if closeParams.NowProfit < 3 || closeParams.NowProfit > -3 { 80 | closeResult.Complete = false 81 | return closeResult 82 | } 83 | closeResult.Complete = TradeLine5.MarketReversal(position.Symbol, position.Side) 84 | return closeResult 85 | } 86 | 87 | func (TradeLine5 TradeLine5) MarketReversal(symbol string, positionSide string) (isReversal bool) { 88 | kline_1d, err1 := binance.GetKlineData(symbol, "1d", 50) 89 | if err1 != nil { 90 | return false 91 | } 92 | kline_1d_close := GetLineClosePrices(kline_1d) 93 | 94 | ma1d_3, _ := CalculateSimpleMovingAverage(kline_1d_close, 3) // ma3 95 | ma1d_7, _ := CalculateSimpleMovingAverage(kline_1d_close, 7) // ma7 96 | ma1d_15, _ := CalculateSimpleMovingAverage(kline_1d_close, 15) // ma15 97 | 98 | if positionSide== "LONG" { 99 | if Kdj(ma1d_7, ma1d_3, 4) && Kdj(ma1d_15, ma1d_3, 4) && utils.IsAsc(ma1d_3[0:3]) { 100 | return true 101 | } 102 | } 103 | if positionSide == "SHORT" { 104 | if Kdj(ma1d_3, ma1d_7, 4) && Kdj(ma1d_3, ma1d_15, 4) && utils.IsDesc(ma1d_3[0:3]) { 105 | return true 106 | } 107 | } 108 | return false 109 | } 110 | 111 | 112 | func (TradeLine5 TradeLine5) checkLine(kLines []*futures.Kline) bool { 113 | // 判定是否最近有个一次突变 114 | for _, item := range kLines { 115 | open, _ := strconv.ParseFloat(item.Open, 64) 116 | high, _ := strconv.ParseFloat(item.High, 64) 117 | low, _ := strconv.ParseFloat(item.Low, 64) 118 | if (high - low) / open >= 0.015 { 119 | return true 120 | } 121 | } 122 | return false 123 | } 124 | -------------------------------------------------------------------------------- /feature/strategy/line/line6.go: -------------------------------------------------------------------------------- 1 | package line 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | "go_binance_futures/feature/strategy" 6 | "go_binance_futures/utils" 7 | "strconv" 8 | ) 9 | 10 | type TradeLine6 struct { 11 | } 12 | 13 | // 交易逻辑: 看的是 3min 线,高频交易,适合震荡行情,猛涨时做空,反之做多 14 | // 做多逻辑 15 | // 1. 当前价格 < 3min前k线的开盘价格 16 | // 2. 最近3钟的变化幅度大于 0.15% 17 | // 做空相反 18 | func (TradeLine6 TradeLine6) GetCanLongOrShort(openParams strategy.OpenParams) (openResult strategy.OpenResult) { 19 | symbols := openParams.Symbols 20 | openResult.CanLong = false 21 | openResult.CanShort = false 22 | 23 | kline_1, err1 := binance.GetKlineData(symbols.Symbol, "3m", 20) 24 | if err1 != nil { 25 | return openResult 26 | } 27 | 28 | lastOpenPrice, _ := strconv.ParseFloat(kline_1[0].Open, 64) 29 | nowPrice, _ := strconv.ParseFloat(kline_1[0].Close, 64) 30 | 31 | percentLimit := 0.015 // 变化幅度 32 | 33 | if (nowPrice > lastOpenPrice) && (nowPrice - lastOpenPrice) / lastOpenPrice >= percentLimit { 34 | openResult.CanShort = true 35 | } 36 | if (nowPrice < lastOpenPrice) && (lastOpenPrice - nowPrice) / lastOpenPrice >= percentLimit { 37 | openResult.CanLong = true 38 | } 39 | 40 | return openResult 41 | } 42 | 43 | // 达到止盈或止损后判断是否可以平仓 44 | // 3min 最新价格是否跌破前一个3min的收盘价 45 | func (TradeLine6 TradeLine6) CanOrderComplete(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 46 | symbols := closeParams.Symbols // 交易对 47 | position := closeParams.Position // 当前仓位 48 | closeResult.Complete = false 49 | 50 | lines, err := binance.GetKlineData(symbols.Symbol, "3m", 2) 51 | if err != nil { 52 | closeResult.Complete = true 53 | return closeResult 54 | } 55 | close0, _ := strconv.ParseFloat(lines[0].Close, 64) 56 | close1, _ := strconv.ParseFloat(lines[1].Close, 64) 57 | if position.Side == "LONG" { 58 | closeResult.Complete = close0 < close1 // 价格在下跌中 59 | } else if position.Side == "SHORT" { 60 | closeResult.Complete = close0 > close1 // 价格在上涨中 61 | } else { 62 | closeResult.Complete = true 63 | } 64 | return closeResult 65 | } 66 | 67 | // 达到止盈或止损前判定是否可以平仓 68 | // 1. 1天的kline线,ma7和ma3金叉,ma15和ma3金叉,ma3线3连跌 69 | func (TradeLine6 TradeLine6) AutoStopOrder(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 70 | position := closeParams.Position // 当前仓位 71 | closeResult.Complete = false 72 | 73 | if closeParams.NowProfit < 3 || closeParams.NowProfit > -3 { 74 | closeResult.Complete = false 75 | return closeResult 76 | } 77 | closeResult.Complete = TradeLine6.MarketReversal(position.Symbol, position.Side) 78 | return closeResult 79 | } 80 | 81 | func (TradeLine6 TradeLine6) MarketReversal(symbol string, positionSide string) (isReversal bool) { 82 | kline_1d, err1 := binance.GetKlineData(symbol, "1d", 50) 83 | if err1 != nil { 84 | return false 85 | } 86 | kline_1d_close := GetLineClosePrices(kline_1d) 87 | 88 | ma1d_3, _ := CalculateSimpleMovingAverage(kline_1d_close, 3) // ma3 89 | ma1d_7, _ := CalculateSimpleMovingAverage(kline_1d_close, 7) // ma7 90 | ma1d_15, _ := CalculateSimpleMovingAverage(kline_1d_close, 15) // ma15 91 | 92 | if positionSide== "LONG" { 93 | if Kdj(ma1d_7, ma1d_3, 4) && Kdj(ma1d_15, ma1d_3, 4) && utils.IsAsc(ma1d_3[0:3]) { 94 | return true 95 | } 96 | } 97 | if positionSide == "SHORT" { 98 | if Kdj(ma1d_3, ma1d_7, 4) && Kdj(ma1d_3, ma1d_15, 4) && utils.IsDesc(ma1d_3[0:3]) { 99 | return true 100 | } 101 | } 102 | return false 103 | } 104 | -------------------------------------------------------------------------------- /feature/strategy/line/line7.go: -------------------------------------------------------------------------------- 1 | package line 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | "go_binance_futures/feature/strategy" 6 | "go_binance_futures/utils" 7 | "strconv" 8 | ) 9 | 10 | type TradeLine7 struct { 11 | } 12 | 13 | // 交易逻辑: 根据肯纳特通道判断是否可以开仓 14 | // kc1(50, 2.75) 15 | // kc2(50, 3.75) 16 | // 做多逻辑 17 | // 1. 价格跌破最低轨道(kc2下轨),然后突破到次低轨道(kc1下轨)时,做多,建议止盈50%位置在 kc1 中轨附近位置,剩余 50% 止盈50%位置在 kc1 上轨附近位置 18 | // 做空相反 19 | func (TradeLine TradeLine7) GetCanLongOrShort(openParams strategy.OpenParams) (openResult strategy.OpenResult) { 20 | symbols := openParams.Symbols 21 | openResult.CanLong = false 22 | openResult.CanShort = false 23 | 24 | limit := 150 25 | period := 50 26 | multiplier1 := 2.75 // 窄通道 27 | multiplier2 := 3.75 // 宽通道 28 | kline_1, err := binance.GetKlineData(symbols.Symbol, "4h", limit) 29 | if err != nil { 30 | return openResult 31 | } 32 | kline_2, err := binance.GetKlineData(symbols.Symbol, "12h", limit) 33 | if err != nil { 34 | return openResult 35 | } 36 | // kline_3, err := binance.GetKlineData(symbol, "1d", limit) 37 | // if err != nil { 38 | // return false, false 39 | // } 40 | 41 | if len(kline_1) < limit || len(kline_2) < limit { 42 | return openResult 43 | } 44 | 45 | high1, low1, close1, _ := GetLineFloatPrices(kline_1) 46 | upper1, _, lower1 := CalculateKeltnerChannels(high1, low1, close1, period, multiplier1) // kc1 47 | upper2, _, lower2 := CalculateKeltnerChannels(high1, low1, close1, period, multiplier2) // kc2 48 | 49 | close2 := GetLineClosePrices(kline_2) 50 | limitPeriod := 12 // 最近n根k线 51 | 52 | // 之前的最低价格跌破了 kc2 的下轨,然后当前价格超越了 kc1 下轨,止损位置在 kc1 下轨附近位置,止盈50%位置在 kc1 中规附近位置,剩余 50% 止盈50%位置在 kc1 上轨附近位置 53 | // 大级别看起来是上升通道 54 | if (close1[0] > lower1[0] && close1[1] < lower1[1]) { 55 | for i := 2; i < limitPeriod; i++ { 56 | // 最近10根k线最低价格在kc2下轨之下 57 | if low1[i] < lower2[i] { 58 | // 大级别看起来是上升通道 59 | if (utils.IsDesc(close2[0:2])) { 60 | openResult.CanLong = true 61 | return openResult 62 | } 63 | } 64 | } 65 | } 66 | 67 | // 之前的最高价格超越了 kc2 的上轨,然后当前价格跌破了 kc1 上轨,止损位置在 kc1 上轨附近位置,止盈50%位置在 kc1 中规附近位置,剩余 50% 止盈50%位置在 kc1 下轨附近位置 68 | // 大级别看起来是下降通道 69 | if (close1[0] < upper1[0] && close1[1] > upper1[1]) { 70 | for i := 1; i < limitPeriod; i++ { 71 | // 最近10根k线最高价格在kc2上轨之上 72 | if high1[i] > upper2[i] { 73 | // 大级别看起来是下降通道 74 | if (utils.IsAsc(close2[0:3])) { 75 | openResult.CanShort = true 76 | return openResult 77 | } 78 | } 79 | } 80 | } 81 | 82 | return openResult 83 | } 84 | 85 | func (TradeLine TradeLine7) CanOrderComplete(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 86 | symbols := closeParams.Symbols // 交易对 87 | position := closeParams.Position // 当前仓位 88 | closeResult.Complete = false 89 | 90 | lines, err := binance.GetKlineData(symbols.Symbol, "3m", 2) 91 | if err != nil { 92 | closeResult.Complete = true 93 | return closeResult 94 | } 95 | close0, _ := strconv.ParseFloat(lines[0].Close, 64) 96 | close1, _ := strconv.ParseFloat(lines[1].Close, 64) 97 | if position.Side == "LONG" { 98 | closeResult.Complete = close0 < close1 // 价格在下跌中 99 | } else if position.Side == "SHORT" { 100 | closeResult.Complete = close0 > close1 // 价格在上涨中 101 | } else { 102 | closeResult.Complete = true 103 | } 104 | return closeResult 105 | } 106 | 107 | // 达到止盈或止损前判定是否可以平仓 108 | // 1. 1天的kline线,ma7和ma3金叉,ma15和ma3金叉,ma3线3连跌 109 | func (TradeLine TradeLine7) AutoStopOrder(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 110 | position := closeParams.Position // 当前仓位 111 | closeResult.Complete = false 112 | 113 | if closeParams.NowProfit < 3 || closeParams.NowProfit > -3 { 114 | closeResult.Complete = false 115 | return closeResult 116 | } 117 | closeResult.Complete = TradeLine.MarketReversal(position.Symbol, position.Side) 118 | return closeResult 119 | } 120 | 121 | func (TradeLine TradeLine7) MarketReversal(symbol string, positionSide string) (isReversal bool) { 122 | // kline_1d, err1 := binance.GetKlineData(symbol, "1d", 50) 123 | // if err1 != nil { 124 | // return false 125 | // } 126 | // kline_1d_close := GetLineClosePrices(kline_1d) 127 | 128 | // ma1d_3, _ := CalculateSimpleMovingAverage(kline_1d_close, 3) // ma3 129 | // ma1d_7, _ := CalculateSimpleMovingAverage(kline_1d_close, 7) // ma7 130 | // ma1d_15, _ := CalculateSimpleMovingAverage(kline_1d_close, 15) // ma15 131 | 132 | // if positionSide== "LONG" { 133 | // if Kdj(ma1d_7, ma1d_3, 4) && Kdj(ma1d_15, ma1d_3, 4) && utils.IsAsc(ma1d_3[0:3]) { 134 | // return true 135 | // } 136 | // } 137 | // if positionSide == "SHORT" { 138 | // if Kdj(ma1d_3, ma1d_7, 4) && Kdj(ma1d_3, ma1d_15, 4) && utils.IsDesc(ma1d_3[0:3]) { 139 | // return true 140 | // } 141 | // } 142 | return false 143 | } 144 | -------------------------------------------------------------------------------- /feature/strategy/line/line_custom.go: -------------------------------------------------------------------------------- 1 | package line 2 | 3 | import ( 4 | "encoding/json" 5 | "go_binance_futures/feature/api/binance" 6 | "go_binance_futures/feature/strategy" 7 | "go_binance_futures/technology" 8 | "go_binance_futures/types" 9 | "strconv" 10 | 11 | "github.com/beego/beego/v2/core/logs" 12 | "github.com/expr-lang/expr" 13 | ) 14 | 15 | type TradeLineCustom struct { 16 | } 17 | 18 | // 交易逻辑: 自定义逻辑 19 | func (TradeLine TradeLineCustom) GetCanLongOrShort(openParams strategy.OpenParams) (openResult strategy.OpenResult) { 20 | coin := openParams.Symbols 21 | openResult.CanLong = false 22 | openResult.CanShort = false 23 | 24 | var strategyConfig technology.StrategyConfig 25 | err := json.Unmarshal([]byte(coin.Strategy), &strategyConfig) 26 | if err != nil { 27 | logs.Error("Error unmarshalling JSON Symbol: ", coin.Symbol) 28 | logs.Error("Error unmarshalling JSON:", err.Error()) 29 | return openResult 30 | } 31 | env := InitParseEnv(coin.Symbol, coin.Technology) 32 | for _, strategy := range strategyConfig { 33 | if strategy.Enable && (strategy.Type == "long" || strategy.Type == "short") { 34 | program, err := expr.Compile(strategy.Code, expr.Env(env)) 35 | if err != nil { 36 | logs.Error("Error Strategy Compile Symbol: ", coin.Symbol) 37 | logs.Error("Error Strategy Compile:", err.Error()) 38 | continue 39 | } 40 | output, err := expr.Run(program, env) 41 | if err != nil { 42 | logs.Error("Error Strategy Run Symbol: ", coin.Symbol) 43 | logs.Error("Error Strategy Run:", err.Error()) 44 | continue 45 | } 46 | if result, ok := output.(bool); ok && result { 47 | if strategy.Type == "long" { 48 | openResult.CanLong = true 49 | } else if strategy.Type == "short" { 50 | openResult.CanShort = true 51 | } 52 | break 53 | } 54 | } 55 | } 56 | return openResult 57 | } 58 | 59 | // 达到止盈或止损率之后的判定是否可以平仓 60 | func (TradeLine TradeLineCustom) CanOrderComplete(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 61 | coin := closeParams.Symbols // 交易对 62 | position := closeParams.Position // 当前仓位 63 | closeResult.Complete = false 64 | 65 | var strategyConfig technology.StrategyConfig 66 | err := json.Unmarshal([]byte(coin.Strategy), &strategyConfig) 67 | if err != nil { 68 | logs.Error("Error unmarshalling JSON Symbol: ", coin.Symbol) 69 | logs.Error("Error unmarshalling JSON:", err.Error()) 70 | return closeResult 71 | } 72 | findStrategy := false 73 | env := InitParseEnv(coin.Symbol, coin.Technology) 74 | env["ROI"] = closeParams.NowProfit // 当前收益率 75 | env["Position"] = types.FuturesPositionCode{ 76 | Symbol: coin.Symbol, 77 | Side: position.Side, 78 | Amount: func() float64 { 79 | amount, err := strconv.ParseFloat(position.Amount, 64) 80 | if err != nil { 81 | logs.Error("Error parsing Amount to float64:", err.Error()) 82 | return 0 83 | } 84 | return amount 85 | }(), 86 | Leverage: position.Leverage, 87 | EntryPrice: func() float64 { 88 | entryPrice, err := strconv.ParseFloat(position.EntryPrice, 64) 89 | if err != nil { 90 | logs.Error("Error parsing EntryPrice to float64:", err.Error()) 91 | return 0 92 | } 93 | return entryPrice 94 | }(), 95 | MarkPrice: func() float64 { 96 | markPrice, err := strconv.ParseFloat(position.MarkPrice, 64) 97 | if err != nil { 98 | logs.Error("Error parsing MarkPrice to float64:", err.Error()) 99 | return 0 100 | } 101 | return markPrice 102 | }(), 103 | UnrealizedProfit: func() float64 { 104 | unrealizedProfit, err := strconv.ParseFloat(position.UnrealizedProfit, 64) 105 | if err != nil { 106 | logs.Error("Error parsing UnrealizedProfit to float64:", err.Error()) 107 | return 0 108 | } 109 | return unrealizedProfit 110 | }(), 111 | Mock: false, 112 | CreateTime: position.CreateTime, 113 | SourceType: position.SourceType, 114 | } // 当前仓位信息 115 | for _, strategy := range strategyConfig { 116 | if strategy.Enable && (strategy.Type == "close_long" || strategy.Type == "close_short") { 117 | if strategy.Type == "close_long" && position.Side != "LONG" { 118 | // 平多仓的策略,当前仓位不是多仓,跳过 119 | continue 120 | } 121 | if strategy.Type == "close_short" && position.Side != "SHORT" { 122 | // 平空仓的策略,当前仓位不是空仓,跳过 123 | continue 124 | } 125 | 126 | program, err := expr.Compile(strategy.Code, expr.Env(env)) 127 | if err != nil { 128 | logs.Error("Error Strategy Compile Symbol: ", coin.Symbol) 129 | logs.Error("Error Strategy Compile:", err.Error()) 130 | continue 131 | } 132 | output, err := expr.Run(program, env) 133 | if err != nil { 134 | logs.Error("Error Strategy Run Symbol: ", coin.Symbol) 135 | logs.Error("Error Strategy Run:", err.Error()) 136 | continue 137 | } 138 | findStrategy = true // 发现有正常能执行的平仓策略 139 | if result, ok := output.(bool); ok && result { 140 | closeResult.Complete = true 141 | } 142 | } 143 | } 144 | if !findStrategy { 145 | // 没有定义平仓策略,使用简单的策略平仓 146 | return TradeLine.simpleCloseStrategy(closeParams) 147 | } 148 | return closeResult 149 | } 150 | 151 | // 达到止盈或止损止前的判定是否可以平仓 152 | func (TradeLine TradeLineCustom) AutoStopOrder(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 153 | closeResult.Complete = false 154 | return closeResult 155 | } 156 | 157 | func (TradeLine TradeLineCustom) simpleCloseStrategy(closeParams strategy.CloseParams) (closeResult strategy.CloseResult) { 158 | coin := closeParams.Symbols // 交易对 159 | position := closeParams.Position // 当前仓位 160 | closeResult.Complete = false 161 | 162 | if closeParams.NowProfit < 3 || closeParams.NowProfit > -3 { 163 | // 收益率小于3%或者大于-3%, 不平仓 164 | closeResult.Complete = false 165 | return closeResult 166 | } 167 | 168 | lines, err := binance.GetKlineData(coin.Symbol, "5m", 2) 169 | if err != nil { 170 | logs.Error("Error GetKlineData Symbol in line_custom: ", coin.Symbol) 171 | logs.Error("Error GetKlineData Symbol in line_custom:", err.Error()) 172 | closeResult.Complete = true 173 | return closeResult 174 | } 175 | close0, _ := strconv.ParseFloat(lines[0].Close, 64) 176 | close1, _ := strconv.ParseFloat(lines[1].Close, 64) 177 | if position.Side == "LONG" { 178 | closeResult.Complete = close0 < close1 // 价格在下跌中 179 | } else if position.Side == "SHORT" { 180 | closeResult.Complete = close0 > close1 // 价格在上涨中 181 | } else { 182 | // BOTH 不处理双向持仓类型 183 | closeResult.Complete = true 184 | } 185 | return closeResult 186 | } 187 | -------------------------------------------------------------------------------- /feature/strategy/line_Interface.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "go_binance_futures/models" 5 | "go_binance_futures/types" 6 | ) 7 | 8 | 9 | type OpenParams struct { 10 | Symbols *models.Symbols 11 | } 12 | 13 | type OpenResult struct { 14 | CanLong bool 15 | CanShort bool 16 | } 17 | 18 | type CloseParams struct { 19 | Symbols *models.Symbols 20 | Position types.FuturesPosition 21 | NowProfit float64 // 当前收益率% 22 | } 23 | 24 | type CloseResult struct { 25 | Complete bool 26 | } 27 | 28 | type LineStrategy interface { 29 | // 是否可以买多或者买空 30 | GetCanLongOrShort(openParams OpenParams) (openResult OpenResult) 31 | // 达到止盈或止损点后判断是否可以平仓 32 | CanOrderComplete(closeParams CloseParams) (closeResult CloseResult) 33 | // 达到止盈或止损前判定是否可以平仓 34 | AutoStopOrder(closeParams CloseParams) (closeResult CloseResult) 35 | } 36 | 37 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go_binance_futures 2 | 3 | go 1.21 4 | 5 | require github.com/beego/beego/v2 v2.1.0 6 | 7 | require ( 8 | github.com/adshao/go-binance/v2 v2.6.1 9 | github.com/expr-lang/expr v1.16.9 10 | github.com/go-sql-driver/mysql v1.7.0 11 | github.com/golang-jwt/jwt/v5 v5.2.1 12 | github.com/lib/pq v1.10.5 13 | github.com/mattn/go-sqlite3 v1.14.22 14 | github.com/smartystreets/goconvey v1.6.4 15 | ) 16 | 17 | require ( 18 | github.com/beorn7/perks v1.0.1 // indirect 19 | github.com/bitly/go-simplejson v0.5.0 // indirect 20 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 21 | github.com/golang/protobuf v1.5.3 // indirect 22 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect 23 | github.com/gorilla/websocket v1.5.3 // indirect 24 | github.com/hashicorp/golang-lru v0.5.4 // indirect 25 | github.com/json-iterator/go v1.1.12 // indirect 26 | github.com/jtolds/gls v4.20.0+incompatible // indirect 27 | github.com/kr/text v0.2.0 // indirect 28 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect 29 | github.com/mitchellh/mapstructure v1.5.0 // indirect 30 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 31 | github.com/modern-go/reflect2 v1.0.2 // indirect 32 | github.com/pkg/errors v0.9.1 // indirect 33 | github.com/prometheus/client_golang v1.15.1 // indirect 34 | github.com/prometheus/client_model v0.3.0 // indirect 35 | github.com/prometheus/common v0.42.0 // indirect 36 | github.com/prometheus/procfs v0.9.0 // indirect 37 | github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect 38 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect 39 | golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect 40 | golang.org/x/net v0.7.0 // indirect 41 | golang.org/x/sys v0.6.0 // indirect 42 | golang.org/x/text v0.7.0 // indirect 43 | google.golang.org/protobuf v1.30.0 // indirect 44 | gopkg.in/yaml.v3 v3.0.1 // indirect 45 | ) 46 | -------------------------------------------------------------------------------- /image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/image.jpg -------------------------------------------------------------------------------- /img/en/coins.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/coins.jpg -------------------------------------------------------------------------------- /img/en/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/config.png -------------------------------------------------------------------------------- /img/en/custom_type1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/custom_type1.png -------------------------------------------------------------------------------- /img/en/custom_type2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/custom_type2.png -------------------------------------------------------------------------------- /img/en/dinding_future1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/dinding_future1.jpg -------------------------------------------------------------------------------- /img/en/dingding_listen1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/dingding_listen1.jpg -------------------------------------------------------------------------------- /img/en/fundingrate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/fundingrate.jpg -------------------------------------------------------------------------------- /img/en/fundingrate_history.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/fundingrate_history.jpg -------------------------------------------------------------------------------- /img/en/futures_notice.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/futures_notice.jpg -------------------------------------------------------------------------------- /img/en/listen_chat_kc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/listen_chat_kc.jpg -------------------------------------------------------------------------------- /img/en/listen_futures.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/listen_futures.jpg -------------------------------------------------------------------------------- /img/en/listen_slack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/listen_slack.jpg -------------------------------------------------------------------------------- /img/en/listen_spot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/listen_spot.jpg -------------------------------------------------------------------------------- /img/en/order.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/order.jpg -------------------------------------------------------------------------------- /img/en/rush.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/rush.jpg -------------------------------------------------------------------------------- /img/en/spot_notice.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/spot_notice.jpg -------------------------------------------------------------------------------- /img/en/strategy_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/strategy_001.png -------------------------------------------------------------------------------- /img/en/strategy_002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/strategy_002.png -------------------------------------------------------------------------------- /img/en/te_001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/te_001.jpg -------------------------------------------------------------------------------- /img/en/te_002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/te_002.jpg -------------------------------------------------------------------------------- /img/en/te_003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/en/te_003.jpg -------------------------------------------------------------------------------- /img/zh/coins.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/coins.jpg -------------------------------------------------------------------------------- /img/zh/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/config.png -------------------------------------------------------------------------------- /img/zh/custom_type1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/custom_type1.png -------------------------------------------------------------------------------- /img/zh/custom_type2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/custom_type2.png -------------------------------------------------------------------------------- /img/zh/dingding.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/dingding.jpeg -------------------------------------------------------------------------------- /img/zh/dingding_future1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/dingding_future1.jpg -------------------------------------------------------------------------------- /img/zh/dingding_listen1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/dingding_listen1.jpg -------------------------------------------------------------------------------- /img/zh/feature_notice.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/feature_notice.jpg -------------------------------------------------------------------------------- /img/zh/fundingrate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/fundingrate.jpg -------------------------------------------------------------------------------- /img/zh/fundingrate_history.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/fundingrate_history.jpg -------------------------------------------------------------------------------- /img/zh/listen_chat_kc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/listen_chat_kc.jpg -------------------------------------------------------------------------------- /img/zh/listen_dingding.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/listen_dingding.jpg -------------------------------------------------------------------------------- /img/zh/listen_futures.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/listen_futures.jpg -------------------------------------------------------------------------------- /img/zh/listen_slack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/listen_slack.jpg -------------------------------------------------------------------------------- /img/zh/listen_spot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/listen_spot.jpg -------------------------------------------------------------------------------- /img/zh/order.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/order.jpg -------------------------------------------------------------------------------- /img/zh/rush.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/rush.jpg -------------------------------------------------------------------------------- /img/zh/spot_notice.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/spot_notice.jpg -------------------------------------------------------------------------------- /img/zh/strategy_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/strategy_001.png -------------------------------------------------------------------------------- /img/zh/strategy_002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/strategy_002.png -------------------------------------------------------------------------------- /img/zh/te_001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/te_001.jpg -------------------------------------------------------------------------------- /img/zh/te_002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/te_002.jpg -------------------------------------------------------------------------------- /img/zh/te_003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/img/zh/te_003.jpg -------------------------------------------------------------------------------- /lang/config/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "futures": { 3 | "coin": "coin", 4 | "side": "side", 5 | "sub_type": "sub type", 6 | "buy": "buy", 7 | "buy_fail": "buy fail", 8 | "sell": "sell", 9 | "sell_fail": "sell fail", 10 | "open": "open", 11 | "close": "close", 12 | "close_long": "close long", 13 | "close_short": "close short", 14 | "position_side": "position side", 15 | "time": "time", 16 | "profit": "profit", 17 | "auto_order": "auto order", 18 | "yes": "yes", 19 | "no": "no", 20 | "change_percent": "change percent", 21 | "long": "long", 22 | "short": "short", 23 | "success": "success", 24 | "fail": "fail", 25 | "price": "price", 26 | "close_price": "close price", 27 | "quantity": "quantity", 28 | "leverage": "leverage", 29 | "status": "status", 30 | "error": "error", 31 | "remarks": "remarks", 32 | "strategy_name": "strategy name", 33 | "custom_strategy_test": "custom strategy test", 34 | 35 | "open_notice_title": "futures open notice", 36 | "close_notice_title": "futures close notice", 37 | "new_coin_rush_notice_title": "new futures rush notice", 38 | "notice_price_title": "futures notice price", 39 | "listen_kline_base_title": "futures kline listen", 40 | "listen_keltner_channels_title": "futures keltner channels listen", 41 | "listen_custom_title": "futures custom listen", 42 | "listen_funding_rate_title": "funding rate listen", 43 | "wind_of_change": "wind of change", 44 | "stop_loss": "stop loss", 45 | "target_profit": "target profit", 46 | "fast_up": "fast up", 47 | "fast_down": "fast down", 48 | "funding_rate": "funding rate", 49 | "profit_by_funding_rate": "profit by funding rate", 50 | "notice_position_convert_title": "futures position convert notice", 51 | "income_positive": "income positive", 52 | "income_negative": "income negative", 53 | 54 | "now_price": "now price", 55 | "stop_loss_price": "stop loss price", 56 | "target_half_profit_price": "half profit price", 57 | "target_all_profit_price": "all profit price", 58 | "desired_price": "desired price" 59 | }, 60 | "spot": { 61 | "coin": "coin", 62 | "side": "side", 63 | "sub_type": "sub type", 64 | "buy": "buy", 65 | "buy_fail": "buy fail", 66 | "sell": "sell", 67 | "sell_fail": "sell fail", 68 | "time": "time", 69 | "profit": "profit", 70 | "auto_order": "auto order", 71 | "yes": "yes", 72 | "no": "no", 73 | "change_percent": "change percent", 74 | "price": "price", 75 | "quantity": "quantity", 76 | "status": "status", 77 | "error": "error", 78 | "remarks": "remarks", 79 | "success": "success", 80 | "fail": "fail", 81 | 82 | "new_coin_rush_notice_title": "spot new coin rush notice", 83 | "notice_title": "spot notice", 84 | "notice_price_title": "spot notice price", 85 | "kline_listen_base_title": "spot kline listen", 86 | "fast_up": "fast up", 87 | "fast_down": "fast down", 88 | "notice_auto_order": "notice auto order", 89 | "new_coin_rush_buy": "new coin rush buy", 90 | "new_coin_rush_sell": "new coin rush sell" 91 | } 92 | } -------------------------------------------------------------------------------- /lang/config/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "futures": { 3 | "coin": "币种", 4 | "side": "类型", 5 | "sub_type": "子类型", 6 | "buy": "买入", 7 | "buy_fail": "买入失败", 8 | "sell": "卖出", 9 | "sell_fail": "卖出失败", 10 | "open": "开仓", 11 | "close": "平仓", 12 | "close_long": "平仓(多)", 13 | "close_short": "平仓(空)", 14 | "position_side": "持仓方向", 15 | "time": "时间", 16 | "profit": "盈亏", 17 | "auto_order": "自动下单", 18 | "yes": "是", 19 | "no": "否", 20 | "change_percent": "涨跌幅", 21 | "long": "做多", 22 | "short": "做空", 23 | "success": "成功", 24 | "fail": "失败", 25 | "price": "价格", 26 | "close_price": "平仓价格", 27 | "quantity": "数量", 28 | "leverage": "杠杆", 29 | "status": "状态", 30 | "error": "错误信息", 31 | "remarks": "备注", 32 | "strategy_name": "策略名称", 33 | "custom_strategy_test": "合约自定义策略测试", 34 | 35 | "open_notice_title": "开仓合约交易通知", 36 | "close_notice_title": "平仓合约交易通知", 37 | "new_coin_rush_notice_title": "新合约抢购通知", 38 | "notice_price_title": "合约通知价格", 39 | "listen_kline_base_title": "合约K线监控", 40 | "listen_keltner_channels_title": "合约肯纳特通道监控通知", 41 | "listen_custom_title": "合约自定义监听", 42 | "listen_funding_rate_title": "合约资金费率监控通知", 43 | "wind_of_change": "风险改变", 44 | "stop_loss": "止损", 45 | "target_profit": "止盈", 46 | "fast_up": "快速上涨", 47 | "fast_down": "快速下跌", 48 | "funding_rate": "资金费率", 49 | "profit_by_funding_rate": "吃资金费用", 50 | "notice_position_convert_title": "合约持仓收益转换通知", 51 | "income_positive": "收益转正", 52 | "income_negative": "收益转负", 53 | 54 | "now_price": "当前价格", 55 | "stop_loss_price": "止损价格", 56 | "target_half_profit_price": "半仓止盈价格", 57 | "target_all_profit_price": "全仓止盈价格", 58 | "desired_price": "期望止盈价格" 59 | }, 60 | "spot": { 61 | "coin": "币种", 62 | "side": "类型", 63 | "sub_type": "子类型", 64 | "buy": "买入", 65 | "buy_fail": "买入失败", 66 | "sell": "卖出", 67 | "sell_fail": "卖出失败", 68 | "time": "时间", 69 | "profit": "盈亏", 70 | "auto_order": "自动下单", 71 | "yes": "是", 72 | "no": "否", 73 | "change_percent": "涨跌幅", 74 | "price": "价格", 75 | "quantity": "数量", 76 | "status": "状态", 77 | "error": "错误信息", 78 | "remarks": "备注", 79 | "success": "成功", 80 | "fail": "失败", 81 | 82 | "new_coin_rush_notice_title": "新币交易通知", 83 | "notice_title": "现货交易通知", 84 | "notice_price_title": "现货通知价格", 85 | "kline_listen_base_title": "现货K线通监控", 86 | "fast_up": "快速上涨", 87 | "fast_down": "快速下跌", 88 | "notice_auto_order": "通知后自动下单", 89 | "new_coin_rush_buy": "新币抢购", 90 | "new_coin_rush_sell": "新币挖矿抢卖" 91 | } 92 | } -------------------------------------------------------------------------------- /lang/utils.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "os" 8 | "regexp" 9 | "strings" 10 | 11 | "github.com/beego/beego/v2/core/config" 12 | "github.com/beego/beego/v2/core/logs" 13 | ) 14 | 15 | var langTextMap, _ = ReadLangJsonFile("") // 全局加载 16 | 17 | func GetLanguage() string { 18 | var language, _ = config.String("language") // 通知语言 19 | return language 20 | } 21 | 22 | func ReadLangJsonFile(filePath string) (langTextMap map[string]interface{}, err error) { 23 | if filePath == "" { 24 | lang := GetLanguage() 25 | logs.Info("use lang:", lang) 26 | filePath = fmt.Sprintf("./lang/config/%s.json", lang) 27 | } 28 | jsonFile, err := os.Open(filePath) 29 | if err != nil { 30 | fmt.Println("Error opening file:", err) 31 | return langTextMap, err 32 | } 33 | defer jsonFile.Close() 34 | 35 | // 读取文件内容 36 | byteValue, _ := io.ReadAll(jsonFile) 37 | 38 | if err := json.Unmarshal(byteValue, &langTextMap); err != nil { 39 | fmt.Println("Error unmarshalling JSON:", err) 40 | return langTextMap, err 41 | } 42 | return langTextMap, nil 43 | } 44 | 45 | func Lang(key string) (text string) { 46 | defer func() { 47 | if r := recover(); r != nil { 48 | text = key 49 | } 50 | }() 51 | keys := strings.Split(key, ".") 52 | 53 | tempText := langTextMap 54 | for index, k := range keys { 55 | if tempText[k] == nil { 56 | return key 57 | } 58 | if index < len(keys) - 1 { 59 | tempText = tempText[k].(map[string]interface{}) 60 | } else { 61 | text = fmt.Sprintf("%s", tempText[k]) 62 | } 63 | } 64 | return text 65 | } 66 | 67 | func LangMatch(text string) string { 68 | result := regexp. 69 | MustCompile(`\{([^}]+)\}`). 70 | ReplaceAllStringFunc(text, func (match string) string { 71 | content := match[1 : len(match)-1] 72 | return Lang(content) 73 | }) 74 | return result 75 | } 76 | 77 | // 蛇形转驼峰,首字母大写 78 | func ToCamelCase(snakeStr string) string { 79 | components := strings.Split(snakeStr, "_") 80 | for i := 0; i < len(components); i++ { 81 | if components[i] != "" { 82 | components[i] = strings.ToUpper(components[i][:1]) + components[i][1:] 83 | } 84 | } 85 | return strings.Join(components, "") 86 | } 87 | 88 | -------------------------------------------------------------------------------- /middlewares/auth.go: -------------------------------------------------------------------------------- 1 | package middlewares 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | 8 | "github.com/beego/beego/v2/core/config" 9 | "github.com/beego/beego/v2/server/web/context" 10 | "github.com/golang-jwt/jwt/v5" 11 | ) 12 | 13 | var webIndex, _ = config.String("web::index") 14 | var secretKey, _ = config.String("web::secret_key") 15 | 16 | var excludeRoutes = []string{ 17 | "/login", 18 | "/pull", 19 | "/pm2-log", 20 | "/pm2-log2", 21 | "/" + webIndex, 22 | } 23 | 24 | func JwtMiddleware(ctx *context.Context) { 25 | request := ctx.Request 26 | path := ctx.Request.URL.Path 27 | 28 | // 跳过白名单 29 | for _, excludeRoute := range excludeRoutes { 30 | if match, _ := pathMatch(path, excludeRoute); match { 31 | return 32 | } 33 | } 34 | 35 | // 获取请求头中的JWT Token 36 | authHeader := request.Header.Get("Authorization") 37 | if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") { 38 | ctx.Redirect(http.StatusUnauthorized, "/" + webIndex + "/index.html") 39 | return 40 | } 41 | 42 | tokenString := authHeader[len("Bearer "):] 43 | 44 | // 解析并验证Token 45 | token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { 46 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 47 | return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) 48 | } 49 | return []byte(secretKey), nil 50 | }) 51 | 52 | if err != nil { 53 | ctx.Redirect(http.StatusUnauthorized, "/" + webIndex + "/index.html") 54 | return 55 | } 56 | 57 | // 验证通过,将claims放入上下文供后续处理器使用 58 | if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { 59 | ctx.Input.SetData("user", claims) 60 | } else { 61 | ctx.Redirect(http.StatusUnauthorized, "/" + webIndex + "/index.html") 62 | return 63 | } 64 | } 65 | 66 | func pathMatch(actualPath, pattern string) (bool, error) { 67 | if strings.HasSuffix(pattern, "/*") { 68 | prefix := pattern[:len(pattern)-2] 69 | return strings.HasPrefix(actualPath, prefix), nil 70 | } 71 | return actualPath == pattern, nil 72 | } -------------------------------------------------------------------------------- /middlewares/cors.go: -------------------------------------------------------------------------------- 1 | package middlewares 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/beego/beego/v2/server/web/context" 7 | ) 8 | 9 | 10 | func CorsMiddleware(ctx *context.Context) { 11 | request := ctx.Request 12 | ctx.ResponseWriter.Header().Set("Access-Control-Allow-Origin", "*") 13 | ctx.ResponseWriter.Header().Set("Access-Control-Allow-Methods", "*") 14 | ctx.ResponseWriter.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, X-HTTP-Method-Override") 15 | ctx.ResponseWriter.Header().Set("Access-Control-Max-Age", "3600") 16 | 17 | // // 允许 OPTIONS 方法 18 | if request.Method == http.MethodOptions { 19 | ctx.ResponseWriter.WriteHeader(http.StatusNoContent) 20 | return 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /models/delivery_symbols.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type DeliverySymbols struct { 4 | ID int64 `orm:"column(id)" json:"id"` 5 | Symbol string `orm:"column(symbol)" json:"symbol"` 6 | PercentChange float64 `orm:"column(percentChange)" json:"percentChange"` // 24小时涨跌幅 7 | Close string `orm:"column(close)" json:"close"` // 最新成交价格 8 | Open string `orm:"column(open)" json:"open"` // 24小时开盘价 9 | Low string `orm:"column(low)" json:"low"` // 24小时最低价 10 | High string `orm:"column(high)" json:"high"` // 24小时最高价 11 | Enable int `orm:"column(enable)" json:"enable"` // 是否开启 12 | UpdateTime int64 `orm:"column(updateTime)" json:"updateTime"` // 更新时间 13 | LastClose string `orm:"column(lastClose)" json:"lastClose"` // 上一次的最新成交价格 14 | LastUpdateTime int64 `orm:"column(lastUpdateTime)" json:"lastUpdateTime"` // 上一次的更新时间 15 | BaseVolume float64 `orm:"column(baseVolume)" json:"baseVolume"` // 24小时成交量 16 | QuoteVolume float64 `orm:"column(quoteVolume)" json:"quoteVolume"` // 24小时成交额 USDT 17 | CloseQty float64 `orm:"column(closeQty)" json:"closeQty"` // 最新成交价格上的成交量 18 | TradeCount float64 `orm:"column(tradeCount)" json:"tradeCount"` // 24小时交易数 19 | 20 | Leverage int64 `orm:"column(leverage)" json:"leverage"` // 合约倍数 21 | MarginType string `orm:"column(marginType)" json:"marginType"` // 杠杆类型 ISOLATED(逐仓), CROSSED(全仓) 22 | TickSize string `orm:"column(tickSize)" json:"tickSize"` // 交易金额精度 23 | StepSize string `orm:"column(stepSize)" json:"stepSize"` // 交易数量精度 24 | Usdt string `orm:"column(usdt)" json:"usdt"` // 交易金额 25 | Profit string `orm:"column(profit)" json:"profit"` // 盈利率 26 | Loss string `orm:"column(loss)" json:"loss"` // 损失率 27 | Technology string `orm:"column(technology);type(text)" json:"technology"` // 技术指标配置 json 28 | Strategy string `orm:"column(strategy);type(text)" json:"strategy"` // 策略 json 29 | StrategyType string `orm:"column(strategy_type)" json:"strategy_type"` // 策略类型 // global, line_x, custom 30 | Pin int64 `orm:"column(pin)" json:"pin"` // 置顶 31 | Sort int64 `orm:"column(sort)" json:"sort"` // 排序 32 | Type string `orm:"column(type)" json:"type"` // USDT, USDC 33 | } 34 | 35 | func (u *DeliverySymbols) TableName() string { 36 | return "delivery_symbols" 37 | } -------------------------------------------------------------------------------- /models/futures_orders.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type FuturesOrder struct { 4 | ID int64 `orm:"column(id)" json:"id"` 5 | Symbol string `orm:"column(symbol)" json:"symbol"` 6 | ClientOrderId string `orm:"column(client_order_id)" json:"client_order_id"` // 客户端自定义ID 7 | OrderId string `orm:"column(order_id)" json:"order_id"` // 订单ID 8 | Side string `orm:"column(side)" json:"side"` // BUY, SELL 9 | PositionSide string `orm:"column(position_side)" json:"position_side"` // 持仓方向 BOTH LONG SHORT 10 | Type string `orm:"column(type)" json:"type"` // 下单类型 LIMIT 限价单, MARKET 市价单, STOP 止盈止损单, STOP_MARKET 止损市价单, TAKE_PROFIT 止盈限价单, TAKE_PROFIT_MARKET 止盈市价单, TRAILING_STOP_MARKET 跟踪止损单 11 | Status string `orm:"column(status)" json:"status"` // 订单状态 NEW(新建订单), PARTIALLY_FILLED(部分成交), FILLED(全部成交), CANCELED(已撤销), REJECTED(订单被拒绝), EXPIRED(订单过期) 12 | Price string `orm:"column(price)" json:"price"` // 下单价格 13 | OrigQty string `orm:"column(orig_qty)" json:"orig_qty"` // 下单委托数量 14 | ExecutedQty string `orm:"column(executed_qty)" json:"executed_qty"` // 已成交数量 15 | AveragePrice string `orm:"column(average_price)" json:"average_price"` // 平均成交价 16 | StopPrice string `orm:"column(stop_price)" json:"stop_price"` // 条件订单触发价格,对追踪止损单无效 17 | CommissionAsset string `orm:"column(commission_asset)" json:"commission_asset"` // 手续费类型 USDT 18 | Commission string `orm:"column(commission)" json:"commission"` // 手续费 19 | RealizedPnL string `orm:"column(realized_pnl)" json:"realized_pnl"` // 已实现盈亏 20 | 21 | CreateTime int64 `orm:"column(createTime)" json:"createTime"` 22 | UpdateTime int64 `orm:"column(updateTime)" json:"updateTime"` 23 | } 24 | 25 | func (u *FuturesOrder) TableName() string { 26 | return "futures_orders" 27 | } -------------------------------------------------------------------------------- /models/futures_postions.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type FuturesPosition struct { 4 | ID int64 `orm:"column(id)" json:"id"` 5 | Symbol string `orm:"column(symbol)" json:"symbol"` 6 | Side string `orm:"column(side)" json:"side"` // 持仓方向, BOTH, LONG, SHORT 7 | Amount string `orm:"column(amount)" json:"amount"` // 持仓数量 8 | MarginType string `orm:"column(margin_type)" json:"margin_type"` // isolated, cross 9 | Leverage int64 `orm:"column(leverage)" json:"leverage"` // 合约倍数 10 | IsolatedWallet string `orm:"column(isolated_wallet)" json:"isolated_wallet"` // 逐仓钱包余额 11 | EntryPrice string `orm:"column(entry_price)" json:"entry_price"` // 开仓价格 12 | MarkPrice string `orm:"column(mark_price)" json:"mark_price"` // 标记价格 13 | UnrealizedProfit string `orm:"column(unrealized_profit)" json:"unrealized_profit"` // 未实现盈亏 14 | AccumulatedRealized string `orm:"column(accumulated_realized)" json:"accumulated_realized"` // (费前)累计实现损益 15 | MaintenanceMarginRequired string `orm:"column(maintenance_margin_required)" json:"maintenance_margin_required"` // 维持保证金 16 | CreateTime int64 `orm:"column(createTime)" json:"createTime"` 17 | UpdateTime int64 `orm:"column(updateTime)" json:"updateTime"` 18 | } 19 | 20 | func (u *FuturesPosition) TableName() string { 21 | return "futures_positions" 22 | } -------------------------------------------------------------------------------- /models/spot_symbols.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type SpotSymbols struct { 4 | ID int64 `orm:"column(id)" json:"id"` 5 | Symbol string `orm:"column(symbol)" json:"symbol"` 6 | PercentChange float64 `orm:"column(percentChange)" json:"percentChange"` // 24小时涨跌幅 7 | Close string `orm:"column(close)" json:"close"` // 最新成交价格 8 | Open string `orm:"column(open)" json:"open"` // 24小时开盘价 9 | Low string `orm:"column(low)" json:"low"` // 24小时最低价 10 | High string `orm:"column(high)" json:"high"` // 24小时最高价 11 | Enable int `orm:"column(enable)" json:"enable"` // 是否开启 12 | UpdateTime int64 `orm:"column(updateTime)" json:"updateTime"` // 更新时间 13 | LastClose string `orm:"column(lastClose)" json:"lastClose"` // 上一次的最新成交价格 14 | LastUpdateTime int64 `orm:"column(lastUpdateTime)" json:"lastUpdateTime"` // 上一次的更新时间 15 | BaseVolume float64 `orm:"column(baseVolume)" json:"baseVolume"` // 24小时成交量 16 | QuoteVolume float64 `orm:"column(quoteVolume)" json:"quoteVolume"` // 24小时成交额 USDT 17 | CloseQty float64 `orm:"column(closeQty)" json:"closeQty"` // 最新成交价格上的成交量 18 | TradeCount float64 `orm:"column(tradeCount)" json:"tradeCount"` // 24小时交易数 19 | 20 | TickSize string `orm:"column(tickSize)" json:"tickSize"` // 交易金额精度 21 | StepSize string `orm:"column(stepSize)" json:"stepSize"` // 交易数量精度 22 | Usdt string `orm:"column(usdt)" json:"usdt"` // 交易金额 23 | Profit string `orm:"column(profit)" json:"profit"` // 盈利率 24 | Loss string `orm:"column(loss)" json:"loss"` // 损失率 25 | Technology string `orm:"column(technology);type(text)" json:"technology"` // 技术指标配置 json 26 | Strategy string `orm:"column(strategy);type(text)" json:"strategy"` // 策略 json 27 | StrategyType string `orm:"column(strategy_type)" json:"strategy_type"` // 策略类型 // global, line_x, custom 28 | Pin int64 `orm:"column(pin)" json:"pin"` // 置顶 29 | Sort int64 `orm:"column(sort)" json:"sort"` // 排序 30 | Type string `orm:"column(type)" json:"type"` // USDT, USDC 31 | } 32 | 33 | func (u *SpotSymbols) TableName() string { 34 | return "spot_symbols" 35 | } -------------------------------------------------------------------------------- /notify/common.go: -------------------------------------------------------------------------------- 1 | package notify 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/beego/beego/v2/core/config" 7 | ) 8 | 9 | func nowTime() string { 10 | return time.Now().Format("2006-01-02 15:04:05") 11 | } 12 | 13 | func getStatusColor(status string) string { 14 | var color string 15 | switch status { 16 | case "success": 17 | color = "#008000" 18 | case "fail": 19 | color = "#FF0000" 20 | default: 21 | color = "#008000" 22 | } 23 | return color 24 | } 25 | 26 | func GetNotifyChannel() (pusher Pusher) { 27 | var notification_channel, _ = config.String("notification::channel") // 通知方式 28 | switch (notification_channel) { 29 | case "dingding": 30 | pusher = DingDing{} 31 | case "slack": 32 | pusher = Slack{} 33 | default: 34 | pusher = DingDing{} 35 | } 36 | return pusher 37 | } 38 | -------------------------------------------------------------------------------- /notify/notify_interface.go: -------------------------------------------------------------------------------- 1 | package notify 2 | 3 | type FuturesOrderParams struct { 4 | Title string 5 | Symbol string 6 | Side string // buy,sell 7 | PositionSide string // long, short 8 | Price float64 9 | Quantity float64 10 | Leverage float64 11 | Profit float64 12 | ChangePercent float64 13 | Remarks string 14 | Status string // success, fail 15 | Error string // 错误信息 16 | } 17 | 18 | type FuturesNoticeParams struct { 19 | Title string 20 | Symbol string 21 | Side string // buy,sell 22 | PositionSide string // long, short 23 | Price float64 24 | AutoOrder string 25 | Status string // success, fail 26 | Error string // 错误信息 27 | } 28 | 29 | type FuturesListenParams struct { 30 | Title string 31 | Symbol string 32 | Side string // buy,sell 33 | PositionSide string // long, short 34 | Price float64 35 | StrategyName string 36 | Remarks string 37 | ChangePercent float64 38 | FundingRate float64 39 | 40 | NowPrice float64 41 | StopLossPrice float64 42 | TargetHalfProfitPrice float64 43 | TargetAllProfitPrice float64 44 | DesiredPrice float64 45 | } 46 | 47 | type SpotOrderParams struct { 48 | Title string 49 | Symbol string 50 | Side string // buy,sell 51 | Price float64 52 | Quantity float64 53 | Profit float64 54 | ChangePercent float64 55 | Remarks string 56 | Status string // success, fail 57 | Error string // 错误信息 58 | } 59 | 60 | type SpotNoticeParams struct { 61 | Title string 62 | Symbol string 63 | Side string // buy,sell 64 | Price float64 65 | AutoOrder string 66 | } 67 | 68 | type SpotListenParams struct { 69 | Title string 70 | Symbol string 71 | Side string // buy,sell 72 | Price float64 73 | Remarks string 74 | ChangePercent float64 75 | FundingRate float64 76 | 77 | NowPrice float64 78 | StopLossPrice float64 79 | TargetHalfProfitPrice float64 80 | TargetAllProfitPrice float64 81 | DesiredPrice float64 82 | } 83 | 84 | type FuturesTestParams struct { 85 | Title string 86 | Symbol string 87 | Type string // open, close 88 | PositionSide string // long, short 89 | Price float64 90 | ClosePrice float64 91 | Quantity float64 92 | Leverage float64 93 | Profit float64 94 | StrategyName string 95 | Remarks string 96 | } 97 | 98 | type FuturesPositionConvertParams struct { 99 | Title string 100 | Symbol string 101 | Status string // profit, loss 102 | PositionSide string // long, short 103 | Leverage string 104 | Price string 105 | UnRealizedProfit string 106 | } 107 | 108 | type Pusher interface { 109 | TestPusher() 110 | FuturesCustomStrategyTest(params FuturesTestParams) 111 | FuturesPositionConvert(params FuturesPositionConvertParams) 112 | 113 | FuturesOpenOrder(params FuturesOrderParams) 114 | FuturesCloseOrder(params FuturesOrderParams) 115 | FuturesNotice(params FuturesNoticeParams) 116 | FuturesListenKlineBase(params FuturesListenParams) 117 | FuturesListenKlineKc(params FuturesListenParams) 118 | FuturesListenKlineCustom(params FuturesListenParams) 119 | FuturesListenFundingRate(params FuturesListenParams) 120 | 121 | SpotOrder(params SpotOrderParams) 122 | SpotNotice(params SpotNoticeParams) 123 | SpotListenKlineBase(params SpotListenParams) 124 | } -------------------------------------------------------------------------------- /rate/index.go: -------------------------------------------------------------------------------- 1 | package rate 2 | 3 | import ( 4 | "go_binance_futures/feature/api/binance" 5 | "go_binance_futures/models" 6 | "strconv" 7 | 8 | "github.com/beego/beego/v2/client/orm" 9 | "github.com/beego/beego/v2/core/logs" 10 | ) 11 | 12 | // 监听套利情况 13 | func ListenRateEat() { 14 | o := orm.NewOrm() 15 | var symbolList []models.EatRateSymbols 16 | query := o.QueryTable("eat_rate_symbols").Filter("enable", 1) 17 | _, err := query.OrderBy("ID").All(&symbolList) 18 | if err != nil { 19 | logs.Error("listRateEat error:", err.Error()) 20 | return 21 | } 22 | 23 | for _, symbols := range symbolList { 24 | futuresSymbol := symbols.FuturesSymbol 25 | oldProfit := symbols.Profit 26 | lastProfitTime := symbols.LastProfitTime 27 | incomes, err := binance.GetIncome(binance.IncomeParams{ 28 | Symbol: futuresSymbol, 29 | IncomeType: "FUNDING_FEE", // 资金费率 30 | StartTime: symbols.LastProfitTime, 31 | }) 32 | 33 | if err != nil { 34 | logs.Error("GetIncome error, symbol:", futuresSymbol) 35 | logs.Error("err:", err.Error()) 36 | continue 37 | } 38 | for _, income := range incomes { 39 | feeFloat, _ := strconv.ParseFloat(income.Income, 64) 40 | oldProfit += feeFloat 41 | lastProfitTime = income.Time 42 | } 43 | symbols.Profit = oldProfit 44 | symbols.LastProfitTime = lastProfitTime + 1000 * 60 * 60 // 往后加1小时 45 | o.Update(&symbols) // _ 是受影响的条数 46 | } 47 | } -------------------------------------------------------------------------------- /routers/router.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "go_binance_futures/controllers" 5 | 6 | "github.com/beego/beego/v2/server/web" 7 | ) 8 | 9 | func init() { 10 | web.Router("/login", &controllers.LoginController{}, "post:Post") // 登录 11 | 12 | web.Router("/service/config", &controllers.IndexController{}, "get:GetServiceConfig;put:EditServiceConfig") // 服务配置信息 13 | web.Router("/test-pusher", &controllers.IndexController{}, "post:TestPusher") // 测试推送 14 | 15 | web.Router("/features", &controllers.FeatureController{}, "get:Get;post:Post") // 列表查询和新增 16 | web.Router("/features/:id", &controllers.FeatureController{}, "delete:Delete;put:Edit") // 更新和删除 17 | web.Router("/features/enable/:flag", &controllers.FeatureController{}, "put:UpdateEnable") // 修改所有的合约交易对开启关闭 18 | web.Router("/features/batch", &controllers.FeatureController{}, "put:BatchEdit") // 修改所有的合约交易 19 | web.Router("/features/strategy-rule/test/:id", &controllers.FeatureController{}, "post:TestStrategyRule") // 测试策略规则 20 | 21 | web.Router("/test-strategy-results", &controllers.TestStrategyResultController{}, "get:Get;delete:DeleteAll") // 测试策略的下单和平仓 22 | web.Router("/test-strategy-results/:id", &controllers.TestStrategyResultController{}, "delete:Delete") // 删除某个测试策略结果 23 | web.Router("/test-strategy-results/test/:symbol", &controllers.TestStrategyResultController{}, "post:TestStrategyRule") // 测试策略结果的某个平仓测试是否符合条件 24 | 25 | web.Router("/spots", &controllers.SpotController{}, "get:Get;post:Post") // 列表查询和新增 26 | web.Router("/spots/:id", &controllers.SpotController{}, "delete:Delete;put:Edit") // 更新和删除 27 | web.Router("/spots/enable/:flag", &controllers.SpotController{}, "put:UpdateEnable") // 修改所有的合约交易对开启关闭 28 | web.Router("/spots/batch", &controllers.SpotController{}, "put:BatchEdit") // 修改所有的合约交易 29 | // web.Router("/spots/strategy-rule/test/:id", &controllers.SpotController{}, "post:TestStrategyRule") // 测试策略规则 30 | 31 | web.Router("/rush", &controllers.RushController{}, "get:Get;post:Post") // 列表查询和新增 32 | web.Router("/rush/:id", &controllers.RushController{}, "delete:Delete;put:Edit") // 更新和删除 33 | web.Router("/rush/enable/:flag", &controllers.RushController{}, "put:UpdateEnable") // 修改所有的交易对开启关闭 34 | 35 | web.Router("/notice/coin", &controllers.NoticeCoinController{}, "get:Get;post:Post") // 列表查询和新增 36 | web.Router("/notice/coin/:id", &controllers.NoticeCoinController{}, "delete:Delete;put:Edit") // 更新和删除 37 | web.Router("/notice/coin/enable/:flag", &controllers.NoticeCoinController{}, "put:UpdateEnable") // 修改所有的交易对开启关闭 38 | 39 | web.Router("/listen/coin", &controllers.ListenCoinController{}, "get:Get;post:Post") // 列表查询和新增 40 | web.Router("/listen/coin/:id", &controllers.ListenCoinController{}, "delete:Delete;put:Edit") // 更新和删除 41 | web.Router("/listen/coin/kc-chart/:id", &controllers.ListenCoinController{}, "get:GetKcLineChart") // kcChart 42 | web.Router("/listen/coin/enable/:flag", &controllers.ListenCoinController{}, "put:UpdateEnable") // 修改所有的交易对开启关闭 43 | web.Router("/listen/funding-rates", &controllers.ListenCoinController{}, "get:GetFundingRates") // 合约费率列表 44 | web.Router("/listen/funding-rate/history", &controllers.ListenCoinController{}, "get:GetFundingRateHistory") // 合约费率历史 45 | web.Router("/listen/strategy-rule/test/:id", &controllers.ListenCoinController{}, "post:TestStrategyRule") // 测试策略规则 46 | 47 | web.Router("/orders", &controllers.OrderController{}, "get:Get;delete:DeleteAll") // order list 和 删除所有 order 48 | web.Router("/orders/:id", &controllers.OrderController{}, "delete:Delete") // 删除某个订单 49 | web.Router("/config", &controllers.ConfigController{}, "get:Get;put:Edit") // config get and edit 50 | 51 | web.Router("/futures/account", &controllers.AccountController{}, "get:GetBinanceFuturesAccount") // 获取合约账户信息 52 | web.Router("/futures/positions", &controllers.AccountController{}, "get:GetBinanceFuturesPositions") // 获取合约持仓信息 53 | web.Router("/futures/open-orders", &controllers.AccountController{}, "get:GetBinanceFuturesOpenOrders") // 获取合约挂单信息 54 | web.Router("/futures/local/positions", &controllers.AccountController{}, "get:GetLocalFuturesPositions") // 获取本地存储的合约持仓信息 55 | web.Router("/futures/local/positions/:id", &controllers.AccountController{}, "put:EditLocalFuturesPositions;delete:DelLocalFuturesPositions") // 修复和删除本地存储的合约持仓信息 56 | web.Router("/futures/local/open-orders", &controllers.AccountController{}, "get:GetLocalFuturesOpenOrders") // 获取本地存储的挂单信息 57 | 58 | web.Router("/fund-rate/eat", &controllers.EatRateController{}, "get:Get;post:Post") // 列表查询和新增 59 | web.Router("/fund-rate/eat/:id", &controllers.EatRateController{}, "delete:Delete;put:Edit") // 更新和删除 60 | web.Router("/fund-rate/eat/start/:id", &controllers.EatRateController{}, "post:Start") // start 61 | web.Router("/fund-rate/eat/end/:id", &controllers.EatRateController{}, "post:End") // end 62 | 63 | web.Router("/strategy-templates", &controllers.StrategyTemplateController{}, "get:Get;post:Post") // 策略模板 64 | web.Router("/strategy-templates/:id", &controllers.StrategyTemplateController{}, "delete:Delete;put:Edit") // 策略模板更新 65 | web.Router("/strategy-templates/test/:symbol", &controllers.StrategyTemplateController{}, "post:TestStrategyRule") // 测试策略规则 66 | 67 | web.Router("/start", &controllers.CommandController{}, "post:Start") // start 68 | web.Router("/stop", &controllers.CommandController{}, "post:Stop") // stop 69 | web.Router("/pull", &controllers.CommandController{}, "post:GitPull") // git pull 70 | web.Router("/pm2-log", &controllers.CommandController{}, "get:Pm2Log") // pm2-log 71 | } 72 | 73 | -------------------------------------------------------------------------------- /spot/spot_util.go: -------------------------------------------------------------------------------- 1 | package spot 2 | 3 | import ( 4 | "go_binance_futures/spot/api/binance" 5 | 6 | "github.com/beego/beego/v2/core/logs" 7 | ) 8 | 9 | // 获取币的交易精度 10 | func GetCoinOrderSize(symbol string) (string, string) { 11 | tickSize := "0.0" 12 | stepSize := "0.0" 13 | res, err := binance.GetExchangeInfo() 14 | if err != nil { 15 | logs.Error("GetExchangeInfoError:", err) 16 | return tickSize, stepSize 17 | } 18 | for _, item := range res.Symbols { 19 | if item.Symbol == symbol { 20 | priceFilter := item.PriceFilter() 21 | if priceFilter != nil { 22 | tickSize = priceFilter.TickSize // 价格精度 23 | } 24 | lotSizeFilter := item.LotSizeFilter() 25 | if lotSizeFilter != nil { 26 | stepSize = lotSizeFilter.StepSize // 数量精度 27 | } 28 | return tickSize, stepSize 29 | } 30 | } 31 | return tickSize, stepSize 32 | } 33 | -------------------------------------------------------------------------------- /static/css/chunk-0b00d329.0729eea3.css: -------------------------------------------------------------------------------- 1 | .code-full .CodeMirror.cm-s-monokai.CodeMirror-wrap{min-height:600px}.CodeMirror-hints{z-index:9999} -------------------------------------------------------------------------------- /static/css/chunk-0fa4606c.55664688.css: -------------------------------------------------------------------------------- 1 | .pagination-container[data-v-62e794ce]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-62e794ce]{display:none}.filter-item[data-v-f6a57230]{margin-right:10px}.symbol-close-click[data-v-f6a57230]{color:red;font-size:15px}.symbol-open[data-v-f6a57230]{color:green} -------------------------------------------------------------------------------- /static/css/chunk-18e00042.53be0b48.css: -------------------------------------------------------------------------------- 1 | .app-container .CodeMirror{border:1px solid #1890ff!important;height:calc(100vh - 140px)!important} -------------------------------------------------------------------------------- /static/css/chunk-241e90b9.fcc2b018.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-dialog{position:absolute;left:0;right:0;background:inherit;z-index:15;padding:.1em .8em;overflow:hidden;color:inherit}.CodeMirror-dialog-top{border-bottom:1px solid #eee;top:0}.CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0}.CodeMirror-dialog input{border:none;outline:none;background:transparent;width:20em;color:inherit;font-family:monospace}.CodeMirror-dialog button{font-size:70%}.CodeMirror-hints{position:absolute;z-index:10;overflow:hidden;list-style:none;margin:0;padding:2px;-webkit-box-shadow:2px 3px 5px rgba(0,0,0,.2);box-shadow:2px 3px 5px rgba(0,0,0,.2);border-radius:3px;border:1px solid silver;background:#fff;font-size:90%;font-family:monospace;max-height:20em;overflow-y:auto}.CodeMirror-hint{margin:0;padding:0 4px;border-radius:2px;white-space:pre;color:#000;cursor:pointer}li.CodeMirror-hint-active{background:#08f;color:#fff} -------------------------------------------------------------------------------- /static/css/chunk-267b3368.7b9e4106.css: -------------------------------------------------------------------------------- 1 | .social-signup-container[data-v-7309fbbb]{margin:20px 0}.social-signup-container .sign-btn[data-v-7309fbbb]{display:inline-block;cursor:pointer}.social-signup-container .icon[data-v-7309fbbb]{color:#fff;font-size:24px;margin-top:8px}.social-signup-container .qq-svg-container[data-v-7309fbbb],.social-signup-container .wx-svg-container[data-v-7309fbbb]{display:inline-block;width:40px;height:40px;line-height:40px;text-align:center;padding-top:1px;border-radius:4px;margin-bottom:20px;margin-right:5px}.social-signup-container .wx-svg-container[data-v-7309fbbb]{background-color:#24da70}.social-signup-container .qq-svg-container[data-v-7309fbbb]{background-color:#6ba2d6;margin-left:50px}@supports(-webkit-mask:none) and (not (cater-color:#fff)){.login-container .el-input input{color:#fff}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#fff;height:47px;caret-color:#fff}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #283443 inset!important;box-shadow:inset 0 0 0 1000px #283443!important;-webkit-text-fill-color:#fff!important}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container[data-v-18b40b00]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.login-container .login-form[data-v-18b40b00]{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-18b40b00]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-18b40b00]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-18b40b00]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-18b40b00]{position:relative}.login-container .title-container .title[data-v-18b40b00]{font-size:26px;color:#eee;margin:0 auto 40px auto;text-align:center;font-weight:700}.login-container .title-container .set-language[data-v-18b40b00]{color:#fff;position:absolute;top:3px;font-size:18px;right:0;cursor:pointer}.login-container .show-pwd[data-v-18b40b00]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.login-container .thirdparty-button[data-v-18b40b00]{position:absolute;right:0;bottom:6px}@media only screen and (max-width:470px){.login-container .thirdparty-button[data-v-18b40b00]{display:none}} -------------------------------------------------------------------------------- /static/css/chunk-3b6cce88.13a7e89e.css: -------------------------------------------------------------------------------- 1 | .wscn-http404-container[data-v-26fcd89f]{-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);position:absolute;top:40%;left:50%}.wscn-http404[data-v-26fcd89f]{position:relative;width:1200px;padding:0 50px;overflow:hidden}.wscn-http404 .pic-404[data-v-26fcd89f]{position:relative;float:left;width:600px;overflow:hidden}.wscn-http404 .pic-404__parent[data-v-26fcd89f]{width:100%}.wscn-http404 .pic-404__child[data-v-26fcd89f]{position:absolute}.wscn-http404 .pic-404__child.left[data-v-26fcd89f]{width:80px;top:17px;left:220px;opacity:0;-webkit-animation-name:cloudLeft-data-v-26fcd89f;animation-name:cloudLeft-data-v-26fcd89f;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1s;animation-delay:1s}.wscn-http404 .pic-404__child.mid[data-v-26fcd89f]{width:46px;top:10px;left:420px;opacity:0;-webkit-animation-name:cloudMid-data-v-26fcd89f;animation-name:cloudMid-data-v-26fcd89f;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1.2s;animation-delay:1.2s}.wscn-http404 .pic-404__child.right[data-v-26fcd89f]{width:62px;top:100px;left:500px;opacity:0;-webkit-animation-name:cloudRight-data-v-26fcd89f;animation-name:cloudRight-data-v-26fcd89f;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1s;animation-delay:1s}@-webkit-keyframes cloudLeft-data-v-26fcd89f{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@keyframes cloudLeft-data-v-26fcd89f{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@-webkit-keyframes cloudMid-data-v-26fcd89f{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@keyframes cloudMid-data-v-26fcd89f{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@-webkit-keyframes cloudRight-data-v-26fcd89f{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}@keyframes cloudRight-data-v-26fcd89f{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}.wscn-http404 .bullshit[data-v-26fcd89f]{position:relative;float:left;width:300px;padding:30px 0;overflow:hidden}.wscn-http404 .bullshit__oops[data-v-26fcd89f]{font-size:32px;line-height:40px;color:#1482f0;margin-bottom:20px;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__headline[data-v-26fcd89f],.wscn-http404 .bullshit__oops[data-v-26fcd89f]{font-weight:700;opacity:0;-webkit-animation-name:slideUp-data-v-26fcd89f;animation-name:slideUp-data-v-26fcd89f;-webkit-animation-duration:.5s;animation-duration:.5s}.wscn-http404 .bullshit__headline[data-v-26fcd89f]{font-size:20px;line-height:24px;color:#222;margin-bottom:10px;-webkit-animation-delay:.1s;animation-delay:.1s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-26fcd89f]{font-size:13px;line-height:21px;color:grey;margin-bottom:30px;-webkit-animation-delay:.2s;animation-delay:.2s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-26fcd89f],.wscn-http404 .bullshit__return-home[data-v-26fcd89f]{opacity:0;-webkit-animation-name:slideUp-data-v-26fcd89f;animation-name:slideUp-data-v-26fcd89f;-webkit-animation-duration:.5s;animation-duration:.5s}.wscn-http404 .bullshit__return-home[data-v-26fcd89f]{display:block;float:left;width:110px;height:36px;background:#1482f0;border-radius:100px;text-align:center;color:#fff;font-size:14px;line-height:36px;cursor:pointer;-webkit-animation-delay:.3s;animation-delay:.3s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@-webkit-keyframes slideUp-data-v-26fcd89f{0%{-webkit-transform:translateY(60px);transform:translateY(60px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes slideUp-data-v-26fcd89f{0%{-webkit-transform:translateY(60px);transform:translateY(60px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}} -------------------------------------------------------------------------------- /static/css/chunk-5150fc2a.0729eea3.css: -------------------------------------------------------------------------------- 1 | .code-full .CodeMirror.cm-s-monokai.CodeMirror-wrap{min-height:600px}.CodeMirror-hints{z-index:9999} -------------------------------------------------------------------------------- /static/css/chunk-689b6e77.a034e0e4.css: -------------------------------------------------------------------------------- 1 | .pagination-container[data-v-62e794ce]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-62e794ce]{display:none}.filter-item[data-v-7b611cde]{margin-right:10px}.info-table-expand[data-v-7b611cde]{font-size:0}.info-table-expand label[data-v-7b611cde]{width:90px;color:#99a9bf}.info-table-expand .el-form-item[data-v-7b611cde]{margin-right:0;margin-bottom:0;width:100%} -------------------------------------------------------------------------------- /static/css/chunk-6d7794e0.329dbe9f.css: -------------------------------------------------------------------------------- 1 | .pagination-container[data-v-62e794ce]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-62e794ce]{display:none}.code-full .CodeMirror.cm-s-monokai.CodeMirror-wrap{min-height:600px}.CodeMirror-hints{z-index:9999} -------------------------------------------------------------------------------- /static/css/chunk-73c4c992.098e3fbb.css: -------------------------------------------------------------------------------- 1 | .dashboard-container[data-v-eae11942]{margin-left:20px}.dashboard-text[data-v-eae11942]{margin-bottom:5px;font-size:14px;line-height:20px}.red[data-v-eae11942]{color:red}.green[data-v-eae11942]{color:green} -------------------------------------------------------------------------------- /static/css/chunk-937da73a.329dbe9f.css: -------------------------------------------------------------------------------- 1 | .pagination-container[data-v-62e794ce]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-62e794ce]{display:none}.code-full .CodeMirror.cm-s-monokai.CodeMirror-wrap{min-height:600px}.CodeMirror-hints{z-index:9999} -------------------------------------------------------------------------------- /static/css/chunk-d696a108.b1dd7a76.css: -------------------------------------------------------------------------------- 1 | .errPage-container[data-v-35ca77fc]{width:800px;max-width:100%;margin:100px auto}.errPage-container .pan-back-btn[data-v-35ca77fc]{background:#008489;color:#fff;border:none!important}.errPage-container .pan-gif[data-v-35ca77fc]{margin:0 auto;display:block}.errPage-container .pan-img[data-v-35ca77fc]{display:block;margin:0 auto;width:100%}.errPage-container .text-jumbo[data-v-35ca77fc]{font-size:60px;font-weight:700;color:#484848}.errPage-container .list-unstyled[data-v-35ca77fc]{font-size:14px}.errPage-container .list-unstyled li[data-v-35ca77fc]{padding-bottom:5px}.errPage-container .list-unstyled a[data-v-35ca77fc]{color:#008489;text-decoration:none}.errPage-container .list-unstyled a[data-v-35ca77fc]:hover{text-decoration:underline} -------------------------------------------------------------------------------- /static/css/chunk-libs.3dfb7769.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:inherit;font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}[hidden],template{display:none}#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;-webkit-box-shadow:0 0 10px #29d,0 0 5px #29d;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;-webkit-transform:rotate(3deg) translateY(-4px);transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;-webkit-box-sizing:border-box;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}@keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}} -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/static/favicon.ico -------------------------------------------------------------------------------- /static/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/static/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /static/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/static/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /static/img/401.089007e7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/static/img/401.089007e7.gif -------------------------------------------------------------------------------- /static/img/404.a57b6f31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/static/img/404.a57b6f31.png -------------------------------------------------------------------------------- /static/img/404_cloud.0f4bc32b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorry510/go_binance_futures/8bdf4f7a61cf8e8ea060434e02d794c7311d1695/static/img/404_cloud.0f4bc32b.png -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 币安量化交易
-------------------------------------------------------------------------------- /static/js/chunk-267b3368.26ed6cec.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-267b3368"],{4514:function(t,e,n){"use strict";n("6d38")},"6d38":function(t,e,n){},"95dc":function(t,e,n){},"9ed6":function(t,e,n){"use strict";n.r(e);var s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"login-container"},[n("el-form",{ref:"loginForm",staticClass:"login-form",attrs:{model:t.loginForm,rules:t.loginRules,autocomplete:"on","label-position":"left"}},[n("div",{staticClass:"title-container"},[n("h3",{staticClass:"title"},[t._v(" "+t._s(t.$t("login.title"))+" ")]),n("lang-select",{staticClass:"set-language"})],1),n("el-form-item",{attrs:{prop:"username"}},[n("span",{staticClass:"svg-container"},[n("svg-icon",{attrs:{"icon-class":"user"}})],1),n("el-input",{ref:"username",attrs:{placeholder:t.$t("login.username"),name:"username",type:"text",tabindex:"1",autocomplete:"on"},model:{value:t.loginForm.username,callback:function(e){t.$set(t.loginForm,"username",e)},expression:"loginForm.username"}})],1),n("el-tooltip",{attrs:{content:"Caps lock is On",placement:"right",manual:""},model:{value:t.capsTooltip,callback:function(e){t.capsTooltip=e},expression:"capsTooltip"}},[n("el-form-item",{attrs:{prop:"password"}},[n("span",{staticClass:"svg-container"},[n("svg-icon",{attrs:{"icon-class":"password"}})],1),n("el-input",{key:t.passwordType,ref:"password",attrs:{type:t.passwordType,placeholder:t.$t("login.password"),name:"password",tabindex:"2",autocomplete:"on"},on:{blur:function(e){t.capsTooltip=!1}},nativeOn:{keyup:[function(e){return t.checkCapslock(e)},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:t.handleLogin(e)}]},model:{value:t.loginForm.password,callback:function(e){t.$set(t.loginForm,"password",e)},expression:"loginForm.password"}}),n("span",{staticClass:"show-pwd",on:{click:t.showPwd}},[n("svg-icon",{attrs:{"icon-class":"password"===t.passwordType?"eye":"eye-open"}})],1)],1)],1),n("el-button",{staticStyle:{width:"100%","margin-bottom":"30px"},attrs:{loading:t.loading,type:"primary"},nativeOn:{click:function(e){return e.preventDefault(),t.handleLogin(e)}}},[t._v(" "+t._s(t.$t("login.logIn"))+" ")])],1),n("el-dialog",{attrs:{title:t.$t("login.thirdparty"),visible:t.showDialog},on:{"update:visible":function(e){t.showDialog=e}}},[t._v(" "+t._s(t.$t("login.thirdpartyTips"))+" "),n("br"),n("br"),n("br"),n("social-sign")],1)],1)},o=[],i=(n("8b03"),n("5227"),n("90c8"),n("1131")),a=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"social-signup-container"},[n("div",{staticClass:"sign-btn",on:{click:function(e){return t.wechatHandleClick("wechat")}}},[n("span",{staticClass:"wx-svg-container"},[n("svg-icon",{staticClass:"icon",attrs:{"icon-class":"wechat"}})],1),t._v(" WeChat ")]),n("div",{staticClass:"sign-btn",on:{click:function(e){return t.tencentHandleClick("tencent")}}},[n("span",{staticClass:"qq-svg-container"},[n("svg-icon",{staticClass:"icon",attrs:{"icon-class":"qq"}})],1),t._v(" QQ ")])])},r=[],c={name:"SocialSignin",methods:{wechatHandleClick:function(t){alert("ok")},tencentHandleClick:function(t){alert("ok")}}},l=c,u=(n("aa05"),n("9bf6")),d=Object(u["a"])(l,a,r,!1,null,"7309fbbb",null),p=d.exports,g={name:"Login",components:{LangSelect:i["a"],SocialSign:p},data:function(){var t=function(t,e,n){e.length<1?n(new Error("Please enter the correct user name")):n()},e=function(t,e,n){e.length<2?n(new Error("The password can not be less than 2 digits")):n()};return{loginForm:{username:"",password:""},loginRules:{username:[{required:!0,trigger:"blur",validator:t}],password:[{required:!0,trigger:"blur",validator:e}]},passwordType:"password",capsTooltip:!1,loading:!1,showDialog:!1,redirect:void 0,otherQuery:{}}},watch:{$route:{handler:function(t){var e=t.query;e&&(this.redirect=e.redirect,this.otherQuery=this.getOtherQuery(e))},immediate:!0}},created:function(){},mounted:function(){""===this.loginForm.username?this.$refs.username.focus():""===this.loginForm.password&&this.$refs.password.focus()},destroyed:function(){},methods:{checkCapslock:function(t){var e=t.key;this.capsTooltip=e&&1===e.length&&e>="A"&&e<="Z"},showPwd:function(){var t=this;"password"===this.passwordType?this.passwordType="":this.passwordType="password",this.$nextTick((function(){t.$refs.password.focus()}))},handleLogin:function(){var t=this;this.$refs.loginForm.validate((function(e){if(!e)return console.log("error submit!!"),!1;t.loading=!0,t.$store.dispatch("user/login",t.loginForm).then((function(){console.log("direct",t.redirect),t.$router.push({path:t.redirect||"/",query:t.otherQuery}),t.loading=!1})).catch((function(){t.loading=!1}))}))},getOtherQuery:function(t){return Object.keys(t).reduce((function(e,n){return"redirect"!==n&&(e[n]=t[n]),e}),{})}}},f=g,h=(n("f7d9"),n("4514"),Object(u["a"])(f,s,o,!1,null,"18b40b00",null));e["default"]=h.exports},aa05:function(t,e,n){"use strict";n("e441")},e441:function(t,e,n){},f7d9:function(t,e,n){"use strict";n("95dc")}}]); -------------------------------------------------------------------------------- /static/js/chunk-2d2105d3.f51ab7bf.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d2105d3"],{b829:function(n,e,o){"use strict";o.r(e);o("7019"),o("8d8a"),o("1a71");var a,t,c={name:"AuthRedirect",created:function(){var n=window.location.search.slice(1);window.localStorage&&(window.localStorage.setItem("x-admin-oauth-code",n),window.close())},render:function(n){return n()}},d=c,i=o("9bf6"),l=Object(i["a"])(d,a,t,!1,null,null,null);e["default"]=l.exports}}]); -------------------------------------------------------------------------------- /static/js/chunk-2d230fe7.1a45108d.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d230fe7"],{ef3c:function(e,r,n){"use strict";n.r(r);n("8d8a"),n("9d08");var t,u,a={created:function(){var e=this.$route,r=e.params,n=e.query,t=r.path;this.$router.replace({path:"/"+t,query:n})},render:function(e){return e()}},c=a,o=n("9bf6"),p=Object(o["a"])(c,t,u,!1,null,null,null);r["default"]=p.exports}}]); -------------------------------------------------------------------------------- /static/js/chunk-3b6cce88.0bf5b908.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-3b6cce88"],{"1db4":function(t,s,a){"use strict";a.r(s);var e=function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"wscn-http404-container"},[a("div",{staticClass:"wscn-http404"},[t._m(0),a("div",{staticClass:"bullshit"},[a("div",{staticClass:"bullshit__oops"},[t._v("OOPS!")]),t._m(1),a("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),a("div",{staticClass:"bullshit__info"},[t._v("Please check that the URL you entered is correct, or click the button below to return to the homepage.")]),a("a",{staticClass:"bullshit__return-home",attrs:{href:""}},[t._v("Back to home")])])])])},c=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"pic-404"},[e("img",{staticClass:"pic-404__parent",attrs:{src:a("a36b"),alt:"404"}}),e("img",{staticClass:"pic-404__child left",attrs:{src:a("26fc"),alt:"404"}}),e("img",{staticClass:"pic-404__child mid",attrs:{src:a("26fc"),alt:"404"}}),e("img",{staticClass:"pic-404__child right",attrs:{src:a("26fc"),alt:"404"}})])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"bullshit__info"},[t._v("All rights reserved "),a("a",{staticStyle:{color:"#20a0ff"},attrs:{href:"https://wallstreetcn.com",target:"_blank"}},[t._v("wallstreetcn")])])}],i={name:"Page404",computed:{message:function(){return"The webmaster said that you can not enter this page..."}}},l=i,n=(a("207f"),a("9bf6")),r=Object(n["a"])(l,e,c,!1,null,"26fcd89f",null);s["default"]=r.exports},"207f":function(t,s,a){"use strict";a("dd35")},"26fc":function(t,s,a){t.exports=a.p+"static/img/404_cloud.0f4bc32b.png"},a36b:function(t,s,a){t.exports=a.p+"static/img/404.a57b6f31.png"},dd35:function(t,s,a){}}]); -------------------------------------------------------------------------------- /static/js/chunk-d696a108.0e3e7205.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-d696a108"],{"24e2":function(t,a,i){"use strict";i.r(a);var s=function(){var t=this,a=t.$createElement,i=t._self._c||a;return i("div",{staticClass:"errPage-container"},[i("el-button",{staticClass:"pan-back-btn",attrs:{icon:"el-icon-arrow-left"},on:{click:t.back}},[t._v(" 返回 ")]),i("el-row",[i("el-col",{attrs:{span:12}},[i("h1",{staticClass:"text-jumbo text-ginormous"},[t._v(" Oops! ")]),t._v(" gif来源"),i("a",{attrs:{href:"https://zh.airbnb.com/",target:"_blank"}},[t._v("airbnb")]),t._v(" 页面 "),i("h2",[t._v("你没有权限去该页面")]),i("h6",[t._v("如有不满请联系你领导")]),i("ul",{staticClass:"list-unstyled"},[i("li",[t._v("或者你可以去:")]),i("li",{staticClass:"link-type"},[i("router-link",{attrs:{to:"/dashboard"}},[t._v(" 回首页 ")])],1),i("li",{staticClass:"link-type"},[i("a",{attrs:{href:"https://www.taobao.com/"}},[t._v("随便看看")])]),i("li",[i("a",{attrs:{href:"#"},on:{click:function(a){a.preventDefault(),t.dialogVisible=!0}}},[t._v("点我看图")])])])]),i("el-col",{attrs:{span:12}},[i("img",{attrs:{src:t.errGif,width:"313",height:"428",alt:"Girl has dropped her ice cream."}})])],1),i("el-dialog",{attrs:{visible:t.dialogVisible,title:"随便看"},on:{"update:visible":function(a){t.dialogVisible=a}}},[i("img",{staticClass:"pan-img",attrs:{src:t.ewizardClap}})])],1)},e=[],r=i("cc6c"),l=i.n(r),n={name:"Page401",data:function(){return{errGif:l.a+"?"+ +new Date,ewizardClap:"https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646",dialogVisible:!1}},methods:{back:function(){this.$route.query.noGoBack?this.$router.push({path:"/dashboard"}):this.$router.go(-1)}}},c=n,o=(i("fa66"),i("9bf6")),u=Object(o["a"])(c,s,e,!1,null,"35ca77fc",null);a["default"]=u.exports},cc6c:function(t,a,i){t.exports=i.p+"static/img/401.089007e7.gif"},f1a1:function(t,a,i){},fa66:function(t,a,i){"use strict";i("f1a1")}}]); -------------------------------------------------------------------------------- /technology/define.go: -------------------------------------------------------------------------------- 1 | package technology 2 | 3 | type IndicatorConfig struct { 4 | Name string `json:"name"` // 指标名称 5 | Enable bool `json:"enable"` // 是否启用 6 | KlineInterval string `json:"kline_interval"` // K线周期 7 | Period int `json:"period"` // 周期 8 | Multiplier float64 `json:"multiplier,omitempty"` // 可选字段 9 | StdDevMultiplier float64 `json:"std_dev_multiplier,omitempty"` // 可选字段 10 | } 11 | 12 | // 顶层技术指标配置结构 13 | type TechnologyConfig struct { 14 | MA []IndicatorConfig `json:"ma"` // 简单移动平均线 15 | EMA []IndicatorConfig `json:"ema"` // 指数移动平均线 16 | RSI []IndicatorConfig `json:"rsi"` // 相对强弱指数 17 | KC []IndicatorConfig `json:"kc"` // 肯特纳通道 18 | BOLL []IndicatorConfig `json:"boll"` // 布林带 19 | ATR []IndicatorConfig `json:"atr"` // 平均真实波幅 20 | } 21 | 22 | type StrategyConfig [] struct { 23 | Name string `json:"name"` // 策略名称 24 | Enable bool `json:"enable"` // 是否启用 25 | Code string `json:"code"` // 自定义规则的表达式 26 | Type string `json:"type"` // long,short,close_long,close_short 27 | } -------------------------------------------------------------------------------- /tests/default_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | "runtime" 8 | "path/filepath" 9 | 10 | "github.com/beego/beego/v2/core/logs" 11 | 12 | _ "go_binance_futures/routers" 13 | 14 | beego "github.com/beego/beego/v2/server/web" 15 | . "github.com/smartystreets/goconvey/convey" 16 | ) 17 | 18 | func init() { 19 | _, file, _, _ := runtime.Caller(0) 20 | apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator)))) 21 | beego.TestBeegoInit(apppath) 22 | } 23 | 24 | 25 | // TestBeego is a sample to run an endpoint test 26 | func TestBeego(t *testing.T) { 27 | r, _ := http.NewRequest("GET", "/", nil) 28 | w := httptest.NewRecorder() 29 | beego.BeeApp.Handlers.ServeHTTP(w, r) 30 | 31 | logs.Trace("testing", "TestBeego", "Code[%d]\n%s", w.Code, w.Body.String()) 32 | 33 | Convey("Subject: Test Station Endpoint\n", t, func() { 34 | Convey("Status Code Should Be 200", func() { 35 | So(w.Code, ShouldEqual, 200) 36 | }) 37 | Convey("The Result Should Not Be Empty", func() { 38 | So(w.Body.Len(), ShouldBeGreaterThan, 0) 39 | }) 40 | }) 41 | } 42 | 43 | -------------------------------------------------------------------------------- /types/futures_define.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type FuturesPosition struct { 4 | Symbol string `orm:"column(symbol)" json:"symbol"` 5 | Side string `orm:"column(side)" json:"side"` // 持仓方向, BOTH, LONG, SHORT 6 | Amount string `orm:"column(amount)" json:"amount"` // 持仓数量 7 | MarginType string `orm:"column(margin_type)" json:"margin_type"` // isolated, cross 8 | Leverage int64 `orm:"column(leverage)" json:"leverage"` // 合约倍数 9 | IsolatedWallet string `orm:"column(isolated_wallet)" json:"isolated_wallet"` // 逐仓钱包余额 10 | EntryPrice string `orm:"column(entry_price)" json:"entry_price"` // 开仓价格 11 | MarkPrice string `orm:"column(mark_price)" json:"mark_price"` // 标记价格 12 | UnrealizedProfit string `orm:"column(unrealized_profit)" json:"unrealized_profit"` // 未实现盈亏 13 | SourceType string `orm:"column(source_type)" json:"source_type"` // 数据来源, 0:api, 1:local 14 | CreateTime int64 `orm:"column(create_time)" json:"create_time"` // 创建时间 15 | } 16 | 17 | // 自定义策略中的持仓信息 18 | type FuturesPositionCode struct { 19 | Symbol string `orm:"column(symbol)" json:"symbol"` 20 | Side string `orm:"column(side)" json:"side"` // 持仓方向, BOTH, LONG, SHORT 21 | Amount float64 `orm:"column(amount)" json:"amount"` // 持仓数量 22 | Leverage int64 `orm:"column(leverage)" json:"leverage"` // 合约倍数 23 | EntryPrice float64 `orm:"column(entry_price)" json:"entry_price"` // 开仓价格 24 | MarkPrice float64 `orm:"column(mark_price)" json:"mark_price"` // 标记价格 25 | UnrealizedProfit float64 `orm:"column(unrealized_profit)" json:"unrealized_profit"` // 未实现盈亏 26 | Mock bool `orm:"column(mock)" json:"mock"` // 是否是模拟持仓 27 | CreateTime int64 `orm:"column(create_time)" json:"create_time"` // 创建时间 28 | SourceType string `orm:"column(source_type)" json:"source_type"` // 数据来源, 0:api, 1:local 29 | } 30 | 31 | type FuturesOrder struct { 32 | Symbol string `orm:"column(symbol)" json:"symbol"` 33 | ClientOrderId string `orm:"column(client_order_id)" json:"client_order_id"` // 客户端自定义ID 34 | OrderId int64 `orm:"column(order_id)" json:"order_id"` // 订单ID 35 | Side string `orm:"column(side)" json:"side"` // BUY, SELL 36 | PositionSide string `orm:"column(position_side)" json:"position_side"` // 持仓方向 BOTH LONG SHORT 37 | Type string `orm:"column(type)" json:"type"` // 下单类型 LIMIT 限价单, MARKET 市价单, STOP 止盈止损单, STOP_MARKET 止损市价单, TAKE_PROFIT 止盈限价单, TAKE_PROFIT_MARKET 止盈市价单, TRAILING_STOP_MARKET 跟踪止损单 38 | Status string `orm:"column(status)" json:"status"` // 订单状态 NEW(新建订单), PARTIALLY_FILLED(部分成交), FILLED(全部成交), CANCELED(已撤销), REJECTED(订单被拒绝), EXPIRED(订单过期) 39 | Price string `orm:"column(price)" json:"price"` // 下单价格 40 | OrigQty string `orm:"column(orig_qty)" json:"orig_qty"` // 下单委托数量 41 | ExecutedQty string `orm:"column(executed_qty)" json:"executed_qty"` // 已成交数量 42 | UpdateTime int64 `orm:"column(updateTime)" json:"updateTime"` 43 | } -------------------------------------------------------------------------------- /utils/config.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "go_binance_futures/models" 5 | 6 | "github.com/beego/beego/v2/client/orm" 7 | ) 8 | 9 | func GetSystemConfig() (systemConfig models.Config, err error) { 10 | o := orm.NewOrm() 11 | err = o.QueryTable("config").Filter("Id", 1).One(&systemConfig) 12 | if err != nil { 13 | return systemConfig, err 14 | } 15 | return systemConfig, nil 16 | } 17 | 18 | -------------------------------------------------------------------------------- /utils/index.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/beego/beego/v2/core/config" 10 | ) 11 | 12 | var driver, _ = config.String("database::driver") 13 | 14 | func ResJson(code int, data map[string]interface{}, msg ...string) interface{} { 15 | var message string 16 | if (code == 200) { 17 | message = "success" 18 | } 19 | if len(msg) != 0 { 20 | message = msg[0] 21 | } 22 | res := map[string]interface{} { 23 | "code": code, 24 | "msg": message, 25 | "data": data, 26 | } 27 | return res 28 | } 29 | 30 | 31 | func ToJson(data interface{}) string { 32 | b, err := json.Marshal(data) 33 | if err != nil { 34 | return "" 35 | } 36 | return string(b) 37 | } 38 | 39 | // 根据 tickSize 或 stepSize 获取精度 40 | // e.i: (number "0.323443", size "0.01") => 0.32 41 | func GetTradePrecision(number_float64 float64, size string) float64 { 42 | number_string := fmt.Sprintf("%."+strconv.Itoa(GetPow(size))+"f", number_float64) 43 | number_float64_ok, _ := strconv.ParseFloat(number_string, 64) 44 | return number_float64_ok 45 | } 46 | 47 | // >=1 的都等于0 48 | // <1 0.001 => 3 49 | func GetPow(lotsize string) int { 50 | if strings.Index(lotsize, "1") == 0 { 51 | return 0 52 | } 53 | return -(1 - strings.Index(lotsize, "1")) 54 | } 55 | 56 | // 计算逻辑: ma(5)=(收盘价1+收盘价2+收盘价3+收盘价4+收盘价5)/5 57 | func MaN(closePrice []float64, n int) float64 { 58 | allPrice := 0.0 59 | for i := 0; i < n; i++ { 60 | allPrice += closePrice[i] 61 | } 62 | return allPrice / float64(n) 63 | } 64 | 65 | 66 | // ma(n) 的 count 条数据 67 | // @param 收盘价 klineClose 68 | // @param ma(n) n 69 | // @param count 条数数据 70 | func MaNList(closePrice []float64, n int, count int) ([]float64) { 71 | maNList := make([]float64, count) 72 | for i := 0; i < count; i++ { 73 | maNList[i] = MaN(closePrice[i:], n) 74 | } 75 | return maNList 76 | } 77 | 78 | // 是否是一个降序数组 79 | func IsDesc(arr []float64) bool { 80 | for i := 1; i < len(arr); i++ { 81 | if arr[i-1] <= arr[i] { 82 | return false 83 | } 84 | } 85 | return true 86 | } 87 | 88 | // 是否是一个升序数组 89 | func IsAsc(arr []float64) bool { 90 | for i := 1; i < len(arr); i++ { 91 | if arr[i-1] >= arr[i] { 92 | return false 93 | } 94 | } 95 | return true 96 | } 97 | 98 | // 反转数组 99 | func ReverseArray(arr []float64) []float64 { 100 | n := len(arr) 101 | reversed := make([]float64, n) 102 | 103 | left, right := 0, n-1 104 | for left < right { 105 | reversed[left], reversed[right] = arr[right], arr[left] 106 | left++ 107 | right-- 108 | } 109 | 110 | return reversed 111 | } 112 | 113 | func Intervals() []string { 114 | return []string{"1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "8h", "12h", "1d", "3d", "1w", "1M"} 115 | } 116 | 117 | func EscapeJSON(jsonStr string) string { 118 | if driver == "mysql" { 119 | return strings.ReplaceAll(jsonStr, "\\n", "\\\\n") 120 | } 121 | return jsonStr 122 | } -------------------------------------------------------------------------------- /utils/jwt.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | 7 | "github.com/beego/beego/v2/core/config" 8 | "github.com/golang-jwt/jwt/v5" 9 | ) 10 | 11 | // secretKey 是用于签署JWT的密钥 12 | var secretKey, _ = config.String("web::secret_key") 13 | var key = []byte(secretKey) 14 | 15 | func GenerateToken(username string, expires int) (string, error) { 16 | var t = jwt.NewWithClaims(jwt.SigningMethodHS256, 17 | jwt.MapClaims{ 18 | "Username": username, 19 | "jti": strconv.FormatInt(time.Now().UnixNano(), 10), 20 | "iat": time.Now().Unix(), 21 | "exp": time.Now().Add(time.Hour * time.Duration(expires)).Unix(), // 设置JWT的有效期为30天 22 | }) 23 | signedToken, err := t.SignedString(key) 24 | if err != nil { 25 | return "", err 26 | } 27 | return signedToken, nil 28 | } 29 | 30 | --------------------------------------------------------------------------------