private Execution ExecuteArbitrageParallel(Arbitrage arbitrage) { Execution execution = new Execution(arbitrage); Parallel.ForEach(execution.GetSequence(), sequence => { Int32 offset = sequence.Item1; TriangulationStep x = sequence.Item2; TriangulationStep y = sequence.Item3; if (!Config.TradingEnabled) { m_ExchangeEngine.TestOrder(x.Position, x.Symbol, arbitrage.GetQuantity(offset)); return; } OrderResult result = m_ExchangeEngine.PlaceOrder(x.Position, x.Symbol, arbitrage.GetQuantity(offset)); if (x.Position == Position.Buy) { x.Out = result.OtherQuantity; y.In = result.ExecutedQuantity; } else { x.Out = result.ExecutedQuantity; y.In = result.OtherQuantity; } execution.Fees += result.Fees; }); return(execution); }
private void ExecuteArbitrage(Arbitrage arbitrage) { MessageCollection message = new MessageCollection(true, Utilities.FormatMessage(Resources.ArbitrageFound, arbitrage.Identifier, $"{arbitrage.Profit:F4}")); if (Config.TradingEnabled && (Config.TradingExecutionsCap > 0) && (m_Executions.Count >= Config.TradingExecutionsCap)) { message.AppendMessage(Utilities.FormatMessage(Resources.ArbitrageDiscardedCap, Config.TradingExecutionsCap)); m_MessagePump.Signal(message); return; } if (arbitrage.Profit < Config.TradingThresholdProfit) { message.AppendMessage(Utilities.FormatMessage(Resources.ArbitrageDiscardedProfit, $"{Config.TradingThresholdProfit:F4}")); return; } if (m_ExecutionSymbols.Any(arbitrage.GetReferenceAssets().Contains)) { message.AppendMessage(Resources.ArbitrageDiscardedAssets); m_MessagePump.Signal(message); return; } if (m_Executions.Count(x => x.Key >= DateTime.Now.AddSeconds(-1d)) > 1) { message.AppendMessage(Resources.ArbitrageDiscardedCooldown); m_MessagePump.Signal(message); return; } Int32 age = (DateTime.Now - arbitrage.ReferenceTime).Milliseconds; if (age > Config.TradingThresholdAge) { message.AppendMessage(Utilities.FormatMessage(Resources.ArbitrageDiscardedAge, age, Config.TradingThresholdAge)); m_MessagePump.Signal(message); return; } foreach (String referenceAsset in arbitrage.GetReferenceAssets()) { m_ExecutionSymbols.Add(referenceAsset); } try { Execution execution; if (Config.TradingStrategy == Strategy.Parallel) { execution = ExecuteArbitrageParallel(arbitrage); } else { execution = ExecuteArbitrageSequential(arbitrage); } m_Executions[DateTime.Now] = execution; message.AppendMessage(Utilities.FormatMessage(Resources.ArbitrageSucceded, Config.TradingStrategy.ToString().ToUpperInvariant())); foreach (var zip in arbitrage.GetSequence().Zip(execution.GetSequence(), (e, o) => new { Expected = e, Observed = o })) { Int32 offset = zip.Expected.Item1; TriangulationStep eX = zip.Expected.Item2; TriangulationStep eY = zip.Expected.Item3; TriangulationStep oX = zip.Observed.Item2; TriangulationStep oY = zip.Observed.Item3; message.AppendMessage(Utilities.FormatMessage(Resources.ArbitrageStep, offset)); message.AppendMessage(Utilities.FormatMessage(Resources.ArbitrageExpectedConversion, $"{eX.Out:F8}", eX.ReferenceAsset, $"{eY.Out:F8}", eY.ReferenceAsset)); message.AppendMessage(Utilities.FormatMessage(Resources.ArbitrageObservedConversion, $"{oX.Out:F8}", oX.ReferenceAsset, $"{oY.Out:F8}", oY.ReferenceAsset)); } message.AppendMessage(Resources.ArbitrageDeltas); foreach (TriangulationStep step in execution.GetSteps()) { Decimal eDelta = step.Delta; Decimal ePercent = (eDelta / step.Out) * 100M; message.AppendMessage(Utilities.FormatMessage(Resources.ArbitrageDelta, step.ReferenceAsset, (eDelta < 0M) ? String.Empty : " ", $"{eDelta:F8}", $"{ePercent:F4}")); } message.AppendMessage(Resources.ArbitrageOthers); message.AppendMessage(Utilities.FormatMessage(Resources.ArbitrageCommission, $"{(-1M * execution.Fees):F8}")); } catch (Exception e) { message.AppendMessage(Utilities.FormatMessage(Resources.ArbitrageFailed, Config.TradingStrategy.ToString().ToUpperInvariant(), Utilities.GetExceptionMessage(e))); } foreach (String referenceAsset in arbitrage.GetReferenceAssets()) { m_ExecutionSymbols.Remove(referenceAsset); } m_MessagePump.Signal(message); }