Skip to content

Instantly share code, notes, and snippets.

@samuelcaldas
Created April 2, 2023 15:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samuelcaldas/2f9a19e02ff6c4456dc8b904b7fe143e to your computer and use it in GitHub Desktop.
Save samuelcaldas/2f9a19e02ff6c4456dc8b904b7fe143e to your computer and use it in GitHub Desktop.
#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Xml.Linq;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Data;
using NinjaTrader.Gui.Tools;
using NinjaTrader.NinjaScript;
using System.Text.RegularExpressions;
#endregion
//This namespace holds GUI items and is required.
namespace NinjaTrader.Gui.NinjaScript
{
// NT creates an instance of each class derived from "AddOnBase" and call OnWindowCreated/OnWindowDestroyed for every instance and every NTWindow which is created or destroyed...
public class AddOnFramework : AddOnBase
{
private NTMenuItem addOnFrameworkMenuItem;
private NTMenuItem existingMenuItemInControlCenter;
// Same as other NS objects. However there's a difference: this event could be called in any thread
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Example AddOn demonstrating some of the framework's capabilities";
Name = "AddOn Framework";
}
}
// Will be called as a new NTWindow is created. It will be called in the thread of that window
protected override void OnWindowCreated(Window window)
{
// We want to place our AddOn in the Control Center's menus
ControlCenter cc = window as ControlCenter;
if (cc == null)
return;
/* Determine we want to place our AddOn in the Control Center's "New" menu
Other menus can be accessed via the control's "Automation ID". For example: toolsMenuItem, workspacesMenuItem, connectionsMenuItem, helpMenuItem. */
existingMenuItemInControlCenter = cc.FindFirst("ControlCenterMenuItemNew") as NTMenuItem;
if (existingMenuItemInControlCenter == null)
return;
// 'Header' sets the name of our AddOn seen in the menu structure
addOnFrameworkMenuItem = new NTMenuItem { Header = "AddOn Framework", Style = Application.Current.TryFindResource("MainMenuItem") as Style };
// Add our AddOn into the "New" menu
existingMenuItemInControlCenter.Items.Add(addOnFrameworkMenuItem);
// Subscribe to the event for when the user presses our AddOn's menu item
addOnFrameworkMenuItem.Click += OnMenuItemClick;
}
// Will be called as a new NTWindow is destroyed. It will be called in the thread of that window
protected override void OnWindowDestroyed(Window window)
{
if (addOnFrameworkMenuItem != null && window is ControlCenter)
{
if (existingMenuItemInControlCenter != null && existingMenuItemInControlCenter.Items.Contains(addOnFrameworkMenuItem))
existingMenuItemInControlCenter.Items.Remove(addOnFrameworkMenuItem);
addOnFrameworkMenuItem.Click -= OnMenuItemClick;
addOnFrameworkMenuItem = null;
}
}
// Open our AddOn's window when the menu item is clicked on
private void OnMenuItemClick(object sender, RoutedEventArgs e)
{
Core.Globals.RandomDispatcher.BeginInvoke(new Action(() => new AddOnFrameworkWindow().Show()));
}
}
/* Class which implements Tools.INTTabFactory must be created and set as an attached property for TabControl
in order to use tab page add/remove/move/duplicate functionality */
public class AddOnFrameworkWindowFactory : INTTabFactory
{
// INTTabFactory member. Required to create parent window
public NTWindow CreateParentWindow()
{
return new AddOnFrameworkWindow();
}
// INTTabFactory member. Required to create tabs
public NTTabPage CreateTabPage(string typeName, bool isTrue)
{
return new AddOnFrameworkTab();
}
}
/* This is where we define our AddOn window. The actual content is contained inside the tabs of the window defined in public class AddOnFrameworkTab below.
We have to create a new window class which inherits from Tools.NTWindow for styling and implements IWorkspacePersistence interface for ability to save/restore from workspaces. */
public class AddOnFrameworkWindow : NTWindow, IWorkspacePersistence
{
public AddOnFrameworkWindow()
{
// set Caption property (not Title), since Title is managed internally to properly combine selected Tab Header and Caption for display in the windows taskbar
// This is the name displayed in the top-left of the window
Caption = "AddOn Framework";
// Set the default dimensions of the window
Width = 1085;
Height = 900;
// TabControl should be created for window content if tab features are wanted
TabControl tc = new TabControl();
// Attached properties defined in TabControlManager class should be set to achieve tab moving, adding/removing tabs
TabControlManager.SetIsMovable(tc, true);
TabControlManager.SetCanAddTabs(tc, true);
TabControlManager.SetCanRemoveTabs(tc, true);
// if ability to add new tabs is desired, TabControl has to have attached property "Factory" set.
TabControlManager.SetFactory(tc, new AddOnFrameworkWindowFactory());
Content = tc;
/* In order to have link buttons functionality, tab control items must be derived from Tools.NTTabPage
They can be added using extention method AddNTTabPage(NTTabPage page) */
tc.AddNTTabPage(new AddOnFrameworkTab());
// WorkspaceOptions property must be set
Loaded += (o, e) =>
{
if (WorkspaceOptions == null)
WorkspaceOptions = new WorkspaceOptions("AddOnFramework-" + Guid.NewGuid().ToString("N"), this);
};
}
// IWorkspacePersistence member. Required for restoring window from workspace
public void Restore(XDocument document, XElement element)
{
if (MainTabControl != null)
MainTabControl.RestoreFromXElement(element);
}
// IWorkspacePersistence member. Required for saving window to workspace
public void Save(XDocument document, XElement element)
{
if (MainTabControl != null)
MainTabControl.SaveToXElement(element);
}
// IWorkspacePersistence member
public WorkspaceOptions WorkspaceOptions { get; set; }
}
/* This is where we define the actual content of the tabs for our AddOn window.
Note: Class derived from Tools.NTTabPage has to be created if instrument link or interval link functionality is desired.
Tools.IInstrumentProvider and/or Tools.IIntervalProvider interface(s) should be implemented.
Also NTTabPage provides additional functionality for properly naming tab headers using properties and variables such as @FUNCTION, @INSTRUMENT, etc. */
public class AddOnFrameworkTab : NTTabPage, NinjaTrader.Gui.Tools.IInstrumentProvider, NinjaTrader.Gui.Tools.IIntervalProvider
{
#region Variables
public enum AddOnFrameworkDisplay // enum to help determine what to display in our output box
{
acctExec,
acctOrders,
acctPos,
acctStrat,
acctValues,
atmStrategyGetMarketPosition,
atmStrategyGetPositionAveragePrice,
atmStrategyGetPositionQuantity,
atmStrategyGetRealizedPnL,
atmStrategyGetStopTargetOrderStatus,
atmStrategyGetUnrealizedPnL,
buyMarket,
connectionInfo,
connectKinetickEOD,
frameworkManaged,
fundamentalData,
marketData,
marketDataSnapshot,
marketDepthAsk,
marketDepthBid,
onAccountItemUpdate,
onAccountStatusUpdate,
onConnectionStatusUpdate,
onExecutionUpdate,
onNews,
onOrderUpdate,
onPositionUpdate,
onSimulationAccountReset,
requestData,
realtimeData,
sellMarket,
}
// ADDONFRAMEWORK SECTION
private AddOnFrameworkDisplay outputType;
private TextBox outputBox;
// Define objects for the Account section
private AccountSelector accountSelector;
private Button acctValuesButton;
private Button acctExecButton;
private Button acctOrdersButton;
private Button acctPosButton;
private Button acctStratButton;
private Account lastAccount;
private Button onAccountItemUpdateButton;
private Button onAccountStatusUpdateButton;
// Define objects for the Data Access section
private Cbi.Instrument instrument;
private InstrumentSelector instrumentSelector;
private IntervalSelector intervalSelector;
private NumericTextBox daysBack;
private Button requestDataButton;
private Button realtimeDataButton;
private Button fundamentalDataButton;
private Button marketDataButton;
private Button marketDataSnapshotButton;
private Button marketDepthAskButton;
private Button marketDepthBidButton;
// private int barCount = 0; // Used if processing real-time bars only on bar closes
private bool barsRequestSubscribed = false;
private BarsRequest barsRequest;
private MarketData marketData;
private MarketDepth<MarketDepthRow> marketDepth;
private FundamentalData fundamentalData;
// Define objects for the Orders section
private QuantityUpDown qudSelector;
private TifSelector tifSelector;
private AtmStrategy.AtmStrategySelector atmStrategySelector;
private NinjaTrader.NinjaScript.AtmStrategy selectedAtmStrategy;
private Button buyMarketButton;
private Button sellMarketButton;
private Button frameworkManagedButton;
private Button onOrderUpdateButton;
private Button onExecutionUpdateButton;
private Button onPositionUpdateButton;
private Order entryOrder;
private Order frameworkEntryOrder;
private Order profitTarget;
private Order stopLoss;
// Define objects for the Misc section
private Button connectKinetickEODButton;
private Button connectionInfoButton;
private Button onConnectionStatusUpdateButton;
private Button onSimulationAccountResetButton;
private Button onNewsButton;
private Connection connection;
private NewsSubscription newsSubscription;
private NewsItems newsItems;
private Button atmStrategyGetMarketPositionButton;
private Button atmStrategyGetPositionAveragePriceButton;
private Button atmStrategyGetPositionQuantityButton;
private Button atmStrategyGetRealizedPnLButton;
private Button atmStrategyGetStopTargetOrderStatusButton;
private Button atmStrategyGetUnrealizedPnLButton;
#endregion
public AddOnFrameworkTab()
{
Content = LoadXAML();
// Sets the tab header name to be the currently selected instrument's name
TabName = "@INSTRUMENT_FULL";
// Subscribe to account status updates. This event will fire as connection.Status of the hosting connection changes
Account.AccountStatusUpdate += OnAccountStatusUpdate;
// Subscribe to sim account resets (Also happens when rewinding/fast forwarding Playback connection)
Account.SimulationAccountReset += OnSimulationAccountReset;
// Subscribe to connection status events
Connection.ConnectionStatusUpdate += OnConnectionStatusUpdate;
// Subscribe to news
newsSubscription = new NinjaTrader.Data.NewsSubscription();
newsSubscription.Update += OnNews;
newsItems = new NinjaTrader.Data.NewsItems(10); // Maintain the last 10 news items
}
/* We can use loose XAML to define controls and layouts, but we have to find controls after XAML parsing and attach event handlers in C#
Note: XAML with event handlers defined inside WILL FAIL when attempted to load.
Note: XAML with "inline code" WILL FAIL when attempted to load */
private DependencyObject LoadXAML()
{
try
{
using (System.IO.Stream assemblyResourceStream = GetManifestResourceStream("AddOns.AddOnFrameworkTab.xaml"))
{
if (assemblyResourceStream == null)
return null;
System.IO.StreamReader streamReader = new System.IO.StreamReader(assemblyResourceStream);
/* Parse XAML and convert it to the desired type (defined in XAML)
Page is used here, but it can be anything - Window, Grid, StackPannel or simple Button */
Page page = System.Windows.Markup.XamlReader.Load(streamReader.BaseStream) as Page;
DependencyObject pageContent = null;
if (page != null)
{
pageContent = page.Content as DependencyObject;
// Find Output Box
outputBox = LogicalTreeHelper.FindLogicalNode(pageContent, "outputBox") as TextBox;
#region Account Section
// Find account selector
accountSelector = LogicalTreeHelper.FindLogicalNode(pageContent, "accountSelector") as AccountSelector;
// Using an Action to save space since we reference this code twice
Action updateAccountHandlers = () =>
{
if (accountSelector.SelectedAccount != null)
{
if (lastAccount != null)
{
lastAccount.AccountItemUpdate -= OnAccountItemUpdate;
lastAccount.ExecutionUpdate -= OnExecutionUpdate;
lastAccount.OrderUpdate -= OnOrderUpdate;
lastAccount.PositionUpdate -= OnPositionUpdate;
lastAccount = null;
}
// Subscribe to new account subscriptions only if the selected account is connected
// This prevents lingering subscriptions on disconnect
lock(Connection.Connections)
{
if (Connection.Connections.Where(c => c.Status == ConnectionStatus.Connected).Any(c => c.Accounts.Contains(accountSelector.SelectedAccount)))
{
accountSelector.SelectedAccount.AccountItemUpdate += OnAccountItemUpdate;
accountSelector.SelectedAccount.ExecutionUpdate += OnExecutionUpdate;
accountSelector.SelectedAccount.OrderUpdate += OnOrderUpdate;
accountSelector.SelectedAccount.PositionUpdate += OnPositionUpdate;
lastAccount = accountSelector.SelectedAccount;
}
}
}
};
// Since the account selector might not be empty on startup, we autosubscribe the selected account
updateAccountHandlers();
// When the account selector's selection changes, unsubscribe the last account and subscribe the currently selected
accountSelector.SelectionChanged += (o, args) => { updateAccountHandlers(); };
// Find Account Values button and attach event handler
acctValuesButton = LogicalTreeHelper.FindLogicalNode(pageContent, "acctValuesButton") as Button;
if (acctValuesButton != null)
acctValuesButton.Click += OnButtonClick;
// Find Account Executions button and attach event handler
acctExecButton = LogicalTreeHelper.FindLogicalNode(pageContent, "acctExecButton") as Button;
if (acctExecButton != null)
acctExecButton.Click += OnButtonClick;
// Find Account Orders button and attach event handler
acctOrdersButton = LogicalTreeHelper.FindLogicalNode(pageContent, "acctOrdersButton") as Button;
if (acctOrdersButton != null)
acctOrdersButton.Click += OnButtonClick;
// Find Account Positions button and attach event handler
acctPosButton = LogicalTreeHelper.FindLogicalNode(pageContent, "acctPosButton") as Button;
if (acctPosButton != null)
acctPosButton.Click += OnButtonClick;
// Find Account Strategies button and attach event handler
acctStratButton = LogicalTreeHelper.FindLogicalNode(pageContent, "acctStratButton") as Button;
if (acctStratButton != null)
acctStratButton.Click += OnButtonClick;
// Find OnAccountItemUpdate button and attach event handler
onAccountItemUpdateButton = LogicalTreeHelper.FindLogicalNode(pageContent, "onAccountItemUpdateButton") as Button;
if (onAccountItemUpdateButton != null)
onAccountItemUpdateButton.Click += OnButtonClick;
// Find OnAccountStatusUpdate button and attach event handler
onAccountStatusUpdateButton = LogicalTreeHelper.FindLogicalNode(pageContent, "onAccountStatusUpdateButton") as Button;
if (onAccountStatusUpdateButton != null)
onAccountStatusUpdateButton.Click += OnButtonClick;
#endregion
#region Data Access Section
// Find instrument selector and attach event handler
instrumentSelector = LogicalTreeHelper.FindLogicalNode(pageContent, "instrumentSelector") as InstrumentSelector;
if (instrumentSelector != null)
instrumentSelector.InstrumentChanged += OnInstrumentChanged;
// Find interval selector and attach event handler
intervalSelector = LogicalTreeHelper.FindLogicalNode(pageContent, "intervalSelector") as IntervalSelector;
if (intervalSelector != null)
intervalSelector.IntervalChanged += OnIntervalChanged;
// Find days back selector
daysBack = LogicalTreeHelper.FindLogicalNode(pageContent, "daysBackSelector") as NumericTextBox;
// Find Historical Data button and attach event handler
requestDataButton = LogicalTreeHelper.FindLogicalNode(pageContent, "requestDataButton") as Button;
if (requestDataButton != null)
requestDataButton.Click += OnButtonClick;
// Find Real-Time Data button and attach event handler
realtimeDataButton = LogicalTreeHelper.FindLogicalNode(pageContent, "realtimeDataButton") as Button;
if (realtimeDataButton != null)
realtimeDataButton.Click += OnButtonClick;
// Find Fundamental Data button and attach event handler
fundamentalDataButton = LogicalTreeHelper.FindLogicalNode(pageContent, "fundamentalDataButton") as Button;
if (fundamentalDataButton != null)
fundamentalDataButton.Click += OnButtonClick;
// Find Market Data Subscription button and attach event handler
marketDataButton = LogicalTreeHelper.FindLogicalNode(pageContent, "marketDataButton") as Button;
if (marketDataButton != null)
marketDataButton.Click += OnButtonClick;
// Find Market Data Snapshot button and attach event handler
marketDataSnapshotButton = LogicalTreeHelper.FindLogicalNode(pageContent, "marketDataSnapshotButton") as Button;
if (marketDataSnapshotButton != null)
marketDataSnapshotButton.Click += OnButtonClick;
// Find Market Depth Ask button and attach event handler
marketDepthAskButton = LogicalTreeHelper.FindLogicalNode(pageContent, "marketDepthAskButton") as Button;
if (marketDepthAskButton != null)
marketDepthAskButton.Click += OnButtonClick;
// Find Market Depth Bid button and attach event handler
marketDepthBidButton = LogicalTreeHelper.FindLogicalNode(pageContent, "marketDepthBidButton") as Button;
if (marketDepthBidButton != null)
marketDepthBidButton.Click += OnButtonClick;
#endregion
#region Orders Section
// Find Quantity Up-Down selector
qudSelector = LogicalTreeHelper.FindLogicalNode(pageContent, "qudSelector") as QuantityUpDown;
// Find TIF selector
tifSelector = LogicalTreeHelper.FindLogicalNode(pageContent, "tifSelector") as TifSelector;
// Be sure to bind our account selector to our TIF selector to ensure proper functionality
tifSelector.SetBinding(TifSelector.AccountProperty, new Binding { Source = accountSelector, Path = new PropertyPath("SelectedAccount") });
// When our TIF selector's selection changes
tifSelector.SelectionChanged += (o, args) =>
{
// Change the selected TIF in the ATM strategy too
if (atmStrategySelector.SelectedAtmStrategy != null)
atmStrategySelector.SelectedAtmStrategy.TimeInForce = tifSelector.SelectedTif;
};
// Find ATM Strategy selector and attach event handler
atmStrategySelector = LogicalTreeHelper.FindLogicalNode(pageContent, "atmStrategySelector") as AtmStrategy.AtmStrategySelector;
atmStrategySelector.Id = Guid.NewGuid().ToString("N");
if (atmStrategySelector != null)
atmStrategySelector.CustomPropertiesChanged += OnAtmCustomPropertiesChanged;
// Be sure to bind our account selector to our ATM strategy selector to ensure proper functionality
atmStrategySelector.SetBinding(AtmStrategy.AtmStrategySelector.AccountProperty, new Binding { Source = accountSelector, Path = new PropertyPath("SelectedAccount") });
// When our ATM selector's selection changes
atmStrategySelector.SelectionChanged += (o, args) =>
{
if (atmStrategySelector.SelectedItem == null)
return;
if (args.AddedItems.Count > 0)
{
// Change the selected TIF in our TIF selector too
NinjaTrader.NinjaScript.AtmStrategy selectedStrategy = args.AddedItems[0] as NinjaTrader.NinjaScript.AtmStrategy;
if (selectedStrategy != null)
{
tifSelector.SelectedTif = selectedStrategy.TimeInForce;
}
}
};
// Find Buy Market button and attach event handler
buyMarketButton = LogicalTreeHelper.FindLogicalNode(pageContent, "buyMarketButton") as Button;
if (buyMarketButton != null)
buyMarketButton.Click += OnButtonClick;
// Find Sell Market button and attach event handler
sellMarketButton = LogicalTreeHelper.FindLogicalNode(pageContent, "sellMarketButton") as Button;
if (sellMarketButton != null)
sellMarketButton.Click += OnButtonClick;
// Find Framework Managed button and attach event handler
frameworkManagedButton = LogicalTreeHelper.FindLogicalNode(pageContent, "frameworkManagedButton") as Button;
if (frameworkManagedButton != null)
frameworkManagedButton.Click += OnButtonClick;
// Find OnOrderUpdate button and attach event handler
onOrderUpdateButton = LogicalTreeHelper.FindLogicalNode(pageContent, "onOrderUpdateButton") as Button;
if (onOrderUpdateButton != null)
onOrderUpdateButton.Click += OnButtonClick;
// Find OnExecutionUpdate button and attach event handler
onExecutionUpdateButton = LogicalTreeHelper.FindLogicalNode(pageContent, "onExecutionUpdateButton") as Button;
if (onExecutionUpdateButton != null)
onExecutionUpdateButton.Click += OnButtonClick;
// Find OnPositionUpdate button and attach event handler
onPositionUpdateButton = LogicalTreeHelper.FindLogicalNode(pageContent, "onPositionUpdateButton") as Button;
if (onPositionUpdateButton != null)
onPositionUpdateButton.Click += OnButtonClick;
#endregion
#region Misc Section
// Find Connect Kinetick EOD button and attach event handler
connectKinetickEODButton = LogicalTreeHelper.FindLogicalNode(pageContent, "connectKinetickEODButton") as Button;
if (connectKinetickEODButton != null)
connectKinetickEODButton.Click += OnButtonClick;
// Find Connection Info button and attach event handler
connectionInfoButton = LogicalTreeHelper.FindLogicalNode(pageContent, "connectionInfoButton") as Button;
if (connectionInfoButton != null)
connectionInfoButton.Click += OnButtonClick;
// Find OnConnectionStatusUpdate button and attach event handler
onConnectionStatusUpdateButton = LogicalTreeHelper.FindLogicalNode(pageContent, "onConnectionStatusUpdateButton") as Button;
if (onConnectionStatusUpdateButton != null)
onConnectionStatusUpdateButton.Click += OnButtonClick;
// Find OnSimulationAccountReset button and attach event handler
onSimulationAccountResetButton = LogicalTreeHelper.FindLogicalNode(pageContent, "onSimulationAccountResetButton") as Button;
if (onSimulationAccountResetButton != null)
onSimulationAccountResetButton.Click += OnButtonClick;
// Find OnNews button and attach event handler
onNewsButton = LogicalTreeHelper.FindLogicalNode(pageContent, "onNewsButton") as Button;
if (onNewsButton != null)
onNewsButton.Click += OnButtonClick;
// Find atmStrategyGetMarketPositionButton button and attach event handler
atmStrategyGetMarketPositionButton = LogicalTreeHelper.FindLogicalNode(pageContent, "atmStrategyGetMarketPositionButton") as Button;
if (atmStrategyGetMarketPositionButton != null)
atmStrategyGetMarketPositionButton.Click += OnButtonClick;
// Find atmStrategyGetPositionAveragePriceButton button and attach event handler
atmStrategyGetPositionAveragePriceButton = LogicalTreeHelper.FindLogicalNode(pageContent, "atmStrategyGetPositionAveragePriceButton") as Button;
if (atmStrategyGetPositionAveragePriceButton != null)
atmStrategyGetPositionAveragePriceButton.Click += OnButtonClick;
// Find atmStrategyGetPositionQuantityButton button and attach event handler
atmStrategyGetPositionQuantityButton = LogicalTreeHelper.FindLogicalNode(pageContent, "atmStrategyGetPositionQuantityButton") as Button;
if (atmStrategyGetPositionQuantityButton != null)
atmStrategyGetPositionQuantityButton.Click += OnButtonClick;
// Find atmStrategyGetRealizedPnLButton button and attach event handler
atmStrategyGetRealizedPnLButton = LogicalTreeHelper.FindLogicalNode(pageContent, "atmStrategyGetRealizedPnLButton") as Button;
if (atmStrategyGetRealizedPnLButton != null)
atmStrategyGetRealizedPnLButton.Click += OnButtonClick;
// Find atmStrategyGetStopTargetOrderStatusButton button and attach event handler
atmStrategyGetStopTargetOrderStatusButton = LogicalTreeHelper.FindLogicalNode(pageContent, "atmStrategyGetStopTargetOrderStatusButton") as Button;
if (atmStrategyGetStopTargetOrderStatusButton != null)
atmStrategyGetStopTargetOrderStatusButton.Click += OnButtonClick;
// Find atmStrategyGetUnrealizedPnLButton button and attach event handler
atmStrategyGetUnrealizedPnLButton = LogicalTreeHelper.FindLogicalNode(pageContent, "atmStrategyGetUnrealizedPnLButton") as Button;
if (atmStrategyGetUnrealizedPnLButton != null)
atmStrategyGetUnrealizedPnLButton.Click += OnButtonClick;
#endregion
}
return pageContent;
}
}
catch (Exception ex)
{
return null;
}
}
private string FormatValue(Account account, double value)
{
return Core.Globals.FormatCurrency(((double) value).CompareTo(double.MinValue) == 0 ? 0.0 : (double)value, account.Denomination);
}
private void OnButtonClick(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
#region Behavior for Account Section Buttons
// When user presses the Account Values button
if (button != null && ReferenceEquals(button, acctValuesButton))
{
outputType = AddOnFrameworkDisplay.acctValues;
outputBox.Text = "ACCOUNT VALUES";
if (accountSelector == null || accountSelector.SelectedAccount == null)
{
outputBox.AppendText(Environment.NewLine + "No account selected.");
return;
}
outputBox.AppendText(string.Format("{0}Name: {1}{0}Connection: {2}{0}BuyingPower: {3}{0}CashValue: {4}{0}InitialMargin: {5}{0}MaintenanceMargin: {6}{0}NetLiquidation: {7}{0}NetLiquidationByCurrency: {8}{0}RealizedProfitLoss: {9}{0}TotalCashBalance: {10}{0}",
Environment.NewLine,
accountSelector.SelectedAccount.Name,
accountSelector.SelectedAccount.Connection.Options.Name,
FormatValue(accountSelector.SelectedAccount, accountSelector.SelectedAccount.Get(AccountItem.BuyingPower, Currency.UsDollar)),
FormatValue(accountSelector.SelectedAccount, accountSelector.SelectedAccount.Get(AccountItem.CashValue, Currency.UsDollar)),
FormatValue(accountSelector.SelectedAccount, accountSelector.SelectedAccount.Get(AccountItem.InitialMargin, Currency.UsDollar)),
FormatValue(accountSelector.SelectedAccount, accountSelector.SelectedAccount.Get(AccountItem.MaintenanceMargin, Currency.UsDollar)),
FormatValue(accountSelector.SelectedAccount, accountSelector.SelectedAccount.Get(AccountItem.NetLiquidation, Currency.UsDollar)),
FormatValue(accountSelector.SelectedAccount, accountSelector.SelectedAccount.Get(AccountItem.NetLiquidationByCurrency, Currency.UsDollar)),
FormatValue(accountSelector.SelectedAccount, accountSelector.SelectedAccount.Get(AccountItem.RealizedProfitLoss, Currency.UsDollar)),
FormatValue(accountSelector.SelectedAccount, accountSelector.SelectedAccount.Get(AccountItem.TotalCashBalance, Currency.UsDollar))));
}
// When user presses the Account Executions button
if (button != null && ReferenceEquals(button, acctExecButton))
{
outputType = AddOnFrameworkDisplay.acctExec;
outputBox.Text = "ACCOUNT EXECUTIONS";
if (accountSelector == null || accountSelector.SelectedAccount == null)
{
outputBox.AppendText(Environment.NewLine + "No account selected.");
return;
}
// Executions since last connect + NT lookback period for historical executions
lock (accountSelector.SelectedAccount.Executions)
foreach (Execution execution in accountSelector.SelectedAccount.Executions)
outputBox.AppendText(string.Format("{0}Instrument: {1}{0}Quantity: {2}{0}Price: {3}{0}Time: {4}{0}ExecutionID: {5}{0}Exchange: {6}{0}MarketPosition: {7}{0}OrderID: {8}{0}Name: {9}{0}Commission: {10}{0}Rate: {11}{0}",
Environment.NewLine,
execution.Instrument.FullName,
execution.Quantity,
execution.Price,
execution.Time,
execution.ExecutionId,
execution.Exchange,
execution.MarketPosition,
execution.OrderId,
execution.Name,
execution.Commission,
execution.Rate));
}
// When user presses the Account Orders button
if (button != null && ReferenceEquals(button, acctOrdersButton))
{
outputType = AddOnFrameworkDisplay.acctOrders;
outputBox.Text = "ACCOUNT ORDERS";
if (accountSelector == null || accountSelector.SelectedAccount == null)
{
outputBox.AppendText(Environment.NewLine + "No account selected.");
return;
}
// Iterate through all orders (open and closed)
lock (accountSelector.SelectedAccount.Orders)
foreach (Order order in accountSelector.SelectedAccount.Orders)
outputBox.AppendText(string.Format("{0}Instrument: {1}{0}OrderAction: {2}{0}OrderType: {3}{0}Quantity: {4}{0}LimitPrice: {5}{0}StopPrice: {6}{0}OrderState: {7}{0}Filled: {8}{0}AverageFillPrice: {9}{0}Name: {10}{0}OCO: {11}{0}TimeInForce: {12}{0}OrderID: {13}{0}Time: {14}{0}",
Environment.NewLine,
order.Instrument.FullName,
order.OrderAction,
order.OrderType,
order.Quantity,
order.LimitPrice,
order.StopPrice,
order.OrderState,
order.Filled,
order.AverageFillPrice,
order.Name,
order.Oco,
order.TimeInForce,
order.OrderId,
order.Time));
}
// When user presses the Account Positions button
if (button != null && ReferenceEquals(button, acctPosButton))
{
outputType = AddOnFrameworkDisplay.acctPos;
outputBox.Text = "ACCOUNT POSITIONS";
if (accountSelector == null || accountSelector.SelectedAccount == null)
{
outputBox.AppendText(Environment.NewLine + "No account selected.");
return;
}
// Iterate through all open positions
lock (accountSelector.SelectedAccount.Positions)
foreach (Position position in accountSelector.SelectedAccount.Positions)
outputBox.AppendText(string.Format("{0}Instrument: {1}{0}MarketPosition: {2}{0}Quantity: {3}{0}AveragePrice: {4}{0}UnrealizedPnL: {5}{0}Connection: {6}{0}",
Environment.NewLine,
position.Instrument.FullName,
position.MarketPosition,
position.Quantity,
position.AveragePrice,
position.GetUnrealizedProfitLoss(PerformanceUnit.Currency, double.MinValue),
//(position.Instrument.MarketData.Last != null ? position.GetProfitLoss(position.Instrument.MarketData.Last.Price, PerformanceUnit.Currency).ToString() : "(unknown)"),
accountSelector.SelectedAccount.Connection.Options.Name));
}
// When user presses the Account Strategies button
if (button != null && ReferenceEquals(button, acctStratButton))
{
outputType = AddOnFrameworkDisplay.acctStrat;
outputBox.Text = "ACCOUNT STRATEGIES";
if (accountSelector == null || accountSelector.SelectedAccount == null)
{
outputBox.AppendText(Environment.NewLine + "No account selected.");
return;
}
// Iterate through all ATM and NS strategies (active, recovered upon last connect, or deactived since last connect)
lock (accountSelector.SelectedAccount.Strategies)
foreach (StrategyBase strategy in accountSelector.SelectedAccount.Strategies)
outputBox.AppendText(string.Format("{0}Name: {1}{0}ATM Template Name: {2}{0}Instrument: {3}{0}State: {4}{0}Category: {5}{0}",
Environment.NewLine,
strategy.Name,
strategy.Template,
strategy.Instruments[0].FullName, // This is the primary instrument. There might be secondary instruments on NS strategies
strategy.State,
strategy.Category));
}
// When user presses the OnAccountItemUpdate button
if (button != null && ReferenceEquals(button, onAccountItemUpdateButton))
{
outputType = AddOnFrameworkDisplay.onAccountItemUpdate;
outputBox.Text = "OnAccountItemUpdate() Monitor";
if (accountSelector.SelectedAccount == null)
outputBox.AppendText(Environment.NewLine + "No account selected.");
}
// When user presses the OnAccountStatusUpdate button
if (button != null && ReferenceEquals(button, onAccountStatusUpdateButton))
{
outputType = AddOnFrameworkDisplay.onAccountStatusUpdate;
outputBox.Text = "OnAccountStatusUpdate() Monitor";
}
#endregion
#region Behavior for Data Access Section Buttons
// When user presses the Request Data button
if (button != null && ReferenceEquals(button, requestDataButton))
{
outputType = AddOnFrameworkDisplay.requestData;
outputBox.Text = "LOADING DATA...";
// Unsubscribe to any old bars requests
if (barsRequest != null)
{
if (barsRequestSubscribed)
{
barsRequest.Update -= OnBarUpdate;
barsRequestSubscribed = false;
}
barsRequest = null;
}
barsRequest = DoBarsRequest(Instrument, Convert.ToInt32(daysBack.Text.ToString()), false);
}
// When user presses the Realtime Data button
if (button != null && ReferenceEquals(button, realtimeDataButton))
{
outputType = AddOnFrameworkDisplay.realtimeData;
outputBox.Text = "LOADING DATA...";
// Unsubscribe to any old bars requests
if (barsRequest != null)
{
if (barsRequestSubscribed)
{
barsRequest.Update -= OnBarUpdate;
barsRequestSubscribed = false;
}
barsRequest = null;
}
barsRequest = DoBarsRequest(Instrument, Convert.ToInt32(daysBack.Text.ToString()), true);
}
// When user presses the Fundamental Data button
if (button != null && ReferenceEquals(button, fundamentalDataButton))
{
outputType = AddOnFrameworkDisplay.fundamentalData;
if (fundamentalData != null)
{
outputBox.Text = string.Format("FUNDAMENTAL SNAPSHOT{0}Average Daily Volume: {1}{0}Beta: {2}{0}Calendar Year High: {3}{0}Calendar Year High Date: {4}{0}Calendar Year Low: {5}{0}Calendar Year Low Date: {6}{0}Current Ratio: {7}{0}Dividend Amount: {8}{0}Dividend Pay Date: {9}{0}Dividend Yield: {10}{0}Earnings Per Share: {11}{0}5 Yrs Growth Percentage: {12}{0}High 52 Weeks: {13}{0}High 52 Weeks Date: {14}{0}Historical Volatility: {15}{0}Insider Owned: {16}{0}Low 52 Weeks: {17}{0}Low 52 Weeks Date: {18}{0}Market Cap: {19}{0}Next Year EPS: {20}{0}Percent Held By Institutions: {21}{0}Price Earnings Ratio: {22}{0}Revenue Per Share: {23}{0}Shares Outstanding: {24}{0}Short Interest: {25}{0}Short Interest Ratio: {26}{0}VWAP: {27}",
Environment.NewLine,
fundamentalData.AverageDailyVolume,
fundamentalData.Beta,
fundamentalData.CalendarYearHigh,
fundamentalData.CalendarYearHighDate,
fundamentalData.CalendarYearLow,
fundamentalData.CalendarYearLowDate,
fundamentalData.CurrentRatio,
fundamentalData.DividendAmount,
fundamentalData.DividendPayDate,
fundamentalData.DividendYield,
fundamentalData.EarningsPerShare,
fundamentalData.FiveYearsGrowthPercentage,
fundamentalData.High52Weeks,
fundamentalData.High52WeeksDate,
fundamentalData.HistoricalVolatility,
fundamentalData.InsiderOwned,
fundamentalData.Low52Weeks,
fundamentalData.Low52WeeksDate,
fundamentalData.MarketCap,
fundamentalData.NextYearsEarningsPerShare,
fundamentalData.PercentHeldByInstitutions,
fundamentalData.PriceEarningsRatio,
fundamentalData.RevenuePerShare,
fundamentalData.SharesOutstanding,
fundamentalData.ShortInterest,
fundamentalData.ShortInterestRatio,
fundamentalData.VWAP);
}
else
outputBox.Text = string.Format("FUNDAMENTAL SNAPSHOT{0}No instrument selected.", Environment.NewLine);
}
// When user presses the Market Data Subscription button
if (button != null && ReferenceEquals(button, marketDataButton))
{
outputType = AddOnFrameworkDisplay.marketData;
if (marketData == null)
outputBox.Text = string.Format("MARKET DATA{0}No instrument selected.", Environment.NewLine);
else
outputBox.Text = "MARKET DATA";
}
// When user presses the Market Data Snapshot button
if (button != null && ReferenceEquals(button, marketDataSnapshotButton))
{
outputType = AddOnFrameworkDisplay.marketDataSnapshot;
if (marketData != null)
{
outputBox.Text = string.Format("MARKET SNAPSHOT{0}Last: {1}{0}Ask: {2}{0}Bid: {3}{0}DailyHigh: {4}{0}DailyLow: {5}{0}LastClose: {6}{0}Opening: {7}{0}OpenInterest: {8}{0}Settlement: {9}",
Environment.NewLine,
(marketData.Last != null ? marketData.Last.Price.ToString() : "(null)"),
(marketData.Ask != null ? marketData.Ask.Price.ToString() : "(null)"),
(marketData.Bid != null ? marketData.Bid.Price.ToString() : "(null)"),
(marketData.DailyHigh != null ? marketData.DailyHigh.Price.ToString() : "(null)"),
(marketData.DailyLow != null ? marketData.DailyLow.Price.ToString() : "(null)"),
(marketData.LastClose != null ? marketData.LastClose.Price.ToString() : "(null)"),
(marketData.Opening != null ? marketData.Opening.Price.ToString() : "(null)"),
(marketData.OpenInterest != null ? marketData.OpenInterest.Price.ToString() : "(null)"),
(marketData.Settlement != null ? marketData.Settlement.Price.ToString() : "(null)"));
}
else
outputBox.Text = string.Format("MARKET SNAPSHOT{0}No instrument selected.", Environment.NewLine);
}
// When user presses the Market Depth Ask button
if (button != null && ReferenceEquals(button, marketDepthAskButton))
{
outputType = AddOnFrameworkDisplay.marketDepthAsk;
if (marketDepth != null)
{
outputBox.Text = "ASK LADDER";
// Iterate through the ASK side of the price ladder
for (int i = 0; i < marketDepth.Asks.Count; i++)
outputBox.AppendText(string.Format("{0}Position: {2}{1}Price: {3}{1}Volume: {4}",
Environment.NewLine,
"\t",
i,
marketDepth.Asks[i].Price,
marketDepth.Asks[i].Volume));
}
else
outputBox.Text = string.Format("ASK LADDER{0}No instrument selected.", Environment.NewLine);
}
// When user presses the Market Depth Bid button
if (button != null && ReferenceEquals(button, marketDepthBidButton))
{
outputType = AddOnFrameworkDisplay.marketDepthBid;
if (marketDepth != null)
{
outputBox.Text = "BID LADDER";
// Iterate through the BID side of the price ladder
for (int i = 0; i < marketDepth.Bids.Count; i++)
outputBox.AppendText(string.Format("{0}Position: {2}{1}Price: {3}{1}Volume: {4}",
Environment.NewLine,
"\t",
i,
marketDepth.Bids[i].Price,
marketDepth.Bids[i].Volume));
}
else
outputBox.Text = string.Format("BID LADDER{0}No instrument selected.", Environment.NewLine);
}
#endregion
#region Behavior for Orders Section Buttons
// When user presses the Buy Market button
if (button != null && ReferenceEquals(button, buyMarketButton))
{
outputType = AddOnFrameworkDisplay.buyMarket;
outputBox.Text = "SUBMIT BUY MARKET ORDER";
if (accountSelector == null || accountSelector.SelectedAccount == null)
{
outputBox.AppendText(Environment.NewLine + "No account selected.");
return;
}
if (Instrument == null)
{
outputBox.AppendText(Environment.NewLine + "No instrument selected.");
return;
}
// Submit our Buy Market order with the selected ATM strategy
if (atmStrategySelector.SelectedAtmStrategy != null)
{
/* When submitted orders with ATM strategies you MUST set the 'name' property to "Entry" in order for it to work.
Otherwise the entry order will be stuck in OrderState.Initialized and no ATM strategy will be started */
entryOrder = accountSelector.SelectedAccount.CreateOrder(Instrument, OrderAction.Buy, OrderType.Market, tifSelector.SelectedTif, qudSelector.Value, 0, 0, string.Empty, "Entry", null);
selectedAtmStrategy = NinjaTrader.NinjaScript.AtmStrategy.StartAtmStrategy(atmStrategySelector.SelectedAtmStrategy, entryOrder);
}
// If no ATM strategy selected, submit our Buy Market order alone
else
{
entryOrder = accountSelector.SelectedAccount.CreateOrder(Instrument, OrderAction.Buy, OrderType.Market, tifSelector.SelectedTif, qudSelector.Value, 0, 0, string.Empty, "AddOnFramework - Buy Market", null);
accountSelector.SelectedAccount.Submit(new[] { entryOrder });
}
}
// When user presses the Sell Market button
if (button != null && ReferenceEquals(button, sellMarketButton))
{
outputType = AddOnFrameworkDisplay.sellMarket;
outputBox.Text = "SUBMIT SELL MARKET ORDER";
if (accountSelector == null || accountSelector.SelectedAccount == null)
{
outputBox.AppendText(Environment.NewLine + "No account selected.");
return;
}
if (Instrument == null)
{
outputBox.AppendText(Environment.NewLine + "No instrument selected.");
return;
}
// Submit our Sell Market order with the selected ATM strategy
if (atmStrategySelector.SelectedAtmStrategy != null)
{
/* When submitted orders with ATM strategies you MUST set the 'name' property to "Entry" in order for it to work.
Otherwise the entry order will be stuck in OrderState.Initialized and no ATM strategy will be started */
entryOrder = accountSelector.SelectedAccount.CreateOrder(Instrument, OrderAction.Sell, OrderType.Market, tifSelector.SelectedTif, qudSelector.Value, 0, 0, string.Empty, "Entry", null);
selectedAtmStrategy = NinjaTrader.NinjaScript.AtmStrategy.StartAtmStrategy(atmStrategySelector.SelectedAtmStrategy, entryOrder);
}
// If no ATM strategy selected, submit our Sell Market order alone
else
{
entryOrder = accountSelector.SelectedAccount.CreateOrder(Instrument, OrderAction.Sell, OrderType.Market, tifSelector.SelectedTif, qudSelector.Value, 0, 0, string.Empty, "AddOnFramework - Sell Market", null);
accountSelector.SelectedAccount.Submit(new[] { entryOrder });
}
}
// When user presses the Framework Managed button
if (button != null && ReferenceEquals(button, frameworkManagedButton))
{
outputType = AddOnFrameworkDisplay.frameworkManaged;
outputBox.Text = "FRAMEWORK MANAGED BUY MARKET ORDER";
if (accountSelector == null || accountSelector.SelectedAccount == null)
{
outputBox.AppendText(Environment.NewLine + "No account selected.");
return;
}
if (Instrument == null)
{
outputBox.AppendText(Environment.NewLine + "No instrument selected.");
return;
}
// Request real-time data for the management of our stops/targets after we enter into the market
if (barsRequest != null)
{
if (barsRequestSubscribed)
{
barsRequest.Update -= OnBarUpdate;
barsRequestSubscribed = false;
}
barsRequest = null;
}
barsRequest = DoBarsRequest(Instrument, Convert.ToInt32(daysBack.Text.ToString()), true);
/* Submit our entry order to be protected by the framework
See OnOrderUpdate() for the stop/targets.
See OnBarUpdate() for how to modify/cancel orders */
frameworkEntryOrder = accountSelector.SelectedAccount.CreateOrder(Instrument, OrderAction.Buy, OrderType.Market, tifSelector.SelectedTif, qudSelector.Value, 0, 0, string.Empty, "AddOnFramework - Managed Buy Market", null);
accountSelector.SelectedAccount.Submit(new[] { frameworkEntryOrder });
}
// When user presses the OnOrderUpdate button
if (button != null && ReferenceEquals(button, onOrderUpdateButton))
{
outputType = AddOnFrameworkDisplay.onOrderUpdate;
outputBox.Text = "OnOrderUpdate() Monitor";
}
// When user presses the OnExecutionUpdate button
if (button != null && ReferenceEquals(button, onExecutionUpdateButton))
{
outputType = AddOnFrameworkDisplay.onExecutionUpdate;
outputBox.Text = "OnExecutionUpdate() Monitor";
}
// When user presses the OnPositionUpdate button
if (button != null && ReferenceEquals(button, onPositionUpdateButton))
{
outputType = AddOnFrameworkDisplay.onPositionUpdate;
outputBox.Text = "OnPositionUpdate() Monitor";
}
#endregion
#region Behavior for Misc Section Buttons
// When user presses the Connect Kinetick EOD button
if (button != null && ReferenceEquals(button, connectKinetickEODButton))
{
if (connection == null)
{
// Establish connection
connection = Connect("Kinetick – End Of Day (Free)");
}
else
connection.Disconnect();
}
// When user presses the Connection Info button
if (button != null && ReferenceEquals(button, connectionInfoButton))
{
outputType = AddOnFrameworkDisplay.connectionInfo;
outputBox.Text = "CONNECTION INFORMATION";
// Access information about all connected connections
lock (Connection.Connections)
Connection.Connections.ToList().ForEach(c => outputBox.AppendText(string.Format("{0}Connection: {1}{0}Provider: {2}{0}Mode: {3}{0}",
Environment.NewLine,
c.Options.Name,
c.Options.Provider,
c.Options.Mode
)));
}
// When user presses the OnConnectionStatusUpdate button
if (button != null && ReferenceEquals(button, onConnectionStatusUpdateButton))
{
outputType = AddOnFrameworkDisplay.onConnectionStatusUpdate;
outputBox.Text = "OnConnectionStatusUpdate() Monitor";
}
// When user presses the OnSimulationAccountReset button
if (button != null && ReferenceEquals(button, onSimulationAccountResetButton))
{
outputType = AddOnFrameworkDisplay.onSimulationAccountReset;
outputBox.Text = "OnSimulationAccountReset() Monitor";
}
// When user presses the OnNews button
if (button != null && ReferenceEquals(button, onNewsButton))
{
outputType = AddOnFrameworkDisplay.onNews;
outputBox.Text = "OnNews() Monitor";
}
// When user presses the atmStrategyGetMarketPositionButton button
if (button != null && ReferenceEquals(button, atmStrategyGetMarketPositionButton))
{
outputType = AddOnFrameworkDisplay.atmStrategyGetMarketPosition;
outputBox.Text = "Selected ATM Market Position: " + GetAtmStrategyMarketPosition(selectedAtmStrategy);
}
// When user presses the atmStrategyGetPositionAveragePriceButton button
if (button != null && ReferenceEquals(button, atmStrategyGetPositionAveragePriceButton))
{
outputType = AddOnFrameworkDisplay.atmStrategyGetPositionAveragePrice;
outputBox.Text = "Selected ATM Average Position Price: " + GetAtmStrategyPositionAveragePrice(selectedAtmStrategy);
}
// When user presses the atmStrategyGetPositionQuantityButton button
if (button != null && ReferenceEquals(button, atmStrategyGetPositionQuantityButton))
{
outputType = AddOnFrameworkDisplay.atmStrategyGetPositionQuantity;
outputBox.Text = "Selected ATM Average Position Quantity: " + GetAtmStrategyPositionQuantity(selectedAtmStrategy);
}
// When user presses the atmStrategyGetRealizedPnLButton button
if (button != null && ReferenceEquals(button, atmStrategyGetRealizedPnLButton))
{
outputType = AddOnFrameworkDisplay.atmStrategyGetRealizedPnL;
outputBox.Text = "Selected ATM Realized Profit Loss: " + GetAtmStrategyRealizedProfitLoss(selectedAtmStrategy);
}
// When user presses the atmStrategyGetStopTargetOrderStatusButton button
if (button != null && ReferenceEquals(button, atmStrategyGetStopTargetOrderStatusButton))
{
outputType = AddOnFrameworkDisplay.atmStrategyGetStopTargetOrderStatus;
StringBuilder text = new StringBuilder("Selected ATM Orders Status:" + Environment.NewLine);
// Fetch the exit orders from the ATM
foreach (Order exitOrder in selectedAtmStrategy.Orders)
{
string[,] orderStatus = GetAtmStrategyStopTargetOrderStatus(exitOrder.Name, selectedAtmStrategy);
if (orderStatus.Length > 0)
{
text.AppendLine(exitOrder.Name + ":");
for (int i = 0; i < orderStatus.GetLength(0); i++)
{
text.AppendLine("\tAverage fill price is " + orderStatus[i, 0].ToString());
text.AppendLine("\tFilled amount is " + orderStatus[i, 1].ToString());
text.AppendLine("\tCurrent state is " + orderStatus[i, 2].ToString());
}
}
}
outputBox.Text = text.ToString();
}
// When user presses the atmStrategyGetUnrealizedPnLButton button
if (button != null && ReferenceEquals(button, atmStrategyGetUnrealizedPnLButton))
{
outputType = AddOnFrameworkDisplay.atmStrategyGetUnrealizedPnL;
outputBox.Text = "Selected ATM Unrealized Profit Loss: " + GetAtmStrategyUnrealizedProfitLoss(selectedAtmStrategy);
}
#endregion
}
AccountItemEventArgs myArgs;
#region Methods for Account Section
// This method is fired on any change of an 'Account Value'
private void OnAccountItemUpdate(object sender, AccountItemEventArgs e)
{
try
{
myArgs = e;
// Only display messages from this method if our output box is displaying this category of messages
if (outputType != AddOnFrameworkDisplay.onAccountItemUpdate)
return;
/* Dispatcher.InvokeAsync() is needed for multi-threading considerations. When processing events outside of the UI thread, and we want to
influence the UI .InvokeAsync() allows us to do so. It can also help prevent the UI thread from locking up on long operations. */
Dispatcher.InvokeAsync(() =>
{
outputBox.AppendText(string.Format("{0}Account: {1}{0}AccountItem: {2}{0}Value: {3}",
Environment.NewLine,
e.Account.Name,
e.AccountItem,
e.Value));
});
}
catch (Exception error)
{
Dispatcher.InvokeAsync(() =>
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - OnAccountItemUpdate Exception: " + error.ToString();
});
}
}
// This method is fired on any status change of any account
private void OnAccountStatusUpdate(object sender, AccountStatusEventArgs e)
{
try
{
// Only display messages from this method if our output box is displaying this category of messages
if (outputType != AddOnFrameworkDisplay.onAccountStatusUpdate)
return;
/* Dispatcher.InvokeAsync() is needed for multi-threading considerations. When processing events outside of the UI thread, and we want to
influence the UI .InvokeAsync() allows us to do so. It can also help prevent the UI thread from locking up on long operations. */
Dispatcher.InvokeAsync(() =>
{
outputBox.AppendText(string.Format("{0}Account: {1}{0}Status: {2}",
Environment.NewLine,
e.Account.Name,
e.Status));
});
}
catch (Exception error)
{
Dispatcher.InvokeAsync(() =>
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - OnAccountStatusUpdate Exception: " + error.ToString();
});
}
}
#endregion
#region Methods for Data Access Section
// This method is fired when our instrument selector changes instruments
private void OnInstrumentChanged(object sender, EventArgs e)
{
Instrument = sender as Cbi.Instrument;
}
// This method is fired when our interval selector changes intervals
private void OnIntervalChanged(object sender, BarsPeriodEventArgs args)
{
if (args.BarsPeriod == null)
return;
BarsPeriod = args.BarsPeriod;
PropagateIntervalChange(args.BarsPeriod);
}
// This method is fired no fundamental data events. Note: snapshot data provided right on subscription
private void OnFundamentalData(object sender, NinjaTrader.Data.FundamentalDataEventArgs e)
{
try
{
// Only display messages from this method if our output box is displaying this category of messages
if (outputType != AddOnFrameworkDisplay.fundamentalData)
return;
/* Dispatcher.InvokeAsync() is needed for multi-threading considerations. When processing events outside of the UI thread, and we want to
influence the UI .InvokeAsync() allows us to do so. It can also help prevent the UI thread from locking up on long operations. */
Dispatcher.InvokeAsync(() =>
{
// Depending on the type of fundamental data, the value would be stored accordingly
switch (e.FundamentalDataType)
{
#region FundamentalDataType
case FundamentalDataType.AverageDailyVolume:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.Beta:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.CalendarYearHigh:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.CalendarYearHighDate:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DateTimeValue);
break;
case FundamentalDataType.CalendarYearLow:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.CalendarYearLowDate:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DateTimeValue);
break;
case FundamentalDataType.CurrentRatio:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.DividendAmount:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.DividendPayDate:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.DividendYield:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.EarningsPerShare:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.FiveYearsGrowthPercentage:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.High52Weeks:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.High52WeeksDate:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DateTimeValue);
break;
case FundamentalDataType.HistoricalVolatility:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.Low52Weeks:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.Low52WeeksDate:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DateTimeValue);
break;
case FundamentalDataType.MarketCap:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.NextYearsEarningsPerShare:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.PercentHeldByInstitutions:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.PriceEarningsRatio:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.RevenuePerShare:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.SharesOutstanding:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.LongValue);
break;
case FundamentalDataType.ShortInterest:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.ShortInterestRatio:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
case FundamentalDataType.VWAP:
outputBox.Text = string.Format("Type: {0} Value: {1}", e.FundamentalDataType, e.DoubleValue);
break;
#endregion
}
});
}
catch (Exception error)
{
Dispatcher.InvokeAsync(() =>
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - OnFundamentalData Exception: " + error.ToString();
});
}
}
// This method is fired after the market data snapshot data is updated
private void OnMarketData(object sender, NinjaTrader.Data.MarketDataEventArgs e)
{
try
{
// Only display messages from this method if our output box is displaying this category of messages
if (outputType != AddOnFrameworkDisplay.marketData)
return;
/* Dispatcher.InvokeAsync() is needed for multi-threading considerations. When processing events outside of the UI thread, and we want to
influence the UI .InvokeAsync() allows us to do so. It can also help prevent the UI thread from locking up on long operations. */
Dispatcher.InvokeAsync(() =>
{
// Process Last type market data events
if (e.MarketDataType == MarketDataType.Last)
outputBox.Text = string.Format("MARKET DATA{0}Instrument: {1}{0}Time: {2}{0}{3} Price: {4}{0}Ask Price: {5}{0}Bid Price: {6}{0}Volume: {7}{0}",
Environment.NewLine,
e.Instrument.FullName,
e.Time,
e.MarketDataType,
e.Price,
e.Ask,
e.Bid,
e.Volume);
if (e.IsReset == true)
outputBox.Text = string.Format("MARKET DATA - Disconnected{0}IsReset: {1}",
Environment.NewLine,
e.IsReset); // Indicates that the UI should be reset. e.g. on disconnect
});
}
catch (Exception error)
{
if (outputType != AddOnFrameworkDisplay.marketData)
return;
Dispatcher.InvokeAsync(() =>
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - OnMarketData Exception: " + error.ToString();
});
}
}
// This method is fired after the market depth snapshot data is updated
private void OnMarketDepth(object sender, NinjaTrader.Data.MarketDepthEventArgs e)
{
try
{
if (outputType == AddOnFrameworkDisplay.marketDepthAsk)
{
/* Dispatcher.InvokeAsync() is needed for multi-threading considerations. When processing events outside of the UI thread, and we want to
influence the UI .InvokeAsync() allows us to do so. It can also help prevent the UI thread from locking up on long operations. */
Dispatcher.InvokeAsync(() =>
{
if (e.IsReset == true)
{
outputBox.Text = string.Format("ASK LADDER - Disconnected{0}IsReset: {1}",
Environment.NewLine,
e.IsReset); // Indicates that the UI should be reset. e.g. on disconnect
return;
}
outputBox.Text = "ASK LADDER";
// iterate through ASK side of the price ladded
for (int i = 0; i < marketDepth.Asks.Count; i++)
outputBox.AppendText(string.Format("{0}Position: {2}{1}Price: {3}{1}Volume: {4}",
Environment.NewLine,
"\t",
i,
marketDepth.Asks[i].Price,
marketDepth.Asks[i].Volume));
});
}
else if (outputType == AddOnFrameworkDisplay.marketDepthBid)
{
/* Dispatcher.InvokeAsync() is needed for multi-threading considerations. When processing events outside of the UI thread, and we want to
influence the UI .InvokeAsync() allows us to do so. It can also help prevent the UI thread from locking up on long operations. */
Dispatcher.InvokeAsync(() =>
{
if (e.IsReset == true)
{
outputBox.Text = string.Format("BID LADDER - Disconnected{0}IsReset: {1}",
Environment.NewLine,
e.IsReset); // Indicates that the UI should be reset. e.g. on disconnect
return;
}
outputBox.Text = "BID LADDER";
// iterate through ASK side of the price ladded
for (int i = 0; i < marketDepth.Bids.Count; i++)
outputBox.AppendText(string.Format("{0}Position: {2}{1}Price: {3}{1}Volume: {4}",
Environment.NewLine,
"\t",
i,
marketDepth.Bids[i].Price,
marketDepth.Bids[i].Volume));
});
}
else
return;
}
catch (Exception error)
{
if (outputType == AddOnFrameworkDisplay.marketDepthAsk || outputType == AddOnFrameworkDisplay.marketDepthBid)
{
Dispatcher.InvokeAsync(() =>
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - OnMarketDepth Exception: " + error.ToString();
});
}
else
return;
}
}
// This method is fired on real-time bar events
private void OnBarUpdate(object sender, NinjaTrader.Data.BarsUpdateEventArgs e)
{
try
{
/* Modify profit target order on latest price update if limit price is more than 14 ticks away from current price
Note: If a single tick updates several bars, this modification only occurs once on the latest bar */
if (profitTarget != null)
{
// If profit target is now 14 ticks away from the close price, modify the profit target down
if (profitTarget.LimitPrice > e.BarsSeries.GetClose(e.MaxIndex) + 14 * Instrument.MasterInstrument.TickSize)
{
/* For modifying orders use the follow:
.LimitPriceChanged for limit price changes
.StopPriceChanged for stop price changes
.QuantityChanged for quantity changes */
profitTarget.LimitPriceChanged = e.BarsSeries.GetClose(e.MaxIndex) + 4 * Instrument.MasterInstrument.TickSize;
profitTarget.Account.Change(new[] { profitTarget });
// If you wanted to just cancel the order you could use account.Cancel()
// profitTarget.Account.Cancel(new[] { profitTarget });
}
}
// Only display messages from this method if our output box is displaying this category of messages
if (outputType != AddOnFrameworkDisplay.realtimeData)
return;
/* Dispatcher.InvokeAsync() is needed for multi-threading considerations. When processing events outside of the UI thread, and we want to
influence the UI .InvokeAsync() allows us to do so. It can also help prevent the UI thread from locking up on long operations. */
Dispatcher.InvokeAsync(() =>
{
/* Depending on the BarsPeriod type of your barsRequest you can have situations where more than one bar is updated by a single tick
Be sure to process the full range of updated bars to ensure you did not miss a bar. */
// Option 1. If you want to process updated bars on each tick
for (int i = e.MinIndex; i <= e.MaxIndex; i++)
{
// Processing every single tick
outputBox.Text = string.Format("REALTIME BARS{0}Time: {1}{0}Open: {2}{0}High: {3}{0}Low: {4}{0}Close: {5}",
Environment.NewLine,
e.BarsSeries.GetTime(i),
e.BarsSeries.GetOpen(i),
e.BarsSeries.GetHigh(i),
e.BarsSeries.GetLow(i),
e.BarsSeries.GetClose(i));
}
#region Option 2. If you want to process bars at the close of each bar
/* if (barCount == -1)
barCount = e.MinIndex;
if (e.MaxIndex == barCount)
return;
else if (e.MaxIndex > barCount)
{
for (int i = e.MinIndex; i <= e.MaxIndex; i++)
{
// Processing the newly finished bars.
// Note: We only know a bar closed when a new bar forms. This means we need to decrement our index by 1
// otherwise we would be accessing the newly formed bar instead of the finished bar.
outputBox.Text = string.Format("REALTIME BARS (On bar close){0}Time: {1}{0}Open: {2}{0}High: {3}{0}Low: {4}{0}Close: {5}",
Environment.NewLine,
e.BarsSeries.GetTime(i - 1),
e.BarsSeries.GetOpen(i - 1),
e.BarsSeries.GetHigh(i - 1),
e.BarsSeries.GetLow(i - 1),
e.BarsSeries.GetClose(i - 1));
}
barCount = e.MaxIndex;
} */
#endregion
});
}
catch (Exception error)
{
Dispatcher.InvokeAsync(() =>
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - OnBarUpdate Exception: " + error.ToString();
});
}
}
// Get bars. This is also where we subscribe to real-time bar events.
private NinjaTrader.Data.BarsRequest DoBarsRequest(Instrument instrument, int lookBackPeriod, bool subscribeRealTimeData)
{
try
{
NinjaTrader.Data.BarsRequest barsRequest;
// Request x number of days back of data.
if (instrument != null)
{
barsRequest = new NinjaTrader.Data.BarsRequest(instrument, DateTime.Now.AddDays(-lookBackPeriod), DateTime.Now);
// If you wish to request x number of bars back instead you can use this signature:
// barsRequest = new NinjaTrader.Data.BarsRequest(instrument, lookBackPeriod);
}
else
{
outputBox.Text = "Error: Invalid instrument";
return null;
}
// Parameterize your request. We determine the interval via the selection from our interval selector.
barsRequest.BarsPeriod = new NinjaTrader.Data.BarsPeriod { BarsPeriodType = intervalSelector.Interval.BarsPeriodType, Value = intervalSelector.Interval.Value };
barsRequest.TradingHours = NinjaTrader.Data.TradingHours.Get("Default 24 x 7");
// barsRequest.IsDividendAdjusted = true;
// barsRequest.IsResetOnNewTradingDay = false;
// barsRequest.IsSplitAdjusted = true;
// barsRequest.LookupPolicy = LookupPolicies.Provider;
// barsRequest.MergePolicy = MergePolicy.DoNotMerge;
// Attach event handler for real-time events if you want to process real-time data
if (subscribeRealTimeData)
{
barsRequest.Update += OnBarUpdate;
barsRequestSubscribed = true;
}
// Request the bars
barsRequest.Request(new Action<NinjaTrader.Data.BarsRequest, ErrorCode, string>((bars, errorCode, errorMessage) =>
{
Dispatcher.InvokeAsync(new Action(() =>
{
if (errorCode != ErrorCode.NoError)
{
// Handle any errors in requesting bars here
outputBox.Text = string.Format("Error on requesting bars: {0}, {1}", errorCode, errorMessage);
return;
}
// Output the bars if requested
if (outputType == AddOnFrameworkDisplay.requestData)
PrintBarsRequest(bars);
// If requesting real-time bars, but there are currently no connections
if (barsRequestSubscribed)
lock (Connection.Connections)
if (Connection.Connections.FirstOrDefault() == null)
outputBox.Text = string.Format("REALTIME BARS{0}Not connected.", Environment.NewLine);
}));
}));
return barsRequest;
}
catch (Exception error)
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - DoBarsRequest Exception: " + error.ToString();
return null;
}
}
// Output requested bars' OHLCV
private void PrintBarsRequest(NinjaTrader.Data.BarsRequest bars)
{
outputBox.Text = "REQUESTED BARS: " + bars.Bars.Count;
// Process returned bars here. Note: The last returned bar may be a currently in-progress bar
for (int i = 0; i < bars.Bars.Count; i++)
{
// Do whatever you want with the bars
outputBox.AppendText(string.Format("{0}Time: {1}{0}Open: {2}{0}High: {3}{0}Low: {4}{0}Close: {5}{0}Volume: {6}{0}",
Environment.NewLine,
bars.Bars.GetTime(i),
bars.Bars.GetOpen(i),
bars.Bars.GetHigh(i),
bars.Bars.GetLow(i),
bars.Bars.GetClose(i),
bars.Bars.GetVolume(i)));
}
}
#endregion
#region Methods for Order Section
// This method is fired when custom ATM properties are changed
private void OnAtmCustomPropertiesChanged(object sender, NinjaScript.AtmStrategy.CustomPropertiesChangedEventArgs args)
{
// Adjust our TIF and Quantity selectors to the new ATM strategy values
tifSelector.SelectedTif = args.NewTif;
qudSelector.Value = args.NewQuantity;
}
// This method is fired as the status of an order changes
private void OnOrderUpdate(object sender, OrderEventArgs e)
{
try
{
// Submit stop/target bracket orders for our framework managed entry
if (frameworkEntryOrder != null && frameworkEntryOrder == e.Order)
{
// When our entry order fills, submit our profit target and stop loss orders
if (e.OrderState == OrderState.Filled)
{
string oco = Guid.NewGuid().ToString("N");
// Dispatcher.InvokeAsync() is needed for multi-threading considerations.
Dispatcher.InvokeAsync(() =>
{
profitTarget = accountSelector.SelectedAccount.CreateOrder(Instrument, OrderAction.Sell, OrderType.Limit, TimeInForce.Day, 1, frameworkEntryOrder.AverageFillPrice + 10 * Instrument.MasterInstrument.TickSize, 0, oco, "AddOnFramework - Target", null);
stopLoss = accountSelector.SelectedAccount.CreateOrder(Instrument, OrderAction.Sell, OrderType.StopMarket, TimeInForce.Day, 1, 0, frameworkEntryOrder.AverageFillPrice - 10 * Instrument.MasterInstrument.TickSize, oco, "AddOnFramework - Stop", null);
accountSelector.SelectedAccount.Submit(new[] { profitTarget, stopLoss });
});
}
}
// Only display messages from this method if our output box is displaying this category of messages
if (outputType != AddOnFrameworkDisplay.onOrderUpdate && outputType != AddOnFrameworkDisplay.frameworkManaged)
return;
/* Dispatcher.InvokeAsync() is needed for multi-threading considerations. When processing events outside of the UI thread, and we want to
influence the UI .InvokeAsync() allows us to do so. It can also help prevent the UI thread from locking up on long operations. */
Dispatcher.InvokeAsync(() =>
{
outputBox.AppendText(string.Format("{0}Instrument: {1}{0}OrderAction: {2}{0}OrderType: {3}{0}Quantity: {4}{0}LimitPrice: {5}{0}StopPrice: {6}{0}OrderState: {7}{0}Filled: {8}{0}AverageFillPrice: {9}{0}Name: {10}{0}OCO: {11}{0}TimeInForce: {12}{0}OrderID: {13}{0}Time: {14}{0}",
Environment.NewLine,
e.Order.Instrument.FullName,
e.Order.OrderAction,
e.Order.OrderType,
e.Quantity,
e.LimitPrice,
e.StopPrice,
e.OrderState,
e.Filled,
e.AverageFillPrice,
e.Order.Name,
e.Order.Oco,
e.Order.TimeInForce,
e.OrderId,
e.Time));
});
}
catch (Exception error)
{
Dispatcher.InvokeAsync(() =>
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - OnOrderUpdate Exception: " + error.ToString();
});
}
}
// This method is fired as new executions come in, an existing execution is amended (e.g. by the broker's back office), or an execution is removed (e.g. by the broker's back office)
private void OnExecutionUpdate(object sender, ExecutionEventArgs e)
{
try
{
// Only display messages from this method if our output box is displaying this category of messages
if (outputType != AddOnFrameworkDisplay.onExecutionUpdate && outputType != AddOnFrameworkDisplay.buyMarket && outputType != AddOnFrameworkDisplay.sellMarket)
return;
/* Dispatcher.InvokeAsync() is needed for multi-threading considerations. When processing events outside of the UI thread, and we want to
influence the UI .InvokeAsync() allows us to do so. It can also help prevent the UI thread from locking up on long operations. */
Dispatcher.InvokeAsync(() =>
{
outputBox.AppendText(string.Format("{0}Instrument: {1}{0}Quantity: {2}{0}Price: {3}{0}Time: {4}{0}ExecutionID: {5}{0}Exchange: {6}{0}MarketPosition: {7}{0}Operation: {8}{0}OrderID: {9}{0}Name: {10}{0}Commission: {11}{0}Rate: {12}{0}",
Environment.NewLine,
e.Execution.Instrument.FullName,
e.Quantity,
e.Price,
e.Time,
e.ExecutionId,
e.Exchange,
e.MarketPosition,
e.Operation,
e.OrderId,
e.Execution.Name,
e.Execution.Commission,
e.Execution.Rate));
});
}
catch (Exception error)
{
Dispatcher.InvokeAsync(() =>
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - OnExecutionUpdate Exception: " + error.ToString();
});
}
}
// This method is fired as a position changes
private void OnPositionUpdate(object sender, PositionEventArgs e)
{
try
{
// Only display messages from this method if our output box is displaying this category of messages
if (outputType != AddOnFrameworkDisplay.onPositionUpdate)
return;
/* Dispatcher.InvokeAsync() is needed for multi-threading considerations. When processing events outside of the UI thread, and we want to
influence the UI .InvokeAsync() allows us to do so. It can also help prevent the UI thread from locking up on long operations. */
// Dispatcher.InvokeAsync(() =>
// {
// outputBox.AppendText(string.Format("{0}Instrument: {1}{0}MarketPosition: {2}{0}Quantity: {3}{0}AveragePrice: {4}{0}Operation: {5}{0}UnrealizedPnL: {6}{0}Connection: {7}{0}",
// Environment.NewLine,
// e.Position.Instrument.FullName,
// e.MarketPosition,
// e.Quantity,
// e.AveragePrice,
// e.Operation,
// (e.Position.Instrument.MarketData.Last != null ? e.Position.GetProfitLoss(e.Position.Instrument.MarketData.Last.Price, PerformanceUnit.Currency).ToString() : "(unknown)"),
// accountSelector.SelectedAccount.Connection.Options.Name));
// });
}
catch (Exception error)
{
Dispatcher.InvokeAsync(() =>
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - OnPositionUpdate Exception: " + error.ToString();
});
}
}
#endregion
#region Methods for Misc Section
// Connect to a connection
private Connection Connect(string connectionName)
{
try
{
ConnectOptions connectOptions = null;
List<ConnectOptions> connectionOptionList = null;
// copy list of connection options
lock (Core.Globals.ConnectOptions)
connectionOptionList = Core.Globals.ConnectOptions.ToList();
// Get the configured account connection
connectOptions = connectionOptionList.FirstOrDefault(o => o.Name == connectionName);
if (connectOptions == null)
{
outputType = AddOnFrameworkDisplay.connectKinetickEOD;
outputBox.Text = "Could not connect. No connection found.";
return null;
}
// If connection is not already connected, connect.
lock (Connection.Connections)
{
if (Connection.Connections.FirstOrDefault(c => c.Options.Name == connectionName) != null)
return null;
}
Connection connect = Connection.Connect(connectOptions);
// Only return connection if successfully connected
if (connect.Status == ConnectionStatus.Connected)
return connect;
else
return null;
}
catch (Exception error)
{
outputType = AddOnFrameworkDisplay.connectKinetickEOD;
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - Connect Exception: " + error.ToString();
return null;
}
}
// Helpers for information on ATM strategy
private MarketPosition GetAtmStrategyMarketPosition(NinjaTrader.NinjaScript.AtmStrategy atm)
{
if (atm == null || atm.State != State.Realtime || atm.Position == null)
return Cbi.MarketPosition.Flat;
return atm.Position.MarketPosition;
}
private double GetAtmStrategyPositionAveragePrice(NinjaTrader.NinjaScript.AtmStrategy atm)
{
if (atm == null || atm.State != State.Realtime || atm.Position == null)
return 0.0;
return atm.Position.AveragePrice;
}
private int GetAtmStrategyPositionQuantity(NinjaTrader.NinjaScript.AtmStrategy atm)
{
if (atm == null || atm.State != State.Realtime || atm.Position == null)
return 0;
return atm.Position.Quantity;
}
private double GetAtmStrategyRealizedProfitLoss(NinjaTrader.NinjaScript.AtmStrategy atm)
{
if (atm == null)
return 0.0;
lock(atm.Executions)
return Cbi.SystemPerformance.Calculate(atm.Executions).AllTrades.TradesPerformance.Currency.CumProfit;
}
private string[,] GetAtmStrategyStopTargetOrderStatus(string orderName, NinjaTrader.NinjaScript.AtmStrategy atm)
{
if (atm == null)
return new string[0, 0];
if (Regex.Match(orderName, "^stop[1-9][0-9]*$", RegexOptions.IgnoreCase).Success|| Regex.Match(orderName, "^target[1-9][0-9]*$", RegexOptions.IgnoreCase).Success)
{
int idx = 0;
string name = string.Empty;
try
{
idx = Convert.ToInt32(Regex.Replace(orderName, "[a-zA-Z]*", "")) - 1;
name = Regex.Replace(orderName, "[1-9][0-9]*", "").ToLower();
}
catch
{
return new string[0, 0];
}
if (idx > atm.Brackets.Length - 1)
return new string[0, 0];
System.Collections.ObjectModel.Collection<Cbi.Order> orders = (name == "stop" ? atm.GetStopOrders(idx) : atm.GetTargetOrders(idx));
if (orders.Count == 0)
return new string[0, 0];
string[,] ordersArray = new string[orders.Count, 3];
for (int i = 0; i < orders.Count; i++)
{
ordersArray[i, 0] = orders[i].AverageFillPrice.ToString(System.Globalization.CultureInfo.InvariantCulture);
ordersArray[i, 1] = orders[i].Filled.ToString(System.Globalization.CultureInfo.InvariantCulture);
ordersArray[i, 2] = orders[i].OrderState.ToString();
}
return ordersArray;
}
else
return new string[0, 0];
}
private double GetAtmStrategyUnrealizedProfitLoss(NinjaTrader.NinjaScript.AtmStrategy atm)
{
if (atm == null || atm.State != State.Realtime || atm.Position == null)
return 0.0;
return atm.Position.GetUnrealizedProfitLoss(Cbi.PerformanceUnit.Currency);
}
// This method is fired on connection status events
private void OnConnectionStatusUpdate(object sender, ConnectionStatusEventArgs e)
{
try
{
// For multi-threading reasons, work with a copy of the EventArgs to prevent situations where the EventArgs may already be ahead of us while in the middle processing it.
ConnectionStatusEventArgs eCopy = e;
if (eCopy.Connection.Options.Name == "Kinetick – End Of Day (Free)")
{
// Switch button to be a connect button
if (eCopy.Status == ConnectionStatus.Disconnected)
{
connection = null;
Dispatcher.InvokeAsync(() =>
{
connectKinetickEODButton.Content = "Connect Kinetick EOD";
});
}
// Switch button to be a disconnect button
else if (eCopy.Status == ConnectionStatus.Connected)
{
if (connection == null)
connection = eCopy.Connection;
Dispatcher.InvokeAsync(() =>
{
connectKinetickEODButton.Content = "Disconnect Kinetick EOD";
});
}
}
if (outputType != AddOnFrameworkDisplay.onConnectionStatusUpdate)
return;
// If connection hits an error, report it
if (eCopy.Error != ErrorCode.NoError)
{
Dispatcher.InvokeAsync(() =>
{
outputBox.AppendText(string.Format("{0}{1}. Error: {2} NativeError: {3}",
Environment.NewLine,
eCopy.Connection.Options.Name,
eCopy.Error,
eCopy.NativeError));
});
}
Dispatcher.InvokeAsync(() =>
{
outputBox.AppendText(string.Format("{0}{1}. Status: {2}",
Environment.NewLine,
eCopy.Connection.Options.Name,
eCopy.Status));
});
}
catch (Exception error)
{
Dispatcher.InvokeAsync(() =>
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - OnConnectionStatusUpdate Exception: " + error.ToString();
});
}
}
// This method is fired on sim account reset events.
// IMPORTANT: Be sure to recreate bar requests after a reset on the Playback connection
private void OnSimulationAccountReset(object sender, EventArgs e)
{
try
{
if (outputType != AddOnFrameworkDisplay.onSimulationAccountReset)
return;
Account simAccount = (sender as Account);
Dispatcher.InvokeAsync(() =>
{
outputBox.AppendText(string.Format("{0}'{1}' reset.",
Environment.NewLine,
simAccount.Name));
});
// If the account was reset due to a rewind/fast forward of the Playback connection
if (simAccount != null && simAccount.Provider == Provider.Playback)
{
// We would need to redo our bars requests here if we are using them in Playback
}
}
catch (Exception error)
{
Dispatcher.InvokeAsync(() =>
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - OnSimulationAccountReset Exception: " + error.ToString();
});
}
}
// This method is fired as new News events come in. Note: old News events are not provided as you have subscribed
private void OnNews(object sender, NinjaTrader.Data.NewsEventArgs e)
{
try
{
if (outputType != AddOnFrameworkDisplay.onNews)
return;
Dispatcher.InvokeAsync(() =>
{
outputBox.AppendText(string.Format("{0}ID: {1}{0}NewsProvider: {2}{0}Headline: {3}{0}Text: {4}{0}Time: {5}{0}URL: {6}{0}",
Environment.NewLine,
e.Id,
e.NewsProvider,
e.Headline,
e.Text,
e.Time.ToString("yyyyMMddHHmmss"),
(e.Url != null ? e.Url.AbsoluteUri : "(null)")));
});
// Maintain the news items
newsItems.Update(e);
}
catch (Exception error)
{
Dispatcher.InvokeAsync(() =>
{
// It is important to protect NinjaTrader from any unhandled exceptions that may arise from your code
outputBox.Text = "AddOnFramework - OnNews Exception: " + error.ToString();
});
}
}
#endregion
// IInstrumentProvider member. Required if you want to use the instrument link mechanism on this window.
public Cbi.Instrument Instrument
{
get { return instrument; }
set
{
// Unsubscribe to subscriptions to old instruments
if (instrument != null)
{
marketData.Update -= OnMarketData;
marketDepth.Update -= OnMarketDepth;
fundamentalData.Update -= OnFundamentalData;
marketData = null;
marketDepth = null;
fundamentalData = null;
if (barsRequest != null)
{
if (barsRequestSubscribed)
{
barsRequest.Update -= OnBarUpdate;
barsRequestSubscribed = false;
}
barsRequest = null;
}
}
// Subscribe for the new instrument
if (value != null)
{
// Create a market data snapshot and subscribe for updates
marketData = new MarketData(value);
marketData.Update += OnMarketData;
// Create a market depth snapshot and subscribe for updates
marketDepth = new MarketDepth<MarketDepthRow>(value);
marketDepth.Update += OnMarketDepth;
// Create a fundamental data snapshot and subscribe for updates
fundamentalData = new FundamentalData(value);
fundamentalData.Update += OnFundamentalData;
}
instrument = value;
if (instrumentSelector != null)
instrumentSelector.Instrument = value;
// Update the tab header name on AddOnFramework to be the same name as the new instrument
RefreshHeader();
// Send instrument to other windows linked to the same color
PropagateInstrumentChange(value);
}
}
// IIntervalProvider member. Required if you want to use the interval linker mechanism on this window. No functionality has been linked to the interval linker in this sample.
public NinjaTrader.Data.BarsPeriod BarsPeriod
{ get; set; }
// NTTabPage member. Required to determine the text for the tab header name
protected override string GetHeaderPart(string variable)
{
switch (variable)
{
case "@INSTRUMENT":
return Instrument == null ? Resource.GuiNewTab : Instrument.MasterInstrument.Name;
case "@INSTRUMENT_FULL":
return Instrument == null ? Resource.GuiNewTab : Instrument.FullName;
}
return variable;
}
// Called by TabControl when tab is being removed or window is closed
public override void Cleanup()
{
// Unsubscribe and cleanup any remaining resources we may still have open
Account.AccountStatusUpdate -= OnAccountStatusUpdate;
Account.SimulationAccountReset -= OnSimulationAccountReset;
Connection.ConnectionStatusUpdate -= OnConnectionStatusUpdate;
if (accountSelector.SelectedAccount != null)
{
accountSelector.SelectedAccount.AccountItemUpdate -= OnAccountItemUpdate;
accountSelector.SelectedAccount.ExecutionUpdate -= OnExecutionUpdate;
accountSelector.SelectedAccount.OrderUpdate -= OnOrderUpdate;
accountSelector.SelectedAccount.PositionUpdate -= OnPositionUpdate;
}
if (lastAccount != null)
lastAccount = null;
// a call to base.Cleanup() will loop through the visual tree looking for all ICleanable children
// i.e., AccountSelector, AtmStrategySelector, InstrumentSelector, IntervalSelector, TifSelector,
// as well as unregister any link control events
base.Cleanup();
if (marketData != null)
marketData.Update -= OnMarketData;
if (marketDepth != null)
marketDepth.Update -= OnMarketDepth;
if (barsRequest != null && barsRequestSubscribed)
barsRequest.Update -= OnBarUpdate;
if (fundamentalData != null)
fundamentalData.Update -= OnFundamentalData;
}
// NTTabPage member. Required for restoring elements from workspace
protected override void Restore(XElement element)
{
if (element == null)
return;
// Restore the previously selected account
XElement accountElement = element.Element("Account");
if (accountSelector != null && accountElement != null)
accountSelector.DesiredAccount = accountElement.Value;
// Restore the previously selected instrument
XElement instrumentElement = element.Element("Instrument");
if (instrumentElement != null && !string.IsNullOrEmpty(instrumentElement.Value))
Instrument = Cbi.Instrument.GetInstrument(instrumentElement.Value);
}
// NTTabPage member. Required for storing elements to workspace
protected override void Save(XElement element)
{
if (element == null)
return;
// Save the currently selected account
if (accountSelector != null && !string.IsNullOrEmpty(accountSelector.DesiredAccount))
element.Add(new XElement("Account") { Value = accountSelector.DesiredAccount });
// Save the currently selected instrument
if (Instrument != null)
element.Add(new XElement("Instrument") { Value = Instrument.FullName });
}
}
}
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:t="clr-namespace:NinjaTrader.Gui.Tools;assembly=NinjaTrader.Gui"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:AccountPerformance="clr-namespace:NinjaTrader.Gui.AccountPerformance;assembly=NinjaTrader.Gui"
xmlns:AccountData="clr-namespace:NinjaTrader.Gui.AccountData;assembly=NinjaTrader.Gui"
xmlns:AtmStrategy="clr-namespace:NinjaTrader.Gui.NinjaScript.AtmStrategy;assembly=NinjaTrader.Gui" >
<Grid Background="Transparent">
<!-- Define our layout as two columns. Left side will be all of our controls. Right side will be our output box -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="55*"/>
<ColumnDefinition Width="45*"/>
</Grid.ColumnDefinitions>
<!-- Left side controls -->
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid Background="Transparent">
<Grid.RowDefinitions>
<!-- ACCOUNT SECTION (0-4)-->
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<!-- DATA SECTION (5-9) -->
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<!-- ORDERS SECTION (10-15) -->
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<!-- MISC SECTION (16-19) -->
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- ACCOUNT SECTION -->
<TextBlock Text="Account" Style="{StaticResource FontLabel}" FontWeight="Bold">
<TextBlock.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="{StaticResource MarginBase}" Bottom="0" />
</TextBlock.Margin>
</TextBlock>
<t:AccountSelector x:Name="accountSelector" Grid.Row="1">
<t:AccountSelector.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource PaddingColumn}" Bottom="0" Right="{StaticResource MarginBase}" />
</t:AccountSelector.Margin>
</t:AccountSelector>
<!-- Buttons for account values/executions/orders -->
<Button x:Name="acctValuesButton" Grid.Row="2" Grid.Column="0" Content="Account Values">
<Button.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="0" Bottom="0" />
</Button.Margin>
</Button>
<Button x:Name="acctExecButton" Grid.Row="2" Grid.Column="1" Content="Account Executions">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<Button x:Name="acctOrdersButton" Grid.Row="2" Grid.Column="2" Content="Account Orders">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Right="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<!-- Buttons for account positions/strategies -->
<Button x:Name="acctPosButton" Grid.Row="3" Grid.Column="0" Content="Account Positions">
<Button.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="0" Bottom="0" />
</Button.Margin>
</Button>
<Button x:Name="acctStratButton" Grid.Row="3" Grid.Column="1" Content="Account Strategies">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<!-- Buttons for OnAccountItem/StatusUpdate -->
<Button x:Name="onAccountItemUpdateButton" Grid.Row="4" Grid.Column="0" Content="OnAccountItemUpdate">
<Button.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="0" Bottom="{StaticResource MarginBase}" />
</Button.Margin>
</Button>
<Button x:Name="onAccountStatusUpdateButton" Grid.Row="4" Grid.Column="1" Content="OnAccountStatusUpdate">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Right="0" Bottom="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<!-- DATA SECTION -->
<TextBlock Text="Data Access" Style="{StaticResource FontLabel}" FontWeight="Bold" Grid.Row="5" Grid.Column="0">
<TextBlock.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="{StaticResource MarginBase}" Bottom="0" />
</TextBlock.Margin>
</TextBlock>
<!-- Instrument Selector/Interval Selector/DaysBack -->
<t:InstrumentSelector x:Name="instrumentSelector" Grid.Row="6" Grid.Column="0" LastUsedGroup="AddOnFramework">
<t:InstrumentSelector.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource PaddingColumn}" Bottom="0"/>
</t:InstrumentSelector.Margin>
</t:InstrumentSelector>
<Grid Background="Transparent" Grid.Row="6" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<t:IntervalSelector x:Name="intervalSelector" Grid.Column="0" HorizontalAlignment="Left">
<t:IntervalSelector.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource PaddingColumn}" Bottom="0"/>
</t:IntervalSelector.Margin>
</t:IntervalSelector>
<TextBlock Text="Days" Style="{StaticResource FontLabel}" Grid.Column="1" HorizontalAlignment="Right">
<TextBlock.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource PaddingColumn}"/>
</TextBlock.Margin>
</TextBlock>
<t:NumericTextBox x:Name="daysBackSelector" Text="5" ValueType="{x:Type system:Int32}" Width="50" Grid.Column="2">
<t:NumericTextBox.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource PaddingColumn}" Right="{StaticResource MarginBase}"/>
</t:NumericTextBox.Margin>
</t:NumericTextBox>
</Grid>
<!-- Request Data Buttons -->
<Button x:Name="requestDataButton" Grid.Row="7" Grid.Column="0" Content="Request Data">
<Button.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="0" Bottom="0" />
</Button.Margin>
</Button>
<Button x:Name="realtimeDataButton" Grid.Row="7" Grid.Column="1" Content="Real-Time Data">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<Button x:Name="fundamentalDataButton" Grid.Row="7" Grid.Column="2" Content="Fundamental Data">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Right="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<!-- Market Data -->
<TextBlock Text="Market Data" Style="{StaticResource FontLabel}" HorizontalAlignment="Right" Grid.Row="8" Grid.Column="0">
<TextBlock.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Bottom="0" />
</TextBlock.Margin>
</TextBlock>
<Button x:Name="marketDataButton" Grid.Row="8" Grid.Column="1" Content="Subscription">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Right="0" Bottom="0" />
</Button.Margin>
</Button>
<Button x:Name="marketDataSnapshotButton" Grid.Row="8" Grid.Column="2" Content="Snapshot">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Right="{StaticResource MarginBase}" Bottom="0" />
</Button.Margin>
</Button>
<!-- Market Depth -->
<TextBlock Text="Market Depth" Style="{StaticResource FontLabel}" HorizontalAlignment="Right" Grid.Row="9" Grid.Column="0">
<TextBlock.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Bottom="0" />
</TextBlock.Margin>
</TextBlock>
<Button x:Name="marketDepthAskButton" Grid.Row="9" Grid.Column="1" Content="Ask Ladder">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Right="0" Bottom="0" />
</Button.Margin>
</Button>
<Button x:Name="marketDepthBidButton" Grid.Row="9" Grid.Column="2" Content="Bid Ladder">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Right="{StaticResource MarginBase}" Bottom="0" />
</Button.Margin>
</Button>
<!-- ORDERS SECTION -->
<!-- section label -->
<TextBlock Text="Orders" Style="{StaticResource FontLabel}" FontWeight="Bold" Grid.Row="10" Grid.Column="0">
<TextBlock.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="{StaticResource MarginBase}" Bottom="0" />
</TextBlock.Margin>
</TextBlock>
<!-- Controls label -->
<TextBlock Text="Quantity" Style="{StaticResource FontLabel}" Grid.Row="11" Grid.Column="0">
<TextBlock.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Bottom="0" />
</TextBlock.Margin>
</TextBlock>
<TextBlock Text="TIF" Style="{StaticResource FontLabel}" Grid.Row="11" Grid.Column="1">
<TextBlock.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Bottom="0" />
</TextBlock.Margin>
</TextBlock>
<TextBlock Text="ATM Strategy" Style="{StaticResource FontLabel}" Grid.Row="11" Grid.Column="2">
<TextBlock.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Right="{StaticResource MarginBase}" Bottom="0" />
</TextBlock.Margin>
</TextBlock>
<!-- Quantity/TIF/ATM selectors -->
<t:QuantityUpDown x:Name="qudSelector" Value="1" Grid.Row="12" Grid.Column="0">
<t:QuantityUpDown.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginControl}" Bottom="0" />
</t:QuantityUpDown.Margin>
</t:QuantityUpDown>
<t:TifSelector x:Name="tifSelector" Grid.Row="12" Grid.Column="1">
<t:TifSelector.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginControl}" Right="0" Bottom="0" />
</t:TifSelector.Margin>
</t:TifSelector>
<AtmStrategy:AtmStrategySelector x:Name="atmStrategySelector" LinkedQuantity="{Binding ElementName=qudSelector, Path=Value, Mode=OneWay}" Grid.Row="12" Grid.Column="2">
<AtmStrategy:AtmStrategySelector.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginControl}" Right="{StaticResource MarginBase}" Bottom="0" />
</AtmStrategy:AtmStrategySelector.Margin>
</AtmStrategy:AtmStrategySelector>
<!-- Order Buttons -->
<Button x:Name="buyMarketButton" Grid.Row="13" Grid.Column="0" Content="Buy Market">
<Button.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="0" Bottom="0" />
</Button.Margin>
</Button>
<Button x:Name="sellMarketButton" Grid.Row="13" Grid.Column="1" Content="Sell Market">
<Button.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="0" Bottom="0" />
</Button.Margin>
</Button>
<Button x:Name="frameworkManagedButton" Grid.Row="13" Grid.Column="2" Content="Framework Managed">
<Button.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="{StaticResource MarginBase}" Bottom="0" />
</Button.Margin>
</Button>
<!-- Order Events Buttons-->
<Button x:Name="onOrderUpdateButton" Grid.Row="14" Grid.Column="0" Content="OnOrderUpdate">
<Button.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="0" Bottom="0" />
</Button.Margin>
</Button>
<Button x:Name="onExecutionUpdateButton" Grid.Row="14" Grid.Column="1" Content="OnExecutionUpdate">
<Button.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="0" Bottom="0" />
</Button.Margin>
</Button>
<Button x:Name="onPositionUpdateButton" Grid.Row="14" Grid.Column="2" Content="OnPositionUpdate">
<Button.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="{StaticResource MarginBase}" Bottom="0" />
</Button.Margin>
</Button>
<!-- Order grid -->
<AccountData:OrderGrid BorderBrush="{StaticResource BorderThinBrush}" Height="160" VerticalAlignment="Top" BorderThickness="1" Grid.Row="15" Grid.ColumnSpan="4">
<AccountData:OrderGrid.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="{StaticResource MarginBase}" Bottom="0" />
</AccountData:OrderGrid.Margin>
</AccountData:OrderGrid>
<!-- MISC SECTION -->
<TextBlock Text="Misc." Style="{StaticResource FontLabel}" FontWeight="Bold" Grid.Row="16" Grid.Column="0">
<TextBlock.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Bottom="0" Right="{StaticResource MarginBase}" />
</TextBlock.Margin>
</TextBlock>
<Button x:Name="connectKinetickEODButton" Grid.Row="17" Grid.Column="0" Content="Connect Kinetick EOD">
<Button.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Right="0" Bottom="0" />
</Button.Margin>
</Button>
<Button x:Name="connectionInfoButton" Grid.Row="17" Grid.Column="1" Content="Connection Info">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<Button x:Name="onConnectionStatusUpdateButton" Grid.Row="17" Grid.Column="2" Content="OnConnectionStatus">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Right="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<Button x:Name="onSimulationAccountResetButton" Grid.Row="18" Grid.Column="0" Content="OnSimulationAccountReset">
<Button.Margin>
<Thickness Left="{StaticResource MarginBase}" Top="{StaticResource MarginBase}" Bottom="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<Button x:Name="onNewsButton" Grid.Row="18" Grid.Column="1" Content="OnNews">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Bottom="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<Button x:Name="atmStrategyGetMarketPositionButton" Grid.Row="19" Grid.Column="0" Content="Get ATM Market Position">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Bottom="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<Button x:Name="atmStrategyGetPositionAveragePriceButton" Grid.Row="19" Grid.Column="1" Content="Get ATM Position Average Price">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Bottom="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<Button x:Name="atmStrategyGetPositionQuantityButton" Grid.Row="19" Grid.Column="2" Content="Get ATM Position Quantity">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Bottom="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<Button x:Name="atmStrategyGetRealizedPnLButton" Grid.Row="20" Grid.Column="0" Content="Get ATM Realized PnL">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Bottom="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<Button x:Name="atmStrategyGetStopTargetOrderStatusButton" Grid.Row="20" Grid.Column="1" Content="Get ATM Stop Target Order Status">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Bottom="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
<Button x:Name="atmStrategyGetUnrealizedPnLButton" Grid.Row="20" Grid.Column="2" Content="Get ATM Unrealized PnL">
<Button.Margin>
<Thickness Left="{StaticResource MarginButtonLeft}" Top="{StaticResource MarginBase}" Bottom="{StaticResource MarginBase}"/>
</Button.Margin>
</Button>
</Grid>
</ScrollViewer>
<!-- Right side output box -->
<TextBox x:Name="outputBox" Margin="{Binding Source={StaticResource MarginBase}}" Grid.Column="1" IsReadOnly="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"/>
</Grid>
</Page>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment