This strategy is an abstract example of decision-making from coupled graph geometry. It models the market using two different weighted graphs that represent two distinct kinds of structure:

1) A regime dynamics graph (Graph A)Graph A represents the market as a finite set of latent regimes (states). A directed edge from state ???? to state ???? encodes the likelihood that the market transitions from
???? to ????. Probabilities ???????????? are converted into edge “lengths” using an information-geometric map:

wij =?log(pij)

This turns the transition system into a metric-like space where likely transitions are short and unlikely transitions are long. Once edge lengths are defined, Floyd–Warshall computes shortest path distances between all state pairs, allowing indirect multi-step transitions to determine effective connectivity. The strategy then summarizes the global geometry of the regime space using a Wiener-like index—the sum of distances across all unordered state pairs. Conceptually, this measures whether the regime system is compact (states are mutually reachable through short, high-probability pathways) or diffuse (states are separated by long or improbable paths). A compact regime graph corresponds to more orderly, predictable evolution; a diffuse one corresponds to fragmented, noisy evolution.

2) An asset coupling graph (Graph B)

Graph B represents relationships among assets as an undirected weighted graph. Here, edge weights are derived from correlation:

wij ?=1?? corrij ??

Strongly correlated assets are “close” (small distance), while weakly related assets are “far.” Again, shortest paths produce an effective distance structure, and a Wiener-like sum over all asset pairs becomes a scalar measure of system-wide coupling. Low Wiener (tight graph) means assets behave as a single cluster—diversification is weak, and portfolio risk concentrates. Higher Wiener implies more separation and potentially better diversification.

3) Coupling the graphs into a single control variable The strategy’s mathematical core is a coupling rule that treats: Regime compactness as a proxy for signal quality / predictability, and Asset coupling as a proxy for systemic risk / lack of diversification.

It combines them through a logistic squashing function:

Score=?(??Compactness(StateGraph)???Coupling(AssetGraph))

The sigmoid ?(?) maps the result into [0,1], producing a risk throttle. Abstractly, the throttle increases when the regime space is geometrically “tight” (predictable transitions) and decreases when the asset space is geometrically “tight” (high correlation concentration). The outcome is not a specific entry/exit rule, but a mathematically grounded allocator: a mechanism that scales risk based on two interacting notions of structure—temporal structure in regime evolution and cross-sectional structure in asset dependence.

In short, the code demonstrates how global graph metrics (via shortest-path geometry and Wiener-like sums) can be used as compact, interpretable features to modulate trading aggressiveness in a principled, system-level way.

Code
// TGr05.cpp - Zorro64 Strategy DLL (C++) - Two coupled graphs for algo trading

#include <zorro.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define INF 1e30

// =============================== Graph (OO) ===============================
class WeightedGraph {
public:
  int n = 0;
  double* d = 0;   // adjacency/shortest-path matrix (n*n)

  WeightedGraph(int N) { init(N); }
  ~WeightedGraph() { shutdown(); }

  void init(int N) {
    shutdown();
    n = N;
    d = (double*)malloc((size_t)n*(size_t)n*sizeof(double));
    if(!d) quit("OOM: WeightedGraph matrix");
    reset();
  }

  void shutdown() {
    if(d) free(d);
    d = 0;
    n = 0;
  }

  inline int pos(int r,int c) const { return r*n + c; }

  void reset() {
    for(int r=0;r<n;r++)
      for(int c=0;c<n;c++)
        d[pos(r,c)] = (r==c) ? 0.0 : INF;
  }

  // Floyd-Warshall shortest paths
  void allPairsShortest() {
    for(int k=0;k<n;k++)
      for(int i=0;i<n;i++)
        for(int j=0;j<n;j++) {
          double cand = d[pos(i,k)] + d[pos(k,j)];
          if(cand < d[pos(i,j)]) d[pos(i,j)] = cand;
        }
  }

  // Wiener-like sum over unordered pairs (symmetrized)
  double wienerUndirectedLike() const {
    double W = 0.0;
    for(int i=0;i<n;i++)
      for(int j=i+1;j<n;j++) {
        double dij = d[pos(i,j)];
        double dji = d[pos(j,i)];
        W += 0.5*(dij + dji);
      }
    return W;
  }

  void dump(const char* title) const {
    printf("\n%s", title);
    for(int r=0;r<n;r++) {
      printf("\n");
      for(int c=0;c<n;c++) {
        double v = d[pos(r,c)];
        if(v > 1e20) printf("  INF ");
        else printf("%5.2f ", v);
      }
    }
  }
};

// ========================== Graph A: Markov State Graph ===================
// Edge weight = -log(p_ij)  (probability -> distance)
class RegimeMarkovGraph {
public:
  WeightedGraph G;

  RegimeMarkovGraph(int states) : G(states) {}

  static double probToDist(double p) {
    if(p <= 0.0) return INF;
    if(p > 1.0) p = 1.0;
    return -log(p);
  }

  void setTransitionProb(int i,int j,double p) {
    G.d[G.pos(i,j)] = probToDist(p);
  }

  // Example presets: directional vs choppy (toy)
  void buildDirectional4() {
    G.reset();
    setTransitionProb(0,0,0.65); setTransitionProb(0,1,0.30); setTransitionProb(0,2,0.04); setTransitionProb(0,3,0.01);
    setTransitionProb(1,0,0.25); setTransitionProb(1,1,0.60); setTransitionProb(1,2,0.12); setTransitionProb(1,3,0.03);
    setTransitionProb(2,0,0.03); setTransitionProb(2,1,0.12); setTransitionProb(2,2,0.60); setTransitionProb(2,3,0.25);
    setTransitionProb(3,0,0.01); setTransitionProb(3,1,0.04); setTransitionProb(3,2,0.30); setTransitionProb(3,3,0.65);
  }

