[실무 강의] 파이썬으로 뚝딱! 코인/주식 API 실시간 가격 확인 및 첫 모의 자동매매 봇 만들기 (2026년 최신) 완벽 마스터 튜토리얼

자, 이제 여러분은 2026년 최신 IT/코딩 실무 기술을 파이썬으로 직접 구현해 볼 시간입니다. 이 강의는 단순히 이론을 넘어, 실제 코인/주식 API를 활용하여 실시간 가격을 확인하고, 나아가 첫 모의 자동매매 봇을 직접 만들어보는 실전 튜토리얼입니다. 복잡한 개념은 잠시 접어두고, 핵심 기능 구현에 집중하여 누구나 쉽게 따라 할 수 있도록 설계되었습니다. 준비되셨다면, 바로 시작해봅시다.

학습 목표

이 강의를 통해 여러분은 다음 목표를 달성하게 될 것입니다.

  • 파이썬 requests 라이브러리를 활용하여 코인 및 주식 API에 접근하는 방법을 이해합니다.
  • 업비트(Upbit) 또는 한국투자증권(KIS Developers)과 같은 주요 거래소/증권사의 공시 API를 통해 실시간 시세 데이터를 조회하는 코드를 작성합니다.
  • API 키 관리의 중요성을 인식하고, 안전하게 설정 파일을 다루는 기본 방법을 습득합니다.
  • 가장 기본적인 매매 전략(예: 특정 가격 도달 시 매수/매도)을 파이썬 코드로 구현하여 첫 모의 자동매매 봇을 만듭니다.
  • 실시간 데이터 기반의 자동화된 의사결정 로직의 기초를 다집니다.

사전 준비 사항

본격적인 실습에 앞서, 원활한 진행을 위해 다음 환경을 설정해 주셔야 합니다. 이는 2026년 현재 가장 보편적이고 효율적인 개발 환경입니다.

개발 환경 설정

  • 운영체제(OS): Windows 10/11, macOS (최신 버전 권장), 또는 Ubuntu 22.04 LTS 이상의 Linux 환경.
  • 통합 개발 환경(IDE): Visual Studio Code (VS Code) 최신 버전 설치를 강력히 권장합니다. 파이썬 확장팩(Python Extension)도 함께 설치해 주세요.
  • 파이썬(Python) 버전: Python 3.10 이상 버전을 설치해야 합니다. 2026년 현재 안정성과 성능 면에서 가장 권장되는 버전입니다. (예: Python 3.11 또는 3.12)

필수 라이브러리 설치

터미널 또는 명령 프롬프트(VS Code의 터미널 사용 권장)를 열고 다음 명령어를 실행하여 필요한 파이썬 라이브러리들을 설치합니다.

pip install requests pyjwt python-dotenv pandas
  • requests: HTTP 요청을 보내 API와 통신하기 위한 필수 라이브러리입니다.
  • pyjwt: API 인증에 필요한 JWT(JSON Web Token)를 생성하는 데 사용됩니다. (주로 업비트 등 일부 API에서 필요)
  • python-dotenv: API 키와 같은 민감한 정보를 환경 변수로 안전하게 관리하기 위해 사용합니다.
  • pandas: 데이터 분석 및 처리에 유용하며, 시세 데이터를 효율적으로 다룰 때 사용될 수 있습니다. (선택 사항이지만 강력 권장)

성공적으로 설치가 완료되면, 이제 코딩을 시작할 모든 준비가 끝났습니다.

단계별 실습 과정

이제 본격적으로 코드를 작성하며 실시간 시세 확인 및 모의 자동매매 봇을 만들어봅시다.

1. API 키 발급 및 설정

실제 API에 접근하려면 API 키가 필요합니다. 업비트(Upbit) 또는 한국투자증권(KIS Developers)에 접속하여 개발자 계정을 생성하고 API 키(Access Key, Secret Key)를 발급받으세요. 절대 이 키를 코드에 직접 노출하거나 Git 저장소에 올리지 마세요! .env 파일을 통해 안전하게 관리하는 방법을 사용합니다.

프로젝트 폴더에 .env 파일을 생성하고 발급받은 키를 다음과 같이 저장합니다.

# .env 파일 내용
UPBIT_ACCESS_KEY="YOUR_UPBIT_ACCESS_KEY"
UPBIT_SECRET_KEY="YOUR_UPBIT_SECRET_KEY"

