免费公开一个简易版的对冲策略,其中包含了实现高级功能的下单接口,为新手提供一些开发思路。
有开发OKX交易接口能力的看这里:
OKX没有节点返佣时下单tag参数填写 4944196c4337SUDE 可获得40%经纪商返佣到你自己账户(策略默认行为)不使用我提供的交易模板也一样自动返佣到你自己账户。
OKX已有节点返佣时经纪商额外返佣比例为12%,也是全自动返到你自己的账户。
没有FMZ平台账号的看这里;
注册返红包链接 https://www.fmz.com/sign-up/6203996
高级下单功能模板函数
支持功能:
1市价下单:币安 okx gate 现货与合约
2高级选项下单: okx gate 币安 bitget Bybit mexc CoinEx kucoin
3修改订单: okx,币安,CoinEx,Bybit,gate
1:创建订单
$.creatOrder( e , instId , side , type , price , amount , timeInForce , reduceOnly , isModif , sleepMode )
参数说明:
e 交易所对象
instId 交易对名称
side 下单方向 ‘buy’ 或 ‘sell’
type 下单类型 limit 限价单 market 市价单
price 下单价格
amount 下单数量
timeInForce 高级选项
GTC - Good Till Cancel 成交为止
IOC - Immediate or Cancel 无法立即成交(吃单)的部分就撤销
FOK - Fill or Kill 无法全部立即成交就撤销
GTX - Good Till Crossing 无法成为挂单方就撤销
reduceOnly bool 是否是只减仓合约可用
isModif bool 是否为修改订单
sleepMode bool 当超过下单频率是否等待
调用成功后生成交易订单信息然后执行下单函数
2:执行下单 \(.batchOrders( e , orderBox , rMode , ws , tdMode ) 参数说明: e 交易所对象, orderBox 数组 就是 \).creatOrder 生成的订单信息列表,支持批量下单 rMode 可填 ‘go’ 就是异步下单之后获取结果 否则是阻塞下单获取结果 ws 当用websocket链接下单时传入ws对象 tdMode okx账户专用参数,根据账户类型 现货一般使用 ‘cash’ 合约使用 ‘cross’
3:查看执行结果 function handleWaitList() 具体使用方法可以查看本策略里的代码
策略使用说明: 创建机器人: 1:添加交易所时注意选择交易对,策略下单交易为配置的交易对。 目前支持 币安 OKX gate 现货与合约交易所之间的对冲。 2:点击按钮 做多匹配 或 做空匹配 开始确定做多与做空的交易所账户在匹配信息里可以看到匹配状态 3:点击对冲开仓或平仓,将对进行匹配成功的交易对同时执行买入与卖出操作实现对冲。
有问题的可以在电报群找我 https://t.me/FMZ_makebit
var exKeyList = []
var exInfo = []
// index : index,
// key : key,
// exname : exname,
// quote : quote,
// label : label,
// currency : currency,
// balance : {},
// positon : {},
// ticker : {},
// contractType : ".swap",
// waitResultsList : [],
// roomId : null,
var configInfo = {}
var tradeRoom = []
var roomId = 0
const MarginLevel = 20
const red = "#FF0000"
const green = "#238E23"
const orange = "#FF7F00"
const blue = "#0000ff"
var okxAcctLv = ''
var tdMode = "cash"
function initExInfo(){
//将每个交易所
var index = 0
exchanges.forEach( function( e ){
var exname = e.GetName()
if( exname == 'OKEX' || exname == 'Futures_OKCoin' ){
var ret = e.IO("api", "GET", "/api/v5/account/config")
//1:简单交易模式,2:单币种保证金模式 ,3:跨币种保证金模式 ,4:组合保证金模式
if( ret ){
okxAcctLv = ret.data[0].acctLv
if( okxAcctLv == 3 || exname == 'Futures_OKCoin'){
tdMode = "cross"
}
if( okxAcctLv == 4 ){throw exname+"不支持组合保证金模式"}
}else{throw "Okx获取账户配置信息失败"}
}
if(typeof(configInfo[exname]) == 'undefined' ){
configInfo[exname] = {}
}
configInfo[exname] = e.GetMarkets()
var quote = e.GetQuoteCurrency()
var label = e.GetLabel()
var isFutures = exname.indexOf("Futures_") == 0
var key = exname+'@'+quote+'@'+label
if( exKeyList.indexOf(key) == -1 ){
exKeyList.push(key)
}else{
Log("重复的交易所对象请删除",key)
return
}
if( isFutures ){
e.SetContractType("swap")
//e.SetMarginLevel(MarginLevel)
}
configInfo[exname]
var currency = e.GetCurrency()
var info = {
index : index,
key : key,
exname : exname,
quote : quote,
label : label,
currency : currency,
balance : {},
positon : {},
ticker : {},
contractType : "",
waitResultsList : [],
roomId : null,
}
if( isFutures ){
info.contractType = ".swap",
info.positon[currency] = {
Amount : 0,
FrozenAmount : 0,
Price : 0,
Type : 0,
Profit : 0,
}
}
exInfo.push(info)
index++
})
}
function getConfig( info ){
var key = info.currency+ info.contractType
if( !configInfo[info.exname][key].MinNotional ){
configInfo[info.exname][key].MinNotional = 5
}
if( !configInfo[info.exname][key].CtVal ){
configInfo[info.exname][key].CtVal = 1
}
if( info.exname == 'GateIO' && !configInfo[info.exname][key].MinQty){
configInfo[info.exname][key].MinQty = Number(configInfo[info.exname][key].Info.min_base_amount)
}
return configInfo[info.exname][key]
}
function updateBalance(){
exInfo.forEach( function( item ){
var e = exchanges[item.index]
var Assets = e.GetAssets()
Assets.forEach( function( coin ){
if( typeof(item.balance[coin.Currency]) == 'undefined' ){
item.balance[coin.Currency] = {
free : 0,
locked : 0,
}
}
item.balance[coin.Currency].free = coin.Amount
item.balance[coin.Currency].locked = coin.FrozenAmount
})
})
}
function updatePos(){
exInfo.forEach( function( item ){
var e = exchanges[item.index]
var isFutures = item.exname.indexOf("Futures_") == 0
if( isFutures ){
var positon = e.GetPositions("USDT.swap")
if( positon ){
for( cur in item.positon ){
item.positon[cur] = {
Amount : 0,
FrozenAmount : 0,
Price : 0,
Type : 0,
Profit : 0,
}
}
}
positon.forEach( function( pos ){
if( typeof(item.positon[item.currency]) == 'undefined' ){
item.positon[item.currency] = {
Amount : 0,
FrozenAmount : 0,
Price : 0,
Type : 0,
Profit : 0,
}
}
item.positon[item.currency].Amount = pos.Type == PD_SHORT ? -1*pos.Amount : pos.Amount
item.positon[item.currency].FrozenAmount = pos.FrozenAmount
item.positon[item.currency].Price = pos.Price
item.positon[item.currency].Type = pos.Type
item.positon[item.currency].Profit = pos.Profit
})
}
})
}
function updateTicker(){
exInfo.forEach( function( item ){
var e = exchanges[item.index]
var ticker = e.GetTicker()
item.ticker = ticker
})
}
function getTableInfo(){
var logstatus = ''
var table1 = {
type: "table",
title:"账号信息汇总",
scroll : "auto",
cols: ["index","key","当前交易对","可用资金(U)","可用币","买一价","卖一价","持仓价格","持仓数量","浮动盈亏","持仓方向","最小开仓金额","匹配状态","买入","卖出","做多匹配","做空匹配","取消匹配"],
rows: []
}
exInfo.forEach( function( info ){
var buy1 = 0
var sell1 = 0
var price = 0
var amount = 0
var profit = 0
var free = 0
var stock = 0
var type = '/'
var 匹配状态 = info.roomId > 0 ? "已匹配"+ info.roomId + green : "未匹配" + orange
var config = getConfig(info)
var 最小开仓金额 = 0
if( info.ticker ){
最小开仓金额 = _N(Math.max(config.MinNotional,config.MinQty*config.CtVal*info.ticker.Last),2)
buy1 = info.ticker.Sell
sell1 = info.ticker.Buy
}
if( info.positon[info.currency] ){
price = _N(info.positon[info.currency].Price,config.PricePrecision)
amount = info.positon[info.currency].Amount
profit = info.positon[info.currency].Profit
if( amount != 0 ){
type = info.positon[info.currency].Type == PD_LONG ? "多":"空"
}
}
if( info.balance[info.quote] ){
free = _N(info.balance[info.quote].free,2)
}
if( info.balance[config.BaseAsset]){
stock = _N(info.balance[config.BaseAsset].free,config.AmountPrecision)
}
var button1 = {
'type': 'button',
'cmd': info.key + "|buyMatch",
'name': '做多匹配' ,
"class": "btn btn-xs btn-primary"
}
var button2 = {
'type': 'button',
'cmd': info.key + "|sellMatch",
'name': '做空匹配' ,
"class": "btn btn-xs btn-warning"
}
var button3 = {
'type': 'button',
'cmd': info.key + "|cancelMatch",
'name': '取消匹配' ,
"class": "btn btn-xs btn-danger"
}
var button7 = {
'type': 'button',
'cmd': info.key + "|buyPos",
'name': '买入' ,
"class": "btn btn-xs btn-primary"
}
var button8 = {
'type': 'button',
'cmd': info.key + "|sellPos",
'name': '卖出' ,
"class": "btn btn-xs btn-warning"
}
table1.rows.push([
info.index,
info.key,
info.currency,
free,
stock,
buy1,
sell1,
price,
amount,
profit,
type,
最小开仓金额,
匹配状态,
button7,
button8,
button1,
button2,
button3,
])
})
var table2 = {
type: "table",
title:"匹配信息",
scroll : "auto",
cols: ["roomId","做多方","做空方","匹配状态","开仓价差率","对冲开仓","对冲平仓","删除匹配"],
rows: []
}
tradeRoom.forEach( function( room ){
var buyExInfo = null
var sellExInfo = null
if( room.buyKey ){
buyExInfo = getInfoByKey(room.buyKey)
}
if( room.sellKey ){
sellExInfo = getInfoByKey(room.sellKey)
}
var button4 = {
'type': 'button',
'cmd': room.roomId + "|open",
'name': '对冲开仓' ,
"class": "btn btn-xs btn-primary"
}
var button5 = {
'type': 'button',
'cmd': room.roomId + "|close",
'name': '对冲平仓' ,
"class": "btn btn-xs btn-warning"
}
var button6 = {
'type': 'button',
'cmd': room.roomId + "|delRoom",
'name': '删除匹配' ,
"class": "btn btn-xs btn-danger"
}
var 价差比 = '/'
if( room.state == 1 && buyExInfo && buyExInfo.ticker && sellExInfo && sellExInfo.ticker ){
// var buyConfig = getConfig(buyExInfo)
// var sellConfig = getConfig(sellExInfo)
价差比 = (sellExInfo.ticker.Buy - buyExInfo.ticker.Sell)/buyExInfo.ticker.Sell
价差比 = _N( 价差比 * 100 , 3 ) + '%'
}
table2.rows.push( [
room.roomId,
buyExInfo ? buyExInfo.key : '待匹配',
sellExInfo ? sellExInfo.key : '待匹配',
room.state == 1 ? "匹配成功" + green : "匹配中" + orange,
价差比,
button4,
button5,
button6,
] )
})
logstatus = (
'`' + JSON.stringify([table1]) + '`' + '\n'+
'`' + JSON.stringify([table2]) + '`' + '\n')
return logstatus
}
function getMaxOrderAmout( info , side , reduceOnly ){
var config = getConfig(info)
//子线程
var maxAmount = 0
var isFutures = info.exname.indexOf("Futures_") == 0
var freeBalance = info.balance[info.quote].free
var freeStock = 0
if( typeof(info.balance[config.BaseAsset]) != 'undefined'){
freeStock = info.balance[config.BaseAsset].free
}
//计算最大可下单量
if( isFutures ){ //有深度信息应该就是支持交易的
var amount = info.positon[info.currency].Amount
var 原仓位可平数 = 0
var 保证金可开仓数 = 0
if( (side == 'buy' && amount < 0) || (side == 'sell' && amount > 0)){
原仓位可平数 = Math.abs( amount )
}
if( !reduceOnly ){
保证金可开仓数 = (freeBalance)*MarginLevel*0.99/info.ticker.Last
保证金可开仓数 = 保证金可开仓数/config.CtVal
}
//maxBuyAmount 是合约下单量,1000SATS这种
maxAmount = Math.min( 原仓位可平数 + 保证金可开仓数 , tradeQuoteLimit/info.ticker.Last/config.CtVal )
}else{
if( side == 'buy' ){
maxAmount = Math.min( freeBalance*0.99 , tradeQuoteLimit)/info.ticker.Last/config.CtVal
}else{
maxAmount = Math.min( freeStock , tradeQuoteLimit/info.ticker.Last/config.CtVal)
}
}
maxAmount = _N(maxAmount,config.AmountPrecision)
return maxAmount
}
function tradeSingle( key , side ){
var reduceOnly = false
var info = getInfoByKey(key)
if( !info ){
Log("未找到info",key)
return
}
var isFutures = info.exname.indexOf("Futures_") == 0
if( isFutures ){ //有深度信息应该就是支持交易的
var amount = info.positon[info.currency].Amount
if( (side == 'buy' && amount < 0) || (side == 'sell' && amount > 0)){
reduceOnly = true
}
}
var maxAmount = getMaxOrderAmout( info , side , reduceOnly )
Log( info.key,"side",side,"maxAmount",maxAmount)
creatOrder( info , side , info.ticker.Last , maxAmount , reduceOnly )
}
function tradePoll( roomId , reduceOnly ){
Log( "roomId:" , roomId , "reduceOnly:" , reduceOnly )
var roomInfo = getRoomInfoById( roomId )
if( !roomInfo ){
Log("未找到room信息",roomInfo)
return
}else if( roomInfo.state != 1 ){
Log( roomInfo.roomId,"匹配未完成")
return
}
//分别找到多空两边的信息
var longExInfo = getInfoByKey(roomInfo.buyKey)
var shortExInfo = getInfoByKey(roomInfo.sellKey)
if( !longExInfo || !shortExInfo ){
Log(longExInfo,"多空双方信息不齐全",shortExInfo)
return
}
var buyIsFutures = false
var sellIsFutures = false
var buyConfig = null
var sellConfig = null
var maxBuyAmount = 0
var maxSellAmount = 0
var realBuyAmount = 0
var realSellAmount = 0
if( reduceOnly ){
buyIsFutures = shortExInfo.exname.indexOf( 'Futures_' ) == 0
sellIsFutures = longExInfo.exname.indexOf( 'Futures_' ) == 0
buyConfig = getConfig(shortExInfo)
sellConfig = getConfig(longExInfo)
maxBuyAmount = getMaxOrderAmout( shortExInfo , 'buy' , reduceOnly )
maxSellAmount = getMaxOrderAmout( longExInfo , 'sell' , reduceOnly )
realBuyAmount = maxBuyAmount*buyConfig.CtVal
realSellAmount = maxSellAmount*sellConfig.CtVal
}else{
buyIsFutures = longExInfo.exname.indexOf( 'Futures_' ) == 0
sellIsFutures = shortExInfo.exname.indexOf( 'Futures_' ) == 0
buyConfig = getConfig(longExInfo)
sellConfig = getConfig(shortExInfo)
maxBuyAmount = getMaxOrderAmout( longExInfo , 'buy' , reduceOnly )
maxSellAmount = getMaxOrderAmout( shortExInfo , 'sell' , reduceOnly )
realBuyAmount = maxBuyAmount*buyConfig.CtVal
realSellAmount = maxSellAmount*sellConfig.CtVal
}
//精度处理
var fixAmout = Math.min( realBuyAmount , realSellAmount )
if( (buyIsFutures && sellIsFutures) || (!buyIsFutures && !sellIsFutures)){
fixAmout = Math.min(_N( fixAmout/buyConfig.CtVal , buyConfig.AmountPrecision )*buyConfig.CtVal , _N( fixAmout/sellConfig.CtVal , sellConfig.AmountPrecision )*sellConfig.CtVal )
realBuyAmount = _N( fixAmout/buyConfig.CtVal , buyConfig.AmountPrecision )
realSellAmount = _N( fixAmout/sellConfig.CtVal , sellConfig.AmountPrecision )
}else if(buyIsFutures){
realBuyAmount = _N( fixAmout/buyConfig.CtVal , buyConfig.AmountPrecision )
//现货精度以修正后的合约的精度为准
realSellAmount = realBuyAmount*buyConfig.CtVal
realSellAmount = _N( realSellAmount/sellConfig.CtVal , sellConfig.AmountPrecision )
}else if( sellIsFutures ){
realSellAmount = _N( fixAmout/sellConfig.CtVal , sellConfig.AmountPrecision )
realBuyAmount = realSellAmount*sellConfig.CtVal
realBuyAmount = _N( realBuyAmount/buyConfig.CtVal , buyConfig.AmountPrecision )
}
if( realBuyAmount > 0 && realSellAmount > 0 ){
if( realBuyAmount < buyConfig.MinQty ||
realSellAmount < sellConfig.MinQty ){
Log(realBuyAmount,'<',buyConfig.MinQty,"不满足最小下单量要求")
Log(realSellAmount,'<',sellConfig.MinQty,"不满足最小下单量要求")
return
}
if( (!buyIsFutures || !reduceOnly) &&
realBuyAmount * buyConfig.CtVal * longExInfo.ticker.Last < buyConfig.MinNotional ){
Log( realBuyAmount * buyConfig.CtVal * longExInfo.ticker.Last,"不满足最小下单金额要求",buyConfig.MinNotional)
return
}
if( (!sellIsFutures || !reduceOnly) &&
realSellAmount * sellConfig.CtVal * shortExInfo.ticker.Last < sellConfig.MinNotional ){
Log( realSellAmount * sellConfig.CtVal * shortExInfo.ticker.Last,"不满足最小下单金额要求",sellConfig.MinNotional)
return
}
if( reduceOnly ){
creatOrder( shortExInfo , 'buy' , shortExInfo.ticker.Last , realBuyAmount , reduceOnly )
creatOrder( longExInfo , 'sell' , longExInfo.ticker.Last , realSellAmount , reduceOnly )
}else{
creatOrder( longExInfo , 'buy' , longExInfo.ticker.Last , realBuyAmount , reduceOnly )
creatOrder( shortExInfo , 'sell' , shortExInfo.ticker.Last , realSellAmount , reduceOnly )
}
}
}
//创建订单
function creatOrder( info , side , price , amount , reduceOnly ){
var orderList = []
var e = exchanges[info.index]
var config = getConfig(info)
let order = $.creatOrder( e , config.Symbol , side , 'MARKET' , price , amount , 'ioc' , reduceOnly , false )
if( order ){
orderList.push( order )
}else{
Log(info.key,"生成订单失败")
return
}
batchOrders( info , orderList , 'go' )
}
//下单
function batchOrders( info , orderList , rmode ){
var ret = []
if( orderList.length <= 0 ){
return ret
}
var e = exchanges[info.index]
ret = $.batchOrders( e , orderList , rmode , null , tdMode )
if( ret ){
ret.forEach( function(event){
info.waitResultsList.push( event )
})
}
return ret
}
//处理订单结果
function handleWaitList(){
exInfo.forEach( function(info){
var newResultList = []
info.waitResultsList.forEach( function(event){
if( event.obj == null ){
Log( info.key , "wait对象创建失败" )
}else if (typeof (event.obj.wait) != "undefined") {
let result = event.obj.wait(1);
if (typeof (result) != "undefined") {
if( event.name == 'batchOrders' ){
if( result ){
try {result = JSON.parse(result)} catch (error) {}
Log( event.exname,"下单结果result",result)
}else{
Log( result,event.exname,event,"下单失败",red)
}
}
}else{
newResultList.push( event )
}
}
})
info.waitResultsList = newResultList
})
}
function getInfoByKey( key ){
var info = null
for( var i = 0 ; i < exInfo.length ; i++ ){
if( key == exInfo[i].key ){
info = exInfo[i]
return info
}
}
}
function getRoomInfoById( roomId ){
var room = null
for( var i = 0 ; i < tradeRoom.length ; i++ ){
if( roomId == tradeRoom[i].roomId ){
room = tradeRoom[i]
return room
}
}
}
function roomMatch( key , side ){
//首先扫描是否有匹配
var info = getInfoByKey( key )
if(!info){
Log("未找到指定账户信息key",key)
return
}
//扫描tradeRoom
var roomInfo = null
for(var i = 0 ; i < tradeRoom.length ; i++ ){
if(tradeRoom[i].buyKey == key || tradeRoom[i].sellKey == key ){
roomInfo = tradeRoom[i]
break
}
}
if( side == 'buyMatch'){
if( roomInfo && (roomInfo.buyKey == key || roomInfo.sellKey == key)){
Log(key,"已经匹配过了")
}else{
for(var i = 0 ; i < tradeRoom.length ; i++ ){
if(tradeRoom[i].buyKey == null ){
tradeRoom[i].buyKey = key
info.roomId = tradeRoom[i].roomId
if( tradeRoom[i].sellKey ){
tradeRoom[i].state = 1
}
return
}
}
creatRoom( key , null )
}
}else if( side == 'sellMatch' ){
if( roomInfo && (roomInfo.sellKey == key || roomInfo.buyKey == key)){
Log(key,"已经匹配过了")
}else{
for(var i = 0 ; i < tradeRoom.length ; i++ ){
if(tradeRoom[i].sellKey == null ){
tradeRoom[i].sellKey = key
info.roomId = tradeRoom[i].roomId
if( tradeRoom[i].buyKey ){
tradeRoom[i].state = 1
}
return
}
}
creatRoom( null , key )
}
}else if( side == 'cancelMatch' ){
Log("取消匹配",key)
var exInfo = getInfoByKey(key)
exInfo.roomId = null
var newRoomInfo = []
tradeRoom.forEach( function(room){
if( room.buyKey == key ){
room.buyKey = null
room.state = 0
}else if( room.sellKey == key ){
room.sellKey = null
room.state = 0
}
if( room.buyKey || room.sellKey ){
newRoomInfo.push(room)
}
})
tradeRoom = newRoomInfo
}
}
function creatRoom( buyKey , sellKey ){
roomId++
var roomInfo = {
roomId : roomId,
buyKey : buyKey,
sellKey : sellKey,
state : 0 // 0 等待匹配 1 匹配完成
}
if( buyKey ){
var exInfo = getInfoByKey(buyKey)
exInfo.roomId = roomId
}
if( sellKey ){
var exInfo = getInfoByKey(sellKey)
exInfo.roomId = roomId
}
if( buyKey && sellKey ){
roomInfo.state = 1
}
tradeRoom.push( roomInfo )
}
function delRoom( roomId ){
var index = null
for( var i = 0 ; i < tradeRoom.length ; i++ ){
if( tradeRoom[i].roomId == roomId ){
if( tradeRoom[i].buyKey ){
var info = getInfoByKey(tradeRoom[i].buyKey)
info.roomId = null
tradeRoom[i].buyKey = null
tradeRoom[i].state = 0
}
if( tradeRoom[i].sellKey ){
var info = getInfoByKey(tradeRoom[i].sellKey)
info.roomId = null
tradeRoom[i].sellKey = null
tradeRoom[i].state = 0
}
index = i
break
}
}
if( index != null ){
tradeRoom.splice(index, 1)
}
}
function handleCommand(){
var strCommand = GetCommand()
if( strCommand ){
Log("收到指令",strCommand)
if (strCommand ==='clearlog') {
LogReset(1)
LogVacuum()
Log("已清空日志!");
}
else{
if( IsVirtual() != true ){
var arr = strCommand.split("|")
if( arr.length == 2) {
var key = arr[0]
var side = arr[1]
if( side == "buyMatch" || side == "sellMatch" || side == "cancelMatch" ){
roomMatch( key , side )
}else if( side == 'open' ){
Log("对冲开仓")
tradePoll( key , false )
}else if( side == 'close'){
Log("对冲平仓")
tradePoll( key , true )
}else if( side == 'delRoom' ){
delRoom( key )
Log("删除匹配")
}else if( side == 'buyPos' ){
tradeSingle( key , 'buy')
}else if( side == 'sellPos' ){
tradeSingle( key , 'sell')
}
}
}
}
}
}
function restoreIt( info ){
exInfo.forEach( function( item ){
//如果保存数据里有覆盖
for( var i = 0 ; i < info.exInfo.length ; i++ ){
if( info.exInfo[i].key == item.key ){
item.roomId = info.exInfo[i].roomId
break
}
}
})
roomId = info.roomId
tradeRoom = info.tradeRoom
//过滤一次room信息
var newRoomInfo = []
tradeRoom.forEach(function(item){
var keepFlag = true
if( item.buyKey ){
var buyInfo = getInfoByKey( item.buyKey )
var sellInfo = getInfoByKey( item.sellKey )
if( !buyInfo || buyInfo.roomId != item.roomId ){
keepFlag = false
if( sellInfo ){
sellInfo.roomId = null
}
}
}
if( item.sellKey ){
var sellInfo = getInfoByKey( item.sellKey )
var buyInfo = getInfoByKey( item.buyKey )
if( !sellInfo || sellInfo.roomId != item.roomId ){
keepFlag = false
if( buyInfo ){
buyInfo.roomId = null
}
}
}
if( keepFlag ){
newRoomInfo.push( item )
}
})
tradeRoom = newRoomInfo
}
function main() {
initExInfo()
var reInfo = null
if( RestoreIt ){reInfo = $.Load("restoreInfo")}
if( reInfo ){
Log("开始恢复数据")
restoreIt( reInfo )
}
while( true ){
handleCommand()
var now = new Date()
var nowTs = Date.now()
updateBalance()
updatePos()
updateTicker()
handleWaitList()
var allstatus = ''
allstatus += getTableInfo()
var 轮询耗时 = Date.now() - nowTs
allstatus+=('`'+JSON.stringify({'type':'button', 'cmd': 'clearlog', 'name': '清空日志'})+'`'+'\n');
allstatus+=('轮询耗时: ' + 轮询耗时 + ' 毫秒, 当前时间:' + _D() + ', 星期' + ['日', '一', '二', '三', '四', '五', '六'][now.getDay()] + '\n');
LogStatus(allstatus)
Sleep(5000)
}
}
function onexit(){
Log("Exit");
handle_exit()
}
function onerror(msg){
Log("errorExit",msg);
handle_exit()
}
var restoreInfo = {
exInfo : exInfo,
roomId : roomId,
tradeRoom : tradeRoom,
}
function handle_exit(){
if( RestoreIt ){
Log("数据持久化")
restoreInfo.exInfo = exInfo
restoreInfo.roomId = roomId
restoreInfo.tradeRoom = tradeRoom
var ret = $.Save("restoreInfo", restoreInfo)
if( !ret ){
Log("保存历史数据是失败")
for( key in restoreInfo ){
Log( key , restoreInfo[key] )
}
}
}else{
$.Save("restoreInfo", null)
}
}