177 lines
8.0 KiB
C#
177 lines
8.0 KiB
C#
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<DateTime, Price> pricesByDate = new Dictionary<DateTime, Price>();
|
|
Dictionary<DateTime, Signal> signalsByDate = new Dictionary<DateTime, Signal>();
|
|
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<DateTime, Price> pricesByDate = new Dictionary<DateTime, Price>();
|
|
Dictionary<DateTime, Signal> supportSignalsByDate = new Dictionary<DateTime, Signal>();
|
|
Dictionary<DateTime, Signal> signalsByDate = new Dictionary<DateTime, Signal>();
|
|
Dictionary<DateTime, Signal> crossoverSignalsByDate = new Dictionary<DateTime, Signal>();
|
|
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;index<tradeDates.Count;index++)
|
|
{
|
|
DateTime tradingDate = tradeDates[index];
|
|
if (!pricesByDate.ContainsKey(tradingDate)) continue;
|
|
if (!supportSignalsByDate.ContainsKey(tradingDate)) continue;
|
|
if (!signalsByDate.ContainsKey(tradingDate)) continue;
|
|
if(!crossoverSignalsByDate.ContainsKey(tradingDate))continue;
|
|
Signal signal = signalsByDate[tradingDate];
|
|
Signal supportSignal = supportSignalsByDate[tradingDate];
|
|
Signal crossoverSignal=crossoverSignalsByDate[tradingDate];
|
|
Price price = pricesByDate[signal.SignalDate];
|
|
if(signal.IsStrongBuy() && portfolio.AvailableCash>0 &&!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;
|
|
}
|
|
}
|
|
} |