Compare commits

...

43 Commits

Author SHA1 Message Date
Olivia Appleton 565b77f5d7
Update tests, dependency 2022-12-17 19:17:17 -05:00
Olivia Appleton 13836b28b6
update test 2022-11-16 22:07:02 -05:00
Olivia Appleton b12c204329
more refactoring 2022-11-16 04:55:52 -05:00
Olivia Appleton 956a209b31
more refactoring yay 2022-11-16 04:37:15 -05:00
Olivia Appleton 68651da283
Use UUID5 table names 2022-11-16 04:09:21 -05:00
Olivia Appleton c6d11324c1
Begin an API for plugins to store state 2022-11-16 04:01:39 -05:00
Olivia Appleton a5471218cd
Refactor to simplify 2022-11-16 04:00:26 -05:00
Olivia Appleton 0bb0a097ac
Reduce duplication 2022-11-16 00:35:58 -05:00
Olivia Appleton cb5aabe509
Fix some linting errors 2022-11-16 00:30:10 -05:00
Olivia Appleton e5c8246328
Close #18 2022-11-16 00:06:56 -05:00
Olivia Appleton b2a81127aa
essentially back to feature parity 2022-11-09 19:09:18 -05:00
Olivia Appleton a98fe5b712
Fix username, ignore new submodule 2022-11-09 03:51:07 -05:00
Olivia Appleton 5d66ba3a0a
bump version 2022-11-09 02:30:47 -05:00
Olivia Appleton 7b2afaa2b7
Start porting application code to new API 2022-11-09 02:20:06 -05:00
Olivia Appleton 1841d2f8ed
Don't use an account to read values 2022-11-09 01:57:27 -05:00
Olivia Appleton 6bfda3cabf
Add a state submodule 2022-11-09 01:57:05 -05:00
Olivia Appleton 82cad4b3b6 update submodules 2022-11-07 01:19:02 -05:00
Olivia Appleton cf68cf0bb3 put in correct file 2022-11-07 01:15:55 -05:00
Olivia Appleton cf217d11a8 add submodule for future trading actions 2022-11-07 01:14:22 -05:00
Olivia Appleton 31d4352cea
Satisfy the linter 2022-11-06 00:35:01 -04:00
Olivia Appleton f500b42d4b
Fix OtherMarketValue._binary_value() when probability is None 2022-11-06 00:15:17 -04:00
Olivia Appleton f90f7f5723
Satisfy the linter 2022-11-05 23:31:05 -04:00
Olivia Appleton d5d7fe496d
Finalize merge
Note that this seems to run correctly for everything
except bot market 16 (the Margorie Taylor Green one)
2022-11-05 23:13:21 -04:00
Olivia Appleton aad22db53e
Merge branch 'dev' 2022-11-05 23:05:23 -04:00
Olivia Appleton c9a02d57c4 Merge branch 'main' of github.com:LivInTheLookingGlass/ManifoldMarketManager 2022-11-06 03:00:12 +00:00
Olivia Appleton 69dd014601 Update databases 2022-11-06 02:57:22 +00:00
Olivia Appleton 51a82154ff
Bump version, graph 2022-11-05 22:44:21 -04:00
Olivia Appleton e870a8c20a
formatting 2022-11-05 22:42:36 -04:00
Olivia Appleton 3a7178b03d
Update dependency 2022-11-05 22:42:24 -04:00
Olivia Appleton adc00407f4
Include patch from main 2022-11-05 22:42:04 -04:00
Olivia Appleton ffdc4be3b2
Begin new CLI (#31) 2022-11-05 22:41:41 -04:00
Olivia Appleton 91ccd0065c
Formatting 2022-11-05 22:39:41 -04:00
Olivia Appleton 7d824a9b02
Add dependabot 2022-11-05 17:27:49 -04:00
Olivia Appleton da8b2bb2bd
Update main branch name 2022-11-05 01:41:27 -04:00
Olivia Appleton a4dae3edc7
Update configuration slightly 2022-11-05 01:40:58 -04:00
Olivia Appleton 1e66014619
Change main branch 2022-11-05 01:36:04 -04:00
Olivia Appleton 1abf48386a
Update badges 2022-11-05 01:32:50 -04:00
Olivia Appleton 80bbe9900a
Enable Github Actions 2022-11-05 01:22:44 -04:00
Olivia Appleton 6de1efa10e
Fix OtherMarketValue._binary_value (#30)
* Fix OtherMarketValue._binary_value

* Changes for production server

* remove stray breakpoint

* Fix up `this`, update tests
2022-11-02 17:32:53 -04:00
Olivia Appleton 00bc568f2d
Begin account support 2022-10-31 16:02:35 -04:00
Olivia Appleton ce75d73911
Update image name 2022-10-31 16:01:42 -04:00
Olivia Appleton 61b84cb887
Rename top level directory 2022-10-31 15:59:12 -04:00
Olivia Appleton 61fcc47695
License change (#25)
* change LICENSE from AGPLv3 to MIT
* Update setup.cfg to reflect this
2022-10-28 18:14:11 -04:00
338 changed files with 4690 additions and 2767 deletions

View File

@ -1,8 +1,8 @@
[bumpversion]
current_version = 0.7.0.2
current_version = 0.8.0.19
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\.(?P<build>\d+)
serialize = {major}.{minor}.{patch}.{build}
[bumpversion:file:setup.cfg]
[bumpversion:file:src/__init__.py]
[bumpversion:file:ManifoldMarketManager/__init__.py]

15
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,15 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "gitsubmodule" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

61
.github/workflows/codacy.yml vendored Normal file
View File

@ -0,0 +1,61 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow checks out code, performs a Codacy security scan
# and integrates the results with the
# GitHub Advanced Security code scanning feature. For more information on
# the Codacy security scan action usage and parameters, see
# https://github.com/codacy/codacy-analysis-cli-action.
# For more information on Codacy Analysis CLI in general, see
# https://github.com/codacy/codacy-analysis-cli.
name: Codacy Security Scan
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '34 10 * * 2'
permissions:
contents: read
jobs:
codacy-security-scan:
permissions:
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
name: Codacy Security Scan
runs-on: ubuntu-latest
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout code
uses: actions/checkout@v3
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
- name: Run Codacy Analysis CLI
uses: codacy/codacy-analysis-cli-action@d840f886c4bd4edc059706d09c6a1586111c540b
with:
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
# You can also omit the token and run the tools that support default configurations
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
verbose: true
output: results.sarif
format: sarif
# Adjust severity of non-security issues
gh-code-scanning-compat: true
# Force 0 exit code to allow SARIF file generation
# This will handover control about PR rejection to the GitHub side
max-allowed-issues: 2147483647
# Upload the SARIF file generated in the previous step
- name: Upload SARIF results file
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: results.sarif

74
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,74 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '18 8 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

24
.github/workflows/dependency-review.yml vendored Normal file
View File

@ -0,0 +1,24 @@
# Dependency Review Action
#
# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
#
# Source repository: https://github.com/actions/dependency-review-action
# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v3
- name: 'Dependency Review'
uses: actions/dependency-review-action@v2
with:
license-check: true
vulnerability-check: true
fail-on-scopes: runtime, development, unknown

8
.gitmodules vendored
View File

@ -1,6 +1,6 @@
[submodule "PyManifold"]
path = src/PyManifold
path = ManifoldMarketManager/PyManifold
url = https://github.com/LivInTheLookingGlass/PyManifold
[submodule "src/test/manifold"]
path = src/test/manifold
url = https://github.com/LivInTheLookingGlass/manifold/
[submodule "src/manibots"]
path = ManifoldMarketManager/manibots
url = https://github.com/LivInTheLookingGlass/manibots.git

682
LICENSE
View File

@ -1,661 +1,21 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
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 AGPL, see
<https://www.gnu.org/licenses/>.
MIT License
Copyright (c) 2022 Olivia Appleton
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.

View File

@ -17,7 +17,7 @@ MYPY?=true
# If specified, perform benchmarking (WARNING: silently disables code coverage)
BENCHMARK?=false
pytest_args?= -vl
pytest_args?= -vl --ignore=./ManifoldMarketManager/manibots
ifeq ($(BENCHMARK),true)
pytest_args += --benchmark-min-time=0.05 --benchmark-sort=fullname --benchmark-group-by=fullfunc --benchmark-verbose
@ -29,7 +29,7 @@ pytest_args += --flake8 --isort --pydocstyle
endif
ifeq ($(LINT),only)
pytest_args += --ignore=./src/test --ignore=./src/PyManifold/tests
pytest_args += --ignore=./ManifoldMarketManager/test --ignore=./ManifoldMarketManager/PyManifold/tests
COV=false
endif
@ -38,7 +38,7 @@ pytest_args += --mypy --mypy-ignore-missing-imports
endif
ifneq ($(COV),false)
pytest_args += --cov=src --cov-branch --cov-report=term
pytest_args += --cov=ManifoldMarketManager --cov-branch --cov-report=term
endif
ifneq ($(https_proxy), )
@ -80,7 +80,7 @@ test_%:
.PHONY: _test
_test:
@source env_personal.sh && ManifoldMarketManager_NO_CACHE=1 PYTHONPATH=${PYTHONPATH}:./src/PyManifold $(PY) -m pytest src $(pytest_args) -k 'not mypy-status' --ignore=./src/test/manifold
@source env_personal.sh && ManifoldMarketManager_NO_CACHE=1 PYTHONPATH=${PYTHONPATH}:./ManifoldMarketManager/PyManifold $(PY) -m pytest ManifoldMarketManager $(pytest_args) -k 'not mypy-status'
.PHONY: dependencies
ifeq ($(MYPY),true)
@ -96,7 +96,7 @@ endif
.PHONY: run_%
# Run a specific account
run_%: LICENSE dependencies
@source env_$*.sh && $(PY) -O -m src
@source env_$*.sh && $(PY) -O -m ManifoldMarketManager run
.PHONY: run
# Run all known accounts
@ -125,7 +125,7 @@ build: dependencies clean LICENSE
.PHONY: clean
# Clean up after a build
clean:
@rm -rf build dist logs .benchmarks .pytest_cache src/*.egg-info test-reporter-latest-linux-amd64 .coverage .requirements.txt coverage.xml
@rm -rf build dist logs .benchmarks .pytest_cache ManifoldMarketManager/*.egg-info test-reporter-latest-linux-amd64 .coverage .requirements.txt coverage.xml
@cd docs && $(MAKE) clean $(MFLAGS)
@mkdir logs
@ -139,7 +139,7 @@ publish: build test_all upload_coverage
# Build a dependency graph of this package
graph: dependencies
@$(PIP) install pydeps $(USER_FLAG)
@PYTHONPATH=${PYTHONPATH}:./src/PyManifold $(PY) -m pydeps --noshow --cluster -x src.test pytest --max-bacon 100 -T png src
@PYTHONPATH=${PYTHONPATH}:./ManifoldMarketManager/PyManifold $(PY) -m pydeps --noshow --cluster -x ManifoldMarketManager.test pytest --max-bacon 100 -T png ManifoldMarketManager
.PHONY: import_%
# Create one or more markets from example.json and add them to the specified account
@ -158,7 +158,7 @@ upload_coverage: .coverage
# Make a PyInstaller exe (status: not yet supported)
exe:
$(PYTHON) -m pip install -r requirements.txt --user
$(PYTHON) -OO -m PyInstaller -F -n "research_bundle" $(ENTRY) --add-data src/defaults:src/defaults
$(PYTHON) -OO -m PyInstaller -F -n "research_bundle" $(ENTRY) --add-data ManifoldMarketManager/defaults:ManifoldMarketManager/defaults
.PHONY: html
# Make html docs (status: beta)

BIN
ManifoldMarketManager.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@ -0,0 +1 @@
Subproject commit a4d537edc7b872f0902a6d33e19ed46fe3c65254

View File

@ -0,0 +1,195 @@
"""Manifold Market Manager Library.
This module provides the ability to construct a Manifold Markets manager bot. In particular, it gives you a framework
to have automatically resolving markets. A future goal may be to allow trading, but this will likely be done using
limits, rather than time sensitive trades. Expect intervals of minutes, not microseconds.
In order to use this library, some things need to be loaded in your environment variables. See the comments below for
more information on this.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from logging import getLogger
from os import getenv
from pathlib import Path
from pickle import dumps, loads
from sqlite3 import register_adapter, register_converter
from sys import path as _sys_path
from typing import TYPE_CHECKING, Generic, Iterable, Literal, Mapping, Sequence, Union, cast
from warnings import warn
from attrs import define, field
_sys_path.append(str(Path(__file__).parent.joinpath("PyManifold")))
from .account import Account # noqa: E402
from .caching import parallel # noqa: E402
from .consts import AnyResolution, Outcome, T # noqa: E402
from .util import DictDeserializable # noqa: E402
if TYPE_CHECKING: # pragma: no cover
from logging import Logger
from typing import Any
from .consts import OutcomeType
@define(slots=False) # type: ignore
class Rule(ABC, Generic[T], DictDeserializable):
"""The basic unit of market automation, rules defmine how a market should react to given events."""
tags_used: set[str] = field(factory=set, init=False, repr=False, hash=False)
logger: Logger = field(init=False, repr=False, hash=False)
def __attrs_post_init__(self) -> None:
"""Ensure that the logger object is created."""
if hasattr(super(), '__attrs_post_init__'):
super().__attrs_post_init__() # type: ignore
self.logger = getLogger(f"{type(self).__qualname__}[{id(self)}]")
@abstractmethod
def _value(
self,
market: Market,
account: Account
) -> T: # pragma: no cover
...
def __getstate__(self) -> Mapping[str, Any]:
"""Remove sensitive/non-serializable state before dumping to database."""
state = self.__dict__.copy()
if 'tags_used' in state:
del state['tags_used']
if 'logger' in state:
del state['logger']
return state
def value(
self,
market: Market,
account: Account,
format: Literal['NONE'] | OutcomeType = 'NONE',
refresh: bool = False
) -> AnyResolution:
"""Return the resolution value of a market, appropriately formatted for its market type."""
ret = self._value(market, account)
if (ret is None) or (ret == "CANCEL") or (format == 'NONE'):
return cast(AnyResolution, ret)
elif format in Outcome.BINARY_LIKE():
return self.__binary_value(market, account, ret)
elif format in Outcome.MC_LIKE():
return self.__multiple_choice_value(market, account, ret)
raise ValueError()
def __binary_value(self, market: Market, account: Account, ret: Any) -> float:
if not isinstance(ret, str) and isinstance(ret, Sequence):
(ret, ) = ret
elif isinstance(ret, Mapping) and len(ret) == 1:
ret = cast(Union[str, int, float], next(iter(ret.items()))[0])
if isinstance(ret, (int, float, )):
return ret
elif isinstance(ret, str):
return float(ret)
raise TypeError(ret, format, market)
def __multiple_choice_value(self, market: Market, account: Account, ret: Any) -> Mapping[int, float]:
if isinstance(ret, Mapping):
ret = {int(val): share for val, share in ret.items()}
elif isinstance(ret, (int, str)):
ret = {int(ret): 1}
elif isinstance(ret, float) and ret.is_integer():
ret = {int(ret): 1}
elif isinstance(ret, Iterable):
ret = {int(val): 1 for val in ret}
else:
raise TypeError(ret, format, market)
return normalize_mapping(ret)
@abstractmethod
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str: # pragma: no cover
raise NotImplementedError(type(self))
def explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
"""Explain how the market will resolve and decide to resolve."""
return self._explain_abstract(indent, **kwargs)
def explain_specific(self, market: Market, account: Account, indent: int = 0, sig_figs: int = 4) -> str:
"""Explain why the market is resolving the way that it is."""
return self._explain_specific(market, account, indent, sig_figs)
def _explain_specific(self, market: Market, account: Account, indent: int = 0, sig_figs: int = 4) -> str:
f_val = parallel(self._value, market, account)
warn("Using a default specific explanation. This probably isn't what you want!")
ret = self.explain_abstract(indent=indent).rstrip('\n')
ret += " (-> "
val = f_val.result()
if val == "CANCEL":
ret += "CANCEL)\n"
return ret
if isinstance(self, rule.DoResolveRule) or market.market.outcomeType == Outcome.BINARY:
if val is True or val == 100:
ret += "YES)\n"
elif not val:
ret += "NO)\n"
else:
ret += f"{round_sig_figs(cast(float, val))}%)\n"
elif market.market.outcomeType == Outcome.PSEUDO_NUMERIC:
ret += round_sig_figs(cast(float, val))
elif market.market.outcomeType in Outcome.MC_LIKE():
val_ = cast(Mapping[int, float], val)
ret += "{"
for idx, (key, weight) in enumerate(val_.items()):
if idx:
ret += ", "
ret += f"{key}: {round_sig_figs(weight * 100)}%"
ret += "})\n"
return ret
from . import market, rule, util # noqa: E402
from .market import Market # noqa: E402
from .rule import DoResolveRule, ResolutionValueRule # noqa: E402
from .util import dynamic_import, get_client, normalize_mapping, require_env, round_sig_figs # noqa: E402
register_adapter(rule.Rule, dumps) # type: ignore
register_converter("Rule", loads)
register_adapter(market.Market, dumps)
register_converter("Market", loads)
VERSION = "0.8.0.19"
__version_info__ = tuple(int(x) for x in VERSION.split('.'))
__all__ = [
"__version_info__", "VERSION", "DoResolveRule", "ResolutionValueRule", "Rule", "Market", "get_client", "rule",
"util", "require_env"
]
if getenv("DEBUG"): # pragma: no cover
import sys
def info(type, value, tb): # type: ignore # pragma: no cover
"""Open a postmortem pdb prompt on exception, if able."""
if hasattr(sys, 'ps1') or not sys.stderr.isatty():
# we are in interactive mode or we don't have a tty-like
# device, so we call the default hook
sys.__excepthook__(type, value, tb)
else:
import pdb
import traceback
# we are NOT in interactive mode, print the exception...
traceback.print_exception(type, value, tb)
print()
# ...then start the debugger in post-mortem mode.
pdb.post_mortem(tb)
sys.excepthook = info
# dynamically load optional plugins where able to
exempt = {
'__init__', '__main__', '__pycache__', 'application', 'test', 'PyManifold', 'py.typed', 'http_cache.sqlite',
*__all__
}
dynamic_import(__file__, __name__, __all__, exempt)

View File

@ -0,0 +1,39 @@
"""Runner script for ManifoldMarketManager.
Includes a variety of command line options which can be explored by invoking with the `--help` flag.
Note that the behavior of this runner script is not yet stable. Many changes are going to occur between its current
state and the desired production behavior. These changes include:
- [ ] Multiple Account Support
- [ ] Create markets using JSON
- [ ] Import markets using JSON
- [ ] Queue markets to be created in the future
- [ ] Run hooks on various Markets, ex: when they are
- [ ] queued
- [ ] created
- [ ] resolved
- [ ] cancelled
- [ ] Use an event loop (maybe asyncio) rather that a sleep loop
- [ ] Allow rules to store data in the db (and clean up after them)
"""
from __future__ import annotations
from logging import DEBUG, INFO, basicConfig, getLogger
from os import getenv
from .application import parse_args
args = parse_args()
if args.logging:
# Enable logging
basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
level=(INFO if not getenv("DEBUG") else DEBUG),
filename=getenv("LogFile"),
)
logger = getLogger(__name__)
exit(args.func(**vars(args)))

View File

@ -0,0 +1,55 @@
"""Contains information needed to manage accounts."""
from __future__ import annotations
from dataclasses import dataclass, field, fields
from os import getenv
from pickle import dumps, loads
from typing import cast
from cryptography.fernet import Fernet
prop_to_env = {
"ManifoldToken": "ManifoldAPIKey",
"GithubToken": "GithubAccessToken",
}
@dataclass
class Account:
"""Represent a Manifold account and its related API access keys."""
# Section: Manifold
ManifoldToken: str
ManifoldUsername: str = ''
# Section: GitHub
GithubUsername: str = ''
GithubToken: str = ''
# Section: Telegram
TelegramAPIKey: str = ''
TelegramChatID: str = ''
# Internals section
key: bytes = field(default_factory=Fernet.generate_key, compare=False, repr=False)
def to_bytes(self) -> bytes:
"""Generate encrypted bytes to represent this account."""
return Fernet(self.key).encrypt(dumps(self))
@staticmethod
def from_bytes(buff: bytes, key: bytes) -> 'Account':
"""Decrypt and deserialize an Account from an encrypted bytestring."""
return cast(Account, loads(Fernet(key).decrypt(buff)))
@staticmethod
def from_env() -> 'Account':
"""Try to infer an account from environment variables."""
kwargs = {}
for f in fields(Account):
if f.init:
value = getenv(prop_to_env.get(f.name, f.name), None)
if value is not None:
kwargs[f.name] = value
return Account(key=Fernet.generate_key(), **kwargs)

View File

@ -0,0 +1,434 @@
"""Contains functions which are needed to run the runner script, but nowhere else."""
from __future__ import annotations
from argparse import ArgumentParser, Namespace
from datetime import datetime, timedelta
from itertools import count
from logging import getLogger
from os import getenv
from time import sleep
from traceback import format_exc
from typing import TYPE_CHECKING, Tuple, cast
from . import market, require_env
from .consts import AVAILABLE_SCANNERS, EnvironmentVariable, MarketStatus, Response
from .state.persistant import register_db, remove_markets, select_markets, update_market
if TYPE_CHECKING: # pragma: no cover
from sqlite3 import Connection
from typing import Any
from . import Market
from .account import Account
logger = getLogger(__name__)
def parse_args(*args: Any, **kwargs: Any) -> Namespace:
"""Parse arguments for the CLI."""
main_parser = ArgumentParser()
main_parser.add_argument('--no-logging', action='store_false', dest='logging', default=True)
main_parser.add_argument('-v', '--verbose', action='count', default=0)
main_parser.add_argument('--just-parse', action='store_true', default=False)
subparsers = main_parser.add_subparsers()
import_parser = subparsers.add_parser('import')
import_parser.add_argument('account', action='store', type=str)
import_parser.add_argument('file', action='store', type=str, nargs='?')
import_parser.add_argument('--interactive', action='store_true')
group = import_parser.add_mutually_exclusive_group(required=False)
group.add_argument('--yaml', action='store_true')
group.add_argument('--json', action='store_true')
group.add_argument('--repl', action='store_true')
import_parser.set_defaults(func=import_command)
# TODO: add templates here
quick_import_parser = subparsers.add_parser('quick-import')
quick_import_parser.add_argument('account', action='store', type=str)
quick_import_parser.add_argument(
'--resolve-when', nargs=2, action='append',
help="Should be a qualified rule name, followed by a JSON string of its initializers"
)
quick_import_parser.add_argument(
'--resolve-to', nargs=2, action='append', required=True,
help="Should be a qualified rule name, followed by a JSON string of its initializers"
)
quick_import_parser.add_argument('-n', '--notes', type=str, action='store', default='')
group = quick_import_parser.add_mutually_exclusive_group(required=True)
group.add_argument('-u', '--url', action='store', type=str)
group.add_argument('-s', '--slug', action='store', type=str)
group.add_argument('-i', '--id', dest='id_', action='store', type=str)
quick_import_parser.add_argument('-c', '--check-rate', action='store', dest='rate', help='Check rate in hours')
quick_import_parser.add_argument('-rnd', '--round', dest='round_', action='store_true')
quick_import_parser.add_argument('-cur', '--current', action='store_true')
quick_import_parser.add_argument(
'-rd', '--rel-date', action='store', dest='rel_date',
help='Please give as "year/month/day" or "year-month-day". Used in: poll, git PR'
)
quick_import_parser.add_argument(
'-pr', '--pull-request', action='store', dest='pr_slug', help='Please give as "owner/repo/num"'
)
quick_import_parser.add_argument('-pb', '--pull-binary', action='store_true', dest='pr_bin')
quick_import_parser.add_argument('-rs', '--random-seed', action='store')
quick_import_parser.add_argument('-rr', '--random-rounds', action='store', type=int, default=1)
quick_import_parser.add_argument('-ri', '--random-index', action='store_true')
quick_import_parser.add_argument('-is', '--index-size', action='store', type=int)
quick_import_parser.set_defaults(func=quick_create_command)
# must finish import_parser first
create_parser = subparsers.add_parser('create', parents=[import_parser], add_help=False)
create_parser.add_argument('--queue-if-no-funds', action='store_true')
create_parser.add_argument('--queue', action='store_true')
create_parser.set_defaults(func=create_command)
quick_create_parser = subparsers.add_parser('quick-create')
quick_create_parser.add_argument(
'type', type=str, choices=["BINARY", "PSEUDO_NUMERIC", "FREE_RESPONSE", "MULTIPLE_CHOICE"]
)
quick_create_parser.add_argument('account', action='store', type=str)
quick_create_parser.add_argument('close-on', action='store', type=str)
quick_create_parser.add_argument(
'--resolve-when', nargs=2, action='append',
help="Should be a qualified rule name, followed by a JSON string of its initializers"
)
quick_create_parser.add_argument(
'--resolve-to', nargs=2, action='append', required=True,
help="Should be a qualified rule name, followed by a JSON string of its initializers"
)
quick_create_parser.add_argument('-n', '--notes', type=str, action='store', default='')
quick_create_parser.set_defaults(func=quick_create_command)
scan_parser = subparsers.add_parser('scan')
scan_parser.add_argument('--disable-all', action='store_false', dest='all_scanners', default=True)
for scanner in AVAILABLE_SCANNERS:
scan_parser.add_argument(
f'--enable-{scanner.replace(".", "-")}', dest='scanners', action='append_const', const=scanner
)
scan_parser.set_defaults(func=scan_command)
run_parser = subparsers.add_parser('run')
run_parser.add_argument('--enable-all-scanners', action='store_true', dest='all_scanners', default=False)
for scanner in AVAILABLE_SCANNERS:
run_parser.add_argument(
f'--enable-{scanner.replace(".", "-")}', dest='scanners', action='append_const', const=scanner
)
run_parser.add_argument(
'-r', '--refresh', action='store_true',
help="Ignore time last checked and look at all markets immediately"
)
run_parser.add_argument('-c', '--console-only', action='store_true')
run_parser.set_defaults(func=run_command)
loop_parser = subparsers.add_parser('loop', parents=[run_parser], add_help=False)
loop_parser.add_argument(
'-p', '--period', action='store', type=float, help='how long to wait between loops, in minutes'
)
loop_parser.add_argument(
'-t', '--times', action='store', type=float, default=float('inf'),
help='how many times to loop (default infinity)'
)
loop_parser.set_defaults(func=loop_command)
edit_parser = subparsers.add_parser('edit')
edit_parser.add_argument('ids', nargs='+', type=int)
edit_parser.set_defaults(func=edit_command)
remove_parser = subparsers.add_parser('remove')
remove_parser.add_argument('ids', nargs='+', type=int)
remove_parser.add_argument('--assume-yes', '-y', action='store_true')
remove_parser.set_defaults(func=remove_command)
list_parser = subparsers.add_parser('list')
list_parser.add_argument('--stats', action='store_true')
list_parser.add_argument('--sig-figs', action='store', type=int, default=4)
list_parser.set_defaults(func=list_command)
parsed: Namespace = main_parser.parse_args(*args, **kwargs)
if hasattr(parsed, 'all_scanners') and parsed.all_scanners:
parsed.scanners = AVAILABLE_SCANNERS
return parsed
def _print_uncaught_args(kwargs: dict[str, Any]) -> None:
if getenv("DEBUG") and kwargs:
print("Unrecognized arguments:")
print("\n".join(f'{key}: {value}' for key, value in kwargs.items()))
def import_command(**kwargs: Any) -> int:
"""Import markets from a file without creating any."""
_print_uncaught_args(kwargs)
return -1
def quick_import_command(
url: str | None = None,
slug: str | None = None,
id_: str | None = None,
rel_date: str | None = None,
random_index: bool = False,
random_seed: bool = False,
random_rounds: int = 1,
round_: bool = False,
current: bool = False,
index_size: int | None = None,
pr_slug: str | None = None,
pr_bin: bool = False,
**kwargs: Any
) -> int:
"""Import a single market using the old-style arguments."""
_print_uncaught_args(kwargs)
if url:
mkt = Market.from_url(url)
elif slug:
mkt = Market.from_slug(slug)
else:
mkt = Market.from_id(cast(str, id_))
if rel_date:
sections = rel_date.split('/')
if len(sections) == 1:
sections = rel_date.split('-')
try:
date: None | tuple[int, int, int] = tuple(int(x) for x in sections) # type: ignore[assignment]
except ValueError:
raise
else:
date = None
if random_index:
from .rule.generic import ResolveRandomIndex
mkt.resolve_to_rules.append(
ResolveRandomIndex(random_seed, size=index_size, rounds=random_rounds)
)
if round_:
from .rule.manifold.this import RoundValueRule
mkt.resolve_to_rules.append(RoundValueRule()) # type: ignore
if current:
from .rule.manifold.this import CurrentValueRule
mkt.resolve_to_rules.append(CurrentValueRule())
if pr_slug:
from .rule.github import ResolveToPR, ResolveToPRDelta, ResolveWithPR
pr_: list[str | int] = list(pr_slug.split('/'))
pr_[-1] = int(pr_[-1])
pr = cast(Tuple[str, str, int], tuple(pr_))
mkt.do_resolve_rules.append(ResolveWithPR(*pr))
if date:
mkt.resolve_to_rules.append(ResolveToPRDelta(*pr, datetime(*date)))
elif pr_bin:
mkt.resolve_to_rules.append(ResolveToPR(*pr))
else:
raise ValueError("No resolve rule provided")
if not mkt.do_resolve_rules:
if not date:
from .rule.manifold.this import ThisMarketClosed
mkt.do_resolve_rules.append(ThisMarketClosed())
else:
from .rule.generic import ResolveAtTime
mkt.do_resolve_rules.append(ResolveAtTime(datetime(*date)))
with register_db() as conn:
idx = max(((0, ), *conn.execute("SELECT id FROM markets;")))[0] + 1
conn.execute("INSERT INTO markets values (?, ?, ?, ?);", (idx, mkt, 1, None))
conn.commit()
msg = f"Successfully added as ID {idx}!"
print(msg)
logger.info(msg)
return 0
def create_command(**kwargs: Any) -> int:
"""Create markets from a file, then import them."""
_print_uncaught_args(kwargs)
return -1
def quick_create_command(**kwargs: Any) -> int:
"""Quickly create a single market without need for a file, then import it."""
_print_uncaught_args(kwargs)
return -1
def scan_command(**kwargs: Any) -> int:
"""Scan services for markets to create."""
_print_uncaught_args(kwargs)
return -1
def run_command(
refresh: bool = False,
console_only: bool = False,
scanners: list[str] = None, # type: ignore[assignment]
**kwargs: Any
) -> int:
"""Go through our markets and take actions if needed."""
_print_uncaught_args(kwargs)
return main(refresh, console_only) or 0
def loop_command(
period: float = 5,
times: float = 5,
**kwargs: Any
) -> int:
"""Run this service multiple times."""
# TODO: turn this into an event queue instead
for i in count():
if i > times:
break
run_command(**kwargs)
sleep(period * 60)
return 0
def edit_command(**kwargs: Any) -> int:
"""Edit a market from a temporary file or repl."""
_print_uncaught_args(kwargs)
return -1
def remove_command(
ids: list[int],
assume_yes: bool = False,
**kwargs: Any
) -> int:
"""Remove markets from the database."""
_print_uncaught_args(kwargs)
for id_ in ids:
with register_db() as conn:
try:
((mkt, ), ) = conn.execute(
"SELECT market FROM markets WHERE id = ?;",
(id_, )
)
except ValueError:
print(f"No market with id {id_} exists.")
return 1
question = f'Are you sure you want to remove {id_}: "{mkt.market.question}" (y/N)?'
if (assume_yes or input(question).lower().startswith('y')):
conn.execute(
"DELETE FROM markets WHERE id = ?;",
(id_, )
)
conn.commit()
logger.info(f"{id_} removed from db")
return 0
def list_command(
stats: bool = False,
verbose: int = 0,
sig_figs: int = 4,
**kwargs: Any
) -> int:
"""List markets from the database in varying verbosity."""
_print_uncaught_args(kwargs)
with register_db() as conn:
id_: int
mkt: Market
check_rate: float
last_check: datetime | None
for id_, mkt, check_rate, last_check in conn.execute("SELECT * FROM markets"):
info = f"Market ID: {id_} (internal), {mkt.market.id} (manifold)\n"
hours = int(check_rate)
minutes = (check_rate - hours) // 60
seconds = ((check_rate - hours) / 60 - minutes) // 60
info += f"Checks every {hours}:{minutes}:{seconds}\tLast checked: {last_check}\n"
info += f"Question: {mkt.market.question}\n"
if verbose:
info += mkt.explain_abstract(sig_figs=sig_figs) + "\n"
print(info)
return 0
def watch_reply(conn: Connection, id_: int, mkt: Market, account: Account, console_only: bool = False) -> None:
"""Watch for a reply from the bot manager in order to check the bot's work."""
text = (f"Hey, we need to resolve {id_} to {mkt.resolve_to(account)}. It currently has a value of "
f"{mkt.current_answer(account)}. This market is called: {mkt.market.question}\n\n")
# text += mkt.explain_abstract()
try:
text += "\n\n" + mkt.explain_specific(account)
except Exception:
print(format_exc())
logger.exception("Unable to explain a market's resolution automatically")
if not console_only:
from .confirmation.telegram import tg_main
response = tg_main(text, account)
else:
if input(text + " Use this default value? (y/N) ").lower().startswith("y"):
response = Response.USE_DEFAULT
elif input("Cancel the market? (y/N) ").lower().startswith("y"):
response = Response.CANCEL
else:
response = Response.NO_ACTION
if response == Response.NO_ACTION:
return
elif response == Response.USE_DEFAULT:
resp = mkt.resolve()
elif response == Response.CANCEL:
resp = mkt.cancel()
if mkt.status != MarketStatus.RESOLVED:
raise RuntimeError(resp)
conn.execute(
"DELETE FROM markets WHERE id = ?;",
(id_, )
)
conn.commit()
@require_env(EnvironmentVariable.ManifoldAPIKey, EnvironmentVariable.DBName)
def main(refresh: bool = False, console_only: bool = False) -> int:
"""Go through watched markets and act on rules (resolve, trade, etc)."""
mkt: market.Market
fallback_account = Account.from_env()
with register_db() as conn:
for id_, mkt, check_rate, last_checked, account in select_markets((), db=conn):
if account is None:
account = fallback_account
msg = f"Currently checking ID {id_} for account {account}: {mkt.market.question}"
print(msg)
logger.info(msg)
# print(mkt.explain_abstract())
print("\n\n" + mkt.explain_specific(account) + "\n\n")
check = (refresh or not last_checked or (datetime.now() > last_checked + timedelta(hours=check_rate)))
msg = f' - [{"x" if check else " "}] Should I check?'
print(msg)
logger.info(msg)
if check:
check = mkt.should_resolve(account)
msg = f' - [{"x" if check else " "}] Is elligible to resolve?'
print(msg)
logger.info(msg)
if check:
watch_reply(conn, id_, mkt, account, console_only)
if mkt.market.isResolved:
msg = " - [x] Market resolved, removing from db"
print(msg)
logger.info(msg)
remove_markets(conn, id_)
conn.commit()
update_market(
row_id=id_,
market=mkt,
check_rate=check_rate,
last_checked=datetime.now() if check else last_checked,
account=account,
db=conn,
)
conn.commit()
return 0

View File

@ -8,12 +8,14 @@ from typing import TYPE_CHECKING, Generic, TypeVar, cast
import requests_cache
from .consts import EnvironmentVariable
if TYPE_CHECKING: # pragma: no cover
from typing import Any, Callable, Optional
T = TypeVar("T")
CACHE = not getenv("ManifoldMarketManager_NO_CACHE")
CACHE = not getenv(EnvironmentVariable.NO_CACHE)
if CACHE:
requests_cache.install_cache(expire_after=360, allowable_methods=('GET', ))
executor = ThreadPoolExecutor(thread_name_prefix="ManifoldMarketManagerWorker_")

View File

@ -0,0 +1 @@
"""Allow users to request confirmation on platforms other than the command line."""

View File

@ -0,0 +1,111 @@
"""Allow users to request confirmation via Telegram."""
from __future__ import annotations
from asyncio import get_event_loop, new_event_loop, set_event_loop
from dataclasses import dataclass
from logging import getLogger
from os import getenv
from typing import TYPE_CHECKING, cast
from telegram import __version__ as TG_VER
try:
from telegram import __version_info__
except ImportError:
__version_info__ = (0, 0, 0, "", 0) # type: ignore[assignment]
if __version_info__ < (20, 0, 0, "alpha", 1):
raise RuntimeError(
f"This example is not compatible with your current PTB version {TG_VER}. To view the "
f"{TG_VER} version of this example, "
f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
)
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CallbackQueryHandler
if TYPE_CHECKING: # pragma: no cover
from telegram import Update
from telegram.ext import ContextTypes
from ..account import Account
from ..consts import EnvironmentVariable, Response
logger = getLogger(__name__)
@dataclass
class State:
"""Keeps track of global state for while the Telegram Bot is running."""
application: Application = None # type: ignore
last_response: Response = Response.NO_ACTION
last_text: str = ""
state = State()
keyboard1 = [
[
InlineKeyboardButton("Do Nothing", callback_data=Response.NO_ACTION),
InlineKeyboardButton("Resolve to Default", callback_data=Response.USE_DEFAULT),
],
[InlineKeyboardButton("Cancel Market", callback_data=Response.CANCEL)],
]
keyboard2 = [
[
InlineKeyboardButton("Yes", callback_data="YES"),
InlineKeyboardButton("No", callback_data="NO"),
],
]
async def buttons(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Parse the CallbackQuery and update the message text."""
logger.info("Got into the buttons handler")
query = update.callback_query
if query is None or query.data is None:
raise ValueError()
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
await query.answer()
logger.info("Got a response from Telegram (%r)", query.data)
if query.data in ("YES", "NO"):
state.last_text += "\n" + query.data
await query.edit_message_text(text=state.last_text)
if query.data != "YES":
logger.info("Was not told yes. Backing out to ask again")
reply_markup = InlineKeyboardMarkup(keyboard1)
await query.edit_message_reply_markup(reply_markup=reply_markup)
else:
logger.info("Confirmation received, shutting dowm Telegram subsystem")
get_event_loop().stop() # lets telegram bot know it can stop
else:
state.last_response = Response(int(query.data))
logger.info("This corresponds to %r", state.last_response)
reply_markup = InlineKeyboardMarkup(keyboard2)
state.last_text += f"\nSelected option: {state.last_response.name}. Are you sure?"
await query.edit_message_text(text=state.last_text)
await query.edit_message_reply_markup(reply_markup=reply_markup)
def tg_main(text: str, account: Account) -> Response:
"""Run the bot."""
async def post_init(self): # type: ignore
reply_markup = InlineKeyboardMarkup(keyboard1)
if account.TelegramChatID is None:
raise EnvironmentError()
await self.bot.send_message(text=text, reply_markup=reply_markup, chat_id=int(account.TelegramChatID))
application = Application.builder().token(
cast(str, getenv(EnvironmentVariable.TelegramAPIKey))
).post_init(post_init).build()
application.add_handler(CallbackQueryHandler(buttons))
state.application = application
state.last_text = text
set_event_loop(new_event_loop())
application.run_polling()
return state.last_response

View File

@ -53,6 +53,9 @@ AVAILABLE_RULES = [
"manifold.user.ResolveToUserCreatedVolume"
]
AVAILABLE_SCANNERS: list[str] = []
ENV_PREFIX = "ManifoldMarketManager"
class EnvironmentVariable(str, Enum):
"""Represents an Environment Variable that is used by this program."""
@ -64,6 +67,8 @@ class EnvironmentVariable(str, Enum):
TelegramAPIKey = "TelegramAPIKey" # Optional. Run --console-only if you don't want to use a Telegram channel
TelegramChatID = "TelegramChatID" # Optional. See above
LogFile = "LogFile" # REQUIRED. What file to put the log in
AccountKeys = f"{ENV_PREFIX}_KEYS"
NO_CACHE = f"{ENV_PREFIX}_NO_CACHE"
class MarketStatus(Enum):

@ -0,0 +1 @@
Subproject commit 54ae9aa13f37f30a01ab26824f24714d2a23542b

View File

@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, cast
from pyee import EventEmitter
from pyee.cls import evented
from .account import Account
from .caching import parallel
from .consts import EnvironmentVariable, MarketStatus, Outcome
from .rule import get_rule
@ -37,25 +38,25 @@ class Market(DictDeserializable):
Events
------
before_check(market: Market):
after_check(market: Market):
before_check(market: Market, account: Account):
after_check(market: Market, account: Account):
Called before/after a market is checked. Please don't put anything intensive in here.
before_create(market: Market):
after_create(market: Market):
before_create(market: Market, account: Account):
after_create(market: Market, account: Account):
Called before/after a market is created.
before_resolve(market: Market, outcome: AnyResolution):
after_resolve(market: Market, outcome: AnyResolution, response: Response):
before_resolve(market: Market, account: Account, outcome: AnyResolution):
after_resolve(market: Market, account: Account, outcome: AnyResolution, response: Response):
Called before/after a market is resolved. Please don't put anything intensive in here.
before_remove(market: Market):
after_remove(market: Market):
before_remove(market: Market, account: Account):
after_remove(market: Market, account: Account):
Called before/after a market is removed from the database.
"""
market: APIMarket = field(repr=False, compare=False)
client: ManifoldClient = field(default_factory=get_client, repr=False, compare=False)
client: ManifoldClient = field(repr=False, compare=False, default_factory=get_client)
notes: str = field(default='')
do_resolve_rules: list[Rule[Optional[bool]]] = field(default_factory=list)
resolve_to_rules: list[Rule[AnyResolution]] = field(default_factory=list)
@ -93,17 +94,11 @@ class Market(DictDeserializable):
def __setstate__(self, state: Mapping[str, Any]) -> None:
"""Rebuild sensitive/non-serializable state after retrieving from database."""
self.__dict__.update(state)
self.client = get_client()
if not hasattr(self, "event_emitter"):
self.event_emitter = EventEmitter()
self.event_emitter._lock = Lock()
self.__post_init__()
@property
def id(self) -> str:
"""Return the ID of a market as reported by Manifold."""
return self.market.id
def refresh(self) -> None:
"""Ensure market data is recent."""
self.market = self.client.get_market_by_id(self.market.id)
@ -120,20 +115,17 @@ class Market(DictDeserializable):
@classmethod
def from_url(cls, url: str, *args: Any, **kwargs: Any) -> Market:
"""Reconstruct a Market object from the market url and other arguments."""
api_market = get_client().get_market_by_url(url)
return cls(api_market, *args, **kwargs)
return cls(get_client().get_market_by_url(url), *args, **kwargs)
@classmethod
def from_slug(cls, slug: str, *args: Any, **kwargs: Any) -> Market:
"""Reconstruct a Market object from the market slug and other arguments."""
api_market = get_client().get_market_by_slug(slug)
return cls(api_market, *args, **kwargs)
return cls(get_client().get_market_by_slug(slug), *args, **kwargs)
@classmethod
def from_id(cls, id: str, *args: Any, **kwargs: Any) -> Market:
"""Reconstruct a Market object from the market ID and other arguments."""
api_market = get_client().get_market_by_id(id)
return cls(api_market, *args, **kwargs)
return cls(get_client().get_market_by_id(id), *args, **kwargs)
@classmethod
def from_dict(cls, env: ModJSONDict) -> Market:
@ -147,28 +139,29 @@ class Market(DictDeserializable):
env_copy[name] = rules
return super().from_dict(env_copy)
def _after_resolve(self, market: Market, outcome: AnyResolution, response: Response) -> None:
self.client.create_comment(self.market, self.explain_specific(), mode='markdown')
def _after_resolve(self, market: Market, account: Account, outcome: AnyResolution, response: Response) -> None:
try:
get_client(account).create_comment(self.market, self.explain_specific(account), mode='markdown')
except Exception:
self.logger.exception("Could not post a comment")
def explain_abstract(self, **kwargs: Any) -> str:
"""Explain how the market will resolve and decide to resolve."""
# set up potentially necessary information
if "max_" not in kwargs:
kwargs["max_"] = self.market.max
if "time_rules" not in kwargs:
kwargs["time_rules"] = self.do_resolve_rules
if "value_rules" not in kwargs:
kwargs["value_rules"] = self.resolve_to_rules
kwargs.update({
"max_": kwargs.get("max_", self.market.max),
"time_rules": kwargs.get("time_rules", self.do_resolve_rules),
"value_rules": kwargs.get("value_rules", self.resolve_to_rules),
})
return explain_abstract(**kwargs)
def explain_specific(self, sig_figs: int = 4) -> str:
def explain_specific(self, account: Account, sig_figs: int = 4) -> str:
"""Explain why the market is resolving the way that it is."""
shim = ""
rule_: Rule[Any]
for rule_ in self.do_resolve_rules:
shim += rule_.explain_specific(market=self, indent=1, sig_figs=sig_figs)
if not (self.market.isResolved or self.should_resolve()):
shim += rule_.explain_specific(market=self, account=account, indent=1, sig_figs=sig_figs)
if not (self.market.isResolved or self.should_resolve(account)):
ret = (f"This market is not resolving, because none of the following are true:\n{shim}\nWere it to "
"resolve now, it would follow the decision tree below:\n")
else:
@ -176,24 +169,23 @@ class Market(DictDeserializable):
"decision tree below:\n")
ret += "- If the human operator agrees:\n"
for rule_ in self.resolve_to_rules:
ret += rule_.explain_specific(market=self, indent=1, sig_figs=sig_figs)
ret += rule_.explain_specific(market=self, account=account, indent=1, sig_figs=sig_figs)
ret += "\nFinal Value: "
ret += self.__format_resolve_to(sig_figs)
ret += self.__format_resolve_to(sig_figs, account)
return ret
def __format_resolve_to(self, sig_figs: int) -> str:
val = self.resolve_to()
def __format_resolve_to(self, sig_figs: int, account: Account) -> str:
val = self.resolve_to(account)
if val == "CANCEL":
ret = "CANCEL"
pass
elif isinstance(val, bool) or self.market.outcomeType == Outcome.BINARY:
defaults: dict[AnyResolution, str] = {
True: "YES", 100: "YES", 100.0: "YES",
False: "NO"
}
if val in defaults:
ret = defaults[val]
else:
ret = round_sig_figs(cast(float, val), sig_figs) + "%"
return defaults[val]
return round_sig_figs(cast(float, val), sig_figs) + "%"
elif self.market.outcomeType in Outcome.MC_LIKE():
assert not isinstance(val, (float, str))
ret = "{"
@ -202,16 +194,15 @@ class Market(DictDeserializable):
ret += ", " * bool(idx)
ret += f"{key}: {round_sig_figs(weight * 100 / total, sig_figs)}%"
ret += "}"
else:
ret = str(val)
return ret
return ret
return str(val)
def should_resolve(self) -> bool:
def should_resolve(self, account: Account) -> bool:
"""Return whether the market should resolve, according to our rules."""
futures = [parallel(rule.value, self) for rule in (self.do_resolve_rules or ())]
futures = [parallel(rule.value, self, account) for rule in (self.do_resolve_rules or ())]
return any(future.result() for future in futures) and not self.market.isResolved
def resolve_to(self) -> AnyResolution:
def resolve_to(self, account: Account) -> AnyResolution:
"""Select a value to be resolved to.
This is done by iterating through a series of Rules, each of which have
@ -225,23 +216,26 @@ class Market(DictDeserializable):
"""
assert self.market.outcomeType != "NUMERIC"
chosen = None
futures = [parallel(rule.value, self, format=self.market.outcomeType) for rule in (self.resolve_to_rules or ())]
futures = [
parallel(rule.value, self, account, format=self.market.outcomeType)
for rule in (self.resolve_to_rules or ())
]
for f_rule in futures:
chosen = f_rule.result()
if chosen is not None:
break
if chosen is None:
raise RuntimeError()
if chosen is None:
chosen = f_rule.result()
else:
f_rule.cancel()
assert chosen is not None
return chosen
def current_answer(self) -> AnyResolution:
def current_answer(self, account: Account) -> AnyResolution:
"""Return the current market consensus."""
from .rule.manifold.this import CurrentValueRule
assert self.market.outcomeType != "NUMERIC"
return CurrentValueRule().value(self, format=self.market.outcomeType)
return CurrentValueRule().value(self, account, format=self.market.outcomeType)
@require_env(EnvironmentVariable.ManifoldAPIKey)
def resolve(self, override: Optional[AnyResolution] = None) -> Response:
def resolve(self, account: Account, override: Optional[AnyResolution] = None) -> Response:
"""Resolve this market according to our resolution rules.
Returns
@ -249,29 +243,30 @@ class Market(DictDeserializable):
Response
How Manifold interprets our request, and some JSON data on it
"""
_override: AnyResolution | tuple[float, float]
if override is None:
_override = self.resolve_to()
else:
_override = override
if _override == "CANCEL":
return self.cancel()
override = self.resolve_to(account)
if self.market.outcomeType in Outcome.MC_LIKE():
if not isinstance(_override, Mapping):
if override != "CANCEL" and self.market.outcomeType in Outcome.MC_LIKE():
if not isinstance(override, Mapping):
raise TypeError()
_override = {int(id_): weight for id_, weight in _override.items()}
override = {int(id_): weight for id_, weight in override.items()}
self.event_emitter.emit('before_resolve', self, _override)
ret: Response = self.client.resolve_market(self.market, _override)
ret.raise_for_status()
self.logger.info("I was resolved")
self.market.isResolved = True
self.event_emitter.emit('after_resolve', self, _override, ret)
return ret
try:
self.logger.info("I am announcing that I will be resolved to %r", override)
self.event_emitter.emit('before_resolve', self, account, override)
self.logger.info("I am resolving to %r...", override)
ret: Response = get_client(account).resolve_market(self.market, override)
ret.raise_for_status()
self.logger.info("I was resolved to %r", override)
self.market.isResolved = True
self.event_emitter.emit('after_resolve', self, account, override, ret)
return ret
except Exception:
self.logger.exception("I encountered an error during resolution")
raise
@require_env(EnvironmentVariable.ManifoldAPIKey)
def cancel(self) -> Response:
def cancel(self, account: Account) -> Response:
"""Cancel this market.
Returns
@ -279,11 +274,7 @@ class Market(DictDeserializable):
Response
How Manifold interprets our request, and some JSON data on it
"""
ret: Response = self.client.cancel_market(self.market)
ret.raise_for_status()
self.logger.info("I was cancelled")
self.market.isResolved = True
return ret
return self.resolve(account, "CANCEL")
def on(self, *args, **kwargs): # type: ignore
"""Register an event with EventEmitter."""

View File

@ -11,7 +11,7 @@ a rule with your plugin:
from __future__ import annotations
from importlib import import_module
from typing import Any, Type, cast
from typing import Any, Optional, Type, cast
from attrs import define
@ -32,7 +32,7 @@ def get_rule(type_: str) -> Type[Rule[Any]]:
@define(slots=False) # type: ignore
class DoResolveRule(Rule[bool]):
class DoResolveRule(Rule[Optional[bool]]):
"""The subtype of rule which determines if a market should resolve, returning a bool."""

View File

@ -18,6 +18,7 @@ if TYPE_CHECKING: # pragma: no cover
from pymanifold.types import JSONDict
from ..account import Account
from ..market import Market
from ..util import ModJSONDict
@ -39,8 +40,8 @@ class AbstractRule(Generic[T], Rule[T]):
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
return f"{' ' * indent}- {self._explainer_stub}\n"
def _explain_specific(self, market: Market, indent: int = 0, sig_figs: int = 4) -> str:
return f"{' ' * indent}- {self._explainer_stub} (-> {self.value(market, format='NONE')})\n"
def _explain_specific(self, market: Market, account: Account, indent: int = 0, sig_figs: int = 4) -> str:
return f"{' ' * indent}- {self._explainer_stub} (-> {self.value(market, account, format='NONE')})\n"
@define(slots=False) # type: ignore
@ -52,9 +53,9 @@ class UnaryRule(AbstractRule[T]):
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
return super()._explain_abstract(indent, **kwargs) + self.child.explain_abstract(indent + 1, **kwargs)
def _explain_specific(self, market: Market, indent: int = 0, sig_figs: int = 4) -> str:
return super()._explain_specific(market, indent, sig_figs) +\
self.child.explain_specific(market, indent + 1, sig_figs)
def _explain_specific(self, market: Market, account: Account, indent: int = 0, sig_figs: int = 4) -> str:
return super()._explain_specific(market, account, indent, sig_figs) +\
self.child.explain_specific(market, account, indent + 1, sig_figs)
@classmethod
def from_dict(cls, env: ModJSONDict) -> 'UnaryRule[T]':
@ -89,10 +90,10 @@ class BinaryRule(AbstractRule[T]):
ret += self.rule2.explain_abstract(indent + 1, **kwargs)
return ret
def _explain_specific(self, market: Market, indent: int = 0, sig_figs: int = 4) -> str:
ret = super()._explain_specific(market, indent, sig_figs)
ret += self.rule1.explain_specific(market, indent + 1, sig_figs)
ret += self.rule2.explain_specific(market, indent + 1, sig_figs)
def _explain_specific(self, market: Market, account: Account, indent: int = 0, sig_figs: int = 4) -> str:
ret = super()._explain_specific(market, account, indent, sig_figs)
ret += self.rule1.explain_specific(market, account, indent + 1, sig_figs)
ret += self.rule2.explain_specific(market, account, indent + 1, sig_figs)
return ret
@ -119,11 +120,11 @@ class VariadicRule(AbstractRule[T]):
ret += rule.explain_abstract(indent + 1, **kwargs)
return ret
def _explain_specific(self, market: Market, indent: int = 0, sig_figs: int = 4) -> str:
val = round_sig_figs(cast(float, self._value(market)), sig_figs)
def _explain_specific(self, market: Market, account: Account, indent: int = 0, sig_figs: int = 4) -> str:
val = round_sig_figs(cast(float, self._value(market, account)), sig_figs)
ret = f"{' ' * indent}- {self._explainer_stub} (-> {val})\n"
for rule in self.rules:
ret += rule.explain_specific(market, indent + 1, sig_figs)
ret += rule.explain_specific(market, account, indent + 1, sig_figs)
return ret
@ -137,7 +138,7 @@ class ResolveRandomSeed(ResolutionValueRule):
args: Sequence[Any] = ()
kwargs: JSONDict = Factory(dict)
def _value(self, market: Market) -> Any:
def _value(self, market: Market, account: Account) -> Any:
source = Random(self.seed)
method = getattr(source, self.method)
for _ in range(self.rounds):

View File

@ -19,6 +19,7 @@ if TYPE_CHECKING: # pragma: no cover
from concurrent.futures import Future
from typing import Any, ClassVar, DefaultDict, Literal, MutableSequence
from ..account import Account
from ..consts import FreeResponseResolution, MultipleChoiceResolution
from ..market import Market
from ..util import ModJSONDict
@ -30,8 +31,8 @@ class NegateRule(UnaryRule[Optional[BinaryResolution]]):
_explainer_stub: ClassVar[str] = "Resolve False if the below is True, and vice versa"
def _value(self, market: Market) -> bool:
return not self.child._value(market)
def _value(self, market: Market, account: Account) -> bool:
return not self.child._value(market, account)
@define(slots=False)
@ -40,8 +41,8 @@ class EitherRule(BinaryRule[Optional[BinaryResolution]]):
_explainer_stub: ClassVar[str] = "Resolve True if either of the below resolves True, otherwise resolve False"
def _value(self, market: Market) -> bool:
return bool(self.rule1._value(market)) or bool(self.rule2._value(market))
def _value(self, market: Market, account: Account) -> bool:
return bool(self.rule1._value(market, account)) or bool(self.rule2._value(market, account))
@define(slots=False)
@ -50,8 +51,8 @@ class BothRule(BinaryRule[Optional[BinaryResolution]]):
_explainer_stub: ClassVar[str] = "Resolve True if both of the below resolve to True, otherwise resolve False"
def _value(self, market: Market) -> bool:
return bool(self.rule1._value(market)) and bool(self.rule2._value(market))
def _value(self, market: Market, account: Account) -> bool:
return bool(self.rule1._value(market, account)) and bool(self.rule2._value(market, account))
@define(slots=False)
@ -60,8 +61,8 @@ class NANDRule(BinaryRule[Optional[BinaryResolution]]):
_explainer_stub: ClassVar[str] = "Resolve True if one or more of the below resolves False, otherwise resolve False"
def _value(self, market: Market) -> bool:
return not (self.rule1._value(market) and self.rule2._value(market))
def _value(self, market: Market, account: Account) -> bool:
return not (self.rule1._value(market, account) and self.rule2._value(market, account))
@define(slots=False)
@ -70,8 +71,8 @@ class NeitherRule(BinaryRule[Optional[BinaryResolution]]):
_explainer_stub: ClassVar[str] = "Resolve False if either of the below resolve to True, otherwise resolve True"
def _value(self, market: Market) -> bool:
return not (self.rule1._value(market) or self.rule2._value(market))
def _value(self, market: Market, account: Account) -> bool:
return not (self.rule1._value(market, account) or self.rule2._value(market, account))
@define(slots=False)
@ -80,8 +81,8 @@ class XORRule(BinaryRule[Optional[BinaryResolution]]):
_explainer_stub: ClassVar[str] = "Resolve False if the below resolve to the same value, otherwise resolve True"
def _value(self, market: Market) -> bool:
return bool(bool(self.rule1._value(market)) ^ bool(self.rule2._value(market)))
def _value(self, market: Market, account: Account) -> bool:
return bool(bool(self.rule1._value(market, account)) ^ bool(self.rule2._value(market, account)))
@define(slots=False)
@ -90,8 +91,8 @@ class XNORRule(BinaryRule[Optional[BinaryResolution]]):
_explainer_stub: ClassVar[str] = "Resolve True if the below resolve to the same value, otherwise resolve False"
def _value(self, market: Market) -> bool:
return bool(self.rule1._value(market)) == bool(self.rule2._value(market))
def _value(self, market: Market, account: Account) -> bool:
return bool(self.rule1._value(market, account)) == bool(self.rule2._value(market, account))
@define(slots=False)
@ -102,8 +103,8 @@ class ImpliesRule(BinaryRule[Optional[BinaryResolution]]):
"Resolve True if the next line resolves False, otherwise resolves to the value of the item after"
)
def _value(self, market: Market) -> bool:
return not self.rule1._value(market) or bool(self.rule2._value(market))
def _value(self, market: Market, account: Account) -> bool:
return not self.rule1._value(market, account) or bool(self.rule2._value(market, account))
@define(slots=False)
@ -114,9 +115,9 @@ class ConditionalRule(BinaryRule[BinaryResolution]):
"Cancels if the next line resolves False, otherwise resolves to the value of the item after"
)
def _value(self, market: Market) -> BinaryResolution:
f_val1 = parallel(self.rule1._value, market)
f_val2 = parallel(self.rule2._value, market)
def _value(self, market: Market, account: Account) -> BinaryResolution:
f_val1 = parallel(self.rule1._value, market, account)
f_val2 = parallel(self.rule2._value, market, account)
if not f_val1.result():
return "CANCEL"
return f_val2.result()
@ -128,7 +129,7 @@ class ResolveAtTime(DoResolveRule):
resolve_at: datetime
def _value(self, market: Market) -> bool:
def _value(self, market: Market, account: Account) -> bool:
"""Return True iff the current time is after resolve_at."""
try:
return datetime.now(timezone.utc) >= self.resolve_at
@ -145,7 +146,7 @@ class ResolveToValue(Generic[T], Rule[T]):
resolve_value: T
def _value(self, market: Market) -> T:
def _value(self, market: Market, account: Account) -> T:
return self.resolve_value
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
@ -158,8 +159,8 @@ class ModulusRule(BinaryRule[PseudoNumericResolution]):
_explainer_stub: ClassVar[str] = "A mod B, where A is the next line and B the line after"
def _value(self, market: Market) -> Literal["CANCEL"] | float:
val1, val2 = self.rule1._value(market), self.rule2._value(market)
def _value(self, market: Market, account: Account) -> Literal["CANCEL"] | float:
val1, val2 = self.rule1._value(market, account), self.rule2._value(market, account)
if val1 == "CANCEL" or val2 == "CANCEL":
return "CANCEL"
return val1 % val2
@ -171,10 +172,10 @@ class AdditiveRule(VariadicRule[PseudoNumericResolution]):
_explainer_stub: ClassVar[str] = "The sum of the below"
def _value(self, market: Market) -> Literal["CANCEL"] | float:
def _value(self, market: Market, account: Account) -> Literal["CANCEL"] | float:
"""Return the sum of the underlying rules."""
ret: float = 0
futures = [parallel(rule.value, market, format='PSEUDO_NUMERIC') for rule in self.rules]
futures = [parallel(rule.value, market, account, format='PSEUDO_NUMERIC') for rule in self.rules]
for f_rule in futures:
val = cast(
PseudoNumericResolution,
@ -192,10 +193,10 @@ class MultiplicitiveRule(VariadicRule[PseudoNumericResolution]):
_explainer_stub: ClassVar[str] = "The product of the below"
def _value(self, market: Market) -> Literal["CANCEL"] | float:
def _value(self, market: Market, account: Account) -> Literal["CANCEL"] | float:
"""Return the product of the underlying rules."""
ret: float = 1
futures = [parallel(rule.value, market, format='PSEUDO_NUMERIC') for rule in self.rules]
futures = [parallel(rule.value, market, account, format='PSEUDO_NUMERIC') for rule in self.rules]
for f_rule in futures:
val = cast(
PseudoNumericResolution,
@ -231,7 +232,7 @@ class ResolveRandomIndex(ResolveRandomSeed):
method = 'randrange'
super().__init__(seed, method, *args, **kwargs)
def _value(self, market: Market) -> int:
def _value(self, market: Market, account: Account) -> int:
market.refresh()
if self.method == 'randrange':
self.args = (self.start, self.size)
@ -240,7 +241,7 @@ class ResolveRandomIndex(ResolveRandomSeed):
items = [(int(idx), float(obj)) for idx, obj in market.market.pool.items() if int(idx) >= self.start]
self.args = (range(self.start, self.start + len(items)), )
self.kwargs["weights"] = [prob for _, prob in items]
return cast(int, super()._value(market))
return cast(int, super()._value(market, account))
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
ret = f"{' ' * indent}- Resolve to a random index, given some original seed. This one operates on a "
@ -257,7 +258,7 @@ class ResolveMultipleValues(ResolutionValueRule):
shares: MutableSequence[tuple[ResolutionValueRule, float]] = Factory(list)
def _value(self, market: Market) -> FreeResponseResolution | MultipleChoiceResolution:
def _value(self, market: Market, account: Account) -> FreeResponseResolution | MultipleChoiceResolution:
ret: DefaultDict[int, float] = defaultdict(float)
for rule, part in self.shares:
val = cast(Dict[Union[str, int], Future[float]], parallel(rule.value, market, format='FREE_RESPONSE'))

View File

@ -3,12 +3,10 @@
from __future__ import annotations
from datetime import datetime, timezone
from os import getenv
from typing import TYPE_CHECKING, cast
from attrs import define
from github3 import GitHub
from github3 import login as gh_login
from github3 import GitHub, login
from ..caching import parallel
from ..consts import EnvironmentVariable
@ -17,23 +15,25 @@ from . import DoResolveRule, ResolutionValueRule
if TYPE_CHECKING: # pragma: no cover
from concurrent.futures import Future
from typing import Any, Optional
from typing import Any, Callable, Optional, TypeVar
from github3.issues import Issue
from github3.pulls import PullRequest
from ..account import Account
from ..market import Market
T = TypeVar("T")
def unauth_login() -> GitHub:
"""Return an unauthorized login to GitHub."""
return GitHub()
@require_env(EnvironmentVariable.GithubAccessToken, EnvironmentVariable.GithubUsername)
def login() -> GitHub:
"""Return an authorized login to GitHub."""
return gh_login(username=getenv('GithubUsername'), token=getenv('GithubAccessToken'))
def auth_login(account: Account) -> GitHub:
"""Translate our account objects into their account fields."""
return login(username=account.GithubUsername, token=account.GithubToken)
@define(slots=False)
@ -44,13 +44,17 @@ class GitHubIssueMixin:
repo: str
number: int
def f_issue(self) -> Future[Issue]:
"""Return a Future object which resolves to the relevant Issue object."""
return parallel(login().issue, self.owner, self.repo, self.number)
def f_generic(self, account: Account, func: Callable[[GitHub, str, str, int], T]) -> T:
"""Return the request method that you feed in, and we provide the lookup info."""
return func(auth_login(account), self.owner, self.repo, self.number)
def f_pr(self) -> Future[PullRequest]:
def f_issue(self, account: Account) -> Future[Issue]:
"""Return a Future object which resolves to the relevant Issue object."""
return parallel(self.f_generic, account, GitHub.issue)
def f_pr(self, account: Account) -> Future[PullRequest]:
"""Return a Future object which resolves to the relevant PullRequest object."""
return parallel(login().pull_request, self.owner, self.repo, self.number)
return parallel(self.f_generic, account, GitHub.pull_request)
@define(slots=False)
@ -58,10 +62,10 @@ class ResolveWithPR(DoResolveRule, GitHubIssueMixin):
"""Return True if the specified PR was merged in the past."""
@require_env(EnvironmentVariable.GithubAccessToken, EnvironmentVariable.GithubUsername)
def _value(self, market: Market) -> bool:
def _value(self, market: Market, account: Account) -> bool:
"""Return True if the issue is closed or the PR is merged, otherwise False."""
f_issue = self.f_issue()
f_pr = self.f_pr()
f_issue = self.f_issue(account)
f_pr = self.f_pr(account)
issue = f_issue.result()
if issue.state != 'open':
return True
@ -71,11 +75,11 @@ class ResolveWithPR(DoResolveRule, GitHubIssueMixin):
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
return f"{' ' * indent}- If the GitHub PR {self.owner}/{self.repo}#{self.number} was merged in the past.\n"
def _explain_specific(self, market: Market, indent: int = 0, sig_figs: int = 4) -> str:
f_issue = self.f_issue()
f_pr = self.f_pr()
def _explain_specific(self, market: Market, account: Account, indent: int = 0, sig_figs: int = 4) -> str:
f_issue = self.f_issue(account)
f_pr = self.f_pr(account)
ret = f"{' ' * indent}- If either of the conditions below are True (-> {self.value(market)})\n"
ret = f"{' ' * indent}- If either of the conditions below are True (-> {self.value(market, account)})\n"
indent += 1
issue = f_issue.result()
ret += f"{' ' * indent}- If the state of the pull request is not open (-> {issue.state})\n"
@ -88,8 +92,8 @@ class ResolveWithPR(DoResolveRule, GitHubIssueMixin):
class ResolveToPR(ResolutionValueRule, GitHubIssueMixin):
"""Resolve to True if the PR is merged, otherwise False."""
def _value(self, market: Market) -> bool:
pr = self.f_pr().result()
def _value(self, market: Market, account: Account) -> bool:
pr = self.f_pr(account).result()
return pr is not None and pr.merged
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
@ -99,8 +103,8 @@ class ResolveToPR(ResolutionValueRule, GitHubIssueMixin):
ret += f"{' ' * indent}- Otherwise, resolve to NO.\n"
return ret
def _explain_specific(self, market: Market, indent: int = 0, sig_figs: int = 4) -> str:
pr = self.f_pr().result()
def _explain_specific(self, market: Market, account: Account, indent: int = 0, sig_figs: int = 4) -> str:
pr = self.f_pr(account).result()
if pr is None:
merge_time = None
else:
@ -115,8 +119,8 @@ class ResolveToPRDelta(ResolutionValueRule, GitHubIssueMixin):
start: datetime
def _value(self, market: Market) -> float:
pr = self.f_pr().result()
def _value(self, market: Market, account: Account) -> float:
pr = self.f_pr(account).result()
if pr is None or pr.merged_at is None:
return cast(float, market.market.max)
delta = cast(datetime, pr.merged_at) - self.start.replace(tzinfo=timezone.utc)
@ -133,11 +137,11 @@ class ResolveToPRDelta(ResolutionValueRule, GitHubIssueMixin):
ret += ".\n"
return ret
def _explain_specific(self, market: Market, indent: int = 0, sig_figs: int = 4) -> str:
pr = self.f_pr().result()
def _explain_specific(self, market: Market, account: Account, indent: int = 0, sig_figs: int = 4) -> str:
pr = self.f_pr(account).result()
if pr is None:
merge_time = None
else:
merge_time = pr.merged_at
return (f"{' ' * indent}- How long after {self.start} was the pull request is merged? (-> "
f"{merge_time or 'Not yet merged'} -> {self.value(market)})\n")
f"{merge_time or 'Not yet merged'} -> {self.value(market, account)})\n")

View File

@ -5,15 +5,14 @@ from __future__ import annotations
from typing import TYPE_CHECKING, cast
from attrs import define
from pymanifold.lib import ManifoldClient
from ...caching import parallel
from ...util import get_client
if TYPE_CHECKING: # pragma: no cover
from concurrent.futures import Future
from typing import Optional
from pymanifold.lib import ManifoldClient
from pymanifold.types import Market as APIMarket
from ...market import Market
@ -25,7 +24,7 @@ __all__ = ('this', 'other', 'user', 'ManifoldMarketMixin')
class ManifoldMarketMixin:
"""A mixin class that holds the access to a Manifold market."""
id_: str = ''
id_: str = None # type: ignore[assignment]
slug: Optional[str] = None
url: Optional[str] = None
@ -33,18 +32,18 @@ class ManifoldMarketMixin:
"""Ensure we have at least the id."""
if hasattr(super(), '__attrs_post_init__'):
super().__attrs_post_init__() # type: ignore
if self.id_:
if self.id_ is not None:
return
elif self.slug:
slug = self.slug
else:
slug = self.slug = cast(str, self.url).split("/")[-1]
self.id_ = get_client().get_market_by_slug(slug).id
self.id_ = ManifoldClient().get_market_by_slug(slug).id
def api_market(self, client: Optional[ManifoldClient] = None, market: Optional[Market] = None) -> APIMarket:
"""Return an APIMarket object associated with this rule's market."""
if client is None:
client = get_client()
client = ManifoldClient()
return client.get_market_by_id(self.id_)
def f_api_market(

View File

@ -6,11 +6,12 @@ from time import time
from typing import TYPE_CHECKING, cast
from attrs import define
from pymanifold.utils.math import prob_to_number_cpmm1
from ... import Rule
from ...caching import parallel
from ...consts import BinaryResolution, Outcome, T
from ...util import market_to_answer_map, prob_to_number_cpmm1, round_sig_figs
from ...util import market_to_answer_map, round_sig_figs
from .. import DoResolveRule
from ..abstract import ResolveRandomSeed
from . import ManifoldMarketMixin
@ -20,6 +21,7 @@ if TYPE_CHECKING: # pragma: no cover
from pymanifold.types import Market as APIMarket
from ...account import Account
from ...consts import AnyResolution
from ...market import Market
@ -28,7 +30,7 @@ if TYPE_CHECKING: # pragma: no cover
class OtherMarketClosed(DoResolveRule, ManifoldMarketMixin):
"""A rule that checks whether another market is closed."""
def _value(self, market: Market) -> bool:
def _value(self, market: Market, account: Account) -> bool:
mkt = self.api_market(market=market)
assert mkt.closeTime is not None
return bool(mkt.isResolved or (mkt.closeTime < time() * 1000))
@ -41,7 +43,7 @@ class OtherMarketClosed(DoResolveRule, ManifoldMarketMixin):
class OtherMarketResolved(DoResolveRule, ManifoldMarketMixin):
"""A rule that checks whether another market is resolved."""
def _value(self, market: Market) -> bool:
def _value(self, market: Market, account: Account) -> bool:
return bool(self.api_market().isResolved)
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
@ -52,7 +54,7 @@ class OtherMarketResolved(DoResolveRule, ManifoldMarketMixin):
class OtherMarketUniqueTraders(ManifoldMarketMixin, Rule[int]):
"""A rule that checks whether another market is resolved."""
def _value(self, market: Market) -> int:
def _value(self, market: Market, account: Account) -> int:
return len(
{bet.userId for bet in self.api_market(market=market).bets} - {None}
)
@ -65,7 +67,7 @@ class OtherMarketUniqueTraders(ManifoldMarketMixin, Rule[int]):
class OtherMarketValue(Rule[T], ManifoldMarketMixin):
"""A rule that resolves to the value of another rule."""
def _value(self, market: Market) -> T:
def _value(self, market: Market, account: Account) -> T:
mkt = self.api_market(market=market)
if mkt.resolution == "CANCEL":
ret: AnyResolution = "CANCEL"
@ -85,24 +87,34 @@ class OtherMarketValue(Rule[T], ManifoldMarketMixin):
def _binary_value(self, market: Market, mkt: APIMarket | None = None) -> float:
if mkt is None:
mkt = self.api_market(market=market)
if mkt.isResolved:
if mkt.resolution == "YES":
return True
elif mkt.resolution == "NO":
return False
return cast(float, mkt.resolutionProbability)
return cast(float, mkt.probability)
if mkt.resolution == "YES":
ret: float = True
elif mkt.resolution == "NO":
ret = False
elif mkt.resolutionProbability is not None:
ret = mkt.resolutionProbability
elif mkt.probability is not None:
ret = mkt.probability
else:
prob = mkt.bets[-1].probAfter
assert prob is not None
ret = prob
return ret
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
return (f"{' ' * indent}- Resolved (or current, if not resolved) value of `{self.id_}` "
f"({self.api_market().question}).\n")
def _explain_specific(self, market: Market, indent: int = 0, sig_figs: int = 4) -> str:
def _explain_specific(self, market: Market, account: Account, indent: int = 0, sig_figs: int = 4) -> str:
f_mkt = self.f_api_market(market=market)
f_val = parallel(self._value, market)
f_val = parallel(self._value, market, account)
mkt = f_mkt.result()
ret = (f"{' ' * indent}- Resolved (or current, if not resolved) value of `{self.id_}` "
f"({mkt.question}) (-> ")
if hasattr(self, 'id_'):
ret = (f"{' ' * indent}- Resolved (or current, if not resolved) value of `{self.id_}` "
f"({mkt.question}) (-> ")
else:
ret = f"{' ' * indent}- Resolved (or current, if not resolved) value of this market (-> "
val = f_val.result()
if val == "CANCEL":
ret += "CANCEL"
@ -149,12 +161,12 @@ class AmplifiedOddsRule(OtherMarketValue[BinaryResolution], ResolveRandomSeed):
a: int = 1
def _value(self, market: Market) -> BinaryResolution:
def _value(self, market: Market, account: Account) -> BinaryResolution:
val = OtherMarketValue._binary_value(self, market)
if val is True:
return True
if val is False:
if ResolveRandomSeed._value(self, market) < (1 / self.a):
if ResolveRandomSeed._value(self, market, account) < (1 / self.a):
return False
return "CANCEL"
return val / (val + (1 - val) / self.a) * 100
@ -173,9 +185,9 @@ class AmplifiedOddsRule(OtherMarketValue[BinaryResolution], ResolveRandomSeed):
ret += f"{' ' * indent}- Otherwise, resolve to the equivalent price of the reference market\n"
return ret
def _explain_specific(self, market: Market, indent: int = 0, sig_figs: int = 4) -> str:
f_val = parallel(self._value, market)
other_exp = parallel(OtherMarketValue._explain_specific, self, market, indent + 1, sig_figs)
def _explain_specific(self, market: Market, account: Account, indent: int = 0, sig_figs: int = 4) -> str:
f_val = parallel(self._value, market, account)
other_exp = parallel(OtherMarketValue._explain_specific, self, market, account, indent + 1, sig_figs)
ret = f"{' ' * indent}- Amplified odds: (-> "
val = f_val.result()
if val == "CANCEL":

View File

@ -19,36 +19,40 @@ if TYPE_CHECKING: # pragma: no cover
from pymanifold.lib import ManifoldClient
from pymanifold.types import Market as APIMarket
from ...account import Account
from ...market import Market
@define(slots=False)
class ThisToOtherConverter(ManifoldMarketMixin):
"""A mixin class that converts market accesses to reuse `other` code."""
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Ensure that we override the market fetch methods."""
super().__init__(id_="N/A")
id_: str = "N/A"
def api_market(self, client: Optional[ManifoldClient] = None, market: Optional[Market] = None) -> APIMarket:
"""Return an APIMarket object associated with this rule's market."""
assert market is not None
market.refresh()
return market.market
class ThisMarketClosed(ThisToOtherConverter, OtherMarketClosed):
@define(slots=False)
class ThisMarketClosed(OtherMarketClosed, ThisToOtherConverter):
"""A rule that checks whether its associated market is closed."""
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
return "If this market reaches its close date\n"
class CurrentValueRule(ThisToOtherConverter, OtherMarketValue[T]):
@define(slots=False)
class CurrentValueRule(OtherMarketValue[T], ThisToOtherConverter):
"""Resolve to the current market-consensus value."""
def _explain_abstract(self, indent: int = 0, **kwargs: Any) -> str:
return "Resolves to the current market value\n"
@define(slots=False)
class UniqueTradersRule(ThisToOtherConverter, OtherMarketUniqueTraders):
"""Resolve to the current number of unique traders."""
@ -63,7 +67,7 @@ class FibonacciValueRule(Rule[Union[float, Mapping[int, float]]]):
exclude: set[int] = Factory(set)
min_rewarded: float = 0.0001
def _value(self, market: Market) -> float | dict[int, float]:
def _value(self, market: Market, account: Account) -> float | dict[int, float]:
market.refresh()
items = market_to_answer_map(market, self.exclude, (lambda id_, probability: probability < self.min_rewarded))
rank = sorted(items, key=items.__getitem__)
@ -84,13 +88,13 @@ class RoundValueRule(CurrentValueRule[Union[BinaryResolution, PseudoNumericResol
_explainer_stub: ClassVar[str] = "Resolves to round(MKT)"
def _value(self, market: Market) -> float:
def _value(self, market: Market, account: Account) -> float:
if market.market.outcomeType in Outcome.MC_LIKE():
raise RuntimeError()
elif market.market.outcomeType == Outcome.BINARY:
assert market.market.probability
return bool(round(market.market.probability))
return round(cast(float, super()._value(market)))
return round(cast(float, super()._value(market, account)))
@define(slots=False)
@ -99,7 +103,7 @@ class PopularValueRule(Rule[Union[MultipleChoiceResolution, FreeResponseResoluti
size: int = 1
def _value(self, market: Market) -> FreeResponseResolution | MultipleChoiceResolution:
def _value(self, market: Market, account: Account) -> FreeResponseResolution | MultipleChoiceResolution:
market.refresh()
answers = market_to_answer_map(market)
final_answers: dict[int, float] = {}

View File

@ -13,6 +13,7 @@ from ... import Rule
if TYPE_CHECKING: # pragma: no cover
from typing import Any, ClassVar, Literal
from ...account import Account
from ...market import Market
@ -30,7 +31,7 @@ class ManifoldUserRule(Rule[float]):
assert cls.attr
assert cls.attr_desc
def _value(self, market: Market) -> float:
def _value(self, market: Market, account: Account) -> float:
user = ManifoldClient()._get_user_raw(self.user)
return cast(float, cast(JSONDict, user[self.attr])[self.field])

View File

@ -0,0 +1,7 @@
"""Store state both during and between sessions."""
from __future__ import annotations
from . import persistant, volatile
__all__ = ('persistant', 'volatile')

View File

@ -0,0 +1,231 @@
"""Store state between sessions."""
from __future__ import annotations
from logging import getLogger
from os import getenv
from pathlib import Path
from sqlite3 import PARSE_COLNAMES, PARSE_DECLTYPES, connect
from typing import TYPE_CHECKING
from uuid import NAMESPACE_DNS, uuid5
from ..account import Account
from ..consts import EnvironmentVariable
from ..util import require_env
if TYPE_CHECKING: # pragma: no cover
from datetime import datetime
from sqlite3 import Connection, Cursor
from typing import Any, Callable, Iterable, Sequence
from ..market import Market
from ..util import T
logger = getLogger(__name__)
def db_wrapper(func: Callable[..., T]) -> Callable[..., T]:
"""Wrap a function so that it automatically gets a reference to the database if one is not provided."""
def wrapper(*args: Any, db: Connection | None = None, **kwargs: Any) -> T:
if db is None:
with register_db() as db:
return func(*args, db=db, **kwargs)
return func(*args, db=db, **kwargs)
return wrapper
@require_env(EnvironmentVariable.DBName)
def register_db() -> Connection:
"""Get a connection to the appropriate database for this bot."""
name = getenv("DBName")
if name is None:
raise EnvironmentError()
do_initialize = not Path(name).exists()
conn = connect(name, detect_types=PARSE_COLNAMES | PARSE_DECLTYPES)
if do_initialize:
conn.execute(
"CREATE TABLE accounts "
"(id INTEGER PRIMARY KEY AUTOINCREMENT, manidfold_id TEXT NOT NULL, username TEXT NOT NULL, "
"raw_account BLOB, is_encrypted BOOLEAN, account Account)"
)
conn.execute(
"CREATE TABLE markets "
"(id INTEGER PRIMARY KEY AUTOINCREMENT, market Market, check_rate REAL NOT NULL, last_checked TIMESTAMP, "
"account INTEGER REFERENCES \"accounts\" (\"id\") ON DELETE SET NULL)"
)
conn.execute(
"CREATE TABLE pending "
"(id INTEGER PRIMARY KEY AUTOINCREMENT, request ManagerRequest NOT NULL, priority REAL NOT NULL, "
"account INTEGER REFERENCES \"accounts\" (\"id\") ON DELETE SET NULL)"
)
conn.execute(
"CREATE TABLE scanners "
"(id INTEGER PRIMARY KEY AUTOINCREMENT, scanner EventEmitter NOT NULL, state Namesapce, "
"check_rate REAL NOT NULL, last_checked TIMESTAMP, "
"account INTEGER REFERENCES \"accounts\" (\"id\") ON DELETE SET NULL)"
)
conn.commit()
logger.info("Database up and initialized.")
return conn
@db_wrapper
def remove_markets(
*row_id: int,
db: Connection = None # type: ignore[assignment]
) -> None:
"""Attempt to delete a market in the database."""
assert db is not None
db.execute(f"DELETE FROM markets WHERE {' OR '.join(['id = ?'] * len(row_id))}", row_id)
@db_wrapper
def find_account(
account: Account,
db: Connection = None # type: ignore[assignment]
) -> int:
"""Find the ID of an account, if it's registered."""
id_, _ = select_account(username=account.ManifoldUsername, key=account.key)
return id_
@db_wrapper
def update_market(
row_id: int,
market: Market | None = None,
check_rate: float | None = None,
last_checked: datetime | None = None,
account_id: int | None = None,
account: Account | None = None,
db: Connection = None # type: ignore[assignment]
) -> None:
"""Attempt to update a market in the database."""
assert db is not None
params: tuple[Any, ...] = ()
q_additions = []
for name, value in {
"market": market,
"check_rate": check_rate,
"last_checked": last_checked,
"account_id": account_id,
"account": account,
}.items():
if value is not None:
q_additions.append(f"{name}=?")
params += (value, )
if not params:
raise ValueError("you need to actually update something")
query = f"UPDATE markets SET {', '.join(q_additions)} WHERE id=?"
params += (row_id, )
db.execute(query, params)
@db_wrapper
def select_markets(
keys: Sequence[bytes] = (),
db: Connection = None # type: ignore[assignment]
) -> Iterable[tuple[int, Market, float, datetime | None, Account | None]]:
"""Attempt to load ALL market objects from the database, with their associated metadata.
Requires: some number of keys if your market has encrypted accounts associated with it.
Depends on: select_account()
"""
assert db is not None
key_strs = getenv(EnvironmentVariable.AccountKeys, "").split(",")
keys = (*keys, *(bytes.fromhex(x) for x in key_strs))
row: tuple[int, Market, float, datetime | None, int | None]
for row in db.execute("SELECT * from markets"):
row_id, market, check_rate, last_checked, *extra = row
account_id: int | None
if extra:
(account_id, ) = extra
else:
account_id = None
account: Account | None = None
if account_id is not None:
for key in keys:
_, account = select_account(db_id=account_id, key=key)
break
yield (row_id, market, check_rate, last_checked, account)
@db_wrapper
def select_account(
db_id: int | None = None,
manifold_id: str | None = None,
username: str | None = None,
key: bytes = b'',
db: Connection = None # type: ignore[assignment]
) -> tuple[int, Account]:
"""Attempt to load and decrypt a SINGLE account object from the database.
Raises an error if not exactly one is returned or if it cannot be decrypted.
"""
assert db is not None
query = "from accounts select id, raw_account, account, is_encrypted where "
params: tuple[Any, ...] = ()
q_additions = []
for name, value in {
"id": db_id,
"manifold_id": manifold_id,
"username": username,
}.items():
if value is not None:
q_additions.append(f"{name} = ?")
params += (value, )
query += ", ".join(q_additions)
((id_, raw_account, account, is_encrypted), ) = db.execute(query, params)
if is_encrypted:
account = Account.from_bytes(raw_account, key)
return (id_, account)
class DatabaseNamespace:
"""Reperesent a namespace in the database for use by various rules and plugins.
This requires you to give a schema and a DNS-formatted table name
"""
def __init__(self, name: str, schema: dict[str, str | type]):
"""Given a name and schema, get a helper object to interact with only your part of the database.
Schema should be formatted as a dictionary of names to types.
Name should be formatted as a URI that describes your table. For instance, if I was making a table to store
state for a scanner of OpenStreetMap tasks, I might name it `scanner.osm.projects`.
"""
self.uuid = uuid5(NAMESPACE_DNS, name).hex
str_schema = ", ".join(f"{name} {type_}" for name, type_ in schema.items())
self.execute(f"CREATE TABLE {self.uuid} IF NOT EXIST ? ({str_schema})", commit=True)
def execute(self, query: str, commit: bool = False) -> Cursor:
"""Perform basic sanitization that I don't expect to defeat real effort unless you use this responsibly."""
if len(self.uuid) == 32 or ';' in query:
raise ValueError()
with register_db() as db:
ret = db.execute(query)
if commit:
db.commit()
return ret
def select(
self,
names: Sequence[str] = ("*", )
) -> Cursor:
"""Select from your database namespace."""
return self.execute(f"SELECT {', '.join(names)} FROM {self.uuid}")
def remove(
self,
names: Sequence[str] = ("*", )
) -> Cursor:
"""Remove from your database namespace."""
raise NotImplementedError()
def update(
self,
names: Sequence[str] = ("*", )
) -> Cursor:
"""Update values in your database namespace."""
raise NotImplementedError()

View File

@ -0,0 +1 @@
"""Store state during sessions."""

View File

@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
from pytest import fixture, mark
from ...account import Account
from ...market import Market
from ...util import get_client
from .. import manifold_vcr
@ -13,11 +14,13 @@ if TYPE_CHECKING: # pragma: no cover
from .. import PytestRequest
account = Account(ManifoldUsername='Test Case', ManifoldToken='FAKE_TOKEN')
# slug -> everything else
examples: dict[str, Any] = {
'amplified-odds-100x-will-a-nuclear-4acd2868830b': {
'market': None,
'client': None,
'client': get_client(account),
'do_resolve_rules': [[
'manifold.other.OtherMarketResolved',
{'id_': 'jsqfBFbbIyP4X40L6VSo'}
@ -38,18 +41,18 @@ examples: dict[str, Any] = {
def amplified_example(request: PytestRequest[str]) -> Market:
with manifold_vcr.use_cassette(f'examples/amplified_odds/fetch/{request.param}.yaml'):
ret = Market.from_dict(examples[request.param])
ret.client = client = get_client()
ret.market = client.get_market_by_slug(request.param)
# ret.client = client = get_client()
ret.market = ret.client.get_market_by_slug(request.param)
ret.market.isResolved = False
return ret
@mark.depends(on=(
"src/test/rule/manifold/test_other.py::test_AmplifiedOddsRule",
"ManifoldMarketManager/test/rule/manifold/test_other.py::test_AmplifiedOddsRule",
))
def test_AmplifiedOddsMarket(amplified_example: Market) -> None:
with manifold_vcr.use_cassette(f'examples/amplified_odds/{amplified_example.id}.yaml'):
if amplified_example.should_resolve():
amplified_example.resolve()
with manifold_vcr.use_cassette(f'examples/amplified_odds/{amplified_example.market.id}.yaml'):
if amplified_example.should_resolve(account):
amplified_example.resolve(account)
else: # pragma: no cover
raise RuntimeError()

View File

@ -245,8 +245,6 @@ interactions:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
@ -502,8 +500,6 @@ interactions:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
@ -599,8 +595,6 @@ interactions:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
Vary:
- Origin
X-Matched-Path:
@ -612,4 +606,302 @@ interactions:
status:
code: 200
message: OK
- request:
body: '{"contractId": "KlooTljBuurIgQddBI9D", "markdown": "This market is resolving
because of the following trigger(s):\n - If `jsqfBFbbIyP4X40L6VSo` is resolved
(Will a nuclear weapon be launched in combat in the next 2 weeks?). (-> YES)\n\nIt
will follow the decision tree below:\n- If the human operator agrees:\n - Amplified
odds: (-> CANCEL)\n - If the referenced market resolves True, resolve True\n -
Resolved (or current, if not resolved) value of `jsqfBFbbIyP4X40L6VSo` (Will
a nuclear weapon be launched in combat in the next 2 weeks?) (-> CANCEL)\n -
If it resolved NO, generate a random number using a predetermined seed\n -
If the number is less than `1 / a` (100 -> ~0.01), resolve NO\n - Otherwise,
resolve N/A\n - Otherwise, resolve to the equivalent price of the reference
market\n\nFinal Value: CANCEL"}'
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
Content-Length:
- '839'
Content-Type:
- application/json
User-Agent:
- python-requests/2.28.1
method: POST
uri: https://manifold.markets/api/v0/comment
response:
body:
string: '{"message":"An unknown error occurred."}'
headers:
Cache-Control:
- public, max-age=0, must-revalidate
Connection:
- keep-alive
Content-Length:
- '40'
Content-Type:
- application/json; charset=utf-8
Date:
- Thu, 17 Nov 2022 02:07:50 GMT
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Vary:
- Origin
X-Matched-Path:
- /api/v0/comment
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::d2psl-1668650869741-aa744ba9e330
status:
code: 500
message: Internal Server Error
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/market/jsqfBFbbIyP4X40L6VSo
response:
body:
string: !!binary |
MUAVAAC/e1H7+kXvGydgY2wjTVN319566ZKuXXfXTv3A9ktMisEBnCyLMnoDudDhQPLv8MPh+H+X
A347ffv2pHmQYFlb/EBaYAFGQZCUpRnmnJvzav1NNQHZzgVMDwr28bD9cte29+dH8b9gD/LnswcK
XUCdfLjvQUHaiMNqtTp9t29m3cwPvLhbTT7v9PlrARTKn32JGJweERTcPf2lY9TBuO/EOk/jEPsf
ZppLKeq64FVVV7LnWDdHnXR4CRYUDClNUS2XdigWO+93FueIofMuoUuLzo9LnS1vNgMX/+zbrPrz
0hi/l6ufezzclo/36d/4Xa/nP9/exryc9m+Pjb35FBuZdUChsz4imH1JXhSNYIwxCocZYzLegYL/
jLVEEzd3FnUgJ9STd6RFYvXsugF7Yhzp/NjqRIwjaUDi8HciOTkhfsTPQCHpXQT1653CHCwoGFLi
dX3Uzmy97RejDh+Y4lLBaHky1mY6mw0ylLWY/Z/MZGW9LXBbcd30JVCYvLegLrDegJJywQWTrKmb
uhCM5xReb59B8bKsFoWQdZ7XhSgqKa8UpuBb3Rpr0hkUWzAmeVkJLppa1LXgnMIEii14XhWsrouy
ycu6qlhOIfmk7YM5zKY36QyKS0bBz6nzI/44TwgKvtyvb55egcKI3aCdiSMo6KZxzDhQOHo7jwgq
LyRflJIJIQpW8ZrCMbtKLr76OURQjIKJTxi9PWIPKoUZKQSM3s7baq03EE2WO5SXMq8KWVUljR4D
726rY3qZesfOkbzgRSPquqLQY+yCmRDoCyTKsvf2Iia1oH4lx5h00Lugp6HES/g7AX0qBd9cwQnk
whEjSZ683j6b7AQ8okskDTq5866JpPNua8KIPUnerWbrw3mPEuddljAm43ZkmsPkI0YCV3qBUYeP
CC9sve3h+n4Xtrj1AcmmS4Q3lOQszylQETAZfaUXViUFRLvzacCADHqefLD9osm9X9+vU2p/a/tJ
NbI8K7Fh1wE=
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- public, max-age=0, must-revalidate
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:24:24 GMT
Etag:
- W/"12l8utm5ovi11t"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::792tc-1671319464741-276ef026a889
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/market/jsqfBFbbIyP4X40L6VSo
response:
body:
string: !!binary |
MUAVAAC/e1H7+kXvGydgY2wjTVN319566ZKuXXfXTv3A9ktMisEBnCyLMnoDudDhdBD7fvi/nY4X
+vXftwc1DxLsFkjNa4EFGAW1krJQc85dORmWP3qK1nMB04OCfTxsv9y17f35Ufwv2IP8+eyBQhdQ
Jx/ue1CQNuKwWq1O3+2bWTfzAy/uVpPPO33+WgAF+LsvEYPTI4KCu6e/dIw6GNdOressD7H/YYa5
lKKuC15VdSX/7OvmqJMOL8GCgiGlKarl0g7FYuf9zuIcMXTeJXRp0flxqbPlzWbg4p99m1V/Xhrj
93L1c4+H2/LxPv0bv+v1/Ofb25iX0/7tsbE3n2Ijsw4odNZHJLMtyYuiEYwxRuEwY0zGO1Dwn7GW
aOLmzqIO5IR68o60SKyeXTdgT4wjnR9bnYhxJA1IHP5OJCcnxI/4GSgkvYugfr1TmIMFBUNKuu6P
2pmtt/1i1OEDU1xu0FuejLWZzmaDDGUtZv8nM1lZbwvcVlw3fQkUJu8tqAusN6CkXHDBJGvqpi4E
4zmF19tnULwsq0UhZJ3ndSGKSsorhSn4VrfGmnQGxRaMSV5WgoumFnUtOKcwgWILnlcFq+uibPKy
riqWU0g+aftgDrPpTTqD4pJR8HPq/Ig/zhOCgi/365unV6AwYjdoZ+IICrppHDMOFI7eziOCygvJ
F6VkQoiCVbymcKwukouvfg4RFKNg4hNGb4/Yg0phRgoBo7fztRrrDWSj43blpcyrQlYVpNZj0N1u
dUwvU+/YKZIXvGhEXVcUeoxdMBMDfQFfOqB+XSDJP2PSQe+CngagEBt/J1Dw7fSC8Oo5YiTJk9fb
ZyddgEd0iaRBJws+NZF03m1NGLEnyVvSbH2I+ZQ477KEMRm3I9McJh8xEqAcCZcjV3qBUYePCIp9
TuttD9d3tKTPkQpa3PqAZNMlwhtKcpbntBfFhES782nAgHoyTz7YfjG8/D4E976Da+Xv3X5yUUmZ
haTx6w==
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- public, max-age=0, must-revalidate
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:54:14 GMT
Etag:
- W/"tlc2arell811t"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::jhd9v-1671321254658-cd6a1bc21fab
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/market/jsqfBFbbIyP4X40L6VSo
response:
body:
string: !!binary |
MUAVAAC/e1H7+kXvGydgY2wjTVN319566ZKuXXfXTv3A9ktMisEBnCyLMnoDudDhdBD7fvi/nY4X
+vXftwc1DxLsFkjNa4EFGAW1krJQc85dORmWP3qK1nMB04OCfTxsv9y17f35Ufwv2IP8+eyBQhdQ
Jx/ue1CQNuKwWq1O3+2bWTfzAy/uVpPPO33+WgAF+LsvEYPTI4KCu6e/dIw6GNdOressD7H/YYa5
lKKuC15VdSX/7OvmqJMOL8GCgiGlKarl0g7FYuf9zuIcMXTeJXRp0flxqbPlzWbg4p99m1V/Xhrj
93L1c4+H2/LxPv0bv+v1/Ofb25iX0/7tsbE3n2Ijsw4odNZHJLMtyYuiEYwxRuEwY0zGO1Dwn7GW
aOLmzqIO5IR68o60SKyeXTdgT4wjnR9bnYhxJA1IHP5OJCcnxI/4GSgkvYugfr1TmIMFBUNKuu6P
2pmtt/1i1OEDU1xu0FuejLWZzmaDDGUtZv8nM1lZbwvcVlw3fQkUJu8tqAusN6CkXHDBJGvqpi4E
4zmF19tnULwsq0UhZJ3ndSGKSsorhSn4VrfGmnQGxRaMSV5WgoumFnUtOKcwgWILnlcFq+uibPKy
riqWU0g+aftgDrPpTTqD4pJR8HPq/Ig/zhOCgi/365unV6AwYjdoZ+IICrppHDMOFI7eziOCygvJ
F6VkQoiCVbymcKwukouvfg4RFKNg4hNGb4/Yg0phRgoBo7fztRrrDWSj43blpcyrQlYVpNZj0N1u
dUwvU+/YKZIXvGhEXVcUeoxdMBMDfQFfOqB+XSDJP2PSQe+CngagEBt/J1Dw7fSC8Oo5YiTJk9fb
ZyddgEd0iaRBJws+NZF03m1NGLEnyVvSbH2I+ZQ477KEMRm3I9McJh8xEqAcCZcjV3qBUYePCIp9
TuttD9d3tKTPkQpa3PqAZNMlwhtKcpbntBfFhES782nAgHoyTz7YfjG8/D4E976Da+Xv3X5yUUmZ
haTx6w==
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- public, max-age=0, must-revalidate
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:54:14 GMT
Etag:
- W/"tlc2arell811t"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
- HIT
X-Vercel-Id:
- cle1::cle1::j4972-1671321255226-394308e6489b
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/market/jsqfBFbbIyP4X40L6VSo
response:
body:
string: !!binary |
MUAVAAC/e1H7+kXvGydgY2wjTVN319566ZKuXXfXTv3A9ktMisEBnCyLMnoDudDhdBD7fvi/nY4X
+vXftwc1DxLsFkjNa4EFGAW1krJQc85dORmWP3qK1nMB04OCfTxsv9y17f35Ufwv2IP8+eyBQhdQ
Jx/ue1CQNuKwWq1O3+2bWTfzAy/uVpPPO33+WgAF+LsvEYPTI4KCu6e/dIw6GNdOressD7H/YYa5
lKKuC15VdSX/7OvmqJMOL8GCgiGlKarl0g7FYuf9zuIcMXTeJXRp0flxqbPlzWbg4p99m1V/Xhrj
93L1c4+H2/LxPv0bv+v1/Ofb25iX0/7tsbE3n2Ijsw4odNZHJLMtyYuiEYwxRuEwY0zGO1Dwn7GW
aOLmzqIO5IR68o60SKyeXTdgT4wjnR9bnYhxJA1IHP5OJCcnxI/4GSgkvYugfr1TmIMFBUNKuu6P
2pmtt/1i1OEDU1xu0FuejLWZzmaDDGUtZv8nM1lZbwvcVlw3fQkUJu8tqAusN6CkXHDBJGvqpi4E
4zmF19tnULwsq0UhZJ3ndSGKSsorhSn4VrfGmnQGxRaMSV5WgoumFnUtOKcwgWILnlcFq+uibPKy
riqWU0g+aftgDrPpTTqD4pJR8HPq/Ig/zhOCgi/365unV6AwYjdoZ+IICrppHDMOFI7eziOCygvJ
F6VkQoiCVbymcKwukouvfg4RFKNg4hNGb4/Yg0phRgoBo7fztRrrDWSj43blpcyrQlYVpNZj0N1u
dUwvU+/YKZIXvGhEXVcUeoxdMBMDfQFfOqB+XSDJP2PSQe+CngagEBt/J1Dw7fSC8Oo5YiTJk9fb
ZyddgEd0iaRBJws+NZF03m1NGLEnyVvSbH2I+ZQ477KEMRm3I9McJh8xEqAcCZcjV3qBUYePCIp9
TuttD9d3tKTPkQpa3PqAZNMlwhtKcpbntBfFhES782nAgHoyTz7YfjG8/D4E976Da+Xv3X5yUUmZ
haTx6w==
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- public, max-age=0, must-revalidate
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:54:14 GMT
Etag:
- W/"tlc2arell811t"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
- HIT
X-Vercel-Id:
- cle1::cle1::zg7sd-1671321255488-f881fda765fd
status:
code: 200
message: OK
version: 1

View File

@ -146,8 +146,6 @@ interactions:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
@ -157,4 +155,96 @@ interactions:
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/bets?contractId=Dx7vPilTr1rgQQVETM2Z
response:
body:
string: !!binary |
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
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- max-age=15, public
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:47:10 GMT
Etag:
- W/"fqpffj1rsja09"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/bets
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::w4swv-1671320830105-617de9a68d8d
status:
code: 200
message: OK
version: 1

View File

@ -0,0 +1,248 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/market/GdIMBXtOnA1wj3KJAYvj
response:
body:
string: !!binary |
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
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- public, max-age=0, must-revalidate
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:59:37 GMT
Etag:
- W/"l7ixek8t8y9y8"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::m779k-1671321577269-115946d2c8d1
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/bets?contractId=GdIMBXtOnA1wj3KJAYvj
response:
body:
string: !!binary |
cfz/AAC/X87qdB2V1HuC6hfSRxqtcMjtiDF4D6tK2JhQmOTQsv/++pxRtQYITiDsm31PdRWSBYFE
sigIue/cd1+zM7PbZGlCfsIUAgkpJU2D3EXQXRC0FoHBgFAVNC1id3ayn9ltC9RAajFipusQ3gGZ
JaoT+NGiRX9JbGk55O5J12/9f3+z6aa/3DZZb1LMN00n222a9Xmd9TLFp3aP68Pm+3qsF2ePu/vq
sx60V8fhlHWySdNsst7fbD59303r6fbrpGmyHnSy1bzYTtr14qRpsh50smrdFNt2fdI0WQ9+Otl0
c9/UzWK1nbbLd/6Sm9di3WyyHkWeLNxITSgYkney1botD5pJu26yHuSQUNiZyISRBOOwt3ddLJqs
lw2n1bZd/3PV7NtNm/38V+1Pts0660GOksQdxYiBEwN1snlbLPuLdrfcZj3LhRHZkRXFSLGTFV++
jPBOu6i/L7bF+nE9z3rZ63a72vR+/55M101ZbJrNtl0XL03+0rYv86ZYTTd51S5+7+F3+XtRLLfT
qrso1rNmu8mL1Wqzard51S5+t793m2bdnS6Kl2bzi06G02rbrq9OYjf/opPdcrZsP5b5avny32K+
/XfR1NPiP9t21iz/9UIT1E3dZWu8KxZVt8Qyuk0xETWzUihlnWxaZ73svjmezNrv6nFUnffbZQ38
/J51sunm8LWYz5vlS5P1JsV803Sydret2opeQcgqvOpx06yXVTbP26p2uV0X1RabbZ7W538ORtub
ZR8/3vjyoj/ev2UdCjLT1IPposl6aGYJXN3U9KfzV6YZAGAOgBImmJTB9DiNOB6OQpVhrLfTcX19
V97S5+Pwcy39h68lwAzZDZ3DXXf1SXmsjJt9CZWnTuQ4Pkcfh4fjhquDwycdNY/x5/Nrtrvtj6lQ
dwaxoCS016EyBC9u4Zss5KZAisKJRJjJOtRlY5ScExlhsDJAYuuovkCed9oPZNzsf9HJarLKN3vA
PwnW2GBTUbcJh65Egm7UNXQNyrJmpKJ0fFgJ9ng/nb+S1lRRhBWu/VEvDX+K9Xa6/Oe+WNbFfJ5h
yCNA2bSPzgOqQNZEPIDYREiQ1MB07KCoCZnYHUNIOPX6J2rT9yHDeeU8et4up2X7Rc3l2prZbIms
7bMk5hAJTI+gFOSJ3ZoQkTkAETMzSVqXnDfZy1V5xy/9IdbjGQ7fLvbH/eMMuwu1yvyVQ7XFbtOs
q3a5bZZE/VUX3d/9m1eU06kctc3sult+tlX7sr6r/C7m9uH10efDyf/7Ojh+975d3P27Sdatsp/O
30xuSR00YgKkwjqdsPbnpCO1ngmLsjCjUSQyelC4/KjtmbphlZySQ0JXBkwKf0FELCX6dNy8b/jq
G8qPhyNezF93p688z5wHid+MBgqLaXJU4giSUI0oTNh/OqMR0sCVEvrSSZMlJsKUgBgTC8sdYTUk
tyB/J7HSIhVxi02JBTqAGTglC0lpVyhL1f/rwODt+uYxDU+qOQwu5p+Lyencvh76vXYpnEXxn/Bq
OGV4d0wLPb5QSUVyqEpm6vAKLv3s7O3qC+/2o9e3XfuwJKdPCggKT+gorIKsmLPjchHUW+UaPB/p
HT5cc9RLiAlsWAzuOKM+NSKMDJZQQlQMlgg25H6z3+fF8p/b1+l8utp0gY7mkEQlOZM5oJ4nbUZX
p8XSjMcyOfjzdvx19fJ69pJmh9d3GmdJRqPJD+1ffFw9tS1/7hqm0fm0P1xf6cPXPT4dEKeDcM0j
iRmHCAjOO8gblOEwSq0mnj+XizE285frgk3akn6b19MChq8lXw0ORmVNk9NJMytPtZrf3j0MG02j
V9bD9eXHg3NBeAXOXZ4IFEVmkHrP1wV6omwAF8HJGWfO7G5jMoEcwJEBgUAg0JgG3kQ4d0uRXCmJ
KCBFyVIunCClcJEAV3RXeijAwp0lkiRKLJixUiK2qZ/JAo8OjMEXsP6bIBVUcLdDGXvrWKf37fXj
+/CUD6poh37jfNFP5ZrKsXV1Wlv5h7GYjxkMelWaAhmYKaEk1uCpoDCxS1dSNEJxw6OUJGnCGBCs
4OweHHCIeZmxkGrifsqGgp5MBJMIlHjx53tsoqrERBXi0kZUNYhM3tHx+VH/6unjFEfXB/V4V548
e+7hP52/r70HNqUgTRwDSRqDc1HVMCRw42g9c1LGS9BiY0dMjY99vkSSShnSFijTOno2PL0vTa4e
5h9ky+rp+elou/rKWKPIhoHd04WWW7hyYtQIF2y4dgvPYA7gDo4A7IiJgWefD0CRcCJOakrmRj9L
KMYH+tx8XLgDI3XiwcJuydSTsEOIA5POJER3yp3dWAAwQFBsTTOR+U3il9nqa673bXF9vJ2M5312
JOFgVyr40UJkJMU2yHcajAgCmIVYTr1kcoeXaTVMALE0hVdBUyWoCjQei0QW+0WpORG6eCt2vPkz
8MXb6uOgX02O5fFTry788CBNduPvZ32Ls2lz97kr717vx+MdXY9qjly2gaC4kmPCcJUIms6vH+2W
1W/snTeDg0KQJsAUdyvynqHBUZ0Sv+ikJp7UNTQNa6kRTMRWlbUy1YUYFfnbKmNPR5Koi4mmrpea
utJUk24pVerqpLHJRMLU6kzf18DJTdSxC+rGMviUO5klE3FAkLDUDbuur0YVsokyl/3kUs6k0bWf
zt+ON/Ic8pQoqXNyTCpvzEwBAK2e3RgshRCEJVY7roLcb8rBVfjM1FkvezlaP7ycfNR8s+B4vxvd
ju727lJ3o1g9kKOCHmv1riaRp8NFcLuTRXWQvYk0DOPiDMNgg8+nB+n0DeKlOwQEuEsyN3KnVNCx
1vhxeVmP+Pbh+Xu4vT35Wqwno4GPFqrRTgdRTsAGzIjoolSPSyYmSWYNEWxO6xaCfFB6H2JOYKO4
PzywGVmoRsp62JDhcTagDaa+uAFBZYYtVvWyo8TB2WA/KGfplh/vzp8Hx0f9V2sFp0nerVDVFldP
xBurjNgNHPJrEWQiAFfeQGTVOzIPEuv113ugC0x9NmJQQzEFGBqwmINbwgepqDvVACzlFARMDOR6
yaNnT8nMAlSSOyRteOHgcO1gcCD1dA48xo9JaLliWOVfBU7goZJh1CtBFaw44vqCis+j6CHdX97O
y4ePPl6/D0+/Pj4G60E2g86e3ss632ZUlDgICIulUujM1MfyYTg7p7lXMPp+3qzO14/HpcpdgPAB
x/L1RGNSuBNDrmds2SXMHUhZBIUQuLFLdpzKAULUjNAMREiAXeIFc9eJwUvX0SZfQRfN2WJTzJus
9zcrmyNyUdY0IPuO9aNQs1yCh8srhdSw+QxAcvqrhWOfznCiiDNnhZ4OEL0EDSeSfAM5y7Gy1+nm
oZ3XWW+73l3xi13lPbti+dfsWrMwsK29N35PG7WlwlTBjgsfQ4dCcEep/Yz799Gm/JpfFdVZn07u
T7LRMXDZLJMGBZ1CziMU86iK6+eFSAA7CiRWp97sYmoCHuQO7rgByLZJ20TKoGqBDPsq1OaNw3Q7
TLNyP5k8wvqC3g9stRvqQU/Wywn5rPD94izy2eCIqGbPNVBgSjGMwQKmWClc1CGIKeK+jKtihbGT
RUdbBAzykOTCMyql1nEqcd7CJxNBQq+y7qGGJd60ZXV7RKtJM4759zJ9PbBW1IG0V7VW49AwpRCP
VsINLxS5RhClURVJ68gSxGYcjFZ7U3IESRwcunOwEJj4djHTjtXtK2nlqK7f3X1bTpf/nLSbbbMO
Y1joDRU5CAB3z3ZHzj0Sq6RkGOhyU0wJ+CvdbQ/25WrXv4VqdPka748vh5uH1ekTLF8vM3P7qtVR
mqc1xL5LcowQcWTiRLIh2Kdsn/d3vz8Y9S++P291f7TYbhYlviwrq3V2cXrdLJZDGIw+LmB7dDdi
+qXtKLAfXsUi0783R2QQRgFXtQSbocb6fnP5dtB/Qj57mE1W7fijvNkOtPhOUweFMAKLZKroLmNy
1/YsN7yai8r0vYyQknsFEnHUmR0pzoaj4fryZiKrw4uz1Wj3+eRHZgdr+dEorOaWkgtGokSdeGSp
ydPKNihvnu888A5GDZUz9C1EBijbnMOucUCLE5rTDjlEYmFL8ZwDhSEZJgrd4UNeYzHsLgaMPbi5
QTy70DMgByMMQLKUWE05/q0jOiOR+1gwnMyv9esfPRcT5GBkZQnJHz2LCAzGBSDiUcVdJrfDg6+2
OSzunhcyenz3+3QzNv2wQOAkocLI7kzbvhhA9YJ9LmCHR2O1GUf0C9bb/KcSZSqebm382YyOl9WU
bwdQ9/fpg9MUBYYnc5JARlgUovauh9iQihXMEoZHPNkAanoldsBEhpzCyLe1z0DU3DQsBDQlNabk
CS8hheB2t+VcASIyNpAiTSlpoAOaMybmnTUaDpAa9XRgifkjnngUNoGXPOaeREwwUMkNcNoi8LdG
AxFHRSOV+wzRlRMr6HZ9YXmuwXI2VVkXb4fr9+H47O7x5mS19cH0c7CpP6tVxiEvhqlL35SThxK4
R3iytN0+luXPs6kipbbkJFzUtEXsaFcved5+DeLh7eZwUaxHV+vnOsJxZWkSBqk8DEQDE7CzJPKk
Q+rc0FrclhqpCNNQUSRWZwl+eAjO8J//4tqro6ubQ6BpmDnxnDycQFg1KRg4g0cyodT3fMwMuf89
5sUuRoO3HX/ePw/5Nt4P3o4PZ066NprKq+n+fDl4ba7adjZdvpzOi83mAsRGYkJCEqnbYQARoJhH
6AN2KafOMkswe5q/j6nl2fP++vvoz+HtxRo5XZQrUYAl5BBzgYmQGr+9uoe7Y6IQtmvJay+8mU/3
02LNruf4vMPP6nDQv+jkFpGAhCepP8PLUjSaohtliq4wl93k0nRFwCV5merk2U6ntxDkiqYJNWly
2JwAACfVVq/vZJP9rZ06zieIMqI0p6Ak+4CmkUtQgfFIxs+fOOURyv7uagQawyQJBnP/3d3v9tex
ZFkCJCgcDhYQDNzk0k0hDQjoskBSoGiUo9TgJMJZdGg0Hdx/cPEOkpBSWsdQinhk2C/hTJEjK6Yv
XsFmJjUIA0K6rR9C2c0YE5hY3NFBigdQ1PkzuY7+KoOtlAz6JE7Dq0Cg19PT6nxz2j+Wm1NcmjMc
Dcg1UFl2I1bFxGA3mEq39JNA9XmJCMQ8MNJF1ELNeWOkd9iD8vHyPCwYUlKFzYXTzONaTC1K1IC/
QShDkRyoaaSZwLyLiEhrn9gWERmlIaKk4EVD8CWzzk0vV/F73+3/7lLV6sfjrNFuR/e4QiiGIAQi
IWV2EUrqAHZOr6kIkbocAI/qpn02PP1xHUW80r3piRPQ0GI8PDAQGBMSovU3NicKgWQ19pRIZKuQ
QCqZcXUNZmJwsUFhxgCsSoIkQCfoK/0FQCCGiI1Vh9A69vG68XbcDnr3m95Qb7H9MpvIJlOUjCzh
cq6z9pk9uzU0Yi5aDYzPDiY98vE8EMFkdidWQUdKW0QQvZQLNnYhVfRU0+7/vdpvvZfFT+N7OC2P
8PxHJ+OlJsIMEsL4AvbCKN1VRETnqBkbJst/HMWk1YdFFdQqAZ0CAEHw1B7AYtUIpkq9kbRCwUKZ
EZNRNIHtpznvMuvBMhyXf+4iHPTeldftnezKw/vvcYo8Q7Mhkwr8f4RziKfhwaINOFVKjcmUwZBI
JNKFLRMoksyNQ5ONTqC0AuFBwXh8Tw1xVwhTaD3dJMSaGpI4k4ZzUdAdBmrVabH9GnSot17fbGFf
AubtSnosiFOVRY5EM3t0qMtdKVeevywR9UjQM2pVsP7DzGiRooz3HGwmYagkkpsAsLkVx2HvqM20
YmT2/q2BFrpcBvEuDu+1gjfD0ah8H/Z+urT9/Bwezr0f/fakE3hN0vJFiAU7KkIihcePhSEDKzKf
C0QFxSuPUCCKEoQZeAZegIKvTppHFqpi4QofOSwoQQApP+fp3OBYfEZ8dSJQJ3j2tuqfwXW6foNL
72HVoBKrggQqNZa8tBpXy8EMFG4hERiwsmcgWepVhObY+whcgAFAEIimouRwCcufUMqCTTDIhYUs
2QFTyc5WBo6D7fx1ac2by9Y2W6p8m1WAMRAQCjAkqmIXKK8WONKSVCDKHaIRmxBWgw4pG9lLSTXA
MqdF4keHMPlicczw2CSjSg0ShvOJ8u3P1sc1hYXIji6ESBIUyiNmygiaX8wh6E283M5kftTeYz1Y
v7RKfn9tjLMP9PVpUoKAUENis5EEEFoZQTtoonOBmrDbkClDvRkgS6m/fioCEDIiKUAxzF0pwrAF
EOES9q7Y9/rPna4bt3G/rp76+8n1MsKL6EvRDrGl57FdKtsG9uqyBWwdhecAWyEAKvvQx4F0fZW/
4o0kir4PKAQnB2LGNNREIo6dHk0FjicklreO/Bcl6VqYB2QODoYAFxmuwOn2FsKkE4l7bUgHjeV0
WvZW59PLznMdzWT/8oa3/p57X7jA6bRi2LtdU3GxY6mYRIERaG9VDA4qVj4hVqJ4UmQKBIUAXTgG
cSBrJopXEDNqDp//pBdBoKbTR5IG47v/8JDkKo2omIrlqyUFeZAzAAEo0RfTg/Qezw9xIjq6LDuL
u31Xd73D6fvpel8OxGW+KERdZw64V8P9wVpQyzAVP+nG+rv7fg291iJrD+fB/rL6Hh6rHQYc4v5T
VqOP+ku42drWh7qcyexh9bXfjxaxkJO8q8r5eT0/PB8vh5c5ifz29v8SnN4v848aLV/RlrVi1nIl
QIsrWa3oamELvnLAwDqXuawa3WviQgUB04wkbVLVvGBkVSEPCxn65qLRLYvr4tdbis3SbXbdZIzb
QgJxfoMUzNBIHSJCBgoI67AE4hrZX9VE3axXa4KMhVepFdtDhYfjHfDMdFZhA512vRuPm3X9OOkq
Nd87esVg5P33h0OtNAkh44WIEEpXAjNEvdlFqRCo3AdD8rWikKYQBIz4BzlnKCMUuROepbmAaoQY
jiaHydycyUd44X17+vR86Awm7fPnuLx0e7lXMZ5lonRSOiihCqYCosRwNQD316roaoCFbF+v1tId
3Tc3tGs2Oo8W9t7W1PdZrr1FkEsu4sWwyEK9WOaPAGHzvX/T+kvOn3uwOBFSLJcJUFVqfe4fdo8n
L9etEXfOgy8dtLg65/QakRKDN7fmSUaqBLjLLDzdRNaikFWF5yFq7CLA3/8H
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- max-age=15, public
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:59:37 GMT
Etag:
- W/"9u0rw0ww7wkdz"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/bets
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::w4swv-1671321577507-f05df93860a9
status:
code: 200
message: OK
version: 1

View File

@ -0,0 +1,367 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/market/VFXzjLOLH8WM64Cm37GK
response:
body:
string: !!binary |
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
wR0clQc=
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- public, max-age=0, must-revalidate
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Tue, 25 Oct 2022 18:50:31 GMT
Etag:
- W/"3uwvjy0hcsojw"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::8lbkj-1666723831545-26e5830117f5
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/bets?market=VFXzjLOLH8WM64Cm37GK
response:
body:
string: "<!DOCTYPE html><html lang=\"en\"><head><meta charSet=\"utf-8\"/><meta
property=\"og:title\" name=\"twitter:title\" content=\"Manifold Markets \u2014
A market for every question\"/><meta name=\"description\" content=\"Manifold
Markets lets you create a market on any question. Sign up in 30 seconds and
start trading on politics, sports, or anything that interests you.\"/><meta
property=\"og:description\" name=\"twitter:description\" content=\"Manifold
Markets lets you create a market on any question. Sign up in 30 seconds and
start trading on politics, sports, or anything that interests you.\"/><meta
property=\"og:url\" content=\"https://manifold.markets\"/><meta name=\"twitter:card\"
content=\"summary\"/><meta name=\"twitter:site\" content=\"@manifoldmarkets\"/><meta
property=\"og:image\" content=\"https://manifold.markets/logo-cover.png\"/><meta
name=\"twitter:image\" content=\"https://manifold.markets/logo-bg-white.png\"/><meta
name=\"viewport\" content=\"width=device-width, initial-scale=1\"/><meta name=\"apple-itunes-app\"
content=\"app-id=6444136749\"/><link rel=\"manifest\" href=\"manifest.json\"/><title>500:
Internal Server Error</title><meta name=\"next-head-count\" content=\"13\"/><link
rel=\"icon\" href=\"/favicon.ico\"/><link rel=\"preconnect\" href=\"https://fonts.gstatic.com\"
crossorigin=\"anonymous\"/><link href=\"https://fonts.googleapis.com/css2?family=Major+Mono+Display&amp;family=Readex+Pro:wght@300;400;600;700&amp;display=swap\"
rel=\"stylesheet\" crossorigin=\"anonymous\"/><link rel=\"preload\" href=\"/_next/static/css/97455fec8684a1cc.css\"
as=\"style\"/><link rel=\"stylesheet\" href=\"/_next/static/css/97455fec8684a1cc.css\"
data-n-g=\"\"/><noscript data-n-css=\"\"></noscript><script defer=\"\" nomodule=\"\"
src=\"/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js\"></script><script
src=\"/_next/static/chunks/webpack-09fe3cd31550f706.js\" defer=\"\"></script><script
src=\"/_next/static/chunks/framework-158b26224d322dce.js\" defer=\"\"></script><script
src=\"/_next/static/chunks/main-b021e68a7c605a21.js\" defer=\"\"></script><script
src=\"/_next/static/chunks/pages/_app-94bbca8800e6c43a.js\" defer=\"\"></script><script
src=\"/_next/static/chunks/pages/_error-d274bfcc303811f7.js\" defer=\"\"></script><script
src=\"/_next/static/8u8jLjCoHGBbBV29iO2Pu/_buildManifest.js\" defer=\"\"></script><script
src=\"/_next/static/8u8jLjCoHGBbBV29iO2Pu/_ssgManifest.js\" defer=\"\"></script></head><body
class=\"font-readex-pro bg-gray-50\"><div id=\"__next\"><div></div><div style=\"font-family:-apple-system,
BlinkMacSystemFont, Roboto, &quot;Segoe UI&quot;, &quot;Fira Sans&quot;, Avenir,
&quot;Helvetica Neue&quot;, &quot;Lucida Grande&quot;, sans-serif;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center\"><div><style>\n
\ body { margin: 0; color: #000; background: #fff; }\n .next-error-h1
{\n border-right: 1px solid rgba(0, 0, 0, .3);\n }\n\n
\ @media (prefers-color-scheme: dark) {\n body
{ color: #fff; background: #000; }\n .next-error-h1 {\n border-right:
1px solid rgba(255, 255, 255, .3);\n }\n }</style><h1
class=\"next-error-h1\" style=\"display:inline-block;margin:0;margin-right:20px;padding:0
23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px\">500</h1><div
style=\"display:inline-block;text-align:left;line-height:49px;height:49px;vertical-align:middle\"><h2
style=\"font-size:14px;font-weight:normal;line-height:49px;margin:0;padding:0\">Internal
Server Error<!-- -->.</h2></div></div></div></div><script id=\"__NEXT_DATA__\"
type=\"application/json\">{\"props\":{\"pageProps\":{\"statusCode\":500}},\"page\":\"/_error\",\"query\":{},\"buildId\":\"8u8jLjCoHGBbBV29iO2Pu\",\"nextExport\":true,\"isFallback\":false,\"gip\":true,\"scriptLoader\":[]}</script></body></html>"
headers:
Accept-Ranges:
- bytes
Access-Control-Allow-Origin:
- '*'
Age:
- '13'
Cache-Control:
- public, max-age=0, must-revalidate
Connection:
- keep-alive
Content-Disposition:
- inline; filename="500"
Content-Length:
- '3802'
Content-Type:
- text/html; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:37:13 GMT
Etag:
- '"f09253e060179e55d9170231032ee446"'
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
X-Matched-Path:
- /500
X-Vercel-Cache:
- HIT
X-Vercel-Id:
- cle1:cle1::xmtc9-1671320233419-7927f368b05e
status:
code: 500
message: Internal Server Error
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/bets?contractId=VFXzjLOLH8WM64Cm37GK
response:
body:
string: !!binary |
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
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- max-age=15, public
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:46:44 GMT
Etag:
- W/"164ashfft8hj6e"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/bets
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::7ks8t-1671320804612-7c326057ec6c
status:
code: 200
message: OK
version: 1

View File

@ -99,8 +99,6 @@ interactions:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
@ -110,4 +108,78 @@ interactions:
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/bets?contractId=cUDUvkVTXKuuyIkf0Fpz
response:
body:
string: !!binary |
UchMAAC/vj/fr9/JUveVgO3jJ9LVBHfA7e0DSoFCp2lyEgcMSRzshK5U/f3FhR5u/9oVE10UNZrw
/nvv3+0OZzvbcSZpotluIxtNsQlGVJVittvcDMNmolSgqF0HmStGom/lccEEttMu/uP/fKE2+Gxc
djagER6AAE4wBQpaKKwUT5CLK1vYuu2cb9CoNFW0CXIFGqH5agmNu25BzWo3Yev7/uURJSgP1nS2
WLvaohERAigRoBXVkKA+2vBsaotG6OhDtChBbfDZxJY+WDTCAyqp0IQyzJUQGGiCct90weTdfYFG
KN/8vbmet+vdQ99/3p9LPGtvKEF9tGF8NZ0Jm1ChETp2XRtHw2Hpgs1MtLHzwRzs4OD9obKmdXGQ
+3p4xcNsWJumc3lam3C2XRyYto2t7wa5r4d+2EcbUlebg413dHb0IdrPL00Ppqp865pD6pr0EEyM
qVBKaS2FTrmSQgKVvNQZV6zIwDCWF+Xg1B7+MlX3o7aFM392/mybHyUzUFrJUkLyPGUUl6kqsjJV
RmihmcCMlyhBfbThvkAjtDxPmo3Kx+9u8WhwefrfbW5PD3H2tH+nKEGm9n3ToRHBCfJ9l/uaDyEo
QZU3zbheMR4PQIsEuThuOivuAvFogo1oRORAAnAqiSIKJFey0t020YbG1CLml9ZGNPrSfhYfZtai
EU5Q5S69K1z3+bCorUxX+lC/R38nyMWfR1NVtjnsoel38sVEo+Iw+VJ5EFNqiuR1ZPPF22H9Nss/
fuP42lfTRnnEkEAza4KxT798TNXuTTD2js4U/S9Uv+TyUzDymDi3iupCpMCUSBnLijQDsGnJdEEg
V1JwjMzXlygFFFPFQk2Kqg/UTxbQAScKlNRSAqNSxgACDwaIwzB7C318O9+WV7u7t9XmzHaLUPO3
4IN7XY96UQyUHGUTmVBKCcoEB6KxYGzBrAsJDRNdwUKmNXBMGDvoeUH3403dNNmiOz11Zlub4wnS
BJMDXbLtHyaazLU2dt6ATm9YaMckaAWSE0w5ZgRLEJFW6hXSEZ629zs6y4P/gPkh5DInQmUmxRjr
lFkh0yzHOiWYW5XT3OQsbwtxOMVMKqWxVpoqlToICKL78FLIo+880NYs9Wut7MbPz5O+vGTPE7ky
n+vpYqvZdkLXlnjWut138oVQCOJ1HkWss2GHwQ5rfD+T+R1YasExpzLksfrGkZwpijUIAZoQRV48
PcDAJeVEECwJUYx2LzlvZnWErBzSRxty33S2QWkdZjger3fj37fxy3xDHsVl9/ttn73sH9rXl/fu
9bkVt/SdVX19WRWu+BG1SBkB15ZEgCSEYU6E0IpIIh0C/OVO/ZghWmkvrieh1z/mMMzTVfX8+abP
p11Op511t6fFykZ2ub5VQwuIqW5nMS+uD6aY3PLgtnpaTrOvPbqCTXuFN4SfBNHcDSY0O/cC0xi4
IlRISqQA3kJpBHOMg8AVOgq/hqdWw3JZvpzqyQ5X+fo15ZLDB1oKQgkGDVyCEIlJSPsa3ElRz3MA
anzZfy1+J1+/FrpFYlG5qzORgXQ1bMI3dVYe3fW+WR/to/dn1xzmlYlRvwhyCUrLoj/T5Yjc8abv
6GxJCMWUwVVpJTLLGFfWpCrTKmUAWaolsyljWDItM11omSBsV+hN3Q9gQbVESksX1w4yAIonA6xy
0HZ/WhYTujq+faz2xxDcXlzHCXI5kMAxFZooLSiXPCn0vDe70LOlMXQfZwLj2x7qXu3Wpx7+X71v
Yakuk9P0J0X3I2wSG7wozeOhn7hL87KplqQ6m8vP9ezFriYJvk09UDWokPqA2lVKsbCu5b1JN7Dh
siBB1NDmpIgFaNKlV9Zbt6oCPNQ/iXJzou2PXBaJgRBUHGHJizoV22jgtnzP+liRe9phomkm+42S
kB8M7OzIJxBOff87
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- max-age=15, public
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:47:13 GMT
Etag:
- W/"b3ba9cxts3sj"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/bets
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::w26gv-1671320833361-f2d17ab1b017
status:
code: 200
message: OK
version: 1

View File

@ -88,8 +88,6 @@ interactions:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
@ -99,4 +97,90 @@ interactions:
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/bets?contractId=eBuljtDFa2dR7bXSaYyY
response:
body:
string: !!binary |
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
zeGT5Fu9b1dXyqXr3wE=
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- max-age=15, public
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:46:47 GMT
Etag:
- W/"119tmefc6oe6dq"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/bets
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::np9qz-1671320807828-9c62f954e6bd
status:
code: 200
message: OK
version: 1

View File

@ -0,0 +1,596 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/market/gCFMraDRTKwOr2l7zzJF
response:
body:
string: !!binary |
MdQUAAC/5quersQDuhMlkxT1I1AUWT5Yty7p3BhbsO7wJD3LRChSISl7TtF158cDHgy/ix3w3Dxr
GQXSAgkswJYlEAQSGIQBUN7Eq+OLMWqcH2AnMDDfPvwZ8W7//MflKSrXvr///gAcxkiYQ/w8gYH1
H3t9ebl9uXubuy/3l/389WuNbxeRprsKONCXHRJFjwuBAVwx5kiJMI4nQXlkwE2M7YdoerZl2dSV
rPum0bqSBne+OWPGeIgODJxyXpPZ7Y420oCJUg4RZyrnEGZHuNpUjmHZncVu2C3osx2LBeMr5VTi
uqY15HIMyy7stkSxsAvOlD6oh9eI/oN6eKrkX+8H/825cvXzJ3T540KTxV9yeCX/cVBj10yyKbBB
VegWj0UnsC2G5jhUSFK0jQIOowuJQs3UCi1bWWshhODwtlHKNngw8Ld1juUTsfsthpXQs4O3wbMT
nomhZ+F4tKNFxzAuVzZcmRJKfwIOGecE5l+4hOgm+I/DFh0YOOVy3AW9PQY3ldH93U1yu4t1rsgn
Ksgaiw2KRyrQF+F4BA5rCA7MD3i5/wamquuyrzupG9Wruq5bzeHxCUyry052sm+k1LKrpPzJYY1h
wME6m69gRCn6XvSd6FrV6b7RisMKRpSVrmpda6k61fRt3bUccsjovti3zU42X8FIrUohlZCqF0o0
nVYcwpbHsNDzdSUw8Ovnx5v9C3BYaDyht2kBA+O6LIUEDufgtoXA1L0u20rXuu1bpbu+4/AhSv8W
tpjACA427SkFd6YJzBFdIg4OUz6s03turemlrJq+rvqWw0RpjHY9aB8rRmdxIZ/ZOWRKzPoc2EDW
zwzZYp3NGK8sxBm9fcdsg2f5hJnZxDAzR5gy2zchlnAhllYac9wWhok93jw/cWZLKpn1maLXCNBp
mzGElSKUZugndrH55KyDszOhIpXf/Xd/60IiNmEmtuX+hbEcmBJKF0IWQjIpTd2zdQEOmf7PdwXw
zwE=
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- public, max-age=0, must-revalidate
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:59:28 GMT
Etag:
- W/"12vw07zdmsc112"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::74zvs-1671321568186-c608f7bd72b4
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/bets?contractId=gCFMraDRTKwOr2l7zzJF
response:
body:
string: !!binary |
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
lU7UXFM29r+tCmaiDNLSOZKju/7znwE=
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- max-age=15, public
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:59:28 GMT
Etag:
- W/"14s00o4i48x46wz"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/bets
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::mnwlq-1671321568412-c6581aad559f
status:
code: 200
message: OK
version: 1

View File

@ -171,8 +171,6 @@ interactions:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/market/[id]
X-Vercel-Cache:
@ -182,4 +180,121 @@ interactions:
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
User-Agent:
- python-requests/2.28.1
method: GET
uri: https://manifold.markets/api/v0/bets?contractId=opaEWymtuX61FK2gKwXL
response:
body:
string: !!binary |
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
9cP9usz2lG4M2rfH96m8zH+E2jid18s=
headers:
Access-Control-Allow-Origin:
- '*'
Age:
- '0'
Cache-Control:
- max-age=15, public
Connection:
- keep-alive
Content-Encoding:
- br
Content-Type:
- application/json; charset=utf-8
Date:
- Sat, 17 Dec 2022 23:47:05 GMT
Etag:
- W/"16auljc0zmedwl"
Server:
- Vercel
Strict-Transport-Security:
- max-age=63072000
Transfer-Encoding:
- chunked
X-Matched-Path:
- /api/v0/bets
X-Vercel-Cache:
- MISS
X-Vercel-Id:
- cle1::cle1::bzhj9-1671320825642-dff87b58a9cb
status:
code: 200
message: OK
version: 1

Some files were not shown because too many files have changed in this diff Show More