0bps

Strategy vault / Grid

Basic Grid Trading Strategy

Implementation of basic grid trading strategy This is not the optimal version at present. I am currently running Binance's WebSocket version. If needed, I can change Binance's WebSocket version to one that runs on…

Author
xl9211
Complexity
Intermediate · 156 lines
Parameters
7

Implementation of basic grid trading strategy

This is not the optimal version at present. I am currently running Binance's WebSocket version. If needed, I can change Binance's WebSocket version to one that runs on the Inventor.

Parameters

NameDefaultWhat it controls
trading_pairBTC_USDTtrading pair
lower_pricefalseRange lower limit price
upper_pricefalseRange upper limit price
grid_num2Number of Grids
trading_per_gridfalsePurchase quantity per grid
price_precision2Order price accuracy
amount_precision5Order quantity precision

Source Python

'''backtest
start: 2021-08-01 00:00:00
end: 2021-09-01 00:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Binance","currency":"BTC_USDT","balance":1000000,"stocks":0,"fee":[0.06,0.075]}]
args: [["lower_price",30000],["upper_price",50000],["grid_num",200],["trading_per_grid",0.1]]
'''

grid = dict()
interval = 0


class GridItem:
    def __init__(self, order_id, status, price):
        self.order_id = order_id
        self.status = status  # -1:This point is blank; -2: no order has been placed at this point
        self.side = 0  # 1:Buy; 2: Sell; 0: Unknown
        self.price = price


def create_grid(ticker_price):
    global grid
    global interval

    exchange.SetCurrency(trading_pair)
    exchange.SetPrecision(price_precision, amount_precision)

    interval = _N((upper_price - lower_price) / grid_num, price_precision)
    
    grid[lower_price] = GridItem(-1, -2, lower_price)
    grid[upper_price] = GridItem(-1, -2, upper_price)
    for i in range(1, grid_num):
        price = lower_price + interval * i
        grid[price] = GridItem(-1, -2, price)
        
    buy_stocks = (grid_num - int((ticker_price - lower_price) / interval) - 1) * trading_per_grid
    buy_2_sell(buy_stocks)
    
    
def buy_2_sell(buy_stocks):
    while True:
        account = exchange.GetAccount()
        stocks = _N(account['Stocks'], amount_precision)
        if stocks < buy_stocks:
            depth = exchange.GetDepth()
            price = depth['Asks'][0]['Price']
            vol = _N(price * trading_per_grid, price_precision)
            min_vol = 1.0 / (10 ** price_precision)
            exchange.Buy(-1, vol if vol > min_vol else min_vol)
        else:
            break
        Sleep(1000)


def set_blank(price):
    grid[price].order_id = -1
    grid[price].status = -1
    grid[price].side = 0


def update_order():
    last_blank_price = -1
    close_price_list = set()

    for grid_item in grid.values():
        if grid_item.status == -1:
            last_blank_price = grid_item.price
        elif grid_item.status == -2:
            close_price_list.add(grid_item.price)
        else:
            order = exchange.GetOrder(grid_item.order_id)
            grid_item.status = order["Status"]
            if grid_item.status == 1:
                close_price_list.add(grid_item.price)
    return close_price_list, last_blank_price


def remove_blank(close_price_list, last_blank_price):
    if last_blank_price != -1:
        close_price_list.discard(last_blank_price)


def set_left_blank(close_price_list, left_side_grid, last_blank_price):
    set_blank(left_side_grid)
    close_price_list.discard(left_side_grid)
    if last_blank_price != -1:
        close_price_list.add(last_blank_price)
    
    
def set_right_blank(close_price_list, right_side_grid, last_blank_price):
    set_blank(right_side_grid)
    close_price_list.discard(right_side_grid)
    if last_blank_price != -1:
        close_price_list.add(last_blank_price)


def trace():
    close_price_list, last_blank_price = update_order()
    
    has_buy_order = False
    for price in close_price_list:
        if grid[price].side == 1:
            has_buy_order = True
            break
    
    if has_buy_order:
        ticker_price = min(close_price_list) - interval / 2.0
    else:
        ticker = exchange.GetTicker()
        ticker_price = ticker['Last']
    
    if not grid:
        create_grid(ticker_price)

    left_side_grid = int((ticker_price - lower_price) / interval) * interval + lower_price
    right_side_grid = left_side_grid + interval

    # Set blank points
    # There is no transaction on the left side of the current price and no transaction on the right side of the current price. It does not exist.
    # The blank point on the left side of the current price and the blank point on the right side of the current price do not exist.
    # If either the left or right side is blank, there is no need to update the blank point
    if left_side_grid >= upper_price or right_side_grid <= lower_price:
        # Outside the range, no blank points are needed
        remove_blank(close_price_list, last_blank_price)
    elif grid[left_side_grid].status == 0 and grid[right_side_grid].status == 1:
        set_right_blank(close_price_list, right_side_grid, last_blank_price)
    elif grid[left_side_grid].status == 1 and grid[right_side_grid].status == 0:
        set_left_blank(close_price_list, left_side_grid, last_blank_price)
    elif grid[left_side_grid].status == 1 and grid[right_side_grid].status == 1:
        set_right_blank(close_price_list, right_side_grid, last_blank_price)
    elif grid[left_side_grid].status == -2 and grid[right_side_grid].status == -2:
        set_right_blank(close_price_list, right_side_grid, last_blank_price)

    # Place an order again
    for price in close_price_list:            
        if price <= ticker_price:
            order_id = exchange.Buy(price, trading_per_grid)
            side = 1
        else:
            buy_2_sell(trading_per_grid)
            order_id = exchange.Sell(price, trading_per_grid)
            side = 2

        if order_id:
            grid[price].order_id = order_id
            grid[price].status = 0
            grid[price].side = side


def main():
    while True:
        trace()
        Sleep(1000)

Published by xl9211 on the FMZ strategy square · last modified 2021-09-27. 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.

in Claude Code, Cursor, Codex & 4 more
/agenkit build a grid bot with inventory caps and order deduplication from strategies/Basic-Grid-Trading-Strategy.md
  1. npx agenkit install engineering-kit in your project
  2. Paste the command into your agent and approve each phase
  3. 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.

Related specs

esc

Try , , or .

↑↓ move↵ open