import numpy as np
import math
import akshare as ak
import os
from datetime import datetime, timedelta, date
import pandas as pd
import mplfinance as mpf
import mysql_database
from EmailTest import send_email, parse_return_email
from dataclasses import dataclass
import time
import threading
import yaml  # 添加YAML支持

'''
todo

1 运行过程框架调整，支持多个turtle同时监测
2 增加运行状态写入yaml文件，读取文件恢复状态
'''

@dataclass
class BuyState:
    trigger_time: float # 触发次数
    buy_price: float # 买入价格
    add_price: float # 加仓价格
    stop_price: float # 止损价格
    is_gap_up: bool # 是否跳空高开
    shares: int # 买入股数
    atr: int # ATR
    available_cash: float # 可用资金
    
@dataclass
class TradeLog:
    data: str # 时间
    type: str # 操作类型
    buy_price: float # 买入价格
    shares: int # 买入股数
    cost: float # 成本
    atr: int # ATR
    available_cash: float # 可用资金
    all_shares: float # 总股数
    all_cost: float # 总成本
    Net_value: float # 净值
    Net_return: float # 净收益

@dataclass 
class BreakOutLog:
    # 记录突破信息
    data: str # 时间
    breakout_price: float # 突破价格
    lose_price: float # 亏损价格
    valid_or_not: str # 是否有效
    win_or_lose: bool # 是否盈利

def calc_sma_atr_pd(kdf,period):
    """计算TR与ATR

    Args:
        kdf (_type_): 历史数据
        period (_type_): ATR周期

    Returns:
        _type_: 返回kdf，增加TR与ATR列
    """
    kdf['最高'] = kdf['最高'].astype(float)
    kdf['最低'] = kdf['最低'].astype(float)
    kdf['收盘'] = kdf['收盘'].astype(float)
    kdf['HL'] = kdf['最高'] - kdf['最低']
    kdf['HC'] = np.abs(kdf['最高'] - kdf['收盘'].shift(1))
    kdf['LC'] = np.abs(kdf['最低'] - kdf['收盘'].shift(1))
    kdf['TR'] = np.round(kdf[['HL','HC','LC']].max(axis=1), 3)
    # ranges = pd.concat([high_low, high_close, low_close], axis=1)
    # true_range = np.max(ranges, axis=1)
    kdf['ATR'] = np.round(kdf['TR'].rolling(period).mean(), 3)
    
    return kdf.drop(['HL','HC','LC'], axis = 1)

