using System; using System.Text; using System.Collections.Generic; using MarketData.DataAccess; using MarketData.MarketDataModel; using MarketData.Utils; namespace MarketData.Generator { public class SignalTrader { private SignalTrader() { } // stopLossThreshold is a percentage that minimizes the loss on the sell side and sells on the Sell signal if the last // trade price dips below the offering price. public static Portfolio TradeSignals(DateRange tradeDates,Signals signals, Prices prices, double initialCash, double stopLossThreshold, int stopLossDays) { Dictionary pricesByDate = new Dictionary(); Dictionary signalsByDate = new Dictionary(); Portfolio portfolio = new Portfolio(initialCash); for (int index = 0; index < prices.Count; index++) { Price price = prices[index]; pricesByDate.Add(price.Date, price); } for (int index = 0; index < signals.Count; index++) { Signal signal = signals[index]; signalsByDate.Add(signal.SignalDate, signal); } // portfolio.AvailableCash = initialCash; for (int index = 0; index < tradeDates.Count; index++) { DateTime tradingDate = tradeDates[index]; if (!pricesByDate.ContainsKey(tradingDate)) continue; if (!signalsByDate.ContainsKey(tradingDate)) continue; Signal signal = signalsByDate[tradingDate]; if (signal.IsStrongBuy() && !portfolio.HasOpenPosition) { if (portfolio.AvailableCash > 0) { ModelTrade trade = new ModelTrade(); Price price = pricesByDate[signal.SignalDate]; trade.Symbol = price.Symbol; trade.Price = price.Close; trade.Shares = (int)(portfolio.AvailableCash / trade.Price); trade.TradeDate = price.Date; trade.Exposure = trade.Shares * trade.Price; trade.Type = ModelTrade.TradeType.Buy; portfolio.AvailableCash -= (trade.Price * trade.Shares); portfolio.Add(trade); portfolio.HasOpenPosition = true; } } else if (signal.IsStrongSell() && portfolio.HasOpenPosition) { ModelTrade trade = new ModelTrade(); ModelTrade lastTrade = portfolio.GetLastTrade(); Price price = pricesByDate[signal.SignalDate]; if (0.00 == stopLossThreshold && price.Close < lastTrade.Price) continue; int daysHeld = (price.Date - lastTrade.TradeDate).Days; double stopLossPrice = lastTrade.Price - (lastTrade.Price * stopLossThreshold); bool stopLossIndicator = price.Close < stopLossPrice && daysHeld > stopLossDays ? true : false; if (!stopLossIndicator && price.Close < lastTrade.Price) continue; else if (stopLossIndicator) trade.Comment = "Stop Loss. price decrease " + String.Format("{0:p}", stopLossThreshold) + ", holding days (" + daysHeld + ") >" + stopLossDays; portfolio.AvailableCash += ((lastTrade.Shares * price.Close)); trade.Symbol = price.Symbol; trade.Price = price.Close; trade.Shares = (int)(portfolio.AvailableCash / trade.Price); trade.Type = ModelTrade.TradeType.Sell; trade.TradeDate = price.Date; double marketValue = (trade.Price * trade.Shares); // trade.GainLoss = (trade.Price * trade.Shares) - (lastTrade.Price * lastTrade.Shares); trade.GainLoss = marketValue - lastTrade.Exposure; trade.Return = ((marketValue - lastTrade.Exposure) / lastTrade.Exposure); trade.DaysHeld = daysHeld; portfolio.Add(trade); portfolio.HasOpenPosition = false; } } return portfolio; } // Add the MA(10,50) // if (8,17,9) signals a buy and we are on the + side of the MA(10,50) then seek support from the (12,26,9) which must be a buy signal // if (8,17,9) signals a buy and we are on the - side of the MA(10,50) then no support is necessary. // sell when the (8,17,9) signals a sell public static Portfolio TradeSignals(DateRange tradeDates,Signals signals, Signals supportSignals, Signals crossoverSignals,Prices prices, double initialCash) { Dictionary pricesByDate = new Dictionary(); Dictionary supportSignalsByDate = new Dictionary(); Dictionary signalsByDate = new Dictionary(); Dictionary crossoverSignalsByDate = new Dictionary(); Signal prevSignal = null; MDTrace.WriteLine(LogLevel.DEBUG,"[SignalTrader::TradeSignals]"); Portfolio portfolio = new Portfolio(initialCash); int holdingDays = 0; int tradeCount = 0; int averageHoldingDays = 0; for (int index = 0; index < prices.Count; index++) { Price price = prices[index]; pricesByDate.Add(price.Date, price); } for (int index = 0; index < supportSignals.Count; index++) { Signal supportSignal = supportSignals[index]; supportSignalsByDate.Add(supportSignal.SignalDate, supportSignal); } for (int index = 0; index < signals.Count; index++) { Signal signal = signals[index]; signalsByDate.Add(signal.SignalDate, signal); } for (int index = 0; index < crossoverSignals.Count; index++) { Signal signal = crossoverSignals[index]; crossoverSignalsByDate.Add(signal.SignalDate, signal); } // portfolio.AvailableCash = initialCash; for(int index=0;index0 &&!portfolio.HasOpenPosition) { if (null != prevSignal && prevSignal.IsStrongBuy()) continue; if (crossoverSignal.IsBuy() && !supportSignal.IsBuy()) continue; ModelTrade trade = new ModelTrade(); trade.Symbol = price.Symbol; trade.Price = price.Close; trade.Shares = (int)(portfolio.AvailableCash / trade.Price); trade.TradeDate = price.Date; trade.Type = ModelTrade.TradeType.Buy; trade.Exposure = trade.Shares * trade.Price; portfolio.AvailableCash -= (trade.Price * trade.Shares); portfolio.Add(trade); portfolio.HasOpenPosition = true; } else if (signal.IsStrongSell() && portfolio.HasOpenPosition) { ModelTrade trade = new ModelTrade(); ModelTrade lastTrade = portfolio.GetLastTrade(); if (price.Close < lastTrade.Price) continue; portfolio.AvailableCash += ((lastTrade.Shares * price.Close)); trade.Symbol = price.Symbol; trade.Price = price.Close; trade.Shares = (int)(portfolio.AvailableCash / trade.Price); trade.Type = ModelTrade.TradeType.Sell; trade.TradeDate = price.Date; double marketValue = trade.Price * trade.Shares; trade.GainLoss = marketValue - lastTrade.Exposure; trade.Return = (marketValue - lastTrade.Exposure) / lastTrade.Exposure; trade.DaysHeld = (price.Date - lastTrade.TradeDate).Days; ; portfolio.Add(trade); portfolio.HasOpenPosition = false; tradeCount++; holdingDays += (price.Date - lastTrade.TradeDate).Days; averageHoldingDays = holdingDays / tradeCount; } prevSignal = signal; } return portfolio; } } }