Reference guide

Conceptual overview

currencies usd, eur=1.1, gbp=1.3 tags group class: stocks, bonds, cash, gold group geo: us, eu, em assets US_FUND tags us, stocks=0.95, cash=0.05 price 30usd lot 10 EU_FUND tags eu, stocks=0.95, cash=0.05 price 22usd lot 10 EM_FUND tags em, stocks=0.95, cash=0.05 price 110usd lot 10 account Broker default fee 0.05% min 2usd default spread 0.2% US_FUND 700 buy,sell EU_FUND 200 buy,sell spread 0.3% mintrade 100usd maxtrade 5000eur buy 100 for *eur fee 50eur spread 0.1% mintrade 5000eur via OTC sell 100 for *eur fee 100eur spread 0.1% mintrade 5000eur via OTC EM_FUND 500 buy,sell GOLD_FUND 100 tags gold price 70usd buy,sell GLOBAL_BONDS_FUND 100 price 28usd tags us=0.4,eu=0.4,em=0.2,bonds buy,sell USD EUR price *usd,*gbp buy,sell 1000 for *usd spread 0.05% via broker buy,sell fee 0% spread 2% via bank GBP 10_000 tags eu,cash transfers group limit 50_000usd out usd -> tax_deferred via ACH max 2500usd out usd -> tax_deferred via WIRE max 50_000usd fee 0.2% max 50usd group limit none out us_fund -> tax_deferred fee 25usd account Tax_Deferred default fee 0.5% spread 0.2% US_FUND 300 buy,sell EU_FUND 100 buy,sell fee 2% USD transfers in * limit 10_000usd in * via wire fee 15usd in us_fund fee 25usd account Others USD EUR buy,sell Coins 50 tags gold price 300usd buy,sell spread 2% allocation stocks ~ 50%(group) bonds ~ 30%(group) gold ~ 10%(group) cash ~ 10%(total) us ~ 50%(group) eu ~ 25%(group) em ~ 25%(group) eu_fund@tax_deferred ~ 25%(tax_deferred) tax_deferred ~15%(broker) min current+5000usd coins ~ 25%(gold) buy cash@broker ~ 0%(account)

At first, I present a full example for you to have a quick syntax reference and a bird-eye view over the features of the service. I suppose this to be quite self-evident, but some explanations are due and we will work everything out on a bunch of minimal cases.

The service solves the mathematical optimization problem of finding the cheapest set of transactions to bring your portfolio as close as possible to the specified allocation. In order to state the problem, you have to describe your portfolio: assets, positions, allowed transactions, their fees and limits, and the desired allocation. The allocation is specified in terms of aggregates — groups of weighted positions (usually, exposure-weighted, as in this example), and rules the aggregates must follow.

Beware the mathematical optimization. It does not try to understand what you mean by your request, but blindly and strictly follows the problem specification. If the problem admits several solutions (as it usually does), you'll get a random one regardless of its desirability to you, depending on otherwise-inconsequential details of the problem statement (such as ordering of positions and rules, for instance). You need to double-check the obtained solution and maybe adjust either the problem or the solution itself. This service is implemented on the premise that it is easier to check the solution than to calculate it by pen, paper and spreadsheet.

A minimal example

currencies usd account Broker US_FUND 700 price 30usd buy,sell EU_FUND 1000 price 22usd buy,sell EM_FUND 500 price 110usd buy,sell USD allocation us_fund ~ 33%(total) eu_fund ~ 33%(total) em_fund ~ 33%(total)
  • currencies usd
    At first, you declare all currencies used in a portfolio. The first currency in the list will be the primary currency. All calculations are being done in the primary currency, with conversions taking place only upon reading the specification and printing the results; this could introduce small numerical errors.
  • account Broker
    Any position should belong to some account. You may want to introduce "virtual" accounts to hold cash, physical gold, crypto, realty and the like. All names should be unique, i.e. you can't have account and asset with the same name. Names are case-insensitive ("Broker", "BROKER" and "broker" all denote the same entity), may contain only letters, numbers and underscores, and cannot start with a number.
  • US_FUND 700
    This is a position — the name of an asset and its quantity. Quantities may be omitted if zero. You can use underscores to improve readability of numbers ("1000000" vs "1_000_000"). Fractional quantities are allowed, but see discussion on lot sizes. By default, the algorithm operates in whole units.
  • price 30usd
    This specifies the accounting price of the asset. The algorithm uses accounting values to find a desired asset allocation. That is, if you have to pay 100usd to buy an asset with accounting price of 80usd, this asset value will increase only by this 80usd.
  • buy,sell
    You have to say which transactions are possible with the asset. If you omit this block, the algorithm will not try to buy or sell this asset. You can specify multiple buy and sell transactions with different parameters (price, spread, fee, lot size), this is discussed later on.
  • US_FUND ~ 33%(total)
    This is the target allocation for the asset. It is necessary to indicate the reference aggregate when using percentage values. It can be the total portfolio, some account, another asset, tag, etc. The algorithm minimizes the sum of absolute differences between actual and target allocation, subject to imposed transaction and allocation constraints. Transaction losses also regarded as allocation difference. That is, the algorithm searches for the cheapest way to achieve an allocation closest (in absolute terms) to the target, but won't improve allocation error by 100usd if it costs 101usd.