class TurtleTrading(object):
    """对象范围较小，对某一个标的创建一个海龟，如513300，
    计算ATR、唐奇安通道线
    基础数据
    Args:
        object (_type_): _description_
    """
    def __init__(self, TradeCode, type, riskcoe, Capital, cash) -> None:
        self.TradeCode = TradeCode
        self.type = type
        self.riskcoe = riskcoe
        self.Capital = Capital
        self.cash = cash
        self.TrigerTime = 0
        self.BuyStates = list[BuyState]
        
        self.tradeslog = list[TradeLog]
        self.BreakOutLog = list[BreakOutLog]
        self.PriceNow = 0.0
        self.Donchian_20_up = 0.0
        self.Donchian_10_down = 0.0
        self.Donchian_50_up = 0.0
        self.is_gap_up = False # 是否跳空高开
        self.prev_heigh = 0.0 # 前一天最高价

    
    def GetRecentData(self):
        """获取某个标的的最近数据，从两年前到今天, 计算后的数据保存在self.CurrentData

        Returns:
            _type_: _description_
        """
        Today = datetime.today()
        # print(Today)
        formatted_date = Today.strftime("%Y%m%d")
        two_years_ago = (date.today() - timedelta(days=365*2)).strftime("%Y%m%d")
        # print(formatted_date)
        Code = f"{self.TradeCode}"
        CurrentData = ak.fund_etf_hist_em(symbol=Code, period="daily", start_date=two_years_ago, end_date=formatted_date, adjust="")
        
        # 将日期列转换为datetime
        CurrentData = pd.DataFrame(CurrentData)
        CurrentData['日期'] = pd.to_datetime(CurrentData['日期'])
        # print(type(CurrentData['日期'].iloc[0]))
        
        CurrentData.set_index('日期', inplace=True)
        # CurrentData.reset_index(inplace=True)
        # print(type(CurrentData['日期'].iloc[0]))
        # create table
        # stock_database.create_table(Code)
        # stock_database.insert_data(Code, CurrentData)
        # mysql_database.insert_db(CurrentData, Code, True, "'日期'")
        self.CurrentData = CurrentData
        # return self.CurrentData

        
    def CalATR(self, data, ATRday):
        """计算某个标的的ATR，从上市日到今天, 计算后的数据保存在self.CurrentData

        Args:
            ATRday: 多少日ATR
            SaveOrNot (_type_): 是否保存.csv数据
        """

        self.CurrentData = calc_sma_atr_pd(data, ATRday)
        self.N = self.CurrentData['ATR']

        
        # return self.N
    

    def ReadExistData(self, data):
        """除了通过发请求获取数据，也可以读本地的数据库，赋值给self.CurrentData

        Args:
            data (_type_): 本地csv名称
        """
        self.CurrentData = pd.read_csv(data)

    def DrawKLine(self, days):
        """画出k线图看看,画出最近days天的K线图
        """
        
        # 日期部分

        # dates = pd.to_datetime(self.CurrentData['日期'][-days:])
        # # Klinedf['Data'] = pd.to_datetime(self.CurrentData['日期'])
        Klinedf = pd.DataFrame()
        # Klinedf.set_index = Klinedf['Data']
        
        # 其他数据
        Klinedf['Date'] = self.CurrentData['日期'][-days:]
        Klinedf['Open'] = self.CurrentData['开盘'][-days:].astype(float)
        Klinedf['High'] = self.CurrentData['最高'][-days:].astype(float)
        Klinedf['Low'] = self.CurrentData['最低'][-days:].astype(float)
        Klinedf['Close'] = self.CurrentData['收盘'][-days:].astype(float)
        Klinedf['Volume'] = self.CurrentData['成交量'][-days:].astype(float)
        
        Klinedf.set_index(pd.to_datetime(Klinedf['Date']), inplace=True)
        # 画图
        mpf.plot(Klinedf, type='candle', style='yahoo', volume=False, mav=(5,), addplot=[mpf.make_addplot(self.Donchian_up['Upper'][-days:]), mpf.make_addplot(self.Donchian_down['lower'][-days:])], title=f"{self.TradeCode} K线图")
    
    def calculate_donchian_channel_up(self, n):
        """
        计算n日唐奇安上通道

        参数:
        self.CurrentData (DataFrame): 包含价格数据的Pandas DataFrame，包含"High"
        n (int): 时间周期

        返回:self.Donchian
        DataFrame: 唐奇安通道的DataFrame，包含"Upper"
        """
        Donchian = pd.DataFrame()  # 创建一个空的DataFrame用于存储唐奇安通道数据
        # 计算最高价和最低价的N日移动平均线
        name = 'Donchian_' + str(n) + '_upper'
        Donchian[name] = self.CurrentData['最高'].rolling(n).max()  # 使用rolling函数计算n日最高价的移动最大值

        # # 计算中间线
        # Donchian['Middle'] = (self.Donchian['Upper'] + self.Donchian['Lower']) / 2  # 计算上通道和下通道的中间线，但此行代码被注释掉了

        return Donchian  # 返回包含唐奇安上通道的DataFrame
    
    def calculate_donchian_channel_down(self, n):
        """
        计算n日唐奇安上通道

        参数:
        self.CurrentData (DataFrame): 包含价格数据的Pandas DataFrame，包含"High"
        n (int): 时间周期

        返回:self.Donchian
        DataFrame: 唐奇安通道的DataFrame，包含"Upper"
        """
        Donchian = pd.DataFrame()
        # 计算最高价和最低价的N日移动平均线
        name = 'Donchian_' + str(n) + '_lower'
        Donchian[name] = self.CurrentData['最低'].rolling(n).min()

        # # 计算中间线
        # Donchian['Middle'] = (self.Donchian['Upper'] + self.Donchian['Lower']) / 2

        return Donchian
    
    def calc_atr_donchian_short(self):
        """计算ATR、短期唐奇安通道
        """
                    # 计算ATR
        self.CalATR(self.CurrentData, 20)
        # 计算唐奇安通道
        self.Donchian_20_ups = self.calculate_donchian_channel_up(20)
        self.Donchian_50_ups = self.calculate_donchian_channel_up(50)
        self.Donchian_downs = self.calculate_donchian_channel_down(10)
        # 画图
        # self.DrawKLine(days)
        
        # 把self.N, self.Donchian_up, self.Donchian_down, 添加到self.CurrentData后面，保存到mysql数据库
        self.CurrentData = pd.concat([self.CurrentData, self.Donchian_20_ups, self.Donchian_50_ups, self.Donchian_downs], axis=1)

    def get_ready(self, days):
        """创建一个turtle对象，获取数据，计算ATR，计算唐奇安通道

        Args:
            days (_type_): _description_
            n (_type_): _description_
        """
        
        # if 不存在database
        if not mysql_database.check_db_table(f"{self.TradeCode}"):
            self.GetRecentData()
            
            self.calc_atr_donchian_short()
            Code = f"{self.TradeCode}"
            mysql_database.insert_db(self.CurrentData, Code, True, "日期")
        else:

        # 检查数据库最后一条的时间距离今天是否两天以上
            current_date = date.today()
            threshold_date = current_date - timedelta(days=2)
            last_update = mysql_database.check_db_table_last_date(f"{self.TradeCode}")
        
            if  last_update < threshold_date:
                # 如果不存在，则从akshare获取数据并保存到mysql数据库
                mysql_database.delete_table(f"{self.TradeCode}")
                    
                self.GetRecentData()
                
                self.calc_atr_donchian_short()

                Code = f"{self.TradeCode}"
                mysql_database.insert_db(self.CurrentData, Code, True, "日期")
            else:
                # 如果存在，则从mysql数据库中读取数据
                self.CurrentData = mysql_database.fetch_all_data(f"{self.TradeCode}")
            
        
    def CalPositionSize(self):
        """根据风险系数、ATR，计算仓位大小, 存于self.IntPositionSize
        """
        PositionSize = self.riskcoe * self.Capital /(self.N)  # 默认用股票形式了 100
        self.IntPositionSize = int(PositionSize // 100) * 100

    def system1EnterNormal(self, PriceNow, TempDonchian20Upper, BreakOutLog):
        # 没有持仓且价格向上突破---此时包含两种情形：1 对某标的首次使用系统，2 已发生过突破，此时上次突破天然是失败的
        if self.TrigerTime == 0 and PriceNow > TempDonchian20Upper:
        # 买入
            return True
        elif  PriceNow > TempDonchian20Upper:#todo !=0不会满足条件 先跳过 
            self.system1BreakoutValid(PriceNow)
            if BreakOutLog[-1].win_or_lose == None: # TT!= 0且突破且上一次突破unseccessful
                return True
            else:
                return False
        else:
            return False
        
        
    def system1EnterSafe(self, PriceNow, TempDonchian55Upper):
        
        if PriceNow > TempDonchian55Upper[-1]: # 保底的55日突破
            return True
        else:
            return False
        
    def system1BreakoutValid(self, priceNow):
        """判断前一次突破是否成功，是log[-1][5]写入“win”，否则写入“Lose”
        """
        if priceNow < self.BreakOutLog[-1].lose_price:
            self.BreakOutLog[-1].win_or_lose = None
        else:
            self.BreakOutLog[-1].win_or_lose = True
    # 一天结束，计算ATR，计算唐奇安通道，追加到已有的mysql数据库中
    
    def system_1_Out(self, PriceNow, TempDonchian10Lower):
        # 退出:低于20日最低价（多头方向）,空头以突破20日最高价为止损价格--有持仓且价格向下突破
        if self.TrigerTime != 0 and PriceNow < TempDonchian10Lower:
            # 退出
            return True
        
        else:
            return False
    def add(self, PriceNow):
        """加仓
        """
        if self.TrigerTime < 4 and PriceNow > self.BuyStates[self.TrigerTime - 1].add_price:#todo BuyStates是空的
            # 买入
            return True
        else:
            return False
        
    def system_1_stop(self, PriceNow):
        """止损判断：如果当前价格<上一次买入后的止损价格则止损
        """
        if PriceNow < self.BuyStates[self.TrigerTime - 1].stop_price:
            # 买入
            return True
        else:
            return False 
        
    def day_end(self):
        """Save current state to YAML file at the end of the day"""
        # Create state directory if not exists
        state_dir = "state"
        if not os.path.exists(state_dir):
            os.makedirs(state_dir)
        
        # Generate filename with current date
        today = datetime.now().strftime("%Y-%m-%d")
        filename = os.path.join(state_dir, f"{today}.yaml")
        
        # Save state to YAML
        state_data = {
            "turtles": [
                {
                    "TradeCode": t.TradeCode,
                    "type": t.type,
                    "riskcoe": t.riskcoe,
                    "Capital": t.Capital,
                    "cash": t.cash,
                    "TrigerTime": t.TrigerTime,
                    "BuyStates": [vars(bs) for bs in t.BuyStates],
                    "tradeslog": [vars(tl) for tl in t.tradeslog],
                    "BreakOutLog": [vars(bol) for bol in t.BreakOutLog],
                    "PriceNow": t.PriceNow,
                    "Donchian_20_up": t.Donchian_20_up,
                    "Donchian_10_down": t.Donchian_10_down,
                    "Donchian_50_up": t.Donchian_50_up,
                    "is_gap_up": t.is_gap_up,
                    "prev_heigh": t.prev_heigh
                } for t in self.turtles
            ]
        }
        
        with open(filename, 'w') as f:
            yaml.dump(state_data, f)
        
class TurtleTrading_OnTime(object):
    ''' 实时监测主程序，可以处理多个turtle
     
    1、获取实时大盘数据
    2、根据turtles的代码，比较是否触发条件
    3、实时监测主流程
    '''
    
    def load_previous_state(self):
        """Load previous state from YAML file if exists"""
        state_dir = "state"
        today = datetime.now().strftime("%Y-%m-%d")
        yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
        filename = os.path.join(state_dir, f"{yesterday}.yaml")
        
        if os.path.exists(filename):
            with open(filename, 'r') as f:
                state_data = yaml.safe_load(f)
                
            # Restore state
            for turtle_data in state_data.get('turtles', []):
                # Find or create TurtleTrading instance
                turtle = next((t for t in self.turtles if t.TradeCode == turtle_data['TradeCode']), None)
                if not turtle:
                    # Create new instance if not found (should not happen)
                    turtle = TurtleTrading(**turtle_data)
                    self.turtles.append(turtle)
                
                # Restore attributes
                turtle.TradeCode = turtle_data['TradeCode']
                turtle.type = turtle_data['type']
                turtle.riskcoe = turtle_data['riskcoe']
                turtle.Capital = turtle_data['Capital']
                turtle.cash = turtle_data['cash']
                turtle.TrigerTime = turtle_data['TrigerTime']
                turtle.BuyStates = [BuyState(**bs) for bs in turtle_data['BuyStates']]
                turtle.tradeslog = [TradeLog(**tl) for tl in turtle_data['tradeslog']]
                turtle.BreakOutLog = [BreakOutLog(**bol) for bol in turtle_data['BreakOutLog']]
                turtle.PriceNow = turtle_data['PriceNow']
                turtle.Donchian_20_up = turtle_data['Donchian_20_up']
                turtle.Donchian_10_down = turtle_data['Donchian_10_down']
                turtle.Donchian_50_up = turtle_data['Donchian_50_up']
                turtle.is_gap_up = turtle_data['is_gap_up']
                turtle.prev_heigh = turtle_data['prev_heigh']

    def __init__(self, turtles: list[TurtleTrading], user_email):
        self.turtles = turtles  # List of TurtleTrading instances
        self.user_email = user_email
        self.email_events = {}  # Track email response events for each turtle
        
        # Load previous state from YAML if exists
        self.load_previous_state()
        
    def get_stocks_data(self):
        """获取实时股票、基金数据，不保存
        """
        stock_data = ak.stock_zh_a_spot_em()
        stock_data = stock_data.dropna(subset=['最新价'])
        # # print(stock_zh_a_spot_df)
        # # stock_zh_a_spot_df第一列加上时间，精确到分钟
        # stock_zh_a_spot_df['时间'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        # mysql_database.insert_db(stock_zh_a_spot_df, "stock_price", True, "代码")
        
        etf_data = ak.fund_etf_spot_em()
        # etf_data = ak.fund_etf_spot_ths()
        # etf_data = etf_data.dropna(subset=['当前-单位净值'])
        etf_data = etf_data.dropna(subset=['最新价'])
        
        # etf_data['时间'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        # mysql_database.insert_db(etf_data, "etf_price", True, "代码")
        return stock_data, etf_data

    def Buy_stock(self, turtle: TurtleTrading, price_now):
        # 发送邮件 代码self.turtle.TradeCode, 建议买入价格price_now，买入份额self.turtle.IntPositionSize
        
            
        
        subject = "买入"
        body = f"{turtle.TradeCode}，价格{price_now}，份额{turtle.IntPositionSize} \n "
        body += "回复：实际买入价格-买入份额-手续费"
        send_email(subject, body, self.user_email)
        send_email_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        
        #每隔1分钟检测回信，解析邮件。
        
        parsed_email_flag = False
        while not parsed_email_flag:
            time.sleep(60)  # 每次尝试前等待 60 秒
            parse_states, buy_price, buy_share, fee = parse_return_email(
                self.user_email, send_email_time
            )
            
            if parse_states:
                parsed_email_flag = True
                break
            
        # 成功买入
        turtle.TrigerTime += 1
        # 记录self.turtle.BuyStates
        add_price = buy_price + 1/2 * turtle.N
        stop_price = buy_price - 2 * turtle.N
        cost = buy_price * buy_share - fee
        available_cash = turtle.Capital - cost
        
        buy_this_time = BuyState(turtle.TrigerTime, 
                                buy_price, 
                                add_price, 
                                stop_price,
                                False,
                                buy_share, 
                                turtle.N, 
                                available_cash)

        turtle.BuyStates.append(buy_this_time)
        
        # 记录self.turtle.tradeslog
        today = datetime.now().strftime("%Y-%m-%d")
        log_this_time = TradeLog(today,
                                "买入",
                                buy_price,
                                buy_share,
                                cost,
                                turtle.N,
                                available_cash,
                                all_shares=buy_share, 
                                all_cost=cost,
                                Net_value=buy_price * buy_share,
                                Net_return=0)
        turtle.tradeslog.append(log_this_time)
            
            
            
            
    
    def add_stock(self, turtle: TurtleTrading, price_now):
        """加仓

        Args:
            price_now (_type_): 现价
        """
        
        # 加仓
        
        subject = "加仓"
        body = f"{turtle.TradeCode}，价格{price_now}，份额{turtle.IntPositionSize} \n "
        body += "回复：实际买入价格-买入份额-手续费"
        send_email(subject, body, self.user_email)
        send_email_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        #每隔1分钟检测回信，解析邮件。

        parsed_email_flag = False
        while not parsed_email_flag:
            time.sleep(60)  # 每次尝试前等待 60 秒
            parse_states, buy_price, buy_share, fee = parse_return_email(
                self.user_email, send_email_time
            )
            
            if parse_states:
                parsed_email_flag = True
                break
        
        # 成功买入
        turtle.TrigerTime += 1
        # 记录self.turtle.BuyStates
        add_price = buy_price + 1/2 * turtle.N
        stop_price = buy_price - 2 * turtle.N
        cost = buy_price * buy_share - fee
        available_cash = turtle.BuyStates[-1].available_cash - cost
        all_shares = buy_share + turtle.BuyStates[-1].all_shares
        all_cost = cost + turtle.BuyStates[-1].all_cost
        net_value = buy_price * all_shares
        net_return = net_value - all_cost
        buy_this_time = BuyState(turtle.TrigerTime, 
                                buy_price, 
                                add_price, 
                                stop_price,
                                turtle.is_gap_up, 
                                buy_share,
                                turtle.N, 
                                available_cash)

        turtle.BuyStates.append(buy_this_time)


        today = datetime.now().strftime("%Y-%m-%d")
        log_this_time = TradeLog(today,
                                "加仓",
                                buy_price,
                                buy_share,
                                cost,
                                turtle.N,
                                available_cash,
                                all_shares,
                                all_cost,
                                net_value,
                                net_return)
        turtle.tradeslog.append(log_this_time)
        
        # 处理其他次买入的止损价格
        # 检查BuyStates中有几个gap_up,返回个数和索引
        gap_up_num = 0
        gap_up_index = []
        for i in range(len(turtle.BuyStates)):
            if turtle.BuyStates[i].is_gap_up:
                gap_up_num += 1
                gap_up_index.append(i)
        if gap_up_num == 0:
            # 之前BuyStates中的stop_price = stop_price
            for j in range(len(turtle.BuyStates)):
                turtle.BuyStates[j].stop_price = stop_price
        
        if not turtle.is_gap_up and  gap_up_num == 1:
            if gap_up_index[0] == 1:
                number_tobe_change = turtle.TrigerTime -1 - gap_up_index[0]
                for k in range(number_tobe_change):
                    turtle.BuyStates[k+1].stop_price = stop_price  
                    
            elif gap_up_index[0] == 2:
                turtle.BuyStates[2].stop_price = stop_price
                
        elif not turtle.is_gap_up and gap_up_num == 2:
            number_tobe_change = 2
            for k in range(number_tobe_change):
                turtle.BuyStates[k+1].stop_price = stop_price

        
    def stop_sale_stock(self, turtle: TurtleTrading, price_now):
        """止损卖出

        Args:
            price_now (_type_): 现价
        """
        
        # 判断需要卖出几份
        sale_shares = 0
        for i in range(len(turtle.BuyStates)):
            if price_now <= turtle.BuyStates[i].stop_price:
                sale_shares += 1
                break
        # 比较price_now与self.turtle.BuyStates[-1].stop_price
        
        # 发送邮件 代码self.turtle.TradeCode, 建议卖出价格price_now，卖出份额self.turtle.IntPositionSize
        subject = "止损卖出"
        body = f"{turtle.TradeCode}，价格{price_now}，份额{turtle.IntPositionSize * sale_shares} \n "
        body += "回复：实际卖出价格-卖出份额-手续费"
        send_email(subject, body, self.user_email)
        send_email_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        
        # 每隔1分钟检测回信，解析邮件。

        parsed_email_flag = False
        while not parsed_email_flag:
            time.sleep(60)  # 每次尝试前等待 60 秒
            parse_states, sale_price, sale_share, fee = parse_return_email(
                self.user_email, send_email_time
            )
            
            if parse_states:
                parsed_email_flag = True
                break        

        
        # 成功卖出
        turtle.TrigerTime -= sale_shares
        # 记录self.turtle.BuyStates
        available_cash = turtle.BuyStates[-1].available_cash + sale_price * sale_share - fee
        
        # 删除BuyStates中卖出股票的记录
        turtle.BuyStates = turtle.BuyStates[:-sale_shares]
        
        
        sale_this_time = TradeLog(datetime.now().strftime("%Y-%m-%d"),
                                    "止损",
                                    sale_price,
                                    sale_share,
                                    sale_price * sale_share - fee,
                                    turtle.N,
                                    available_cash,
                                    all_shares=0,
                                    all_cost=0,
                                    Net_value=sale_price * sale_share,
                                    Net_return=abs(turtle.Capital - available_cash))
        turtle.tradeslog.append(sale_this_time)
        
    def out_sale_stock(self, turtle: TurtleTrading, price_now):
        """止盈卖出

        Args:
            price_now (_type_): 现价
        """
        
        # 发送邮件 代码self.turtle.TradeCode, 建议卖出价格price_now，卖出份额self.turtle.IntPositionSize
        subject = "止盈卖出"
        body = f"{turtle.TradeCode}，价格{price_now}，份额{turtle.IntPositionSize} \n "
        body += "回复：实际卖出价格-卖出份额-手续费"
        send_email(subject, body, self.user_email)
        send_email_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        
        # 每隔1分钟检测回信，解析邮件。

        parsed_email_flag = False
        while not parsed_email_flag:
            time.sleep(60)  # 每次尝试前等待 60 秒
            parse_states, sale_price, sale_share, fee = parse_return_email(
                self.user_email, send_email_time
            )
            
            if parse_states:
                parsed_email_flag = True
                break        

        # 成功卖出
        turtle.TrigerTime = 0
        # 记录self.turtle.BuyStates
        available_cash = turtle.BuyStates[-1].available_cash + sale_price * sale_share - fee
        turtle.BuyStates = []
        
        sale_this_time = TradeLog(datetime.now().strftime("%Y-%m-%d"),
                                    "止盈",
                                    sale_price,
                                    sale_share,
                                    sale_price * sale_share - fee,
                                    turtle.N,
                                    available_cash,
                                    all_shares=0,
                                    all_cost=0,
                                    Net_value=sale_price * sale_share,
                                    Net_return=abs(turtle.Capital - available_cash))
        turtle.tradeslog.append(sale_this_time)

    # def run_short_trading_loop(self, stock_data, etf_data):

    #     now = datetime.now().time()
    #     # 根据类型获取当前价格
    #     if self.turtle.type == "stock":
    #         self.turtle.PriceNow = float(stock_data.loc[etf_data['代码'] == self.turtle.TradeCode, '最新价'].values[0])

    #     elif self.turtle.type == "etf":
    #         # self.turtle.PriceNow = float(etf_data.loc[etf_data['基金代码'] == self.turtle.TradeCode, '当前-单位净值'].values[0])
    #         self.turtle.PriceNow = float(etf_data.loc[etf_data['代码'] == self.turtle.TradeCode, '最新价'].values[0])

    #     # # 9点30 判断是否跳空高开
    #     if now.hour == 9 and now.minute == 30 and self.turtle.PriceNow > self.turtle.prev_heigh:
    #         self.turtle.is_gap_up = True
            
            
    #     # 判断当前仓位状态并执行相应操作
    #     if self.turtle.TrigerTime == 0:
    #         # 空仓状态
    #         if self.turtle.system1EnterNormal(
    #             self.turtle.PriceNow,
    #             self.turtle.Donchian_20_up,
    #             self.turtle.BreakOutLog
    #         ):
    #             self.Buy_stock(self.turtle.PriceNow)
                
    #             # 突破 记录self.turtle.breakoutlog
    #             today = datetime.now().strftime("%Y-%m-%d")
    #             breakout_this_time = BreakOutLog(today,
    #                                             self.turtle.Donchian_20_up, 
    #                                             self.turtle.Donchian_20_up - 2 * self.turtle.N,
    #                                             'valid',
    #                                             None)
    #             self.turtle.BreakOutLog.append(breakout_this_time)

    #         elif self.turtle.system1EnterSafe(
    #             self.turtle.PriceNow,
    #             self.turtle.Donchian_50_up
    #         ):
    #             self.Buy_stock(self.turtle.PriceNow)

    #     elif 1 <= self.turtle.TrigerTime <= 3:
    #         # # 突破状态
    #         # if self.turtle.system1EnterNormal(
    #         #     self.turtle.PriceNow,
    #         #     self.turtle.Donchian_20_up,
    #         #     self.turtle.BreakOutLog
    #         # ):
    #         #     self.Buy_stock(self.turtle.PriceNow)
    #         # elif self.turtle.system1EnterSafe(
    #         #     self.turtle.PriceNow,
    #         #     self.turtle.Donchian_50_up
    #         # ):
    #         #     self.Buy_stock(self.turtle.PriceNow)
            
    #         # 加仓状态
    #         if self.turtle.add(self.turtle.PriceNow):
    #             self.add_stock(self.turtle.PriceNow)
                
    #         # 止损状态
    #         elif self.turtle.system_1_stop(self.turtle.PriceNow):
    #             self.stop_sale_stock(self.turtle.PriceNow)
            
    #         # 止盈
    #         elif self.turtle.system_1_Out(
    #             self.turtle.PriceNow,
    #             self.turtle.Donchian_10_down
    #         ):
    #             self.out_sale_stock(self.turtle.PriceNow)

    #     elif self.turtle.TrigerTime == 4:
    #         # 满仓  止损 止盈
    #         if self.turtle.system_1_stop(self.turtle.PriceNow):
    #             self.stop_sale_stock(self.turtle.PriceNow)
    #         elif self.turtle.system_1_Out(
    #             self.turtle.PriceNow,
    #             self.turtle.Donchian_10_down
    #         ):
    #             self.out_sale_stock(self.turtle.PriceNow)

    #     # 等待 1 分钟后下一次循环
    #     time.sleep(60)
        
    def Start_short_system(self):
        """启动short系统
        """
        
        # ------------------准备阶段--------------------
        # 获取数据或读取数据  -- 计算ATR Donchian 20 50 up, 20 down
        # 初始化所有turtle
        for turtle in self.turtles:
            # 准备数据
            turtle.get_ready(100)
            turtle.N = float(turtle.CurrentData['ATR'].iloc[-1])
            turtle.prev_heigh = float(turtle.CurrentData['最高价'].iloc[-1])
            turtle.Donchian_20_up = float(turtle.CurrentData['Donchian_20_upper'].iloc[-1])
            turtle.Donchian_50_up = float(turtle.CurrentData['Donchian_50_upper'].iloc[-1])
            turtle.Donchian_10_down = float(turtle.CurrentData['Donchian_10_lower'].iloc[-1])
            turtle.CalPositionSize()
        # ------------------实时监测阶段--------------------
        # 9:00 1、判断是否是新的一周，是则重新计算Position Size
        # 判断是否是新的一周
        if datetime.now().weekday() == 0:
            for turtle in self.turtles:
                turtle.CalPositionSize()
            
        # 每分钟获取一次数据，判断是否触发条件 9:30-11:30 13:00-15:00
        while True:
            # 获取当前时间
            now = datetime.now().time()

            # 判断当前时间是否在交易时段内（9:30-11:30 或 13:00-15:00）
            is_trading_time = (
                (now.hour == 9 and now.minute >= 30) or
                (now.hour == 10 and 0 <= now.minute <= 59) or
                (now.hour == 11 and now.minute <= 30) or
                (now.hour == 13 and 0 <= now.minute <= 59) or
                (now.hour == 14 and 0 <= now.minute <= 59) or
                (now.hour == 15 and now.minute <= 0)
            )


                
            if not is_trading_time:
                # 非交易时间，等待 1 分钟后继续循环
                time.sleep(60)
                continue
            
            is_stop_time = (now.hour > 15 and now.minute > 0) #收盘时间
            if is_stop_time:
                break

            # 获取股票和ETF数据
            self.monitor_all_turtles()
            # 等待一段时间后再次检查
            time.sleep(60)  # 每分钟检查一次
        # ------------------结束阶段--------------------
        # 数据库更新当天数据，增加ATR、donchian数据
        # 直接做个新表
        for turtle in self.turtles:
            mysql_database.delete_table(f"{turtle.TradeCode}")
            turtle.get_ready(100)
            time.sleep(16.5*600)


    def monitor_all_turtles(self):
        """主监控循环"""
        # 获取实时数据
        stock_data, etf_data = self.get_stocks_data()
        
        # 遍历所有turtle进行监控
        for turtle in self.turtles:
            self.monitor_single_turtle(turtle, stock_data, etf_data)
        

    
    def monitor_single_turtle(self, turtle: TurtleTrading, stock_data, etf_data):
        """监控单个turtle的交易条件"""

        now = datetime.now().time()
        if turtle.type == "stock":
            turtle.PriceNow = float(stock_data.loc[etf_data['代码'] == self.turtle.TradeCode, '最新价'].values[0])

        elif turtle.type == "etf":
            # self.turtle.PriceNow = float(etf_data.loc[etf_data['基金代码'] == self.turtle.TradeCode, '当前-单位净值'].values[0])
            turtle.PriceNow = float(etf_data.loc[etf_data['代码'] == self.turtle.TradeCode, '最新价'].values[0])
        
        if now.hour == 9 and now.minute == 30 and self.turtle.PriceNow > self.turtle.prev_heigh:
            turtle.is_gap_up = True
            
        # 判断当前仓位状态并执行相应操作
        if turtle.TrigerTime == 0:
            if turtle.system1EnterNormal(
                turtle.PriceNow,
                turtle.Donchian_20_up,
                turtle.BreakOutLog
            ):
                
                self.start_email_thread(turtle, "买入", turtle.PriceNow)
                
                # 突破 记录self.turtle.breakoutlog
                today = datetime.now().strftime("%Y-%m-%d")
                breakout_this_time = BreakOutLog(today,
                                                turtle.Donchian_20_up, 
                                                turtle.Donchian_20_up - 2 * turtle.N,
                                                'valid',
                                                None)
                turtle.BreakOutLog.append(breakout_this_time)

            elif turtle.system1EnterSafe(
                turtle.PriceNow,
                turtle.Donchian_50_up
            ):
                self.start_email_thread(turtle, "买入", turtle.PriceNow)
                
        elif 1 <= turtle.TrigerTime <= 3:
            
            # 加仓状态
            if turtle.add(turtle.PriceNow):
                self.start_email_thread(turtle, "加仓", turtle.PriceNow)
                
            # 止损状态
            elif turtle.system_1_stop(turtle.PriceNow):
                self.start_email_thread(turtle, "止损", turtle.PriceNow)
            
            # 止盈
            elif turtle.system_1_Out(
                turtle.PriceNow,
                turtle.Donchian_10_down
            ):
                self.start_email_thread(turtle, "止盈", turtle.PriceNow)
            
        elif turtle.TrigerTime == 4:
            # 满仓  止损 止盈
            if turtle.system_1_stop(turtle.PriceNow):
                self.start_email_thread(turtle, "止损", turtle.PriceNow)
            elif turtle.system_1_Out(
                turtle.PriceNow,
                turtle.Donchian_10_down
            ):
                self.start_email_thread(turtle, "止盈", turtle.PriceNow)

    def start_email_thread(self, turtle:TurtleTrading, action, price_now):
        """启动邮件处理线程"""
        event = threading.Event()
        self.email_events[turtle.TradeCode] = event
        
        thread = threading.Thread(
            target=self.handle_email_response,
            args=(turtle, action, price_now, event)
        )
        thread.start()
    
    def handle_email_response(self, turtle:TurtleTrading, action, price_now, event):
        """处理邮件响应的线程"""
        # 发送邮件
        if action == "买入":
            self.Buy_stock(turtle, price_now)
        elif action == "加仓":
            self.add_stock(turtle, price_now)
        elif action == "止损":
            self.stop_sale_stock(turtle, price_now)
        elif action == "止盈":
            self.out_sale_stock(turtle, price_now)
        
        # 等待邮件响应
        event.wait()

if __name__ == '__main__':
    
    user_email = "guoyize2209@163.com"
    t = TurtleTrading('513870', "etf", 0.0025, 100000, 200000)
    # t.get_ready(100)
    
    a = TurtleTrading_OnTime(t, user_email)
    a.Start_short_system()

    # # 全是股票
    # stock_zh_a_spot_df = ak.stock_zh_a_spot_em()
    # # stock_zh_a_spot_df.to_csv("stock_zh_a_spot.txt", sep="\t", index=False, encoding="utf-8")
    # stock_zh_a_spot_df = stock_zh_a_spot_df.dropna(subset=['最新价'])
    # print(stock_zh_a_spot_df)
    
    # # 全是基金
    # etf_data = ak.fund_etf_spot_em()
    # etf_data = etf_data.dropna(subset=['最新价'])
    # etf_data.to_csv("fund_etf_spot.txt", sep="\t", index=False, encoding="utf-8")
    # print(etf_data)
    