internal void CloseOpenTrade(PortfolioHistoricalTradeVM trade, int openTradesIndex) { if (Options.AssetExposureBars) { var longAssetBar = GetAssetExposureBar(trade.LongAsset, openTime); var shortAssetBar = GetAssetExposureBar(trade.ShortAsset, openTime); if (Options.JournalLevel >= 1) { Console.WriteLine($"{trade.Trade.EntryTime} CLOSE LONG {trade.LongAsset} x {trade.LongVolumeAtEntry} | SHORT {trade.ShortAsset} x {trade.ShortVolumeAtEntry}"); } longAssetBar.Close -= trade.LongVolumeAtEntry; shortAssetBar.Close += trade.ShortVolumeAtEntry; } OpenTrades.RemoveAt(openTradesIndex); CurrentBalance += trade.Trade.NetProfit / trade.Component.BacktestResult.InitialBalance; if (Options.ComponentExposureBars) { trade.Component.CloseTrade(trade.Trade); } }
internal void EnterOpenTrade(PortfolioHistoricalTradeVM trade) { #if DEBUG if (Options.Verbosity >= 4) { Console.WriteLine(trade.Trade.DumpProperties()); } #endif //var symbol1 = trade.Component.BacktestResult.Symbol; var symbol = trade.Trade.SymbolCode; string longAsset, shortAsset; if (Options.AssetExposureBars) { if (trade.Component.IsMultiSymbol) { (longAsset, shortAsset) = AssetNameResolver.ResolvePair(symbol); if (trade.LongAsset == null || trade.ShortAsset == null) { throw new AnalysisException($"Could not resolve long and short components of symbol {symbol}"); } } else { if (trade.Component.LongAsset == null) { throw new AnalysisException($"Could not resolve long and short components of symbol {symbol}"); } longAsset = trade.LongAsset = trade.Component.LongAsset; shortAsset = trade.ShortAsset = trade.Component.ShortAsset; } //Console.WriteLine($"EnterOpenTrade | {symbol} | {trade.LongAsset} | {trade.ShortAsset}"); var longAssetBar = GetAssetExposureBar(longAsset, openTime); var shortAssetBar = GetAssetExposureBar(shortAsset, openTime); trade.PopulateEntryVolumes(); if (Options.JournalLevel >= 1) { Console.WriteLine($"{trade.Trade.EntryTime} OPEN LONG {longAsset} x {trade.LongVolumeAtEntry} | SHORT {shortAsset} x {trade.ShortVolumeAtEntry}"); } longAssetBar.Close += trade.LongVolumeAtEntry; shortAssetBar.Close -= trade.ShortVolumeAtEntry; } OpenTrades.Add(trade); if (Options.ComponentExposureBars) { trade.Component.OpenTrade(trade.Trade); } }
/// <param name="trade"></param> /// <param name="sim"></param> /// <returns>Returns negative numbers for Sell trades.</returns> public static double NormalizeVolume(PortfolioHistoricalTradeVM trade, PortfolioSimulation sim, VolumeNormalizationOptions normalizationOptions = null) { // Note: Volume is used here instead of NetVolume, to have positive volumes for both buy and sell trades var normalizedTradeVolume = NormalizeVolumeSource(sim, trade.Component, trade.Trade.Volume); #if DEBUG if (sim.Options.Verbosity >= 5) { Console.WriteLine($"[normalize] {trade.Trade.SymbolCode} normalized volume source from ({trade.Trade.TradeType.ToString()}) {trade.Trade.Volume} to {normalizedTradeVolume}"); } #endif if ((normalizationOptions?.MaxMode ?? sim.Options.VolumeNormalization.MaxMode).Value != VolumeNormalizationTargetMode.None) { double max = normalizationOptions?.Max ?? sim.Options.VolumeNormalization.Max ?? trade.Component.MaxAbsoluteVolume; //switch (mode ?? sim.Options.VolumeNormalization.VolumeNormalizationTargetMax) //{ // case PortfolioNormalizationTargetMaxMode.None: // //var componentMaxNormalizedVolume = NormalizeVolumeSource(sim, trade.Component, trade.Component.MaxAbsoluteVolume); // //max = componentMaxNormalizedVolume; // //max = // break; // case PortfolioNormalizationTargetMaxMode.ToConstant: // max = ; // break; // //case PortfolioNormalizationTargetMaxMode. // //case PortfolioNormalizationTargetMaxMode.ScaleToMinTradeVolume: // // max = // // value = // // if (trade.Trade.TradeType == TradeType.Sell) value *= -1; // // break; // //case PortfolioNormalizationTargetMaxMode.ToBacktestMaxTradeSize: // // value = trade.Trade.Volume / trade.Component.MaxAbsoluteVolume; // // if (trade.Trade.TradeType == TradeType.Sell) value *= -1; // // break; // ////case PortfolioNormalizationMode.ToBacktestConfiguredMaxTradeSize: // //// throw new NotImplementedException(); // //// break; // default: // //case PortfolioNormalizationMode.Unspecified: // throw new ArgumentException(nameof(sim.Options.VolumeNormalization.VolumeNormalizationTargetMax)); //} switch (sim.Options.VolumeNormalization.Curve) { case PortfolioNormalizationCurveType.Linear: normalizedTradeVolume = trade.Trade.TradeType == TradeType.Buy ? trade.Trade.Volume : -trade.Trade.Volume; break; case PortfolioNormalizationCurveType.Step: normalizedTradeVolume = normalizedTradeVolume >= sim.Options.VolumeNormalization.StepThreshold ? max : 0; break; case PortfolioNormalizationCurveType.EaseIn: { double normalizedTo1 = normalizedTradeVolume / max; normalizedTradeVolume = max * Math.Pow(normalizedTo1, sim.Options.VolumeNormalization.EasingExponent ?? VolumeNormalizationOptions.DefaultEasingExponent); break; } case PortfolioNormalizationCurveType.EaseOut: { double normalizedTo1 = normalizedTradeVolume / max; normalizedTradeVolume = max * Math.Pow(1 - (1 - normalizedTo1), sim.Options.VolumeNormalization.EasingExponent ?? VolumeNormalizationOptions.DefaultEasingExponent); break; } //case PortfolioNormalizationCurveType.Unspecified: default: break; } } if (trade.Trade.TradeType == TradeType.Sell) { normalizedTradeVolume *= -1; } #if DEBUG if (sim.Options.Verbosity >= 0) { if (trade.Trade.Volume != 1) { Console.WriteLine($"[NORMALIZE] {trade.Trade.SymbolCode} normalized volume from ({trade.Trade.TradeType.ToString()}) {trade.Trade.Volume} to {normalizedTradeVolume} (final)"); } } #endif return(normalizedTradeVolume); }