Binance Contract Grid 002v
Python strategy by XMaxZone.
PythonGrid
The original spec has no written description. Read the source below.
Parameters
| Name | Default | What it controls |
|---|---|---|
| value | 150 | Opening value |
| pct | 0.01 | Grid interval |
| Show | true | Yes: Display income, No: Display total account |
| target | false | Set target price |
| target_price | 1000 | Target price |
| LogInterval | 600 | Update time of profit curve |
| Interval | 2 | Dormant Time |
Source Python
import time
import requests
import math
# import pandas as pd
InitPrice = 0
updateProfitTime = 0
assets = {}
tradeInfo = {}
accountAssets = {}
runtimeData = {}
Funding = 0 #Automatically retrieve when the account balance is 0
symbol = ''
Version = '0.0.1'
SuccessColor = '#5cb85c' #Success color
DangerColor = '#ff0000' #Dangerous colors
WrningColor = '#f0ad4e' #Warning color
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('Cannot perform backtest')
exit()
if exchange.GetName() != 'Futures_Binance':
Log('Only supports Binance futures exchange!')
exit()
def init():
initData()
CancelOrder()
exchangeInfo = requests.get('https://fapi.binance.com/fapi/v1/exchangeInfo').json()
if exchangeInfo is None:
Log('Unable to connect to Binance network, requires overseas hosting')
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')
#Cancel all unfilled orders
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']) #Get initial funds
# totalProfit = assets['USDT']['total_balance'] - Funding #Calculate income
accountTable = {
'type': "table",
'title': "Profit statistics",
'cols': ["Operating days", "Initial funds", "Existing funds", "Margin balance", "Used margin", "Margin ratio", "Total income", "Estimated annualized", "Estimated monthly", "Average daily"],
'rows': []
}
table = {
'type': 'table',
'title': 'Trading pair information',
'cols': ['Number', '[Mode][Multiple][Position Mode]', 'Currency information', 'Opening direction', 'Initial price', 'Opening quantity', 'Position price', 'Current price', 'Liquidation price', 'Position value', 'Margin', 'Unrealized profit and loss''],
'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
#Log('dayProfit:',dayProfit,'Funding:',Funding)
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(',')):
typestr = 'Long and short positions'
if type == 1:
typestr = 'Only hold long positions'
if type == 2:
typestr = 'Only hold short positions'
direction = 'Short position'
margin = direction
if assets[x]['amount'] != 0:
direction = 'go long' + SuccessColor if assets[symbol]['amount'] > 0 else 'go short' + DangerColor
margin = 'Cross position' if assets[symbol]['marginType'] == 'cross' else 'Isolated position'
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'])+']'+'['+typestr+']',
x,
direction,
InitPrice,
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' + "Last Update: " + _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('Update account timeout!!!')
return
accountAssets = account['Info']['assets']
assets['USDT']['update_time'] = int(time.time()) * 1000 #Convert seconds to milliseconds and update account time synchronously
for i in range(len(account['Info']['positions'])) :
if account['Info']['positions'][i]['symbol'] == symbol :
#Calculate position margin Initial margin + Maintenance margin
assets[symbol]['margin'] = float(account['Info']['positions'][i]['initialMargin']) + float(account['Info']['positions'][i]['maintMargin'])
#Unrealized gains
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']
#Calculate the total margin of positions
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
res = _C(exchange.GetTicker)
if res is None:
Log("Market update exception!!!")
if target:
InitPrice = target_price
_G('InitPrice',InitPrice)
else:
if _G('InitPrice') is None :
InitPrice = res.Last
_G('InitPrice',InitPrice)
else:
InitPrice = _G('InitPrice')
assets[symbol]['price'] = res.Last
def Trade(direction,price,amount):
if amount < 0:
amount = -amount
Log('amount:',amount,'minQty:',tradeInfo[symbol]['minQty'])
if amount < tradeInfo[symbol]['minQty']:
Log(symbol,'The contract value deviates or the iceberg order setting is too small, the minimum transaction amount cannot be reached, and the minimum required:', _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')
#Cancel all unfilled orders
orders = exchange.GetOrders()
if len(orders) < 2 :
return True
return False
def Process():
amount = (1 - float(assets[symbol]['price']) / float(InitPrice)) / float(pct) * float(value) / float(assets[symbol]['price'])
if abs(amount - assets[symbol]['amount']) > _N(value / float(assets[symbol]['price']), tradeInfo[symbol]['amountSize']):
if amount > 0:
if assets[symbol]['amount'] < amount:
CancelOrder()
Trade('BUY', round(float(assets[symbol]['price']), tradeInfo[symbol]['priceSize']), round(amount - float(assets[symbol]['amount']),tradeInfo[symbol]['amountSize']))
if assets[symbol]['amount'] > amount:
CancelOrder()
Trade('SELL', round(float(assets[symbol]['price']), tradeInfo[symbol]['priceSize']), round(amount - float(assets[symbol]['amount']),tradeInfo[symbol]['amountSize']))
if amount < 0:
if assets[symbol]['amount'] < amount:
CancelOrder()
Trade('BUY', round(float(assets[symbol]['price']), tradeInfo[symbol]['priceSize']), round(amount - float(assets[symbol]['amount']),tradeInfo[symbol]['amountSize']))
if assets[symbol]['amount'] > amount:
CancelOrder()
Trade('SELL', round(float(assets[symbol]['price']), tradeInfo[symbol]['priceSize']), round(amount - float(assets[symbol]['amount']),tradeInfo[symbol]['amountSize']))
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, tradeInfo[symbol]['priceSize']), _N(value / buy_price, tradeInfo[symbol]['amountSize']))
Trade('SELL', _N(sell_price, tradeInfo[symbol]['priceSize']), (_N(value / sell_price, tradeInfo[symbol]['amountSize'])))
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 #Calculate the time difference
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'] = 'Running time:' + str(dayDiff) + 'Sky/Heaven' + str(hours) + 'Hour' + str(minutes) + 'Minute'
return ret
def initData():
global symbol
if _G('symbol') is None:
symbol = exchange.GetCurrency().replace('_','')
_G('symbol',symbol)
Log('Initialize the currency:',symbol)
else:
symbol = _G('symbol')
Log('Transaction currency:',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()
#Update account and positions
UpdateAccount()
#Update Quotes
UpdateTick()
#Main logic of strategy
Process()
#Update chart
UpdateStatus()
Sleep(1000 * Interval)
Published by XMaxZone on the FMZ strategy square · last modified 2021-11-19. Collected in the Quant Trading Vault. Unvalidated research: re-test with your own data and costs before trusting it.
Build this with AgenKit
A spec is not a trading system. Paste this into your coding agent and AgenKit runs a specialist team — architecture, test-first build, risk gates, review — with paper trading on by default.
/agenkit build a grid bot with inventory caps and order deduplication from strategies/Binance-Contract-Grid-002v.md- npx agenkit install engineering-kit in your project
- Paste the command into your agent and approve each phase
- Get tests, backtest, risk controls and a paper-trading switch
Understand it first
Before you run this, read the lesson behind the idea and how to backtest it honestly. Specs like this one are the survivors of an unknown number of trials; deflate their results accordingly.