General refactoring of code

This commit is contained in:
Gabe Appleton 2022-09-04 17:41:10 -04:00
parent e7b17cdead
commit 376e67934d
GPG Key ID: AF65A9CA0FF7FD69
10 changed files with 242 additions and 322 deletions

Binary file not shown.

Binary file not shown.

View File

@ -6,7 +6,7 @@ do
do
echo "Running for environment $file"
source $file
python example.py
python -m src
done
echo "Press once <CTRL+C> to check now, and twice to exit before next loop."
hour=0

View File

@ -1,153 +1,49 @@
[
{
"manifold": {
"outcomeType": "BINARY",
"question": "Is supersymmetry correct?",
"description": {
"type": "doc",
"content": [{
"type": "paragraph",
"content": [{
"type": "text",
"text": "https://en.wikipedia.org/wiki/Supersymmetry",
"marks": [{
"type": "link",
"attrs": {
"href": "https://en.wikipedia.org/wiki/Supersymmetry",
"target": "_blank",
"class": null
}
}]
}]
}]
},
"closeTime": "2100-12-31T11:59:59",
"initialProb": 50,
"minValue": null,
"maxValue": null,
"isLogScale": null,
"initialValue": null,
"groups": ["Physics Forecasting", "Olivia"]
},
"time_rules": [
["ResolveAtTime", {
"resolve_at": "2100-12-31T11:59:59"
}]
],
"value_rules": [
["RoundValueRule", {}]
],
"notes": "",
"initial_values": {}
},
{
"manifold": {
"outcomeType": "PSEUDO_NUMERIC",
"question": "When will the next mission to Jupiter be launched?",
"description": {
"type": "doc",
"content": [{
"type": "paragraph",
"content": [{
"type": "text",
"text": "Resolves to the fractional year at which a mission to the Jovian system is launched. This includes flyby missions, orbiters, landers, or any of these types of mission to a moon in orbit of Jupiter. This does not include any bodies which are in resonance with Jupiter but not in orbit."
}]
}],
"processed": true
},
"closeTime": "2100-12-31T11:59:59",
"initialProb": null,
"minValue": 2023,
"maxValue": 2100,
"isLogScale": true,
"initialValue": 2035,
"groups": ["Space", "Olivia"]
},
"time_rules": [
["ResolveAtTime", {
"resolve_at": "2100-12-31T11:59:59"
}]
],
"value_rules": [
["CurrentValueRule", {}]
],
"notes": "",
"initial_values": {}
},
{
"manifold": {
"outcomeType": "PSEUDO_NUMERIC",
"question": "When will the next mission to a minor body between Mars and the Kuiper Belt be launched?",
"description": {
"type": "doc",
"content": [{
"type": "paragraph",
"content": [{
"type": "text",
"text": "Resolves to the fractional year at which a mission to the Neptunian system is launched. This includes flyby missions, orbiters, landers, or miners. This does not include any bodies which are in orbit of a major planet."
}]
}],
"processed": true
},
"closeTime": "2100-12-31T11:59:59",
"initialProb": null,
"minValue": 2023,
"maxValue": 2100,
"isLogScale": true,
"initialValue": 2035,
"groups": ["Space", "Olivia"]
},
"time_rules": [
["ResolveAtTime", {
"resolve_at": "2100-12-31T11:59:59"
}]
],
"value_rules": [
["CurrentValueRule", {}]
],
"notes": "",
"initial_values": {}
},
{
"manifold": {
"outcomeType": "PSEUDO_NUMERIC",
"question": "When will the next mission to Kuiper Belt be launched?",
"description": {
"type": "doc",
"content": [{
"type": "paragraph",
"content": [{
"type": "text",
"text": "Resolves to the fractional year at which a mission to the Kuiper Belt is launched. This includes any body in the Kuiper Belt that is not considered a major planet or in orbit of a major planet."
}]
}],
"processed": true
},
"closeTime": "2100-12-31T11:59:59",
"initialProb": null,
"minValue": 2023,
"maxValue": 2100,
"isLogScale": true,
"initialValue": 2035,
"groups": ["Space", "Olivia"]
},
"time_rules": [
["ResolveAtTime", {
"resolve_at": "2100-12-31T11:59:59"
}]
],
"value_rules": [
["CurrentValueRule", {}]
],
"notes": "",
"initial_values": {}
}
]
{
"manifold": {
"outcomeType": "PSEUDO_NUMERIC",
"question": "When will the next mission to Kuiper Belt be launched?",
"description": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Resolves to the fractional year at which a mission to the Kuiper Belt is launched. This includes any body in the Kuiper Belt that is not considered a major planet or in orbit of a major planet."
}
]
}
],
"processed": true
},
"closeTime": "2100-12-31T11:59:59",
"initialProb": null,
"minValue": 2023,
"maxValue": 2100,
"isLogScale": true,
"initialValue": 2035,
"groups": [
"Space",
"Olivia"
]
},
"time_rules": [
[
"ResolveAtTime",
{
"resolve_at": "2100-12-31T11:59:59"
}
]
],
"value_rules": [
[
"CurrentValueRule",
{}
]
],
"notes": "",
"initial_values": {}
}
]

