ECN2 PowerScript Guidesupport.ecn2.com/powerscript/PowerScriptintro.pdf · PowerScript Engine is a...
-
Upload
phungnguyet -
Category
Documents
-
view
217 -
download
0
Transcript of ECN2 PowerScript Guidesupport.ecn2.com/powerscript/PowerScriptintro.pdf · PowerScript Engine is a...
ECN2
PowerScript™ Introduction to ECN2 Trader™
Scripting Engine
Getting Started with
ECN2Trader Scripting
A practice explanation on ECN2 Trader
PowerScript Engine.
Document Version 1.1
Release Date 2011/10/28
ECN2 Trader Min Version 2.1.87
ECN2 TECHNOLOGIES LLC
Sommario
Getting Started with ECN2Trader Scripting ....................................................................................................... 0
Introduction, What’s ECN2 PowerScript ........................................................................................................... 2
How to begin.. The Script Class ......................................................................................................................... 2
Build a Script in Practice .................................................................................................................................. 11
How to improve indicators start .............................................................................................................. 17
The Market Object ........................................................................................................................................... 25
The Orders Object ........................................................................................................................................... 26
Introduction, What’s ECN2 PowerScript
PowerScript Engine is a set of classes and constructs that allow third parties build strategies and
indicators on ECN2 Trader™ Platform. The PowerScript Engine act as a bridge between user code, written in
VB .Net or C#, and the Trading Platform.
Using the built in PathScript Editor you can easily build and compile any kind of script and include it
in your Platform Scripts List.
How to begin.. The Script Class
The create a new script open PathScript Editor from The platform menu (fig 1), in the editor click on
File->New and then choice the scripting language you want to use, the editor will open a new file formatted
in the selected language and will also add required methods to accomplish the PowerScript Layout. The
result will show as in fig 3.
Fig 1:
Fig 2:
Fig 3
The script source code is a part of a bigger class that provide a set of objects and methods to access
Chart properties, Orders Book and Market Data. The main script class provide the following fields:
Objects
1) Chart refer the chart owning the script.
2) Market refer Market price data.
3) Orders refer the Order container and order commands.
4) TA (shared methods)
Fields
1) InitialBars the minimum bars to enable trade in backtest. Initially set to 50.
Methods
1) SetTimer(milliseconds)
2) StartTimer()
3) StopTimer()
4) Log(string message, MessageType type)
Note: the use of the timer is normally not needed. The script will be advised of each change in the
market invoking the methods onPriceChanged and onOrderChanged.
Classes
1) ECN2Market
2) ECN2Orders
3) ECN2Chart
4) TA
Any Sub Class contain nested types to access underlying data. Here is a schema of classes and
method hierarchy:
ECN2Chart
Events
ChartMouseAction
ChartObjectMouseAction
Properties
BarColor
CandleBearBorderColor
CandleBearColor
CandleBullBorderColor
CandleBullColor
CandlesBorder
ChartBackColorBottom
ChartBackColorTop
ChartDarvasBoxesShow
ChartDarvasStopPercent
ChartFont
ChartForeColor
ChartGridXColor
ChartGridXShow
ChartGridXStyle
ChartGridYColor
ChartGridYShow
ChartGridYStyle
ChartHorizontalSeparators
ChartHorizontalSeparatorsColor
ChartLineColor
ChartPriceScaleAlignment
ChartPriceStyle
ChartSelection
ChartThreeDStyle
ChartWorkspaceLeft
ChartWorkspaceRight
Close
CloseArray
CrossShow
CutomBarsCount
FirstVisibleRecord
Height
High
HighArray
InfoPanelBottom
InfoPanelColorTop
InfoPanelShow
InvokeRequired
LastVisibleRecord
Low
LowArray
ObjectsCount
Open
OpenArray
PanelsCount
PanelY1
PanelY2
SeriesCount
SeriesLineUseUpDownColors
SeriesVolumeUseUpDownColors
TotalBars
ValuePanelColor
Volume
VolumeArray
VolumeBarWeight
VolumeBearColor
VolumeBullColor
VolumeUnchangedColor
Width
Methods
AddLayer
AddObject
AddPanel
AddSeries
CrossOverRecord
CrossOverValue
Dispose (+ 1 overload)
GetChartDate
GetChartRecord
GetLayer
GetObject
GetSeries
GetValueByY
GetXFromIndex
GetYFromValue
RefreshCandles
RemoveAllObjectes
RemoveLayer
RemoveObject
RemovePanel
RemoveSeries
ResetBarColors
Nested Types
PriceScaleType Enum
Right
Both
Left
PriceStyle Enum
Default
PointAndFigure
Renko
Kagi
ThreeLineBreak
EquiVolume
EquiVolumeShadow
CandleVolume
HeikinAshi
BarChart
CandleChart
LineChart
MountainChart
VolumeChart
StaticPosition Enum
None
UpperLeft
BottomLeft
MiddleLeft
UpperCentre
BottomCentre
MiddleCentre
UpperRight
BottomRight
MiddleRight
ZOrder Enum
Back
Front
SeriesType Enum
Line
Mountain
Histogram
ObjectType Enum
Text
BuySymbol
SellSymbol
ExitSymbol
CustomBitmap
LineStyle Enum
Solid
Dash
Dot
DashDot
DashDotDot
ChartSeries Structure
Fields
LineColor
LineStyle
LineWidth
SeriesName
SeriesPanel
SeriesType
Methods
EditRecord
GetRecord
RefreshProperties
Update
ChartLayer Class
Fields
Alignment
Alpha
Properties
Height
Width
Methods
New
Update
Events
LayerPaint
ChartObject Structure
Fields
BitmapFilePath
Date1
Date2
ExtraParams
LineColor
LineStyle
LineWidth
ObjectType
PanelIndex
Selectable
StaticPosition
Text
Value1
Value2
ZOrder
Methods
RefreshProperties
Update
ECN2Orders
Properties
HistoryOrders (indexed)
OpenOrders (indexed)
PendingOrders (indexed)
PositionOpenPL indexed)
PositionRate indexed)
PositionRealizedPL (indexed)
PositionSize (indexed)
DatafeedName
Methods
GetOrderByClientID
GetOrderByDatafeedID
GetOrderDraft
SendCoverPosition
SendFlatten
SendReversePosition
GetDatafeedsList
ChangeDatafeed
SendMassOrderStatusRequest
SendNewOrder
SendOCOOrder
SendOrderCancel
SendOrderCancelReplace
SendOrderStatusRequest
ECN2Market
Properties
PriceDetail (indexed)
SessionVolumes (indexed)
SymbolDetails (indexed)
TicksHistory (indexed)
DatafeedName
Methods
RequestHistory
GetDatafeedsList
ChangeDatafeed
TA
Shared Methods
AD Chaikin A/D Line
ADOSC Chaikin A/D Oscillator
ADX Average Directional Movement Index
ADXR Average Directional Movement Index Rating
APO Absolute Price Oscillator
AROON Aroon
AROONOSC Aroon Oscillator
ATR Average True Range
AVGPRICE Average Price
BBANDS Bollinger Bands
BETA Beta
BOP Balance Of Power
CCI Commodity Channel Index
CDL2CROWS Two Crows
CDL3BLACKCROWS Three Black Crows
CDL3INSIDE Three Inside Up/Down
CDL3LINESTRIKE Three-Line Strike
CDL3OUTSIDE Three Outside Up/Down
CDL3STARSINSOUTH Three Stars In The South
CDL3WHITESOLDIERS Three Advancing White Soldiers
CDLABANDONEDBABY Abandoned Baby
CDLADVANCEBLOCK Advance Block
CDLBELTHOLD Belt-hold
CDLBREAKAWAY Breakaway
CDLCLOSINGMARUBOZU Closing Marubozu
CDLCONCEALBABYSWALL Concealing Baby Swallow
CDLCOUNTERATTACK Counterattack
CDLDARKCLOUDCOVER Dark Cloud Cover
CDLDOJI Doji
CDLDOJISTAR Doji Star
CDLDRAGONFLYDOJI Dragonfly Doji
CDLENGULFING Engulfing Pattern
CDLEVENINGDOJISTAR Evening Doji Star
CDLEVENINGSTAR Evening Star
CDLGAPSIDESIDEWHITE Up/Down-gap side-by-side white lines
CDLGRAVESTONEDOJI Gravestone Doji
CDLHAMMER Hammer
CDLHANGINGMAN Hanging Man
CDLHARAMI Harami Pattern
CDLHARAMICROSS Harami Cross Pattern
CDLHIGHWAVE High-Wave Candle
CDLHIKKAKE Hikkake Pattern
CDLHIKKAKEMOD Modified Hikkake Pattern
CDLHOMINGPIGEON Homing Pigeon
CDLIDENTICAL3CROWS Identical Three Crows
CDLINNECK In-Neck Pattern
CDLINVERTEDHAMMER Inverted Hammer
CDLKICKING Kicking
CDLKICKINGBYLENGTH Kicking - bull/bear determined by the longer
marubozu
CDLLADDERBOTTOM Ladder Bottom
CDLLONGLEGGEDDOJI Long Legged Doji
CDLLONGLINE Long Line Candle
CDLMARUBOZU Marubozu
CDLMATCHINGLOW Matching Low
CDLMATHOLD Mat Hold
CDLMORNINGDOJISTAR Morning Doji Star
CDLMORNINGSTAR Morning Star
CDLONNECK On-Neck Pattern
CDLPIERCING Piercing Pattern
CDLRICKSHAWMAN Rickshaw Man
CDLRISEFALL3METHODS Rising/Falling Three Methods
CDLSEPARATINGLINES Separating Lines
CDLSHOOTINGSTAR Shooting Star
CDLSHORTLINE Short Line Candle
CDLSPINNINGTOP Spinning Top
CDLSTALLEDPATTERN Stalled Pattern
CDLSTICKSANDWICH Stick Sandwich
CDLTAKURI Takuri (Dragonfly Doji with very long lower shadow)
CDLTASUKIGAP Tasuki Gap
CDLTHRUSTING Thrusting Pattern
CDLTRISTAR Tristar Pattern
CDLUNIQUE3RIVER Unique 3 River
CDLUPSIDEGAP2CROWS Upside Gap Two Crows
CDLXSIDEGAP3METHODS Upside/Downside Gap Three Methods
CMO Chande Momentum Oscillator
CORREL Pearson's Correlation Coefficient (r)
DEMA Double Exponential Moving Average
DX Directional Movement Index
EMA Exponential Moving Average
HT_DCPERIOD Hilbert Transform - Dominant Cycle Period
HT_DCPHASE Hilbert Transform - Dominant Cycle Phase
HT_PHASOR Hilbert Transform - Phasor Components
HT_SINE Hilbert Transform - SineWave
HT_TRENDLINE Hilbert Transform - Instantaneous Trendline
HT_TRENDMODE Hilbert Transform - Trend vs Cycle Mode
KAMA Kaufman Adaptive Moving Average
LINEARREG Linear Regression
LINEARREG_ANGLE Linear Regression Angle
LINEARREG_INTERCEPT Linear Regression Intercept
LINEARREG_SLOPE Linear Regression Slope
MA All Moving Average
MACD Moving Average Convergence/Divergence
MACDEXT MACD with controllable MA type
MACDFIX Moving Average Convergence/Divergence Fix 12/26
MAMA MESA Adaptive Moving Average
MAX Highest value over a specified period
MAXINDEX Index of highest value over a specified period
MEDPRICE Median Price
MFI Money Flow Index
MIDPOINT MidPoint over period
MIDPRICE Midpoint Price over period
MIN Lowest value over a specified period
MININDEX Index of lowest value over a specified period
MINMAX Lowest and highest values over a specified period
MINMAXINDEX Indexes of lowest and highest values over a specified
period
MINUS_DI Minus Directional Indicator
MINUS_DM Minus Directional Movement
MOM Momentum
NATR Normalized Average True Range
OBV On Balance Volume
PLUS_DI Plus Directional Indicator
PLUS_DM Plus Directional Movement
PPO Percentage Price Oscillator
ROC Rate of change : ((price/prevPrice)-1)*100
ROCP Rate of change Percentage: (price-prevPrice)/prevPrice
ROCR Rate of change ratio: (price/prevPrice)
ROCR100 Rate of change ratio 100 scale: (price/prevPrice)*100
RSI Relative Strength Index
SAR Parabolic SAR
SAREXT Parabolic SAR - Extended
SMA Simple Moving Average
STDDEV Standard Deviation
STOCH Stochastic
STOCHF Stochastic Fast
STOCHRSI Stochastic Relative Strength Index
SUM Summation
T3 Triple Exponential Moving Average (T3)
TEMA Triple Exponential Moving Average
TRANGE True Range
TRIMA Triangular Moving Average
TRIX 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA
TSF Time Series Forecast
TYPPRICE Typical Price
ULTOSC Ultimate Oscillator
VAR Variance
WCLPRICE Weighted Close Price
WILLR Williams' %R
WMA Weighted Moving Average
Build a Script in Practice
In this section we will create a Bollinger bands indicator and strategy in the same script. The user
will be able to avoid orders send from the platform scripts menu.
Step 1 – Create a new file:
Now we need to add our own variables to the script and make an initialization routine that add
panels and series to chart and load data in it for the already shown bars. The Init() method serve the scope,
we will do this job writing our code inside it. In this example we will create a set of public properties to
allow users to change some script setting.
Step 2 – Members and initialization
#region Members int BBTIndex = -1; int BBMIndex = -1; int BBBIndex = -1; int m_Period = 14; double m_EntryLots = 1, m_LastPrice, m_StandardDev = 2; DateTime m_LastSignal; TA.MAType m_SMAType = TA.MAType.Sma; System.Drawing.Color BBTColor = System.Drawing.Color.Green; System.Drawing.Color BBMColor = System.Drawing.Color.Red; System.Drawing.Color BBBColor = System.Drawing.Color.Blue; ECN2Chart.LineStyle BBTStyle = ECN2Chart.LineStyle.Solid; ECN2Chart.LineStyle BBMStyle = ECN2Chart.LineStyle.Solid; ECN2Chart.LineStyle BBBStyle = ECN2Chart.LineStyle.Solid; #endregion #region External Properties public int Period { get { return m_Period; } set { m_Period = value; }} public TA.MAType SMAType { get { return m_SMAType; } set { m_SMAType = value; }} public double StandardDev { get { return m_StandardDev; } set { m_StandardDev = value; }} public double EntryLots { get { return m_EntryLots; } set { m_EntryLots = value; }}
public void Init() { } public void onOrderChange(ECN2API.FBDatafeed.Order order ) { } public void onPriceChange(ECN2API.FBDatafeed.PriceUpdate price ) { } public void onTimer() { }
public System.Drawing.Color BTopColor { get { return BBTColor; } set { BBTColor = value; }} public System.Drawing.Color BLineColor { get { return BBMColor; } set { BBMColor = value; }} public System.Drawing.Color BBottomColor { get { return BBBColor; } set { BBBColor = value; }} public ECN2Chart.LineStyle SmaStyle1 { get { return BBTStyle; } set { BBTStyle = value; }} public ECN2Chart.LineStyle SmaStyle2 { get { return BBMStyle; } set { BBMStyle = value; }} public ECN2Chart.LineStyle SmaStyle3 { get { return BBBStyle; } set { BBBStyle = value; }} #endregion //Set Initial Indicator Properties and Values for each bar on init public void Init() { if (BBTIndex != -1) Chart.RemoveSeries(BBTIndex); ECN2Chart.ChartSeries BBT = new ECN2Chart.ChartSeries(); BBT.SeriesName = "Bollinger Band Top"; BBT.SeriesPanel = 0; BBT.SeriesType = ECN2Chart.SeriesType.Line; BBT.LineColor = BBTColor; BBT.LineStyle = BBTStyle; BBT.LineWidth = 1; BBTIndex = Chart.AddSeries(BBT); if (BBMIndex != -1) Chart.RemoveSeries(BBMIndex); ECN2Chart.ChartSeries BBM = new ECN2Chart.ChartSeries(); BBM.SeriesName = "Bollinger Band Line"; BBM.SeriesPanel = 0; BBM.SeriesType = ECN2Chart.SeriesType.Line; BBM.LineColor = BBMColor; BBM.LineStyle = BBMStyle; BBM.LineWidth = 1; BBMIndex = Chart.AddSeries(BBM); if (BBBIndex != -1) Chart.RemoveSeries(BBBIndex); ECN2Chart.ChartSeries BBB = new ECN2Chart.ChartSeries(); BBB.SeriesName = "Bollinger Band Bottom"; BBB.SeriesPanel = 0; BBB.SeriesType = ECN2Chart.SeriesType.Line; BBB.LineColor = BBBColor; BBB.LineStyle = BBBStyle; BBB.LineWidth = 1; BBBIndex = Chart.AddSeries(BBB); double[] BBTBuffer = new double[Chart.TotalBars - 1]; double[] BBMBuffer = new double[Chart.TotalBars - 1]; double[] BBBBuffer = new double[Chart.TotalBars - 1]; double lstval1 = ECN2API.ECN2PowerScript.ChartNullValue, lstval2 = ECN2API.ECN2PowerScript.ChartNullValue, lstval3 = ECN2API.ECN2PowerScript.ChartNullValue; int Start = 0, Total = 0, startRecord = 0; //Calculate Bollinger Bands and store the result on arrays
TA.Bbands(startRecord, Chart.TotalBars - 1, Chart.CloseArray, m_Period, m_StandardDev, m_StandardDev, m_SMAType, out Start, out Total, BBTBuffer, BBMBuffer, BBBBuffer);
At this point we have three series on the chart showing the Bollinger bands, now we need to
update the last value at each price change and check if order conditions are raised and in this case send an
order.
Step 3 – Updating Values
public void onPriceChange(ECN2API.FBDatafeed.PriceUpdate price ) { double[] BBTBuffer = new double[m_Period + 1];
double[] BBMBuffer = new double[m_Period + 1]; double[] BBBBuffer = new double[m_Period + 1]; int Start, Total, signal, LastChartIndex = Chart.CloseArray.Length-1;
TA.Bbands(LastChartIndex - m_Period, LastChartIndex, Chart.CloseArray, m_Period, m_StandardDev, m_StandardDev, m_SMAType, out Start, out Total, BBTBuffer, BBMBuffer, BBBBuffer); double val1 = Math.Round(BBTBuffer[Total - 1], Market.SymbolDetails.DecimalPrecision+1); double val2 = Math.Round(BBMBuffer[Total - 1], Market.SymbolDetails.DecimalPrecision+1); double val3 = Math.Round(BBBBuffer[Total - 1], Market.SymbolDetails.DecimalPrecision+1);
double lastTop = Chart.GetSeries(BBTIndex).GetRecord(1); double lastBottom = Chart.GetSeries(BBBIndex).GetRecord(1);
//Edit each record of the new series with the new calculated value for (int i = 0; i <= Total; i++) {
double val1 = Math.Round(BBTBuffer[Total - i], Market.SymbolDetails.DecimalPrecision+1); double val2 = Math.Round(BBMBuffer[Total - i], Market.SymbolDetails.DecimalPrecision+1); double val3 = Math.Round(BBBBuffer[Total - i], Market.SymbolDetails.DecimalPrecision+1);
if (val1 != 0 && val2 != 0 && val3 != 0) { Chart.GetSeries(BBTIndex).EditRecord(i, val1); Chart.GetSeries(BBMIndex).EditRecord(i, val2); Chart.GetSeries(BBBIndex).EditRecord(i, val3); lstval1 = val1; lstval2 = val2; lstval3 = val3; } else { Chart.GetSeries(BBTIndex).EditRecord(i, lstval1); Chart.GetSeries(BBMIndex).EditRecord(i, lstval2); Chart.GetSeries(BBBIndex).EditRecord(i, lstval3); } } //Clean Array.Resize(ref BBTBuffer, 0); Array.Resize(ref BBMBuffer, 0); Array.Resize(ref BBBBuffer, 0); BBTBuffer = null; BBMBuffer = null; BBBBuffer = null; }
In the code above we has calculated only the last value of the three bands and updated it on the
chart, this will keep our indicator updated during chart progression.
Focusing on orders, as you can see we set a variable signal checking if the price cross over the top band or
under the bottom one; then we call an user defined function to send the order. Let’s look at this methods:
Step 4 – Send Order
private void Buy() { ECN2API.FBDatafeed.Order orderDraft = Orders.GetOrderDraft (ECN2API.FBDatafeed.Order.OrderType.Market, ECN2API.FBDatafeed.Order.OrderSide.Long, EntryLots, "", Market.PriceDetail.Ask[0],0,0);
Orders.SendNewOrder(orderDraft); }
if (m_LastPrice == 0) m_LastPrice = Market.PriceDetail.Price; if (val1 != 0 && val2 != 0 && val3 != 0) { Chart.GetSeries(BBTIndex).EditRecord(0, val1); Chart.GetSeries(BBMIndex).EditRecord(0, val2); Chart.GetSeries(BBBIndex).EditRecord(0, val3); if (lastTop != 0 && lastBottom != 0) {
if (Market.PriceDetail.Price > val1 && m_LastPrice <= lastTop && m_LastSignal != Chart.GetChartDate(0) && Orders.PendingOrders.Length == 0 && Orders.PositionSize <= 0) {
//Buy Signal m_LastSignal = Chart.GetChartDate(0); signal = 1;
} else if (Market.PriceDetail.Price < val3 && m_LastPrice >= lastBottom && m_LastSignal != Chart.GetChartDate(0) && Orders.PendingOrders.Length == 0 && Orders.PositionSize >= 0) {
//Sell Signal m_LastSignal = Chart.GetChartDate(0); signal = -1; } }
} else { Log("Value Skip on " + price.TradeDateTime.ToString(),
ScriptContainer.MessageType.Log); }
m_LastPrice = Market.PriceDetail.Price; Array.Resize(ref BBTBuffer, 0); Array.Resize(ref BBMBuffer, 0); Array.Resize(ref BBBBuffer, 0); BBTBuffer = null; BBMBuffer = null; BBBBuffer = null; if (signal == 1) { Buy(); } else if (signal == -1) { Sell(); } }
As you can see, send an order is not a big deal, the ECN2API.FBDatafeed.Order class is not
part of the Script class, it is derived from ECN2API and there are a lot of order types and functionalities
applicable to an order draft. Another Class derived from ECN2API is the the
ECN2API.FBDatafeed.PriceUpdate class that contain a symbol price information including depth of
market. The code completion inside PathScript editor help to discover any field on these objects. Refer to
ECN2API documentation if you need more info on these classes.
And Now the result!
Step 5 – Add the indicator/strategy to a chart
If you have opened your editor from pathfinder menu, it will be linked to the platform and any
change in the indicators list will be reflected on the Platform menus. Otherwise, you need to restart your
application to see new indicators listed. In any of the case above, at end, you will find the script listed in the
chart menu as in the following image:
private void Sell() {
ECN2API.FBDatafeed.Order orderDraft = Orders.GetOrderDraft(ECN2API.FBDatafeed.Order.OrderType.Market, ECN2API.FBDatafeed.Order.OrderSide.Short, EntryLots, "", Market.PriceDetail.Bid[0],0,0);
Orders.SendNewOrder(orderDraft); }
Clicking on the name of the indicator, it will be added to chart and a submenu will appair on the right of the
indicator name:
The red icon appaired in the upper left corner of the chart indicate that this strategy can send orders and
these are not actives at the moment. To activate orders submission you need to click on the green icon
“Enable Orders…” in the script sub menu. When orders will be activated the icon will change as follow:
The options button for our script will open the following window:
How to improve indicators start
As we said, you can use the embedded Init() function to initialize your script but what happen if
your initialization function take a lot of time to execute? Any script event is invoked on UI thread to avoid
thread exceptions accessing the chart, then all the UI will freeze until your Init() function will return. In the
following indicator we will show how to use the timer methods to do a long initialization without freeze the
UI.
The Pivot Points indicator, Part 1: Members and base methods
#region Members bool verbose = false; public class PivotData { public PivotData() { P = R1 = R2 = R3 = R4 = R5 = S1 = S2 = S3 = S4 = S5 = ECN2API.ECN2PowerScript.ChartNullValue; } public double P; public double R1; public double R2; public double R3; public double R4; public double R5; public double S1; public double S2; public double S3; public double S4; public double S5; } System.Threading.Thread loader = null; PivotData[] PivotsData; bool dataLoaded; int PivotIndex = R1Index = R2Index = R3Index = R4Index = R5Index = S1Index = S2Index = S3Index = S4Index = S5Index = InfoLayerIndex = -1; double LastHigh, LastLow, DayClose, Pivot; double R1, R2, R3, R4, R5, S1, S2, S3, S4, S5, P;
System.Drawing.Color _PivotColor = System.Drawing.Color.Orange; System.Drawing.Color _R1Color = System.Drawing.Color.LightGreen; System.Drawing.Color _R2Color = System.Drawing.Color.LightGreen; System.Drawing.Color _R3Color = System.Drawing.Color.LightGreen; System.Drawing.Color _R4Color = System.Drawing.Color.LightGreen; System.Drawing.Color _R5Color = System.Drawing.Color.LightGreen; System.Drawing.Color _InfoPanelColor = System.Drawing.Color.Gray; System.Drawing.Color _S1Color = System.Drawing.Color.Red; System.Drawing.Color _S2Color = System.Drawing.Color.Red; System.Drawing.Color _S3Color = System.Drawing.Color.Red; System.Drawing.Color _S4Color = System.Drawing.Color.Red; System.Drawing.Color _S5Color = System.Drawing.Color.Red; ECN2Chart.LineStyle _PivotStyle = ECN2Chart.LineStyle.Solid; ECN2Chart.LineStyle _R1Style = ECN2Chart.LineStyle.Solid; ECN2Chart.LineStyle _R2Style = ECN2Chart.LineStyle.Solid; ECN2Chart.LineStyle _R3Style = ECN2Chart.LineStyle.Solid; ECN2Chart.LineStyle _R4Style = ECN2Chart.LineStyle.Solid; ECN2Chart.LineStyle _R5Style = ECN2Chart.LineStyle.Solid; ECN2Chart.LineStyle _S1Style = ECN2Chart.LineStyle.Solid; ECN2Chart.LineStyle _S2Style = ECN2Chart.LineStyle.Solid; ECN2Chart.LineStyle _S3Style = ECN2Chart.LineStyle.Solid; ECN2Chart.LineStyle _S4Style = ECN2Chart.LineStyle.Solid; ECN2Chart.LineStyle _S5Style = ECN2Chart.LineStyle.Solid; #endregion
#region Base Metods public void AddPivotSeries() { if (PivotIndex >= 0) Chart.RemoveSeries(PivotIndex); if (R1Index >= 0) Chart.RemoveSeries(R1Index); if (R2Index >= 0) Chart.RemoveSeries(R2Index); if (R3Index >= 0) Chart.RemoveSeries(R3Index); if (R4Index >= 0) Chart.RemoveSeries(R4Index); if (R5Index >= 0) Chart.RemoveSeries(R5Index); if (S1Index >= 0) Chart.RemoveSeries(S1Index); if (S2Index >= 0) Chart.RemoveSeries(S2Index); if (S3Index >= 0) Chart.RemoveSeries(S3Index); if (S4Index >= 0) Chart.RemoveSeries(S4Index); if (S5Index >= 0) Chart.RemoveSeries(S5Index); ECN2Chart.ChartSeries Pivot = new ECN2Chart.ChartSeries(); Pivot.SeriesName = "Pivot"; Pivot.SeriesPanel = 0; Pivot.SeriesType = ECN2Chart.SeriesType.Line; Pivot.LineColor = PivotColor; Pivot.LineStyle = PivotStyle; Pivot.LineWidth = 1; PivotIndex = Chart.AddSeries(Pivot); ECN2Chart.ChartSeries R1 = new ECN2Chart.ChartSeries(); R1.SeriesName = "R1"; R1.SeriesPanel = 0; R1.SeriesType = ECN2Chart.SeriesType.Line; R1.LineColor = R1Color; R1.LineStyle = R1Style; R1.LineWidth = 1; R1Index = Chart.AddSeries(R1);
#region Properties //External Exposed Properties /// <summary> //Pivot Color /// </summary> public System.Drawing.Color PivotColor { get { return _PivotColor; } set { _PivotColor = value; }} public System.Drawing.Color InfoPanelColor { get { return _InfoPanelColor; } set { _InfoPanelColor = value; }} public System.Drawing.Color R1Color { get { return _R1Color; } set { _R1Color = value; }} public System.Drawing.Color R2Color { get { return _R2Color; } set { _R2Color = value; }} public System.Drawing.Color R3Color { get { return _R3Color; } set { _R3Color = value; }} public System.Drawing.Color R4Color { get { return _R4Color; } set { _R4Color = value; }} public System.Drawing.Color R5Color { get { return _R5Color; } set { _R5Color = value; }} public System.Drawing.Color S1Color { get { return _S1Color; } set { _S1Color = value; }} public System.Drawing.Color S2Color { get { return _S2Color; } set { _S2Color = value; }} public System.Drawing.Color S3Color { get { return _S3Color; } set { _S3Color = value; }} public System.Drawing.Color S4Color { get { return _S4Color; } set { _S4Color = value; }} public System.Drawing.Color S5Color { get { return _S5Color; } set { _S5Color = value; }} public ECN2Chart.LineStyle PivotStyle { get { return _PivotStyle; } set { _PivotStyle = value; }} public ECN2Chart.LineStyle R1Style { get { return _R1Style; } set { _R1Style = value; }} public ECN2Chart.LineStyle R2Style { get { return _R2Style; } set { _R2Style = value; }} public ECN2Chart.LineStyle R3Style { get { return _R3Style; } set { _R3Style = value; }} public ECN2Chart.LineStyle R4Style { get { return _R4Style; } set { _R4Style = value; }} public ECN2Chart.LineStyle R5Style { get { return _R5Style; } set { _R5Style = value; }} public ECN2Chart.LineStyle S1Style { get { return _S1Style; } set { _S1Style = value; }} public ECN2Chart.LineStyle S2Style { get { return _S2Style; } set { _S2Style = value; }} public ECN2Chart.LineStyle S3Style { get { return _S3Style; } set { _S3Style = value; }} public ECN2Chart.LineStyle S4Style { get { return _S4Style; } set { _S4Style = value; }} public ECN2Chart.LineStyle S5Style { get { return _S5Style; } set { _S5Style = value; }} #endregion
ECN2Chart.ChartSeries R2 = new ECN2Chart.ChartSeries(); R2.SeriesName = "R2"; R2.SeriesPanel = 0; R2.SeriesType = ECN2Chart.SeriesType.Line; R2.LineColor = R2Color; R2.LineStyle = R2Style; R2.LineWidth = 1; R2Index = Chart.AddSeries(R2); ECN2Chart.ChartSeries R3 = new ECN2Chart.ChartSeries(); R3.SeriesName = "R3"; R3.SeriesPanel = 0; R3.SeriesType = ECN2Chart.SeriesType.Line; R3.LineColor = R3Color; R3.LineStyle = R3Style; R3.LineWidth = 1; R3Index = Chart.AddSeries(R3); ECN2Chart.ChartSeries R4 = new ECN2Chart.ChartSeries(); R4.SeriesName = "R4"; R4.SeriesPanel = 0; R4.SeriesType = ECN2Chart.SeriesType.Line; R4.LineColor = R4Color; R4.LineStyle = R4Style; R4.LineWidth = 1; R4Index = Chart.AddSeries(R4); ECN2Chart.ChartSeries R5 = new ECN2Chart.ChartSeries(); R5.SeriesName = "R5"; R5.SeriesPanel = 0; R5.SeriesType = ECN2Chart.SeriesType.Line; R5.LineColor = R5Color; R5.LineStyle = R5Style; R5.LineWidth = 1; R5Index = Chart.AddSeries(R5); ECN2Chart.ChartSeries S1 = new ECN2Chart.ChartSeries(); S1.SeriesName = "S1"; S1.SeriesPanel = 0; S1.SeriesType = ECN2Chart.SeriesType.Line; S1.LineColor = S1Color; S1.LineStyle = S1Style; S1.LineWidth = 1; S1Index = Chart.AddSeries(S1); ECN2Chart.ChartSeries S2 = new ECN2Chart.ChartSeries(); S2.SeriesName = "S2"; S2.SeriesPanel = 0; S2.SeriesType = ECN2Chart.SeriesType.Line; S2.LineColor = S2Color; S2.LineStyle = S2Style; S2.LineWidth = 1; S2Index = Chart.AddSeries(S2); ECN2Chart.ChartSeries S3 = new ECN2Chart.ChartSeries(); S3.SeriesName = "S3"; S3.SeriesPanel = 0; S3.SeriesType = ECN2Chart.SeriesType.Line; S3.LineColor = S3Color; S3.LineStyle = S3Style; S3.LineWidth = 1; S3Index = Chart.AddSeries(S3); ECN2Chart.ChartSeries S4 = new ECN2Chart.ChartSeries(); S4.SeriesName = "S4"; S4.SeriesPanel = 0; S4.SeriesType = ECN2Chart.SeriesType.Line; S4.LineColor = S4Color; S4.LineStyle = S4Style; S4.LineWidth = 1; S4Index = Chart.AddSeries(S4); ECN2Chart.ChartSeries S5 = new ECN2Chart.ChartSeries(); S5.SeriesName = "S5"; S5.SeriesPanel = 0; S5.SeriesType = ECN2Chart.SeriesType.Line; S5.LineColor = S5Color; S5.LineStyle = S5Style; S5.LineWidth = 1; S5Index = Chart.AddSeries(S5);
So far everything is the same… we have just added a set of base methods to help us during initialization and
to show a set of public properties to the user. Before continue to the initialization routines, take a look the
last piece of code. In the method “AddInfoLayer” we have used the function AddLayer to add a separate
layer on the chart, this layer will be painted over the chart screen, the chart will fire the event LayerPaint
on each layer at each paint cycle. In this indicator we will use a separate layer to draw an info panel on top
left corner of the chart, showing current pivot values.
Part2: The Asynchronous initialization
The Asynchronous loading routine:
private void LoadPivotData() { dataLoaded = false; Log("Start Loading", ScriptContainer.MessageType.Log); PivotsData = new PivotData[Chart.TotalBars]; if (verbose) ParentContainer.AddMessage("Start Calculating", ScriptContainer.MessageType.Log); LastHigh = Chart.get_High(Chart.TotalBars-1); LastLow = Chart.get_Low(Chart.TotalBars-1); DayClose = Chart.get_Open(Chart.TotalBars-1); Pivot = (LastHigh + LastLow + DayClose) / 3; DateTime lastDate = Chart.GetChartDate(Chart.TotalBars-1).Date;
#region Init public void Init() { ParentContainer.AddMessage("Adding Series", ScriptContainer.MessageType.Log);
AddPivotSeries(); if (verbose) ParentContainer.AddMessage("Adding Info Layer", ScriptContainer.MessageType.Log); AddInfoLayer();
//Kill previous thread if any if (loader != null && loader.IsAlive) loader.Abort(); loader = new System.Threading.Thread(LoadPivotData); //Start the loader thread loader.Start(); SetTimer(100); StartTimer(); }
} public void AddInfoLayer() {
//Remove previous layers if any if (InfoLayerIndex >= 0) Chart.RemoveLayer(InfoLayerIndex);
//Add a new layer 250x150 pixels on top left corner with a bit of transparence. InfoLayerIndex = Chart.AddLayer(250, 150, ECN2Chart.StaticPosition.UpperLeft, 192);
//Subscribe the layer paint event
if (InfoLayerIndex >= 0) Chart.GetLayer(InfoLayerIndex).LayerPaint += onLayerPaint; }
if (Chart.ChartSelection.Periodicity < ECN2API.FBDatafeed.Periodicity.Daily) { if (verbose) ParentContainer.AddMessage("Requesting Daily History", ScriptContainer.MessageType.Log); int totDays = 1 + (int)((DateTime.Now - Chart.GetChartDate(Chart.TotalBars - 1).Date).TotalDays + 1); List<ECN2API.FBDatafeed.BarData> prevdays = Market.RequestHistory(ECN2API.FBDatafeed.Periodicity.Daily,1,totDays); if (prevdays != null && prevdays.Count >0 && Chart.GetChartDate(Chart.TotalBars - 1) > prevdays[0].TradeDate) { if (verbose) ParentContainer.AddMessage("History Received, first Bar: " + prevdays[0].TradeDate.ToString(), ScriptContainer.MessageType.Log); LastHigh = prevdays[0].HighPrice; LastLow = prevdays[0].LowPrice; DayClose = prevdays[0].ClosePrice; Pivot = (LastHigh + LastLow + DayClose) / 3; if (verbose) ParentContainer.AddMessage("History Close " + DayClose.ToString(), ScriptContainer.MessageType.Log); if (verbose) ParentContainer.AddMessage("History High " + LastHigh.ToString(), ScriptContainer.MessageType.Log); if (verbose) ParentContainer.AddMessage("History Low " + LastLow.ToString(), ScriptContainer.MessageType.Log); lastDate = prevdays[0].TradeDate; } } if (verbose) ParentContainer.AddMessage("Start Date " + Chart.GetChartDate(Chart.TotalBars - 1).ToString(), ScriptContainer.MessageType.Log); if (verbose) ParentContainer.AddMessage("End Date " + Chart.GetChartDate(0).ToString(), ScriptContainer.MessageType.Log);
for(int i = Chart.TotalBars-2; i >= 0; i--) { //if (verbose) ParentContainer.AddMessage("Record " + i.ToString(), ScriptContainer.MessageType.Log); if(Chart.get_High(i+1) > LastHigh) LastHigh = Chart.get_High(i+1); if(Chart.get_Low(i+1) < LastLow) LastLow= Chart.get_Low(i+1); //if (verbose) ParentContainer.AddMessage("High " + LastHigh.ToString() + " - Low " + LastLow.ToString(), ScriptContainer.MessageType.Log); DateTime currentDate = Chart.GetChartDate(i).Date; if(currentDate != lastDate) { if (verbose) ParentContainer.AddMessage("New Close " + Chart.get_Close(i+1).ToString(), ScriptContainer.MessageType.Log); if (verbose) ParentContainer.AddMessage("Last High " + LastHigh.ToString(), ScriptContainer.MessageType.Log); if (verbose) ParentContainer.AddMessage("Last Low " + LastLow.ToString(), ScriptContainer.MessageType.Log); if (verbose) ParentContainer.AddMessage("Record " + i.ToString() + " - Date " + currentDate.ToString(), ScriptContainer.MessageType.Log); DayClose = Chart.get_Close(i+1); Pivot = (LastHigh + LastLow + DayClose) / 3; P = Pivot; R1 = (2*P) - LastLow; S1 = (2*P) - LastHigh; R2 = P + (LastHigh - LastLow); S2 = P - (LastHigh - LastLow); R3 = (2*P) + (LastHigh - (2*LastLow)); S3 = (2*P) - ((2* LastHigh) - LastLow); R4 = (3*P) + (LastHigh - (3*LastLow)); S4 = (3*P) - ((3* LastHigh) - LastLow); R5 = (4*P) + (LastHigh - (4*LastLow)); S5 = (4*P) - ((4* LastHigh) - LastLow); LastLow = DayClose; LastHigh = DayClose; }
The script events
#region Strategy Events public void onOrderChange(ECN2API.FBDatafeed.Order order ) { } DateTime lastDate; public void onPriceChange(ECN2API.FBDatafeed.PriceUpdate price ) { if(Chart.get_High(1) > LastHigh) LastHigh = Chart.get_High(1); if(Chart.get_Low(1) < LastLow) LastLow= Chart.get_Low(1); if(Chart.GetChartDate(0).Date != Chart.GetChartDate(1).Date) { DayClose = Chart.get_Close(1); Pivot = (LastHigh + LastLow + DayClose) / 3; P = Pivot; R1 = (2*P) - LastLow; S1 = (2*P) - LastHigh; R2 = P + (LastHigh - LastLow); S2 = P - (LastHigh - LastLow); R3 = (2*P) + (LastHigh - (2*LastLow)); S3 = (2*P) - ((2* LastHigh) - LastLow); R4 = (3*P) + (LastHigh - (3*LastLow)); S4 = (3*P) - ((3* LastHigh) - LastLow); R5 = (4*P) + (LastHigh - (4*LastLow)); S5 = (4*P) - ((4* LastHigh) - LastLow); LastLow = DayClose; LastHigh = DayClose; } if(Chart.GetChartDate(0) != lastDate) { Chart.GetSeries(PivotIndex).EditRecord(0, P); Chart.GetSeries(R1Index).EditRecord(0, R1); Chart.GetSeries(S1Index).EditRecord(0, S1); Chart.GetSeries(R2Index).EditRecord(0, R2); Chart.GetSeries(S2Index).EditRecord(0, S2); Chart.GetSeries(R3Index).EditRecord(0, R3); Chart.GetSeries(S3Index).EditRecord(0, S3); Chart.GetSeries(R4Index).EditRecord(0, R4); Chart.GetSeries(S4Index).EditRecord(0, S4); Chart.GetSeries(R5Index).EditRecord(0, R5); Chart.GetSeries(S5Index).EditRecord(0, S5); } lastDate = Chart.GetChartDate(0); }
PivotData current = new PivotData(); if (P > 0) { current.P = P; current.R1 = R1; current.R2 = R2; current.R3 = R3; current.R4 = R4; current.R5 = R5; current.S1 = S1; current.S2 = S2; current.S3 = S3; current.S4 = S4; current.S5 = S5; } PivotsData[i] = current; lastDate = currentDate; } dataLoaded = true; Log("End Loading", ScriptContainer.MessageType.Log); } #endregion
As you can read, in the init function we set the property and then start a new asynchronous thread, in this
thread we get daily history to calculate pivot values starting from previous day. To synchronize the process
we use a Boolean variable “dataLoaded” that will be catched by the timer function. The timer, found
dataLoaded set, will load the data and also stop itself.
And now, the info layer:
#region Panel Paint private void onLayerPaint(ref ECN2Chart.ChartLayer Layer ,ref ECN2Chart.ChartLayer.LayerPaintEventArgs e) { try { using (System.Drawing.Pen borderPen = new System.Drawing.Pen(Chart.ChartForeColor, 2)) { int xOrigin = Chart.ChartWorkspaceLeft + 8; int yOrigin = 22; int darkness = 128; using (System.Drawing.Brush backBrush = new System.Drawing.Drawing2D.LinearGradientBrush(new Rectangle(xOrigin, yOrigin, 162, 125), Color.FromArgb(255,_InfoPanelColor.R,_InfoPanelColor.G ,_InfoPanelColor.B), Color.FromArgb(255, Math.Max(0,_InfoPanelColor.R - darkness), Math.Max(0,_InfoPanelColor.G - darkness), Math.Max(0,_InfoPanelColor.B - darkness)), System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal)) { e.Graphic.FillRectangle(backBrush, new Rectangle(xOrigin, yOrigin, 162, 125));
backBrush.Dispose(); }
e.Graphic.DrawRectangle(borderPen, new Rectangle(xOrigin, yOrigin, 162, 125)); int dp = Market.SymbolDetails.DecimalPrecision;
public void onTimer() { if (dataLoaded) { StopTimer(); Log("Updating series. Tot records: " + PivotsData.Length.ToString(), ScriptContainer.MessageType.Log); for (int i= 0; i < PivotsData.Length; i++) { if (PivotsData[i] != null) { Chart.GetSeries(PivotIndex).EditRecord(i, PivotsData[i].P); Chart.GetSeries(R1Index).EditRecord(i, PivotsData[i].R1); Chart.GetSeries(S1Index).EditRecord(i, PivotsData[i].S1); Chart.GetSeries(R2Index).EditRecord(i, PivotsData[i].R2); Chart.GetSeries(S2Index).EditRecord(i, PivotsData[i].S2); Chart.GetSeries(R3Index).EditRecord(i, PivotsData[i].R3); Chart.GetSeries(S3Index).EditRecord(i, PivotsData[i].S3); Chart.GetSeries(R4Index).EditRecord(i, PivotsData[i].R4); Chart.GetSeries(S4Index).EditRecord(i, PivotsData[i].S4); Chart.GetSeries(R5Index).EditRecord(i, PivotsData[i].R5); Chart.GetSeries(S5Index).EditRecord(i, PivotsData[i].S5); } } System.Array.Resize(ref PivotsData, 0); } } #endregion
if (dataLoaded) { using (System.Drawing.Brush textBrush = new System.Drawing.SolidBrush(Chart.ChartForeColor)) { System.Drawing.Font tmpFont = new System.Drawing.Font(Chart.ChartFont.FontFamily, (Single)13); e.Graphic.DrawString("Pivot " + Math.Round(P, dp).ToString(), tmpFont, textBrush, xOrigin + 20, yOrigin + 2); tmpFont.Dispose(); textBrush.Dispose(); } System.Drawing.Font txFont = new System.Drawing.Font(Chart.ChartFont.FontFamily, (Single)10); using (System.Drawing.Brush textBrush = new System.Drawing.SolidBrush(R1Color)) { e.Graphic.DrawString("R1 " + Math.Round(R1, dp).ToString(), txFont, textBrush, xOrigin + 2, yOrigin + 25); e.Graphic.DrawString("R2 " + Math.Round(R2, dp).ToString(), txFont, textBrush, xOrigin + 2, yOrigin + 45); e.Graphic.DrawString("R3 " + Math.Round(R3, dp).ToString(), txFont, textBrush, xOrigin + 2, yOrigin + 65); e.Graphic.DrawString("R4 " + Math.Round(R4, dp).ToString(), txFont, textBrush, xOrigin + 2, yOrigin + 85); e.Graphic.DrawString("R5 " + Math.Round(R5, dp).ToString(), txFont, textBrush, xOrigin + 2, yOrigin + 105); textBrush.Dispose(); } using (System.Drawing.Brush textBrush = new System.Drawing.SolidBrush(S1Color)) { e.Graphic.DrawString("S1 " + Math.Round(S1, dp).ToString(), txFont, textBrush, xOrigin + 82, yOrigin + 25); e.Graphic.DrawString("S2 " + Math.Round(S2, dp).ToString(), txFont, textBrush, xOrigin + 82, yOrigin + 45); e.Graphic.DrawString("S3 " + Math.Round(S3, dp).ToString(), txFont, textBrush, xOrigin + 82, yOrigin + 65); e.Graphic.DrawString("S4 " + Math.Round(S4, dp).ToString(), txFont, textBrush, xOrigin + 82, yOrigin + 85); e.Graphic.DrawString("S5 " + Math.Round(S5, dp).ToString(), txFont, textBrush, xOrigin + 82, yOrigin + 105); textBrush.Dispose(); } txFont.Dispose(); } else { using (System.Drawing.Brush textBrush = new System.Drawing.SolidBrush(Chart.ChartForeColor)) { System.Drawing.Font tmpFont = new System.Drawing.Font(Chart.ChartFont.FontFamily, (Single)14); e.Graphic.DrawString("Loading Data", tmpFont, textBrush, xOrigin + 10, yOrigin + 60); tmpFont.Dispose(); textBrush.Dispose(); } } borderPen.Dispose(); e.Modified = true; } } catch { } } #endregion
Note how on LayerPaint we use the graphic object passed inside event args to paint our own panel graphic,
note also that we need to set the modified flag inside event args, this value will notify to the chart that this
layer is different from the last painted, in this way we can avoid unneeded layer calculations just leaving
e.modified as false.
That’s all you need to start with indicators, try to write your own code and explore all chart capabilities and
properties. Now we will focus on Market and Orders objects. And this is the result…
The Market Object
We have seen how to get Bar values from the chart with the indexed properties Close, Open, etc..
But how to access the current price book? And how to get prices history? The answer is with the Market
object. The Market object is described as follow:
All market properties are indexed to allow access to symbols different from the current one displayed in
the chart. Then, for example, we can call PriceDetail to get the current book for the selectiod symbol or call
PriceDetail(“symbolName”) (get_PriceDetail(“symbolName”) on c# ) to get a specific symbol book. In the
same way we can get symbols info, Session Volumes per price and access the current ticks buffer for any
symbol in the selected datafeed.
The Orders Object
As for market properties, the orders properties are indexed and allow access to many other
positions specifying the symbol name. The Orders Object is described as follow.
The methods beginning with “Send” do not provide a return value, these methods starts asynchronous
operations that will cause the event onOrderChange to be raised. An interesting method is GetOrderDraft,
when you create a new order there are a lot of info that you need to include, many of these info are
standard and depends on the datafeed. The method GetOrderDraft will compile an order draft for you just
giving some basic value. Anyway, the Draft returned can be modified by the user before send.