From 9bca280232178239fcfd3373b951bc3434be2fa7 Mon Sep 17 00:00:00 2001 From: guoyz Date: Sun, 25 May 2025 15:27:01 +0800 Subject: [PATCH] =?UTF-8?q?states=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TurtleOnTime copy.py | 342 +++++++++++++++++++++++---------------- TurtleOnTime.py | 369 ++++++++++++++++++++++++------------------- 2 files changed, 413 insertions(+), 298 deletions(-) diff --git a/TurtleOnTime copy.py b/TurtleOnTime copy.py index 88abe86..fe109b4 100644 --- a/TurtleOnTime copy.py +++ b/TurtleOnTime copy.py @@ -10,6 +10,7 @@ from EmailTest import send_email, parse_return_email from dataclasses import dataclass import time import threading +import yaml # 添加YAML支持 ''' todo @@ -335,21 +336,95 @@ class TurtleTrading(object): return False def day_end(self): - pass + """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): """获取实时股票、基金数据,不保存 """ @@ -369,13 +444,13 @@ class TurtleTrading_OnTime(object): # mysql_database.insert_db(etf_data, "etf_price", True, "代码") return stock_data, etf_data - def Buy_stock(self, price_now): + def Buy_stock(self, turtle: TurtleTrading, price_now): # 发送邮件 代码self.turtle.TradeCode, 建议买入价格price_now,买入份额self.turtle.IntPositionSize subject = "买入" - body = f"{self.turtle.TradeCode},价格{price_now},份额{self.turtle.IntPositionSize} \n " + 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") @@ -394,23 +469,23 @@ class TurtleTrading_OnTime(object): break # 成功买入 - self.turtle.TrigerTime += 1 + turtle.TrigerTime += 1 # 记录self.turtle.BuyStates - add_price = buy_price + 1/2 * self.turtle.N - stop_price = buy_price - 2 * self.turtle.N + add_price = buy_price + 1/2 * turtle.N + stop_price = buy_price - 2 * turtle.N cost = buy_price * buy_share - fee - available_cash = self.turtle.Capital - cost + available_cash = turtle.Capital - cost - buy_this_time = BuyState(self.turtle.TrigerTime, + buy_this_time = BuyState(turtle.TrigerTime, buy_price, add_price, stop_price, False, buy_share, - self.turtle.N, + turtle.N, available_cash) - self.turtle.BuyStates.append(buy_this_time) + turtle.BuyStates.append(buy_this_time) # 记录self.turtle.tradeslog today = datetime.now().strftime("%Y-%m-%d") @@ -419,19 +494,19 @@ class TurtleTrading_OnTime(object): buy_price, buy_share, cost, - self.turtle.N, + turtle.N, available_cash, all_shares=buy_share, all_cost=cost, Net_value=buy_price * buy_share, Net_return=0) - self.turtle.tradeslog.append(log_this_time) + turtle.tradeslog.append(log_this_time) - def add_stock(self, price_now): + def add_stock(self, turtle: TurtleTrading, price_now): """加仓 Args: @@ -441,7 +516,7 @@ class TurtleTrading_OnTime(object): # 加仓 subject = "加仓" - body = f"{self.turtle.TradeCode},价格{price_now},份额{self.turtle.IntPositionSize} \n " + 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") @@ -460,26 +535,26 @@ class TurtleTrading_OnTime(object): break # 成功买入 - self.turtle.TrigerTime += 1 + turtle.TrigerTime += 1 # 记录self.turtle.BuyStates - add_price = buy_price + 1/2 * self.turtle.N - stop_price = buy_price - 2 * self.turtle.N + add_price = buy_price + 1/2 * turtle.N + stop_price = buy_price - 2 * turtle.N cost = buy_price * buy_share - fee - available_cash = self.turtle.BuyStates[-1].available_cash - cost - all_shares = buy_share + self.turtle.BuyStates[-1].all_shares - all_cost = cost + self.turtle.BuyStates[-1].all_cost + 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(self.turtle.TrigerTime, + buy_this_time = BuyState(turtle.TrigerTime, buy_price, add_price, stop_price, - self.turtle.is_gap_up, + turtle.is_gap_up, buy_share, - self.turtle.N, + turtle.N, available_cash) - self.turtle.BuyStates.append(buy_this_time) + turtle.BuyStates.append(buy_this_time) today = datetime.now().strftime("%Y-%m-%d") @@ -488,43 +563,43 @@ class TurtleTrading_OnTime(object): buy_price, buy_share, cost, - self.turtle.N, + turtle.N, available_cash, all_shares, all_cost, net_value, net_return) - self.turtle.tradeslog.append(log_this_time) + turtle.tradeslog.append(log_this_time) # 处理其他次买入的止损价格 # 检查BuyStates中有几个gap_up,返回个数和索引 gap_up_num = 0 gap_up_index = [] - for i in range(len(self.turtle.BuyStates)): - if self.turtle.BuyStates[i].is_gap_up: + 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(self.turtle.BuyStates)): - self.turtle.BuyStates[j].stop_price = stop_price + for j in range(len(turtle.BuyStates)): + turtle.BuyStates[j].stop_price = stop_price - if not self.turtle.is_gap_up and gap_up_num == 1: + if not turtle.is_gap_up and gap_up_num == 1: if gap_up_index[0] == 1: - number_tobe_change = self.turtle.TrigerTime -1 - gap_up_index[0] + number_tobe_change = turtle.TrigerTime -1 - gap_up_index[0] for k in range(number_tobe_change): - self.turtle.BuyStates[k+1].stop_price = stop_price + turtle.BuyStates[k+1].stop_price = stop_price elif gap_up_index[0] == 2: - self.turtle.BuyStates[2].stop_price = stop_price + turtle.BuyStates[2].stop_price = stop_price - elif not self.turtle.is_gap_up and gap_up_num == 2: + elif not turtle.is_gap_up and gap_up_num == 2: number_tobe_change = 2 for k in range(number_tobe_change): - self.turtle.BuyStates[k+1].stop_price = stop_price + turtle.BuyStates[k+1].stop_price = stop_price - def stop_sale_stock(self, price_now): + def stop_sale_stock(self, turtle: TurtleTrading, price_now): """止损卖出 Args: @@ -533,15 +608,15 @@ class TurtleTrading_OnTime(object): # 判断需要卖出几份 sale_shares = 0 - for i in range(len(self.turtle.BuyStates)): - if price_now <= self.turtle.BuyStates[i].stop_price: + 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"{self.turtle.TradeCode},价格{price_now},份额{self.turtle.IntPositionSize * sale_shares} \n " + 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") @@ -561,12 +636,12 @@ class TurtleTrading_OnTime(object): # 成功卖出 - self.turtle.TrigerTime -= sale_shares + turtle.TrigerTime -= sale_shares # 记录self.turtle.BuyStates - available_cash = self.turtle.BuyStates[-1].available_cash + sale_price * sale_share - fee + available_cash = turtle.BuyStates[-1].available_cash + sale_price * sale_share - fee # 删除BuyStates中卖出股票的记录 - self.turtle.BuyStates = self.turtle.BuyStates[:-sale_shares] + turtle.BuyStates = turtle.BuyStates[:-sale_shares] sale_this_time = TradeLog(datetime.now().strftime("%Y-%m-%d"), @@ -574,15 +649,15 @@ class TurtleTrading_OnTime(object): sale_price, sale_share, sale_price * sale_share - fee, - self.turtle.N, + turtle.N, available_cash, all_shares=0, all_cost=0, Net_value=sale_price * sale_share, - Net_return=abs(self.turtle.Capital - available_cash)) - self.turtle.tradeslog.append(sale_this_time) + Net_return=abs(turtle.Capital - available_cash)) + turtle.tradeslog.append(sale_this_time) - def out_sale_stock(self, price_now): + def out_sale_stock(self, turtle: TurtleTrading, price_now): """止盈卖出 Args: @@ -591,7 +666,7 @@ class TurtleTrading_OnTime(object): # 发送邮件 代码self.turtle.TradeCode, 建议卖出价格price_now,卖出份额self.turtle.IntPositionSize subject = "止盈卖出" - body = f"{self.turtle.TradeCode},价格{price_now},份额{self.turtle.IntPositionSize} \n " + 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") @@ -610,106 +685,106 @@ class TurtleTrading_OnTime(object): break # 成功卖出 - self.turtle.TrigerTime = 0 + turtle.TrigerTime = 0 # 记录self.turtle.BuyStates - available_cash = self.turtle.BuyStates[-1].available_cash + sale_price * sale_share - fee - 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, - self.turtle.N, + turtle.N, available_cash, all_shares=0, all_cost=0, Net_value=sale_price * sale_share, - Net_return=abs(self.turtle.Capital - available_cash)) - self.turtle.tradeslog.append(sale_this_time) + Net_return=abs(turtle.Capital - available_cash)) + turtle.tradeslog.append(sale_this_time) - def run_short_trading_loop(self, stock_data, etf_data): + # 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]) + # 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]) + # 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 + # # # 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) + # # 判断当前仓位状态并执行相应操作 + # 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) + # # 突破 记录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 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) + # 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) + # # 加仓状态 + # 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_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.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) + # 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) + # # 等待 1 分钟后下一次循环 + # time.sleep(60) def Start_short_system(self): """启动short系统 @@ -767,9 +842,10 @@ class TurtleTrading_OnTime(object): # ------------------结束阶段-------------------- # 数据库更新当天数据,增加ATR、donchian数据 # 直接做个新表 - mysql_database.delete_table(f"{self.turtle.TradeCode}") - self.turtle.get_ready(100) - time.sleep(16.5*600) + 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): @@ -849,7 +925,7 @@ class TurtleTrading_OnTime(object): ): self.start_email_thread(turtle, "止盈", turtle.PriceNow) - def start_email_thread(self, turtle, action, price_now): + def start_email_thread(self, turtle:TurtleTrading, action, price_now): """启动邮件处理线程""" event = threading.Event() self.email_events[turtle.TradeCode] = event @@ -860,17 +936,17 @@ class TurtleTrading_OnTime(object): ) thread.start() - def handle_email_response(self, turtle, action, price_now, event): + def handle_email_response(self, turtle:TurtleTrading, action, price_now, event): """处理邮件响应的线程""" # 发送邮件 if action == "买入": - self.Buy_stock(price_now) + self.Buy_stock(turtle, price_now) elif action == "加仓": - self.add_stock(price_now) + self.add_stock(turtle, price_now) elif action == "止损": - self.stop_sale_stock(price_now) + self.stop_sale_stock(turtle, price_now) elif action == "止盈": - self.out_sale_stock(price_now) + self.out_sale_stock(turtle, price_now) # 等待邮件响应 event.wait() diff --git a/TurtleOnTime.py b/TurtleOnTime.py index 418ae90..b199517 100644 --- a/TurtleOnTime.py +++ b/TurtleOnTime.py @@ -91,7 +91,6 @@ class TurtleTrading(object): self.cash = cash self.TrigerTime = 0 self.BuyStates = list[BuyState] - self.state_file = f"state_{self.TradeCode}.yaml" # 状态文件名 self.tradeslog = list[TradeLog] self.BreakOutLog = list[BreakOutLog] @@ -337,54 +336,93 @@ class TurtleTrading(object): return False def day_end(self): - self.save_state() # 每日结束保存状态 - - def save_state(self): - """保存当前状态到YAML文件""" + """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 = { - 'TrigerTime': self.TrigerTime, - 'BuyStates': [vars(bs) for bs in self.BuyStates], - 'tradeslog': [vars(tl) for tl in self.tradeslog], - 'BreakOutLog': [vars(bol) for bol in self.BreakOutLog], - 'PriceNow': self.PriceNow, - 'Donchian_20_up': self.Donchian_20_up, - 'Donchian_10_down': self.Donchian_10_down, - 'Donchian_50_up': self.Donchian_50_up, - 'is_gap_up': self.is_gap_up, - 'prev_heigh': self.prev_heigh + "main_state": { + "user_email": self.user_email, + "email_events": self.email_events, + "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] + } for t in self.turtles + ] + } } - with open(self.state_file, 'w') as file: - yaml.dump(state_data, file) - - def load_state(self): - """从YAML文件加载状态""" - if os.path.exists(self.state_file): - with open(self.state_file, 'r') as file: - state_data = yaml.safe_load(file) - self.TrigerTime = state_data['TrigerTime'] - self.BuyStates = [BuyState(**bs) for bs in state_data['BuyStates']] - self.tradeslog = [TradeLog(**tl) for tl in state_data['tradeslog']] - self.BreakOutLog = [BreakOutLog(**bol) for bol in state_data['BreakOutLog']] - self.PriceNow = state_data['PriceNow'] - self.Donchian_20_up = state_data['Donchian_20_up'] - self.Donchian_10_down = state_data['Donchian_10_down'] - self.Donchian_50_up = state_data['Donchian_50_up'] - self.is_gap_up = state_data['is_gap_up'] - self.prev_heigh = state_data['prev_heigh'] + + 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): """获取实时股票、基金数据,不保存 """ @@ -404,13 +442,13 @@ class TurtleTrading_OnTime(object): # mysql_database.insert_db(etf_data, "etf_price", True, "代码") return stock_data, etf_data - def Buy_stock(self, price_now): + def Buy_stock(self, turtle: TurtleTrading, price_now): # 发送邮件 代码self.turtle.TradeCode, 建议买入价格price_now,买入份额self.turtle.IntPositionSize subject = "买入" - body = f"{self.turtle.TradeCode},价格{price_now},份额{self.turtle.IntPositionSize} \n " + 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") @@ -429,23 +467,23 @@ class TurtleTrading_OnTime(object): break # 成功买入 - self.turtle.TrigerTime += 1 + turtle.TrigerTime += 1 # 记录self.turtle.BuyStates - add_price = buy_price + 1/2 * self.turtle.N - stop_price = buy_price - 2 * self.turtle.N + add_price = buy_price + 1/2 * turtle.N + stop_price = buy_price - 2 * turtle.N cost = buy_price * buy_share - fee - available_cash = self.turtle.Capital - cost + available_cash = turtle.Capital - cost - buy_this_time = BuyState(self.turtle.TrigerTime, + buy_this_time = BuyState(turtle.TrigerTime, buy_price, add_price, stop_price, False, buy_share, - self.turtle.N, + turtle.N, available_cash) - self.turtle.BuyStates.append(buy_this_time) + turtle.BuyStates.append(buy_this_time) # 记录self.turtle.tradeslog today = datetime.now().strftime("%Y-%m-%d") @@ -454,19 +492,19 @@ class TurtleTrading_OnTime(object): buy_price, buy_share, cost, - self.turtle.N, + turtle.N, available_cash, all_shares=buy_share, all_cost=cost, Net_value=buy_price * buy_share, Net_return=0) - self.turtle.tradeslog.append(log_this_time) + turtle.tradeslog.append(log_this_time) - def add_stock(self, price_now): + def add_stock(self, turtle: TurtleTrading, price_now): """加仓 Args: @@ -476,7 +514,7 @@ class TurtleTrading_OnTime(object): # 加仓 subject = "加仓" - body = f"{self.turtle.TradeCode},价格{price_now},份额{self.turtle.IntPositionSize} \n " + 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") @@ -495,26 +533,26 @@ class TurtleTrading_OnTime(object): break # 成功买入 - self.turtle.TrigerTime += 1 + turtle.TrigerTime += 1 # 记录self.turtle.BuyStates - add_price = buy_price + 1/2 * self.turtle.N - stop_price = buy_price - 2 * self.turtle.N + add_price = buy_price + 1/2 * turtle.N + stop_price = buy_price - 2 * turtle.N cost = buy_price * buy_share - fee - available_cash = self.turtle.BuyStates[-1].available_cash - cost - all_shares = buy_share + self.turtle.BuyStates[-1].all_shares - all_cost = cost + self.turtle.BuyStates[-1].all_cost + 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(self.turtle.TrigerTime, + buy_this_time = BuyState(turtle.TrigerTime, buy_price, add_price, stop_price, - self.turtle.is_gap_up, + turtle.is_gap_up, buy_share, - self.turtle.N, + turtle.N, available_cash) - self.turtle.BuyStates.append(buy_this_time) + turtle.BuyStates.append(buy_this_time) today = datetime.now().strftime("%Y-%m-%d") @@ -523,43 +561,43 @@ class TurtleTrading_OnTime(object): buy_price, buy_share, cost, - self.turtle.N, + turtle.N, available_cash, all_shares, all_cost, net_value, net_return) - self.turtle.tradeslog.append(log_this_time) + turtle.tradeslog.append(log_this_time) # 处理其他次买入的止损价格 # 检查BuyStates中有几个gap_up,返回个数和索引 gap_up_num = 0 gap_up_index = [] - for i in range(len(self.turtle.BuyStates)): - if self.turtle.BuyStates[i].is_gap_up: + 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(self.turtle.BuyStates)): - self.turtle.BuyStates[j].stop_price = stop_price + for j in range(len(turtle.BuyStates)): + turtle.BuyStates[j].stop_price = stop_price - if not self.turtle.is_gap_up and gap_up_num == 1: + if not turtle.is_gap_up and gap_up_num == 1: if gap_up_index[0] == 1: - number_tobe_change = self.turtle.TrigerTime -1 - gap_up_index[0] + number_tobe_change = turtle.TrigerTime -1 - gap_up_index[0] for k in range(number_tobe_change): - self.turtle.BuyStates[k+1].stop_price = stop_price + turtle.BuyStates[k+1].stop_price = stop_price elif gap_up_index[0] == 2: - self.turtle.BuyStates[2].stop_price = stop_price + turtle.BuyStates[2].stop_price = stop_price - elif not self.turtle.is_gap_up and gap_up_num == 2: + elif not turtle.is_gap_up and gap_up_num == 2: number_tobe_change = 2 for k in range(number_tobe_change): - self.turtle.BuyStates[k+1].stop_price = stop_price + turtle.BuyStates[k+1].stop_price = stop_price - def stop_sale_stock(self, price_now): + def stop_sale_stock(self, turtle: TurtleTrading, price_now): """止损卖出 Args: @@ -568,15 +606,15 @@ class TurtleTrading_OnTime(object): # 判断需要卖出几份 sale_shares = 0 - for i in range(len(self.turtle.BuyStates)): - if price_now <= self.turtle.BuyStates[i].stop_price: + 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"{self.turtle.TradeCode},价格{price_now},份额{self.turtle.IntPositionSize * sale_shares} \n " + 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") @@ -596,12 +634,12 @@ class TurtleTrading_OnTime(object): # 成功卖出 - self.turtle.TrigerTime -= sale_shares + turtle.TrigerTime -= sale_shares # 记录self.turtle.BuyStates - available_cash = self.turtle.BuyStates[-1].available_cash + sale_price * sale_share - fee + available_cash = turtle.BuyStates[-1].available_cash + sale_price * sale_share - fee # 删除BuyStates中卖出股票的记录 - self.turtle.BuyStates = self.turtle.BuyStates[:-sale_shares] + turtle.BuyStates = turtle.BuyStates[:-sale_shares] sale_this_time = TradeLog(datetime.now().strftime("%Y-%m-%d"), @@ -609,15 +647,15 @@ class TurtleTrading_OnTime(object): sale_price, sale_share, sale_price * sale_share - fee, - self.turtle.N, + turtle.N, available_cash, all_shares=0, all_cost=0, Net_value=sale_price * sale_share, - Net_return=abs(self.turtle.Capital - available_cash)) - self.turtle.tradeslog.append(sale_this_time) + Net_return=abs(turtle.Capital - available_cash)) + turtle.tradeslog.append(sale_this_time) - def out_sale_stock(self, price_now): + def out_sale_stock(self, turtle: TurtleTrading, price_now): """止盈卖出 Args: @@ -626,7 +664,7 @@ class TurtleTrading_OnTime(object): # 发送邮件 代码self.turtle.TradeCode, 建议卖出价格price_now,卖出份额self.turtle.IntPositionSize subject = "止盈卖出" - body = f"{self.turtle.TradeCode},价格{price_now},份额{self.turtle.IntPositionSize} \n " + 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") @@ -645,106 +683,106 @@ class TurtleTrading_OnTime(object): break # 成功卖出 - self.turtle.TrigerTime = 0 + turtle.TrigerTime = 0 # 记录self.turtle.BuyStates - available_cash = self.turtle.BuyStates[-1].available_cash + sale_price * sale_share - fee - 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, - self.turtle.N, + turtle.N, available_cash, all_shares=0, all_cost=0, Net_value=sale_price * sale_share, - Net_return=abs(self.turtle.Capital - available_cash)) - self.turtle.tradeslog.append(sale_this_time) + Net_return=abs(turtle.Capital - available_cash)) + turtle.tradeslog.append(sale_this_time) - def run_short_trading_loop(self, stock_data, etf_data): + # 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]) + # 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]) + # 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 + # # # 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) + # # 判断当前仓位状态并执行相应操作 + # 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) + # # 突破 记录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 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) + # 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) + # # 加仓状态 + # 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_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.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) + # 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) + # # 等待 1 分钟后下一次循环 + # time.sleep(60) def Start_short_system(self): """启动short系统 @@ -802,9 +840,10 @@ class TurtleTrading_OnTime(object): # ------------------结束阶段-------------------- # 数据库更新当天数据,增加ATR、donchian数据 # 直接做个新表 - mysql_database.delete_table(f"{self.turtle.TradeCode}") - self.turtle.get_ready(100) - time.sleep(16.5*600) + 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): @@ -884,7 +923,7 @@ class TurtleTrading_OnTime(object): ): self.start_email_thread(turtle, "止盈", turtle.PriceNow) - def start_email_thread(self, turtle, action, price_now): + def start_email_thread(self, turtle:TurtleTrading, action, price_now): """启动邮件处理线程""" event = threading.Event() self.email_events[turtle.TradeCode] = event @@ -895,17 +934,17 @@ class TurtleTrading_OnTime(object): ) thread.start() - def handle_email_response(self, turtle, action, price_now, event): + def handle_email_response(self, turtle:TurtleTrading, action, price_now, event): """处理邮件响应的线程""" # 发送邮件 if action == "买入": - self.Buy_stock(price_now) + self.Buy_stock(turtle, price_now) elif action == "加仓": - self.add_stock(price_now) + self.add_stock(turtle, price_now) elif action == "止损": - self.stop_sale_stock(price_now) + self.stop_sale_stock(turtle, price_now) elif action == "止盈": - self.out_sale_stock(price_now) + self.out_sale_stock(turtle, price_now) # 等待邮件响应 event.wait()