Redesign roll calculator with auto first entry and chained add legs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-23 17:44:27 +08:00
parent 253d353206
commit d938bc6c59
6 changed files with 482 additions and 112 deletions
+62 -11
View File
@@ -1,6 +1,10 @@
"""hub_calculator_lib 测算逻辑。"""
from hub_calculator_lib import calc_roll_calculator, calc_trend_calculator
from hub_calculator_lib import (
calc_initial_roll_qty,
calc_roll_calculator,
calc_trend_calculator,
)
def test_trend_calculator_long_basic():
@@ -21,8 +25,6 @@ def test_trend_calculator_long_basic():
assert data["risk_budget_u"] == 50.0
assert len(data["rows"]) >= 2
assert data["rows"][0]["label"] == "首仓"
assert data["first_profit_u"] is not None
assert data["first_profit_u"] > 0
def test_trend_calculator_short_rejects_bad_bounds():
@@ -41,20 +43,69 @@ def test_trend_calculator_short_rejects_bad_bounds():
assert err is not None
def test_roll_calculator_long():
def test_roll_calculator_first_leg_auto():
data, err = calc_roll_calculator(
direction="long",
capital_usdt=1000,
risk_percent=5,
qty_existing=10,
entry_existing=100,
entry_price=100,
stop_loss=95,
take_profit=120,
add_price=105,
new_stop_loss=98,
add_legs=[],
legs_done=0,
)
assert err is None
assert data is not None
assert data["add_contracts"] > 0
assert data["qty_after"] > 10
assert data["profit_at_tp_u"] is not None
assert data["first_contracts"] == 10.0
assert len(data["rows"]) == 1
assert data["rows"][0]["loss_at_sl_u"] == 50.0
assert data["rows"][0]["profit_at_tp_u"] == 200.0
def test_roll_calculator_chain_two_legs():
data, err = calc_roll_calculator(
direction="long",
capital_usdt=1000,
risk_percent=5,
entry_price=100,
stop_loss=95,
take_profit=120,
add_legs=[
{"add_price": 105, "new_stop_loss": 98},
{"add_price": 108, "new_stop_loss": 101},
],
legs_done=0,
)
assert err is None
assert data is not None
assert len(data["rows"]) == 3
assert data["rows"][0]["label"] == "首仓"
assert data["rows"][1]["label"] == "滚仓1"
assert data["rows"][2]["label"] == "滚仓2"
assert float(data["final_contracts"]) > float(data["first_contracts"])
def test_roll_calculator_rejects_too_many_legs():
data, err = calc_roll_calculator(
direction="long",
capital_usdt=1000,
risk_percent=5,
entry_price=100,
stop_loss=95,
take_profit=120,
add_legs=[
{"add_price": 105, "new_stop_loss": 98},
{"add_price": 108, "new_stop_loss": 101},
{"add_price": 110, "new_stop_loss": 103},
{"add_price": 112, "new_stop_loss": 105},
],
legs_done=0,
)
assert data is None
assert err is not None
def test_initial_roll_qty():
qty, err = calc_initial_roll_qty("long", 100, 95, 50)
assert err is None
assert qty == 10.0