Strategy Blocks Reference ========================= Strategy blocks control *how* your strategy runs — the configuration, timing, entry/exit logic, and portfolio operations. Unlike indicators (which return numbers), strategy blocks perform actions or define conditions. ---- Configuration ------------- .. _config: Config Block ~~~~~~~~~~~~~ Defines all strategy-wide settings. Every workspace must contain exactly **one** Config block. .. list-table:: :header-rows: 1 :widths: 30 15 55 * - Parameter - Default - Description * - **Start Date** - ``1998-01-01`` - First date to include in the backtest. * - **End Date** - ``2019-12-31`` - Last date of the test period. * - **Starting Cash** - ``100,000`` - Initial portfolio value in USD. * - **Slots** - ``10`` - Maximum number of open positions at any one time. * - **Data Index** - ``NASDAQ`` - Benchmark index used by :ref:`index_value` blocks (NASDAQ or SPY). * - **Stop Loss** ⚙ - ``0.05`` - Percentage loss at which a position is automatically closed (e.g. ``0.05`` = 5%). * - **Take Profit** ⚙ - ``0.05`` - Percentage gain at which a position is automatically closed. * - **Primary Interval** ⚙ - ``2w`` - Interval for the main rebalancing loop (e.g. ``1d``, ``1w``, ``2w``, ``1m``). * - **Secondary Interval** ⚙ - ``1d`` - Interval for secondary logic block. * - **Tertiary Interval** ⚙ - ``2d`` - Interval for tertiary logic block. * - **Quaternary Interval** ⚙ - ``2w`` - Interval for fourth logic block. * - **Optimizer** - Off - Enable to activate multi-value testing. Fields marked ⚙ accept comma-separated values. **Interval format:** ``Nd`` (N trading days), ``Nw`` (N weeks), ``Nm`` (N months). .. tip:: Enable **Optimizer** and enter comma-separated values (e.g. ``0.03, 0.05, 0.10`` for Stop Loss) to automatically run all combinations and return the best-performing parameters. ---- Interval Checks --------------- .. _on_interval: On Interval ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Wraps a block of logic that should run only on a specific interval boundary — e.g. every 2 weeks, every month. The four available intervals map directly to the Config block's interval settings. .. list-table:: :header-rows: 1 :widths: 30 70 * - Field - Description * - **Interval** - Which Config interval to check: Primary / Secondary / Tertiary / Quaternary. * - **Do** *(nested blocks)* - Blocks placed inside here execute only when the interval fires. **How it works:** The engine checks whether the current trading day marks the start of a new interval period. If yes, the nested blocks execute. If not, they are skipped. **Example:** Set Primary Interval to ``2w`` and place a rebalance block inside — the portfolio rebalances every two weeks. .. note:: Multiple interval blocks can exist in one workspace. Use Primary for rebalancing and Secondary for daily checks (e.g. stop-loss management). ---- Triggers -------- .. _check_stop_loss: Check Stop Loss ~~~~~~~~~~~~~~~~ Evaluates all open positions and closes any that have fallen by the stop-loss percentage defined in the Config block. .. list-table:: :header-rows: 1 :widths: 30 70 * - Field - Description * - **Execution** - ``eod`` (end of day) — close at today's close. ``sod`` (start of day) — close at today's open. .. warning:: Place this block inside a **daily** interval (Secondary Interval = ``1d``) to check stop-losses every trading day. If you only check weekly, a position could fall far beyond the stop level before being closed. ---- .. _check_take_profit: Check Take Profit ~~~~~~~~~~~~~~~~~~ Evaluates all open positions and closes any that have risen by the take-profit percentage defined in the Config block. .. list-table:: :header-rows: 1 :widths: 30 70 * - Field - Description * - **Execution** - ``eod`` or ``sod`` — same as Check Stop Loss above. ---- Portfolio --------- .. _portfolio_rebalance_enter: Rebalance: Enter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Enters positions for a list of selected symbols, filling available slots up to the **Slots** limit. Positions are sized equally by default (``1 / slots``). .. list-table:: :header-rows: 1 :widths: 30 15 55 * - Field - Default - Description * - **Open Reason** - ``Rebalance`` - Label recorded in the trade log for this entry. * - **Execution** - ``eod`` - ``eod`` (fill at today's close) or ``sod`` (fill at today's open). * - **Entry Type** - ``Immediate`` - ``Immediate`` — enters now. ``Next-Week Limit`` — schedules a limit order for the following week. * - **Limit Offset %** - ``0.5`` - Only for *Next-Week Limit*: order placed this many % below the current close. * - **Selected** *(input)* - — - Symbol list from a ranking/filter block (e.g. :ref:`momentum`, :ref:`rank_by_volatility`). .. note:: If fewer symbols are selected than available slots, remaining slots stay in cash. ---- .. _portfolio_rebalance_exit: Rebalance: Exit ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Closes positions for a list of symbols. Use this to liquidate stocks that no longer meet your selection criteria. .. list-table:: :header-rows: 1 :widths: 30 70 * - Field - Description * - **Execution** - ``eod`` or ``sod``. * - **Selected** *(input)* - Symbol list of positions to exit. **Typical pattern:** On each rebalancing day, call Exit with the old list, then Enter with the new ranked list. ---- .. _enter_next_week_limit: Enter Next-Week Limit Orders ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Schedules limit-buy orders for the following trading week at a discounted price. The orders are stored internally and evaluated each day until filled or cancelled. .. list-table:: :header-rows: 1 :widths: 30 15 55 * - Field - Default - Description * - **Symbols** *(input)* - — - List of symbols to schedule. * - **Discount %** ⚙ - ``0.5`` - Limit price = setup-week close × (1 − discount/100). **How it works:** 1. On the rebalancing day the block records ``limit_price = close × (1 − z%)`` for each symbol. 2. Each subsequent day the engine checks: if ``day_low ≤ limit_price``, the position is entered. 3. Unexecuted orders are cancelled at the next rebalance. .. tip:: Pair with :ref:`execute_pending_limit_orders` placed inside the daily (Secondary Interval) block. ---- .. _execute_pending_limit_orders: Execute Limit Orders ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Checks all pending limit orders each day and fills any where the intra-day low reached the limit price. This block takes no parameters — place it inside a **daily** interval check. **Fill condition:** ``Low ≤ limit_price`` → enter at ``limit_price``. ---- Filters & Ranking ----------------- .. _rank_by_volatility: Rank by Volatility (HV252) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sorts a symbol list by their 252-day annualised historical volatility. .. list-table:: :header-rows: 1 :widths: 30 15 55 * - Field - Default - Description * - **Symbols** *(input)* - — - Input symbol list (e.g. from a Momentum block). * - **Order** - Low→High - ``Low→High`` (least volatile first) or ``High→Low`` (most volatile first). * - **Top K** - ``0`` - Return only the top K symbols. ``0`` = fill all available slots. **Formula:** ``HV252 = StdDev(daily returns, 252) × √252`` .. note:: *Low volatility* strategies (Low→High order) tend to produce better risk-adjusted returns. This is known as the **Low Volatility Anomaly**. ---- .. _stock_cooldown_filter: Stock Cooldown Filter ~~~~~~~~~~~~~~~~~~~~~~ Removes stocks that were stopped out within the last *N* trading days. Prevents re-entering a position that just triggered a stop-loss. .. list-table:: :header-rows: 1 :widths: 30 15 55 * - Field - Default - Description * - **Symbols** *(input)* - — - Input list to filter. * - **Cooldown Days** - ``20`` - Minimum days since the last stop-out before a symbol is eligible again. ---- Logic & Conditions ------------------ .. _if_block: IF Block (Condition) ~~~~~~~~~~~~~~~~~~~~~ Executes the nested blocks only when a condition is **true**. The condition is built by connecting comparison blocks to the **Condition** input. .. list-table:: :header-rows: 1 :widths: 30 70 * - Field - Description * - **Condition** *(input)* - A boolean expression built from comparison blocks. * - **Do** *(nested)* - Blocks to execute when the condition is met. **Example condition tree:** .. code-block:: text RSI(14) > Threshold(30) AND Index Value > SMA(200) Both sub-conditions must be true for the nested blocks to execute. .. tip:: Nest multiple IF blocks to build complex multi-condition entry logic. ---- .. _logic_compare: Comparison Block ~~~~~~~~~~~~~~~~~ Compares two values and returns **true** or **false**. .. list-table:: :header-rows: 1 :widths: 25 75 * - Operator - Meaning * - ``=`` - Equal to * - ``≠`` - Not equal to * - ``<`` - Less than * - ``≤`` - Less than or equal to * - ``>`` - Greater than * - ``≥`` - Greater than or equal to Connect an indicator to the **A** slot, choose an operator, and connect a :ref:`threshold` to the **B** slot. ---- .. _logic_operation: AND / OR Block ~~~~~~~~~~~~~~~ Combines two boolean conditions. - **AND** — both must be true. - **OR** — at least one must be true. Chain multiple AND/OR blocks to express complex rules: ``(RSI > 30) AND (MACD > 0) AND (Index > SMA200)`` ---- .. _logic_negate: NOT Block ~~~~~~~~~~ Inverts a boolean condition. ``NOT (RSI > 70)`` is equivalent to ``RSI ≤ 70``. ---- Processing ---------- .. _trading_loop: Trading Loop ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The main outer loop that iterates through every trading day in the backtest period. All interval checks and portfolio operations must be nested inside this block. This block takes no configuration parameters — it simply wraps the rest of your strategy. .. warning:: Every workspace needs exactly **one** Trading Loop block. All other strategy blocks (Interval Checks, IF blocks, Portfolio operations) must be nested inside it. **Typical workspace structure:** .. code-block:: text [Config Block] [Trading Loop] └── [On Interval — Primary (2w)] │ └── [IF Block: Index > SMA(200)] │ └── [Rank by Momentum → Rebalance: Enter] │ └── [Rebalance: Exit] └── [On Interval — Secondary (1d)] └── [Check Stop Loss (eod)] └── [Execute Limit Orders]