From 1ce85617824716fc6ae3ef9b843dd0f4d8b28ea8 Mon Sep 17 00:00:00 2001 From: Natercio Moniz Date: Sat, 11 Jul 2026 15:24:54 +0100 Subject: [PATCH] Fix selector behaviour (#26) Co-authored-by: Natercio Moniz Co-committed-by: Natercio Moniz --- LICENSE.md | 55 ---------------------------------- cmd/any2anexoj-cli/main.go | 8 ++--- internal/report.go | 20 +++++++++---- internal/selectors.go | 3 ++ licenses/biter777-countries.md | 24 +++++++++++++++ licenses/jedib0t-go-pretty.md | 21 +++++++++++++ 6 files changed, 66 insertions(+), 65 deletions(-) create mode 100644 licenses/biter777-countries.md create mode 100644 licenses/jedib0t-go-pretty.md diff --git a/LICENSE.md b/LICENSE.md index 53d1f3d..6a34f51 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -618,58 +618,3 @@ an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. - diff --git a/cmd/any2anexoj-cli/main.go b/cmd/any2anexoj-cli/main.go index c2887da..ae0fb89 100644 --- a/cmd/any2anexoj-cli/main.go +++ b/cmd/any2anexoj-cli/main.go @@ -25,7 +25,7 @@ var ( format = pflag.StringP("format", "f", "table", "Output format: table or csv") ofAPIKey = pflag.String("open-figi-api-key", "", "An OpenFIGI API key for faster report generation (better rate api rate limits)") // TODO: improve documentation on selectors - selectors = pflag.StringSlice("selectors", nil, "Only process entries that conform to all the selectors:") + selectors = pflag.StringSlice("selectors", nil, "Only process entries that conform to all the selectors: code, assetCountry") ) func main() { @@ -51,13 +51,11 @@ func run(ctx context.Context) error { slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: logLevel}))) if platform == nil || len(*platform) == 0 { - slog.Error("--platform flag is required") - os.Exit(1) + return fmt.Errorf("--platform flag is required") } if lang == nil || len(*lang) == 0 { - slog.Error("--language flag is required") - os.Exit(1) + return fmt.Errorf("--language flag is required") } reader, err := getReader(*platform, *ofAPIKey) diff --git a/internal/report.go b/internal/report.go index fb72ea1..b8e5a59 100644 --- a/internal/report.go +++ b/internal/report.go @@ -68,7 +68,8 @@ func BuildReport(ctx context.Context, reader RecordReader, writer ReportWriter, case <-ctx.Done(): return ctx.Err() case <-progTicker.C: - slog.InfoContext(ctx, "Progress update", + slog.InfoContext( + ctx, "Progress update", slog.Int64("total_records", buysCount+sellsCount), slog.Int64("sell_records", sellsCount), slog.Int64("buy_records", buysCount), @@ -108,10 +109,13 @@ func BuildReport(ctx context.Context, reader RecordReader, writer ReportWriter, // processRecord either adds buys to the queue or consumes buys from the queue when processing a // sell record. -// Selectors are only applied on sells for performance reasons. It's much cheaper to just accumulate -// buys and only actually inspect a record once a sell happens due to potential network requests to +// +// NOTE: Selectors are only applied when processing sell records for performance reasons. It's much +// cheaper to just accumulate buys and only actually inspect any records once a sell happens. This +// avoids potential network requests to for every single record. func processRecord(ctx context.Context, q *FillerQueue, rec Record, sel Selector, writer ReportWriter) error { - slog.Debug("Report: processing record", + slog.Debug( + "Report: processing record", slog.String("symbol", rec.Symbol()), slog.String("side", rec.Kind().String()), ) @@ -122,7 +126,8 @@ func processRecord(ctx context.Context, q *FillerQueue, rec Record, sel Selector case KindSell: if !sel(rec) { - slog.Debug("Report: skipping record", + slog.Debug( + "Report: skipping record", slog.String("symbol", rec.Symbol()), slog.String("side", rec.Kind().String()), ) @@ -137,6 +142,11 @@ func processRecord(ctx context.Context, q *FillerQueue, rec Record, sel Selector return ErrInsufficientBoughtVolume } + // Since we don't apply selectors while processing buys we need to apply them here. + if !sel(buy) { + continue + } + matchedQty, filled := buy.Fill(unmatchedQty) if filled { diff --git a/internal/selectors.go b/internal/selectors.go index 895e300..3362898 100644 --- a/internal/selectors.go +++ b/internal/selectors.go @@ -16,12 +16,15 @@ func And(a, b Selector) Selector { } } +// OnlyNature will only select records with the given Nature n (G01, G20, etc...). func OnlyNature(n Nature) Selector { return func(r Record) bool { return r.Nature() == n } } +// OnlyAssetCountry will only select records with the given ISO code c (620 for Portugal, 196 for +// Cyprus, etc...). func OnlyAssetCountry(c int64) Selector { return func(r Record) bool { return r.AssetCountry() == c diff --git a/licenses/biter777-countries.md b/licenses/biter777-countries.md new file mode 100644 index 0000000..3d43387 --- /dev/null +++ b/licenses/biter777-countries.md @@ -0,0 +1,24 @@ +Copyright (c) 2019 Biter, biter2004@yandex.ru. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/jedib0t-go-pretty.md b/licenses/jedib0t-go-pretty.md new file mode 100644 index 0000000..a284bc6 --- /dev/null +++ b/licenses/jedib0t-go-pretty.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 jedib0t + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.