# 한국투자증권 API 키 (모의투자용 예시)
KIS_APP_KEY="YOUR_KIS_APP_KEY"
KIS_APP_SECRET="YOUR_KIS_APP_SECRET"

그리고 이 키들을 불러올 config.py 파일을 생성합니다.

# config.py

import os
from dotenv import load_dotenv

load_dotenv()

UPBIT_ACCESS_KEY = os.getenv("UPBIT_ACCESS_KEY")
UPBIT_SECRET_KEY = os.getenv("UPBIT_SECRET_KEY")

KIS_APP_KEY = os.getenv("KIS_APP_KEY")
KIS_APP_SECRET = os.getenv("KIS_APP_SECRET")

if not all([UPBIT_ACCESS_KEY, UPBIT_SECRET_KEY]):
    print("경고: Upbit API 키가 설정되지 않았습니다. .env 파일을 확인하세요.")

if not all([KIS_APP_KEY, KIS_APP_SECRET]):
    print("경고: KIS API 키가 설정되지 않았습니다. .env 파일을 확인하세요.")

2. 실시간 가격 조회 구현

2.1. 코인 가격 조회 (Upbit API 예시)

업비트 API를 사용하여 특정 코인의 실시간 가격을 조회하는 코드를 작성합니다. 여기서는 REST API를 사용하여 현재가를 가져오는 간단한 예시를 보여드립니다.

upbit_price_checker.py 파일을 생성합니다.

# upbit_price_checker.py

import requests
import jwt
import uuid
import hashlib
from urllib.parse import urlencode
import config

def get_upbit_ticker(market_code):
    """
    업비트에서 단일 종목의 현재가를 조회합니다.
    :param market_code: 시장 코드 (예: 'KRW-BTC', 'KRW-ETH')
    :return: 현재가 정보를 담은 딕셔너리 또는 None
    """
    server_url = "https://api.upbit.com/v1/ticker"
    headers = {"Accept": "application/json"}
    params = {"markets": market_code}

    try:
        response = requests.get(server_url, params=params, headers=headers)
        response.raise_for_status() # HTTP 오류 발생 시 예외 발생
        data = response.json()
        if data:
            return data[0] # 첫 번째 종목 정보 반환
        else:
            print(f"[{market_code}] 종목 정보를 찾을 수 없습니다.")
            return None
    except requests.exceptions.RequestException as e:
        print(f"API 요청 오류 발생: {e}")
        return None

if __name__ == "__main__":
    # 조회할 코인 목록
    coin_list = ["KRW-BTC", "KRW-ETH", "KRW-XRP"]

    print("--- Upbit 실시간 코인 가격 조회 (2026년 최신) ---")
    for coin in coin_list:
        ticker_info = get_upbit_ticker(coin)
        if ticker_info:
            print(f"[{ticker_info['market']}] 현재가: {ticker_info['trade_price']:,} KRW, "
                  f"전일 대비: {ticker_info['signed_change_rate'] * 100:.2f}% ({ticker_info['signed_change_price']:,} KRW)")
        else:
            print(f"[{coin}] 가격 정보를 가져오지 못했습니다.")
    print("--------------------------------------------------")

위 코드를 실행하면 지정된 코인들의 현재 가격 정보를 터미널에 출력합니다.

2.2. 주식 가격 조회 (Kis Developers API 예시 – 모의투자)

한국투자증권(KIS Developers) API는 인증 절차가 다소 복잡하지만, 여기서는 모의투자 환경에서 필요한 현재가 조회 로직의 기초를 다룹니다. 실제 주식 API는 Access Token 발급, 계좌 조회 등 복잡한 과정을 거쳐야 하므로, 여기서는 개념적인 접근과 함께 모의 투자 시뮬레이션에 필요한 데이터를 가져오는 방법을 설명합니다. (실제 KIS API 연동은 Access Token 발급 후 진행되어야 합니다.)

kis_price_checker.py 파일을 생성합니다.

# kis_price_checker.py

import requests
import config

# KIS Developers API 기본 URL (모의투자용)
# 실제 환경과 모의투자 환경의 URL이 다릅니다.
# 2026년 현재 KIS API 모의투자 URL은 변경될 수 있으니 공식 문서를 확인하세요.
KIS_BASE_URL = "https://openapivts.koreainvestment.com:29443" # 모의투자용 URL 예시

