"""A 股涨跌停纯函数测试(板块表 + 封板判断)。全用 raw 价格。""" import pandas as pd from sanguo_trader.limit import ( get_board, limit_ratio, limit_up_price, limit_down_price, is_one_word_lock, is_t_lock, is_locked_for_buy_symbol, is_locked_for_sell_symbol, ) from sanguo_trader.models import AccountConfig CFG = AccountConfig(initial_capital=1_000_000) def bar(open, high, low, close): return pd.Series({"open": open, "high": high, "low": low, "close": close}) # ---- 板块分类 ---- def test_board_classification(): assert get_board("600000") == "main" # 沪市主板 assert get_board("000001") == "main" # 深市主板 assert get_board("300750") == "gem" # 创业板 assert get_board("301128") == "gem" # 创业板(301) assert get_board("688981") == "star" # 科创板 assert get_board("689009") == "star" # 科创板(689) assert get_board("830799") == "bse" # 北交所 assert get_board("920002") == "bse" # 北交所(920) # ---- 幅度查表 ---- def test_limit_ratio(): assert limit_ratio("main", is_st=False) == 0.10 assert limit_ratio("gem", is_st=False) == 0.20 assert limit_ratio("star", is_st=False) == 0.20 assert limit_ratio("bse", is_st=False) == 0.30 # ST 统一 5% assert limit_ratio("main", is_st=True) == 0.05 assert limit_ratio("gem", is_st=True) == 0.05 assert limit_ratio("star", is_st=True) == 0.05 assert limit_ratio("bse", is_st=True) == 0.30 # 北交所 ST 同 30% # ---- 涨跌停价(按 pricetick 取整)---- def test_limit_up_price_rounds_to_pricetick(): # 10.00 * 1.10 = 11.00 assert limit_up_price(10.0, 0.10, 0.01) == 11.0 # 9.99 * 1.20 = 11.988 → 11.99 assert limit_up_price(9.99, 0.20, 0.01) == 11.99 # 5.00 * 1.30 = 6.5 assert limit_up_price(5.0, 0.30, 0.01) == 6.5 def test_limit_down_price_rounds_to_pricetick(): # 10.00 * 0.90 = 9.00 assert limit_down_price(10.0, 0.10, 0.01) == 9.0 # 10.00 * 0.80 = 8.00 assert limit_down_price(10.0, 0.20, 0.01) == 8.0 # ---- 一字板 / T 字板 ---- def test_one_word_lock_detected(): up = limit_up_price(10.0, 0.10, 0.01) assert is_one_word_lock(bar(11.0, 11.0, 11.0, 11.0), up) is True # 非一字 assert is_one_word_lock(bar(11.0, 11.5, 10.8, 11.0), up) is False assert is_one_word_lock(bar(11.0, 11.0, 10.5, 11.0), up) is False # T 字板非一字 def test_t_lock_detected(): up = limit_up_price(10.0, 0.10, 0.01) # T 字板:开=涨停 收=涨停 lowopen(盘中反弹过) assert is_locked_for_sell_symbol( bar(9.0, 9.5, 9.0, 9.0), "600000", 10.0, CFG, is_st=False ) is True def test_sell_not_locked_when_opened(): # 非跌停形态 → 可卖 assert is_locked_for_sell_symbol( bar(9.5, 9.8, 9.2, 9.6), "600000", 10.0, CFG, is_st=False ) is False # ---- C1: 浮点 == 比较漏判封板 ---- # limit_up_price(5.05, 0.10, 0.01) 由于浮点 = 5.5600000000000005, # bar.open=5.56 时若用 == 比较会判 False → 一字板当可成交(CRITICAL)。 def test_one_word_lock_float_precision_main_board(): """5.05 一字涨停:limit_up 浮点尾数差不应漏判。""" up = limit_up_price(5.05, 0.10, 0.01) # 触发条件:浮点 == 比较会失败(5.5600000000000005 != 5.56) assert is_one_word_lock(bar(5.56, 5.56, 5.56, 5.56), up) is True assert is_locked_for_buy_symbol( bar(5.56, 5.56, 5.56, 5.56), "600000", 5.05, CFG, is_st=False ) is True def test_one_word_lock_float_precision_gem_board(): """创业板 300750,9.99×1.2 浮点尾数差。""" up = limit_up_price(9.99, 0.20, 0.01) # 11.988 → 11.99 assert is_one_word_lock(bar(11.99, 11.99, 11.99, 11.99), up) is True assert is_locked_for_buy_symbol( bar(11.99, 11.99, 11.99, 11.99), "300750", 9.99, CFG, is_st=False ) is True def test_t_lock_float_precision_st_stock(): """ST 0.35×1.05 = 0.3675 → 0.37(浮点尾数差),T 字板。""" up = limit_up_price(0.35, 0.05, 0.01) # T 字板:开=收=涨停,low