public static Correlation GetCorrelation(this PortfolioSimulation sim, string correlationId) { var chunks = correlationId.Split('|'); var c = new Correlation(sim.Portfolio.FindComponent(chunks[0]), sim.Portfolio.FindComponent(chunks[1])); foreach (var component in c.Components) { if (!component.NormalizationMultiplier.HasValue) { component.UpdateComponentNormalizationMultiplier(sim); } } return(c); }
public static void CorrelateTimeInMarket(this PortfolioSimulation sim, Correlation c) { IEnumerable <PortfolioHistoricalTradeVM> AllTrades = c.Components .Select(b => b.Trades.OfType <_HistoricalTrade>().Select(t => new PortfolioHistoricalTradeVM { Trade = t, Component = b })) .SelectMany(v => v); var tradeEvents = AllTrades.Select(t => new TradeEnterExitEvent { Enter = true, Time = t.Trade.EntryTime, Trade = t }) .Concat(AllTrades.Select(t => new TradeEnterExitEvent { Enter = false, Time = t.Trade.ClosingTime, Trade = t })) .OrderBy(e => e.Time); //var TradesByEntryTime = AllTrades // .OrderBy(t => t.Trade.EntryTime) // .ToList(); // TODO: Normalized versions of these that are rated versus the max normalized score (which should be 1) TimeSpan BothLong = TimeSpan.Zero; TimeSpan BothShort = TimeSpan.Zero; TimeSpan OppositeDirection = TimeSpan.Zero; TimeSpan OffsettingNonZero = TimeSpan.Zero; TimeSpan BothFlat = TimeSpan.Zero; TimeSpan OneOpen = TimeSpan.Zero; // Not sure if this is useful yet. double MultipliedByDifference = 0; var openTrades = new List <PortfolioHistoricalTradeVM>(); double netVolume1 = 0; double netVolume2 = 0; double normalizedNetVolume1 = 0; double normalizedNetVolume2 = 0; double delta = normalizedNetVolume2 - normalizedNetVolume1; DateTime simTime = sim.Portfolio.Start.Value; TimeSpan totalTime; foreach (var e in tradeEvents) { #region Time Delta TimeSpan timeDelta; if (sim.Options.StartTime != default && e.Time < sim.Options.StartTime) { timeDelta = e.Time - sim.Options.StartTime; } else if (sim.Options.EndTime != default && e.Time > sim.Options.EndTime) { timeDelta = sim.Options.StartTime - e.Time; } else { timeDelta = e.Time - simTime; } if (timeDelta < TimeSpan.Zero) { continue; // If equal to zero, keep going, since there may be multiple events at the same bar opens. } totalTime += timeDelta; #endregion #region Collect stats from previous time delta #if DEBUG if (sim.Options.Verbosity >= 5) { WriteLine($"1: {normalizedNetVolume1} 2: {normalizedNetVolume2}"); } #endif // TODO NEXT: for the period of time between simTime and trade entry time (timeDelta), multiply this period of time by the volume delta over that time. if (normalizedNetVolume2 > 0 && normalizedNetVolume1 > 0) { BothLong += timeDelta; } else if (normalizedNetVolume2 < 0 && normalizedNetVolume1 < 0) { BothShort += timeDelta; } else if (normalizedNetVolume2 == 0 && normalizedNetVolume1 == 0) { BothFlat += timeDelta; } if (normalizedNetVolume2 < 0 && normalizedNetVolume1 > 0 || normalizedNetVolume2 < 0 && normalizedNetVolume1 > 0) { OppositeDirection += timeDelta; } else if (normalizedNetVolume2 != 0 && normalizedNetVolume1 == 0 || normalizedNetVolume2 == 0 && normalizedNetVolume1 != 0) { OneOpen += timeDelta; } // Another idea: non-zero, offsetting if (normalizedNetVolume2 != 0 && normalizedNetVolume1 == -normalizedNetVolume2) { OffsettingNonZero += timeDelta; } // These two normalized volumes should be normalized to -1 to +1. //MillisecondsMultipliedByDifference += timeDelta.TotalMilliseconds * (normalizedNetVolume2 - normalizedNetVolume1); MultipliedByDifference += timeDelta.TotalMilliseconds * Math.Pow(normalizedNetVolume2 - normalizedNetVolume1, 2); #endregion if (sim.Options.EndTime != default && simTime >= sim.Options.EndTime) { break; } #region Process the trade open/close event if (e.Enter) { sim.WriteLineVerbosity("OPEN trade:" + e.Trade.Trade.NetVolume, 5); openTrades.Add(e.Trade); } else { sim.WriteLineVerbosity("CLOSE trade:" + e.Trade.Trade.NetVolume, 5); if (!openTrades.Remove(e.Trade)) { WriteLine("[correlation] Removing from open trades - NOT FOUND: " + e + $" (entry time: {e.Trade.Trade.EntryTime})"); } } #endregion #region Set up state for the next trade: netVolume1 = 0; netVolume2 = 0; normalizedNetVolume1 = 0; normalizedNetVolume2 = 0; foreach (var t in openTrades) { if (t.Component.ComponentId == c.Component1.ComponentId) { var volumeDelta = t.Trade.TradeType == TradeType.Buy ? t.Trade.Volume : -t.Trade.Volume; netVolume1 += t.Trade.TradeType == TradeType.Buy ? t.Trade.Volume : -t.Trade.Volume; normalizedNetVolume1 += PortfolioNormalization.NormalizeVolume(t, sim, CorrelationNormalizationOptions); normalizedNetVolume1 = netVolume1; } else { #if DEBUG if (t.Component.ComponentId != c.Component2.ComponentId) { throw new UnreachableCodeException(); } #endif netVolume2 += t.Trade.TradeType == TradeType.Buy ? t.Trade.Volume : -t.Trade.Volume; normalizedNetVolume2 += PortfolioNormalization.NormalizeVolume(t, sim, CorrelationNormalizationOptions); normalizedNetVolume2 = netVolume2; } } delta = normalizedNetVolume2 - normalizedNetVolume1; simTime = e.Time; #endregion } //var PositionSimilarityScore = String.Format(sim.Options.NumberFormat, ((BothLong + BothShort) - Opposite).TotalMilliseconds / totalTime.TotalMilliseconds); //var PositioningSimilarityScore = String.Format(sim.Options.NumberFormat, ((BothLong + BothShort + BothFlat) - Opposite).TotalMilliseconds / totalTime.TotalMilliseconds); var SameDir = BothLong + BothShort; var BothOpen = SameDir + OppositeDirection; var BothSamePositioning = BothOpen + BothFlat; WriteLine(new { TotalTime = totalTime, BothFlat, BothLong, BothShort, OppositeDirection, BothOpen, SameDir, //OffsettingNonZero, BothFlatPc = (BothFlat.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), BothLongPc = (BothLong.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), BothShortPc = (BothShort.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), SameDirPc = (SameDir.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), OppositeDirectionPc = (OppositeDirection.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), BothOpenPc = (BothOpen.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), OffsettingNonZeroPc = (OffsettingNonZero.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), //OppositePc = (Opposite.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), SameDirectionSimilarityPc = ((SameDir).TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString() + " / " + (BothOpen.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), OppositeDirectionSimilarityPc = ((OppositeDirection).TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString() + " / " + (BothOpen.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), OpenDirectionNetSimilarityPc = ((SameDir - OppositeDirection).TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString() + " / " + (BothOpen.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), // TODO: same as OpenDirectionNetSimilarityPc but use normalized volumes: //NetAmplifyingOrHedgingPc = ((SameDir - OppositeDirection).TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString() // + " / " + (BothOpen.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), SimilarityWhenSharedDirectionPc = ((BothSamePositioning - OppositeDirection).TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString() + " / " + (BothSamePositioning.TotalMilliseconds / totalTime.TotalMilliseconds).ToPercentString(), DifferenceScore = Math.Sqrt(MultipliedByDifference / totalTime.TotalMilliseconds), }.DumpProperties("Time in market")); }