Archive

Visual Studio

I record a Kata as I write a Xamarin.Forms app from scratch:

Advertisements

Let’s reformat the following types:

type Account =        
    | Checking of decimal
    | Savings  of decimal
    | Business of decimal

type Command =
    | Withdraw of Account * decimal
    | Deposit  of Account * decimal
    | Transfer of Account * Account * decimal

type Response =
    | Withdrawal       of State   * State
    | WithdrawalFailed of Account * decimal

    | Deposited        of State   * State             
    | DepositFailed    of Account * decimal

    | Transferred      of TransferSummary   * decimal
    | TransferFailed   of Account * Account * decimal

and State =
    | BeforeDeposit    of Account * decimal
    | AfterDeposit     of Account

    | BeforeWithdrawal of Account * decimal
    | AfterWithdrawal  of Account

and TransferSummary = { 
    FromBalanceBefore: Account ; ToBalanceBefore: Account
    FromBalanceAfter:  Account ; ToBalanceAfter:  Account }

To:

type Account =        
    | Checking of decimal
    | Savings  of decimal
    | Business of decimal

type Command =
    | Withdraw of Account * balance: decimal
    | Deposit  of Account * balance: decimal
    | Transfer of from:     Account * 
                  dest:     Account * 
                  balance:  decimal
type Response =
    | Withdrawal       of before: State * 
                          after:  State

    | WithdrawalFailed of Account * balance: decimal

    | Deposited        of before: State * 
                          after:  State     
                                 
    | DepositFailed    of Account * balance: decimal

    | Transferred      of TransferSummary   * balance: decimal
    | TransferFailed   of from:    Account * 
                          dest:    Account * 
                          balance: decimal
and State =
    | BeforeDeposit    of Account * balance: decimal
    | AfterDeposit     of Account

    | BeforeWithdrawal of Account * balance: decimal
    | AfterWithdrawal  of Account

and TransferSummary = { 
    FromBalanceBefore: Account ; ToBalanceBefore: Account
    FromBalanceAfter:  Account ; ToBalanceAfter:  Account }

Intro

This is my fourth post on my journey of building a trading app using F# and Xamarin.Forms. In my last post, I brain-dumped XAML and F# for selling shares of stock. In this post, I will brain-dump the syntax for buying shares of stock for both the UI and viewmodels.

Solution Explorer

I modeled my solution explorer to reflect my interpretation of Clean Architecture:

UI

The user-experience for buying shares is the following:

XAML

Here’s the XAML for the Buy page:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              xmlns:manageTrades="clr-namespace:ManageTrades.ViewModels;assembly=ManageTrades"              x:Class="FnTrade.BuyPage">

    <Grid Margin="10,5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Label Grid.Row="0" Grid.Column="0" Text="{Binding Symbol,     StringFormat='Buying ({0:F0})'}}" FontSize="Large" />
        <Label Grid.Row="2" Grid.Column="0" Text="{Binding StockPrice, StringFormat='{}{0:c} per share'}}"        FontSize="Medium" HorizontalTextAlignment="Start" HorizontalOptions="Start" />
        <Label Grid.Row="3" Grid.Column="0" Text="{Binding Total,      StringFormat='{}{0:c} Invested'}}" FontSize="Medium" HorizontalTextAlignment="Start" HorizontalOptions="Start" />
        <Label Grid.Row="4" Grid.Column="1" Text="{Binding Balance,    StringFormat='{}{0:c} balance'}}" FontSize="Large" HorizontalOptions="Start" />

        <Entry Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding BuyQty, Mode=TwoWay}"                 Placeholder="Enter number of shares to buy" HorizontalTextAlignment="Center" Keyboard="Numeric" />

        <Label Grid.Row="7" Grid.Column="1" Text="{Binding BuyValue, StringFormat='{}{0:c}'}" HorizontalTextAlignment="End" />

        <Button Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" Text="Confirm"                  Command="{Binding Confirm}" IsEnabled="{Binding CanBuy}" />
    </Grid>

</ContentPage>

The code-behind of the Buy page subscribes to a confirmation-request event:

using ManageTrades.ViewModels;
using Xamarin.Forms;

namespace FnTrade
{
    using static Core.Entities;
    using static Integration.Factories;

