using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Xml;
using TradeIdeas.XML;
using TradeIdeas.TIProData;
using TradeIdeas.TIProGUI;
using TradeIdeas.TIProData.Configuration;
using System.Reflection;
using System.Collections.Specialized;
using System.Web;
using System.IO;
using TradeIdeas.MiscSupport;
using TradeIdeas.TIProData.Interfaces;
namespace TIProAutoTradeExtension
{
public enum TradePositionSizing { FixedShares, FixedDollars, BasedOnStopLoss }
public enum TradeEntryType { Limit, Market, StopLimit, Stop }
//public enum TradeEntryLimitReference { Last, BidAsk, Filter }
public enum TradeEntryLimitReference { Last, Filter }
///
/// Mode for how to treat the limit offset amount: as a dollar amount (default) or a percent
///
public enum TradeEntryLimitOffsetType {
///
/// Limit offset amount is in dollars
///
Dollars,
///
/// Limit offset amount is interpreted as a percent
///
Percent
}
public enum TradeStopLossType { None, Percent, Dollars, Filter }
///
/// Mode for trading AI signals: RiskOff, RiskOn, BestAtSignalTime
///
public enum AiTradingMode
{
///
/// Strategy specific trading instruction are in effect
///
RiskOff,
///
/// Strategy specific trading instructions are mostly ignored
///
RiskOn
///
/// Choose RiskOn or RiskOff depending on best mode for segment at signal time
///
//BestAtSignalTime
};
///
/// The type of time stop order to be used for a strategy.
///
public enum TradeTimeStopType
{
///
/// No time stop order will be entered.
///
None,
///
/// A time stop order will be entered at a certain time of day on the same day of the trade signal.
///
TimeOfDay,
///
/// A time stop order will be entered a certain amount of time after the trade signal.
///
TimeAfterSignal,
///
/// At the market open after a certain number of days.
///
AtOpenDays,
///
/// At the market close after a certain number of days.
///
AtCloseDays
}
///
/// Fired when a server-side advanced exit triggers for an AI trade.
///
/// The row data.
public delegate void AdvancedExitTriggeredHandler(RowData rowData);
public delegate void NewAlertHandler(RowData rowData, TradingStrategy strategy);
public class TradingStrategy: IRowDataCapable
{
private bool _long = true;
[ColumnInfoAttribute(DisplayName = "Side", Format = "longshort", SizeHint = "XXXXX")]
public bool Long
{
get { return _long; }
set { _long = value; }
}
private TradeEntryType _tradeEntryType = TradeEntryType.Limit;
[ColumnInfoAttribute(DisplayName = "Entry Type", SizeHint = "XXXXXXX", Format = "textalignright")]
public TradeEntryType TradeEntryType
{
get { return _tradeEntryType; }
set { _tradeEntryType = value; }
}
private string _id = "";
[Browsable(false)]
public string Id
{
get { return _id; }
set { _id = value; }
}
private AiTradingMode _aiTradingMode = AiTradingMode.RiskOff;
[Browsable(false)]
public AiTradingMode AiTradingMode
{
get { return _aiTradingMode; }
set { _aiTradingMode = value; }
}
private string _name = "";
[ColumnInfoAttribute(SizeHint="XXXXXXXXXXXXXXXXXX", Format = "strategyname")]
public string Name
{
get { return _name; }
set { _name = value; }
}
private double? _winrate = null;
//[ColumnInfoAttribute(DisplayName = "Win %", Format="1", SizeHint = "Win %XX", MidForColor = 50, MinForColor = 30, MaxForColor = 80.0)]
[Browsable(false)]
public double? WinRate
{
get { return _winrate; }
set { _winrate = value; }
}
private double? _omTrades = null;
//[ColumnInfoAttribute(DisplayName = "Simulated Trades", Format="0", SizeHint = "TradesXX")]
[Browsable(false)]
public double? OmTrades
{
get { return _omTrades; }
set { _omTrades = value; }
}
private double? _profitFactor = null;
//[ColumnInfoAttribute(DisplayName = "Profit Factor", Format="2", SizeHint = "ProfitXX", MinForColor = 0.5, MidForColor = 1, MaxForColor = 5.0)]
[Browsable(false)]
public double? ProfitFactor
{
get { return _profitFactor; }
set { _profitFactor = value; }
}
private double? _oddsMakerProgress = null;
//[ColumnInfoAttribute(DisplayName = "Odds Maker Progress", Format="2", SizeHint = "Odds MakerXX", MinForColor = -100, MidForColor = 0, MaxForColor = 100, DefaultVisible = false)]
[Browsable(false)]
public double? OddsMakerProgress
{
get { return _oddsMakerProgress; }
set { _oddsMakerProgress = value; }
}
private bool _oddsMakerIsRunning = false;
[Browsable(false)]
public bool OddsMakerIsRunning
{
get { return _oddsMakerIsRunning; }
set { _oddsMakerIsRunning = value; }
}
private bool _oddsMakerIsQueued = false;
[Browsable(false)]
public bool OddsMakerIsQueued
{
get { return _oddsMakerIsQueued; }
set { _oddsMakerIsQueued = value; }
}
private string _oddsMakerCsv = "";
[Browsable(false)]
public string OddsMakerCsv
{
get { return _oddsMakerCsv; }
set { _oddsMakerCsv = value; }
}
private string _lastSymbol = "";
[ColumnInfoAttribute(DisplayName = "Last Symbol", SizeHint = "SymbolXX")]
public string LastSymbol
{
get { return _lastSymbol; }
set { _lastSymbol = value; }
}
private decimal _lastSignalPrice = 0;
[ColumnInfoAttribute(DisplayName = "Last Signal Price", SizeHint = "SymbolXX", Format="2")]
public decimal LastSignalPrice
{
get { return _lastSignalPrice; }
set { _lastSignalPrice = value; }
}
private DateTime? _lastSignalTime = null;
[ColumnInfoAttribute(Format = "relativetime", DisplayName = "Last Signal Time", SizeHint = "Last SignalXX")]
public DateTime? LastSignalTime
{
get { return _lastSignalTime; }
set { _lastSignalTime = value; }
}
private bool _tradingEnabled = false;
[ColumnInfoAttribute(DisplayName = "Strategy Status", Format = "onoff", SizeHint = "StrategyXX")]
public bool TradingEnabled
{
get { return _tradingEnabled; }
set { _tradingEnabled = value; }
}
private DateTime _startTime = GuiEnvironment.GetMarketOpenLocalTime();
[ColumnInfoAttribute(DisplayName="Start Time",Format = "tod", SizeHint = "XX:XX XX")]
public DateTime StartTime
{
get { return _startTime; }
set { _startTime = value; }
}
private int _startTimeMinutesFromOpen = GetXmlValue("START_TIME_MINUTES_FROM_OPEN", 5);
[ColumnInfoAttribute(DisplayName="Start Time Minutes from Open",Format = "0")]
public int StartTimeMinutesFromOpen
{
get { return _startTimeMinutesFromOpen; }
set
{
_startTimeMinutesFromOpen = value;
DateTime startTimeInMarketTimeZone = GuiEnvironment.GetMarketOpenInMarketTimeZone().AddMinutes(_startTimeMinutesFromOpen);
_startTime = GuiEnvironment.ConvertTimeCheckingKindProperty(startTimeInMarketTimeZone, GuiEnvironment.GetMarketTimeZone(), TimeZoneInfo.Local);
}
}
private DateTime _endTime = GuiEnvironment.GetMarketCloseLocalTime();
[ColumnInfoAttribute(DisplayName = "End Time", Format = "tod", SizeHint = "XX:XX XX")]
public DateTime EndTime
{
get { return _endTime; }
set { _endTime = value; }
}
private int _endTimeMinutesFromClose = GetXmlValue("END_TIME_MINUTES_BEFORE_CLOSE", 15);
[ColumnInfoAttribute(DisplayName="End Time Minutes from Close",Format = "0")]
public int EndTimeMinutesFromClose
{
get { return _endTimeMinutesFromClose; }
set
{
_endTimeMinutesFromClose = value;
DateTime endTimeInMarketTimeZone = GuiEnvironment.GetMarketCloseInMarketTimeZone().AddMinutes(-_endTimeMinutesFromClose);
_endTime = GuiEnvironment.ConvertTimeCheckingKindProperty(endTimeInMarketTimeZone, GuiEnvironment.GetMarketTimeZone(), TimeZoneInfo.Local);
}
}
private int _maxDailyOrders = GetXmlValue("MAX_DAILY_ORDERS", 10);
[ColumnInfoAttribute(Format = "0", DisplayName = "Max Daily Trades", SizeHint = "Max DailyXX")]
public int MaxDailyOrders
{
get { return _maxDailyOrders; }
set { _maxDailyOrders = value; }
}
private bool _cfdEnabled = false;
///
/// Should this strategy use CFDs or the normal underlying? This applies to non-US only.
///
[ColumnInfoAttribute(Format = "truefalse", DisplayName = "CFD Enabled", SizeHint = "FALSEXX")]
public bool CfdEnabled
{
get { return _cfdEnabled; }
set { _cfdEnabled = value; }
}
// tracking attributes
private int _tradesSoFarToday = 0;
[ColumnInfoAttribute(DisplayName = "Trades Today", Format = "0", SizeHint = "Trades")]
public int TradesSoFarToday
{
get { return _tradesSoFarToday; }
set { _tradesSoFarToday = value; }
}
[ColumnInfoAttribute(DisplayName = "Stop Loss / Profit Target", Format = "textaligncenter", SizeHint = "Profit TargetXX")]
public string ProfitTargetStopLoss
{
get { return _stopLossOffset.ToString("N2") + " / " + _targetOffset.ToString("N2"); }
}
private int _timeStopDays;
[ColumnInfoAttribute(DisplayName = "Exit Date", Format = "0", SizeHint = "ExitXX")]
public int TimeStopDays
{
get { return _timeStopDays; }
set { _timeStopDays = value; }
}
private decimal _openProfit = 0;
[ColumnInfoAttribute(Format="profit", DisplayName = "Open P&L", SizeHint = "OpenXX")]
public decimal OpenProfit
{
get { return _openProfit; }
set
{
_openProfit = value;
AddProfitToBars(TotalProfit);
}
}
private decimal _closedProfit = 0;
///
/// Gets an abbreviated summary of the entry strategy. Currently used to supply informative text in the
/// right click menus for initiating a trade in a symbol from the Strategy Trades window.
///
/// The trade data.
/// The instrument.
///
internal string GetSummary(RowData tradeData, Instrument instrument)
{
string toReturn = Name;
if (null != instrument)
{
toReturn += ": ";
if (TradeEntryType == TradeEntryType.Limit)
toReturn += "Limit $" + BrokerManager.GetLimitPrice(tradeData, this, instrument.MinMove.GetValueOrDefault(0.01));
else if (TradeEntryType == TradeEntryType.Market)
{
toReturn += "Market";
if (instrument.Last > 0)
toReturn += " ($" + instrument.Last + ")";
}
else if (TradeEntryType == TradeEntryType.Stop)
toReturn += "Stop $" + BrokerManager.GetEntryStopPrice(tradeData, this, instrument.MinMove.GetValueOrDefault(0.01));
else if (TradeEntryType == TradeEntryType.StopLimit)
toReturn += "Stop $" + BrokerManager.GetEntryStopPrice(tradeData, this, instrument.MinMove.GetValueOrDefault(0.01)) + " Limit $" + BrokerManager.GetLimitPrice(tradeData, this, instrument.MinMove.GetValueOrDefault(0.01));
}
return toReturn;
}
[ColumnInfoAttribute(Format = "profit", DisplayName = "Closed P&L", SizeHint = "ClosedXX")]
public decimal ClosedProfit
{
get { return _closedProfit; }
set
{
_closedProfit = value;
AddProfitToBars(TotalProfit);
}
}
[ColumnInfoAttribute(Format = "profit", DisplayName = "Total P&L", SizeHint = "ClosedXX")]
public decimal TotalProfit
{
get { return _closedProfit + _openProfit; }
}
private string _route = "SMART";
[ColumnInfoAttribute(DefaultVisible = false, DisplayName = "Selected Route", SizeHint = "SelectedXX")]
public string Route
{
get { return _route; }
set { _route = value; }
}
private string _contra = "SMART";
[ColumnInfoAttribute(DefaultVisible = false, DisplayName = "Contra", SizeHint = "SelectedXX")]
public string Contra
{
get { return _contra; }
set { _contra = value; }
}
private bool _useCustomRoute = false;
[Browsable(false)]
public bool UseCustomRoute
{
get { return _useCustomRoute; }
set { _useCustomRoute = value; }
}
private StreamingAlerts _mainAlerts;
[Browsable(false)]
public StreamingAlerts MainAlerts {
get { return _mainAlerts; }
set
{
_mainAlerts = value;
StartStreamingAlerts();
}
}
private StreamingAlerts _exitAlerts;
[Browsable(false)]
public StreamingAlerts ExitAlerts
{
get { return _exitAlerts; }
set { _exitAlerts = value; }
}
private IConnectionMaster _connectionMaster;
[Browsable(false)]
public IConnectionMaster ConnectionMaster
{
get { return _connectionMaster; }
set { _connectionMaster = value; }
}
private bool _queueOrders = false;
[Browsable(false)]
public bool QueueOrders
{
get { return _queueOrders; }
set { _queueOrders = value; }
}
private bool _useOrderThrottle = true;
[Browsable(false)]
public bool UseOrderThrottle
{
get { return _useOrderThrottle; }
set { _useOrderThrottle = value; }
}
private bool _useSentOrderThrottling = true;
///
/// Uses threshold of SENDING orders for order throttling rather than FILLING. True = sending, False = filling
/// This corresponds to the drop down on the Edit Trading Strategy form.
///
[Browsable(false)]
public bool UseSentOrderThrottling
{
get { return _useSentOrderThrottling; }
set { _useSentOrderThrottling = value; }
}
// entry order attributes
private TradePositionSizing _positionSizing = TradePositionSizing.FixedDollars;
[Browsable(false)]
public TradePositionSizing PositionSizing
{
get { return _positionSizing; }
set { _positionSizing = value; }
}
private decimal _positionSizeValue = GetXmlValue("POSITION_SIZE_VALUE", 10000.0);
[ColumnInfoAttribute(Format = "0", DisplayName = "Position Size", DefaultVisible = false, SizeHint = "PositionXX")]
public decimal PositionSizeValue
{
get { return _positionSizeValue; }
set { _positionSizeValue = value; }
}
private decimal _targetOffset;
[ColumnInfoAttribute(Format = "2", DisplayName = "Target Offset", DefaultVisible = false, SizeHint = "TargetXX")]
public decimal TargetOffset
{
get { return _targetOffset; }
set { _targetOffset = value; }
}
// Target Price - RVH20211227
private decimal _targetPrice;
[ColumnInfoAttribute(Format = "2", DisplayName = "Target Price", DefaultVisible = false, SizeHint = "TargetXX")]
public decimal TargetPrice
{
get { return _targetPrice; }
set { _targetPrice = value; }
}
private TradeStopLossType _targetType = TradeStopLossType.None;
[ColumnInfoAttribute(DisplayName = "Profit Target", DefaultVisible = false, SizeHint = "TargetXX")]
public TradeStopLossType TargetType
{
get { return _targetType; }
set { _targetType = value; }
}
private TradeStopLossType _stopLossType = TradeStopLossType.None;
[ColumnInfoAttribute(DisplayName = "Stop Loss Type", DefaultVisible = false, SizeHint = "TypeXX")]
public TradeStopLossType StopLossType
{
get { return _stopLossType; }
set { _stopLossType = value; }
}
[ColumnInfoAttribute(DisplayName = "Stop Loss", DefaultVisible = false, SizeHint = "StopXX")]
public string StopLossDisplay
{
get
{
if (_stopLossType == TradeStopLossType.Dollars)
return "$" + _stopLossOffset.ToString("N2");
else if (_stopLossType == TradeStopLossType.Percent)
return _stopLossOffset.ToString("N2") + "%";
else
return "";
}
}
private TradeTimeStopType _timeStopType = TradeTimeStopType.None;
///
/// Gets or sets the time stop type.
///
[ColumnInfoAttribute(DisplayName = "Time Stop Type", DefaultVisible = false, SizeHint = "StopXX")]
public TradeTimeStopType TimeStopType
{
get { return _timeStopType; }
set { _timeStopType = value; }
}
private TradeEntryLimitReference _tradeEntryLimitReference = TradeEntryLimitReference.Last;
[Browsable(false)]
public TradeEntryLimitReference TradeEntryLimitReference
{
get { return _tradeEntryLimitReference; }
set { _tradeEntryLimitReference = value; }
}
private int _timeStopMinutesAfterOpen = GetXmlValue("TIME_STOP_MINUTES_AFTER_OPEN", 375);
[ColumnInfoAttribute(DisplayName = "Time Stop Minutes After Open", DefaultVisible = false, SizeHint = "MinutesXX")]
public int TimeStopMinutesAfterOpen
{
get { return _timeStopMinutesAfterOpen; }
set { _timeStopMinutesAfterOpen = value; }
}
private int _timeStopDuration = GetXmlValue("TIME_STOP_DURATION", 60);
[ColumnInfoAttribute(DisplayName = "Time Stop Duration", DefaultVisible = false, SizeHint = "DurationXX")]
public int TimeStopDuration
{
get { return _timeStopDuration; }
set { _timeStopDuration = value; }
}
[ColumnInfoAttribute(DisplayName = "Time Stop", DefaultVisible = false, SizeHint = "MinutesXX", Format = "0")]
public int? TimeStopDisplay
{
get
{
if (_timeStopType == TradeTimeStopType.TimeAfterSignal)
return _timeStopDuration;
else if (_timeStopType == TradeTimeStopType.TimeOfDay)
return _timeStopMinutesAfterOpen;
else
return null;
}
}
private TradeStopLossType _trailingStopType = TradeStopLossType.None;
[ColumnInfoAttribute(Format = "textalignright", DisplayName = "Trailing Stop Type", DefaultVisible = false, SizeHint = "TrailingXX")]
public TradeStopLossType TrailingStopType
{
get { return _trailingStopType; }
set { _trailingStopType = value; }
}
private IBroker _broker = null;
[ColumnInfoAttribute(DisplayName = "Broker", DefaultVisible = false, SizeHint = "BrokerXX")]
[TypeConverter(typeof(BrokerConverter))]
public IBroker Broker
{
get { return _broker; }
set { _broker = value; }
}
private bool _usingProfiles = false;
///
/// Gets or sets a value indicating whether strategy uses allocation profiles. True = using profiles, False = using direct to account method.
///
///
/// true if [using profiles]; otherwise, false.
///
[ColumnInfoAttribute(DisplayName = "Using Profiles", DefaultVisible = false, SizeHint = "AccountXX")]
public bool UsingProfiles
{
get { return _usingProfiles; }
set { _usingProfiles = value; }
}
///
/// Gets or sets the allocation profile. Only in use when UsingProfiles is set to true.
///
///
/// The allocation profile.
///
[ColumnInfoAttribute(DisplayName = "Allocation Profile", DefaultVisible = false, SizeHint = "AccountXX")]
public string AllocationProfile { get; set; } = "";
///
/// The account name. Since the robot isn't connected to the broker when the layout is opened, we don't have
/// a list of available accounts at that time. This records the account name to be used to map to the actual
/// account once that list becomes available.
///
private string _accountName = "";
///
/// The account name. Priority is _account but if not available then _accountName.
///
[Browsable(false)]
public string AccountName => null == Account ? _accountName : Account.Name;
///
/// The account that this strategy should use when sending trades.
///
[ColumnInfoAttribute(DisplayName = "Account", DefaultVisible = false, SizeHint = "AccountXX")]
public Account Account { get; set; } = null;
[Browsable(false)]
public string LimitOrderFilter { get; set; } = "";
[Browsable(false)]
public string EntryStopOrderFilter { get; set; } = "";
[Browsable(false)]
public decimal LimitOrderOffset { get; set; } = GetXmlValue("LIMIT_ORDER_OFFSET", -0.02);
///
/// How should the LimitOrderOffset numeric value be interpreted. Dollars or percent.
///
[Browsable(false)]
public TradeEntryLimitOffsetType LimitOffsetType { get; set; } = TradeEntryLimitOffsetType.Dollars;
[Browsable(false)]
public double LimitRelativeOffset { get; set; }
private string _timeInForce = "DAY";
[Browsable(false)]
public string TimeInForce
{
get { return _timeInForce; }
set { _timeInForce = value; }
}
private int _limitGoodForSeconds = GetXmlValue("LIMIT_GOOD_FOR_SECONDS", 300);
[Browsable(false)]
public int LimitGoodForSeconds
{
get { return _limitGoodForSeconds; }
set { _limitGoodForSeconds = value; }
}
// stop loss attributes
private decimal _stopLossOffset;
[Browsable(false)]
public decimal StopLossOffset
{
get { return _stopLossOffset; }
set { _stopLossOffset = value; }
}
// Stop Loss Price - RVH20211227
private decimal _stopLossPrice;
[Browsable(false)]
public decimal StopLossPrice
{
get { return _stopLossPrice; }
set { _stopLossPrice = value; }
}
private string _stopLossFilter = "Price";
[Browsable(false)]
public string StopLossFilter
{
get { return _stopLossFilter; }
set { _stopLossFilter = value; }
}
// exit order attribute
private bool _useLastAsReference = true;
[Browsable(false)]
public bool UseLastAsReference
{
get { return _useLastAsReference; }
set { _useLastAsReference = value; }
}
// time stop attributes
// trailing stop attributes
private decimal _trailingStopOffset;
[Browsable(false)]
public decimal TrailingStopOffset
{
get { return _trailingStopOffset; }
set { _trailingStopOffset = value; }
}
private string _trailingStopFilter = "Price";
[Browsable(false)]
public string TrailingStopFilter
{
get { return _trailingStopFilter; }
set { _trailingStopFilter = value; }
}
// target attributes
private string _targetFilter = "Price";
[Browsable(false)]
public string TargetFilter
{
get { return _targetFilter; }
set { _targetFilter = value; }
}
private TradeStopLossType _scaleType = TradeStopLossType.None;
[Browsable(false)]
public TradeStopLossType ScaleType
{
get { return _scaleType; }
set { _scaleType = value; }
}
private double _scaleStart;
[Browsable(false)]
public double ScaleStart
{
get { return _scaleStart; }
set { _scaleStart = value; }
}
private string _scaleFilter = "Price";
[Browsable(false)]
public string ScaleFilter
{
get { return _scaleFilter; }
set { _scaleFilter = value; }
}
private bool _outsideRth;
///
/// Used to specify whether an order should be filled Outside Regular Trading Hours.
///
[Browsable(false)]
public bool OutsideRth
{
get => _outsideRth;
set => _outsideRth = value;
}
// TODO: exit alert
private Dictionary _sentOrders = new Dictionary();
///
/// Keeps track of the number of orders sent per symbol for this strategy.
///
[Browsable(false)]
public Dictionary SentOrders
{
get { return _sentOrders; }
set { _sentOrders = value; }
}
private HashSet _ordersBeingSent = new HashSet();
///
/// Keeps track of orders currently being sent. In the event that two alerts come in very close together
/// this list can be checked to make sure an order isn't currently in progress. An instrument is added to
/// this list at the start of and removed after orders are placed and the instrument is
/// added to SentOrders.
///
[Browsable(false)]
public HashSet OrdersBeingSent
{
get { return _ordersBeingSent; }
set { _ordersBeingSent = value; }
}
private HashSet _ordersSentTowardsMax = new HashSet();
///
/// Keeps track of orders sent. This is used to track if the MaxDailyOrder set in the strategy has been
/// met. This list is checked in to determine if an order can be submitted.
/// SentEntryOrders is not good to use for this purpose since some broker interfaces don't add to that list
/// until broker callbacks are done using async processing.
///
[Browsable(false)]
public HashSet OrdersSentTowardsMax
{
get { return _ordersSentTowardsMax; }
set { _ordersSentTowardsMax = value; }
}
private HashSet _entryOrders = new HashSet();
///
/// Set of all entry orders submitted for this strategy.
///
[Browsable(false)]
public HashSet EntryOrders
{
get { return _entryOrders; }
set { _entryOrders = value; }
}
private HashSet _allOrders = new HashSet();
///
/// All orders submitted through this strategy. See also EntryOrders which is a subset of this.
///
[Browsable(false)]
public HashSet AllOrders
{
get { return _allOrders; }
set { _allOrders = value; }
}
///
/// Positions associated with this trading strategy.
///
private Dictionary _positions = new Dictionary();
public void UpdatePosition(Position position)
{
if (_positions.ContainsKey(position.Instrument))
_positions[position.Instrument] = position;
else
_positions.Add(position.Instrument, position);
}
private List _signals = new List();
[Browsable(false)]
public List Signals
{
get { return _signals; }
set { _signals = value; }
}
///
/// Keeps track of signals received by this strategy by symbol name
///
private Dictionary> _signalsBySymbol = new Dictionary>();
private bool _oneDailyChancePerSymbol = true;
[Browsable(false)]
public bool OneDailyChancePerSymbol
{
get { return _oneDailyChancePerSymbol; }
set { _oneDailyChancePerSymbol = value; }
}
private bool _allowDuplicateOrders = false;
[Browsable(false)]
public bool AllowDuplicateOrders
{
get { return _allowDuplicateOrders; }
set { _allowDuplicateOrders = value; }
}
private int _customDisplaySize = 0;
[Browsable(false)]
public int CustomDisplaySize
{
get { return _customDisplaySize; }
set { _customDisplaySize = value; }
}
private string _tags = "";
[Browsable(false)]
public string Tags
{
get { return _tags; }
set { _tags = value; }
}
private string _destination = "";
[Browsable(false)]
public string Destination
{
get { return _destination; }
set { _destination = value; }
}
///
/// Set of all entry orders submitted for this strategy.
///
[ColumnInfoAttribute(DisplayName = "Sent Entry Orders", Format = "0", SizeHint = "OrdersXX", DefaultVisible = false)]
public int SentEntryOrders
{
get { return _entryOrders.Count; }
}
///
/// Keeps track of orders sent. This is used to track if the MaxDailyOrder set in the strategy has been
/// met. This list count is checked in to determine if an order can be submitted.
/// SentEntryOrders is not good to use for this purpose since some broker interfaces don't add to that list
/// until broker callbacks are done using async processing.
///
[ColumnInfoAttribute(DisplayName = "Sent Orders Towards Max", Format = "0", SizeHint = "OrdersXX", DefaultVisible = false)]
public int SentOrdersTowardsMax
{
get { return _ordersSentTowardsMax.Count; }
}
///
/// Set of all entry orders filled for this strategy.
///
[ColumnInfoAttribute(DisplayName = "Filled Entry Orders", Format = "0", SizeHint = "OrdersXX", DefaultVisible = false)]
public int FilledEntryOrders
{
get
{
var filledOrders = _entryOrders.ToList().Where(x => x.Status == TradeOrderStatus.Filled || x.Status == TradeOrderStatus.PartiallyFilled);
return filledOrders.Count();
}
}
[Browsable(false)]
public List ProfitBars = new List();
//[ColumnInfoAttribute(DisplayName = "Simulated Performance", Format = "thermograph", SizeHint = "XXXXXXXXXXXXXXXXXXXXXXX", DefaultVisible = true)]
[Browsable(false)]
public Thermograph OddsMakerThermograph { get; set; }
private void AddProfitToBars(decimal profit)
{
double profitAsDouble = (double)profit;
if (ProfitBars.Count == 0 || ProfitBars.Last().Date.ToString("yyyy-MM-dd hh:mm") != ServerFormats.Now.ToString("yyyy-MM-dd hh:mm"))
{
ProfitBars.Add(new Bar(ServerFormats.Now, profitAsDouble, profitAsDouble, profitAsDouble, profitAsDouble));
return;
}
Bar lastBar = ProfitBars.Last();
if (lastBar.High < profitAsDouble)
lastBar.High = profitAsDouble;
if (lastBar.Low > profitAsDouble)
lastBar.Low = profitAsDouble;
lastBar.Close = profitAsDouble;
}
private Dictionary _balancesOpen = new Dictionary();
private Dictionary _balancesClosed = new Dictionary();
private Dictionary _holdings = new Dictionary();
private Dictionary _columnKeys = new Dictionary();
public event NewAlertHandler onNewAlertReceived;
public TradingStrategy(IConnectionMaster connectionMaster)
{
_connectionMaster = connectionMaster;
}
///
/// Tells the strategy about an execution associated with it for reporting purposes.
///
///
public void AddExecution(TradeExecution execution)
{
Instrument instrument = execution.Instrument;
decimal shares = execution.IsBuy ? execution.Shares : -Math.Abs(execution.Shares);
decimal price = (decimal)execution.Price;
decimal balance = -shares * price;
if (_holdings.ContainsKey(instrument))
{
decimal priorShares = _holdings[instrument];
decimal priorBalance = _balancesOpen[instrument];
_balancesOpen[instrument] += balance;
_holdings[instrument] += shares;
if ((priorShares < 0 && _holdings[instrument] > priorShares) || (priorShares > 0 && _holdings[instrument] < priorShares))
{
decimal averageCostPerShare = priorBalance / priorShares;
_balancesClosed[instrument] += balance - shares * averageCostPerShare;
}
if (_holdings[instrument] == 0)
_balancesOpen[instrument] = 0;
}
else
{
_balancesOpen.Add(instrument, -shares * price);
_balancesClosed.Add(instrument, 0);
_holdings.Add(instrument, shares);
}
UpdateProfit();
}
public static string MakeConfigStringEdits(string configString)
{
configString = ModifyConfigString(configString, "Price");
configString = ModifyConfigString(configString, "SmartStopD");
return configString;
}
///
/// By default ensures that price is displayed as a column in the strategy. Use the filter parameter to specify another filter.
///
///
///
///
public static string ModifyConfigString(string configString, string filter = "Price")
{
bool filterFound = false;
int maxShowNumber = -1;
try
{
NameValueCollection parameters = HttpUtility.ParseQueryString(configString);
foreach (string key in parameters.AllKeys)
{
if (key.StartsWith("show") && parameters[key] != "")
{
int showNumber = int.Parse(key.Replace("show", ""));
if (showNumber > maxShowNumber)
maxShowNumber = showNumber;
if (parameters[key] == filter)
filterFound = true;
}
}
}
catch { }
maxShowNumber++;
if (!filterFound)
configString += "&show" + maxShowNumber + "=" + filter;
return configString;
}
public static bool ColumnIsShown(string configString, string filter)
{
NameValueCollection parameters = HttpUtility.ParseQueryString(configString);
bool filterFound = false;
foreach (string key in parameters.AllKeys)
{
if (key.StartsWith("show") && parameters[key] != "")
{
if (parameters[key] == filter)
filterFound = true;
}
}
return filterFound;
}
public void UpdateProfit()
{
_openProfit = 0;
_closedProfit = 0;
foreach (Instrument instrument in _balancesClosed.Keys.ToList())
{
decimal currentShares = _holdings[instrument];
decimal openBalance = _balancesOpen[instrument];
decimal currentWorth = instrument.Last * currentShares;
if (instrument.Last == 0)
currentWorth = -openBalance;
if (currentShares != 0)
_openProfit += currentWorth + openBalance;
_closedProfit += _balancesClosed[instrument];
}
}
///
/// Controls whether initial history for today's trades has been retrieved.
///
private bool _historyComplete = false;
private bool _historyRunning = false;
private List _historyQueue = new List();
private History _history = null;
///
/// Alert Streaming is only started if users are authorized.
///
private void StartStreamingAlerts()
{
// Allow streaming alerts only for premium and above user subscriptions.
if (!(GuiEnvironment.Authorizations.ContainsKey("oddsmaker") && GuiEnvironment.Authorizations["oddsmaker"]))
return;
if ( null != _mainAlerts && _mainAlerts.Config != "")
{
DateTime now = GuiEnvironment.GetNowInMarketTimeZone();
DateTime marketOpen = GuiEnvironment.GetMarketOpenInMarketTimeZone();
if (!_historyComplete && now > marketOpen)
LoadHistory();
else
{
_mainAlerts.Stop();
_mainAlerts.StreamingAlertsData -= _mainAlerts_StreamingAlertsData;
_mainAlerts.StreamingAlertsConfig -= _mainAlerts_StreamingAlertsConfig;
_mainAlerts.StreamingAlertsData += new StreamingAlertsData(_mainAlerts_StreamingAlertsData);
_mainAlerts.StreamingAlertsConfig += new StreamingAlertsConfig(_mainAlerts_StreamingAlertsConfig);
_mainAlerts.Start();
StreamingAlertsStarted = true;
}
}
}
[Browsable(false)]
public bool StreamingAlertsStarted { get; set; }
private bool _chartLongStrategy = false;
///
/// Default strategy for long trades on a chart
///
[ColumnInfoAttribute(Format = "onoff", DisplayName = "Default Chart Long Strategy", SizeHint = "FALSEXX")]
public bool ChartLongStrategy { get { return _chartLongStrategy; } set { _chartLongStrategy = value; } }
private bool _chartShortStrategy = false;
///
/// Default strategy for short trades on a chart
///
[ColumnInfoAttribute(Format = "onoff", DisplayName = "Default Chart Short Strategy", SizeHint = "FALSEXX")]
public bool ChartShortStrategy { get { return _chartShortStrategy; } set { _chartShortStrategy = value; } }
private void LoadHistory()
{
HistoryRequest historyRequest = new HistoryRequest();
historyRequest.Config = _mainAlerts.Config;
historyRequest.EndTime = GuiEnvironment.GetMarketOpenLocalTime();
historyRequest.MaxCount = 100;
historyRequest.StartTime = null;
_history = _connectionMaster.HistoryManager.GetHistory(historyRequest);
_history.HistoryData += _history_HistoryData;
_history.HistoryStatus += _history_HistoryStatus;
_historyQueue.Clear();
_historyRunning = true;
GuiEnvironment.LogMessage("[TradingStrategy] Starting history request for strategy " + _name);
_history.Start();
}
void _history_HistoryStatus(History sender)
{
_historyRunning = sender.HistoryDisposition != HistoryDisposition.Done && sender.HistoryDisposition != HistoryDisposition.NotSent;
// we used to respond to the MoreAvailable even to keep going, but that seems like a bad idea. We've seen people try to load
// a very chatty alert window and the program crashes. I say 1000 historical events is enough.
if (sender.HistoryDisposition == HistoryDisposition.MoreAvailable)
{
_history.Stop();
LoadHistoryQueue();
}
if (sender.HistoryDisposition == HistoryDisposition.Done)
LoadHistoryQueue();
}
private void LoadHistoryQueue()
{
try
{
int queuedAlerts = _historyQueue.Count;
GuiEnvironment.LogMessage("[TradingStrategy] Starting processing " + queuedAlerts + " alerts from strategy " + _name);
List positions = new List();
int todaysTrades = 0;
foreach (RowData alert in _historyQueue.ToList())
{
string symbol = alert.GetSymbol();
TradeDecision decision = BrokerManager.ShouldTakeTrade(alert, this, positions, true);
if (decision.TakeTrade)
{
if (!_signalsBySymbol.ContainsKey(symbol))
_signalsBySymbol.Add(symbol, new HashSet());
HashSet signals = _signalsBySymbol[symbol];
Signal signal = GetSignal(alert);
signals.Add(signal);
LastSymbol = alert.GetSymbol();
LastSignalTime = alert.GetTime();
LastSignalPrice = (decimal)alert.GetPrice();
todaysTrades++;
}
}
_historyQueue.Clear();
_historyComplete = true;
GuiEnvironment.LogMessage("[TradingStrategy] Done processing " + todaysTrades + " trades out of " + queuedAlerts + " alerts from strategy " + _name);
StartStreamingAlerts();
}
catch (Exception e)
{
string debugView = e.StackTrace;
}
}
public bool SignaledToday(string symbol)
{
return _signalsBySymbol.ContainsKey(symbol);
}
private Signal GetSignal(RowData rowData)
{
Signal signal = new TIProAutoTradeExtension.Signal();
signal.Symbol = rowData.GetSymbol();
signal.Strategy = this;
signal.RowData = rowData;
return signal;
}
void _history_HistoryData(List alerts, History sender)
{
foreach (RowData rowData in alerts)
{
_historyQueue.Insert(0, rowData);
}
}
void _mainAlerts_StreamingAlertsConfig(StreamingAlerts source)
{
string debugView = source.Columns.ToString();
_columnKeys.Clear();
foreach (ColumnInfo column in source.Columns.ToList())
{
string rowDataKey = column.WireName;
string internalCode = column.InternalCode;
if (!_columnKeys.ContainsKey(internalCode))
_columnKeys.Add(internalCode, rowDataKey);
}
}
public string GetRowDataKey(string internalCode)
{
if (_columnKeys.ContainsKey(internalCode))
return _columnKeys[internalCode];
else
{
if (internalCode.StartsWith("c_"))
return internalCode;
else
return "c_" + internalCode;
}
}
///
/// Constructor using xml generated by TradingStrategy.Save().
///
/// Connection master reference
/// Xml
public TradingStrategy(IConnectionMaster connectionMaster, XmlNode xml)
{
StreamingAlertsStarted = false;
_connectionMaster = connectionMaster;
_id = xml.Property("ID", "");
if (_id == "")
_id = GenerateId();
_name = xml.Property("NAME", "");
_long = xml.Property("LONG", true);
_cfdEnabled = xml.Property("CFD_ENABLED", false);
// MAIN_ALERTS and EXIT_ALERTS are premium and above features.
// As of this change the oddsmaker authorization is equivalent to the premium plan.
// We will initialize _mainAlerts if authorization is not complete. Once authorization is complete we apply autorization logic.
if (GuiEnvironment.Authorizations.Count == 0 || (GuiEnvironment.Authorizations.ContainsKey("oddsmaker") && GuiEnvironment.Authorizations["oddsmaker"]))
{
string mainAlertsConfig = xml.Property("MAIN_ALERTS", "");
if (mainAlertsConfig != "")
{
mainAlertsConfig = MakeConfigStringEdits(mainAlertsConfig);
_mainAlerts = _connectionMaster.StreamingAlertsManager.GetAlerts(mainAlertsConfig);
StartStreamingAlerts();
}
string exitAlertsConfig = xml.Property("EXIT_ALERTS", "");
if (exitAlertsConfig != "")
{
_exitAlerts = _connectionMaster.StreamingAlertsManager.GetAlerts(exitAlertsConfig);
_exitAlerts.StreamingAlertsData += new StreamingAlertsData(_exitAlerts_StreamingAlertsData);
_exitAlerts.Start();
}
}
_usingProfiles = xml.Property("USING_PROFILES", false);
AllocationProfile = xml.Property("ALLOCATION_PROFILE", "");
_accountName = xml.Property("ACCOUNT_NAME", "");
// Rename Simulated Trading Default to TI Paper Trading
if (_accountName == "Simulated Trading Default")
_accountName = "TI Paper Trading";
_queueOrders = xml.Property("QUEUE_ORDERS", false);
if (xml.Property("START_TIME_MINUTES_AFTER_OPEN").Count() > 0)
{
_startTimeMinutesFromOpen = xml.Property("START_TIME_MINUTES_AFTER_OPEN", GetXmlValue("START_TIME_MINUTES_AFTER_OPEN", 5));
StartTime = GuiEnvironment.GetMarketOpenLocalTime().AddMinutes(_startTimeMinutesFromOpen);
}
else if (xml.Property("START_TIME").Count() > 0)
{
DateTime startTime;
DateTime.TryParse(xml.Property("START_TIME", GuiEnvironment.GetMarketOpenLocalTime().ToString()), out startTime);
int minutesFromOpen = (int)(startTime - GuiEnvironment.GetMarketOpenLocalTime()).TotalMinutes;
int minutesFromClose = (int)(startTime - GuiEnvironment.GetMarketCloseLocalTime()).TotalMinutes;
if (minutesFromOpen < 0)
StartTime = GuiEnvironment.GetMarketOpenLocalTime();
else if (minutesFromClose > 0)
StartTime = GuiEnvironment.GetMarketCloseLocalTime();
else
StartTime = startTime;
}
else
StartTime = GuiEnvironment.GetMarketOpenLocalTime();
if (xml.Property("END_TIME_MINUTES_BEFORE_CLOSE").Count() > 0)
{
_endTimeMinutesFromClose = xml.Property("END_TIME_MINUTES_BEFORE_CLOSE", GetXmlValue("END_TIME_MINUTES_BEFORE_CLOSE", 15));
EndTime = GuiEnvironment.GetMarketCloseLocalTime().AddMinutes(-_endTimeMinutesFromClose);
}
else if (xml.Property("END_TIME").Count() > 0)
{
DateTime endTime;
DateTime.TryParse(xml.Property("END_TIME", GuiEnvironment.GetMarketCloseLocalTime().ToString()), out endTime);
int minutesFromOpen = (int)(endTime - GuiEnvironment.GetMarketOpenLocalTime()).TotalMinutes;
int minutesFromClose = (int)(endTime - GuiEnvironment.GetMarketCloseLocalTime()).TotalMinutes;
if (minutesFromOpen < 0)
EndTime = GuiEnvironment.GetMarketOpenLocalTime();
else if (minutesFromClose > 0)
EndTime = GuiEnvironment.GetMarketCloseLocalTime();
else
EndTime = endTime;
}
else
EndTime = GuiEnvironment.GetMarketCloseLocalTime();
_maxDailyOrders = xml.Property("MAX_DAILY_ORDERS", GetXmlValue("MAX_DAILY_ORDERS", 10));
_chartLongStrategy = xml.Property("CHART_LONG_DEFAULT", false);
_chartShortStrategy = xml.Property("CHART_SHORT_DEFAULT", false);
_useOrderThrottle = xml.Property("USE_ORDER_THROTTLE", true);
if (_useOrderThrottle)
_useSentOrderThrottling = xml.Property("USE_SENT_ORDER_THROTTLE", true);
else
_useSentOrderThrottling = true;
_useCustomRoute = xml.Property("USE_CUSTOM_ROUTE", false);
_route = xml.Property("ROUTE", "SMART");
_contra = xml.Property("CONTRA", "SMART");
_positionSizing = xml.PropertyEnum("TRADE_POSITION_SIZING", TradePositionSizing.FixedShares);
_positionSizeValue = (decimal)xml.Property("POSITION_SIZE_VALUE", GetXmlValue("POSITION_SIZE_VALUE", 10000));
_tradeEntryType = xml.PropertyEnum("TRADE_ENTRY_TYPE", TradeEntryType.Market);
_tradeEntryLimitReference = xml.PropertyEnum("LIMIT_REFERENCE", TradeEntryLimitReference.Last);
LimitOrderOffset = (decimal)xml.Property("LIMIT_ORDER_OFFSET", (double)GetXmlValue("LIMIT_ORDER_OFFSET", -0.02));
LimitOffsetType = xml.PropertyEnum("LIMIT_OFFSET_TYPE", TradeEntryLimitOffsetType.Dollars);
LimitRelativeOffset = xml.Property("LIMIT_RELATIVE_OFFSET", 0.00);
LimitOrderFilter = xml.Property("LIMIT_ORDER_FILTER");
EntryStopOrderFilter = xml.Property("ENTRY_STOP_ORDER_FILTER");
_timeInForce = xml.Property("TIME_IN_FORCE", "DAY");
_limitGoodForSeconds = xml.Property("LIMIT_GOOD_FOR_SECONDS", GetXmlValue("LIMIT_GOOD_FOR_SECONDS", 120));
_stopLossType = xml.PropertyEnum("TRADE_STOP_LOSS_TYPE", TradeStopLossType.None);
_stopLossOffset = (decimal)xml.Property("STOP_LOSS_OFFSET", 0.00);
_stopLossFilter = xml.Property("STOP_LOSS_FILTER", "");
_useLastAsReference = xml.Property("USE_LAST_AS_REFERENCE", true);
_timeStopType = xml.PropertyEnum("TRADE_TIME_STOP_TYPE", TradeTimeStopType.None);
_timeStopMinutesAfterOpen = xml.Property("TIME_STOP_MINUTES_AFTER_OPEN", GetXmlValue("TIME_STOP_MINUTES_AFTER_OPEN", 375));
_timeStopDuration = xml.Property("TIME_STOP_DURATION", GetXmlValue("TIME_STOP_DURATION", 60));
_timeStopDays = xml.Property("TIME_STOP_DAYS", 0);
_trailingStopType = xml.PropertyEnum("TRADE_TRAILING_STOP_TYPE", TradeStopLossType.None);
// StopLoss and TrailingStop are mutually exclusive, with StopLoss as the preferred approach.
if (_stopLossType != TradeStopLossType.None)
_trailingStopType = TradeStopLossType.None;
_trailingStopOffset = (decimal)xml.Property("TRAILING_STOP_OFFSET", 0.00);
_trailingStopFilter = xml.Property("TRAILING_STOP_FILTER", "");
_targetType = xml.PropertyEnum("TRADE_TARGET_TYPE", TradeStopLossType.None);
_targetOffset = (decimal)xml.Property("TARGET_OFFSET", 0.00);
_targetFilter = xml.Property("TARGET_FILTER", "");
_scaleType = xml.PropertyEnum("SCALE_TYPE", TradeStopLossType.None);
_scaleStart = xml.Property("SCALE_START", 0);
_scaleFilter = xml.Property("SCALE_FILTER", "");
_oneDailyChancePerSymbol = xml.Property("ONE_DAILY_CHANCE_PER_SYMBOL", true);
_allowDuplicateOrders = xml.Property("ALLOW_DUPLICATE_ORDERS", false);
_customDisplaySize = xml.Property("CUSTOM_DISPLAY_SIZE", 0);
_tags = xml.Property("TAGS", "");
_destination = xml.Property("DESTINATION", "");
}
public TradingStrategy(TradingStrategy strategy)
{
_connectionMaster = strategy.ConnectionMaster;
_id = GenerateId();
_name = strategy.Name;
_cfdEnabled = strategy.CfdEnabled;
_useLastAsReference = strategy.UseLastAsReference;
_long = strategy.Long;
_mainAlerts = strategy.MainAlerts;
_exitAlerts = strategy.ExitAlerts;
_queueOrders = strategy.QueueOrders;
StartTime = strategy.StartTime;
EndTime = strategy.EndTime;
_useCustomRoute = strategy.UseCustomRoute;
_route = strategy.Route;
_contra = strategy.Contra;
_maxDailyOrders = strategy.MaxDailyOrders;
_useOrderThrottle = strategy.UseOrderThrottle;
_useSentOrderThrottling = strategy.UseSentOrderThrottling;
_positionSizing = strategy.PositionSizing;
_positionSizeValue = strategy.PositionSizeValue;
_tradeEntryType = strategy.TradeEntryType;
_tradeEntryLimitReference = strategy.TradeEntryLimitReference;
LimitOrderFilter = strategy.LimitOrderFilter;
EntryStopOrderFilter = strategy.EntryStopOrderFilter;
LimitOrderOffset = strategy.LimitOrderOffset;
LimitOffsetType = strategy.LimitOffsetType;
LimitRelativeOffset = strategy.LimitRelativeOffset;
_limitGoodForSeconds = strategy.LimitGoodForSeconds;
_stopLossType = strategy.StopLossType;
_stopLossOffset = strategy.StopLossOffset;
_stopLossFilter = strategy.StopLossFilter;
_timeStopType = strategy.TimeStopType;
_timeStopMinutesAfterOpen = strategy.TimeStopMinutesAfterOpen;
_timeStopDuration = strategy.TimeStopDuration;
_timeStopDays = strategy.TimeStopDays;
_trailingStopType = strategy.TrailingStopType;
// StopLoss and TrailingStop are mutually exclusive, with StopLoss as the preferred approach.
if (_stopLossType != TradeStopLossType.None)
_trailingStopType = TradeStopLossType.None;
_trailingStopOffset = strategy.TrailingStopOffset;
_trailingStopFilter = strategy.TrailingStopFilter;
_targetType = strategy.TargetType;
_targetOffset = strategy.TargetOffset;
_targetFilter = strategy.TargetFilter;
_scaleType = strategy.ScaleType;
_scaleStart = strategy.ScaleStart;
_scaleFilter = strategy.ScaleFilter;
_oneDailyChancePerSymbol = strategy.OneDailyChancePerSymbol;
_allowDuplicateOrders = strategy.AllowDuplicateOrders;
_customDisplaySize = strategy.CustomDisplaySize;
Account = strategy.Account;
AllocationProfile = strategy.AllocationProfile;
_usingProfiles = strategy.UsingProfiles;
_tags = strategy.Tags;
_destination = strategy.Destination;
}
///
/// Return unique string to uniquely identify trading strategies.
///
///
public static string GenerateId()
{
return Guid.NewGuid().ToString();
}
public override string ToString()
{
return _name;
}
public RowData ToRowData()
{
if (Id == "1" || Id == "-1")
{
RowData rowData = new RowData();
rowData.Data.Add("Name", _name);
rowData.Data.Add("Long", _long);
rowData.Data.Add("OpenProfit", _openProfit);
rowData.Data.Add("ClosedProfit", _closedProfit);
rowData.Data.Add("TotalProfit", TotalProfit);
rowData.Data.Add("TradesSoFarToday", _tradesSoFarToday);
rowData.Data.Add("TradingEnabled", true);
rowData.Data.Add("AdvancedExitsEnabled", this);
rowData.Data.Add("object", this);
return rowData;
}
else
{
RowData rowData = RowDataHelper.GetRowDataFromObject(this);
return rowData;
}
}
public XmlNode Save(XmlNode parent)
{
XmlNode description = parent.NewNode("TRADING_STRATEGY");
try
{
description.SetProperty("NAME", _name);
description.SetProperty("LONG", _long);
description.SetProperty("CFD_ENABLED", _cfdEnabled);
// MAIN_ALERTS and EXIT_ALERTS are premium and above features.
// As of this change the oddsmaker authorization is equivalent to the premium plan.
if (GuiEnvironment.Authorizations.ContainsKey("oddsmaker") && GuiEnvironment.Authorizations["oddsmaker"])
{
if (null != _mainAlerts)
description.SetProperty("MAIN_ALERTS", _mainAlerts.Config);
if (null != _exitAlerts)
description.SetProperty("EXIT_ALERTS", _exitAlerts.Config);
}
description.SetProperty("QUEUE_ORDERS", _queueOrders);
description.SetProperty("ID", _id);
description.SetProperty("START_TIME_MINUTES_AFTER_OPEN", _startTimeMinutesFromOpen);
description.SetProperty("END_TIME_MINUTES_BEFORE_CLOSE", _endTimeMinutesFromClose);
description.SetProperty("USE_CUSTOM_ROUTE", _useCustomRoute);
description.SetProperty("ROUTE", _route);
description.SetProperty("CONTRA", _contra);
description.SetProperty("MAX_DAILY_ORDERS", _maxDailyOrders);
description.SetProperty("USE_ORDER_THROTTLE", _useOrderThrottle);
description.SetProperty("CHART_LONG_DEFAULT", _chartLongStrategy);
description.SetProperty("CHART_SHORT_DEFAULT", _chartShortStrategy);
if (_useOrderThrottle)
description.SetProperty("USE_SENT_ORDER_THROTTLE", _useSentOrderThrottling);
else
description.SetProperty("USE_SENT_ORDER_THROTTLE", true);
description.SetProperty("TRADE_POSITION_SIZING", _positionSizing);
description.SetProperty("POSITION_SIZE_VALUE", _positionSizeValue);
description.SetProperty("TRADE_ENTRY_TYPE", _tradeEntryType);
description.SetProperty("LIMIT_REFERENCE", _tradeEntryLimitReference);
description.SetProperty("LIMIT_ORDER_OFFSET", LimitOrderOffset);
description.SetProperty("LIMIT_OFFSET_TYPE", LimitOffsetType);
description.SetProperty("LIMIT_ORDER_FILTER", LimitOrderFilter);
description.SetProperty("ENTRY_STOP_ORDER_FILTER", EntryStopOrderFilter);
description.SetProperty("LIMIT_RELATIVE_OFFSET", LimitRelativeOffset);
description.SetProperty("TIME_IN_FORCE", _timeInForce);
description.SetProperty("LIMIT_GOOD_FOR_SECONDS", _limitGoodForSeconds);
description.SetProperty("TRADE_STOP_LOSS_TYPE", _stopLossType);
description.SetProperty("STOP_LOSS_OFFSET", _stopLossOffset);
description.SetProperty("STOP_LOSS_FILTER", _stopLossFilter);
description.SetProperty("USE_LAST_AS_REFERENCE", _useLastAsReference);
description.SetProperty("TRADE_TIME_STOP_TYPE", _timeStopType);
description.SetProperty("TIME_STOP_MINUTES_AFTER_OPEN", _timeStopMinutesAfterOpen);
description.SetProperty("TIME_STOP_DURATION", _timeStopDuration);
description.SetProperty("TIME_STOP_DAYS", _timeStopDays);
description.SetProperty("TRADE_TRAILING_STOP_TYPE", _trailingStopType);
description.SetProperty("TRAILING_STOP_OFFSET", _trailingStopOffset);
description.SetProperty("TRAILING_STOP_FILTER", _trailingStopFilter);
description.SetProperty("TRADE_TARGET_TYPE", _targetType);
description.SetProperty("TARGET_OFFSET", _targetOffset);
description.SetProperty("TARGET_FILTER", _targetFilter);
description.SetProperty("SCALE_TYPE", _scaleType);
description.SetProperty("SCALE_START", _scaleStart);
description.SetProperty("SCALE_FILTER", _scaleFilter);
description.SetProperty("ONE_DAILY_CHANCE_PER_SYMBOL", _oneDailyChancePerSymbol);
description.SetProperty("ALLOW_DUPLICATE_ORDERS", _allowDuplicateOrders);
description.SetProperty("CUSTOM_DISPLAY_SIZE", _customDisplaySize);
description.SetProperty("TAGS", _tags);
description.SetProperty("USING_PROFILES", _usingProfiles);
description.SetProperty("ALLOCATION_PROFILE", AllocationProfile);
if (null != Account)
{
// Only saved the old name for backwards conpatibility.
var accountName = (Account.Name == "TI Paper Trading") ? "Simulated Trading Default" : Account.Name;
description.SetProperty("ACCOUNT_NAME", accountName);
}
else if (!String.IsNullOrEmpty(_accountName))
description.SetProperty("ACCOUNT_NAME", _accountName);
description.SetProperty("DESTINATION", _destination);
}
catch (Exception e)
{
string debugView = e.ToString();
}
return description;
}
void _exitAlerts_StreamingAlertsData(List data, StreamingAlerts source)
{
// TODO
}
private HashSet _symbolsToday = new HashSet();
void _mainAlerts_StreamingAlertsData(List data, StreamingAlerts source)
{
ProcessAlerts(data);
}
///
/// Processes RowData list.
///
/// List of RowData
private void ProcessAlerts(List data)
{
foreach (RowData rowData in data)
{
ProcessAlert(rowData);
}
}
///
/// Processes alert from RowData instance. This can come from an alert strategy or generated manually as
/// the "Trade This" menu items do.
///
///
public void ProcessAlert(RowData rowData)
{
if (rowData.GetAsString(CommonAlertFields.HISTORICAL) != "1")
{
if (rowData.GetSymbol() != "")
{
string symbol = rowData.GetSymbol();
if (!_symbolsToday.Contains(symbol) || !OneDailyChancePerSymbol)
{
LastSymbol = symbol;
double price = rowData.GetPrice(0.0);
if (price != 0.0)
LastSignalPrice = (decimal)price;
if (rowData.GetTime().HasValue)
LastSignalTime = rowData.GetTime().Value;
else
LastSignalTime = ServerFormats.Now;
}
if (!_symbolsToday.Contains(symbol))
_symbolsToday.Add(symbol);
}
if (null != onNewAlertReceived)
onNewAlertReceived(rowData, this);
}
}
private OddsMakerRequest GetOddsMakerRequest()
{
OddsMakerRequest request = new OddsMakerRequest();
request.SuccessDirectionUp = _long;
request.DaysOfTest = GuiEnvironment.GetGeneralInfo(GuiEnvironment.FindConnectionMaster("").SendManager).GetOddsMakerDays();
request.SkipDays = 0;
if (null != _mainAlerts)
request.EntryCondition = _mainAlerts.Config;
else
return null;
request.EntryTimeStart = (int)(_startTime - GuiEnvironment.GetMarketOpenLocalTime()).TotalMinutes;
request.EntryTimeEnd = -(int)(_endTime - GuiEnvironment.GetMarketCloseLocalTime()).TotalMinutes;
request.Location = SelectedLocation.US;
request.SuccessTypePercent = _stopLossType == TradeStopLossType.Percent || _targetType == TradeStopLossType.Percent;
request.SuccessMinMove = 0.01;
if (_targetType == TradeStopLossType.Dollars || _targetType == TradeStopLossType.Percent)
request.ProfitTarget = (double)_targetOffset;
else if (_targetType == TradeStopLossType.Filter && _targetFilter != "")
{
request.UseTargetFilter = true;
request.TargetFilter = _targetFilter;
}
if (_stopLossType == TradeStopLossType.Dollars || _stopLossType == TradeStopLossType.Percent)
request.StopLoss = (double)_stopLossOffset;
else if (_stopLossType == TradeStopLossType.Filter && _stopLossFilter != "")
{
request.UseStopFilter = true;
request.StopFilter = _stopLossFilter;
}
request.StopLossWiggle = false;
if (_timeStopType == TradeTimeStopType.TimeAfterSignal)
{
request.TimeoutType = TimeoutType.MinutesAfterEntry;
request.TimeoutMinutes = _timeStopDuration;
}
else if (_timeStopType == TradeTimeStopType.TimeOfDay)
{
if (_timeStopMinutesAfterOpen == 0 && _timeStopDays > 0)
{
request.TimeoutType = TimeoutType.Open;
request.AtOpenDays = _timeStopDays;
}
else if (_timeStopDays > 0 && _timeStopMinutesAfterOpen >= 385)
{
request.TimeoutType = TimeoutType.FutureClose;
request.AtCloseDays = _timeStopDays;
}
else
{
request.TimeoutType = TimeoutType.Close;
request.BeforeCloseMinutes = 390 - _timeStopMinutesAfterOpen;
}
}
request.XmlMode = true;
if (null != _exitAlerts)
{
request.ExitConditionAlert = _exitAlerts.Config;
request.ExitConditionType = ExitConditionType.Alert;
}
else if (_trailingStopType == TradeStopLossType.Percent)
{
request.ExitConditionType = ExitConditionType.TrailingPercent;
request.ExitConditionTrailingStop = (double)_trailingStopOffset;
}
else
request.ExitConditionType = ExitConditionType.None;
request.RequestCsvFile = true;
return request;
}
public void RunOddsMaker()
{
if (!_oddsMakerIsRunning)
{
_oddsMakerIsRunning = true;
OddsMakerRequest request = GetOddsMakerRequest();
OddsMaker oddsMaker = _connectionMaster.OddsMakerManager.GetOddsMaker(request);
oddsMaker.OddsMakerProgressXml += oddsMaker_OddsMakerProgressXml;
oddsMaker.OddsMakerCSV += new OddsMakerCSV(oddsMaker_OddsMakerCSV);
oddsMaker.OddsMakerStatus += oddsMaker_OddsMakerStatus;
if (oddsMaker.CanStart())
{
_oddsMakerCsv = "";
oddsMaker.Start();
}
else
_oddsMakerIsRunning = false;
}
}
void oddsMaker_OddsMakerProgressXml(XmlNode progress, OddsMaker sender)
{
double percentComplete = progress.Property("PERCENT_COMPLETE", -1.0);
if (percentComplete != -1.0)
_oddsMakerProgress = percentComplete;
}
void oddsMaker_OddsMakerStatus(OddsMaker sender)
{
if (sender.OddsMakerDisposition == OddsMakerDisposition.Done || sender.OddsMakerDisposition == OddsMakerDisposition.NotStarted)
_oddsMakerIsRunning = false;
else if (sender.OddsMakerDisposition == OddsMakerDisposition.Working)
_oddsMakerIsRunning = true;
if (sender.OddsMakerDisposition == OddsMakerDisposition.Done)
{
if (_oddsMakerCsv == "")
{
_winrate = null;
_omTrades = 0;
_profitFactor = null;
}
_oddsMakerProgress = 100;
_oddsMakerIsRunning = false;
}
}
private void oddsMaker_OddsMakerCSV(string csv, OddsMaker sender)
{
try
{
_oddsMakerCsv = csv;
TextReader textReader = new StringReader(csv);
LumenWorks.Framework.IO.Csv.CsvReader csvReader = new LumenWorks.Framework.IO.Csv.CsvReader(textReader, true);
List tradeRecords = new List();
while (csvReader.ReadNextRecord())
{
OddsMakerTradeRecord tradeRecord = OddsMakerResults.GetTradeFromCsvRecord(csvReader, _long, _stopLossType != TradeStopLossType.None, _targetType != TradeStopLossType.None);
if (null != tradeRecord)
tradeRecords.Add(tradeRecord);
}
_omTrades = tradeRecords.Count;
int winningTrades = tradeRecords.Count(x => x.ProfitDollars() > 0);
int losingTrades = tradeRecords.Count(x => x.ProfitDollars() <= 0);
double winningProfit = 0;
double averageWinner = 0;
if (winningTrades > 0)
{
winningProfit = tradeRecords.Where(x => x.ProfitDollars() > 0).Sum(w => w.ProfitDollars());
averageWinner = tradeRecords.Where(x => x.ProfitDollars() > 0).Average(w => w.ProfitDollars());
}
double losingProfit = 0;
double averageLoser = 0;
if (tradeRecords.Where(x => x.ProfitDollars() < 0).Count() > 0)
{
losingProfit = Math.Abs(tradeRecords.Where(x => x.ProfitDollars() < 0).Sum(w => w.ProfitDollars()));
averageLoser = tradeRecords.Where(x => x.ProfitDollars() < 0).Average(w => w.ProfitDollars());
}
_winrate = Math.Round((double)winningTrades / (double)_omTrades * 100, 1);
double loserate = 100 - _winrate.Value;
double avgWinnerLoserRatio = 3;
if (Math.Abs(averageLoser) > 0)
avgWinnerLoserRatio = averageWinner / Math.Abs(averageLoser);
_profitFactor = Math.Round(winningProfit / losingProfit, 2);
var dailyResults = from n in tradeRecords
group n by n.EntryTime.ToString("yyyy-MM-dd") into g
select new
{
YMD = g.Key,
Day = g.First().EntryTime,
NumTrades = g.Count(),
Profit = g.Sum(x => x.ProfitDollars()),
Winners = g.Count(x => x.ProfitDollars() > 0),
WinningProfit = g.Where(x => x.ProfitDollars() > 0).Sum(x => x.ProfitDollars()),
LosingProfit = Math.Abs(g.Where(x => x.ProfitDollars() < 0).Sum(x => x.ProfitDollars())),
WinRate = Math.Round((double)g.Count(x => x.ProfitDollars() > 0) / (double)g.Count() * 100, 1),
AvgGainPercent = g.Average(x => x.ProfitPercent()),
AvgGainDollars = g.Average(x => x.ProfitDollars()),
TotalPercent = g.Sum(x => x.ProfitPercent()),
TotalDollars = g.Sum(x => x.ProfitDollars())
};
if (dailyResults.Count() > 0)
{
double maxDailyProfit = dailyResults.Max(x => x.Profit);
double minDailyProfit = dailyResults.Min(x => x.Profit);
double maximum = Math.Max(Math.Abs(maxDailyProfit), Math.Abs(minDailyProfit));
List dailyProfits = new List();
DateTime firstDayWithTrades = dailyResults.First().Day;
DateTime dateAfterLastTrade = dailyResults.Last().Day.AddDays(1);
DateTime endDate = new DateTime(dateAfterLastTrade.Year, dateAfterLastTrade.Month, dateAfterLastTrade.Day, 0, 0, 0);
DateTime day = firstDayWithTrades;
while (day <= endDate)
{
var possibleDailyResultSet = dailyResults.Where(x => x.Day.ToString("yyyy-MM-dd") == day.ToString("yyyy-MM-dd"));
if (day.DayOfWeek != DayOfWeek.Sunday && day.DayOfWeek != DayOfWeek.Saturday)
{
if (possibleDailyResultSet.Count() > 0)
dailyProfits.Add(possibleDailyResultSet.First().Profit);
else
dailyProfits.Add(0);
}
day = day.AddDays(1);
}
OddsMakerThermograph = new Thermograph();
OddsMakerThermograph.Max = maximum;
OddsMakerThermograph.Min = -maximum;
OddsMakerThermograph.Mid = 0;
OddsMakerThermograph.Values = dailyProfits;
}
_oddsMakerIsRunning = false;
}
catch (Exception e)
{
string debugView = e.Message;
}
}
private static int GetXmlValue(string key, int def)
{
return GuiEnvironment.XmlConfig.Node("ROBOT").Node(key).Property("VALUE", def);
}
private static decimal GetXmlValue(string key, double def)
{
return (decimal)GuiEnvironment.XmlConfig.Node("ROBOT").Node(key).Property("VALUE", def);
}
///
/// Returns true if all filters required have values in the rowData signal object or if no filter values are
/// required by this strategy. Returns false if filter values are required and aren't included in the rowData.
///
/// Trade signal.
///
internal bool RequiredFiltersAvailable(RowData rowData)
{
if (!AnyFiltersRequired())
return true;
return FilterValuesNeeded(rowData).Count == 0;
}
///
/// Examines rowData and returns a list of filter values that are required to execute this strategy. For example, if
/// the stop is based on the SmartStopD and rowData doesn't contain a value for SmartStopD, then this function will
/// return a List with a single value SmartStopD.
///
/// Trade signal
/// List of strings of filter values required.
public List FilterValuesNeeded(RowData rowData)
{
List requiredFilters = FiltersRequired();
List needFilterValues = new List();
foreach (string filter in requiredFilters)
{
double filterValue = rowData.GetAsDouble("c_" + filter, double.NaN);
if (double.IsNaN(filterValue) && !needFilterValues.Contains(filter))
needFilterValues.Add(filter);
}
return needFilterValues;
}
private bool AnyFiltersRequired()
{
return FiltersRequired().Count > 0;
}
private List FiltersRequired() {
var requiredFilters = new List();
if (TradeEntryLimitReference == TradeEntryLimitReference.Filter)
requiredFilters.Add(LimitOrderFilter);
if ((TradeEntryType == TradeEntryType.StopLimit && EntryStopOrderFilter != "") || (TradeEntryType == TradeEntryType.Stop && EntryStopOrderFilter != ""))
requiredFilters.Add(EntryStopOrderFilter);
if (StopLossType == TradeStopLossType.Filter)
requiredFilters.Add(_stopLossFilter);
if (TrailingStopType == TradeStopLossType.Filter)
requiredFilters.Add(_trailingStopFilter);
if (TargetType == TradeStopLossType.Filter)
requiredFilters.Add(_targetFilter);
return requiredFilters;
}
///
/// The minimum minutes from close for time stop. IB prevents a good-after-time order from being entered TOO close to the close.
///
public static readonly int MIN_MINUTES_FROM_CLOSE_FOR_TIME_STOP = 5;
public override bool Equals(System.Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to Point return false.
TradingStrategy c = obj as TradingStrategy;
if ((System.Object)c == null)
{
return false;
}
// Return true if the fields match:
return (_id == c._id);
}
public bool Equals(TradingStrategy c)
{
// If parameter is null return false:
if ((object)c == null)
{
return false;
}
// Return true if the fields match:
return (_id == c._id);
}
public override int GetHashCode()
{
return _id.GetHashCode();
}
}
public class BrokerConverter : TypeConverter
{
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
{
if (destinationType == typeof(IBroker))
{
IBroker broker = (IBroker)value;
return broker.Name();
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
}