We can extend your original strategy into a market-depth-aware options trading system, combining Zorro's native Level II order-book analysis, fuzzy logic, volatility estimation, and dynamic options selection.
The important improvement is that the strategy will no longer rely exclusively on historical closing prices. It will also examine the current distribution of buy and sell liquidity before deciding whether to open an options position.
1. Zorro's built-in order-book functions
Zorro provides three native functions for market-depth analysis, documented in its official manual. They require Zorro S.

Function Purpose
orderUpdate() Retrieves market-depth quotes from the broker or historical data
orderCVD() Constructs a cumulative distribution of order-book liquidity
cpd() Measures cumulative relative bid or ask volume at a price
These can be combined into three analytical signals:
- Bid pressure: Relative liquidity on the buying side.
- Ask pressure: Relative liquidity on the selling side.
- Order-book imbalance: Difference between buying and selling pressure, normalized by their combined liquidity.
A useful mathematical representation is:

\[
I_t=\frac{B_t-A_t}{B_t+A_t}
\]

Where \(B_t\) and \(A_t\) are comparable bid-side and ask-side cumulative liquidity measurements.
A positive imbalance indicates more visible bid liquidity; a negative imbalance indicates more visible ask liquidity. Neither guarantees the next price movement, because displayed orders can be canceled or moved. (Latex formula)


Code
// OrderBookFuzzyStrangle_v1.c
// Zorro S / lite-C -- SPY long strangle with underlying order-book filter.
// Requires synchronized historical .t2 book snapshots and .t8 options chains
// for Test/Train; GET_BOOK and options support from broker for Trade.
// Research prototype: validate fills, execution, and broker behavior in demo.

#include <contract.c>

#define BOOK_HANDLE         1
#define VOL_PERIOD         20
#define MIN_BOOK_QUOTES     6
#define MIN_DTE            30
#define EXIT_DTE           10
#define MIN_IMBALANCE    0.15
#define EXIT_IMBALANCE   0.07
#define MIN_FUZZY        0.65
#define MOVE_MULTIPLIER  1.10
#define MAX_DEBIT_USD  1500.0
#define PROFIT_FRACTION  0.50
#define LOSS_FRACTION    0.35

// Average absolute close-to-close change, in SPY dollars per hourly bar.
var meanAbsoluteMove(vars Prices, int Period)
{
  var Sum = 0;
  int i;
  for(i = 1; i <= Period; i++)
    Sum += abs(Prices[i-1] - Prices[i]);
  return Sum / Period;
}

// Returns 1 if a usable book snapshot exists and fills the imbalance output.
int readBookImbalance(var Spot, var* Imbalance)
{
  int N = orderUpdate(0,BOOK_HANDLE); // History\SPY.t2 or broker GET_BOOK
  if(N < MIN_BOOK_QUOTES) return 0;

  T2* Quotes = dataStr(BOOK_HANDLE,OrderRow,0);
  if(!Quotes) return 0;

  var Distance = 0.01*Spot;  // visible liquidity within +/-1% of SPY
  if(orderCVD(Quotes,N,Distance) < MIN_BOOK_QUOTES) return 0;

  // cpd returns values in 0..100 normalized to the larger book side.
  var BidDepth = cpd(-(Spot-Distance));
  var AskDepth = cpd(Spot+Distance);
  var Total = BidDepth+AskDepth;
  if(Total <= 0) return 0;

  *Imbalance = (BidDepth-AskDepth)/Total;
  return 1;
}

// Returns 1 if open options should be liquidated on risk/expiration rules.
int exitByRisk()
{
  var Cost = 0;
  var OpenPnL = 0;
  int ExpireSoon = 0;
  int Legs = 0;

  for(current_trades) {
    if(!TradeIsOpen || !TradeIsLong || !TradeIsContract) continue;
    Cost += TradePriceOpen*TradeUnits;
    OpenPnL += TradeProfit;
    if(contractDays(ThisTrade) <= EXIT_DTE) ExpireSoon = 1;
    Legs++;
  }

  if(!Legs) return 0;
  if(ExpireSoon) return 1;
  if(Cost <= 0) return 0;
  if(OpenPnL >= PROFIT_FRACTION*Cost) return 1;
  if(OpenPnL <= -LOSS_FRACTION*Cost) return 1;
  return 0;
}