    public partial class BuyPage : ContentPage
    {
        public BuyPage(BuyViewModel viewModel)
        {
            InitializeComponent();
            BindingContext = viewModel;

            getDispatcher().ConfirmBuyRequested += async (s, e) =>
                {
                    var requestInfo = e as RequestInfo;
                    var confirmed = await DisplayAlert("Confirmation",
                                          $"Buying ({(requestInfo).Quantity }) shares of {requestInfo.Symbol}?",
                                          "Confirm", "Cancel");
                    if (confirmed) getDispatcher().ExecuteBuy(requestInfo);
                };
        }
    }
}

When the purchase of shares is confirmed, the app presents a purchase request confirmation.

The following is the XAML for the purchase shares request confirmation:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              x:Class="FnTrade.RequestBuyConfirmedPage">

    <Grid Margin="10,5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Text="Request Sent"                 FontSize="Large" TextColor="Green" FontAttributes="Bold" HorizontalOptions="Center" />

        <StackLayout Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" VerticalOptions="Center">
            <StackLayout  Orientation="Horizontal" VerticalOptions="Center">
                <Label Text="{Binding Quantity, StringFormat='Buy ({0:F0}) shares of'}}"                         FontSize="Medium" HorizontalOptions="Center" HorizontalTextAlignment="Center" />

                <Label Text="{Binding Symbol}"                        FontSize="Medium" HorizontalOptions="Start" />
            </StackLayout>

            <Label Grid.Row="2" Grid.Column="1" Text="{Binding Total, StringFormat='{}{0:c}'}}"                    FontSize="24" FontAttributes="Bold" HorizontalOptions="End" />
        </StackLayout>
    </Grid>
</ContentPage>

ViewModels

Here’s the viewmodel for buying shares:

namespace ManageTrades.ViewModels

open System
open System.Windows.Input
open Core.IntegrationLogic
open Core.Entities
open Integration.Factories
open Services

type BuyViewModel(info:SharesInfo) as this =

    inherit ViewModelBase()

    let dispatcher = getDispatcher()
    let accountId =  getAccountId()
    let broker =     getBroker() :> IBroker

    let mutable buyQty = ""
    let mutable canBuy = false
    let mutable buyValue = 0m

    let confirm = DelegateCommand( (fun _ -> this.ConfirmBuy()) ,
                                    fun _ -> true ) :> ICommand

    member this.Symbol     with get() = info.Shares.Symbol
    member this.Balance    with get() = info.Balance
    member this.StockPrice with get() = info.PricePerShare
    member this.Total      with get() = ((decimal)info.Shares.Qty * info.PricePerShare)

    member this.BuyQty
        with get() =      buyQty
        and  set(value) = buyQty <- value
                          let success , validQty = Int32.TryParse buyQty
                          if  success then this.CanBuy <- info.PricePerShare
                                                          *(decimal)validQty  <= this.Balance
                          else this.CanBuy <- false
                          this.UpdateBuyValue()
                          base.NotifyPropertyChanged(<@ this.BuyQty @>)
    member this.BuyValue
        with get() =      buyValue
        and  set(value) = buyValue <- value
                          base.NotifyPropertyChanged(<@ this.BuyValue @>)

    member this.CanBuy   with get() =      canBuy
                         and  set(value) = canBuy <- value
                                           base.NotifyPropertyChanged(<@ this.CanBuy @>)
    member this.Confirm = confirm

    member private this.ConfirmBuy() =
        if this.CanBuy then
            dispatcher.ConfirmBuy { AccountId = accountId
                                    Symbol    = this.Symbol
                                    Quantity  = Int32.Parse this.BuyQty }

    member private this.UpdateBuyValue() =
        let success , validQty = Int32.TryParse buyQty
        if  success
        then this.BuyValue <- (decimal)validQty * info.PricePerShare
        else this.BuyValue <- 0m

Here’s the viewmodel for the purchase request confirmation:

namespace ManageTrades.ViewModels

open Core.IntegrationLogic
open Core.Entities
open Services
open Integration.Factories

type RequestBuyConfirmedViewModel(request:RequestInfo) =

    inherit ViewModelBase()

    let broker = getBroker() :> IBroker
    let mutable total = 0m

    member this.Symbol   with get() = request.Symbol
    member this.Quantity with get() = request.Quantity
    member this.Total    with get() =      total
                         and  set(value) = total <- value
                                           base.NotifyPropertyChanged(<@ this.Total @>)
    member this.Load() =
        match broker.GetInfo request.Symbol with
        | Some info -> this.Total <- info.Price * (decimal) request.Quantity         | None      -> failwith (sprintf "Failed to retrieve stock information for %s" this.Symbol)

