Overall Statistics
Total Trades
910
Average Win
0.42%
Average Loss
-0.33%
Compounding Annual Return
20.970%
Drawdown
39.400%
Expectancy
1.096
Net Profit
1116.155%
Sharpe Ratio
0.869
Probabilistic Sharpe Ratio
20.349%
Loss Rate
8%
Win Rate
92%
Profit-Loss Ratio
1.28
Alpha
0.024
Beta
1.198
Annual Standard Deviation
0.184
Annual Variance
0.034
Information Ratio
0.845
Tracking Error
0.055
Treynor Ratio
0.134
Total Fees
$954.47
Estimated Strategy Capacity
$320000.00
Lowest Capacity Asset
DBP TP2MIF0KNIAT
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Common.Logging.Factory;
using QuantConnect.Data;
using QuantConnect.Indicators;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Util;

namespace QuantConnect.Algorithm.CSharp {
public class AllWeather : QCAlgorithm {
  private Strat[] _strategies;

  public override void Initialize() {
    SetStartDate(2009, 1, 1);
    SetCash(100000);

    var weightings = new (string Sector, Weighting Target)[] {
      ("Industrial", new("SPY", 0.35m)),
      ("Metals", new("DBP", 0.10m)),
      ("Crypto", new("GBTC", 0.35m)),
      ("China", new("GXC", 0.06m)),
      ("Tech", new("XLK", 0.16m)),
      ("Energy", new("XLE", 0.09m)),
      ("Finance", new("XLF", 0.08m)),
      ("Health", new("XLV", 0.07m)),
      ("Fixed", new("TLT", 0.15m))
    };

    var coreInvestor = new LongFixedRatio(weightings.Select(x => x.Target), leverage: 3.3m);
    Schedule.On(DateRules.MonthStart(), TimeRules.At(10, 00), coreInvestor.ScheduleRebalance);
    _strategies = new[] { coreInvestor };

    foreach (var inv in _strategies) {
      foreach (var sym in inv.Symbols) {
        var equity = AddEquity(sym, Resolution.Daily);
        equity.SetDataNormalizationMode(DataNormalizationMode.Raw);
      }
    }
  }

  public override void OnData(Slice data) {
    Plot("Values", "Margin Remaining", Portfolio.MarginRemaining / Portfolio.TotalPortfolioValue);
    Plot("Values", "Margin Used", Portfolio.TotalMarginUsed / Portfolio.TotalPortfolioValue);
    foreach (var inv in _strategies) {
      inv.OnData(this, data);
    }
  }
}

internal interface Strat {
  public decimal Leverage { get; }
  public IEnumerable<Symbol> Symbols { get; }
  public void OnData(QCAlgorithm algo, Slice slice);
}

internal class LongFixedRatio : Strat {
  private readonly Weighting[] _weightings;
  private bool _shouldRebalance = true;

  public decimal Leverage { get; }

  public IEnumerable<Symbol> Symbols => _weightings.Select(x => x.Symbol);

  public LongFixedRatio(IEnumerable<Weighting> weightings, decimal leverage) {
    _weightings = weightings.ToArray();
    Leverage = leverage;
  }

  public void ScheduleRebalance() {
    _shouldRebalance = true;
  }

  public void OnData(QCAlgorithm algo, Slice slice) {
    if (_shouldRebalance) {
      var P = algo.Portfolio;
      var validWeightings = _weightings.Where(w => slice.Bars.ContainsKey(w.Symbol)).ToArray();
      var totalWeight = validWeightings.Select(w => w.Weight).Sum();
      var realizedWeightings = validWeightings.Select(x => x with { Weight = x.Weight / totalWeight });

      var totalMargin = 2 * P.TotalMarginUsed + P.MarginRemaining;

      foreach (var wt in realizedWeightings) {
        var close = slice.Bars[wt.Symbol].Close;
        int goalQty = (int)Math.Truncate(0.9m * totalMargin * wt.Weight / close);
        var currentQty = (int)P[wt.Symbol].Quantity;

        int toBuyQty = goalQty - currentQty;

        algo.Plot("Space", wt.Symbol, (P.MarginRemaining - toBuyQty * close) / P.TotalPortfolioValue);
        algo.LimitOrder(wt.Symbol, toBuyQty, close);
      }

      _shouldRebalance = false;
    }
  }
}

record Weighting(Symbol Symbol, decimal Weight) {
  public Weighting(string symbol, decimal weight) :
    this(Symbol.Create(symbol, SecurityType.Equity, Market.USA), weight) { }
}
}