def get_kis_stock_price(stock_code):
    """
    KIS Developers API를 통해 주식 현재가를 조회합니다.
    이 함수는 실제 API 호출을 위한 기본 구조를 보여줍니다.
    실제 사용 시에는 Access Token 발급 및 인증 헤더 설정이 선행되어야 합니다.
    :param stock_code: 종목 코드 (예: '005930' for 삼성전자)
    :return: 현재가 정보를 담은 딕셔너리 또는 None
    """
    # API 호출을 위한 Access Token은 사전에 발급받아야 합니다.
    # 여기서는 예시를 위해 더미 토큰을 사용하거나, 직접 발급받은 토큰을 사용해야 합니다.
    # access_token = "YOUR_KIS_ACCESS_TOKEN" # 실제 토큰으로 교체 필요

    # KIS API의 경우, TR(Transaction) 코드와 함께 요청해야 합니다.
    # 'FHKST01010100'은 주식 현재가 일자별 시세 TR 코드 예시입니다.
    # 실제는 'FHKST01010100' 대신 현재가 조회를 위한 TR 코드를 사용해야 합니다.
    # 여기서는 단순 조회를 위한 예시이므로, 실제 API와는 다를 수 있습니다.
    path = "/uapi/domestic-stock/v1/quotations/inquire-price"
    url = f"{KIS_BASE_URL}{path}"

    headers = {
        "Content-Type": "application/json; charset=utf-8",
        "authorization": f"Bearer {config.KIS_APP_KEY}", # 실제 Access Token으로 변경
        "appkey": config.KIS_APP_KEY,
        "appsecret": config.KIS_APP_SECRET,
        "tr_id": "FHKST01010100", # 주식 현재가 조회 TR ID 예시 (실제 TR ID 확인 필요)
        "custtype": "P" # 개인
    }

    params = {
        "fid_cond_mrkt_div_code": "J", # J: 주식, F: 선물옵션
        "fid_input_iscd": stock_code # 종목코드
    }

    try:
        # KIS API는 GET 요청 시에도 body에 데이터를 담는 경우가 있습니다.
        # 여기서는 편의상 GET으로 처리하지만, 실제 API 명세를 따르는 것이 중요합니다.
        response = requests.get(url, headers=headers, params=params)
        response.raise_for_status()
        data = response.json()
        if data and data.get('rt_cd') == '0': # 성공 코드 확인
            return data.get('output')
        else:
            print(f"[{stock_code}] KIS API 요청 실패: {data.get('msg1')}")
            return None
    except requests.exceptions.RequestException as e:
        print(f"KIS API 요청 오류 발생: {e}")
        return None

if __name__ == "__main__":
    stock_list = ["005930", "035720", "000660"]

    print("--- KIS Developers 주식 가격 조회 (모의투자 예시) ---")
    print("참고: 이 코드는 실제 KIS API 연동을 위한 Access Token 발급 및 "
          "정확한 TR ID 설정이 필요합니다. 현재는 예시 구조만 제공합니다.")
    for stock in stock_list:
        # 실제 API 호출 없이 더미 데이터로 시뮬레이션
        # real_price_info = get_kis_stock_price(stock)
        # if real_price_info:
        #    print(f"[{stock}] 현재가: {real_price_info.get('stck_prpr')}")
        
        # 모의를 위한 임의 가격 설정
        import random
        mock_price = random.randint(50000, 100000) if stock == "005930" else random.randint(10000, 50000)
        print(f"[{stock}] 모의 현재가: {mock_price:,} KRW")
    print("--------------------------------------------------")

3. 첫 모의 자동매매 봇 구현

이제 앞서 배운 가격 조회 기능을 활용하여 아주 간단한 모의 자동매매 봇을 만들어봅니다. 여기서는 실제 거래 없이, 내부적으로 가상의 자산과 주문을 관리하는 형태로 구현합니다.

3.1. 거래 전략 수립

가장 단순한 전략을 사용합니다.

  • 매수 조건: 현재가가 특정 기준 가격(예: 5분 전 가격 또는 일중 최저가)보다 X% 이상 하락하면 매수.
  • 매도 조건: 보유 중인 종목의 현재가가 매수 가격보다 Y% 이상 상승하면 이익 실현 매도, 또는 Z% 이상 하락하면 손절매도.