Domain Types

The following module reflects the entities that will be used in my trading app:

module Core.Entities

type StockInfo = {
    Symbol  : string
    Price   : decimal
    DayLow  : decimal
    DayHigh : decimal
}

type Shares = {
    AccountId : string
    Symbol    : string
    Qty       : int
}

type SharesInfo ={
    Shares        : Shares
    PricePerShare : decimal
    Total         : decimal
    Balance       : decimal
}

type Owner = { AccountId:string ; Symbol:string }

type RequestInfo = {
    AccountId : string
    Symbol    : string
    Quantity  : int
}

type InsufficientFunds = {
    PurchaseAttempt : RequestInfo
    Balance         : decimal
    StockPrice      : decimal
}    

type PurchaseResult =
    | PurchaseRequested of RequestInfo
    | UnknownSymbol     of RequestInfo
    | InvalidQuantity   of RequestInfo
    | InsufficientFunds of InsufficientFunds

type SellResult =
    | SellRequested        of RequestInfo
    | InsufficientQuantity of RequestInfo

Conclusion

The source code for this app can be found here on GitHub.

Intro

This post is the third post within my series of posts regarding the construction of a trading application using Xamarin.Forms and F#. In this post, I share my source code for selling shares of a stock.

User Interface

The following reflects the user-experience for selling a stock:

The following XAML reflects the above UI:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              xmlns:manageTrades="clr-namespace:ManageTrades.ViewModels;assembly=ManageTrades"              x:Class="FnTrade.SellPage">

    <Grid Margin="10,5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Label Grid.Row="0" Grid.Column="0" Text="{Binding Symbol,     StringFormat='Selling ({0:F0})'}}" FontSize="Large" />
        <Label Grid.Row="2" Grid.Column="0" Text="{Binding Shares,     StringFormat='{0:F0} shares available'}}" FontSize="Medium" HorizontalTextAlignment="Start" HorizontalOptions="Start" />
        <Label Grid.Row="3" Grid.Column="0" Text="{Binding StockPrice, StringFormat='{}{0:c} per share'}}"       FontSize="Medium" HorizontalTextAlignment="Start" HorizontalOptions="Start" />
        <Label Grid.Row="4" Grid.Column="1" Text="{Binding Total,      StringFormat='{}{0:c} Invested'}}" FontSize="Large" HorizontalOptions="Start" />

        <Entry Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding SellQty, Mode=TwoWay}"                 Placeholder="Enter number of shares to sell" HorizontalTextAlignment="Center" Keyboard="Numeric" />

        <Label Grid.Row="7" Grid.Column="1" Text="{Binding SellValue, StringFormat='{}{0:c}'}"                 HorizontalTextAlignment="End" />

        <Button Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" Text="Confirm"                  Command="{Binding Confirm}" IsEnabled="{Binding CanSell}" />
    </Grid>

</ContentPage>

Here’s some more UI in regards to the user story of selling shares:

ViewModels

The HomeViewmodel was implemented as follows:

namespace Home.ViewModels

open System.Windows.Input
open Core.IntegrationLogic
open Core.Entities
open Integration.Factories
open Services
open Search
open System.Collections.ObjectModel

type HomeViewModel() as this =

    inherit ViewModelBase()

    let dispatcher = getDispatcher()
    let accountId =  getAccountId()
    let broker =     getBroker() :> IBroker

    let searchCommand =
        DelegateCommand( (fun _ -> this.StockInfo <- getQuote broker this.Symbol) ,                           fun _ -> true) :> ICommand
    let sellCommand =
        DelegateCommand( (fun o -> dispatcher.Sell (o :?> SharesInfo) ) ,
                          fun _ -> true) :> ICommand
    let buyCommand =
        DelegateCommand( (fun o -> dispatcher.Buy  { Owner.AccountId=accountId ; Symbol=o :?> string} ) ,
                          fun _ -> true) :> ICommand

    let mutable symbol = ""
    let mutable stockInfo = None
    let mutable investments = ObservableCollection<SharesInfo>()

    member this.Load() =
        let result = broker.InvestmentsOf accountId
        this.Investments <- ObservableCollection<SharesInfo>(result)

    member this.Symbol
        with get() =      symbol
        and  set(value) = symbol <- value
                          base.NotifyPropertyChanged(<@ this.Symbol @>)
    member this.StockInfo
        with get() =      stockInfo
        and  set(value) = stockInfo <- value
                          base.NotifyPropertyChanged(<@ this.StockInfo @>)
    member this.Investments
        with get() =      investments
        and  set(value) = investments <- value
                          base.NotifyPropertyChanged(<@ this.Investments @>)

    member this.Search = searchCommand
    member this.Sell =   sellCommand
    member this.Buy =    buyCommand

