import config, csv import pandas as pd from binance.client import Client client = Client(config.API_KEY, config.API_SECRET) csvfile = open('symbol_locator.csv', 'w', newline='') symbols_writer = csv.writer(csvfile) symbols = client.futures_position_information() df = pd.DataFrame(symbols) df.drop(['positionAmt', 'entryPrice', 'markPrice', 'unRealizedProfit', 'marginType', 'isolatedMargin', 'isAutoAddMargin', 'positionSide', 'liquidationPrice', 'leverage', 'maxNotionalValue'], axis=1, inplace=True) print(df) df.to_csv("symbol_locator.csv", index=True, sep=' ')
def capital_allocation(symbol, long_or_short): # Login info to connect to Binance Bot 1 key = 'HJw3P8MUWXMHL7q21NaA7Hj0R5A3aA6cAbpzi28cOZcwFP6K21DhHsDToXFDFqb6' secret = '6tJDLPX7E34J0Tnfjkc7LRYXGhQaefg8Ib8NXyz3ji2MHNNrFjghr4oyax2o6euz' client_bot1 = Client(key, secret) position_information = client_bot1.futures_position_information() if (long_or_short == 'Long'): long_unrealizedPnl = 0 num_long_positions = 0 for pos in position_information: if (float(pos['positionAmt']) > 0): long_unrealizedPnl = long_unrealizedPnl + (float( pos['unRealizedProfit']) / float(pos['notional'])) * 10000 num_long_positions = num_long_positions + 1 if (num_long_positions == 0): long_unrealizedPnl = 0 else: long_unrealizedPnl = long_unrealizedPnl / num_long_positions if (long_unrealizedPnl < -25): print('Long Unrealized:', long_unrealizedPnl, '0.00') return 0.0 elif (-25 < long_unrealizedPnl and long_unrealizedPnl <= 50): print('Long Unrealized:', long_unrealizedPnl, '0.015') return .015 elif (50 < long_unrealizedPnl and long_unrealizedPnl <= 100): print('Long Unrealized:', long_unrealizedPnl, '0.012') return .012 elif (100 < long_unrealizedPnl): print('Long Unrealized:', long_unrealizedPnl, '0.01') return .01 if (long_or_short == 'Short'): short_unrealizedPnl = 0 num_short_positions = 0 for pos in position_information: if (float(pos['positionAmt']) < 0): short_unrealizedPnl = short_unrealizedPnl + ( float(pos['unRealizedProfit']) / (-1 * float(pos['notional']))) * 10000 num_short_positions = num_short_positions + 1 if (num_short_positions == 0): short_unrealizedPnl = 0 else: short_unrealizedPnl = short_unrealizedPnl / num_short_positions if (short_unrealizedPnl < -25): print('Short Unrealized:', short_unrealizedPnl, '0.00') return 0.0 elif (-25 < short_unrealizedPnl and short_unrealizedPnl <= 50): print('Short Unrealized:', short_unrealizedPnl, '0.015') return .015 elif (50 < short_unrealizedPnl and short_unrealizedPnl <= 100): print('Short Unrealized:', short_unrealizedPnl, '0.012') return .012 elif (100 < short_unrealizedPnl): print('Short Unrealized:', short_unrealizedPnl, '0.01') return .01 return 0.0
class Trader(): """ Purpose is to trade live on the Binance Crypto Exchange Attributes ----------- symbol: str client: Client object - From binance.py capital: float - represents how much money is in account leverage: float - how many multiples of your money you want to trade tf: int - timeframe df: pd.DataFrame - data that will be analyzed. Methods ---------- load_account set_leverage set_timeframe get_position get_necessary_data set_asset make_row load_existing_asset start_trading Please look at each method for descriptions """ def __init__(self): self.start = False self.client = None self.capital = None self.leverage = 1 / 1000 self.tf = None self.symbol = None self.loader = _DataLoader() self.df = None def check_status(self) -> bool: """ Reads a txt file to see whether user wants trading to be on or off :return boolean of True or False. True means trading is on, and false means it is off. """ local_path = os.path.dirname( os.path.dirname( os.path.abspath("__file__"))) + r"\local\trade_status.txt" status = pd.read_csv(local_path).set_index('index') self.start = bool(status.loc[0, 'status']) def load_account(self, additional_balance: int = 20000) -> str: """ Sign in to account using API_KEY and using Binance API """ local_path = os.path.dirname( os.path.dirname( os.path.abspath("__file__"))) + r"\local\binance_api.txt" info = pd.read_csv(local_path).set_index('Name') API_KEY = info.loc["API_KEY", "Key"] SECRET = info.loc["SECRET", "Key"] self.client = Client(API_KEY, SECRET) # Initializing how much money I have on the exchange. Sample 20000 has been added. self.capital = int( float(self.client.futures_account_balance()[0] ['balance'])) + additional_balance return "Welcome Haseab" def set_leverage(self, leverage: float) -> float: """Sets the current leverage of the account: should normally be 1. And 0.001 for testing purposes""" self.leverage = leverage return self.leverage def set_timeframe(self, tf: int) -> int: """Sets the timeframe of the trading data""" self.tf = tf return self.tf def get_necessary_data(self, symbol: str, tf: int, max_candles_needed: int) -> pd.DataFrame: """ Gets the minimum necessary data to trade this asset. Only a symbol and timeframe need to be inputted Note: This method is used as a way to tackle the 1000 candle limit that is currently on the Binance API. A discrete set of ~1000 group candles will be determined, and then the data will be extracted from each, using the _get_binance_futures_candles method, and then all of them will be merged together. Parameters: symbol: str - Symbol of price ticker Ex. "BTCUSDT", "ETHUSDT" tf: int - Timeframe wanted Ex. 1, 3, 5, 77, 100 max_candles_needed: int - Maximum candles needed in the desired timeframe Ex. 231, 770, 1440 :return pd.DataFrame of candlestick data """ now = time.time() df = pd.DataFrame() ranges = Helper.determine_candle_positions(max_candles_needed, tf) # Grabbing each set of about 1000 candles and appending them one after the other for i in range(len(ranges)): try: df = df.append( self.loader._get_binance_futures_candles( symbol, int(ranges[i]), int(ranges[i + 1]), now)) except IndexError: pass return df.drop_duplicates() def _update_data( self, diff: int, symbol: str, ) -> None: """ Used to update the trading data with the exchange data so that it is real time Parameters: ----------- diff: a number that explains how many minutes of disparity there is between current data and live data. :return dataframe with the most up-to-date data. """ # Calculating how many minutes to fetch from API minutes = math.floor(diff) + 1 # Getting minute candlestick data. Number of minute candlesticks represented by "minutes" variable last_price = self.loader._get_binance_futures_candles(symbol, minutes) # Adding a legible Datetime column and using the timestamp data to obtain datetime data last_price['Datetime'] = [ datetime.fromtimestamp(i / 1000) for i in last_price.index ] last_price = last_price[[ "Datetime", "Open", "High", "Low", "Close", "Volume" ]] # Abstracting minute data into appropriate tf last_price = self.loader._timeframe_setter(last_price, self.tf, 0) # Updating original data with new data. self.df = self.df.append(last_price).drop_duplicates() def get_position(self, symbol: str) -> float: """ Gets the total amount of current position for a given symbol Parameters: ------------ symbol: str Ex. "BTCUSDT" "ETHUSDT" Returns float Ex. If a 1.000 BTC position is open, it will return 1.0 """ return float([ i["positionAmt"] for i in self.client.futures_position_information() if i['symbol'] == symbol ][0]) def set_asset(self, symbol: str, tf: int) -> str: """ Set Symbol of the ticker and load the necessary data with the given timeframe to trade it Parameters: ------------ symbol: str Ex. "BTCUSDT", "ETHUSDT" :return str response """ self.symbol = symbol max_candles_needed = 231 # For Binance API purposes, 240 min needs to be inputted as "4h" on binance when fetching data map_tf = { 1: "1m", 3: "3m", 5: "5m", 15: "15m", 30: "30m", 60: "1h", 120: "2h", 240: "4h", 360: "6h", 480: "8h" } start_time = int((time.time() - self.tf * 235 * 60) * 1000) if tf in [1, 3, 5, 15, 30, 60, 120, 240, 360, 480]: # Note: 12H, 1D, 3D, 1W, 1M are also recognized # Fetching data from Binance if it matches the eligible timeframe, as it will be faster df = Helper.into_dataframe( self.client.futures_klines(symbol=symbol, interval=map_tf[tf], startTime=start_time)) df.drop(df.tail(1).index, inplace=True) else: # If it doesn't match Binance available timeframes, it must be transformed after fetching 1m data. df = self.get_necessary_data(symbol, tf, max_candles_needed) df = self.loader._timeframe_setter(df, tf) # Adding Datetime column for readability of the timestamp. Also more formatting done df['Datetime'] = [datetime.fromtimestamp(i / 1000) for i in df.index] df = df[["Datetime", "Open", "High", "Low", "Close", "Volume"]] df[["Open", "High", "Low", "Close", "Volume"]] = df[["Open", "High", "Low", "Close", "Volume"]].astype(float) self.df = df return f"Symbol changed to {self.symbol}" def make_row( self, high=[], low=[], volume=[], count: int = 0, open_price: float = None, open_date: str = None ) -> (pd.DataFrame, list, list, list, list, list): """ Helper function used to update the last row of a dataframe, using all of the data in the previous "last row" as input. This is used to update the price of the candlestick live, with the incomplete current candle constantly updating. Parameters: ----------- high: list Ex. [23348, 23350, 23335, 23330, 23339] low: list Ex. [23300, 23345, 23335, 23320, 23300] volume: list Ex. [47, 31, 110, 117, 2, 55] count: int Ex. 1,2,3 open_price: float Ex. 23342 open_date: str Ex. "2020-08-04 17:33:02" Returns tuple -> (pd.DataFrame, list, list, list, list, list) """ # row variable gets a pd.DataFrame of size 1. row = self.loader._get_binance_futures_candles("BTCUSDT", 1).reset_index() timestamp = row.loc[0, "Timestamp"] close = float(row.loc[0, "Close"]) high.append(float(row.loc[0, "High"])) low.append(float(row.loc[0, "Low"])) volume.append(float(row.loc[0, "Volume"])) # Initial values of a candle that only get set on the first iteration. if count == 0: open_price = row.loc[0, "Open"] open_date = timestamp # Adding to previous candlestick data of the last row by updating the row. dfrow = pd.DataFrame([[ open_date, datetime.fromtimestamp(open_date / 1000), open_price, max(high), min(low), close, sum(volume) ]], columns=[ "Timestamp", "Datetime", "Open", "High", "Low", "Close", "Volume" ]) dfrow[["Open", "High", "Low", "Close", "Volume"]] = dfrow[["Open", "High", "Low", "Close", "Volume"]].astype(float) dfrow = dfrow.set_index("Timestamp") return dfrow, high, low, volume, open_price, open_date def load__existing_asset(self, df: pd.DataFrame) -> pd.DataFrame: """Sets the trading data to an already existing dataframe, passed in as an argument""" self.df = df return self.df def start_trading(self, strategy: FabStrategy, executor, tf: int, sensitivity=0, debug=False) -> str: """ Starts the process of trading live. Each minute, the last row of the data is updated and Rule #2 is checked, otherwise, it waits till the end of the timeframe where it gets the latest data before it checks the rest of the rules. If it does see something following the rules, it will buy/short, given the initial parameters it has. (e.g. leverage, quantity) This process continues indefinetly, unless interrupted. Returns None. """ count, open_price = 0, 0 open_date = None high, low, volume = [], [], [] # Loading data into strategy, and creating moving averages. strategy.load_data(self.df) strategy.create_objects() # Checking to see whether trading is on or off. If it is off then it will not enter while loop. self.check_status() while self.start != False: diff = Helper.calculate_minute_disparity(self.df, tf) if round(time.time() % 60, 1) == 0 and diff <= tf: # Getting the most up-to-date row of the <tf>-min candlestick dfrow, high, low, volume, open_price, open_date = self.make_row( high, low, volume, count, open_price, open_date) if debug == True: print(dfrow) print(f"{tf - diff} minutes left") # Updating moving averages strategy.load_data(self.df.append(dfrow)) strategy.create_objects() # Checking only Rule 2, because it needs a minute by minute check. # Second condition is making sure that there is no existing position if strategy.rule_2_buy_enter( -1, sensitivity) and self.get_position( self.symbol) == 0: trade_info = executor.enter_market(self.symbol, "BUY", 2) elif strategy.rule_2_short_enter( -1, sensitivity) and self.get_position( self.symbol) == 0: trade_info = executor.enter_market(self.symbol, "SELL", 2) # Saves CPU usage, waits 5 seconds before the next minute time.sleep(50) count += 1 elif diff > tf: # Updating data using Binance API instead of appending the completed final row from the while loop self._update_data(math.floor(diff), "BTCUSDT") # Updating Moving averages strategy.load_data(self.df) strategy.create_objects() # Checks for the rest of the rules if strategy.rule_2_short_stop(-1) and self.get_position(self.symbol) < 0 and \ executor.live_trade_history.last_trade().rule == 2: trade_info = executor.exit_market( self.symbol, 2, self.get_position(self.symbol)) elif strategy.rule_2_buy_stop(-1) and self.get_position(self.symbol) > 0 and \ executor.live_trade_history.last_trade().rule == 2: trade_info = executor.exit_market( self.symbol, 2, self.get_position(self.symbol)) elif strategy.rule_1_buy_enter(-1) and self.get_position( self.symbol) == 0: trade_info = executor.enter_market(self.symbol, "BUY", 1) elif strategy.rule_1_buy_exit(-1) and self.get_position( self.symbol) > 0: trade_info = executor.exit_market( self.symbol, 1, self.get_position(self.symbol)) elif strategy.rule_1_short_enter(-1) and self.get_position( self.symbol) == 0: trade_info = executor.enter_market(self.symbol, "SELL", 1) elif strategy.rule_1_short_exit(-1) and self.get_position( self.symbol) < 0: trade_info = executor.exit_market( self.symbol, 1, self.get_position(self.symbol)) elif strategy.rule_3_buy_enter(-1) and self.get_position( self.symbol) == 0: trade_info = executor.enter_market(self.symbol, "BUY", 3) elif strategy.rule_3_short_enter(-1) and self.get_position( self.symbol) == 0: trade_info = executor.enter_market(self.symbol, "SELL", 3) # Resetting candlesticks high, low, volume = [], [], [] count = 0 time.sleep(1) if debug == True: print(diff) print("next") self.check_status() return "Trading Stopped"
class BinanceWrapperAPI(WrapperAPI): # (n) means that parameter is necessary def __init__(self, pair, target, precision): self.pair = pair self.precision = precision self.target = target self.client = Client(keys.binance_api_key, keys.binance_api_secret) def get_my_balance_coin(self, coin): balances = self.client.futures_account_balance() balance = find(lambda v: v['asset'] == coin, balances) return float(balance['withdrawAvailable']) if balance else 0 def get_mycoin(self): return self.get_my_balance_coin('USDT') def get_position_targ(self) -> Tuple[float, bool]: risks = self.client.futures_position_information(symbol=self.target) position = find(lambda v: v['symbol'] == self.pair, risks) amt = float(position['positionAmt']) return abs(amt), amt >= 0 def get_balance_interest(self): amount, _ = self.get_position_targ() return amount def buy_order(self, amount: float): return self.post_order_market("BUY", amount) def sell_order(self, amount: float): return self.post_order_market("SELL", amount) def post_order_market(self, side: str, quantity: float): try: res = self.client.futures_create_order( symbol=self.pair, side=side, type="MARKET", # positionSide=pside, quantity=roundt(quantity, self.precision)) return [True, res['orderId']] except BinanceAPIException as e: raise (e) return [False, e] def get_open_orders(self): return self.client.get_open_orders(symbol=self.pair) def get_position(self) -> Literal["none", "long", "shor"]: amount, posi = self.get_position_targ() if amount == 0: return 'none' return "long" if posi else "shor" def get_ask(self): return float(self.client.get_ticker(symbol=self.pair)['askPrice']) def get_bid(self): return float(self.client.get_ticker(symbol=self.pair)['bidPrice']) def get_order_status(self, id) -> Literal["COMP", "NEW", "EXPIRE"]: try: res = self.client.futures_get_order(symbol=self.pair, orderId=id) if res['status'] == "FILLED": return "COMP" # other: CANCELED, PARTIALLY_FILLED return "NEW" except BinanceAPIException as e: return "EXPIRE"
class Orders: def __init__(self, SYMBOL, LEWAR, PYRAMID_MAX, ORDER_SIZE, is_it_for_real=False): self.SYMBOL = SYMBOL self.LEWAR = LEWAR self.PYRAMID_MAX = PYRAMID_MAX self.ORDER_SIZE = ORDER_SIZE self.side = {"LONG": 'BUY', "SHORT": 'SELL'} self.side_reversed = { "SHORT": 'BUY', "LONG": 'SELL', "BUY": 'SELL', "SELL": 'BUY' } if is_it_for_real: with open("api_keys.txt") as d: api_keys = d.readlines() api_keys[0] = api_keys[0].strip() self.client = Client(api_keys[0], api_keys[1]) self.zagranie_list = [] try: client.futures_change_margin_type(symbol=self.SYMBOL + 'USDT', marginType="ISOLATED") except: pass self.client.futures_change_leverage(symbol=self.SYMBOL + 'USDT', leverage=self.LEWAR) def update(self): l = len(self.zagranie_list) for i, zagranie in enumerate(self.zagranie_list[::-1]): zagranie.update() if zagranie.state == 'closed': zagranie.close() del zagranie_list[l - 1 - i] def order_size(self, price, balance): #return 0.6**pyramid*ORDER_SIZE*balance*LEWAR/price return self.ORDER_SIZE * balance * self.LEWAR / price def get_balance(self): a = self.client.futures_account_balance() for e in a: if e['asset'] == 'USDT': return float(e['withdrawAvailable']) def positionInfo(self): a = self.client.futures_position_information() for e in a: if e['symbol'] == self.SYMBOL + 'USDT': return e def create_order(self, side, price=None, TARGET_CHANGE=True): print(f"Opening {side}") if len(self.zagranie_list) >= self.PYRAMID_MAX: print("Pyramid max reached") return None balance = self.get_balance() if price: order = self.client.futures_create_order( symbol=self.SYMBOL + 'USDT', side=self.side[side], type='LIMIT', timeInForce=TIME_IN_FORCE_GTC, quantity=self.order_size(price, balance), price=price, newOrderRespType="RESULT") else: order = self.client.futures_create_order( symbol=SYMBOL + 'USDT', side=self.side[side], type='MARKET', quantity=self.order_size( self.price, balance), #JAKIS DOMYSLMY PRICE TRZRBA DAC newOrderRespType="RESULT") if TARGET_CHANGE: if side == "LONG": oco_limit_price = self.price * (1 + TARGET_CHANGE) oco_stop_price = self.price * (1 - TARGET_CHANGE) elif side == "SHORT": oco_limit_price = self.price * (1 - TARGET_CHANGE) oco_stop_price = self.price * (1 + TARGET_CHANGE) zagranie = Zagranie(self, side, order['orderId'], oco_limit_price, oco_stop_price) self.zagranie_list.append(zagranie) return order['orderId'] def close(self, side): print(f"Closing {side}") l = len(self.zagranie_list) for i, zagranie in enumerate(self.zagranie_list[::-1]): if zagranie.side == side: zagranie.close() del zagranie_list[l - 1 - i]
#callbackRate=1, ##reduceOnly='True' ) order_Sellsl_ID=order_sellsl['orderId'] print(order_sellsl) print("Or der IDsl: " + str(order_Sellsl_ID)) """ buy_price, sell_price, wr_macd_signal = implement_wr_macd_strategy( aapl['Close'], aapl['wr_14'], aapl['macd'], aapl['macd_signal']) print("la señal vale:", wr_macd_signal) # POSITION close_price = aapl['Close'] if client.futures_position_information( symbol=symbolo)[-1]['positionAmt'] != "0": if client.futures_position_information( symbol=symbolo )[-1]['positionAmt'] != "0" and wr_macd_signal[ -1] == -1 and wr_macd_signal[-2] == 0 and COMPRANDO[0] == 1: close_price = client.futures_create_order(symbol=symbolo, side='SELL', type="MARKET", quantity=cantidad, redueOnly='True') telegram_send.send( messages=["ORDEN DE CERRADA COMPRA WILL+MAC", symbolo]) print("ORDEN DE CERRADA COMPRA WILL+MAC", symbolo) #if len(client.futures_get_open_orders(symbol=symbolo))!=0: #client.futures_cancel_all_open_orders(symbol=symbolo) elif client.futures_position_information(
while datetime.now() < (launching_time + duration) or float(dfMarket.loc[dfMarket.index[-1], 'current_position']) != 0.0: apiMarkPrice = client.futures_mark_price(symbol = symbol) time = datetime.fromtimestamp(apiMarkPrice['time']/1000).strftime("%Y-%m-%d %H:%M:%S") price = apiMarkPrice['markPrice'] current_values = pd.Series([0]*len(column_names),column_names) dfMarket.loc[len(dfMarket)+1] = current_values dfMarket.loc[dfMarket.index[-1], 'Time'] = time dfMarket.loc[dfMarket.index[-1], 'Mean'] = price dfMarket['SMA_3']= dfMarket.iloc[:,1].rolling(window=20).mean() dfMarket['SMA_10']= dfMarket.iloc[:,1].rolling(window=100).mean() dfMarket['SMA_50']= dfMarket.iloc[:,1].rolling(window=300).mean() dfMarket.loc[dfMarket.index[-1], 'total'] = client.futures_account()['totalMarginBalance'] dfMarket.loc[dfMarket.index[-1], 'current_position'] = client.futures_position_information()[0]['isolatedMargin'] dfMarket.loc[dfMarket.index[-1], 'Argent Fixe']= client.futures_account()['maxWithdrawAmount'] dfMarket.loc[dfMarket.index[-1], 'Solde Net'] = client.futures_account()['totalWalletBalance'] try : dfMarket.loc[dfMarket.index[-1], '%PnL'] = (float(client.futures_position_information()[0]['markPrice'])/float(client.futures_position_information()[0]['entryPrice']) -1)* float(client.futures_position_information()[0]['leverage'])*100 except : dfMarket.loc[dfMarket.index[-1], '%PnL'] = 0.0 #check if the last row of SMA_50 is not empty if not (pd.isnull(dfMarket.loc[dfMarket.index[-1], 'SMA_50'])): #STRATEGY dfMarket.loc[dfMarket.index[-1], 'signals'] = np.where((dfMarket.loc[dfMarket.index[-1], 'SMA_3'] > dfMarket.loc[dfMarket.index[-1], 'SMA_10'] and dfMarket.loc[dfMarket.index[-1], 'SMA_3'] > dfMarket.loc[dfMarket.index[-1], 'SMA_50']),1,0) dfMarket.loc[dfMarket.index[-1], 'positions'] = dfMarket.loc[dfMarket.index[-1], 'signals'] - dfMarket.loc[dfMarket.index[-2], 'signals'] print(float(dfMarket.loc[dfMarket.index[-1], 'current_position']) ) print(dfMarket.loc[dfMarket.index[-1], 'positions']) if dfMarket.loc[dfMarket.index[-1], 'positions'] == 1 and float(dfMarket.loc[dfMarket.index[-1], 'current_position']) == 0.0:
import binance from binance.client import Client from auth import key, secret import math import time from datetime import datetime, timedelta import requests import json from notify_run import Notify import tuning # Initiate notify and binance clients notify = Notify() client = Client(key, secret) position = client.futures_position_information()[0] if abs(float(position["positionAmt"])) < 0.001: holding = 0 holdingBTC = 0 lastbuy = 0 lastsell = 0 elif float(position['liquidationPrice']) < float(position['entryPrice']): holding = 1 holdingBTC = float(position['positionAmt']) lastbuy = float(position['entryPrice']) lastsell = 0 else: holding = -1 holdingBTC = float(position['positionAmt']) * -1 lastbuy = 0 lastsell = float(position['entryPrice'])
class Trader(): """ Purpose is to trade live on the Binance Crypto Exchange Attributes ----------- symbol: str client: Client object - From binance.py capital: float - represents how much money is in account leverage: float - how many multiples of your money you want to trade tf: int - timeframe df: pd.DataFrame - data that will be analyzed. Methods ---------- load_account set_leverage set_timeframe get_position get_necessary_data set_asset make_row load_existing_asset start_trading Please look at each method for descriptions """ def __init__(self, binance=True, qtrade=False, db=False, ib=False): if binance: self.binance = Client() # self.ftx = FtxClient() self.capital = None self.leverage = 1 self.tf = None self.executor = TradeExecutor() self.loader = _DataLoader(db=db, qtrade=qtrade, ib=ib) self.illustrator = Illustrator() self.strategy = FabStrategy() self.order_history = pd.DataFrame() self.trade_replay = [] self.precisions = { symbol_dic['symbol']: symbol_dic['quantityPrecision'] for symbol_dic in self.loader.binance.futures_exchange_info() ['symbols'] } def load_account(self, additional_balance: int = 0) -> str: """ Sign in to account using API_KEY and using Binance API """ # local_path_binance = os.path.dirname(os.path.dirname(os.path.abspath("__file__"))) + r"\local\binance_api.txt" # local_path_ftx = os.path.dirname(os.path.dirname(os.path.abspath("__file__"))) + r"\local\ftx_api.txt" # info_binance = pd.read_csv(local_path_binance).set_index('Name') # info_ftx = pd.read_csv(local_path_ftx).set_index('Name') API_KEY_BINANCE = config('API_KEY_BINANCE') API_SECRET_BINANCE = config('API_SECRET_BINANCE') # API_KEY_FTX = config('API_KEY_FTX') # API_SECRET_FTX = config('API_SECRET_FTX') self.binance = Client(api_key=API_KEY_BINANCE, api_secret=API_SECRET_BINANCE) # self.ftx = FtxClient(api_key=API_KEY_FTX, api_secret=API_SECRET_FTX) return "Connected to Binance" def get_capital(self, additional_balance=4944.2649865): initial_margin = float( self.binance.futures_account()['totalInitialMargin']) available_balance = float( self.binance.futures_account()['availableBalance']) self.capital = (initial_margin + available_balance + additional_balance) * self.leverage return self.capital def load_futures_trading_history(self, start_time, end_time=None): df = pd.DataFrame() ranges = Helper.determine_timestamp_positions(start_time, end_time, limit=1) for index, timestamp in enumerate(ranges): try: df = df.append( pd.DataFrame( self.binance.futures_account_trades( startTime=ranges[index], endTime=ranges[index + 1]))) except (IndexError) as e: pass return df.reset_index(drop=True) def get_tickers(self): tickers = pd.DataFrame(self.binance.futures_ticker()) tickers = tickers.rename( { 'lastPrice': 'close', 'openPrice': 'open', 'highPrice': 'high', 'lowPrice': 'low', 'openTime': 'timestamp' }, axis=1) tickers['date'] = Helper.millisecond_timestamp_to_datetime( tickers['timestamp']) return tickers[[ 'symbol', 'timestamp', 'date', 'open', 'high', 'low', 'close', 'volume' ]].set_index('symbol') def show_live_chart(self, symbol, tf, refresh_rate=1): df_tf_sma = self.show_current_chart(symbol, tf) while True: Helper.sleep(refresh_rate) clear_output(wait=True) df_tf_sma = self.show_current_chart(symbol, tf, data=df_tf_sma) def show_current_chart(self, symbol=None, tf=None, data=()): if len(data) == 0: data = self.set_asset(symbol, tf, max_candles_needed=375, futures=True) else: row = self.set_asset(symbol, tf, max_candles_needed=1, futures=True) if data['date'].iloc[-1] == row['date'].iloc[-1]: data = data[:-1].append(row) else: data = data.append(row) tf_data = data['tf'].iloc[0] df_tf = self.loader._timeframe_setter( data, tf // tf_data, keep_last_row=True) if tf != tf_data else data return self.illustrator.graph_df(df_tf, sma=False) def get_necessary_data(self, symbol: str, tf: int, max_candles_needed: int = 235) -> pd.DataFrame: df = pd.DataFrame() start_datetime = str( Helper.datetime_from_tf(tf, daily_1m_candles=1440, max_candles_needed=max_candles_needed)[0]) df = df.append( self.loader.get_binance_candles(symbol, tf, start_date=start_datetime)) df['symbol'] = [symbol for _ in range(len(df))] return df.drop_duplicates() def get_necessary_data_futures( self, symbol: str, tf: int, max_candles_needed: int = 245) -> pd.DataFrame: """ Gets the minimum necessary data to trade this asset. Note: This method is used as a way to tackle the 1000 candle limit that is currently on the Binance API. A discrete set of ~1000 group candles will be determined, and then the data will be extracted from each, using the _get_binance_futures_candles method, and then all of them will be merged together. Parameters: symbol: str - Symbol of price ticker Ex. "BTCUSDT", "ETHUSDT" tf: int - Timeframe wanted Ex. 1, 3, 5, 77, 100 max_candles_needed: int - Maximum candles needed in the desired timeframe Ex. 231, 770, 1440 :return pd.DataFrame of candlestick data """ now = time.time() df = pd.DataFrame() ranges = Helper.determine_candle_positions(max_candles_needed, tf) for i in range(len(ranges)): try: df = df.append( self.loader._get_binance_futures_candles( symbol, tf, int(ranges[i]), int(ranges[i + 1]), now)) except IndexError: pass df['symbol'] = [symbol for _ in range(len(df))] return df.drop_duplicates() def get_single_asset_signals(self, screener, symbol, tf, enter=True, shift=0): df = self.set_asset(symbol, tf, max_candles_needed=245) binance_df = int(df['tf'].iloc[0]) df_tf = self.loader._timeframe_setter(df, tf // binance_df, keep_last_row=True, shift=shift) strat = FabStrategy() df_ma = strat.load_data(df_tf) return df_ma, screener._check_for_tf_signals(df_ma, max_candle_history=10, enter=enter) def set_asset(self, symbol: str, tf: int, max_candles_needed: int = 245, futures=False) -> pd.DataFrame: binance_tf = Helper.find_greatest_divisible_timeframe(tf) new_max_candles = max_candles_needed * (tf // binance_tf) if futures: df = self.get_necessary_data_futures(symbol, binance_tf, new_max_candles) return df df = self.get_necessary_data(symbol, binance_tf, new_max_candles) self.df, self.tf, self.symbol = df, tf, symbol return df def get_current_trade_progress(self, key=()) -> pd.DataFrame: if len(key) == 0: key = self.key df = pd.DataFrame(self.binance.futures_position_information()) df[['positionAmt', 'entryPrice', 'markPrice']] = df[['positionAmt', 'entryPrice', 'markPrice']].astype(float) df = df[df['positionAmt'] != 0][[ 'symbol', 'positionAmt', 'entryPrice', 'markPrice' ]].reset_index(drop=True) df = df.rename( { 'positionAmt': 'size', 'entryPrice': 'enter price', 'markPrice': 'current price' }, axis=1) df['side'] = np.where(df['size'] > 0, 'BUY', 'SELL') df['size'] = df['size'].abs() df['usd size'] = df['size'] * df['enter price'] short_profitability = ( (0.9996**2) * (2 - df['current price'] / df['enter price']) - 1) * 100 long_profitability = ( (0.9996**2) * (df['current price'] / df['enter price']) - 1) * 100 df['pnl (%)'] = np.where(df['side'] == 'SELL', short_profitability, long_profitability) df['pnl (USD)'] = df['usd size'] * (df['pnl (%)'] / 100) df['share'] = df['usd size'] / df['usd size'].sum() df = df.round(2) df = df[[ 'symbol', 'side', 'enter price', 'size', 'usd size', 'current price', 'pnl (%)', 'pnl (USD)', 'share' ]] current_positions = df if type(key) != type(None): recent_signals = pd.read_csv( r"C:\Users\haseab\Desktop\Python\PycharmProjects\FAB\local\Workers\Recent signals.txt" ) current_positions['tf'] = [ int(recent_signals.set_index('symbol').loc[symbol, 'tf']) for symbol in current_positions['symbol'].values ] current_positions = self.key[['win loss rate', 'avg pnl']].reset_index().merge( current_positions, on=['symbol', 'tf']) self.current_positions = current_positions return current_positions def _check_trade_close(self, screener, current_positions): trades_to_close = [] for symbol, tf in current_positions.reset_index().set_index( ['symbol', 'tf']).index: try: df_tf = screener.df_dic[(symbol, tf)] df_ma = self.strategy.load_data(df_tf) display(Markdown(f"## {symbol} {tf}")) display(self.illustrator.graph_df(df_ma[-50:], flat=True)) enter_signal, enter_x_last_row, *rest = screener._check_for_tf_signals( df_tf, max_candle_history=10, enter=True) close_signal, exit_x_last_row, *rest = screener._check_for_tf_signals( df_tf, max_candle_history=10, enter=False) self.df_tf, self.close_signal, self.exit_x_last_row = df_tf, close_signal, exit_x_last_row, if close_signal: # raise Exception(close_signal, exit_x_last_row, enter_x_last_row) if enter_x_last_row and exit_x_last_row < enter_x_last_row: continue last_entry_date = pd.Timestamp( self.recent_signals.reset_index().set_index( ['symbol', 'tf']).loc[(symbol, tf), 'date']) last_exit_date = self.df['date'].iloc[self.exit_x_last_row] if last_entry_date > last_exit_date: print('failed') continue trades_to_close.append(symbol) print(f'{symbol} Trade Closed') except AttributeError as e: return [] return trades_to_close def trade_free_capital(self, executor, current_positions, remaining_to_invest, trades_to_enter): print("-----------------------------------------------") print(f"Free capital: {remaining_to_invest} USD") print("-----------------------------------------------", end="\n\n\n") final_trades_to_enter = self.check_against_max_trade_size( trades_to_enter, current_positions, self.max_trade_size) self.order_history = self.order_history.append( executor.enter_market(client=self.binance, symbol_side_rule=final_trades_to_enter, remaining_capital=remaining_to_invest, number_of_trades=len(trades_to_enter), reason='free_capital', max_trade_size=self.max_trade_size)) display(self.order_history.tail(len(trades_to_enter))) self.order_history.to_csv("FAB Order History.csv") self.trade_replay.append( self.get_current_trade_progress(printout=False)) def calculate_remaining_capital(self, current_positions, capital): total_invested = current_positions['usd size'].sum( ) if len(current_positions) > 0 else 0 remaining_to_invest = capital - total_invested return remaining_to_invest def get_positions_amount(self, list_of_symbols: list, full_close=False, divide_by=1, max_trades=3) -> list: """ Gets the total amount of current position for a given symbol Parameters: ------------ symbol: str Ex. "BTCUSDT" "ETHUSDT" Returns float Ex. If a 1.000 BTC position is open, it will return 1.0 """ # [{'symbol':'CHRUSDT', 'positionAmt':1}] position_values = {} for symbol in list_of_symbols: last_price = float( self.binance.futures_mark_price(symbol=symbol)['markPrice']) for coin_futures_info in self.binance.futures_position_information( ): if coin_futures_info['symbol'] == symbol: amount = float(coin_futures_info["positionAmt"]) amount = round(amount, self.precisions[symbol]) if divide_by > 1: amount = round(amount - amount / divide_by, self.precisions[symbol]) position_values[ symbol] = amount if amount > 0 else 10**( -self.precisions[symbol]) if full_close: position_values[symbol] = amount else: return position_values return position_values def get_key(self, essential_metrics, win_loss_threshold=0.350, pnl_threshold=1.025, data_point_threshold=5): key = essential_metrics[ essential_metrics['amount of data'] >= data_point_threshold] key = key[key['avg pnl'] > pnl_threshold] key = key[key['win loss rate'] > win_loss_threshold] key['win loss rate'] = key['win loss rate'].round(1) key['wnl drawdown'] = 0.4 * key['win loss rate'] + 0.6 * key[ 'longest drawdown'] master_key = key.reset_index().set_index(['symbol', 'tf']).sort_values( 'wnl drawdown', ascending=False) # master_key = key.reset_index().set_index(['symbol', 'tf']).sort_values(by=['win loss rate', 'avg pnl'], ascending=[False, False]) self.key = master_key return master_key def _profit_optimization(self, key, current_positions, trades_to_enter, number_of_trades): """ Returns: final_trades_to_enter: Ex. [('ZILUSDT', "Short", 'Rule 2'), ('BTCUSDT', 'Long', 'Rule 1'), ("ETHUSDT", "Short", 'Rule 3')] trades_to_close: Ex. ['ADAUSDT'] """ print() print("Optimizing Trades:... ") temp_metrics = key.reset_index().set_index( ['symbol', 'side', 'rule no']) trades_of_interest = [(symbol, int(temp_metrics.loc[(symbol, side, rule), 'tf'])) for symbol, side, rule in trades_to_enter if (symbol, side, rule) in temp_metrics.index] if not trades_of_interest: print("Didn't make it past key filter", end='\n\n') return [], [] current_position_symbols_tf_pair = Helper().nparray_to_tuple( current_positions.reset_index()[['symbol', 'tf']].values) all_symbols = list( set(trades_of_interest).union( set(current_position_symbols_tf_pair))) self.current_position_symbols_tf_pair, self.trades_of_interest, self.all_symbols = current_position_symbols_tf_pair, trades_of_interest, all_symbols self.top_x_symbols = sorted( all_symbols, key=lambda x: (key.loc[x, 'win loss rate'], key.loc[x, 'avg pnl']), reverse=True)[:number_of_trades] partial_trades_to_enter = list( set(self.top_x_symbols).intersection( set(trades_of_interest)).difference( set(current_position_symbols_tf_pair))) trades_to_close = list( set(current_position_symbols_tf_pair).difference( set(self.top_x_symbols))) trades_to_close = [symbol for symbol, tf in trades_to_close] final_trades_to_enter = [ (symbol, side, rule) for (symbol, tf), (symbol, side, rule) in zip(trades_of_interest, trades_to_enter) if (symbol, tf) in partial_trades_to_enter ] for tup in trades_of_interest: if tup in final_trades_to_enter: raise Exception(tup, trades_of_interest, final_trades_to_enter) return trades_to_close, final_trades_to_enter def get_dividing_factor(self, current_positions, final_trades_to_enter, number_of_trades): dividing_factor = (len(current_positions) + len(final_trades_to_enter)) / len(current_positions) minimum_threshold = current_positions['usd size'].sum() * 0.95 // len( current_positions) if dividing_factor > 1 and minimum_threshold > ( self.capital) // number_of_trades: print(minimum_threshold, (self.capital) // number_of_trades) return dividing_factor return 1 def optimize_trades(self, executor, current_positions, trades_to_enter, number_of_trades): if len(current_positions) != 0: self.position_capital = self.get_capital() trades_to_close, final_trades_to_enter = self._profit_optimization( self.key, current_positions, trades_to_enter, number_of_trades) if trades_to_close: self.order_history = self.order_history.append( executor.exit_market(self.binance, self.get_positions_amount( trades_to_close, full_close=True), reason='lower rank')) display(self.order_history) current_positions = self.get_current_trade_progress( printout=False) if final_trades_to_enter: dividing_factor = self.get_dividing_factor( current_positions, final_trades_to_enter, number_of_trades) positions_amount = self.get_positions_amount( current_positions['symbol'].values, divide_by=dividing_factor, max_trades=number_of_trades) if positions_amount: self.order_history = self.order_history.append( executor.exit_market(self.binance, positions_amount, reason='making space')) self.position_capital = self.get_capital() final_trades_to_enter = self.check_against_max_trade_size( final_trades_to_enter, current_positions, self.max_trade_size) self.remaining_capital = self.calculate_remaining_capital( current_positions, self.position_capital) self.order_history = self.order_history.append( executor.enter_market(self.binance, final_trades_to_enter, self.remaining_capital, len(final_trades_to_enter), reason='higher rank', max_trade_size=self.max_trade_size)) display(self.order_history) else: print("No Trades made") self.trades_to_close, self.final_trades_to_enter = trades_to_close, final_trades_to_enter self.order_history.to_csv("FAB Order History.csv") self.trade_replay.append( self.get_current_trade_progress(printout=False)) def close_any_old_trades(self, screener, executor, current_positions): if len(current_positions) == 0: return False trades_to_close = self._check_trade_close(screener, current_positions) if trades_to_close: self.order_history = self.order_history.append( executor.exit_market(self.binance, self.get_positions_amount( trades_to_close, full_close=True), reason='trade closed')) def update_trades_to_enter(self, current_positions, trades_to_enter): if len(trades_to_enter) == 0: trades_to_enter = [(symbol, self.executor.dic[side], None) for symbol, side in current_positions[ ['symbol', 'side']].values] return trades_to_enter def check_against_max_trade_size(self, trades_to_enter, current_positions, max_trade_size): final_trades_to_enter = [] for symbol, side, rule in trades_to_enter: if symbol in current_positions[ 'symbol'].values and current_positions.set_index( 'symbol').loc[symbol, 'usd size'] >= max_trade_size * 0.99: print('worked') continue final_trades_to_enter.append((symbol, side, rule)) return final_trades_to_enter def monitor_fab(self, screener, df_metrics, tfs, number_of_trades=3, leverage=1, recency=-1, additional_balance=0, max_trade_size=None): # self.order_history = pd.DataFrame() self.load_account() executor = self.executor self.leverage = leverage essential_metrics = Helper.drop_extra_delays(df_metrics, screener.tf_delay_match) self.starting_capital = self.get_capital( additional_balance=additional_balance) self.max_trade_size = max_trade_size self.key = self.get_key(essential_metrics) # self.key.to_csv ('Optimization Function Key.csv') now = Helper.current_minute_datetime() while True: if datetime.now() >= now + pd.Timedelta(1, 'minute'): clear_output(wait=True) print("Current time: \t", datetime.now(), end='\n\n') current_positions = self.get_current_trade_progress( key=self.key) if len(current_positions) > number_of_trades: raise Exception( f"You have more than {number_of_trades} active trades! Close first" ) if current_positions['usd size'].sum( ) > self.starting_capital * 1.5: raise Exception('We are too overexposed!', current_positions['usd size'].sum()) self.close_any_old_trades(screener, executor, current_positions) remaining_capital = self.calculate_remaining_capital( current_positions, self.capital) ## trades_to_enter is a list of lists (Ex. [('BANDUSDT', 'Short', 'Rule2'), ('BCHUSDT', 'Short', 'Rule 1')] ## df_recent_signals is a regular screener dataframe trades_to_enter, recent_signals = screener.top_trades( trader=self, df_metrics=df_metrics, tfs=tfs, n=number_of_trades, recency=recency) self.trades_to_enter, self.recent_signals = trades_to_enter, recent_signals print("Signals:") display(recent_signals) trades_left = number_of_trades - len(current_positions) if remaining_capital > 100 and trades_left > 0 and trades_to_enter: self.trade_free_capital(executor, current_positions, remaining_capital, trades_to_enter) elif remaining_capital > 100 and len(current_positions) != 0: trades_to_enter = self.update_trades_to_enter( current_positions=current_positions, trades_to_enter=trades_to_enter) self.trade_free_capital(executor, current_positions, remaining_capital, trades_to_enter) elif not trades_to_enter: Helper.sleep(60) now = Helper.current_minute_datetime() continue else: print("Leverage: ", self.leverage) print("Trades to Enter: ", trades_to_enter) print("Max Trades: ", number_of_trades) self.optimize_trades(executor, current_positions, trades_to_enter, number_of_trades) Helper.sleep(60) now = Helper.current_minute_datetime() print() Helper.output_loading()