View File

@ -5,9 +5,9 @@ from json import dump, load
from re import match
from typing import Any, Dict, List, Literal, Optional, Union
from src import rule
from src.rule import explain_abstract, DoResolveRule, ResolutionValueRule
from src.market import Market, get_client
from src import rule, explain_abstract
from src.rule import DoResolveRule, ResolutionValueRule
from src.market import get_client, Market
from example import register_db
from pymanifold.types import DictDeserializable
@ -21,16 +21,19 @@ def main():
for mkt in obj.copy():
my_mkt = CreationRequest.from_dict(mkt).create()
db.execute("INSERT INTO markets VALUES ( (SELECT MAX(id)+1 from markets), ?, 3, NULL);", (my_mkt, ))
((new_id, ), ) = db.execute("SELECT MAX(id) from markets;")
print(f"Added as ID {new_id}")
print(my_mkt.market.url)
obj.remove(mkt)
finally:
db.commit()
db.close()
with open('example.json', 'w') as f:
dump(obj, default=date_serialization_hook)
dump(obj, f, default=date_serialization_hook, indent="\t")
def date_serialization_hook(obj):
"""JSON serializer for objects not serializable by default json code"""
"""JSON serializer for objects not serializable by default json code."""
if isinstance(obj, (datetime, date)):
return obj.isoformat()
raise TypeError(f"Type ${type(obj)} not serializable")
@ -56,13 +59,13 @@ class ManifoldRequest(DictDeserializable):
description: Union[str, Any]
closeTime: int
initialProb: Optional[float] # Note: probability is multiplied by 100, may only allow integers in binary market
initialProb: Optional[float] = None # Note: probability is multiplied by 100, may only allow integers
minValue: Optional[float]
maxValue: Optional[float]
isLogScale: Optional[bool]
initialValue: Optional[float]
groups: List[str] = field(default_factory=list)
minValue: Optional[float] = None
maxValue: Optional[float] = None
isLogScale: Optional[bool] = None
initialValue: Optional[float] = None
tags: List[str] = field(default_factory=list)
def __post_init__(self):
if self.outcomeType == "BINARY":
@ -91,20 +94,27 @@ class CreationRequest:
notes: str = ""
initial_values: Dict[str, int] = field(default_factory=dict)
def __postinit__(self):
def __post_init__(self):
if not self.manifold.description.get("processed"):
for paragraph in explain_abstract(
explanation = "\n" + explain_abstract(
time_rules=self.time_rules, value_rules=self.value_rules
).splitlines():
# if not paragraph:
# continue
self.manifold.description["content"].append({
"type": "paragraph",
"content": [{
"type": "text",
"text": paragraph
}]
})
)
for paragraph in explanation.splitlines():
if paragraph:
if paragraph.startswith(" "):
for idx, character in enumerate(paragraph):
if character != " ":
break
paragraph = paragraph.replace(" ", "&nbsp;", idx)
self.manifold.description["content"].append({
"type": "paragraph",
"content": [{
"type": "text",
"text": paragraph
}]
})
else:
self.manifold.description["content"].append({"type": "paragraph"})
self.manifold.description["processed"] = True
@classmethod
@ -139,7 +149,12 @@ class CreationRequest:
else:
raise ValueError()
return Market(market, do_resolve_rules=self.time_rules, resolve_to_rules=self.value_rules, notes=self.notes)
return Market(
client.get_market_by_id(market.id),
do_resolve_rules=self.time_rules,
resolve_to_rules=self.value_rules,
notes=self.notes
)
if __name__ == '__main__':

