#!/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)