Feat/panoptic straddle strategy - #84
Open
Rythys wants to merge 4 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new core strategy module implementing a Panoptic-based long straddle with a Uniswap V3 LP baseline, plus accompanying unit/e2e tests and documentation updates.
Changes:
- Introduce
PanopticStraddleStrategy/PanopticStraddleEntityand supporting state/params infractal/strategies/panoptic_straddle.py. - Add L1 unit tests and L2 synthetic e2e tests for the new strategy.
- Expose the strategy from
fractal.strategies, and update README badge + CHANGELOG entry.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
fractal/strategies/panoptic_straddle.py |
Adds the new Panoptic straddle entity + strategy implementation. |
fractal/strategies/__init__.py |
Re-exports new strategy/entity/state/params via package init and __all__. |
tests/core/test_panoptic_straddle.py |
Adds unit/invariant tests for the straddle entity/strategy (currently inlines copies). |
tests/core/e2e/test_e2e_panoptic_straddle_synthetic.py |
Adds deterministic synthetic e2e coverage for strategy branches/metrics. |
README.md |
Updates DOI badge formatting/source. |
CHANGELOG.md |
Adds an Unreleased entry documenting the new strategy. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+21
to
+37
| # --------------------------------------------------------------------------- | ||
| # Inline copies of the strategy classes. | ||
| # In the real PR these live in fractal/strategies/panoptic_straddle.py and | ||
| # are imported from there. They are inlined here so the test file is | ||
| # self-contained and can be reviewed independently of the strategy PR. | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| from collections import deque | ||
| from dataclasses import dataclass | ||
| from datetime import UTC, datetime | ||
| from typing import Deque, List | ||
|
|
||
| import numpy as np | ||
|
|
||
| from fractal.core.base import Action, ActionToTake, BaseStrategy, BaseStrategyParams | ||
|
|
||
|
|
|
|
||
| import numpy as np | ||
|
|
||
| from fractal.core.base import Action, ActionToTake, BaseStrategy, BaseStrategyParams |
Comment on lines
+369
to
+385
| for i, price in enumerate(prices): | ||
| iv = iv_values[i] if iv_values is not None else 0.60 | ||
| ts = datetime(2023, 1, 1, i % 24, tzinfo=UTC) | ||
| result.append(Observation( | ||
| timestamp=ts, | ||
| states={ | ||
| "STRADDLE": PanopticPoolGlobalState( | ||
| price=price, fees=fees, liquidity=liquidity, | ||
| volume=5e8, tvl=5e8, iv_annual=iv, | ||
| ), | ||
| "LP": UniswapV3LPGlobalState( | ||
| price=price, fees=fees, liquidity=liquidity, | ||
| volume=5e8, tvl=5e8, | ||
| ), | ||
| }, | ||
| )) | ||
| return result |
Comment on lines
+38
to
+53
| for i, price in enumerate(prices): | ||
| iv = iv_values[i] if iv_values is not None else 0.60 | ||
| result.append(Observation( | ||
| timestamp=datetime(2023, 1, 1, i % 24, tzinfo=UTC), | ||
| states={ | ||
| "STRADDLE": PanopticPoolGlobalState( | ||
| price=price, fees=fees, liquidity=liquidity, | ||
| volume=5e8, tvl=5e8, iv_annual=iv, | ||
| ), | ||
| "LP": UniswapV3LPGlobalState( | ||
| price=price, fees=fees, liquidity=liquidity, | ||
| volume=5e8, tvl=5e8, | ||
| ), | ||
| }, | ||
| )) | ||
| return result |
Comment on lines
+285
to
+294
| if (total_costs > 0 | ||
| and straddle.intrinsic_value | ||
| >= self._params.TAKE_PROFIT_MULT * total_costs): | ||
| reason = 'TAKE_PROFIT' | ||
| elif total_costs >= (self._params.INITIAL_BALANCE | ||
| * self._params.STOP_LOSS_BUDGET_PCT): | ||
| reason = 'STOP_LOSS' | ||
| elif s.bars_held >= self._params.MAX_HOLD_BARS: | ||
| reason = 'TIME_STOP' | ||
| else: |
Comment on lines
+122
to
+131
| @property | ||
| def intrinsic_value(self) -> float: | ||
| if not self._internal_state.is_open: | ||
| return 0.0 | ||
| return abs(self._global_state.price - self._internal_state.entry_price) | ||
|
|
||
| @property | ||
| def balance(self) -> float: | ||
| pnl = self.intrinsic_value - self._internal_state.accumulated_premium | ||
| return self._internal_state.cash + self._internal_state.collateral + pnl |
Comment on lines
+269
to
+274
| if straddle.internal_state.cash < notional * self._params.COLLATERAL_PCT: | ||
| return [] | ||
| return [ActionToTake('STRADDLE', Action('open', { | ||
| 'notional': notional, | ||
| 'collateral': notional * self._params.COLLATERAL_PCT, | ||
| 'commission': notional * self._params.PANOPTIC_COMMISSION_PCT, |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
PanopticStraddleStrategy— a long-straddle strategy on the Panopticperpetual options protocol over Uniswap V3 ETH/USDC 0.3% pool.
Why
Panoptic buyers are the natural counterpart to passive LPs (who are implicitly
short gamma). This strategy enters when pool implied volatility is cheap and
exits on a directional move.
Test plan
tests/core/test_panoptic_straddle.py(53 tests)tests/core/e2e/test_e2e_panoptic_straddle_synthetic.py(10 tests)pytest -m corepre-commit run --all-filesclean