View File

@ -47,6 +47,27 @@ def require_env(*env: str):
return bar
def explain_abstract(**kwargs) -> str:
"""Explain how the market will resolve and decide to resolve."""
ret = "This market will resolve if any of the following are true:\n"
for rule_ in kwargs["time_rules"]:
ret += rule_.explain_abstract(**kwargs)
ret += ("\nIt will resolve based on the following decision tree:\n"
"- If the human operator agrees:\n")
for rule_ in kwargs["value_rules"]:
ret += rule_.explain_abstract(indent=1, **kwargs)
ret += (
"- Otherwise, a manually provided value\n\n"
"Note that the bot operator reserves the right to resolve contrary to the purely automated rules to "
"preserve the spirit of the market. All resolutions are first verified by the human operator."
"\n\n"
"The operator also reserves the right to trade on this market unless otherwise specified. Even if "
"otherwise specified, the operator reserves the right to buy shares for subsidy or to trade for the "
"purposes of cashing out liquidity.\n"
)
return ret
class Rule(DictDeserializable):
"""The basic unit of market automation, rules defmine how a market should react to given events."""

128
src/__main__.py Normal file
View File

@ -0,0 +1,128 @@
from argparse import ArgumentParser
from datetime import datetime
from logging import basicConfig, getLogger, DEBUG, INFO
from os import getenv
from typing import cast, Optional, Tuple
from .application import register_db, main
from . import market, rule
# 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__)
parser = ArgumentParser()
parser.add_argument('-s', '--add-slug', action='store', dest='slug')
parser.add_argument('-i', '--add-id', action='store', dest='id_')
parser.add_argument('-u', '--add-url', action='store', dest='url')
parser.add_argument('-c', '--check-rate', action='store', dest='rate', help='Check rate in hours')
parser.add_argument('-mi', '--min', action='store',
help="Only used for numeric markets, until they add this to the API")
parser.add_argument('-ma', '--max', action='store',
help="Only used for numeric markets, until they add this to the API")
parser.add_argument('-ls', '--log_scale', action='store_true', dest='isLogScale',
help="Only used for numeric markets, until they add this to the API")
parser.add_argument('-r', '--refresh', action='store_true', dest='refresh',
help="Ignore time last checked and look at all markets immediately")
parser.add_argument('-rm', '--remove-id', action='append', dest='rm_id', default=[],
help="Remove a specific market from management. May be repeated.")
parser.add_argument('-rnd', '--round', action='store_true')
parser.add_argument('-cur', '--current', action='store_true')
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')
parser.add_argument('-pr', '--pull-request', action='store', dest='pr_slug', help='Please give as "owner/repo/num"')
parser.add_argument('-pb', '--pull-binary', action='store_true', dest='pr_bin')
parser.add_argument('-sk', '--skip', action='store_true')
parser.add_argument('-co', '--console-only', action='store_true')
parser.add_argument('-rs', '--random-seed', action='store')
parser.add_argument('-rr', '--random-rounds', action='store', type=int, default=1)
parser.add_argument('-ri', '--random-index', action='store_true')
parser.add_argument('-is', '--index-size', action='store', type=int)
args = parser.parse_args()
for id_ in args.rm_id:
conn = register_db()
((mkt, ), ) = conn.execute(
"SELECT market FROM markets WHERE id = ?;",
(id_, )
)
if input(f'Are you sure you want to remove {id_}: "{mkt.market.question}" (y/N)?').lower().startswith('y'):
conn.execute(
"DELETE FROM markets WHERE id = ?;",
(id_, )
)
conn.commit()
logger.info(f"{id_} removed from db")
conn.close()
if any((args.slug, args.id_, args.url)):
if args.url:
args.slug = args.url.split('/')[-1]
if args.slug:
mkt = market.Market.from_slug(args.slug)
else:
mkt = market.Market.from_id(args.id)
if args.rel_date:
sections = args.rel_date.split('/')
if len(sections) == 1:
sections = args.rel_date.split('-')
try:
date: Optional[Tuple[int, int, int]] = tuple(int(x) for x in sections) # type: ignore[assignment]
except ValueError:
raise
else:
date = None
if args.random_index:
mkt.resolve_to_rules.append(
rule.ResolveRandomIndex(args.random_seed, size=args.index_size, rounds=args.random_rounds)
)
if args.round:
mkt.resolve_to_rules.append(rule.RoundValueRule())
if args.current:
mkt.resolve_to_rules.append(rule.CurrentValueRule())
if args.pr_slug:
pr_ = list(args.pr_slug.split('/'))
pr_[-1] = int(pr_[-1])
pr = cast(Tuple[str, str, int], tuple(pr_))
mkt.do_resolve_rules.append(rule.ResolveWithPR(*pr))
if date:
mkt.resolve_to_rules.append(rule.ResolveToPRDelta(*pr, datetime(*date)))
elif args.pr_bin:
mkt.resolve_to_rules.append(rule.ResolveToPR(*pr))
else:
raise ValueError("No resolve rule provided")
if not mkt.do_resolve_rules:
if not date:
raise ValueError("No resolve date provided")
mkt.do_resolve_rules.append(rule.ResolveAtTime(datetime(*date)))
conn = register_db()
idx = max(((0, ), *conn.execute("SELECT id FROM markets;")))[0] + 1
conn.execute("INSERT INTO markets values (?, ?, ?, ?);", (idx, mkt, 1, None))
conn.commit()
print(msg := f"Successfully added as ID {idx}!")
logger.info(msg)
conn.close()
if not args.skip:
main(args.refresh, args.console_only)