  void buildChoppy4() {
    G.reset();
    setTransitionProb(0,0,0.28); setTransitionProb(0,1,0.24); setTransitionProb(0,2,0.25); setTransitionProb(0,3,0.23);
    setTransitionProb(1,0,0.23); setTransitionProb(1,1,0.27); setTransitionProb(1,2,0.26); setTransitionProb(1,3,0.24);
    setTransitionProb(2,0,0.24); setTransitionProb(2,1,0.26); setTransitionProb(2,2,0.27); setTransitionProb(2,3,0.23);
    setTransitionProb(3,0,0.25); setTransitionProb(3,1,0.23); setTransitionProb(3,2,0.24); setTransitionProb(3,3,0.28);
  }

  // Compactness: lower Wiener => more compact; convert to a score in [0,1]
  double compactnessScore() {
    G.allPairsShortest();
    double W = G.wienerUndirectedLike();
    // simple squash: smaller W => larger score
    return 1.0/(1.0 + W);
  }
};

// ========================== Graph B: Asset Relationship Graph =============
// Edge weight = 1 - |corr|  (strong corr => short distance)
class AssetCorrelationGraph {
public:
  WeightedGraph G;

  AssetCorrelationGraph(int assets) : G(assets) {}

  static double corrToDist(double corr) {
    double a = fabs(corr);
    if(a > 1.0) a = 1.0;
    return 1.0 - a; // in [0,1]
  }

  void setCorr(int i,int j,double corr) {
    double w = corrToDist(corr);
    G.d[G.pos(i,j)] = w;
    G.d[G.pos(j,i)] = w;
  }

  // Example 3-asset correlation structure (toy)
  // 0=EUR/USD, 1=GBP/USD, 2=USD/JPY
  void buildToyFX() {
    G.reset();
    // self already 0
    setCorr(0,1,0.85);  // EURUSD ~ GBPUSD high positive corr -> short distance
    setCorr(0,2,-0.30); // EURUSD vs USDJPY mild negative -> larger distance
    setCorr(1,2,-0.25); // GBPUSD vs USDJPY mild negative
  }

  // Coupling: lower Wiener => assets tightly coupled; convert to risk penalty in [0,1]
  double couplingPenalty() {
    G.allPairsShortest();
    double W = G.wienerUndirectedLike();
    // smaller W => higher coupling => higher penalty; invert & squash
    return 1.0/(1.0 + W);
  }
};

// ========================== Strategy: Coupled Two-Graph Logic =============
class TwoGraphStrategy {
public:
  // Hyperparameters
  double alpha = 2.0;   // weight for regime compactness
  double beta  = 1.5;   // weight for asset coupling penalty
  double baseRisk = 1.0;

  // Graphs
  RegimeMarkovGraph RG;
  AssetCorrelationGraph AG;

  TwoGraphStrategy() : RG(4), AG(3) {}

  static double sigmoid(double x) {
    // stable-ish sigmoid
    if(x > 30) return 1.0;
    if(x < -30) return 0.0;
    return 1.0/(1.0 + exp(-x));
  }

  // In a real system:
  // - RG transitions come from online Markov counts over price-action states
  // - AG correlations come from rolling returns correlations of assets
  // Here: toy graphs to demonstrate the coupling logic.
  void buildToyInputs(int useDirectional) {
    if(useDirectional) RG.buildDirectional4();
    else              RG.buildChoppy4();

    AG.buildToyFX();
  }

  // Coupling between two graphs in "mathematical context":
  // we treat RG.compactness as signal quality and AG.coupling as diversification risk.
  double riskThrottle() {
    double comp = RG.compactnessScore();   // higher = better / more predictable
    double coup = AG.couplingPenalty();    // higher = more coupled / more dangerous

    // combined score -> throttle in [0,1]
    double x = alpha*comp - beta*coup;
    return sigmoid(x);
  }

  void explain(double thr) {
    printf("\n\n[TwoGraph] Regime compactness score = %.4f", RG.compactnessScore());
    printf("\n[TwoGraph] Asset coupling penalty   = %.4f", AG.couplingPenalty());
    printf("\n[TwoGraph] Risk throttle (0..1)     = %.4f", thr);
    printf("\n[TwoGraph] Suggested Lots scale     = %.3f", baseRisk * (0.25 + 0.75*thr));
  }

  // Demo run: compare two regimes (directional vs choppy) under same asset coupling
  void demo() {
    // Case A: directional
    buildToyInputs(1);
    double thrA = riskThrottle();
    printf("\n=== CASE A: Directional regime ===");
    RG.G.dump("\nRegime graph shortest distances:");
    AG.G.dump("\nAsset graph shortest distances:");
    explain(thrA);

    // Case B: choppy
    buildToyInputs(0);
    double thrB = riskThrottle();
    printf("\n\n=== CASE B: Choppy regime ===");
    RG.G.dump("\nRegime graph shortest distances:");
    AG.G.dump("\nAsset graph shortest distances:");
    explain(thrB);

    printf("\n\nInterpretation:");
    printf("\n- If regime transitions are compact (predictable), throttle increases.");
    printf("\n- If assets are tightly coupled (low diversification), throttle decreases.");
    printf("\n- The trading engine can use throttle to scale order size / aggression.");
  }
};

// =============================== Entry ===================================
static TwoGraphStrategy* S = 0;

DLLFUNC void run()
{
  if(is(INITRUN))
  {
    if(!S) S = new TwoGraphStrategy();
    S->demo();
    quit("Done.");
  }
}

Last edited by TipmyPip; Yesterday at 20:08.