void buyStrangle(var Spot, var AvgMove)
{
  // Dynamic OTM strike targets in dollars. Select nearest listed strikes.
  var Gap = max(0.01*Spot,8*AvgMove);
  var CallTarget = Spot+Gap;
  var PutTarget = Spot-Gap;
  if(PutTarget <= 0) return;

  // 6 means select by strike (the 4th contractFind parameter is mandatory).
  CONTRACT* Call = contractFind(CALL,MIN_DTE,CallTarget,6);
  CONTRACT* Put = contractFind(PUT,MIN_DTE,PutTarget,6);
  if(!Call || !Put) return;
  if(Call->fStrike <= Spot || Put->fStrike >= Spot) return;
  if(Call->Expiry != Put->Expiry) return;
  if(contractDays(Call) <= EXIT_DTE || contractDays(Put) <= EXIT_DTE)
    return;

  if(contractPrice(Call) <= 0) return;
  if(contractPrice(Put) <= 0) return;
  if(Call->fAsk <= 0 || Put->fAsk <= 0) return;
  if(Call->fBid <= 0 || Put->fBid <= 0) return;

  var Debit = (Call->fAsk+Put->fAsk)*Multiplier;
  if(Debit <= 0 || Debit > MAX_DEBIT_USD) return;

  if(combo(Call,1,Put,1,0,0,0,0) != 2) return;
  MarginCost = comboMargin(1,3); // Stock/ETF options; long positions.

  // The broker receives the combo when the second leg is submitted, if
  // SET_COMBO_LEG is supported. Otherwise separate orders can be submitted.
  enterLong(comboLeg(1));
  enterLong(comboLeg(2));
  printf("\nStrangle order attempted: SPY %.2f, call %.2f, put %.2f, debit %.2f",
    Spot,Call->fStrike,Put->fStrike,Debit);
}

void run()
{
  set(LOGFILE);
  BarPeriod = 60;
  LookBack = 100;
  StartDate = 2020;
  EndDate = 2024;
  History = ".t8"; // Workshop 8: historical options/unadjusted underlying.

  assetList("AssetsIB");
  asset("SPY");
  algo("OBStrangle");
  Multiplier = 100; // Standard SPY option contract; verify with broker.
  Capital = 100000; // Research backtest account, not a live risk control.

  if(is(EXITRUN)) return;

  static int LastChainDate = 0;
  if(is(INITRUN)) {
    LastChainDate = 0;
    return;
  }

  vars Prices = series(priceClose());
  var AvgMove = meanAbsoluteMove(Prices,VOL_PERIOD);
  if(AvgMove <= 0) return;

  FuzzyRange = max(0.01,0.25*AvgMove);
  var Bullish = max(risingF(Prices),valleyF(Prices));
  var Bearish = max(fallingF(Prices),peakF(Prices));

  // In historical tests, options must be refreshed every bar.
  // For live mode, cache the costly IB chain download for the session day.
  int Today = ymd(wdate(0));
  if(!Live || Today != LastChainDate || NumContracts <= 0) {
    if(!contractUpdate(Asset,0,CALL|PUT)) return;
    LastChainDate = Today;
  }

  // These exits must work even when the current order book is unavailable.
  if(NumOpenLong > 0 && exitByRisk()) {
    exitLong("cp"); // Only call and put contracts of this asset/algo.
    return;
  }

  var Imbalance = 0;
  if(!readBookImbalance(priceClose(),&Imbalance)) return;

  plot("Order book imbalance",Imbalance,NEW,BLUE);

  if(NumOpenLong > 0 || NumPendingLong > 0) {
    // Fuzzy bearish price signal plus disappearing book pressure.
    if(Bearish > MIN_FUZZY && abs(Imbalance) < EXIT_IMBALANCE)
      exitLong("cp");
    return;
  }

  // Require a meaningful move, visible bid/ask pressure, and corresponding
  // fuzzy price direction. Long strangle still buys BOTH call and put.
  if(abs(Prices[0]-Prices[1]) < MOVE_MULTIPLIER*AvgMove) return;
  if(abs(Imbalance) < MIN_IMBALANCE) return;

  if(Imbalance > 0 && Bullish >= MIN_FUZZY)
    buyStrangle(priceClose(),AvgMove);
  else if(Imbalance < 0 && Bearish >= MIN_FUZZY)
    buyStrangle(priceClose(),AvgMove);
}