2021-12-07 00:45:45 +08:00
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Name
|
2021-12-07 00:45:45 +08:00
|
|
|
|
|
2023-04-18 23:12:35 +08:00
|
|
|
|
币安合约网格-基础版本-001
|
2021-12-07 00:45:45 +08:00
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Author
|
2021-12-07 00:45:45 +08:00
|
|
|
|
|
|
|
|
|
|
XMaxZone
|
|
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Strategy Description
|
2021-12-07 00:45:45 +08:00
|
|
|
|
|
|
|
|
|
|
策略第一次启动会记录初始价格,后面就会在这个基础价格上做单,大于基础价格会做空,小于基础价格会做多,
|
|
|
|
|
|
这是基础版本,有其他需求的可以拿去自己改。或者联系我
|
|
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Strategy Arguments
|
2021-12-07 00:45:45 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
|Argument|Default|Description|
|
2021-12-07 00:45:45 +08:00
|
|
|
|
|----|----|----|
|
|
|
|
|
|
|value|150|开仓价值|
|
|
|
|
|
|
|pct|0.01|网格间隔|
|
|
|
|
|
|
|Show|true|是:展示收益,否:展示总账户|
|
|
|
|
|
|
|Interval|2|休眠时间|
|
|
|
|
|
|
|LogInterval|600|收益曲线更新时间(s)|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Source (python)
|
2021-12-07 00:45:45 +08:00
|
|
|
|
|
|
|
|
|
|
``` python
|
|
|
|
|
|
# 刚开始学python,有不不合理的地方,见谅!!!
|
|
|
|
|
|
import time
|
|
|
|
|
|
import requests
|
|
|
|
|
|
import math
|
|
|
|
|
|
import pandas as pd
|
|
|
|
|
|
|
|
|
|
|
|
InitPrice = 0
|
|
|
|
|
|
updateProfitTime = 0
|
|
|
|
|
|
assets = {}
|
|
|
|
|
|
tradeInfo = {}
|
|
|
|
|
|
accountAssets = {}
|
|
|
|
|
|
runtimeData = {}
|
|
|
|
|
|
Funding = 0 #账户资金 为0的时候自动获取
|
|
|
|
|
|
symbol = ''
|
|
|
|
|
|
Version = '0.0.1'
|
|
|
|
|
|
SuccessColor = '#5cb85c' #成功颜色
|
|
|
|
|
|
DangerColor = '#ff0000' #危险颜色
|
|
|
|
|
|
WrningColor = '#f0ad4e' #警告颜色
|
|
|
|
|
|
|
|
|
|
|
|
assets['USDT'] = {'unrealised_profit':0,'margin':0,'margin_balance':0,'total_balance':0,'leverage':0,'update_time':0,'margin_ratio':0,'init_balance':0,'profit':0}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if IsVirtual():
|
|
|
|
|
|
Log('不能进行回测')
|
|
|
|
|
|
exit()
|
|
|
|
|
|
|
|
|
|
|
|
if exchange.GetName() != 'Futures_Binance':
|
|
|
|
|
|
Log('只支持币安期货交易所!')
|
|
|
|
|
|
exit()
|
|
|
|
|
|
|
|
|
|
|
|
def init():
|
|
|
|
|
|
initData()
|
|
|
|
|
|
CancelOrder()
|
|
|
|
|
|
exchangeInfo = requests.get('https://fapi.binance.com/fapi/v1/exchangeInfo').json()
|
|
|
|
|
|
if exchangeInfo is None:
|
|
|
|
|
|
Log('无法连接币安网络,需要海外托管者')
|
|
|
|
|
|
exit()
|
|
|
|
|
|
for i in range(len(exchangeInfo['symbols'])):
|
|
|
|
|
|
if exchangeInfo['symbols'][i]['symbol'] == symbol:
|
|
|
|
|
|
assets[symbol] = {'amount': 0,'hold_price': 0,'value': 0,'bid_price': 0,'ask_price': 0,'realised_profit': 0,'margin': 0,'unrealised_profit': 0,
|
|
|
|
|
|
'leverage': 20, 'positionInitialMargin': 0, 'liquidationPrice': 0 }
|
|
|
|
|
|
tradeInfo[symbol] = {'minQty': float(exchangeInfo['symbols'][i]['filters'][1]['minQty']) ,
|
|
|
|
|
|
'priceSize': int((math.log10(1.1/float(exchangeInfo['symbols'][i]['filters'][0]['tickSize'])))),'amountSize': int((math.log10(1.1/float(exchangeInfo['symbols'][i]['filters'][1]['stepSize']))))}
|
|
|
|
|
|
|
|
|
|
|
|
def CancelOrder():
|
|
|
|
|
|
exchange.SetContractType('swap')
|
|
|
|
|
|
#撤销所有未成交订单
|
|
|
|
|
|
orders = exchange.GetOrders()
|
|
|
|
|
|
for x in range(len(orders)):
|
|
|
|
|
|
if orders[x]['Info']['symbol'] == symbol :
|
|
|
|
|
|
exchange.CancelOrder(orders[x]['Id'])
|
|
|
|
|
|
|
|
|
|
|
|
def UpdateStatus():
|
|
|
|
|
|
global Funding,updateProfitTime
|
|
|
|
|
|
if Funding == 0 :
|
|
|
|
|
|
Funding = float(FirstAccount()['Info']['totalWalletBalance']) #获取初始资金
|
|
|
|
|
|
# totalProfit = assets['USDT']['total_balance'] - Funding #计算收益
|
|
|
|
|
|
|
|
|
|
|
|
accountTable = {
|
|
|
|
|
|
'type': "table",
|
|
|
|
|
|
'title': "盈利统计",
|
|
|
|
|
|
'cols': ["运行天数", "初始资金", "现有资金", "保证金余额", "已用保证金", "保证金比率", "总收益", "预计年化", "预计月化", "平均日化"],
|
|
|
|
|
|
'rows': []
|
|
|
|
|
|
}
|
|
|
|
|
|
table = {
|
|
|
|
|
|
'type': 'table',
|
|
|
|
|
|
'title': '交易对信息',
|
|
|
|
|
|
'cols': ['编号', '[模式][倍数]', '币种信息', '开仓方向', '开仓数量', '持仓价格', '当前价格', '强平价格', '持仓价值', '保证金', '未实现盈亏'],
|
|
|
|
|
|
'rows': []
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
profitColors = DangerColor
|
|
|
|
|
|
totalProfit = assets['USDT']['total_balance'] - Funding
|
|
|
|
|
|
runday = runtimeData['dayDiff']
|
|
|
|
|
|
if runday == 0:
|
|
|
|
|
|
runday = 1
|
|
|
|
|
|
if totalProfit > 0:
|
|
|
|
|
|
profitColors = SuccessColor
|
|
|
|
|
|
dayProfit = totalProfit / runday
|
|
|
|
|
|
dayRate = dayProfit / Funding * 100
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
accountTable['rows'].append([
|
|
|
|
|
|
runday,
|
|
|
|
|
|
'$' + str(_N(Funding, 2)),
|
|
|
|
|
|
'$' + str(assets['USDT']['total_balance']),
|
|
|
|
|
|
'$' + str(assets['USDT']['margin_balance']),
|
|
|
|
|
|
'$' + str(assets['USDT']['margin']),
|
|
|
|
|
|
str(_N(assets['USDT']['margin_ratio'], 2)) + '%',
|
|
|
|
|
|
str(_N(totalProfit / Funding * 100, 2)) + "% = $" + str(_N(totalProfit, 2)) + (profitColors),
|
|
|
|
|
|
str(_N(dayRate * 365, 2)) + "% = $" + str(_N(dayProfit * 365, 2)) + (profitColors),
|
|
|
|
|
|
str(_N(dayRate * 30, 2)) + "% = $" + str(_N(dayProfit * 30, 2)) + (profitColors),
|
|
|
|
|
|
str(_N(dayRate, 2)) + "% = $" + str(_N(dayProfit, 2)) + (profitColors)
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i = 1
|
|
|
|
|
|
for x in list(symbol.split(',')):
|
|
|
|
|
|
|
|
|
|
|
|
direction = '空仓'
|
|
|
|
|
|
margin = direction
|
|
|
|
|
|
if assets[x]['amount'] != 0:
|
|
|
|
|
|
direction = '做多' + SuccessColor if assets[symbol]['amount'] > 0 else '做空' + DangerColor
|
|
|
|
|
|
margin = '全仓' if assets[symbol]['marginType'] == 'cross' else '逐仓'
|
|
|
|
|
|
unrealised_profit_color = '#000000'
|
|
|
|
|
|
if assets[symbol]['unrealised_profit'] > 0:
|
|
|
|
|
|
unrealised_profit_color = SuccessColor
|
|
|
|
|
|
if assets[symbol]['unrealised_profit'] < 0:
|
|
|
|
|
|
unrealised_profit_color = DangerColor
|
|
|
|
|
|
|
|
|
|
|
|
infoList = [
|
|
|
|
|
|
i,
|
|
|
|
|
|
'['+margin+']'+'['+str(assets[x]['leverage'])+']',
|
|
|
|
|
|
x,
|
|
|
|
|
|
direction,
|
|
|
|
|
|
assets[x]['amount'],
|
|
|
|
|
|
assets[x]['hold_price'],
|
|
|
|
|
|
assets[x]['price'],
|
|
|
|
|
|
assets[x]['liquidationPrice'],
|
|
|
|
|
|
float(assets[x]['amount']) * float(assets[x]['price']),
|
|
|
|
|
|
assets[x]['positionInitialMargin'],
|
|
|
|
|
|
assets[x]['unrealised_profit'],
|
|
|
|
|
|
]
|
|
|
|
|
|
table['rows'].append(infoList)
|
|
|
|
|
|
|
|
|
|
|
|
retData = runtimeData['str'] + '\n' + "最后更新: " + _D() + '\n' + 'Version:' + Version + '\n'
|
|
|
|
|
|
LogStatus(retData+ '`' + json.dumps(accountTable) + '`\n'+ '`' + json.dumps(table) + '`\n')
|
|
|
|
|
|
|
|
|
|
|
|
if int(time.time()*1000) - updateProfitTime > LogInterval * 1000:
|
|
|
|
|
|
balance = assets['USDT']['total_balance']
|
|
|
|
|
|
key = "initialAccount_" + exchange.GetLabel()
|
|
|
|
|
|
initialAccount = _G(key)
|
|
|
|
|
|
#Log('balance:',balance,'Funding:',Funding,'initialAccount:',initialAccount['Info']['totalWalletBalance'])
|
|
|
|
|
|
if Show:
|
|
|
|
|
|
balance = assets['USDT']['total_balance'] - Funding
|
|
|
|
|
|
LogProfit(_N(balance, 3))
|
|
|
|
|
|
updateProfitTime = int(time.time()*1000)
|
|
|
|
|
|
Profit = _N(balance,0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def UpdateAccount():
|
|
|
|
|
|
# Log('UpdateAccount()')
|
|
|
|
|
|
global accountAssets
|
|
|
|
|
|
account = exchange.GetAccount()
|
|
|
|
|
|
position = exchange.GetPosition()
|
|
|
|
|
|
if account is None and position is None :
|
|
|
|
|
|
Log('更新账户超时!!!')
|
|
|
|
|
|
return
|
|
|
|
|
|
accountAssets = account['Info']['assets']
|
|
|
|
|
|
assets['USDT']['update_time'] = int(time.time()) * 1000 #秒转毫秒 同步更新账户时间
|
|
|
|
|
|
for i in range(len(account['Info']['positions'])) :
|
|
|
|
|
|
if account['Info']['positions'][i]['symbol'] == symbol :
|
|
|
|
|
|
#计算持仓保证金 初始保证金 + 维持保证金
|
|
|
|
|
|
assets[symbol]['margin'] = float(account['Info']['positions'][i]['initialMargin']) + float(account['Info']['positions'][i]['maintMargin'])
|
|
|
|
|
|
#未实现收益
|
|
|
|
|
|
assets[symbol]['unrealised_profit'] = float(account['Info']['positions'][i]['unrealizedProfit'])
|
|
|
|
|
|
assets[symbol]['positionInitialMargin'] = float(account['Info']['positions'][i]['positionInitialMargin'])
|
|
|
|
|
|
assets[symbol]['leverage'] = account['Info']['positions'][i]['leverage']
|
|
|
|
|
|
|
|
|
|
|
|
#计算持仓保证金总额
|
|
|
|
|
|
assets['USDT']['margin'] = float(account['Info']['totalInitialMargin']) + float(account['Info']['totalMaintMargin'])
|
|
|
|
|
|
assets['USDT']['margin_balance'] = float(account['Info']['totalMarginBalance'])
|
|
|
|
|
|
assets['USDT']['total_balance'] = float(account['Info']['totalWalletBalance'])
|
|
|
|
|
|
|
|
|
|
|
|
ps = json.loads(exchange.GetRawJSON())
|
|
|
|
|
|
if len(ps) > 0 :
|
|
|
|
|
|
for x in range(len(ps)):
|
|
|
|
|
|
if ps[x]['symbol'] == symbol:
|
|
|
|
|
|
assets[symbol]['hold_price'] = float(ps[x]['entryPrice'])
|
|
|
|
|
|
assets[symbol]['amount'] = float(ps[x]['positionAmt'])
|
|
|
|
|
|
assets[symbol]['unrealised_profit'] = float(ps[x]['unRealizedProfit'])
|
|
|
|
|
|
assets[symbol]['liquidationPrice'] = float(ps[x]['liquidationPrice'])
|
|
|
|
|
|
assets[symbol]['marginType'] = ps[x]['marginType']
|
|
|
|
|
|
|
|
|
|
|
|
def UpdateTick():
|
|
|
|
|
|
global InitPrice
|
|
|
|
|
|
try:
|
|
|
|
|
|
res = requests.get(f'https://fapi.binance.com/fapi/v1/ticker/price?symbol={symbol}').json()
|
|
|
|
|
|
except:
|
|
|
|
|
|
Log('get ticker time out !')
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if target:
|
|
|
|
|
|
InitPrice = target_price
|
|
|
|
|
|
_G('InitPrice',InitPrice)
|
|
|
|
|
|
else:
|
|
|
|
|
|
if _G('InitPrice') is None :
|
|
|
|
|
|
InitPrice = res['price']
|
|
|
|
|
|
_G('InitPrice',InitPrice)
|
|
|
|
|
|
else:
|
|
|
|
|
|
InitPrice = _G('InitPrice')
|
|
|
|
|
|
|
|
|
|
|
|
assets[symbol]['price'] = res['price']
|
|
|
|
|
|
|
|
|
|
|
|
def Trade(direction,price,amount):
|
|
|
|
|
|
if amount < tradeInfo[symbol]['minQty']:
|
|
|
|
|
|
Log(symbol,'合约价值偏离或冰山委托设置的过小,达不到最小成交额,最小需要:', _N(tradeInfo[symbol]['minQty'] * price,4) + 1)
|
|
|
|
|
|
else:
|
|
|
|
|
|
para = ''
|
|
|
|
|
|
url = '/fapi/v1/order'
|
|
|
|
|
|
para += 'symbol='+ symbol
|
|
|
|
|
|
para += '&side='+ direction
|
|
|
|
|
|
para += '&type=LIMIT&timeInForce=GTC'
|
|
|
|
|
|
para += '&quantity='+ str(amount)
|
|
|
|
|
|
para += '&price='+ str(price)
|
|
|
|
|
|
para += "×tamp="+str(time.time() * 1000);
|
|
|
|
|
|
go = exchange.Go("IO", "api", "POST", url, para)
|
|
|
|
|
|
ret = go.wait()
|
|
|
|
|
|
if ret is not None:
|
|
|
|
|
|
logType = LOG_TYPE_SELL
|
|
|
|
|
|
if direction == 'BUY':
|
|
|
|
|
|
logType =LOG_TYPE_BUY
|
|
|
|
|
|
exchange.Log(logType,price,amount,symbol)
|
|
|
|
|
|
|
|
|
|
|
|
def batch(buy_price,sell_price):
|
|
|
|
|
|
exchange.SetContractType('swap')
|
|
|
|
|
|
#撤销所有未成交订单
|
|
|
|
|
|
orders = exchange.GetOrders()
|
|
|
|
|
|
if len(orders) < 2 :
|
|
|
|
|
|
return True
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def Process():
|
|
|
|
|
|
|
|
|
|
|
|
buy_price = (value / pct - value) / ((value / pct) / float(InitPrice) + assets[symbol]['amount'])
|
|
|
|
|
|
sell_price = (value / pct + value) / ((value / pct) / float(InitPrice) + assets[symbol]['amount'])
|
|
|
|
|
|
|
|
|
|
|
|
if float(buy_price) > float(assets[symbol]['price']) or float(sell_price) < float(assets[symbol]['price']) or batch(buy_price,sell_price):
|
|
|
|
|
|
CancelOrder()
|
|
|
|
|
|
Trade('BUY', _N(buy_price, 5), _N(value / buy_price, 0))
|
|
|
|
|
|
Trade('SELL', _N(sell_price, 5), _N(value / sell_price, 0))
|
|
|
|
|
|
|
|
|
|
|
|
def FirstAccount():
|
|
|
|
|
|
key = "initialAccount_" + exchange.GetLabel()
|
|
|
|
|
|
initialAccount = _G(key)
|
|
|
|
|
|
if initialAccount is None:
|
|
|
|
|
|
initialAccount = exchange.GetAccount()
|
|
|
|
|
|
_G(key, initialAccount)
|
|
|
|
|
|
return initialAccount
|
|
|
|
|
|
|
|
|
|
|
|
def StartTime():
|
|
|
|
|
|
StartTime = _G('StartTime')
|
|
|
|
|
|
if StartTime is None:
|
|
|
|
|
|
StartTime = _D()
|
|
|
|
|
|
_G('StartTime',StartTime)
|
|
|
|
|
|
return StartTime
|
|
|
|
|
|
|
|
|
|
|
|
def RunTime():
|
|
|
|
|
|
ret = {}
|
|
|
|
|
|
startTime = StartTime()
|
|
|
|
|
|
nowTime = _D()
|
|
|
|
|
|
dateDiff = (time.mktime(time.strptime(nowTime,'%Y-%m-%d %H:%M:%S')) - time.mktime(time.strptime(startTime,'%Y-%m-%d %H:%M:%S')) ) * 1000 #计算时间差
|
|
|
|
|
|
dayDiff = math.floor(dateDiff / (24 * 3600 * 1000))
|
|
|
|
|
|
lever1 = dateDiff % (24 * 3600 * 1000 )
|
|
|
|
|
|
hours = math.floor(lever1 / (3600 * 1000))
|
|
|
|
|
|
lever2 = lever1 % (3600 * 1000)
|
|
|
|
|
|
minutes = math.floor(lever2 / (60 * 1000))
|
|
|
|
|
|
|
|
|
|
|
|
ret['dayDiff'] = dayDiff
|
|
|
|
|
|
ret['hours'] = hours
|
|
|
|
|
|
ret['minutes'] = minutes
|
|
|
|
|
|
ret['str'] = '运行时间:' + str(dayDiff) + '天' + str(hours) + '小时' + str(minutes) + '分钟'
|
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
def initData():
|
|
|
|
|
|
global symbol
|
|
|
|
|
|
if _G('symbol') is None:
|
|
|
|
|
|
symbol = exchange.GetCurrency().replace('_','')
|
|
|
|
|
|
_G('symbol',symbol)
|
|
|
|
|
|
Log('初始化币种:',symbol)
|
|
|
|
|
|
else:
|
|
|
|
|
|
symbol = _G('symbol')
|
|
|
|
|
|
Log('交易币种:',symbol)
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
exchange.SetContractType('swap')
|
|
|
|
|
|
exchange.SetMarginLevel(10)
|
|
|
|
|
|
SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused|Unknown")
|
|
|
|
|
|
global runtimeData
|
|
|
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
|
runtimeData = RunTime()
|
|
|
|
|
|
#更新账户和持仓
|
|
|
|
|
|
UpdateAccount()
|
|
|
|
|
|
#更新行情
|
|
|
|
|
|
UpdateTick()
|
|
|
|
|
|
#策略主逻辑
|
|
|
|
|
|
Process()
|
|
|
|
|
|
#更新图表
|
|
|
|
|
|
UpdateStatus()
|
|
|
|
|
|
|
|
|
|
|
|
Sleep(1000 * Interval)
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Detail
|
2021-12-07 00:45:45 +08:00
|
|
|
|
|
|
|
|
|
|
https://www.fmz.com/strategy/322060
|
|
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Last Modified
|
2021-12-07 00:45:45 +08:00
|
|
|
|
|
|
|
|
|
|
2021-10-09 12:58:38
|