Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ The same corpus is browsable at **[aethersystems.net/nano](https://aethersystems

| Momentum | Mean reversion | Trend | Volatility | Volume | Risk | Event volatility |
| --- | --- | --- | --- | --- | --- | --- |
| 4 strategies | 3 strategies | 3 strategies | 2 strategies | 2 strategies | 1 strategy | 11 strategies |
| 4 strategies | 3 strategies | 3 strategies | 2 strategies | 2 strategies | 7 strategies | 11 strategies |

The library is a conformance corpus, not a performance claim, live signal service, or trading recommendation.

Expand Down
15 changes: 14 additions & 1 deletion nano/library/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The current library contains 26 strategies across seven familiar categories. Bro
| `trend/` | `golden_cross`, `macd_histogram_flip`, `donchian_breakout` |
| `volatility/` | `atr_volatility_halt`, `bb_squeeze_breakout` |
| `volume/` | `volume_spike_confirmation`, `obv_trend` |
| `risk/` | `max_drawdown_breaker` |
| `risk/` | `max_drawdown_breaker`, `daily_loss_limit`, `position_concentration_cap`, `correlation_cluster_guard`, `stale_data_halt`, `leverage_ceiling`, `consecutive_loss_circuit` |
| `event_volatility/` | `event_impulse_pullback_long`, `event_impulse_pullback_short`, `cpi_impulse_pullback_long`, `cpi_impulse_pullback_short`, `event_false_first_move_long`, `event_false_first_move_short`, `event_second_leg_long`, `event_second_leg_short`, `event_liquidity_halt`, `event_release_integrity_halt`, `event_whipsaw_halt` |

## Signal conventions
Expand All @@ -50,6 +50,19 @@ Library entries compare **host-provided named signal series** with numeric liter
| OBV trend | `OBV_SLOPE(20)` | Linear-regression slope of OBV |
| equity drawdown | `DRAWDOWN` | Portfolio drawdown percentage |

### Book-control signals (`risk/`)

Risk entries read **portfolio and infrastructure state**, not bar indicators. Every series is nonnegative and rises as the situation worsens, so a control is always a `>=` against a ceiling — the same direction on every rule, which is what makes a stack of them readable at a glance. These rules emit `pause` and `observe` only; none of them proposes a direction.

| Book measurement | Nano signal | Feed convention |
| --- | --- | --- |
| session loss | `DAY_LOSS_PCT` | Realised session loss as a positive percentage of starting equity; host owns the session boundary |
| largest position | `MAX_POSITION_PCT` | Largest single position as a percentage of gross exposure |
| cluster exposure | `CLUSTER_EXPOSURE_PCT` | Largest correlated-cluster exposure as a percentage of gross; host owns cluster assignment |
| feed freshness | `FEED_AGE_SEC` | Seconds since the last accepted tick |
| gross leverage | `GROSS_LEVERAGE` | Gross exposure divided by equity |
| losing streak | `CONSECUTIVE_LOSSES` | Count of consecutive losing closed decisions; host defines close and loss |

### Macro-event signals (`event_volatility/`)

Event strategies consume a **host event engine** rather than bar indicators: the host arms a scheduled release window, measures the post-release tape deterministically, and publishes nonnegative bounded scores. Bull and bear terms are always separate series (never one signed value), which is what keeps twin-armed branches mutually exclusive and inside baseline IR. The full measurement definitions live in [`docs/event_signal_contract.md`](../../docs/event_signal_contract.md).
Expand Down
32 changes: 32 additions & 0 deletions nano/library/risk/consecutive_loss_circuit.nano
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Consecutive loss circuit: after four losing closed decisions in a row, pause
// and escalate to the risk desk for review.
// REGIME: all of them, though it is most informative in a regime the strategy
// was not built for - which is precisely when a run of losses appears.
// CONDITIONS: none. The host decides what closes a decision and what counts as
// a loss; Nano only reads the count.
// INVALIDATION: none. The counter is reset by the host, deliberately, after
// review.
// SHAPE: 5m; the count only changes when a decision closes, so a faster cadence
// re-reads an unchanged number.
// NOT daily_loss_limit: that measures magnitude, this measures sequence. Four
// small losses in a row can cost almost nothing and still be the clearest
// evidence available that the regime has changed. A book can trip this while
// nowhere near any loss limit, which is the point.
// CALIBRATED ON: strategies that close decisions discretely. A continuously
// rebalanced book has no natural notion of a consecutive loss and should not
// arm this.
strategy ConsecutiveLossCircuit {

agent RiskDesk

every 5m {

if CONSECUTIVE_LOSSES >= 4 {

pause()

}

}

}
29 changes: 29 additions & 0 deletions nano/library/risk/consecutive_loss_circuit_ir.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"type": "Strategy",
"nanoIrVersion": "0.1.0",
"name": "ConsecutiveLossCircuit",
"effects": [
"intent.emit",
"log.append"
],
"nodes": [
{
"type": "Schedule",
"interval": "5m"
},
{
"type": "Condition",
"signal": "CONSECUTIVE_LOSSES",
"operator": ">=",
"value": 4
},
{
"type": "Intent",
"action": "PAUSE"
},
{
"type": "Agent",
"name": "RiskDesk"
}
]
}
33 changes: 33 additions & 0 deletions nano/library/risk/correlation_cluster_guard.nano
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Correlation cluster guard: pause when one correlated cluster carries 40
// percent or more of gross exposure, and flag it for review.
// REGIME: matters most in a stress regime, when correlations converge and a
// book that looked diversified stops being diversified.
// CONDITIONS: the host must publish a cluster assignment. Nano does not compute
// correlation - CLUSTER_EXPOSURE_PCT is the host's answer to "how much of the
// book is really one bet".
// INVALIDATION: the cluster breaking up, which is a host measurement and not
// something this rule can observe.
// SHAPE: 15m; cluster membership is a rolling estimate and re-checking it
// faster than it updates only adds noise.
// NOT position_concentration_cap: that one counts a single instrument. This one
// counts many instruments that happen to be the same trade. A book can pass the
// concentration cap on every line and fail this badly.
// CALIBRATED ON: multi-asset books. A single-sector mandate is one cluster by
// construction and should not arm this without raising the threshold.
strategy CorrelationClusterGuard {

agent RiskDesk

every 15m {

if CLUSTER_EXPOSURE_PCT >= 40 {

pause()

observe()

}

}

}
33 changes: 33 additions & 0 deletions nano/library/risk/correlation_cluster_guard_ir.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"type": "Strategy",
"nanoIrVersion": "0.1.0",
"name": "CorrelationClusterGuard",
"effects": [
"intent.emit",
"log.append"
],
"nodes": [
{
"type": "Schedule",
"interval": "15m"
},
{
"type": "Condition",
"signal": "CLUSTER_EXPOSURE_PCT",
"operator": ">=",
"value": 40
},
{
"type": "Intent",
"action": "PAUSE"
},
{
"type": "Intent",
"action": "OBSERVE"
},
{
"type": "Agent",
"name": "RiskDesk"
}
]
}
28 changes: 28 additions & 0 deletions nano/library/risk/daily_loss_limit.nano
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Daily loss limit: when the session's realised loss reaches 2 percent of
// starting equity, pause all proposals and hand control to the risk desk.
// REGIME: all of them. This is a control, not a directional hypothesis.
// CONDITIONS: none. It is armed from the session open.
// INVALIDATION: none. A limit is not a trade. It resets on the next session
// boundary, which the host owns - Nano has no clock of its own.
// SHAPE: 1m, so a fast tape cannot run through the limit between checks.
// NOT max_drawdown_breaker: that measures peak-to-trough over the life of the
// book and can sit quiet through a catastrophic single day if the book was up
// beforehand. This one resets daily and catches exactly that case. Both should
// be armed; neither subsumes the other.
// CALIBRATED ON: nothing instrument-specific. 2 percent is a session-level
// figure and travels unchanged.
strategy DailyLossLimit {

agent RiskDesk

every 1m {

if DAY_LOSS_PCT >= 2 {

pause()

}

}

}
29 changes: 29 additions & 0 deletions nano/library/risk/daily_loss_limit_ir.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"type": "Strategy",
"nanoIrVersion": "0.1.0",
"name": "DailyLossLimit",
"effects": [
"intent.emit",
"log.append"
],
"nodes": [
{
"type": "Schedule",
"interval": "1m"
},
{
"type": "Condition",
"signal": "DAY_LOSS_PCT",
"operator": ">=",
"value": 2
},
{
"type": "Intent",
"action": "PAUSE"
},
{
"type": "Agent",
"name": "RiskDesk"
}
]
}
28 changes: 28 additions & 0 deletions nano/library/risk/leverage_ceiling.nano
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Leverage ceiling: pause when gross exposure reaches 3x equity.
// REGIME: all of them. Leverage is a constraint, not a forecast.
// CONDITIONS: none. Armed whenever the book is open.
// INVALIDATION: none. It is reset by reducing size, deliberately, outside this
// rule.
// SHAPE: 5m; gross exposure moves on fills and on marks, and 5m is fast enough
// to catch both without re-checking a number that has not changed.
// NOT position_concentration_cap: that measures how the book is distributed,
// this measures how large it is in total. A perfectly diversified book at 5x
// gross passes the concentration cap and belongs to this rule.
// CALIBRATED ON: nothing instrument-specific, but 3x is a house figure rather
// than a universal one - a futures book and a cash equity book do not mean the
// same thing by "gross".
strategy LeverageCeiling {

agent RiskDesk

every 5m {

if GROSS_LEVERAGE >= 3 {

pause()

}

}

}
29 changes: 29 additions & 0 deletions nano/library/risk/leverage_ceiling_ir.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"type": "Strategy",
"nanoIrVersion": "0.1.0",
"name": "LeverageCeiling",
"effects": [
"intent.emit",
"log.append"
],
"nodes": [
{
"type": "Schedule",
"interval": "5m"
},
{
"type": "Condition",
"signal": "GROSS_LEVERAGE",
"operator": ">=",
"value": 3
},
{
"type": "Intent",
"action": "PAUSE"
},
{
"type": "Agent",
"name": "RiskDesk"
}
]
}
28 changes: 28 additions & 0 deletions nano/library/risk/position_concentration_cap.nano
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Position concentration cap: pause when any single position exceeds 25
// percent of the book.
// REGIME: all of them. Concentration risk does not care about trend.
// CONDITIONS: none. Armed whenever the book is open.
// INVALIDATION: none. It is reset by the book changing shape, not by a signal.
// SHAPE: 5m; position weights move on fills, not on ticks, so a faster cadence
// would add checks without adding information.
// NOT leverage_ceiling: that measures total size against equity, this measures
// distribution. A book at 1x gross with everything in one name is fully inside
// the leverage limit and is exactly the situation this rule exists for.
// CALIBRATED ON: nothing instrument-specific. 25 percent is a book-level
// figure. A concentrated book by mandate should raise it deliberately rather
// than disarm the rule.
strategy PositionConcentrationCap {

agent RiskDesk

every 5m {

if MAX_POSITION_PCT >= 25 {

pause()

}

}

}
29 changes: 29 additions & 0 deletions nano/library/risk/position_concentration_cap_ir.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"type": "Strategy",
"nanoIrVersion": "0.1.0",
"name": "PositionConcentrationCap",
"effects": [
"intent.emit",
"log.append"
],
"nodes": [
{
"type": "Schedule",
"interval": "5m"
},
{
"type": "Condition",
"signal": "MAX_POSITION_PCT",
"operator": ">=",
"value": 25
},
{
"type": "Intent",
"action": "PAUSE"
},
{
"type": "Agent",
"name": "RiskDesk"
}
]
}
32 changes: 32 additions & 0 deletions nano/library/risk/stale_data_halt.nano
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Stale data halt: pause when the feed has not delivered an accepted tick for
// 30 seconds, and flag the gap for review.
// REGIME: all of them, and it outranks every directional rule. A signal
// computed from a stale tape is not a weak signal, it is a fabricated one.
// CONDITIONS: none. Armed whenever anything downstream is consuming the feed.
// INVALIDATION: none. Freshness is not a view.
// SHAPE: 1m. The cadence bounds how long a stale tape can go unnoticed, so it
// is set to the tightest interval the library uses.
// NOT event_liquidity_halt: that asks whether the book can be traded, this asks
// whether the numbers describing it are real at all. A liquid market with a
// dead feed passes that rule and fails this one.
// CALIBRATED ON: liquid instruments on a continuous session. A thin or
// session-bound instrument has legitimate 30-second gaps and needs a wider
// bound, or the rule fires all day and gets disarmed - which is worse than
// never arming it.
strategy StaleDataHalt {

agent RiskDesk

every 1m {

if FEED_AGE_SEC >= 30 {

pause()

observe()

}

}

}
Loading
Loading