diff --git a/TurtleOnTime.py b/TurtleOnTime.py index 7f2792b..bd0b24d 100644 --- a/TurtleOnTime.py +++ b/TurtleOnTime.py @@ -17,6 +17,7 @@ class BuyState: buy_price: float # 买入价格 add_price: float # 加仓价格 stop_price: float # 止损价格 + is_gap_up: bool # 是否跳空高开 shares: int # 买入股数 atr: int # ATR available_cash: float # 可用资金 @@ -35,6 +36,15 @@ class TradeLog: 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 @@ -74,7 +84,15 @@ class TurtleTrading(object): self.TrigerTime = 0 self.BuyStates = list[BuyState] - self.tradeslog = list[TradeLog] # 交易记录 + 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 @@ -259,7 +277,7 @@ class TurtleTrading(object): return True elif PriceNow > TempDonchian20Upper:#todo !=0不会满足条件 先跳过 self.system1BreakoutValid(PriceNow) - if BreakOutLog[-1][5] == 'Lose': # TT!= 0且突破且上一次突破unseccessful + if BreakOutLog[-1].win_or_lose == None: # TT!= 0且突破且上一次突破unseccessful return True else: return False @@ -277,10 +295,10 @@ class TurtleTrading(object): def system1BreakoutValid(self, priceNow): """判断前一次突破是否成功,是log[-1][5]写入“win”,否则写入“Lose” """ - if priceNow < self.BreakOutLog[-1][3]: - self.BreakOutLog[-1][5] = 'Lose' + if priceNow < self.BreakOutLog[-1].lose_price: + self.BreakOutLog[-1].win_or_lose = None else: - self.BreakOutLog[-1][5] = 'None' + self.BreakOutLog[-1].win_or_lose = True # 一天结束,计算ATR,计算唐奇安通道,追加到已有的mysql数据库中 def system_1_Out(self, PriceNow, TempDonchian10Lower): @@ -294,7 +312,7 @@ class TurtleTrading(object): def add(self, PriceNow): """加仓 """ - if self.TrigerTime < 4 and PriceNow > self.BuyStates[self.TrigerTime - 1][2]:#todo BuyStates是空的 + if self.TrigerTime < 4 and PriceNow > self.BuyStates[self.TrigerTime - 1].add_price:#todo BuyStates是空的 # 买入 return True else: @@ -303,7 +321,7 @@ class TurtleTrading(object): def system_1_stop(self, PriceNow): """止损判断:如果当前价格<上一次买入后的止损价格则止损 """ - if PriceNow < self.BuyStates[self.TrigerTime - 1][3]: + if PriceNow < self.BuyStates[self.TrigerTime - 1].stop_price: # 买入 return True else: @@ -346,126 +364,176 @@ class TurtleTrading_OnTime(object): def Buy_stock(self, price_now): # 发送邮件 代码self.turtle.TradeCode, 建议买入价格price_now,买入份额self.turtle.IntPositionSize - if self.turtle.TrigerTime == 0: # 第一次买入 - subject = "买入" - body = f"{self.turtle.TradeCode},价格{price_now},份额{self.turtle.IntPositionSize} \n " - body += "回复:实际买入价格-买入份额-手续费" - send_email(subject, body, self.user_email) - send_email_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + subject = "买入" + body = f"{self.turtle.TradeCode},价格{price_now},份额{self.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 + ) - #每隔1分钟检测回信,解析邮件。 + if parse_states: + parsed_email_flag = True + break - 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 - - # 成功买入 - self.turtle.TrigerTime += 1 - # 记录self.turtle.BuyStates - add_price = buy_price + 1/2 * self.turtle.N - stop_price = buy_price - 2 * self.turtle.N - cost = buy_price * buy_share - fee - available_cash = self.turtle.Capital - cost - - buy_this_time = BuyState(self.turtle.TrigerTime, - buy_price, - add_price, - stop_price, - buy_share, - self.turtle.N, - available_cash) + # 成功买入 + self.turtle.TrigerTime += 1 + # 记录self.turtle.BuyStates + add_price = buy_price + 1/2 * self.turtle.N + stop_price = buy_price - 2 * self.turtle.N + cost = buy_price * buy_share - fee + available_cash = self.turtle.Capital - cost + + buy_this_time = BuyState(self.turtle.TrigerTime, + buy_price, + add_price, + stop_price, + False, + buy_share, + self.turtle.N, + available_cash) - self.turtle.BuyStates.append(buy_this_time) + self.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, + self.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) + - today = datetime.now().strftime("%Y-%m-%d") - log_this_time = TradeLog(today, - "买入", - buy_price, - buy_share, - cost, - self.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) - else: - # 加仓 - subject = "加仓" - body = f"{self.turtle.TradeCode},价格{price_now},份额{self.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 - # 成功买入 - self.turtle.TrigerTime += 1 - # 记录self.turtle.BuyStates - add_price = buy_price + 1/2 * self.turtle.N - stop_price = buy_price - 2 * self.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 - net_value = buy_price * all_shares - net_return = net_value - all_cost - buy_this_time = BuyState(self.turtle.TrigerTime, - buy_price, - add_price, - stop_price, - buy_share, - self.turtle.N, - available_cash) - - self.turtle.BuyStates.append(buy_this_time) - today = datetime.now().strftime("%Y-%m-%d") - log_this_time = TradeLog(today, - "加仓", - buy_price, - buy_share, - cost, - self.turtle.N, - available_cash, - all_shares, - all_cost, - net_value, - net_return) - self.turtle.tradeslog.append(log_this_time) - pass - + def add_stock(self, price_now): + """加仓 + + Args: + price_now (_type_): 现价 + """ + + # 加仓 + + subject = "加仓" + body = f"{self.turtle.TradeCode},价格{price_now},份额{self.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 + + # 成功买入 + self.turtle.TrigerTime += 1 + # 记录self.turtle.BuyStates + add_price = buy_price + 1/2 * self.turtle.N + stop_price = buy_price - 2 * self.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 + net_value = buy_price * all_shares + net_return = net_value - all_cost + buy_this_time = BuyState(self.turtle.TrigerTime, + buy_price, + add_price, + stop_price, + self.turtle.is_gap_up, + buy_share, + self.turtle.N, + available_cash) + + self.turtle.BuyStates.append(buy_this_time) + + + today = datetime.now().strftime("%Y-%m-%d") + log_this_time = TradeLog(today, + "加仓", + buy_price, + buy_share, + cost, + self.turtle.N, + available_cash, + all_shares, + all_cost, + net_value, + net_return) + self.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: + 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 + + if not self.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] + for k in range(number_tobe_change): + self.turtle.BuyStates[k+1].stop_price = stop_price + + elif gap_up_index[0] == 2: + self.turtle.BuyStates[2].stop_price = stop_price + + elif not self.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 + + def stop_sale_stock(self, price_now): """止损卖出 Args: price_now (_type_): 现价 """ + + # 判断需要卖出几份 + sale_shares = 0 + for i in range(len(self.turtle.BuyStates)): + if price_now <= self.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} \n " + body = f"{self.turtle.TradeCode},价格{price_now},份额{self.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") @@ -564,6 +632,8 @@ class TurtleTrading_OnTime(object): (now.hour == 15 and now.minute <= 0) ) + + # if not is_trading_time: # # 非交易时间,等待 1 分钟后继续循环 # time.sleep(60) @@ -580,6 +650,11 @@ class TurtleTrading_OnTime(object): # 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: # 空仓状态 @@ -589,6 +664,16 @@ class TurtleTrading_OnTime(object): 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 @@ -596,22 +681,28 @@ class TurtleTrading_OnTime(object): 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.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 self.turtle.add(self.turtle.PriceNow): - 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 @@ -619,7 +710,7 @@ class TurtleTrading_OnTime(object): 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( @@ -639,6 +730,7 @@ class TurtleTrading_OnTime(object): # 获取数据或读取数据 -- 计算ATR Donchian 20 50 up, 20 down self.turtle.get_ready(100) self.turtle.N = float(self.turtle.CurrentData['ATR'].iloc[-1]) + self.turtle.prev_heigh = float(self.turtle.CurrentData['最高价'].iloc[-1]) self.turtle.Donchian_20_up = float(self.turtle.CurrentData['Donchian_20_upper'].iloc[-1]) self.turtle.Donchian_50_up = float(self.turtle.CurrentData['Donchian_50_upper'].iloc[-1]) self.turtle.Donchian_10_down = float(self.turtle.CurrentData['Donchian_10_lower'].iloc[-1]) diff --git a/回测/TurtleClassNew.py b/回测/TurtleClassNew.py index e75d99b..5585da4 100755 --- a/回测/TurtleClassNew.py +++ b/回测/TurtleClassNew.py @@ -419,7 +419,7 @@ class Trade(object): return True elif self.TrigerTime != 0 and PriceNow > TempDonchian20Upper[-1]: self.system1BreakoutValid(PriceNow) - if BreakOutLog[-1][5] == 'Lose': # TT!= 0且突破且上一次突破unseccessful + if BreakOutLog[-1][5] == 'Lose': # TrigerTime != 0且突破且上一次突破unseccessful return True else: return False