Transaction losses and currency conversions

currencies usd, eur=1.1, gbp=1.3 account Broker default fee 0.05% min 2usd default spread 0.2% US_FUND 700 price 30usd buy,sell EU_FUND 1000 price 20eur buy,sell EM_FUND 500 price 110usd buy,sell USD EUR price *usd,*gbp buy,sell 1000 for *usd spread 0.05% via broker buy,sell fee 0% spread 2% via bank GBP 10000 allocation us_fund ~ 33%(total) eu_fund ~ 33%(total) em_fund ~ 33%(total)
  • currencies usd, eur=1.1
    For each secondary currency you have to specify its exchange rate to the primary currency. You can also use inverse quotes such as cny=1/6.96.
  • default fee 0.05% min 2usd default spread 0.2%
    Sets the default fee and spread for all transactions in this account that follow this lines. Can be abbreviated in a single line as "default fee 0.05% min 2usd spread 0.2%". Full fee specification see below.
  • USD EUR price *usd,*gbp buy,sell 1000 for *usd spread 0.05% via broker buy,sell fee 0% spread 2% via bank GBP 10000 There is a lot to say about this fragment:
    • You don't need to specify accounting price for currencies, as it is already defined in the currencies block.
    • The price block redefines accounting prices for this position. Asterisk means "calculate this value from existing information".
    • You can also redefine actual prices for transaction by means of the for block. By default, actual transaction prices are accounting prices after applying spread. Thus, "for *usd" makes currency exchange through broker available only for usd, and "buy,sell ... via bank" is equivalent to "buy,sell for *usd,*gbp ... via bank"
    • Note, how fee and spread gets overridden for EUR exchange via bank. If "fee 0%" is not specified, this transaction will entail the default fee.
    • You don't have (and better not to) specify transactions for both sides of the exchange, exception being exchange through different currency pairs such as the following: USD buy,sell 1000 via usd_eur EUR buy,sell 1000 via eur_usd which means you can either exchange 1000usd for appropriate amount of eur, or 1000eur for appropriate amount of usd. "1000" is the lot size for this transaction.
    • TIP: Currency conversion through a bank usually entails withdrawing money to the bank account, exchanging them, and depositing back to the brokerage account; but if this transfers are immaterial to the problem, you can specify the exchange directly in the brokerage account, as in this example.
    • Non-currency-exchange transactions are all the same, but all this intricacies typically arise just here.
  • (Tax modelling is under development.)

Transaction constraints

currencies usd, eur=1.1 account Broker US_FUND 700 price 30usd lot 10 buy,sell EU_FUND 1000 price 22usd buy,sell 10 spread 0.3% mintrade 100usd maxtrade 5000eur buy 100 for *eur fee 50eur spread 0.1% mintrade 5000eur via OTC sell 100 for *eur fee 100eur spread 0.1% mintrade 5000eur via OTC EM_FUND 500 price 110usd buy,sell USD EUR buy,sell fee 0.2% allocation us_fund ~ 33%(total) eu_fund ~ 33%(total) em_fund ~ 33%(total)
  • Constraints, as other parameters, can be specified separately for buy and sell transactions. And, by means of for and via blocks, separately for different currencies and "mediums".
  • "lot 10" — default lot size for all transactions. Unless specified otherwise, the asset will be bought and sold in multiples of this.
  • "buy,sell 10" — "10" is the lot size for this transaction.
  • "mintrade 100usd", "maxtrade 100usd" — minimum and maximum transaction volumes.