The SellViewModel was implemented as follows:

namespace ManageTrades.ViewModels

open System
open System.Windows.Input
open Core.IntegrationLogic
open Core.Entities
open Integration.Factories
open Services

type SellViewModel(info:SharesInfo) as this =

    inherit ViewModelBase()

    let dispatcher = getDispatcher()
    let accountId =  getAccountId()
    let broker =     getBroker() :> IBroker

    let mutable sellQty = ""
    let mutable canSell = false
    let mutable sellValue = 0m

    let confirm = DelegateCommand( (fun _ -> this.ConfirmSell()) ,
                                    fun _ -> true ) :> ICommand

    member this.Symbol     with get() = info.Shares.Symbol
    member this.Shares     with get() = info.Shares.Qty
    member this.StockPrice with get() = info.PricePerShare
    member this.Total      with get() = ((decimal)info.Shares.Qty * info.PricePerShare)

    member this.SellQty
        with get() =      sellQty
        and  set(value) = sellQty <- value
                          let success , validQty = Int32.TryParse sellQty
                          if  success then this.CanSell <- validQty >  0 &&
                                                           validQty <= this.Shares
                          else this.CanSell <- false
                          this.UpdateSellValue()
                          base.NotifyPropertyChanged(<@ this.SellQty @>)
    member this.SellValue
        with get() =     sellValue
        and set(value) = sellValue <- value
                         base.NotifyPropertyChanged(<@ this.SellValue @>)

    member this.CanSell   with get() =      canSell
                          and  set(value) = canSell <- value
                                            base.NotifyPropertyChanged(<@ this.CanSell @>)
    member this.Confirm = confirm

    member private this.ConfirmSell() =
        if this.CanSell then
            dispatcher.ConfirmSell { AccountId = accountId
                                     Symbol    = this.Symbol
                                     Quantity  = Int32.Parse this.SellQty }

    member private this.UpdateSellValue() =
        let success , validQty = Int32.TryParse sellQty
        if  success
        then this.SellValue <- (decimal)validQty * info.PricePerShare
        else this.SellValue <- 0m

The RequestSellConfirmedViewModel is as follows:

namespace ManageTrades.ViewModels

open Core.IntegrationLogic
open Core.Entities
open Services
open Integration.Factories

type RequestSellConfirmedViewModel(request:RequestInfo) =

    inherit ViewModelBase()

    let broker = getBroker() :> IBroker
    let mutable total = 0m

    member this.Symbol   with get() = request.Symbol
    member this.Quantity with get() = request.Quantity
    member this.Total    with get() =      total
                         and  set(value) = total <- value
                                           base.NotifyPropertyChanged(<@ this.Total @>)
    member this.Load() =
        match broker.GetInfo request.Symbol with
        | Some info -> this.Total <- info.Price * (decimal) request.Quantity
        | None      -> failwith (sprintf "Failed to retrieve stock information for %s" this.Symbol)

Application Logic

My app subscribes to events so that it can manage page navigation:

    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();

            MainPage = new NavigationPage(new HomePage());
        }

        protected override void OnStart()
        {
            getDispatcher().SellRequested +=
                async (s, e) => await MainPage.Navigation.PushAsync(
                    new SellPage(new SellViewModel(e as SharesInfo)));

            getDispatcher().ExecuteSellRequested +=
                async (s, e) => await MainPage.Navigation.PushAsync(
                    new RequestSellConfirmedPage(new RequestSellConfirmedViewModel(e as RequestInfo)));
        }

        protected override void OnSleep() { }
        protected override void OnResume() { }
    }
}

The following are events that are declared within my Dispatcher class:

namespace Services

