- ago
I have created an empty PosSizer extension. It does not override configuration or SizePosition method. I compared placing a market order, with and without the possizer

When I do not use the possizer, I get the quantity fine in the Broker.PlaceOrder method. However, if I use the PosSizer without overriding anything in the code and using the default values in the UI configuration, I get quantity as a decimal and incorrect.

Without the sizer, qty => 10
With sizer => 0.001

Below are the screenshots of the transaction object I am getting in the Broker.PlaceOrder method.

Please check and advise.

Without PosSizer


With PosSizer
0
509
Solved
14 Replies

Reply

Bookmark

Sort
Cone8
 ( 4.98% )
- ago
#1
I advise you to implement SizePosition(), an abstract method in PositionSizerBase.
It's not a choice when creating Position Sizer.

0.001 is just an arbitrary "minimal" quantity when that's initialized with the transaction that doesn't have a manual quantity assigned.
0
- ago
#2
Previously, I had an overridden implementation of SizePositon method. It was returning "11". The Quantity in Broker.PlaceOrder method was the same. I then checked by removing the override to see the same result.

Below was the original code

CODE:
// Check the instrument var instrument = InstrumentMaster.GetInstrument(t.MappedSymbol); if (instrument == null) {    Logger.Warning($"The symbol {t.MappedSymbol} is not supported by {DataSource.Name}.");    return 0; } // Non-tradables if (!instrument.IsTradable) {    Logger.Error($"The symbol {t.MappedSymbol} is not tradable.");    return 0; } // If not entry, use the base class if (!t.IsEntry) {    return base.SizePosition(t, bars, idx, basisPrice, equity, cash); } // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (instrument.Type) {    // Future    case InstrumentType.Future when UseFuturesMode(bars):       // Use the future buy premium for both buy/short transactions       basisPrice = Broker.GetRequiredMargin(instrument, OrderSide.Buy, basisPrice);       break;    // Options sell    case InstrumentType.Option when t.TransactionType == TransactionType.Short:       // Update the basis price to include the broker-side margin required for option shorts       basisPrice = Broker.GetRequiredMargin(instrument, OrderSide.Sell, basisPrice);       break; } // Calculate the quantity var qty = base.SizePosition(t, bars, idx, basisPrice, equity, cash); if (qty <= 0) {    return qty; } // Adjust to instrument's lot size qty -= qty % instrument.LotSize; // Return return qty;


The above override returned 11 when the basisPrice was Rs. 450/-, to fill the fixed position value of Rs. 5000/-

Still the Quanity is coming as 0.001 in the Broker.PlaceTrade. The instrument used to test this is a Stock, so the code doesn't walk into the switch statement.
0
- ago
#5
Do you need any other info or debug screenshots of my sizer?
0
- ago
#6
What for? As you know, dozens of our and 3rd party PosSizers work correctly. It's an issue on your end likely caused by your custom market assignment and its decimals, so check all that including the symbol mappings in the Data Manager.
0
- ago
#7
Below are the observations I have

Environment:
DotNet - 8.0.206
OS - Windows 11
WealthLab - v92 Fresh installation (Lite Setup) without any extensions. I actually uninstalled my existing installation and reinstalled freshly after deleting the program files app folder and AppData\Roaming\WealthLab8 folder.

Project
Created a Fresh/Blank Project (VS2022)
CODE:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup>    <TargetFramework>net8.0-windows8.0</TargetFramework>    <Platforms>AnyCPU</Platforms>    <UseWPF>true</UseWPF>    <Version>1.0.0</Version>    <AssemblyVersion>1.0.0</AssemblyVersion>    <FileVersion>1.0.0</FileVersion>    <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">    <NoWarn>1701;1702;</NoWarn> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">    <NoWarn>1701;1702;</NoWarn> </PropertyGroup> <ItemGroup>    <Reference Include="WealthLab.ChartWPF">     <HintPath>C:\Program Files\Quantacula, LLC\WealthLab 8\WealthLab.ChartWPF.dll</HintPath>    </Reference>    <Reference Include="WealthLab.Core">     <HintPath>C:\Program Files\Quantacula, LLC\WealthLab 8\WealthLab.Core.dll</HintPath>    </Reference>    <Reference Include="WealthLab.WPF">     <HintPath>C:\Program Files\Quantacula, LLC\WealthLab 8\WealthLab.WPF.dll</HintPath>    </Reference> </ItemGroup> <Target Name="PostBuild" AfterTargets="PostBuildEvent">    <Exec Command="@ECHO OFF&#xD;&#xA;CMD /v:on /c &quot;..\..\wl-publish-proj-dbg.cmd $(SolutionDir) $(ProjectDir) $(AssemblyName) $(Configuration) 1&quot;" /> </Target> </Project>


