在Heroku上运行的Python-Binance培养瓶应用程序-内存泄漏

bejyjqdl  于 2023-03-18  发布在  Python
关注(0)|答案(1)|浏览(432)

我在Heroku上运行了一个Python-Binance flask 应用程序,我用它来接收来自TradingView的交易警报,并通过API在Binance上执行交易。
我有一个WebSocket设置来监控当前的市场价格,我使用一个函数来监控现货市场的“未平仓交易”,一旦达到止盈就关闭它们。
该应用程序工作正常,但我收到通知Heroku说:
2023年3月13日00:51:10.044146+00:00黑鹿[网页1]:错误R14(超出内存配额)2023-03- 13 T00:51:32.620715+00:00 heroku[web.1]:工艺运行mem= 632 M(123.5%)
我已经将问题隔离到check_tp()函数(当禁用时,我没有内存泄漏)。
有没有人知道是什么导致了泄漏以及如何解决它?我在下面附上了check_tp函数的代码:

def process_stream_data(binance_websocket_api_manager):
    while True:
        try:
            if binance_websocket_api_manager.is_manager_stopping():
                exit(0)
            oldest_data = binance_websocket_api_manager.pop_stream_data_from_stream_buffer()
            is_empty = is_empty_message(oldest_data)
            if is_empty:
                time.sleep(0.01)
            else:
                oldest_data_dict = json.loads(oldest_data)
                data = oldest_data_dict['data']
                #  Handle price change
                handle_price_change(symbol=data['s'], timestamp=data['T'], price=data['p'])
        except Exception as e:
            print(f"Error processing websocket stream data: {e}")
            # Attempt to reconnect to the websocket server
            print("Attempting to reconnect to websocket server...")
            binance_websocket_api_manager.stop_stream(oldest_data)
            binance_websocket_api_manager.start()
            time.sleep(5)  # Wait for 5 seconds before attempting to process stream data again

cache = redis.from_url(os.environ.get("REDIS_URL"))
def handle_price_change(symbol, timestamp, price):
    for s in the_pairs:
        if symbol==s:
            global_prices_write(symbol,price)
    last_execution_time = cache.get('last_execution_time')
    last_stream_check = cache.get('last_stream_check')
    current_time = time.time()
    if not last_execution_time or current_time - float(last_execution_time) >= 15:
        cache.set('last_execution_time', current_time)
        check_tp()
    if not last_stream_check or current_time - float(last_stream_check) >= 300:
        cache.set('last_stream_check', current_time)
        print(global_prices["BTCBUSD"],"STREAM CHECK OK")

def check_tp():
    #print(f"Initial memory usage: {process.memory_info().rss / 1024 / 1024:.2f} MB")
    #print("Checking TPs")
    opt = get_google_open_trades()
    #print(f"Memory usage after get_google_open_trades(): {process.memory_info().rss / 1024 / 1024:.2f} MB")
    sets = get_google_settings()
    #print(f"Memory usage after get_google_settings(): {process.memory_info().rss / 1024 / 1024:.2f} MB")
    for td in opt:
        if td[0]=="Account":
            continue
        perc = 0.0
        sym = td[4]
        order_id = td[2]
        acc = td[0].lower()
        price = td[5]

        for s in sets:
            if s[0].lower() == acc:
                take_profit = s[3]
                break
        
        price_float = 0.0
        price_float=float(price)
        TP = 0.0
        TP = float(take_profit)
        target = float(price_float) + ((float(price_float) * TP) / 100)

        if float(TP) > 0 and float(global_prices_read(sym)) >= target:
            bnan = Client(API_KEYS[acc], API_SECRETS[acc], tld='com')
            inf = bnan.get_my_trades(symbol=sym, orderId=order_id)
            trades = []
            for ele in inf:
                trades.append(ele)
            sell_commission = 0.0
            for td in trades:
                if td["orderId"] == order_id:
                    sell_commission += float(td["commission"])

            newquant = float(td['executedQty']) - float(sell_commission)
            rnd_quantity = format_quantity(sym, newquant, global_prices[sym],bnan)
            order_response = order(binance, "SELL", rnd_quantity, sym, td['clientOrderId'])
            if order_response['side'].upper() == "SELL":
                remove_google_open_trade(order_response['clientOrderId'])
            print(order_response)
    del opt
    del sets
    gc.collect()

enter image description here
该函数还引用了我对Google Sheets API的调用:

def get_google_open_trades():
    # Get all data from the sheet
    result = service.spreadsheets().values().get(
        spreadsheetId=spreadsheet_id,
        range=f'{open_trades_sheet_name}!A:F',  # specify range A:F to include all columns up to F
        valueRenderOption='UNFORMATTED_VALUE',
        dateTimeRenderOption='FORMATTED_STRING'
    ).execute()

    data = result.get('values', [])

    # Find the last row containing data
    last_row = len(data)

    while last_row > 0:
        row_data = data[last_row-1]
        if any(row_data):  # check if the row contains any non-empty cells
            break
        last_row -= 1

    # Trim any empty rows after the last row containing data
    data = data[:last_row]

    # Print the data to the console
    if not data:
        print('No data found.')
    else:
        return data
    
def get_google_settings():
    # Get the last row and column of data in the sheet
    result = service.spreadsheets().values().get(
        spreadsheetId=spreadsheet_id,
        range=f'{sheet_name}!E:E',
        valueRenderOption='UNFORMATTED_VALUE',
        dateTimeRenderOption='FORMATTED_STRING'
    ).execute()
    last_row = len(result.get('values', []))

    result = service.spreadsheets().values().get(
        spreadsheetId=spreadsheet_id,
        range=f'{sheet_name}!1:1',
        valueRenderOption='UNFORMATTED_VALUE',
        dateTimeRenderOption='FORMATTED_STRING'
    ).execute()
    last_col = len(result.get('values', [])[0])

    # Update the range to fetch all data up to the last row and column
    range_name = f"{sheet_name}!A1:F{last_row}"

    # Get all data from the sheet
    result = service.spreadsheets().values().get(
        spreadsheetId=spreadsheet_id,
        range=range_name,
        valueRenderOption='UNFORMATTED_VALUE',
        dateTimeRenderOption='FORMATTED_STRING'
    ).execute()

    data = result.get('values', [])

    # Print the data to the console
    if not data:
        print('No data found.')
    else:
        return data

我尝试过使用gc.collect()强制垃圾收集,并限制函数运行的次数。

0yycz8jy

0yycz8jy1#

https://devcenter.heroku.com/articles/limits#dynos
Heroku记忆力有限
现有程序可能已经有了比较高的内存,当check_tp在执行过程中超过负荷的时候,check_tp里面临时变量比较多,内存比较高频,可以适当的优化程序,当然更有可能是因为opt = get_google_open_trades()sets = get_google_settings(),建议检查一下,当执行到这里的时候,在这两个之前和之后的存储容量。
这只是一些建议,希望能对你有所帮助

相关问题