Multiple accounts

currencies usd assets US_FUND price 30usd lot 10 EU_FUND price 22usd lot 10 EM_FUND price 110usd lot 10 account A default fee 0.05% spread 0.2% US_FUND 700 buy,sell EU_FUND 1000 buy,sell EM_FUND 500 buy,sell USD transfers group limit 50000usd out usd -> B max 2500usd via ACH out usd -> B max 50000usd fee 0.2% max 50usd via WIRE out us_fund -> B fee 25usd account B default fee 0.5% spread 0.2% US_FUND 300 buy,sell EU_FUND 100 buy,sell fee 2% USD transfers in * limit 10000usd in * via wire fee 15usd in us_fund fee 25usd allocation us_fund ~ 33%(total) eu_fund ~ 33%(total) em_fund ~ 33%(total) eu_fund@b ~ 25%(b) b ~15%(a) min current+5000usd
  • The optional assets block contains asset properties common for all accounts. If this block is omitted, the properties defined on the first occurrence of the asset will be set as default
  • The transfers block describes position transfers. out clauses define possible outbound transfers, in clauses specify fees and limits at the receiving account (you don't have to match out and in clauses: absence of a matching in block just means there is no fees and limits at the receiving side).
  • Transfer fees are specified and calculated exactly the same way as transaction fees. Fees specified in the out clause are charged at the sending account, in the in clause — at the receiving account.
  • If you can't pay the transfer fee due to imposed constraints, the transfer is impossible and this situation is not reported.
  • If you have several means of transferring an asset between accounts, you can distinguish them with the via block.
  • in * via wire fee 15usd
    * matches all incoming assets, you can list asset names separated by comma instead.
    via wire matches only those assets which are received through the specified channel.
  • If an incoming transfer matches several in clauses, then all of them apply (fees add up, all limits must be respected).
  • group limit 50000usd
    This clause specifies group limit, i.e. total volume of all transfers defined after this clause must be no more than 50000usd. Both in and out transfers count. If you want to close a group, use group limit none after the last transfer in the group (or group limit amount to start the next).
  • eu_fund@b ~ 25%(b)
    It you want to specify allocation for an asset (or tag, see below) in a specific account, use "@" sign. You could write eu_fund@b ~ 25%(a), i.e. aim to have eu_fund@b about 25% of the "A" account value, but that would be strange.
  • b ~ 15%(a) min current+5000usd
    Make the algorithm try to have the value of "B" account to be 15% of the "A" account, but fund it with at least 5000usd. You can also specify desired minimum in the hard sum (min 20000usd), or specify mandatory withdrawal (max current-5000usd). If you have multiple minimums/maximums specified, the strictest apply.
  • min and max define hard limits, which are enforced whatever the cost. The service will warn you when you should relax these limits for the allocation to be feasible. It tries to find as little adjustment as possible, but it may not be the best from your point of view and may not be accurate.

Exposures

currencies usd, eur=1.1 tags group class: stocks, bonds, cash, gold group geo: us, eu, em account Broker US_FUND 700 tags us, stocks=0.95, cash=0.05 price 30usd buy,sell EU_FUND 1000 tags eu, stocks=0.95, cash=0.05 price 22usd buy,sell EM_FUND 500 tags em, stocks=0.95, cash=0.05 price 110usd buy,sell GOLD_FUND 100 tags gold price 70usd buy,sell GLOBAL_BONDS_FUND 100 price 28usd tags us=0.4,eu=0.4,em=0.2,bonds buy,sell USD tags us,cash EUR tags eu,cash buy,sell account Others USD tags us,cash EUR tags eu,cash buy,sell Coins 50 tags gold price 300usd buy,sell spread 2% allocation stocks ~ 50%(group) bonds ~ 30%(group) cash ~ 10%(group) gold ~ 10%(group) us ~ 50%(group) eu ~ 25%(group) em ~ 25%(group) coins ~ 25%(gold) buy cash@broker ~ 0%(account)
  • Asset allocation is rarely defined in terms of specific tickers. Usually, it is exposures that you need to achieve by allocating money to specific tickers. In this service exposures are defined by means of tags.
  • At first, you have to list all the tags you're going to use.
  • group geo: us, eu, em
    Tags may be grouped. A group of tags may be used as a reference aggregate, and it will have a separate chart in the "Charts" tab. Free-standing tags should be defined before tag groups.
  • tags us, stocks=0.95, cash=0.05
    You assign tags to assets in order to define exposures. Each asset tag has a weight, unit by default. Weights may be greater than unit and may be negative (to reflect, for instance, leveraged and inverse funds). There is no need for the grouped tags' weights to add up to a unit, as well as for each asset to belong to all groups
  • us ~ 50%(group) cash@broker ~ 0%(account)
    You may use keywords group and account instead of actual tag group and account names to refer to the respective aggregate. You can specify allocation rules for any aggregate except tag group, and use any aggregate as a reference aggregate. Note, that it is of little sense to use total portfolio as the reference aggregate for US exposure, as a significant part of our portfolio (gold) cannot be attributed to any region. Thus, we specify that US-attributed assets should amount to 50% of only those assets that do have regional attribution.
  • coins ~ 25%(gold) buy
    buy means that only buying transactions on that asset are possible (equivalent of specifying buy instead of buy,sell for any occurrence of this asset). You can also use sell to prevent buying and hold to prohibit all the trades. This constraints can also be specified for positions and accounts (restricting transactions for all positions in the respective accounts). When several constraints are imposed (even indirectly), the strictest apply. I.e. if you write eur hold eur@broker buy the last one will have no effect, and eur buy eur@broker sell will make eur@broker non-tradeable. This constraint does not affect asset transfers.

Fee specification

The transaction fee in its full glory is specified thus:
fee 1usd + 0.01% + 1eur/unit min 2gbp min 0.02% max 0.05% max 1000gbp use gbp

  • 1usd — fixed part, charged per transaction;
    0.01% — variable part as percent of the transaction volume;
    1eur/unit — variable part, charged per unit of asset (not per lot!);
    min 2gbp — minimum fixed fee, charged when fixed+variable is less than the specified amount;
    min 0.02% — minimum variable fee, charged when fixed+variable is less than the specified percent of the transaction volume;
    max 0.05% — fee cap specified as percent of the transaction volume;
    max 1000gbp — absolute fee cap;
    use gbp — in which currency charge the fee (for asset transfers, see below).
  • You can mix different currencies in the specification, but all amounts are immediately converted to the primary currency.
  • The greater of the minimums and the lesser of the maximums applies. Maximum take precedence over minimum. That is:
    fee = min(max(fixed+variable, fixed_min, variable_min), fixed_max, variable_max)
  • For trades, fee is charged in the price currency. For money transfers — in the currency being transferred. For other asset transfers — in the currency specified in the use block.

Allocation rules

target_aggregate ~ 15%(ref_aggregate) ~ 10_000usd ~ 500units ~ current+5000usd ~ current-5000usd min -10_000usd max 10_000usd ~min 10_000eur min 500units min 15%(ref_aggregate) min current min current+5000usd min current-5000usd buy sell hold
  • The pattern is [target_aggregate] [rule] [rule] [rule]. Note the absence of commas between rules.
  • A [rule] is either one of buy, sell, hold, or constructed as follows: [type] [value].
    Where [type] is one of
    • ~ — "target", the algorithm will minimize the deviation from the value,
    • min — "hard" minimum, enforced by the algorithm, may be reported as infeasible
    • max — "hard" maximum, enforced by the algorithm, may be reported as infeasible,
    • ~min — "soft" minimum, the algorithm minimizes the negative deviation from the value,
    • ~max — "soft" maximum, the algorithm minimizes the positive deviation from the value.
    And [value] is one of
    • 10_000usd — currency value (may be negative),
    • 500units — quantity in units of the asset (may be negative),
    • current — current value of the aggregate,
    • current+5000usd — current value of the aggregate plus (minus) currency value,
    • 15%([ref_aggregate]) — percentage value of the reference aggregate.
    Reference aggregate ([ref_aggregate]) may be
    • total — total portfolio value,
    • account — total value of the respective account (if the target aggregate specifies account),
    • group — total value of the respective tag group (for tags),
    • [aggregate name] — i.e. the name of an account, asset, position ([asset name]@[account name]), tag, tag-in-account ([tag name]@[account name]).