open System.Diagnostics
open System

type Dispatcher() =

    let sellRequested = new Event<EventHandler<_>,_>()
    let buyRequested =  new Event<EventHandler<_>,_>()
    let confirmSellRequested = new Event<EventHandler<_>,_>()
    let executeSellRequested = new Event<EventHandler<_>,_>()

    [<CLIEvent>]
    member this.SellRequested = sellRequested.Publish
    member this.Sell owner = sellRequested.Trigger(this , owner)

    [<CLIEvent>]
    member this.BuyRequested = buyRequested.Publish
    member this.Buy owner = buyRequested.Trigger(this , owner)

    [<CLIEvent>]
    member this.ConfirmSellRequested = confirmSellRequested.Publish
    member this.ConfirmSell info = confirmSellRequested.Trigger(this , info)

    [<CLIEvent>]
    member this.ExecuteSellRequested = executeSellRequested.Publish
    member this.ExecuteSell info = executeSellRequested.Trigger(this , info)

Domain Types

The following are my domain types:

module Core.Entities

type StockInfo = {
    Symbol  : string
    Price   : decimal
    DayLow  : decimal
    DayHigh : decimal
}

type Shares = {
    AccountId : string
    Symbol    : string
    Qty       : int
}

type SharesInfo ={
    Shares        : Shares
    PricePerShare : decimal
    Total         : decimal
}

type Owner = { AccountId:string ; Symbol:string }

type RequestInfo = {
    AccountId : string
    Symbol    : string
    Quantity  : int
}

type InsufficientFunds = {
    PurchaseAttempt : RequestInfo
    Balance         : decimal
    StockPrice      : decimal
}    

type PurchaseResult =
    | PurchaseRequested of RequestInfo
    | UnknownSymbol     of RequestInfo
    | InvalidQuantity   of RequestInfo
    | InsufficientFunds of InsufficientFunds

type SellResult =
    | SellRequested        of RequestInfo
    | InsufficientQuantity of RequestInfo

Conclusion

The source code to my implementation can be found on GitHub.

Intro

I decided to work on my Portfolio targeting the mobile space. In doing so, I plan on leveraging F# and Xamarin.Forms.

Methodology

I’m an advocate of Clean Architecture. As a result, I like to take time to configure my solution explorer. When I’m configuring my solution explorer, I want it to reflect application’s intent.

My solution explorer is below:

solutionexplorer

In the solution explorer above, I have partitioned my application into three segments:

  • Domain – This conveys the intent (i.e. features) of the application.
  • Integration – This manages IO interactions such as UI and web services.
  • Tests – This manages the feedback loop of correctness as I crank-out business logic.

Domain

The following image reflects the Domain segment of my solution explorer:

Domain.png

The image above reflects the projects that map to application features. Having a set of related features mapped to a project library could be beneficial when leveraging static analysis tools. Hence, related features can be isolated and scrutinized for maintainability metrics and code coverage. The information acquired from these static analysis tools can then be fed to QA so that they can make better informed decisions regarding their test strategies and exploratory testing.

Integration

The following image reflects the Integration segment of my solution explorer:

integration

The image above conveys the integration points that my domain relies on. In this case, my domain relies on UI (i.e. Xamarin.Forms) and services (i.e. web services). Note, how I did not configure my application within Solution Explorer to be Xamarin.Forms centric. Hence, I prefer that my domain be loosely coupled if not decoupled from any framework.

Tests

The following image reflects the Tests segment of my solution:
tests

The Tests segment of the solution explorer contains a TestAPI that not only feeds my unit tests input for execution, but also can also be used to feed the actual application itself. This is very useful when identifying anomalies within the application as business logic errors or integration errors. Thus, using this process of elimination will contribute towards a faster resolution as well as cost savings.

Workflow

I continue to build systems inside-out. Specifically, I focus on implementing the policy (aka: business rules) first. I then throw up just enough UI to flush-out edge-cases that my implementation did not yet handle. That activity is still focused on solidifying the policy so that I can expose as many anomalies as I can early within the SDLC (Software Development Life Cycle). I eventually implement the integration details such as a polished UI, web services, a database, etc.

Conclusion

In conclusion, I discussed my plans to build a trading application so that I can add it to my portfolio. I then discussed how I configure my solution explorer in an effort to provide separation of concerns for my overall solution.