#!/usr/bin/env python3 ''' @author: chmick @todo : FINISH DEBUGING ### basic trailing stop loss for binance margin trading ## you can use freely ## delivered with no support , use at your own risk ## it's a while true loop that check a market price and sell ## if it reaches a parameter limit. The limit can change if the trailing stop option ## is activated and price moves in the right direction ## thanks to Sam Chardy for the binance api (you need it , for this program to work ) ## https://github.com/sammchardy/python-binance ''' import sys import os from argparse import ArgumentParser from time import sleep import logging import configparser from logging.handlers import RotatingFileHandler from binance.enums import ORDER_TYPE_MARKET from binance.enums import SIDE_SELL, TIME_IN_FORCE_GTC, SIDE_BUY from binance.client import Client from urllib.request import urlopen def internet_on(): try: response = urlopen('https://www.binance.com/en/', timeout=10) logger.debug("net response %r" % response) return True except: return False ########################################## ### put your api key and secret ########################################### binance_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' binance_secret = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' ## if testMode = False ## the program just play a sound, print a message and exit when triggered testMode = False ## WARNING if testMode = False ## the program will execute #testMode = True ####################################### ### Program ########################################" parser = ArgumentParser(description='binance margin stop loss') parser.add_argument("-l", "--loglevel", dest="loglevel", default='INFO', help="log level default is INFO",choices=['INFO','DEBUG', 'WARNING', 'ERROR']) parser.add_argument("-p", "--pair", dest="pair", default='BTCUSDT', help="pair to watch") group = parser.add_mutually_exclusive_group(required=True) group.add_argument("-L", "--Long", dest="trademode", action='store_const', const='LONG', help="stop loss for long position") group.add_argument("-S", "--Short", dest="trademode", action='store_const', const='SHORT', help="stop loss for short position ") parser.add_argument("-t", "--trigger", dest="trigger", required=True, help="trigger limit", type=float) parser.add_argument("-T", "--trailing", dest="trailing", action='store_true', help="enable trailing stop") list_args = parser.parse_args() config = configparser.ConfigParser() program_name = os.path.basename(sys.argv[0]) pair = list_args.pair formatter = logging.Formatter(fmt='%(asctime)s.%(msecs)03d %(levelname)s #%(threadName)-9s# %(message)s', datefmt='%m-%d %H:%M:%S') logger = logging.getLogger('BinanceStopLoss' + pair + str(os.getpid())) logger.setLevel(list_args.loglevel) logger.critical('loglevel set to : %s' % logger.level) filelogdebug = RotatingFileHandler(filename=os.path.dirname(os.path.abspath(__file__))+'/log/'+__file__ + pair + '.debug.log', mode='a', maxBytes=40960000, backupCount=10) filelogwarn = RotatingFileHandler(filename=os.path.dirname(os.path.abspath(__file__))+'/log/'+__file__ + pair + '.warn.log', mode='a', maxBytes=40960000, backupCount=10) filelogdebug.setLevel(logging.DEBUG) filelogdebug.setFormatter(formatter) filelogwarn.setLevel(logging.WARNING) filelogwarn.setFormatter(formatter) logger.addHandler(filelogdebug) logger.addHandler(filelogwarn) bclient = Client(api_key=binance_key, api_secret=binance_secret ) ## VARIABLES symbolData = bclient.get_symbol_info(symbol=list_args.pair) # baseAsset est le coin que l on trade exemple BNB dans BNBUSDT baseAsset = symbolData['baseAsset'] #quoteAsset est contre quoi on trade exemple USDT dans BNBUSDT quoteAsset = symbolData['quoteAsset'] tempDict = next(item for item in symbolData['filters'] if item["filterType"] == 'PRICE_FILTER') filterPricePrecision = int(symbolData['baseAssetPrecision']) filterTicksize = float(tempDict['tickSize']) tempDict = next(item for item in symbolData['filters'] if item["filterType"] == 'LOT_SIZE') filterMinSize = float(tempDict['minQty']) filterLotStepSize = float(tempDict['stepSize']) filterVolumePrecision = int(symbolData['quotePrecision']) price = 0 previousPrice = 0 trigger = list_args.trigger ### MAIN LOOP while True : sleep(10) previousPrice = price info = bclient.get_margin_account() ## we keep a dictionnary containing a list of what we borrowed borrowedDict = dict() for c in info['userAssets']: if 0 != float(c['borrowed']): borrowedDict[c['asset']] = c ticker = bclient.get_orderbook_ticker(symbol=list_args.pair) print('mode %s , limit %f , last ticker %r'%(list_args.trademode,trigger,ticker)) ## in short mode , we compare trigger and sell price ## if triggered , the program th net borrowed if list_args.trademode == 'SHORT' : price = float(ticker['askPrice']) ## trailing management if price < previousPrice and list_args.trailing == True and previousPrice != 0: trigger = trigger - (previousPrice-price) if price >= trigger : v == 0 temp = borrowedDict.get(baseAsset, None) if temp : v = float(temp['netAsset'] ) print('!!!! trigger value reached in short mode, rebuying %f'%v) print("\a") if not testMode : myLimitOrder = bclient.create_margin_order(symbol=list_args.pair,quantity=v, side=SIDE_BUY, type=ORDER_TYPE_MARKET, timeInForce=TIME_IN_FORCE_GTC) exit(0) ## in LONG mode , we compare trigger and buy price if list_args.trademode == 'LONG': price = float(ticker['bidPrice']) ## trailing management if price > previousPrice and list_args.trailing == True and previousPrice != 0: trigger = trigger + (price - previousPrice) if price <= trigger : ## in long mode we sell everything temp = bclient.get_margin_account()['userAssets'] for a in temp : if a['asset'] == baseAsset : v = float(a['free']) print('trigger value reached in long mode, selling %f'%v) print("\a") if not testMode: myLimitOrder = bclient.create_margin_order(symbol=list_args.pair, quantity=v, side=SIDE_SELL, type=ORDER_TYPE_MARKET, timeInForce=TIME_IN_FORCE_GTC) exit(0)