이 튜토리얼에서는 ‘현재가가 특정 기준 이하로 떨어지면 매수, 매수가 대비 일정 수익률 이상이면 매도’ 전략을 구현해 보겠습니다.

3.2. 모의 매수/매도 로직 작성

simple_mock_bot.py 파일을 생성합니다.

# simple_mock_bot.py

import time
import random
from upbit_price_checker import get_upbit_ticker
# from kis_price_checker import get_kis_stock_price # 주식 모의봇을 만들 경우 사용

class MockTradingBot:
    def __init__(self, initial_krw=1000000):
        self.krw_balance = initial_krw # 초기 원화 잔고 100만원
        self.portfolio = {} # 보유 종목: {'KRW-BTC': {'avg_price': 50000000, 'volume': 0.01}}
        self.trade_logs = [] # 거래 기록
        print(f"모의 봇 시작! 초기 잔고: {self.krw_balance:,} KRW")

    def _log_trade(self, action, market, price, volume, amount):
        log_entry = {
            'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'),
            'action': action,
            'market': market,
            'price': price,
            'volume': volume,
            'amount': amount
        }
        self.trade_logs.append(log_entry)
        print(f"[거래 로그] {action} {market} - 가격: {price:,} KRW, 수량: {volume:.4f}, 금액: {amount:,} KRW")

    def buy_coin(self, market, current_price, target_krw_amount):
        if self.krw_balance < target_krw_amount:
            print(f"[매수 실패] 잔고 부족: {self.krw_balance:,} KRW")
            return

        volume_to_buy = target_krw_amount / current_price
        self.krw_balance -= target_krw_amount

        if market in self.portfolio:
            old_volume = self.portfolio[market]['volume']
            old_avg_price = self.portfolio[market]['avg_price']
            new_total_value = (old_volume * old_avg_price) + (volume_to_buy * current_price)
            new_total_volume = old_volume + volume_to_buy
            self.portfolio[market]['avg_price'] = new_total_value / new_total_volume
            self.portfolio[market]['volume'] = new_total_volume
        else:
            self.portfolio[market] = {'avg_price': current_price, 'volume': volume_to_buy}

        self._log_trade("매수", market, current_price, volume_to_buy, target_krw_amount)
        print(f"[{market}] 매수 완료. 현재 잔고: {self.krw_balance:,} KRW")

    def sell_coin(self, market, current_price, sell_volume=None):
        if market not in self.portfolio or self.portfolio[market]['volume'] == 0:
            print(f"[매도 실패] 보유 중인 {market}이(가) 없습니다.")
            return

        if sell_volume is None or sell_volume > self.portfolio[market]['volume']:
            sell_volume = self.portfolio[market]['volume'] # 전량 매도

        profit = (current_price - self.portfolio[market]['avg_price']) * sell_volume
        sell_amount = current_price * sell_volume
        self.krw_balance += sell_amount
        self.portfolio[market]['volume'] -= sell_volume

        if self.portfolio[market]['volume'] < 0.0001: # 소수점 이하 처리
            del self.portfolio[market]

        self._log_trade("매도", market, current_price, sell_volume, sell_amount)
        print(f"[{market}] 매도 완료 (수익: {profit:,.0f} KRW). 현재 잔고: {self.krw_balance:,} KRW")

    def run_strategy(self, market_code, buy_threshold_percent=2, sell_profit_percent=3, sell_loss_percent=-1):
        print(f"\n--- {market_code} 전략 실행 중 ---")
        ticker = get_upbit_ticker(market_code)
        if not ticker:
            print(f"[{market_code}] 가격 정보를 가져올 수 없습니다. 전략 중단.")
            return

        current_price = ticker['trade_price']
        print(f"[{market_code}] 현재가: {current_price:,} KRW")

        # 매수 로직
        if market_code not in self.portfolio or self.portfolio[market_code]['volume'] == 0:
            # 간단한 매수 조건: 전일 종가 대비 일정 비율 하락 시 매수
            # 실제 전략에서는 이동평균선, RSI 등 더 복잡한 지표를 사용합니다.
            prev_closing_price = ticker.get('prev_closing_price', current_price * 1.01) # 전일 종가가 없으면 현재가보다 약간 높게 가정
            price_change_rate = ((current_price - prev_closing_price) / prev_closing_price) * 100
            
            if price_change_rate <= -buy_threshold_percent: # X% 이상 하락 시 매수
                print(f"[{market_code}] {buy_threshold_percent}% 이상 하락 ({price_change_rate:.2f}%). 매수 시도.")
                self.buy_coin(market_code, current_price, self.krw_balance * 0.1) # 잔고의 10% 매수
            else:
                print(f"[{market_code}] 매수 조건 미달 (하락률: {price_change_rate:.2f}%).")
        # 매도 로직
        else:
            avg_price = self.portfolio[market_code]['avg_price']
            profit_rate = ((current_price - avg_price) / avg_price) * 100

            if profit_rate >= sell_profit_percent: # Y% 이상 수익 시 매도
                print(f"[{market_code}] {sell_profit_percent}% 이상 수익 ({profit_rate:.2f}%). 이익 실현 매도 시도.")
                self.sell_coin(market_code, current_price)
            elif profit_rate <= sell_loss_percent: # Z% 이상 손실 시 손절매도
                print(f"[{market_code}] {abs(sell_loss_percent)}% 이상 손실 ({profit_rate:.2f}%). 손절매도 시도.")
                self.sell_coin(market_code, current_price)
            else:
                print(f"[{market_code}] 매도 조건 미달 (수익률: {profit_rate:.2f}%).")

    def get_current_portfolio_value(self):
        total_value = self.krw_balance
        print("\n--- 현재 포트폴리오 ---")
        print(f"현금 잔고: {self.krw_balance:,} KRW")
        for market, data in self.portfolio.items():
            current_price = get_upbit_ticker(market)['trade_price'] if get_upbit_ticker(market) else data['avg_price']
            asset_value = current_price * data['volume']
            total_value += asset_value
            print(f"- {market}: 보유 수량 {data['volume']:.4f}, 평단가 {data['avg_price']:,} KRW, "
                  f"현재 가치 {asset_value:,.0f} KRW")
        print(f"총 자산 가치: {total_value:,.0f} KRW")
        print("----------------------")