View File

@ -1,13 +1,12 @@
from argparse import ArgumentParser
from asyncio import get_event_loop, new_event_loop, set_event_loop
from dataclasses import dataclass
from datetime import datetime, timedelta
from enum import IntEnum
from logging import basicConfig, getLogger, DEBUG, INFO
from logging import getLogger
from pathlib import Path
from os import getenv
from sqlite3 import connect, PARSE_COLNAMES, PARSE_DECLTYPES
from typing import cast, Optional, Tuple
from typing import cast
from telegram import __version__ as TG_VER
@ -26,14 +25,8 @@ if __version_info__ < (20, 0, 0, "alpha", 1):
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import Application, CallbackQueryHandler, ContextTypes
from src import (market, require_env, rule)
from src import market, require_env
# 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__)
@ -195,117 +188,3 @@ def main(refresh: bool = False, console_only: bool = False):
)
conn.commit()
conn.close()
if __name__ == '__main__':
parser = ArgumentParser()
parser.add_argument('-s', '--add-slug', action='store', dest='slug')
parser.add_argument('-i', '--add-id', action='store', dest='id_')
parser.add_argument('-u', '--add-url', action='store', dest='url')
parser.add_argument('-c', '--check-rate', action='store', dest='rate', help='Check rate in hours')
parser.add_argument('-mi', '--min', action='store',
help="Only used for numeric markets, until they add this to the API")
parser.add_argument('-ma', '--max', action='store',
help="Only used for numeric markets, until they add this to the API")
parser.add_argument('-ls', '--log_scale', action='store_true', dest='isLogScale',
help="Only used for numeric markets, until they add this to the API")
parser.add_argument('-r', '--refresh', action='store_true', dest='refresh',
help="Ignore time last checked and look at all markets immediately")
parser.add_argument('-rm', '--remove-id', action='append', dest='rm_id', default=[],
help="Remove a specific market from management. May be repeated.")
parser.add_argument('-rnd', '--round', action='store_true')
parser.add_argument('-cur', '--current', action='store_true')
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')
parser.add_argument('-pr', '--pull-request', action='store', dest='pr_slug', help='Please give as "owner/repo/num"')
parser.add_argument('-pb', '--pull-binary', action='store_true', dest='pr_bin')
parser.add_argument('-sk', '--skip', action='store_true')
parser.add_argument('-co', '--console-only', action='store_true')
parser.add_argument('-rs', '--random-seed', action='store')
parser.add_argument('-rr', '--random-rounds', action='store', type=int, default=1)
parser.add_argument('-ri', '--random-index', action='store_true')
parser.add_argument('-is', '--index-size', action='store', type=int)
args = parser.parse_args()
for id_ in args.rm_id:
conn = register_db()
((mkt, ), ) = conn.execute(
"SELECT market FROM markets WHERE id = ?;",
(id_, )
)
if input(f'Are you sure you want to remove {id_}: "{mkt.market.question}" (y/N)?').lower().startswith('y'):
conn.execute(
"DELETE FROM markets WHERE id = ?;",
(id_, )
)
conn.commit()
logger.info(f"{id_} removed from db")
conn.close()
if any((args.slug, args.id_, args.url)):
if args.url:
args.slug = args.url.split('/')[-1]
if args.slug:
mkt = market.Market.from_slug(args.slug)
else:
mkt = market.Market.from_id(args.id)
if args.rel_date:
sections = args.rel_date.split('/')
if len(sections) == 1:
sections = args.rel_date.split('-')
try:
date: Optional[Tuple[int, int, int]] = tuple(int(x) for x in sections) # type: ignore[assignment]
except ValueError:
raise
else:
date = None
if args.random_index:
mkt.resolve_to_rules.append(
rule.ResolveRandomIndex(args.random_seed, size=args.index_size, rounds=args.random_rounds)
)
if args.round:
mkt.resolve_to_rules.append(rule.RoundValueRule())
if args.current:
mkt.resolve_to_rules.append(rule.CurrentValueRule())
if args.pr_slug:
pr_ = list(args.pr_slug.split('/'))
pr_[-1] = int(pr_[-1])
pr = cast(Tuple[str, str, int], tuple(pr_))
mkt.do_resolve_rules.append(rule.ResolveWithPR(*pr))
if date:
mkt.resolve_to_rules.append(rule.ResolveToPRDelta(*pr, datetime(*date)))
elif args.pr_bin:
mkt.resolve_to_rules.append(rule.ResolveToPR(*pr))
else:
raise ValueError("No resolve rule provided")
if not mkt.do_resolve_rules:
if not date:
raise ValueError("No resolve date provided")
mkt.do_resolve_rules.append(rule.ResolveAtTime(datetime(*date)))
conn = register_db()
idx = max(((0, ), *conn.execute("SELECT id FROM markets;")))[0] + 1
conn.execute("INSERT INTO markets values (?, ?, ?, ?);", (idx, mkt, 1, None))
conn.commit()
print(msg := f"Successfully added as ID {idx}!")
logger.info(msg)
conn.close()
if not args.skip:
main(args.refresh, args.console_only)