Class 1 - BrokerAdapter.cs
CODE:
public class BrokerAdapter : BrokerBase {    /// <inheritdoc />    public override string Name => "CW Test Broker";    /// <inheritdoc />    protected override bool Connect()    {       UpdateAccounts();       return true;    }    /// <inheritdoc />    public override void UpdateAccounts()    {       // Base       base.UpdateAccounts();       // Default account with $1,000,000       Accounts.Add(new BrokerAccount(this)       {          AccountID = "Default",          Nickname = "Default",          BuyingPower = 1000000,          Cash = 1000000,          CurrencyBalances = new Dictionary<string, double>          {             { "USD", 1000000 }          }       });       // Notify accounts updated       AccountsUpdated();    }    /// <inheritdoc />    public override List<Transaction> GetActiveOrders()    {       // No active orders       return new();    }    /// <inheritdoc />    protected override void PlaceTrade(Transaction t)    {       throw new NotImplementedException();    }    /// <inheritdoc />    protected override void CancelTrade(Transaction t)    {       throw new NotImplementedException();    } }


Class 2 - PositionSizer.cs
CODE:
public class PositionSizer : BasicPositionSizer {    //add parameter to control how many entries per day    public override void GenerateParameters()    {       base.GenerateParameters();       Parameters.Add(new Parameter("Max Entries", ParameterType.Int32, 2, 1.0, 999999999.0));       Parameters.Add(new Parameter("For intraday trades, sum up positions opened during the day", ParameterType.Boolean, false));    }    //Name    public override string Name => "Custom Max Entries per Bar";    //description    public override string Description => "Provides the basic Position Sizing options, with the additional ability to limit the number of entries taken on each bar (or day) of data.";    //initialize    public override void Initialize()    {       base.Initialize();       _maxEntries = Parameters[2].AsInt;       _considerIntraday = Parameters[3].AsBoolean;    }    //Size the position, if max entries exceeded, set size to zero    public override double SizePosition(Transaction t, BarHistory bars, int idx, double basisPrice, double equity, double cash)    {       int count = 0;       if (!_considerIntraday)       {             foreach (Transaction o in Orders)                if (o.TransactionType.IsEntry())                   count++;       }       else       {          if (t.Bars.Scale.IsIntraday)          {             var positionsInSymbolOpenedToday = Positions.AsParallel().AsOrdered().Reverse().TakeWhile(p => p.EntryDate.DayOfYear == t.EntryDate.DayOfYear && p.Symbol == t.Symbol);             count += positionsInSymbolOpenedToday.Count();          }       }       if (count >= _maxEntries)          return 0.0;       else          return base.SizePosition(t, bars, idx, basisPrice, equity, cash);    }    //private members    private int _maxEntries;    private bool _considerIntraday; }


I have no other classes or code in the project. The PositionSizer class is exactly as given as example on the extension's documentation page.

Use Case
1. Run WL8, open a fresh chart with SPY as the default symbol
2. Enable Streaming using Yahoo Finance provider
3. Select the "CW Test Broker" in the broker chooser
4. Select the sizer to be "Custom Max Entries per Bar", leave all other sizer configs at their defaults
5. Click on Buy.

Observation 1
An exception is thrown in the PositionSizer class at the line "foreach (Transaction o in Orders)". If I enclose the foreach statement within a check for Orders != null, then the exception won't happen. When I break inside the PlaceTrade method in the Broker adapter, I still get a "0.001" in the quantity.


When I don't choose any position sizer, I get the quantity of 9 correctly in the PlaceTrade, using the "Fixed Value" option with $5000 as amount.

I have tried built-in position sizers - Equal Shares, Max Entries Per Bar, Meritocratus and Indexitor. All of them except Indexitor throw the below exception. Indexitor results in the same 0.001 as the Quantity.

CODE:
System.NullReferenceException: Object reference not set to an instance of an object. at WealthLab.Backtest.EqualShares.SizePosition(Transaction t, BarHistory bars, Int32 idx, Double basisPrice, Double equity, Double cash) at WealthLab.Backtest.Backtester.CheckAttribute(Transaction reference, Int32 token) at WealthLab.Backtest.Backtester.CalculatePositionSize(BarHistory bars, Int32 idx, Transaction t, Nullable`1 currentEquity) at WealthLab.ChartWPF.CoreChart.ListVisitor(PositionSize def, TransactionType selection, OrderType dic, Double setup2, BrokerBase reference3, String cust4, Boolean doinstance5) at WealthLab.ChartWPF.Chart.RunSetter(TransactionType spec)


I guess this exception is coming from using Orders property without null check. Is it not supposed to be null? Why is it null in my code use-case? What sets this property in the PositionSizer base class?

Overall, what am I doing wrong? Even with a minimal implementation, I could not get the sizers to work. Could there be any wrong with my installation?
0
- ago
#8
Like Cone told in Post #1, confirmed by the source code in your Post #7, your broker provider's boilerplate doesn't contain any instruction to create a Transaction - let alone assign a manual quantity:

0.001 is just an arbitrary "minimal" quantity when that's initialized with the transaction that doesn't have a manual quantity assigned.
0
Glitch8
 ( 10.62% )
- ago
#9
Try removing the NotImplemented exceptions in the broker provider. Those exceptions might be causing issues.
0
- ago
#10
@Eugene

Cone's advice was about implementing SizePosition method and not about broker adapter's methods. I have used the example code of PositionSizer from WL8 documentation page as it is - as shown in my code Post #7. That answers Cone's advice to implement the SizePosition method rather than keeping it default.

To your comment in Post #8 - "your broker provider's boilerplate doesn't contain any instruction to create a Transaction - let alone assign a manual quantity"; - I am testing the code using Debug mode by placing a breakpoint on the only line inside the BrokerAdapter's PlaceTrade method (throw new NotImplementedException()). The PlaceTrade method is supposed to receive a created transaction with the quantity already SET. There is no need to assign any quantity or create the transaction object inside that method. I do not understand what else you mean from that comment.

To your comment - "0.001 is just an arbitrary "minimal" quantity when that's initialized with the transaction that doesn't have a manual quantity assigned" - I understood that from Cone's first reply. However, my question is - "Why am I getting that quantity when the PositionSizer implementation (that I posted in the boilerplate) is returning a quantity of 9". The quantity is 0.001 also for Indexitor builtin sizer.

And to your previous comment (Post #6) - I have tested the built-in position sizers and they are either throwing an exception or giving me the same 0.001 as quantity. There are no Data Sources or Custom Market assignments in the code I posted (Post #7). I used a US symbol with the WL8-builtin market, with absolutely no customizations and also on a clean WL8 installation (without any other WL8 extensions). The project contains only two classes, as posted.

@Glitch

That NotImplementedException definitely throws an exception, but that is not my problem. If I put a breakpoint there in debug mode, I am expecting a quantity of 9 or something meaningful inside the (Transaction t) object. But I am receiving 0.001. That means I cannot send that order to my broker - even if I remove that exception and add some meaningful code there.
0
- ago
#11
QUOTE:
I used a US symbol with the WL8-builtin market, with absolutely no customizations


Are you sure? The very first screenshot reflects the Market to be customized i.e. "NSE - Capital Market - Stocks".
0
- ago
#12
I was referring to the code posted in Post #7. After the initial post, I created a new project to test it out and created the boilerplate (Post #7). It behaved the same way. I don't understand why a simple boiler plate project doesn't work in such a simple scenario.

I could have started #7 as a new thread. Sorry for the confusion.
0
Glitch8
 ( 10.62% )
- ago
#13
I see you're using an Advanced Position Sizer in a streaming chart and the manual BUY button. In this case, there's no Strategy involved so the use case isn't something we exercised. We'll add a bug for this and work on cleaning up the behavior for this case.
1
Best Answer
- ago
#14
Thanks Glitch.
0

Reply

Bookmark

Sort