Files
marketdata/MarketDataLib/Generator/SignalTrader.cs
2024-02-22 14:52:53 -05:00

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;
}
}
}