diff --git a/_doc/CHANGELOG.md b/_doc/CHANGELOG.md index 68dd6c8..8c50be1 100644 --- a/_doc/CHANGELOG.md +++ b/_doc/CHANGELOG.md @@ -6,18 +6,21 @@ - LimitSeller 逻辑优化 -## [ In Progress ] +## [ 4.3.0 ] 2026-01-29 ### 添加 - 临跌停卖点模块,用以预防跌停被锁死的极端风险 - 竞价结束callback接口 ### 修改 +- 系统日志优化 - 部分文案优化 +- 负成本清算显示变动为0 - Subscriber代码拆分重构 ### 删除 -- 旧版schedule移除 +- 旧版scheduler移除 +- 第三方库日志内容精简 ## [ 4.2.0 ] 2025-11-18 diff --git a/delegate/base_subscriber.py b/delegate/base_subscriber.py index f1b8272..a58cfa5 100644 --- a/delegate/base_subscriber.py +++ b/delegate/base_subscriber.py @@ -203,10 +203,11 @@ def check_before_finished(self): # 盘后报告总结 # ----------------------- def daily_summary(self): - if not check_is_open_day(datetime.datetime.now().strftime('%Y-%m-%d')): + now = datetime.datetime.now() + if not check_is_open_day(now.strftime('%Y-%m-%d')): return - curr_date = datetime.datetime.now().strftime('%Y-%m-%d') + curr_date = now.strftime('%Y-%m-%d') if self.open_today_deal_report: try: @@ -229,7 +230,7 @@ def daily_summary(self): try: if self.delegate is not None: asset = self.delegate.check_asset() - self.daily_reporter.check_asset(today=curr_date, asset=asset) + self.daily_reporter.check_asset(today=curr_date, asset=asset, is_afternoon=(now.hour > 12)) except Exception as e: print('Report asset failed: ', e) traceback.print_exc() @@ -337,7 +338,8 @@ def prev_check_open_day(self): curr_time = now.strftime('%H:%M') print(f'[{curr_time}]', end='') is_open_day = check_is_open_day(curr_date) - self.delegate.is_open_day = is_open_day + if self.delegate is not None: + self.delegate.is_open_day = is_open_day class HistorySubscriber(BaseSubscriber): diff --git a/delegate/daily_reporter.py b/delegate/daily_reporter.py index 00a9735..9b64036 100644 --- a/delegate/daily_reporter.py +++ b/delegate/daily_reporter.py @@ -9,7 +9,7 @@ from tools.constants import MSG_INNER_SEPARATOR, MSG_OUTER_SEPARATOR from tools.utils_basic import code_to_symbol -from tools.utils_cache import StockNames +from tools.utils_cache import StockNames, get_prev_trading_date_str from tools.utils_ding import BaseMessager @@ -113,9 +113,10 @@ def today_hold_report(self, today: str, positions): if open_price == 0.0 or curr_price is None: continue + # 负成本不显示变化 + total_change = curr_price - open_price if open_price > 0 else 0 + ratio_change = curr_price / open_price - 1 if open_price > 0 else 0 vol = position.volume - total_change = curr_price - open_price - ratio_change = curr_price / open_price - 1 hold_count += 1 display_list.append([code, curr_price, open_price, vol, ratio_change, total_change]) @@ -147,12 +148,14 @@ def today_hold_report(self, today: str, positions): if self.messager is not None: self.messager.send_text_as_md(text) - def check_asset(self, today: str, asset): - title = f'[{self.account_id}]{self.strategy_name} 盘后清点' - text = title + MSG_OUTER_SEPARATOR + def check_asset(self, today: str, asset, is_afternoon: bool = True): + title = f'[{self.account_id}]{self.strategy_name} {"午盘" if is_afternoon else "早盘"}清点' + text = title increase = get_total_asset_increase(self.path_assets, today, asset.total_asset) if increase is not None: + text += MSG_OUTER_SEPARATOR + total_change = colour_text( f'{"+" if increase > 0 else ""}{round(increase, 2)}', increase > 0, @@ -164,38 +167,38 @@ def check_asset(self, today: str, asset): increase > 0, increase < 0, ) - text += f'当日变动: {total_change}元({ratio_change})' + text += f'{"当日" if is_afternoon else "盘中"}变动: {total_change}元({ratio_change})' if self.today_report_show_bank \ and hasattr(self.delegate, 'xt_trader') \ and hasattr(self.delegate.xt_trader, 'query_bank_info'): - cash_change = 0.0 today_xt = today.replace('-', '') + yesterday_xt = get_prev_trading_date_str(today, 1) bank_info = self.delegate.xt_trader.query_bank_info(self.delegate.account) # 银行信息查询 for bank in bank_info: - if bank.success: + if bank.success or bank.error_msg == '': # 银行卡流水记录查询 transfers = self.delegate.xt_trader.query_bank_transfer_stream( - self.delegate.account, today_xt, today_xt, bank.bank_no, bank.bank_account) + self.delegate.account, yesterday_xt, today_xt, bank.bank_no, bank.bank_account) total_change = sum( -t.balance if t.transfer_direction == '2' else t.balance - for t in transfers if t.success + for t in transfers if (t.success or t.error_msg == '') and t.date == today_xt and t.transfer_status == '成功' ) cash_change += total_change if abs(cash_change) > 0.0001: - cash_change = colour_text( + cash_change_str = colour_text( f'{"+" if cash_change > 0 else ""}{round(cash_change, 2)}', cash_change > 0, cash_change < 0, ) text += MSG_INNER_SEPARATOR - text += f'银证转账: {cash_change}元' + text += f'银证转账: {cash_change_str}元' text += MSG_INNER_SEPARATOR - text += f'持仓市值: {round(asset.market_value, 2)}元' + text += f'{"持仓" if is_afternoon else "浮动"}市值: {round(asset.market_value, 2)}元' text += MSG_INNER_SEPARATOR text += f'剩余现金: {round(asset.cash, 2)}元' diff --git a/delegate/xt_delegate.py b/delegate/xt_delegate.py index d8823af..9165a95 100644 --- a/delegate/xt_delegate.py +++ b/delegate/xt_delegate.py @@ -61,7 +61,7 @@ def __init__( def connect(self, callback: object) -> (XtQuantTrader, bool): session_id = int(time.time()) # 生成session id 整数类型 同时运行的策略不能重复 - print("生成临时 session_id: ", session_id) + print("[交易通道] 连接开始,临时 session_id: ", session_id) self.xt_trader = XtQuantTrader(self.path, session_id) if callback is None: @@ -73,7 +73,7 @@ def connect(self, callback: object) -> (XtQuantTrader, bool): self.xt_trader.start() # 启动交易线程 # 建立交易连接,返回0表示连接成功 - print('正在建立交易连接...', end='') + print('[交易通道] 正在建立交易连接...', end='') connect_result = self.xt_trader.connect() print(f'返回值:{connect_result}...', end='') if connect_result != 0: @@ -83,7 +83,7 @@ def connect(self, callback: object) -> (XtQuantTrader, bool): print('成功!') # 对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功 - print('正在订阅主推回调...', end='') + print('[交易通道] 正在订阅主推回调...', end='') subscribe_result = self.xt_trader.subscribe(self.account) print(f'返回值:{subscribe_result}...', end='') if subscribe_result != 0: @@ -92,15 +92,15 @@ def connect(self, callback: object) -> (XtQuantTrader, bool): return None, False print('成功!') - print('连接完毕。') + print('[交易通道] 连接完毕!') return self.xt_trader, True def reconnect(self) -> None: if self.xt_trader is None and self.is_open_day: # 仅在交易日重连 - print('开始重连交易接口') + print('[交易通道] 开始重连交易接口') _, success = self.connect(self.callback) if success: - print('交易接口重连成功') + print('[交易通道] 交易接口重连成功') if self.subscriber is not None: self.subscriber.resubscribe_tick(True) # else: diff --git a/delegate/xt_subscriber.py b/delegate/xt_subscriber.py index 4e2a27a..450ab79 100644 --- a/delegate/xt_subscriber.py +++ b/delegate/xt_subscriber.py @@ -127,7 +127,8 @@ def callback_sub_whole(self, quotes: Dict) -> None: if self.cache_limits['prev_seconds'] != curr_seconds: self.cache_limits['prev_seconds'] = curr_seconds - print_mark = '.' if len(self.cache_quotes) > 0 else 'x' + print_mark = "'" if int(curr_seconds) % 10 == 9 else "." + print_mark = print_mark if len(self.cache_quotes) > 0 else "x" if int(curr_seconds) % self.execute_interval == 0: # 更全(默认:先记录再执行) diff --git a/run_shield.py b/run_shield.py index 61b84fe..5d22242 100644 --- a/run_shield.py +++ b/run_shield.py @@ -22,6 +22,9 @@ IS_PROD = False # 生产环境标志:False 表示使用掘金模拟盘 True 表示使用QMT账户下单交易 IS_DEBUG = True # 日志输出标记:控制台是否打印debug方法的输出 +CAN_BUY = False # 双重保险,改为 True 才会下单买 +CAN_SELL = False # 双重保险,改为 True 才会下单卖 + PATH_BASE = CACHE_PROD_PATH if IS_PROD else CACHE_TEST_PATH PATH_ASSETS = PATH_BASE + '/assets.csv' # 记录历史净值 @@ -68,7 +71,10 @@ class SellConf: def before_trade_day() -> None: # held_increase() -> None: - update_position_held(disk_lock, my_delegate, PATH_HELD) + + # 持仓自动发现,对于连续做波段的票,会因为券商不同可能有成本价的问题,所以暂时禁用 + # update_position_held(disk_lock, my_delegate, PATH_HELD) + if all_held_inc(disk_lock, PATH_HELD): logging.warning('===== 所有持仓计数 +1 =====') print(f'[持仓计数] All stocks held day +1') @@ -100,7 +106,7 @@ def execute_strategy(curr_date: str, curr_time: str, curr_seconds: str, curr_quo for time_range in SellConf.time_ranges: if time_range[0] <= curr_time <= time_range[1]: if int(curr_seconds) % SellConf.interval == 0: - scan_sell(curr_quotes, curr_date, curr_time, positions) + CAN_SELL & bool(scan_sell(curr_quotes, curr_date, curr_time, positions)) return False diff --git a/run_swords_tdx.py b/run_swords_tdx.py index a9ce597..1cb65b7 100644 --- a/run_swords_tdx.py +++ b/run_swords_tdx.py @@ -28,7 +28,6 @@ PATH_LOGS = PATH_BASE + '/logs.txt' # 记录策略的历史日志 disk_lock = threading.Lock() # 操作磁盘文件缓存的锁 cache_selected: Dict[str, Set] = {} # 记录选股历史,去重 -cache_history: Dict[str, pd.DataFrame] = {} # 记录历史日线行情的信息 { code: DataFrame } class PoolConf: @@ -71,7 +70,7 @@ def before_trade_day() -> None: update_position_held(disk_lock, my_delegate, PATH_HELD) if all_held_inc(disk_lock, PATH_HELD): logging.warning('===== 所有持仓计数 +1 =====') - print(f'All held stock day +1!') + print(f'[持仓计数] All stocks held day +1') # refresh_code_list() -> None: my_pool.refresh() @@ -199,8 +198,8 @@ def execute_strategy(curr_date: str, curr_time: str, curr_seconds: str, curr_quo if __name__ == '__main__': logging_init(path=PATH_LOGS, level=logging.INFO) - STRATEGY_NAME = STRATEGY_NAME if IS_PROD else STRATEGY_NAME + "[测]" - print(f'正在启动 {STRATEGY_NAME}...') + STRATEGY_NAME = STRATEGY_NAME if IS_PROD else STRATEGY_NAME + '[测]' + print(f'[正在启动] {STRATEGY_NAME}') if IS_PROD: from delegate.xt_callback import XtCustomCallback from delegate.xt_delegate import XtDelegate diff --git a/selector/select_wencai.py b/selector/select_wencai.py index d64f5c7..2e1c34f 100644 --- a/selector/select_wencai.py +++ b/selector/select_wencai.py @@ -12,16 +12,16 @@ default_prompt = "中证500成分股,非ST,非科创,MACD金叉,按价格从小到大" # 这里自定义问财选股的问句prompt -def get_prompt(prompt_number: any = 0) -> str: +def get_prompt(prompt_index: any = 0) -> str: ans = default_prompt - if type(prompts) == list and prompt_number < len(prompts): - ans = prompts[prompt_number] + if type(prompts) == list and prompt_index < len(prompts): + ans = prompts[prompt_index] - if type(prompts) == dict and prompt_number in prompts: - ans = prompts[prompt_number] + if type(prompts) == dict and prompt_index in prompts: + ans = prompts[prompt_index] - print('选股问句:', ans, '\n') + print(f'[{prompt_index}]: {ans}') return ans diff --git a/tools/utils_basic.py b/tools/utils_basic.py index b8d5a76..ca62688 100644 --- a/tools/utils_basic.py +++ b/tools/utils_basic.py @@ -14,7 +14,8 @@ def debug(*args, **kwargs): # pandas dataframe 显示配置优化 def pd_show_all() -> None: - pd.set_option('display.width', None) + pd.set_option('display.expand_frame_repr', False) + pd.set_option('display.width', 2000) pd.set_option('display.min_rows', 9999) pd.set_option('display.max_rows', 9999) pd.set_option('display.max_columns', 200) @@ -24,16 +25,43 @@ def pd_show_all() -> None: # logging 模块的初始化配置 -def logging_init(path=None, level=logging.DEBUG, file_line=False): +def logging_init(path=None, level=logging.DEBUG, file_line=False, silence_libs=True): + """ + 初始化日志配置 + :param path: 日志文件路径,None则输出到控制台 + :param level: 日志级别 + :param file_line: 是否显示文件名和行号 + :param silence_libs: 是否静默第三方库日志 + """ file_line_fmt = "" if file_line: file_line_fmt = "%(filename)s[line:%(lineno)d] - %(levelname)s: " + logging.basicConfig( level=level, format=file_line_fmt + "%(asctime)s|%(message)s", filename=path ) + # 静默常见第三方库的日志(完全忽略) + if silence_libs: + noisy_libs = [ + 'gmtrade', 'gmtradelogger', 'tushare', 'urllib3', 'requests', + 'httpx', 'httpcore', 'asyncio', 'websockets', + 'apscheduler', 'schedule', 'chardet', + ] + # 既设置顶层 logger,也把已创建的同前缀子 logger 一并静默(例如 gmtrade.xxx) + for lib in noisy_libs: + logging.getLogger(lib).setLevel(logging.CRITICAL) + logging.getLogger(lib).propagate = False + + for name, obj in logging.root.manager.loggerDict.items(): + if not isinstance(obj, logging.Logger): + continue + if name == lib or name.startswith(lib + '.'): + obj.setLevel(logging.CRITICAL) # 只输出崩溃级日志 + obj.propagate = False # 阻止传播到 root logger + # 多文件 logger的配置 def logger_init(path=None, name='default') -> logging.Logger: diff --git a/tools/utils_cache.py b/tools/utils_cache.py index 2eb0844..cba61d9 100644 --- a/tools/utils_cache.py +++ b/tools/utils_cache.py @@ -300,7 +300,7 @@ def all_held_inc(lock: threading.Lock, path: str) -> bool: else: return False except Exception as e: - print('held days +1 failed! ', e) + print('[持仓计数] Held days +1 failed! ', e) return False diff --git a/tools/utils_remote.py b/tools/utils_remote.py index c5e5514..3d38505 100644 --- a/tools/utils_remote.py +++ b/tools/utils_remote.py @@ -522,7 +522,7 @@ def get_bao_daily_history( [symbol, exchange] = code.split('.') rs = bs.query_history_k_data_plus( f'{exchange.lower()}.{symbol}', - "date,code,open,high,low,close,volume,amount", + "date,code,open,high,low,close,volume,amount,peTTM", start_date=start, end_date=end, frequency='d', diff --git a/trader/buyer.py b/trader/buyer.py index 1ca0e4b..35368ab 100644 --- a/trader/buyer.py +++ b/trader/buyer.py @@ -97,7 +97,7 @@ def buy_selections( # 记录买入历史 if code not in today_buy[curr_date]: today_buy[curr_date].add(code) - logging.warning(f"记录选股 {code}\t现价: {price:.2f}") + logging.warning(f"[记录选股]{code}\t现价: {price:.2f}") else: break return today_buy @@ -115,14 +115,14 @@ def order_buy( buy_volume = volume if self.risk_control and buy_volume > self.slot_capacity / price: buy_volume = math.floor(self.slot_capacity / price / 100) * 100 - logging.warning(f'{code} 超过风险控制,买入量调整为 {buy_volume} 股') + logging.warning(f'[触发风控]{code} 超过风险控制,买入量调整为 {buy_volume} 股') if buy_volume < 1: - print(f'{code} 挂单买量为0,不委托') + logging.warning(f'[取消委托]{code} 挂单买量=0') return False if buy_volume < 200 and is_stock_kc(code): - print(f'{code} 科创最少200,不委托') + logging.warning(f'[取消委托]{code} 科创最少200') return False order_price = price + self.order_premium @@ -132,30 +132,33 @@ def order_buy( buy_type = '市买' if order_price > limit_price: # 如果涨停了只能挂限价单 + final_price = limit_price self.delegate.order_limit_open( code=code, - price=limit_price, + price=final_price, volume=buy_volume, remark=remark, strategy_name=self.strategy_name) else: + final_price = min(order_price, limit_price) self.delegate.order_market_open( code=code, - price=min(order_price, limit_price), + price=final_price, volume=buy_volume, remark=remark, strategy_name=self.strategy_name) else: buy_type = '限买' + final_price = min(order_price, limit_price) self.delegate.order_limit_open( code=code, - price=min(order_price, limit_price), + price=final_price, volume=buy_volume, remark=remark, strategy_name=self.strategy_name) if log: - logging.warning(f'{buy_type}委托 {code} \t现价:{price:.3f} {buy_volume}股') + logging.warning(f'[{buy_type}委托]{code}\t委托价:{final_price:.3f} {buy_volume}股 {remark} ') if self.delegate.callback is not None: self.delegate.callback.record_order( diff --git a/trader/seller.py b/trader/seller.py index 2223eb8..b8ad01e 100644 --- a/trader/seller.py +++ b/trader/seller.py @@ -23,6 +23,7 @@ def order_sell(self, code, quote, volume, remark, log=True) -> None: limit_price = get_limit_down_price(code, quote['lastClose']) if order_price < limit_price: # 如果跌停了只能挂限价单 + sell_type = '限卖' self.delegate.order_limit_close( code=code, price=limit_price, @@ -30,6 +31,7 @@ def order_sell(self, code, quote, volume, remark, log=True) -> None: remark=remark, strategy_name=self.strategy_name) else: + sell_type = '市卖' self.delegate.order_market_close( code=code, price=order_price, @@ -38,7 +40,7 @@ def order_sell(self, code, quote, volume, remark, log=True) -> None: strategy_name=self.strategy_name) if log: - logging.warning(f'{remark} {code}\t现价:{order_price:.3f} {volume}股') + logging.warning(f'[{sell_type}委托]{code}\t委托价:{order_price:.3f} {volume}股 {remark} ') if self.delegate.callback is not None: self.delegate.callback.record_order( @@ -46,11 +48,10 @@ def order_sell(self, code, quote, volume, remark, log=True) -> None: code=code, price=order_price, volume=volume, - side='卖出委托', + side=f'{sell_type}委托', remark=remark) - else: - print(f'{code} 挂单卖量为0,不委托') + logging.warning(f'[取消委托]{code} 挂单卖量=0') def execute_sell( self, diff --git a/trader/seller_components.py b/trader/seller_components.py index 3980d4e..f44123f 100644 --- a/trader/seller_components.py +++ b/trader/seller_components.py @@ -35,9 +35,15 @@ def check_sell( if curr_price <= switch_lower: self.order_sell(code, quote, sell_volume, f'跌{int((1 - self.risk_limit) * 100)}%硬止损') + logging.warning(f'[触发卖出]止损 ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ') return True elif curr_price >= cost_price * self.earn_limit: self.order_sell(code, quote, sell_volume, f'涨{int((self.earn_limit - 1) * 100)}%硬止盈') + logging.warning(f'[触发卖出]止盈 ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ') return True return False @@ -50,7 +56,7 @@ def __init__(self, strategy_name, delegate, parameters): BaseSeller.__init__(self, strategy_name, delegate, parameters) print('临跌停卖点模块', end=' ') self.safe_time_range = parameters.hard_time_range - self.safe_rate = parameters.safe_rate + self.safe_rate = parameters.safe_rate # 在跌停价上方百分之多少卖出,例:0.01 = 还有 1% 跌停时就开始挂卖单 def check_sell( self, code: str, quote: Dict, curr_date: str, curr_time: str, @@ -66,7 +72,12 @@ def check_sell( stop_price = last_close * stop_rate if curr_price <= stop_price: - self.order_sell(code, quote, sell_volume, f'临跌停{int((1 - stop_rate) * 100)}%') + self.order_sell(code, quote, sell_volume, f'走低{int((1 - stop_rate) * 100)}%') + + cost_price = position.open_price + logging.warning(f'[触发卖出]走低 ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ') return True return False @@ -95,6 +106,9 @@ def check_sell( if curr_price < switch_upper: # 未满足盈利目标的仓位 self.order_sell(code, quote, sell_volume, f'{self.switch_hold_days}日换仓卖单') + logging.warning(f'[触发卖出]换仓 ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ') return True return False @@ -123,11 +137,12 @@ def check_sell( for inc_min, inc_max, fall_threshold in self.fall_from_top: # 逐级回落卖出 if (cost_price * inc_min <= max_price < cost_price * inc_max) \ and curr_price < max_price * (1 - fall_threshold): - logging.warning(f'[Sell]' - f'cost_p:{cost_price} max_p:{max_price} ' - f'inc_min:{inc_min} inc_max:{inc_max}') self.order_sell(code, quote, sell_volume, f'涨{int((inc_min - 1) * 100)}%回落{int(fall_threshold * 100)}%') + logging.warning(f'[触发卖出]回落 ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ' + f'最高:{max_price} 区间:{inc_min}-{inc_max} 回落阈值:{fall_threshold}') return True return False @@ -156,11 +171,12 @@ def check_sell( for inc_min, inc_max, fall_percentage in self.return_of_profit: # 逐级回落止盈 if (cost_price * inc_min <= max_price < cost_price * inc_max) \ and curr_price < max_price - (max_price - cost_price) * fall_percentage: - logging.warning(f'[Sell]' - f'cost_p:{cost_price} max_p:{max_price} ' - f'inc_min:{inc_min} inc_max:{inc_max}') self.order_sell(code, quote, sell_volume, f'涨{int((inc_min - 1) * 100)}%回撤{int(fall_percentage * 100)}%') + logging.warning(f'[触发卖出]回撤 ' + f'成本:{round(cost_price, 3)} 卖价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ' + f'最高:{max_price} 区间:{inc_min}-{inc_max} 回撤比值:{fall_percentage}') return True return False @@ -210,11 +226,16 @@ def check_sell( if 0 < held_day < len(history): sell_volume = position.can_use_volume curr_price = quote['lastPrice'] + cost_price = position.open_price open_day_low = history['low'].values[-held_day] * self.open_low_rate # 建仓日新低破掉卖 if curr_price < open_day_low: self.order_sell(code, quote, sell_volume, '破开仓日新低') + logging.warning(f'[触发卖出]开仓日最低 ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ' + f'开仓日最低价:{open_day_low} ') return True # 建仓日尾盘缩量卖出 @@ -224,6 +245,10 @@ def check_sell( open_day_volume = history['volume'].values[-held_day] * self.open_vol_rate if curr_volume < open_day_volume: self.order_sell(code, quote, sell_volume, '缩开仓日地量') + logging.warning(f'[触发卖出]开仓日缩量 ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ' + f'建仓日成交量:{open_day_volume}') return True return False @@ -256,6 +281,11 @@ def check_sell( if curr_price <= ma_value - 0.01: self.order_sell(code, quote, sell_volume, f'破{self.ma_above}日均{ma_value:.2f}') + cost_price = position.open_price + logging.warning(f'[触发卖出]破均线 ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ' + f'均线价格:{ma_value}') return True return False @@ -287,10 +317,23 @@ def check_sell( if cci[0] > self.cci_lower > cci[1]: # CCI 下穿 self.order_sell(code, quote, sell_volume, f'CCI高于{self.cci_lower}') + + curr_price = quote['lastPrice'] + cost_price = position.open_price + logging.warning(f'[触发卖出]CCI ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ' + f'当前CCI下沿:{self.cci_lower}') return True if cci[0] < self.cci_upper < cci[1]: # CCI 上穿 self.order_sell(code, quote, sell_volume, f'CCI低于{self.cci_upper}') + curr_price = quote['lastPrice'] + cost_price = position.open_price + logging.warning(f'[触发卖出]CCI ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ' + f'当前CCI上沿:{self.cci_upper}') return True return False @@ -321,6 +364,13 @@ def check_sell( if wr[0] < self.wr_cross < wr[1]: # WR 上穿 self.order_sell(code, quote, sell_volume, f'WR上穿{self.wr_cross}卖') + curr_price = quote['lastPrice'] + cost_price = position.open_price + logging.warning(f'[触发卖出]WR ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ' + f'当前WR阈值:{self.wr_cross}') + return True return False @@ -356,6 +406,10 @@ def check_sell( if curr_vol < open_vol * self.next_volume_dec_threshold \ and cost_price < curr_price < prev_close * self.next_volume_dec_limit: self.order_sell(code, quote, sell_volume, '次日缩量') + logging.warning(f'[触发卖出]次缩 ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ' + f'次缩阈值:{self.next_volume_dec_threshold}') return True return False @@ -394,6 +448,13 @@ def check_sell( and drop_price > last_close * drop_threshold: self.order_sell(code, quote, sell_volume, f'高开{int((inc_min - 1) * 100)}跌{int(drop_threshold * 100)}%') + + curr_price = quote['lastPrice'] + cost_price = position.open_price + logging.warning(f'[触发卖出]高开 ' + f'成本:{round(cost_price, 3)} 现价:{round(curr_price, 3)} ' + f'涨跌:{round((curr_price / cost_price - 1) * 100, 3)} ' + f'高开范围:{inc_min}-{inc_max} 下落:{drop_threshold}%') return True return False