Archive

Xamarin

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

In the previous post, I discussed my continued journey of learning by building a Xamarin.Forms app using F#.

In that post, I discussed the overall architecture:

SolutionExplorer.png

The architecture above is my attempt at practicing Robert C. Martin’s Clean Architecture. Specifically I make the features of the app the focus of the architecture and I treat everything else as details that are abstracted away via folders.

I treat the integration points of an application as details:

Integration.png

The Integration folder above harbors the modules that integrate with my domain. Such modules would be the application, services, and runtime dependencies.

Types

I began code construction of the application by writing functions to support trade transactions.

The following functions were implemented:

module ManageTradeImpl

open Services
open Core.Entities
open TestAPI

let buyShares (service:IBroker) context balance =
    (context , balance) ||> service.TryPurchase

let sellShares (service:IBroker) context =
    context |> service.TrySell

Amongst, the various types that were flushed out as I wrote functions, I wrote a Broker interface that will eventually rely on IO.

The broker interface was added to my Services library:

services

The Broker Interface is the following:

namespace Services

open Core.Entities

type IBroker =

    abstract member GetInfo       : string -> StockInfo option
    abstract member TryPurchase   : Shares -> decimal -> PurchaseResult
    abstract member TrySell       : Shares -> SellResult
    abstract member InvestmentsOf : string -> SharesWithPrice seq

Here’s how the buyShares function can get executed:

(*Test*)
let context = { AccountId= "Bizmonger"
                Symbol=    "ROK"
                Qty=        100 }

let result = buyShares (MockBroker()) context 5000m

Here’s the result:

val result : PurchaseResult =
  InsufficientFunds {PurchaseAttempt = {AccountId = "Bizmonger";
                                        Symbol = "ROK";
                                        Quantity = 100;};
                     Balance = 5000M;
                     StockPrice = 300M;}

Here’s another example for the buyShares function:

(*Test*)
let context = { AccountId= "Bizmonger"
                Symbol=    "ROK"
                Qty=        5 }

let result = buyShares (MockBroker()) context 5000m

Here’s the result:

val result : PurchaseResult = PurchaseRequested {AccountId = "Bizmonger";
                                                 Symbol = "ROK";
                                                 Quantity = 5;}

The following types have been defined so far for making trades:

module Core.Entities

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

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

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

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

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

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

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

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

type SellResult =
    | SellRequested        of SellInfo
    | InsufficientQuantity of SellInfo

User Interface

A domain is useless if there’s no interaction with it.

Here’s a sample UI that was built in XAML:

Emuator.png

Here’s the XAML that resulted in 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:converters="clr-namespace:FnTrade.Converters;assembly=FnTrade"              xmlns:home="clr-namespace:Home.ViewModels;assembly=Home"              x:Class="FnTrade.HomePage">

    <ContentPage.BindingContext>
        <home:HomeViewModel />
    </ContentPage.BindingContext>

    <ContentPage.Resources>
        <ResourceDictionary>
            <converters:InstanceToBoolConverter x:Key="InstanceToBoolConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

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

        <ListView x:Name="SharesListView" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"                    ItemsSource="{Binding Investments}"                   HorizontalOptions="Center" VerticalOptions="Center">
            <ListView.Header>
                <Grid >
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>

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

                    <Label Grid.Row="0" Grid.Column="0" Text="Symbol" FontAttributes="Bold" HorizontalTextAlignment="Center" />
                    <Label Grid.Row="0" Grid.Column="1" Text="Qty"    FontAttributes="Bold" HorizontalTextAlignment="Center" />
                    <Label Grid.Row="0" Grid.Column="2" Text="Price"  FontAttributes="Bold" HorizontalTextAlignment="Center" />
                    <Label Grid.Row="0" Grid.Column="3" Text="Total"  FontAttributes="Bold" HorizontalTextAlignment="Center" />

                    <BoxView Grid.Row="1" Grid.ColumnSpan="4" Color="Gray" Margin="5,0,5,5"                              HorizontalOptions="FillAndExpand" HeightRequest="1" />
                </Grid>
            </ListView.Header>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>

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

                            <Label Grid.Column="0" Text="{Binding Shares.Symbol}" HorizontalTextAlignment="Center" />
                            <Label Grid.Column="1" Text="{Binding Shares.Qty}"    HorizontalTextAlignment="Center" />
                            <Label Grid.Column="2" Text="{Binding PricePerShare,  StringFormat='{}{0:c}'}" HorizontalTextAlignment="Center" />
                            <Label Grid.Column="3" Text="{Binding Total,          StringFormat='{}{0:c}'}" HorizontalTextAlignment="Center" />
                        </Grid>

                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

        <Button Grid.Row="1" Grid.Column="0" Text="Buy More"                   Command="{Binding Buy}"                  CommandParameter="{Binding Source={x:Reference SharesListView}, Path=SelectedItem.Shares.Symbol}"                 IsEnabled="{Binding Source={x:Reference SharesListView},                                     Path=SelectedItem, Converter={StaticResource InstanceToBoolConverter}}" />

        <Button Grid.Row="1" Grid.Column="1" Text="Sell"                  Command="{Binding Sell}"                 CommandParameter="{Binding Source={x:Reference SharesListView}, Path=SelectedItem.Shares.Symbol}"                 IsEnabled="{Binding Source={x:Reference SharesListView},                                     Path=SelectedItem, Converter={StaticResource InstanceToBoolConverter}}" />
    </Grid>

</ContentPage>

ViewModel

In order for the UI to present dynamic content, I had to implement a viewmodel.
The following viewmodel was implemented:

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 { Owner.AccountId=accountId ; Symbol=o :?> string} ) ,
                          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<SharesWithPrice>()

    member this.Load() =
        let result = broker.InvestmentsOf accountId
        this.Investments <- ObservableCollection<SharesWithPrice>(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

Conclusion

This solution, which is a Work In Process, can be found here 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.