17 using System.Collections.Generic;
24 using static System.FormattableString;
35 private readonly
int _lookback;
37 private readonly TimeSpan _predictionInterval;
38 private readonly decimal _threshold;
39 private readonly Dictionary<Tuple<Symbol, Symbol>, PairData> _pairs;
55 decimal threshold = 1m
59 _resolution = resolution;
60 _threshold = threshold;
61 _predictionInterval = _resolution.ToTimeSpan().Multiply(_lookback);
62 _pairs =
new Dictionary<Tuple<Symbol, Symbol>, PairData>();
65 Name = Invariant($
"{nameof(BasePairsTradingAlphaModel)}({_lookback},{_resolution},{_threshold.Normalize()})");
77 var insights =
new List<Insight>();
79 foreach (var kvp
in _pairs)
81 insights.AddRange(kvp.Value.GetInsightGroup());
96 UpdatePairs(algorithm);
99 foreach (var security
in changes.RemovedSecurities)
101 var symbol = security.Symbol;
102 var keys = _pairs.Keys.Where(k => k.Item1 == symbol || k.Item2 == symbol).ToList();
104 foreach (var key
in keys)
106 var pair = _pairs[key];
124 var assets =
Securities.Select(x => x.Symbol).ToArray();
126 for (var i = 0; i < assets.Length; i++)
128 var assetI = assets[i];
130 for (var j = i + 1; j < assets.Length; j++)
132 var assetJ = assets[j];
134 var pairSymbol = Tuple.Create(assetI, assetJ);
135 var invert = Tuple.Create(assetJ, assetI);
137 if (_pairs.ContainsKey(pairSymbol) || _pairs.ContainsKey(invert))
147 var pairData =
new PairData(algorithm, assetI, assetJ, _predictionInterval, _threshold);
148 _pairs.Add(pairSymbol, pairData);
153 private class PairData : IDisposable
162 private State _state = State.FlatRatio;
164 private readonly QCAlgorithm _algorithm;
165 private readonly Symbol _asset1;
166 private readonly Symbol _asset2;
177 private readonly TimeSpan _predictionInterval;
187 public PairData(QCAlgorithm algorithm, Symbol asset1, Symbol asset2, TimeSpan period, decimal threshold)
189 _algorithm = algorithm;
198 var resolution = algorithm.SubscriptionManager
199 .SubscriptionDataConfigService
200 .GetSubscriptionDataConfigs(symbol)
201 .Min(x => x.Resolution);
203 var name = algorithm.CreateIndicatorName(symbol,
"close", resolution);
206 var consolidator = algorithm.ResolveConsolidator(symbol, resolution);
207 algorithm.RegisterIndicator(symbol, identity, consolidator);
209 return (identity, consolidator);
212 (_asset1Price, _identityConsolidator1) = CreateIdentityIndicator(asset1);
213 (_asset2Price, _identityConsolidator2) = CreateIdentityIndicator(asset2);
215 _ratio = _asset1Price.Over(_asset2Price);
219 _upperThreshold = _mean.Times(upper,
"UpperThreshold");
222 _lowerThreshold = _mean.Times(lower,
"LowerThreshold");
224 _predictionInterval = period;
230 public void Dispose()
232 _algorithm.SubscriptionManager.RemoveConsolidator(_asset1, _identityConsolidator1);
233 _algorithm.SubscriptionManager.RemoveConsolidator(_asset2, _identityConsolidator2);
240 public IEnumerable<Insight> GetInsightGroup()
244 return Enumerable.Empty<Insight>();
248 if (_state != State.LongRatio && _ratio > _upperThreshold)
250 _state = State.LongRatio;
253 var shortAsset1 = Insight.Price(_asset1, _predictionInterval,
InsightDirection.Down);
254 var longAsset2 = Insight.Price(_asset2, _predictionInterval,
InsightDirection.Up);
257 return Insight.Group(shortAsset1, longAsset2);
261 if (_state != State.ShortRatio && _ratio < _lowerThreshold)
263 _state = State.ShortRatio;
266 var longAsset1 = Insight.Price(_asset1, _predictionInterval,
InsightDirection.Up);
267 var shortAsset2 = Insight.Price(_asset2, _predictionInterval,
InsightDirection.Down);
270 return Insight.Group(longAsset1, shortAsset2);
273 return Enumerable.Empty<Insight>();