View File

@ -10,8 +10,8 @@ from typing import Any, Dict, List, Union
from pymanifold import ManifoldClient
from pymanifold.types import Market as APIMarket
from . import require_env
from .rule import explain_abstract, DoResolveRule, ResolutionValueRule
from . import explain_abstract, require_env
from .rule import DoResolveRule, ResolutionValueRule
@lru_cache

View File

@ -11,25 +11,6 @@ import requests
from . import require_env, Rule
def explain_abstract(**kwargs) -> str:
"""Explain how the market will resolve and decide to resolve."""
ret = "This market will resolve if any of the following are true:\n"
for rule in kwargs["time_rules"]:
ret += rule.explain_abstract(**kwargs)
ret += "\nIt will resolve based on the following decision tree:\n"
for rule in kwargs["value_rules"]:
ret += rule.explain_abstract(**kwargs)
ret += (
"\nNote that the bot operator reserves the right to resolve contrary to the purely automated rules to "
"preserve the spirit of the market. All resolutions are first verified by the human operator."
"\n\n"
"The operator also reserves the right to trade on this market unless otherwise specified. Even if "
"otherwise specified, the operator reserves the right to buy shares for subsidy or to trade for the "
"purposes of cashing out liquidity.\n"
)
return ret
class DoResolveRule(Rule):
"""The subtype of rule which determines if a market should resolve, returning a bool."""