if __name__ == "__main__":
    bot = MockTradingBot()
    target_coin = "KRW-ETH"

    print("\n--- 모의 자동매매 봇 5분 간격 실행 시작 (총 30분) ---")
    for i in range(6):
        print(f"\n[시뮬레이션 {i+1}회차]")
        bot.run_strategy(target_coin, buy_threshold_percent=0.5, sell_profit_percent=1.0, sell_loss_percent=-0.5)
        bot.get_current_portfolio_value()
        time.sleep(300) # 5분 대기 (실제는 더 짧게 또는 웹소켓 사용)

    print("\n--- 모의 자동매매 봇 시뮬레이션 종료 ---")
    print("최종 거래 기록:")
    for log in bot.trade_logs:
        print(log)

이 코드는 MockTradingBot 클래스를 정의하여 가상의 잔고와 포트폴리오를 관리합니다. run_strategy 메서드 안에서 get_upbit_ticker 함수를 호출하여 현재가를 가져오고, 미리 정해둔 매수/매도 조건에 따라 가상의 거래를 실행합니다. 실제 API 연동 시에는 upbit.buy_limit_order() 와 같은 함수를 사용하게 될 것입니다.

결과 확인

이제 작성한 스크립트들을 실행하여 결과를 확인해 봅시다.

1. 코인 가격 조회 스크립트 실행

터미널에서 다음 명령어를 실행합니다.

python upbit_price_checker.py

다음과 유사한 출력을 확인할 수 있습니다 (가격은 실시간으로 변동됩니다).

--- Upbit 실시간 코인 가격 조회 (2026년 최신) ---
[KRW-BTC] 현재가: 70,000,000 KRW, 전일 대비: 0.50% (350,000 KRW)
[KRW-ETH] 현재가: 4,000,000 KRW, 전일 대비: -1.20% (-48,000 KRW)
[KRW-XRP] 현재가: 800 KRW, 전일 대비: 2.10% (16 KRW)
--------------------------------------------------

2. 주식 가격 조회 스크립트 (모의) 실행

python kis_price_checker.py

