|
@@ -8,10 +8,11 @@ from fastapi.testclient import TestClient
|
|
|
from src.trader_mcp import strategy_registry, strategy_store
|
|
from src.trader_mcp import strategy_registry, strategy_store
|
|
|
from src.trader_mcp.server import app
|
|
from src.trader_mcp.server import app
|
|
|
from src.trader_mcp.strategy_context import StrategyContext
|
|
from src.trader_mcp.strategy_context import StrategyContext
|
|
|
|
|
+from src.trader_mcp.strategy_sizing import cap_amount_to_balance_target
|
|
|
from src.trader_mcp.strategy_sdk import Strategy as BaseStrategy
|
|
from src.trader_mcp.strategy_sdk import Strategy as BaseStrategy
|
|
|
from strategies.exposure_protector import Strategy as ExposureStrategy
|
|
from strategies.exposure_protector import Strategy as ExposureStrategy
|
|
|
|
|
+from strategies.dumb_trader import Strategy as DumbStrategy
|
|
|
from strategies.grid_trader import Strategy as GridStrategy
|
|
from strategies.grid_trader import Strategy as GridStrategy
|
|
|
-from strategies.trend_follower import Strategy as TrendStrategy
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
STRATEGY_CODE = '''
|
|
STRATEGY_CODE = '''
|
|
@@ -172,7 +173,7 @@ def test_grid_supervision_exposes_adverse_side_open_orders():
|
|
|
assert "sell ladder exposed" in " ".join(supervision["concerns"])
|
|
assert "sell ladder exposed" in " ".join(supervision["concerns"])
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_trend_and_protector_supervision_reports_facts_only():
|
|
|
|
|
|
|
+def test_dumb_trader_and_protector_supervision_reports_facts_only():
|
|
|
class FakeContext:
|
|
class FakeContext:
|
|
|
account_id = "acct-1"
|
|
account_id = "acct-1"
|
|
|
market_symbol = "xrpusd"
|
|
market_symbol = "xrpusd"
|
|
@@ -180,7 +181,7 @@ def test_trend_and_protector_supervision_reports_facts_only():
|
|
|
counter_currency = "USD"
|
|
counter_currency = "USD"
|
|
|
mode = "active"
|
|
mode = "active"
|
|
|
|
|
|
|
|
- trend = TrendStrategy(FakeContext(), {"trade_side": "buy", "order_notional_quote": 1.0})
|
|
|
|
|
|
|
+ trend = DumbStrategy(FakeContext(), {"trade_side": "buy", "order_notional_quote": 1.0})
|
|
|
trend.state.update({"last_price": 1.45, "base_available": 20.0, "counter_available": 20.0, "last_order_at": 0.0})
|
|
trend.state.update({"last_price": 1.45, "base_available": 20.0, "counter_available": 20.0, "last_order_at": 0.0})
|
|
|
trend_supervision = trend._supervision()
|
|
trend_supervision = trend._supervision()
|
|
|
assert trend_supervision["trade_side"] == "buy"
|
|
assert trend_supervision["trade_side"] == "buy"
|
|
@@ -657,7 +658,7 @@ def test_base_strategy_report_uses_context_snapshot():
|
|
|
assert report["position"]["open_orders"][0]["id"] == "o1"
|
|
assert report["position"]["open_orders"][0]["id"] == "o1"
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_trend_follower_uses_policy_and_reports_fit():
|
|
|
|
|
|
|
+def test_dumb_trader_uses_policy_and_reports_fit():
|
|
|
class FakeContext:
|
|
class FakeContext:
|
|
|
id = "s-2"
|
|
id = "s-2"
|
|
|
account_id = "acct-2"
|
|
account_id = "acct-2"
|
|
@@ -676,14 +677,14 @@ def test_trend_follower_uses_policy_and_reports_fit():
|
|
|
def get_strategy_snapshot(self):
|
|
def get_strategy_snapshot(self):
|
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
|
|
|
|
|
|
- strat = TrendStrategy(FakeContext(), {"trade_side": "buy", "order_notional_quote": 1.5})
|
|
|
|
|
|
|
+ strat = DumbStrategy(FakeContext(), {"trade_side": "buy", "order_notional_quote": 1.5})
|
|
|
strat.apply_policy()
|
|
strat.apply_policy()
|
|
|
report = strat.report()
|
|
report = strat.report()
|
|
|
- assert report["fit"]["risk_profile"] == "growth"
|
|
|
|
|
|
|
+ assert report["fit"]["risk_profile"] == "neutral"
|
|
|
assert strat.state["policy_derived"]["order_notional_quote"] > 0
|
|
assert strat.state["policy_derived"]["order_notional_quote"] > 0
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_trend_follower_buys_from_bull_regime_without_explicit_strength():
|
|
|
|
|
|
|
+def test_dumb_trader_buys_on_configured_side_without_regime_input():
|
|
|
class FakeContext:
|
|
class FakeContext:
|
|
|
id = "s-bull"
|
|
id = "s-bull"
|
|
|
account_id = "acct-1"
|
|
account_id = "acct-1"
|
|
@@ -715,15 +716,15 @@ def test_trend_follower_buys_from_bull_regime_without_explicit_strength():
|
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
|
|
|
|
|
|
ctx = FakeContext()
|
|
ctx = FakeContext()
|
|
|
- strat = TrendStrategy(ctx, {"trade_side": "buy", "order_notional_quote": 2.0})
|
|
|
|
|
|
|
+ strat = DumbStrategy(ctx, {"trade_side": "buy", "order_notional_quote": 20.0})
|
|
|
result = strat.on_tick({})
|
|
result = strat.on_tick({})
|
|
|
assert result["action"] == "buy"
|
|
assert result["action"] == "buy"
|
|
|
assert ctx.orders[-1]["side"] == "buy"
|
|
assert ctx.orders[-1]["side"] == "buy"
|
|
|
- assert ctx.orders[-1]["amount"] == 2.0 / 1.2
|
|
|
|
|
- assert strat.state["last_action"] == "buy_trend"
|
|
|
|
|
|
|
+ assert ctx.orders[-1]["amount"] == 20.0 / 1.2
|
|
|
|
|
+ assert strat.state["last_action"] == "buy_dumb"
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_trend_follower_sells_from_bear_regime_without_explicit_strength():
|
|
|
|
|
|
|
+def test_dumb_trader_sells_on_configured_side_without_regime_input():
|
|
|
class FakeContext:
|
|
class FakeContext:
|
|
|
id = "s-bear"
|
|
id = "s-bear"
|
|
|
account_id = "acct-1"
|
|
account_id = "acct-1"
|
|
@@ -755,15 +756,15 @@ def test_trend_follower_sells_from_bear_regime_without_explicit_strength():
|
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
|
|
|
|
|
|
ctx = FakeContext()
|
|
ctx = FakeContext()
|
|
|
- strat = TrendStrategy(ctx, {"trade_side": "sell", "order_notional_quote": 2.0})
|
|
|
|
|
|
|
+ strat = DumbStrategy(ctx, {"trade_side": "sell", "order_notional_quote": 20.0})
|
|
|
result = strat.on_tick({})
|
|
result = strat.on_tick({})
|
|
|
assert result["action"] == "sell"
|
|
assert result["action"] == "sell"
|
|
|
assert ctx.orders[-1]["side"] == "sell"
|
|
assert ctx.orders[-1]["side"] == "sell"
|
|
|
- assert ctx.orders[-1]["amount"] == 2.0 / 1.2
|
|
|
|
|
- assert strat.state["last_action"] == "sell_trend"
|
|
|
|
|
|
|
+ assert ctx.orders[-1]["amount"] == 20.0 / 1.2
|
|
|
|
|
+ assert strat.state["last_action"] == "sell_dumb"
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_trend_follower_buy_only_ignores_bear_regime():
|
|
|
|
|
|
|
+def test_dumb_trader_buy_only_ignores_bear_regime():
|
|
|
class FakeContext:
|
|
class FakeContext:
|
|
|
id = "s-buy-only"
|
|
id = "s-buy-only"
|
|
|
account_id = "acct-1"
|
|
account_id = "acct-1"
|
|
@@ -795,20 +796,20 @@ def test_trend_follower_buy_only_ignores_bear_regime():
|
|
|
minimum_order_value = 10.0
|
|
minimum_order_value = 10.0
|
|
|
|
|
|
|
|
def suggest_order_amount(self, **kwargs):
|
|
def suggest_order_amount(self, **kwargs):
|
|
|
- return 6.0
|
|
|
|
|
|
|
+ return 10.0
|
|
|
|
|
|
|
|
def get_strategy_snapshot(self):
|
|
def get_strategy_snapshot(self):
|
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
|
|
|
|
|
|
ctx = FakeContext()
|
|
ctx = FakeContext()
|
|
|
- strat = TrendStrategy(ctx, {"trade_side": "buy", "order_notional_quote": 2.0})
|
|
|
|
|
|
|
+ strat = DumbStrategy(ctx, {"trade_side": "buy", "order_notional_quote": 20.0})
|
|
|
result = strat.on_tick({})
|
|
result = strat.on_tick({})
|
|
|
assert result["action"] == "buy"
|
|
assert result["action"] == "buy"
|
|
|
assert ctx.orders[-1]["side"] == "buy"
|
|
assert ctx.orders[-1]["side"] == "buy"
|
|
|
- assert strat.state["last_action"] == "buy_trend"
|
|
|
|
|
|
|
+ assert strat.state["last_action"] == "buy_dumb"
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_trend_follower_sell_only_ignores_bull_regime():
|
|
|
|
|
|
|
+def test_dumb_trader_sell_only_ignores_bull_regime():
|
|
|
class FakeContext:
|
|
class FakeContext:
|
|
|
id = "s-sell-only"
|
|
id = "s-sell-only"
|
|
|
account_id = "acct-1"
|
|
account_id = "acct-1"
|
|
@@ -846,14 +847,14 @@ def test_trend_follower_sell_only_ignores_bull_regime():
|
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
|
|
|
|
|
|
ctx = FakeContext()
|
|
ctx = FakeContext()
|
|
|
- strat = TrendStrategy(ctx, {"trade_side": "sell", "order_notional_quote": 2.0})
|
|
|
|
|
|
|
+ strat = DumbStrategy(ctx, {"trade_side": "sell", "order_notional_quote": 20.0})
|
|
|
result = strat.on_tick({})
|
|
result = strat.on_tick({})
|
|
|
assert result["action"] == "sell"
|
|
assert result["action"] == "sell"
|
|
|
assert ctx.orders[-1]["side"] == "sell"
|
|
assert ctx.orders[-1]["side"] == "sell"
|
|
|
- assert strat.state["last_action"] == "sell_trend"
|
|
|
|
|
|
|
+ assert strat.state["last_action"] == "sell_dumb"
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_trend_follower_policy_does_not_override_explicit_order_notional_quote():
|
|
|
|
|
|
|
+def test_dumb_trader_policy_does_not_override_explicit_order_notional_quote():
|
|
|
class FakeContext:
|
|
class FakeContext:
|
|
|
id = "s-explicit"
|
|
id = "s-explicit"
|
|
|
account_id = "acct-1"
|
|
account_id = "acct-1"
|
|
@@ -869,13 +870,13 @@ def test_trend_follower_policy_does_not_override_explicit_order_notional_quote()
|
|
|
def get_strategy_snapshot(self):
|
|
def get_strategy_snapshot(self):
|
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
|
|
|
|
|
|
- strat = TrendStrategy(FakeContext(), {"trade_side": "buy", "order_notional_quote": 10.5})
|
|
|
|
|
|
|
+ strat = DumbStrategy(FakeContext(), {"trade_side": "buy", "order_notional_quote": 10.5})
|
|
|
strat.apply_policy()
|
|
strat.apply_policy()
|
|
|
assert strat.config["order_notional_quote"] == 10.5
|
|
assert strat.config["order_notional_quote"] == 10.5
|
|
|
assert strat.state["policy_derived"]["order_notional_quote"] == 10.5
|
|
assert strat.state["policy_derived"]["order_notional_quote"] == 10.5
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_trend_follower_passes_live_fee_rate_into_sizing_helper():
|
|
|
|
|
|
|
+def test_dumb_trader_passes_live_fee_rate_into_sizing_helper():
|
|
|
class FakeContext:
|
|
class FakeContext:
|
|
|
id = "s-fee"
|
|
id = "s-fee"
|
|
|
account_id = "acct-1"
|
|
account_id = "acct-1"
|
|
@@ -911,7 +912,7 @@ def test_trend_follower_passes_live_fee_rate_into_sizing_helper():
|
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
|
|
|
|
|
|
ctx = FakeContext()
|
|
ctx = FakeContext()
|
|
|
- strat = TrendStrategy(ctx, {"trade_side": "buy", "order_notional_quote": 10.5, "dust_collect": True})
|
|
|
|
|
|
|
+ strat = DumbStrategy(ctx, {"trade_side": "buy", "order_notional_quote": 10.5, "dust_collect": True})
|
|
|
strat.on_tick({})
|
|
strat.on_tick({})
|
|
|
assert ctx.fee_calls == ["xrpusd"]
|
|
assert ctx.fee_calls == ["xrpusd"]
|
|
|
assert ctx.suggest_calls[-1]["fee_rate"] == 0.0025
|
|
assert ctx.suggest_calls[-1]["fee_rate"] == 0.0025
|
|
@@ -957,7 +958,7 @@ def test_grid_sizing_helper_receives_quote_controls_and_dust_collect():
|
|
|
assert ctx.suggest_calls[-1]["levels"] == 3
|
|
assert ctx.suggest_calls[-1]["levels"] == 3
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_trend_follower_buy_clamps_last_order_to_balance_target():
|
|
|
|
|
|
|
+def test_dumb_trader_buy_uses_requested_notional_even_with_balance_target_configured():
|
|
|
class FakeContext:
|
|
class FakeContext:
|
|
|
id = "s-buy-clamp"
|
|
id = "s-buy-clamp"
|
|
|
account_id = "acct-1"
|
|
account_id = "acct-1"
|
|
@@ -991,15 +992,15 @@ def test_trend_follower_buy_clamps_last_order_to_balance_target():
|
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
|
|
|
|
|
|
ctx = FakeContext()
|
|
ctx = FakeContext()
|
|
|
- strat = TrendStrategy(ctx, {"trade_side": "buy", "order_notional_quote": 3.0, "balance_target": 0.5})
|
|
|
|
|
|
|
+ strat = DumbStrategy(ctx, {"trade_side": "buy", "order_notional_quote": 3.0, "balance_target": 0.5})
|
|
|
|
|
|
|
|
result = strat.on_tick({})
|
|
result = strat.on_tick({})
|
|
|
|
|
|
|
|
assert result["action"] == "buy"
|
|
assert result["action"] == "buy"
|
|
|
- assert ctx.orders[-1]["amount"] == 1.0
|
|
|
|
|
|
|
+ assert ctx.orders[-1]["amount"] == 3.0
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_trend_follower_sell_clamps_last_order_to_balance_target():
|
|
|
|
|
|
|
+def test_dumb_trader_sell_uses_requested_notional_even_with_balance_target_configured():
|
|
|
class FakeContext:
|
|
class FakeContext:
|
|
|
id = "s-sell-clamp"
|
|
id = "s-sell-clamp"
|
|
|
account_id = "acct-1"
|
|
account_id = "acct-1"
|
|
@@ -1033,15 +1034,58 @@ def test_trend_follower_sell_clamps_last_order_to_balance_target():
|
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
|
|
|
|
|
|
ctx = FakeContext()
|
|
ctx = FakeContext()
|
|
|
- strat = TrendStrategy(ctx, {"trade_side": "sell", "order_notional_quote": 5.0, "balance_target": 0.5})
|
|
|
|
|
|
|
+ strat = DumbStrategy(ctx, {"trade_side": "sell", "order_notional_quote": 5.0, "balance_target": 0.5})
|
|
|
|
|
|
|
|
result = strat.on_tick({})
|
|
result = strat.on_tick({})
|
|
|
|
|
|
|
|
assert result["action"] == "sell"
|
|
assert result["action"] == "sell"
|
|
|
- assert ctx.orders[-1]["amount"] == 2.0
|
|
|
|
|
|
|
+ assert ctx.orders[-1]["amount"] == 5.0
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_trend_follower_holds_when_balance_target_already_reached():
|
|
|
|
|
|
|
+def test_dumb_trader_sell_holds_sub_minimum_order():
|
|
|
|
|
+ class FakeContext:
|
|
|
|
|
+ id = "s-sell-min"
|
|
|
|
|
+ account_id = "acct-1"
|
|
|
|
|
+ client_id = "cid-1"
|
|
|
|
|
+ mode = "active"
|
|
|
|
|
+ market_symbol = "solusd"
|
|
|
|
|
+ base_currency = "SOL"
|
|
|
|
|
+ counter_currency = "USD"
|
|
|
|
|
+ minimum_order_value = 1.0
|
|
|
|
|
+
|
|
|
|
|
+ def __init__(self):
|
|
|
|
|
+ self.orders = []
|
|
|
|
|
+
|
|
|
|
|
+ def get_price(self, symbol):
|
|
|
|
|
+ return {"price": 86.20062}
|
|
|
|
|
+
|
|
|
|
|
+ def get_fee_rates(self, market_symbol=None):
|
|
|
|
|
+ return {"maker": 0.0, "taker": 0.0}
|
|
|
|
|
+
|
|
|
|
|
+ def suggest_order_amount(self, **kwargs):
|
|
|
|
|
+ return 0.001
|
|
|
|
|
+
|
|
|
|
|
+ def place_order(self, **kwargs):
|
|
|
|
|
+ self.orders.append(kwargs)
|
|
|
|
|
+ return {"ok": True, "order": kwargs}
|
|
|
|
|
+
|
|
|
|
|
+ def get_account_info(self):
|
|
|
|
|
+ return {"balances": [{"asset_code": "USD", "available": 1000.0}, {"asset_code": "SOL", "available": 0.00447}]}
|
|
|
|
|
+
|
|
|
|
|
+ def get_strategy_snapshot(self):
|
|
|
|
|
+ return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
|
|
|
+
|
|
|
|
|
+ ctx = FakeContext()
|
|
|
|
|
+ strat = DumbStrategy(ctx, {"trade_side": "sell", "order_notional_quote": 5.0, "balance_target": 1.0})
|
|
|
|
|
+
|
|
|
|
|
+ result = strat.on_tick({})
|
|
|
|
|
+
|
|
|
|
|
+ assert result["action"] == "hold"
|
|
|
|
|
+ assert result["reason"] == "no usable size"
|
|
|
|
|
+ assert ctx.orders == []
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def test_dumb_trader_holds_when_trade_side_is_symmetrical():
|
|
|
class FakeContext:
|
|
class FakeContext:
|
|
|
id = "s-target-hold"
|
|
id = "s-target-hold"
|
|
|
account_id = "acct-1"
|
|
account_id = "acct-1"
|
|
@@ -1067,15 +1111,45 @@ def test_trend_follower_holds_when_balance_target_already_reached():
|
|
|
def get_strategy_snapshot(self):
|
|
def get_strategy_snapshot(self):
|
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
|
|
|
|
|
|
- strat = TrendStrategy(FakeContext(), {"trade_side": "buy", "order_notional_quote": 3.0, "balance_target": 0.5})
|
|
|
|
|
|
|
+ strat = DumbStrategy(FakeContext(), {"trade_side": "both", "order_notional_quote": 3.0, "balance_target": 0.5})
|
|
|
|
|
|
|
|
result = strat.on_tick({})
|
|
result = strat.on_tick({})
|
|
|
|
|
|
|
|
assert result["action"] == "hold"
|
|
assert result["action"] == "hold"
|
|
|
- assert result["reason"] == "balance target reached"
|
|
|
|
|
|
|
+ assert result["reason"] == "trade_side must be buy or sell"
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def test_cap_amount_to_balance_target_caps_sell_to_live_base():
|
|
|
|
|
+ amount = cap_amount_to_balance_target(
|
|
|
|
|
+ suggested_amount=0.127226,
|
|
|
|
|
+ side="sell",
|
|
|
|
|
+ price=86.20062,
|
|
|
|
|
+ fee_rate=0.0,
|
|
|
|
|
+ balance_target=1.0,
|
|
|
|
|
+ base_available=0.00447,
|
|
|
|
|
+ counter_available=0.0,
|
|
|
|
|
+ min_notional=0.0,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ assert amount == 0.00447
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def test_cap_amount_to_balance_target_rejects_sell_below_min_notional():
|
|
|
|
|
+ amount = cap_amount_to_balance_target(
|
|
|
|
|
+ suggested_amount=0.127226,
|
|
|
|
|
+ side="sell",
|
|
|
|
|
+ price=86.20062,
|
|
|
|
|
+ fee_rate=0.0,
|
|
|
|
|
+ balance_target=1.0,
|
|
|
|
|
+ base_available=0.00447,
|
|
|
|
|
+ counter_available=0.0,
|
|
|
|
|
+ min_notional=1.0,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ assert amount == 0.0
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_trend_follower_balance_target_one_does_not_clamp_size():
|
|
|
|
|
|
|
+def test_dumb_trader_ignores_balance_target_and_keeps_size():
|
|
|
class FakeContext:
|
|
class FakeContext:
|
|
|
id = "s-target-open"
|
|
id = "s-target-open"
|
|
|
account_id = "acct-1"
|
|
account_id = "acct-1"
|
|
@@ -1109,7 +1183,7 @@ def test_trend_follower_balance_target_one_does_not_clamp_size():
|
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
return {"identity": {}, "control": {}, "position": {}, "orders": {}, "execution": {}}
|
|
|
|
|
|
|
|
ctx = FakeContext()
|
|
ctx = FakeContext()
|
|
|
- strat = TrendStrategy(ctx, {"trade_side": "buy", "order_notional_quote": 3.0, "balance_target": 1.0})
|
|
|
|
|
|
|
+ strat = DumbStrategy(ctx, {"trade_side": "buy", "order_notional_quote": 3.0, "balance_target": 0.5})
|
|
|
|
|
|
|
|
result = strat.on_tick({})
|
|
result = strat.on_tick({})
|
|
|
|
|
|