모의 주식 가격이 출력되는 것을 확인할 수 있습니다.

--- KIS Developers 주식 가격 조회 (모의투자 예시) ---
참고: 이 코드는 실제 KIS API 연동을 위한 Access Token 발급 및 정확한 TR ID 설정이 필요합니다. 현재는 예시 구조만 제공합니다.
[005930] 모의 현재가: 75,000 KRW
[035720] 모의 현재가: 25,000 KRW
[000660] 모의 현재가: 15,000 KRW
--------------------------------------------------

3. 모의 자동매매 봇 실행

이제 가장 중요한 모의 자동매매 봇을 실행해 봅시다. 이 봇은 5분마다 한 번씩 전략을 실행하며, 총 30분 동안 동작합니다.

python simple_mock_bot.py

봇이 시작되면 현재 잔고와 함께 지정된 코인(예: KRW-ETH)에 대한 매수/매도 전략 실행 로그가 주기적으로 출력됩니다. 매수 조건이 충족되면 가상의 매수가 이루어지고, 이후 수익률에 따라 매도 또는 손절매가 발생할 수 있습니다. 최종적으로 시뮬레이션 종료 후에는 전체 거래 기록과 포트폴리오 현황을 확인할 수 있습니다.

이 강의를 통해 여러분은 2026년 최신 파이썬 기술을 활용하여 코인/주식 API 데이터를 다루고, 나아가 자신만의 자동매매 봇을 만드는 첫걸음을 성공적으로 내디뎠습니다. 실제 투자는 신중하게 접근해야 하지만, 이 경험은 여러분의 IT 실무 역량을 한층 강화하는 소중한 자산이 될 것입니다. 다음 강의에서 더 심화된 주제로 만나 뵙겠습니다!

댓글 남기기


Warning: getimagesize(): http:// wrapper is disabled in the server configuration by allow_url_fopen=0 in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Warning: getimagesize(http://imgnews.naver.net/image/025/2021/05/12/0003100986_001_20241007052226325.jpg): Failed to open stream: no suitable wrapper could be found in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Warning: getimagesize(): http:// wrapper is disabled in the server configuration by allow_url_fopen=0 in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Warning: getimagesize(http://imgnews.naver.net/image/5879/2026/02/09/0000116011_001_20260209190809431.png): Failed to open stream: no suitable wrapper could be found in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Warning: getimagesize(): http:// wrapper is disabled in the server configuration by allow_url_fopen=0 in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Warning: getimagesize(http://imgnews.naver.net/image/5728/2021/05/17/20210517143935092706cf2d78c68223385348_20210517144015962.jpg): Failed to open stream: no suitable wrapper could be found in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Fatal error: Uncaught ErrorException: md5_file(/hosting/apdldk/html/wp-content/litespeed/js/746d8b15b9022aa7adb05c25bc10e707.js.tmp): Failed to open stream: No such file or directory in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimizer.cls.php:148 Stack trace: #0 [internal function]: litespeed_exception_handler() #1 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimizer.cls.php(148): md5_file() #2 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimize.cls.php(845): LiteSpeed\Optimizer->serve() #3 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimize.cls.php(392): LiteSpeed\Optimize->_build_hash_url() #4 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimize.cls.php(265): LiteSpeed\Optimize->_optimize() #5 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimize.cls.php(226): LiteSpeed\Optimize->_finalize() #6 /hosting/apdldk/html/wp-includes/class-wp-hook.php(341): LiteSpeed\Optimize->finalize() #7 /hosting/apdldk/html/wp-includes/plugin.php(205): WP_Hook->apply_filters() #8 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/core.cls.php(464): apply_filters() #9 [internal function]: LiteSpeed\Core->send_headers_force() #10 /hosting/apdldk/html/wp-includes/functions.php(5481): ob_end_flush() #11 /hosting/apdldk/html/wp-includes/class-wp-hook.php(341): wp_ob_end_flush_all() #12 /hosting/apdldk/html/wp-includes/class-wp-hook.php(365): WP_Hook->apply_filters() #13 /hosting/apdldk/html/wp-includes/plugin.php(522): WP_Hook->do_action() #14 /hosting/apdldk/html/wp-includes/load.php(1308): do_action() #15 [internal function]: shutdown_action_hook() #16 {main} thrown in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimizer.cls.php on line 148